authorgravatar for BarabasGitHub@users.noreply.github.comBas <BarabasGitHub@users.noreply.github.com> 2020-09-08 11:56:59+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-09-08 11:56:59+02:00
log4a6ca735d9b3d466aba37c4488c1235b06a0bc84
tree10ef029ccaefe15c5152c1512a952ca6fdb01358
parent0a40a61548ad9f666ed5300a8910f9040cc1390b
parent389c26025283edef2206d19d9ad1ddc41e98f007
signature Signed by PGP key 4AEE18F83AFDEB23

Merge branch 'master' into improve-windows-networking


119 files changed, 6400 insertions(+), 2054 deletions(-)

build.zig+7-1
......@@ -123,7 +123,13 @@ pub fn build(b: *Builder) !void {
123123 .source_dir = "lib",
124124 .install_dir = .Lib,
125125 .install_subdir = "zig",
126 .exclude_extensions = &[_][]const u8{ "test.zig", "README.md" },
126 .exclude_extensions = &[_][]const u8{
127 "test.zig",
128 "README.md",
129 ".z.0",
130 ".z.9",
131 "rfc1951.txt",
132 },
127133 });
128134
129135 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
doc/langref.html.in+15-15
......@@ -2156,7 +2156,7 @@ test "pointer casting" {
21562156
21572157test "pointer child type" {
21582158 // pointer types have a `child` field which tells you the type they point to.
2159 assert((*u32).Child == u32);
2159 assert(@typeInfo(*u32).Pointer.child == u32);
21602160}
21612161 {#code_end#}
21622162 {#header_open|Alignment#}
......@@ -2184,7 +2184,7 @@ test "variable alignment" {
21842184 assert(@TypeOf(&x) == *i32);
21852185 assert(*i32 == *align(align_of_i32) i32);
21862186 if (std.Target.current.cpu.arch == .x86_64) {
2187 assert((*i32).alignment == 4);
2187 assert(@typeInfo(*i32).Pointer.alignment == 4);
21882188 }
21892189}
21902190 {#code_end#}
......@@ -2202,7 +2202,7 @@ const assert = @import("std").debug.assert;
22022202var foo: u8 align(4) = 100;
22032203
22042204test "global variable alignment" {
2205 assert(@TypeOf(&foo).alignment == 4);
2205 assert(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
22062206 assert(@TypeOf(&foo) == *align(4) u8);
22072207 const as_pointer_to_array: *[1]u8 = &foo;
22082208 const as_slice: []u8 = as_pointer_to_array;
......@@ -4310,8 +4310,8 @@ test "fn type inference" {
43104310const assert = @import("std").debug.assert;
43114311
43124312test "fn reflection" {
4313 assert(@TypeOf(assert).ReturnType == void);
4314 assert(@TypeOf(assert).is_var_args == false);
4313 assert(@typeInfo(@TypeOf(assert)).Fn.return_type.? == void);
4314 assert(@typeInfo(@TypeOf(assert)).Fn.is_var_args == false);
43154315}
43164316 {#code_end#}
43174317 {#header_close#}
......@@ -4611,10 +4611,10 @@ test "error union" {
46114611 foo = error.SomeError;
46124612
46134613 // Use compile-time reflection to access the payload type of an error union:
4614 comptime assert(@TypeOf(foo).Payload == i32);
4614 comptime assert(@typeInfo(@TypeOf(foo)).ErrorUnion.payload == i32);
46154615
46164616 // Use compile-time reflection to access the error set type of an error union:
4617 comptime assert(@TypeOf(foo).ErrorSet == anyerror);
4617 comptime assert(@typeInfo(@TypeOf(foo)).ErrorUnion.error_set == anyerror);
46184618}
46194619 {#code_end#}
46204620 {#header_open|Merging Error Sets#}
......@@ -4991,7 +4991,7 @@ test "optional type" {
49914991 foo = 1234;
49924992
49934993 // Use compile-time reflection to access the child type of the optional:
4994 comptime assert(@TypeOf(foo).Child == i32);
4994 comptime assert(@typeInfo(@TypeOf(foo)).Optional.child == i32);
49954995}
49964996 {#code_end#}
49974997 {#header_close#}
......@@ -6889,7 +6889,7 @@ fn func(y: *i32) void {
68896889 This builtin function atomically dereferences a pointer and returns the value.
68906890 </p>
68916891 <p>
6892 {#syntax#}T{#endsyntax#} must be a {#syntax#}bool{#endsyntax#}, a float,
6892 {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
68936893 an integer or an enum.
68946894 </p>
68956895 {#header_close#}
......@@ -6899,7 +6899,7 @@ fn func(y: *i32) void {
68996899 This builtin function atomically modifies memory and then returns the previous value.
69006900 </p>
69016901 <p>
6902 {#syntax#}T{#endsyntax#} must be a {#syntax#}bool{#endsyntax#}, a float,
6902 {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
69036903 an integer or an enum.
69046904 </p>
69056905 <p>
......@@ -6925,7 +6925,7 @@ fn func(y: *i32) void {
69256925 This builtin function atomically stores a value.
69266926 </p>
69276927 <p>
6928 {#syntax#}T{#endsyntax#} must be a {#syntax#}bool{#endsyntax#}, a float,
6928 {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
69296929 an integer or an enum.
69306930 </p>
69316931 {#header_close#}
......@@ -7208,10 +7208,10 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_v
72087208 more efficiently in machine instructions.
72097209 </p>
72107210 <p>
7211 {#syntax#}T{#endsyntax#} must be a {#syntax#}bool{#endsyntax#}, a float,
7211 {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
72127212 an integer or an enum.
72137213 </p>
7214 <p>{#syntax#}@TypeOf(ptr).alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}</p>
7214 <p>{#syntax#}@typeInfo(@TypeOf(ptr)).Pointer.alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}</p>
72157215 {#see_also|Compile Variables|cmpxchgWeak#}
72167216 {#header_close#}
72177217 {#header_open|@cmpxchgWeak#}
......@@ -7237,10 +7237,10 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
72377237 However if you need a stronger guarantee, use {#link|@cmpxchgStrong#}.
72387238 </p>
72397239 <p>
7240 {#syntax#}T{#endsyntax#} must be a {#syntax#}bool{#endsyntax#}, a float,
7240 {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
72417241 an integer or an enum.
72427242 </p>
7243 <p>{#syntax#}@TypeOf(ptr).alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}</p>
7243 <p>{#syntax#}@typeInfo(@TypeOf(ptr)).Pointer.alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}</p>
72447244 {#see_also|Compile Variables|cmpxchgStrong#}
72457245 {#header_close#}
72467246
lib/std/array_list.zig+10-2
......@@ -46,7 +46,11 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
4646 /// Deinitialize with `deinit` or use `toOwnedSlice`.
4747 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
4848 var self = Self.init(allocator);
49 try self.ensureCapacity(num);
49
50 const new_memory = try self.allocator.allocAdvanced(T, alignment, num, .at_least);
51 self.items.ptr = new_memory.ptr;
52 self.capacity = new_memory.len;
53
5054 return self;
5155 }
5256
......@@ -366,7 +370,11 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
366370 /// Deinitialize with `deinit` or use `toOwnedSlice`.
367371 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
368372 var self = Self{};
369 try self.ensureCapacity(allocator, num);
373
374 const new_memory = try self.allocator.allocAdvanced(T, alignment, num, .at_least);
375 self.items.ptr = new_memory.ptr;
376 self.capacity = new_memory.len;
377
370378 return self;
371379 }
372380
lib/std/c.zig+5
......@@ -330,3 +330,8 @@ pub const FILE = @Type(.Opaque);
330330pub extern "c" fn dlopen(path: [*:0]const u8, mode: c_int) ?*c_void;
331331pub extern "c" fn dlclose(handle: *c_void) c_int;
332332pub extern "c" fn dlsym(handle: ?*c_void, symbol: [*:0]const u8) ?*c_void;
333
334pub extern "c" fn sync() void;
335pub extern "c" fn syncfs(fd: c_int) c_int;
336pub extern "c" fn fsync(fd: c_int) c_int;
337pub extern "c" fn fdatasync(fd: c_int) c_int;
lib/std/child_process.zig+5-7
......@@ -44,10 +44,10 @@ pub const ChildProcess = struct {
4444 stderr_behavior: StdIo,
4545
4646 /// Set to change the user id when spawning the child process.
47 uid: if (builtin.os.tag == .windows) void else ?u32,
47 uid: if (builtin.os.tag == .windows or builtin.os.tag == .wasi) void else ?os.uid_t,
4848
4949 /// Set to change the group id when spawning the child process.
50 gid: if (builtin.os.tag == .windows) void else ?u32,
50 gid: if (builtin.os.tag == .windows or builtin.os.tag == .wasi) void else ?os.gid_t,
5151
5252 /// Set to change the current working directory when spawning the child process.
5353 cwd: ?[]const u8,
......@@ -275,9 +275,7 @@ pub const ChildProcess = struct {
275275 }
276276
277277 fn handleWaitResult(self: *ChildProcess, status: u32) void {
278 // TODO https://github.com/ziglang/zig/issues/3190
279 var term = self.cleanupAfterWait(status);
280 self.term = term;
278 self.term = self.cleanupAfterWait(status);
281279 }
282280
283281 fn cleanupStreams(self: *ChildProcess) void {
......@@ -487,8 +485,8 @@ pub const ChildProcess = struct {
487485 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
488486
489487 const nul_handle = if (any_ignore)
490 windows.OpenFile(&[_]u16{ 'N', 'U', 'L' }, .{
491 .dir = std.fs.cwd().fd,
488 // "\Device\Null" or "\??\NUL"
489 windows.OpenFile(&[_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }, .{
492490 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
493491 .share_access = windows.FILE_SHARE_READ,
494492 .creation = windows.OPEN_EXISTING,
lib/std/coff.zig+66
......@@ -18,11 +18,77 @@ const IMAGE_FILE_MACHINE_I386 = 0x014c;
1818const IMAGE_FILE_MACHINE_IA64 = 0x0200;
1919const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
2020
21pub const MachineType = enum(u16) {
22 Unknown = 0x0,
23 /// Matsushita AM33
24 AM33 = 0x1d3,
25 /// x64
26 X64 = 0x8664,
27 /// ARM little endian
28 ARM = 0x1c0,
29 /// ARM64 little endian
30 ARM64 = 0xaa64,
31 /// ARM Thumb-2 little endian
32 ARMNT = 0x1c4,
33 /// EFI byte code
34 EBC = 0xebc,
35 /// Intel 386 or later processors and compatible processors
36 I386 = 0x14c,
37 /// Intel Itanium processor family
38 IA64 = 0x200,
39 /// Mitsubishi M32R little endian
40 M32R = 0x9041,
41 /// MIPS16
42 MIPS16 = 0x266,
43 /// MIPS with FPU
44 MIPSFPU = 0x366,
45 /// MIPS16 with FPU
46 MIPSFPU16 = 0x466,
47 /// Power PC little endian
48 POWERPC = 0x1f0,
49 /// Power PC with floating point support
50 POWERPCFP = 0x1f1,
51 /// MIPS little endian
52 R4000 = 0x166,
53 /// RISC-V 32-bit address space
54 RISCV32 = 0x5032,
55 /// RISC-V 64-bit address space
56 RISCV64 = 0x5064,
57 /// RISC-V 128-bit address space
58 RISCV128 = 0x5128,
59 /// Hitachi SH3
60 SH3 = 0x1a2,
61 /// Hitachi SH3 DSP
62 SH3DSP = 0x1a3,
63 /// Hitachi SH4
64 SH4 = 0x1a6,
65 /// Hitachi SH5
66 SH5 = 0x1a8,
67 /// Thumb
68 Thumb = 0x1c2,
69 /// MIPS little-endian WCE v2
70 WCEMIPSV2 = 0x169,
71};
72
2173// OptionalHeader.magic values
2274// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
2375const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
2476const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
2577
78// Image Characteristics
79pub const IMAGE_FILE_RELOCS_STRIPPED = 0x1;
80pub const IMAGE_FILE_DEBUG_STRIPPED = 0x200;
81pub const IMAGE_FILE_EXECUTABLE_IMAGE = 0x2;
82pub const IMAGE_FILE_32BIT_MACHINE = 0x100;
83pub const IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x20;
84
85// Section flags
86pub const IMAGE_SCN_CNT_INITIALIZED_DATA = 0x40;
87pub const IMAGE_SCN_MEM_READ = 0x40000000;
88pub const IMAGE_SCN_CNT_CODE = 0x20;
89pub const IMAGE_SCN_MEM_EXECUTE = 0x20000000;
90pub const IMAGE_SCN_MEM_WRITE = 0x80000000;
91
2692const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
2793const IMAGE_DEBUG_TYPE_CODEVIEW = 2;
2894const DEBUG_DIRECTORY = 6;
lib/std/compress.zig created+13
......@@ -0,0 +1,13 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 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.
6const std = @import("std.zig");
7
8pub const deflate = @import("compress/deflate.zig");
9pub const zlib = @import("compress/zlib.zig");
10
11test "" {
12 _ = zlib;
13}
lib/std/compress/deflate.zig created+521
......@@ -0,0 +1,521 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 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// Decompressor for DEFLATE data streams (RFC1951)
8//
9// Heavily inspired by the simple decompressor puff.c by Mark Adler
10
11const std = @import("std");
12const io = std.io;
13const math = std.math;
14const mem = std.mem;
15
16const assert = std.debug.assert;
17
18const MAXBITS = 15;
19const MAXLCODES = 286;
20const MAXDCODES = 30;
21const MAXCODES = MAXLCODES + MAXDCODES;
22const FIXLCODES = 288;
23
24const Huffman = struct {
25 count: [MAXBITS + 1]u16,
26 symbol: [MAXCODES]u16,
27
28 fn construct(self: *Huffman, length: []const u16) !void {
29 for (self.count) |*val| {
30 val.* = 0;
31 }
32
33 for (length) |val| {
34 self.count[val] += 1;
35 }
36
37 if (self.count[0] == length.len)
38 return;
39
40 var left: isize = 1;
41 for (self.count[1..]) |val| {
42 left *= 2;
43 left -= @as(isize, @bitCast(i16, val));
44 if (left < 0)
45 return error.InvalidTree;
46 }
47
48 var offs: [MAXBITS + 1]u16 = undefined;
49 {
50 var len: usize = 1;
51 offs[1] = 0;
52 while (len < MAXBITS) : (len += 1) {
53 offs[len + 1] = offs[len] + self.count[len];
54 }
55 }
56
57 for (length) |val, symbol| {
58 if (val != 0) {
59 self.symbol[offs[val]] = @truncate(u16, symbol);
60 offs[val] += 1;
61 }
62 }
63 }
64};
65
66pub fn InflateStream(comptime ReaderType: type) type {
67 return struct {
68 const Self = @This();
69
70 pub const Error = ReaderType.Error || error{
71 EndOfStream,
72 BadCounts,
73 InvalidBlockType,
74 InvalidDistance,
75 InvalidFixedCode,
76 InvalidLength,
77 InvalidStoredSize,
78 InvalidSymbol,
79 InvalidTree,
80 MissingEOBCode,
81 NoLastLength,
82 OutOfCodes,
83 };
84 pub const Reader = io.Reader(*Self, Error, read);
85
86 bit_reader: io.BitReader(.Little, ReaderType),
87
88 // True if the decoder met the end of the compressed stream, no further
89 // data can be decompressed
90 seen_eos: bool,
91
92 state: union(enum) {
93 // Parse a compressed block header and set up the internal state for
94 // decompressing its contents.
95 DecodeBlockHeader: void,
96 // Decode all the symbols in a compressed block.
97 DecodeBlockData: void,
98 // Copy N bytes of uncompressed data from the underlying stream into
99 // the window.
100 Copy: usize,
101 // Copy 1 byte into the window.
102 CopyLit: u8,
103 // Copy L bytes from the window itself, starting from D bytes
104 // behind.
105 CopyFrom: struct { distance: u16, length: u16 },
106 },
107
108 // Sliding window for the LZ77 algorithm
109 window: struct {
110 const WSelf = @This();
111
112 // invariant: buffer length is always a power of 2
113 buf: []u8,
114 // invariant: ri <= wi
115 wi: usize = 0, // Write index
116 ri: usize = 0, // Read index
117 el: usize = 0, // Number of readable elements
118
119 fn readable(self: *WSelf) usize {
120 return self.el;
121 }
122
123 fn writable(self: *WSelf) usize {
124 return self.buf.len - self.el;
125 }
126
127 // Insert a single byte into the window.
128 // Returns 1 if there's enough space for the new byte and 0
129 // otherwise.
130 fn append(self: *WSelf, value: u8) usize {
131 if (self.writable() < 1) return 0;
132 self.appendUnsafe(value);
133 return 1;
134 }
135
136 // Insert a single byte into the window.
137 // Assumes there's enough space.
138 fn appendUnsafe(self: *WSelf, value: u8) void {
139 self.buf[self.wi] = value;
140 self.wi = (self.wi + 1) & (self.buf.len - 1);
141 self.el += 1;
142 }
143
144 // Fill dest[] with data from the window, starting from the read
145 // position. This updates the read pointer.
146 // Returns the number of read bytes or 0 if there's nothing to read
147 // yet.
148 fn read(self: *WSelf, dest: []u8) usize {
149 const N = math.min(dest.len, self.readable());
150
151 if (N == 0) return 0;
152
153 if (self.ri + N < self.buf.len) {
154 // The data doesn't wrap around
155 mem.copy(u8, dest, self.buf[self.ri .. self.ri + N]);
156 } else {
157 // The data wraps around the buffer, split the copy
158 std.mem.copy(u8, dest, self.buf[self.ri..]);
159 // How much data we've copied from `ri` to the end
160 const r = self.buf.len - self.ri;
161 std.mem.copy(u8, dest[r..], self.buf[0 .. N - r]);
162 }
163
164 self.ri = (self.ri + N) & (self.buf.len - 1);
165 self.el -= N;
166
167 return N;
168 }
169
170 // Copy `length` bytes starting from `distance` bytes behind the
171 // write pointer.
172 // Be careful as the length may be greater than the distance, that's
173 // how the compressor encodes run-length encoded sequences.
174 fn copyFrom(self: *WSelf, distance: usize, length: usize) usize {
175 const N = math.min(length, self.writable());
176
177 if (N == 0) return 0;
178
179 // TODO: Profile and, if needed, replace with smarter juggling
180 // of the window memory for the non-overlapping case.
181 var i: usize = 0;
182 while (i < N) : (i += 1) {
183 const index = (self.wi -% distance) % self.buf.len;
184 self.appendUnsafe(self.buf[index]);
185 }
186
187 return N;
188 }
189 },
190
191 // Compressor-local Huffman tables used to decompress blocks with
192 // dynamic codes.
193 huffman_tables: [2]Huffman = undefined,
194
195 // Huffman tables used for decoding length/distance pairs.
196 hdist: *Huffman,
197 hlen: *Huffman,
198
199 fn stored(self: *Self) !void {
200 // Discard the remaining bits, the lenght field is always
201 // byte-aligned (and so is the data)
202 self.bit_reader.alignToByte();
203
204 const length = (try self.bit_reader.readBitsNoEof(u16, 16));
205 const length_cpl = (try self.bit_reader.readBitsNoEof(u16, 16));
206
207 if (length != ~length_cpl)
208 return error.InvalidStoredSize;
209
210 self.state = .{ .Copy = length };
211 }
212
213 fn fixed(self: *Self) !void {
214 comptime var lencode: Huffman = undefined;
215 comptime var distcode: Huffman = undefined;
216
217 // The Huffman codes are specified in the RFC1951, section 3.2.6
218 comptime {
219 @setEvalBranchQuota(100000);
220
221 const len_lengths = //
222 [_]u16{8} ** 144 ++
223 [_]u16{9} ** 112 ++
224 [_]u16{7} ** 24 ++
225 [_]u16{8} ** 8;
226 assert(len_lengths.len == FIXLCODES);
227 try lencode.construct(len_lengths[0..]);
228
229 const dist_lengths = [_]u16{5} ** MAXDCODES;
230 try distcode.construct(dist_lengths[0..]);
231 }
232
233 self.hlen = &lencode;
234 self.hdist = &distcode;
235 self.state = .DecodeBlockData;
236 }
237
238 fn dynamic(self: *Self) !void {
239 // Number of length codes
240 const nlen = (try self.bit_reader.readBitsNoEof(usize, 5)) + 257;
241 // Number of distance codes
242 const ndist = (try self.bit_reader.readBitsNoEof(usize, 5)) + 1;
243 // Number of code length codes
244 const ncode = (try self.bit_reader.readBitsNoEof(usize, 4)) + 4;
245
246 if (nlen > MAXLCODES or ndist > MAXDCODES)
247 return error.BadCounts;
248
249 // Permutation of code length codes
250 const ORDER = [19]u16{
251 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4,
252 12, 3, 13, 2, 14, 1, 15,
253 };
254
255 // Build the Huffman table to decode the code length codes
256 var lencode: Huffman = undefined;
257 {
258 var lengths = std.mem.zeroes([19]u16);
259
260 // Read the code lengths, missing ones are left as zero
261 for (ORDER[0..ncode]) |val| {
262 lengths[val] = try self.bit_reader.readBitsNoEof(u16, 3);
263 }
264
265 try lencode.construct(lengths[0..]);
266 }
267
268 // Read the length/literal and distance code length tables.
269 // Zero the table by default so we can avoid explicitly writing out
270 // zeros for codes 17 and 18
271 var lengths = std.mem.zeroes([MAXCODES]u16);
272
273 var i: usize = 0;
274 while (i < nlen + ndist) {
275 const symbol = try self.decode(&lencode);
276
277 switch (symbol) {
278 0...15 => {
279 lengths[i] = symbol;
280 i += 1;
281 },
282 16 => {
283 // repeat last length 3..6 times
284 if (i == 0) return error.NoLastLength;
285
286 const last_length = lengths[i - 1];
287 const repeat = 3 + (try self.bit_reader.readBitsNoEof(usize, 2));
288 const last_index = i + repeat;
289 while (i < last_index) : (i += 1) {
290 lengths[i] = last_length;
291 }
292 },
293 17 => {
294 // repeat zero 3..10 times
295 i += 3 + (try self.bit_reader.readBitsNoEof(usize, 3));
296 },
297 18 => {
298 // repeat zero 11..138 times
299 i += 11 + (try self.bit_reader.readBitsNoEof(usize, 7));
300 },
301 else => return error.InvalidSymbol,
302 }
303 }
304
305 if (i > nlen + ndist)
306 return error.InvalidLength;
307
308 // Check if the end of block code is present
309 if (lengths[256] == 0)
310 return error.MissingEOBCode;
311
312 try self.huffman_tables[0].construct(lengths[0..nlen]);
313 try self.huffman_tables[1].construct(lengths[nlen .. nlen + ndist]);
314
315 self.hlen = &self.huffman_tables[0];
316 self.hdist = &self.huffman_tables[1];
317 self.state = .DecodeBlockData;
318 }
319
320 fn codes(self: *Self, lencode: *Huffman, distcode: *Huffman) !bool {
321 // Size base for length codes 257..285
322 const LENS = [29]u16{
323 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
324 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258,
325 };
326 // Extra bits for length codes 257..285
327 const LEXT = [29]u16{
328 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
329 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0,
330 };
331 // Offset base for distance codes 0..29
332 const DISTS = [30]u16{
333 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
334 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577,
335 };
336 // Extra bits for distance codes 0..29
337 const DEXT = [30]u16{
338 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
339 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
340 };
341
342 while (true) {
343 const symbol = try self.decode(lencode);
344
345 switch (symbol) {
346 0...255 => {
347 // Literal value
348 const c = @truncate(u8, symbol);
349 if (self.window.append(c) == 0) {
350 self.state = .{ .CopyLit = c };
351 return false;
352 }
353 },
354 256 => {
355 // End of block symbol
356 return true;
357 },
358 257...285 => {
359 // Length/distance pair
360 const length_symbol = symbol - 257;
361 const length = LENS[length_symbol] +
362 try self.bit_reader.readBitsNoEof(u16, LEXT[length_symbol]);
363
364 const distance_symbol = try self.decode(distcode);
365 const distance = DISTS[distance_symbol] +
366 try self.bit_reader.readBitsNoEof(u16, DEXT[distance_symbol]);
367
368 if (distance > self.window.buf.len)
369 return error.InvalidDistance;
370
371 const written = self.window.copyFrom(distance, length);
372 if (written != length) {
373 self.state = .{
374 .CopyFrom = .{
375 .distance = distance,
376 .length = length - @truncate(u16, written),
377 },
378 };
379 return false;
380 }
381 },
382 else => return error.InvalidFixedCode,
383 }
384 }
385 }
386
387 fn decode(self: *Self, h: *Huffman) !u16 {
388 var len: usize = 1;
389 var code: usize = 0;
390 var first: usize = 0;
391 var index: usize = 0;
392
393 while (len <= MAXBITS) : (len += 1) {
394 code |= try self.bit_reader.readBitsNoEof(usize, 1);
395 const count = h.count[len];
396 if (code < first + count)
397 return h.symbol[index + (code - first)];
398 index += count;
399 first += count;
400 first <<= 1;
401 code <<= 1;
402 }
403
404 return error.OutOfCodes;
405 }
406
407 fn step(self: *Self) !void {
408 while (true) {
409 switch (self.state) {
410 .DecodeBlockHeader => {
411 // The compressed stream is done
412 if (self.seen_eos) return;
413
414 const last = try self.bit_reader.readBitsNoEof(u1, 1);
415 const kind = try self.bit_reader.readBitsNoEof(u2, 2);
416
417 self.seen_eos = last != 0;
418
419 // The next state depends on the block type
420 switch (kind) {
421 0 => try self.stored(),
422 1 => try self.fixed(),
423 2 => try self.dynamic(),
424 3 => return error.InvalidBlockType,
425 }
426 },
427 .DecodeBlockData => {
428 if (!try self.codes(self.hlen, self.hdist)) {
429 return;
430 }
431
432 self.state = .DecodeBlockHeader;
433 },
434 .Copy => |*length| {
435 const N = math.min(self.window.writable(), length.*);
436
437 // TODO: This loop can be more efficient. On the other
438 // hand uncompressed blocks are not that common so...
439 var i: usize = 0;
440 while (i < N) : (i += 1) {
441 var tmp: [1]u8 = undefined;
442 if ((try self.bit_reader.read(&tmp)) != 1) {
443 // Unexpected end of stream, keep this error
444 // consistent with the use of readBitsNoEof
445 return error.EndOfStream;
446 }
447 self.window.appendUnsafe(tmp[0]);
448 }
449
450 if (N != length.*) {
451 length.* -= N;
452 return;
453 }
454
455 self.state = .DecodeBlockHeader;
456 },
457 .CopyLit => |c| {
458 if (self.window.append(c) == 0) {
459 return;
460 }
461
462 self.state = .DecodeBlockData;
463 },
464 .CopyFrom => |*info| {
465 const written = self.window.copyFrom(info.distance, info.length);
466 if (written != info.length) {
467 info.length -= @truncate(u16, written);
468 return;
469 }
470
471 self.state = .DecodeBlockData;
472 },
473 }
474 }
475 }
476
477 fn init(source: ReaderType, window_slice: []u8) Self {
478 assert(math.isPowerOfTwo(window_slice.len));
479
480 return Self{
481 .bit_reader = io.bitReader(.Little, source),
482 .window = .{ .buf = window_slice },
483 .seen_eos = false,
484 .state = .DecodeBlockHeader,
485 .hdist = undefined,
486 .hlen = undefined,
487 };
488 }
489
490 // Implements the io.Reader interface
491 pub fn read(self: *Self, buffer: []u8) Error!usize {
492 if (buffer.len == 0)
493 return 0;
494
495 // Try reading as much as possible from the window
496 var read_amt: usize = self.window.read(buffer);
497 while (read_amt < buffer.len) {
498 // Run the state machine, we can detect the "effective" end of
499 // stream condition by checking if any progress was made.
500 // Why "effective"? Because even though `seen_eos` is true we
501 // may still have to finish processing other decoding steps.
502 try self.step();
503 // No progress was made
504 if (self.window.readable() == 0)
505 break;
506
507 read_amt += self.window.read(buffer[read_amt..]);
508 }
509
510 return read_amt;
511 }
512
513 pub fn reader(self: *Self) Reader {
514 return .{ .context = self };
515 }
516 };
517}
518
519pub fn inflateStream(reader: anytype, window_slice: []u8) InflateStream(@TypeOf(reader)) {
520 return InflateStream(@TypeOf(reader)).init(reader, window_slice);
521}
lib/std/compress/rfc1951.txt created+955
......@@ -0,0 +1,955 @@
1
2
3
4
5
6
7Network Working Group P. Deutsch
8Request for Comments: 1951 Aladdin Enterprises
9Category: Informational May 1996
10
11
12 DEFLATE Compressed Data Format Specification version 1.3
13
14Status of This Memo
15
16 This memo provides information for the Internet community. This memo
17 does not specify an Internet standard of any kind. Distribution of
18 this memo is unlimited.
19
20IESG Note:
21
22 The IESG takes no position on the validity of any Intellectual
23 Property Rights statements contained in this document.
24
25Notices
26
27 Copyright (c) 1996 L. Peter Deutsch
28
29 Permission is granted to copy and distribute this document for any
30 purpose and without charge, including translations into other
31 languages and incorporation into compilations, provided that the
32 copyright notice and this notice are preserved, and that any
33 substantive changes or deletions from the original are clearly
34 marked.
35
36 A pointer to the latest version of this and related documentation in
37 HTML format can be found at the URL
38 <ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html>.
39
40Abstract
41
42 This specification defines a lossless compressed data format that
43 compresses data using a combination of the LZ77 algorithm and Huffman
44 coding, with efficiency comparable to the best currently available
45 general-purpose compression methods. The data can be produced or
46 consumed, even for an arbitrarily long sequentially presented input
47 data stream, using only an a priori bounded amount of intermediate
48 storage. The format can be implemented readily in a manner not
49 covered by patents.
50
51
52
53
54
55
56
57
58Deutsch Informational [Page 1]
59
60RFC 1951 DEFLATE Compressed Data Format Specification May 1996
61
62
63Table of Contents
64
65 1. Introduction ................................................... 2
66 1.1. Purpose ................................................... 2
67 1.2. Intended audience ......................................... 3
68 1.3. Scope ..................................................... 3
69 1.4. Compliance ................................................ 3
70 1.5. Definitions of terms and conventions used ................ 3
71 1.6. Changes from previous versions ............................ 4
72 2. Compressed representation overview ............................. 4
73 3. Detailed specification ......................................... 5
74 3.1. Overall conventions ....................................... 5
75 3.1.1. Packing into bytes .................................. 5
76 3.2. Compressed block format ................................... 6
77 3.2.1. Synopsis of prefix and Huffman coding ............... 6
78 3.2.2. Use of Huffman coding in the "deflate" format ....... 7
79 3.2.3. Details of block format ............................. 9
80 3.2.4. Non-compressed blocks (BTYPE=00) ................... 11
81 3.2.5. Compressed blocks (length and distance codes) ...... 11
82 3.2.6. Compression with fixed Huffman codes (BTYPE=01) .... 12
83 3.2.7. Compression with dynamic Huffman codes (BTYPE=10) .. 13
84 3.3. Compliance ............................................... 14
85 4. Compression algorithm details ................................. 14
86 5. References .................................................... 16
87 6. Security Considerations ....................................... 16
88 7. Source code ................................................... 16
89 8. Acknowledgements .............................................. 16
90 9. Author's Address .............................................. 17
91
921. Introduction
93
94 1.1. Purpose
95
96 The purpose of this specification is to define a lossless
97 compressed data format that:
98 * Is independent of CPU type, operating system, file system,
99 and character set, and hence can be used for interchange;
100 * Can be produced or consumed, even for an arbitrarily long
101 sequentially presented input data stream, using only an a
102 priori bounded amount of intermediate storage, and hence
103 can be used in data communications or similar structures
104 such as Unix filters;
105 * Compresses data with efficiency comparable to the best
106 currently available general-purpose compression methods,
107 and in particular considerably better than the "compress"
108 program;
109 * Can be implemented readily in a manner not covered by
110 patents, and hence can be practiced freely;
111
112
113
114Deutsch Informational [Page 2]
115
116RFC 1951 DEFLATE Compressed Data Format Specification May 1996
117
118
119 * Is compatible with the file format produced by the current
120 widely used gzip utility, in that conforming decompressors
121 will be able to read data produced by the existing gzip
122 compressor.
123
124 The data format defined by this specification does not attempt to:
125
126 * Allow random access to compressed data;
127 * Compress specialized data (e.g., raster graphics) as well
128 as the best currently available specialized algorithms.
129
130 A simple counting argument shows that no lossless compression
131 algorithm can compress every possible input data set. For the
132 format defined here, the worst case expansion is 5 bytes per 32K-
133 byte block, i.e., a size increase of 0.015% for large data sets.
134 English text usually compresses by a factor of 2.5 to 3;
135 executable files usually compress somewhat less; graphical data
136 such as raster images may compress much more.
137
138 1.2. Intended audience
139
140 This specification is intended for use by implementors of software
141 to compress data into "deflate" format and/or decompress data from
142 "deflate" format.
143
144 The text of the specification assumes a basic background in
145 programming at the level of bits and other primitive data
146 representations. Familiarity with the technique of Huffman coding
147 is helpful but not required.
148
149 1.3. Scope
150
151 The specification specifies a method for representing a sequence
152 of bytes as a (usually shorter) sequence of bits, and a method for
153 packing the latter bit sequence into bytes.
154
155 1.4. Compliance
156
157 Unless otherwise indicated below, a compliant decompressor must be
158 able to accept and decompress any data set that conforms to all
159 the specifications presented here; a compliant compressor must
160 produce data sets that conform to all the specifications presented
161 here.
162
163 1.5. Definitions of terms and conventions used
164
165 Byte: 8 bits stored or transmitted as a unit (same as an octet).
166 For this specification, a byte is exactly 8 bits, even on machines
167
168
169
170Deutsch Informational [Page 3]
171
172RFC 1951 DEFLATE Compressed Data Format Specification May 1996
173
174
175 which store a character on a number of bits different from eight.
176 See below, for the numbering of bits within a byte.
177
178 String: a sequence of arbitrary bytes.
179
180 1.6. Changes from previous versions
181
182 There have been no technical changes to the deflate format since
183 version 1.1 of this specification. In version 1.2, some
184 terminology was changed. Version 1.3 is a conversion of the
185 specification to RFC style.
186
1872. Compressed representation overview
188
189 A compressed data set consists of a series of blocks, corresponding
190 to successive blocks of input data. The block sizes are arbitrary,
191 except that non-compressible blocks are limited to 65,535 bytes.
192
193 Each block is compressed using a combination of the LZ77 algorithm
194 and Huffman coding. The Huffman trees for each block are independent
195 of those for previous or subsequent blocks; the LZ77 algorithm may
196 use a reference to a duplicated string occurring in a previous block,
197 up to 32K input bytes before.
198
199 Each block consists of two parts: a pair of Huffman code trees that
200 describe the representation of the compressed data part, and a
201 compressed data part. (The Huffman trees themselves are compressed
202 using Huffman encoding.) The compressed data consists of a series of
203 elements of two types: literal bytes (of strings that have not been
204 detected as duplicated within the previous 32K input bytes), and
205 pointers to duplicated strings, where a pointer is represented as a
206 pair <length, backward distance>. The representation used in the
207 "deflate" format limits distances to 32K bytes and lengths to 258
208 bytes, but does not limit the size of a block, except for
209 uncompressible blocks, which are limited as noted above.
210
211 Each type of value (literals, distances, and lengths) in the
212 compressed data is represented using a Huffman code, using one code
213 tree for literals and lengths and a separate code tree for distances.
214 The code trees for each block appear in a compact form just before
215 the compressed data for that block.
216
217
218
219
220
221
222
223
224
225
226Deutsch Informational [Page 4]
227
228RFC 1951 DEFLATE Compressed Data Format Specification May 1996
229
230
2313. Detailed specification
232
233 3.1. Overall conventions In the diagrams below, a box like this:
234
235 +---+
236 | | <-- the vertical bars might be missing
237 +---+
238
239 represents one byte; a box like this:
240
241 +==============+
242 | |
243 +==============+
244
245 represents a variable number of bytes.
246
247 Bytes stored within a computer do not have a "bit order", since
248 they are always treated as a unit. However, a byte considered as
249 an integer between 0 and 255 does have a most- and least-
250 significant bit, and since we write numbers with the most-
251 significant digit on the left, we also write bytes with the most-
252 significant bit on the left. In the diagrams below, we number the
253 bits of a byte so that bit 0 is the least-significant bit, i.e.,
254 the bits are numbered:
255
256 +--------+
257 |76543210|
258 +--------+
259
260 Within a computer, a number may occupy multiple bytes. All
261 multi-byte numbers in the format described here are stored with
262 the least-significant byte first (at the lower memory address).
263 For example, the decimal number 520 is stored as:
264
265 0 1
266 +--------+--------+
267 |00001000|00000010|
268 +--------+--------+
269 ^ ^
270 | |
271 | + more significant byte = 2 x 256
272 + less significant byte = 8
273
274 3.1.1. Packing into bytes
275
276 This document does not address the issue of the order in which
277 bits of a byte are transmitted on a bit-sequential medium,
278 since the final data format described here is byte- rather than
279
280
281
282Deutsch Informational [Page 5]
283
284RFC 1951 DEFLATE Compressed Data Format Specification May 1996
285
286
287 bit-oriented. However, we describe the compressed block format
288 in below, as a sequence of data elements of various bit
289 lengths, not a sequence of bytes. We must therefore specify
290 how to pack these data elements into bytes to form the final
291 compressed byte sequence:
292
293 * Data elements are packed into bytes in order of
294 increasing bit number within the byte, i.e., starting
295 with the least-significant bit of the byte.
296 * Data elements other than Huffman codes are packed
297 starting with the least-significant bit of the data
298 element.
299 * Huffman codes are packed starting with the most-
300 significant bit of the code.
301
302 In other words, if one were to print out the compressed data as
303 a sequence of bytes, starting with the first byte at the
304 *right* margin and proceeding to the *left*, with the most-
305 significant bit of each byte on the left as usual, one would be
306 able to parse the result from right to left, with fixed-width
307 elements in the correct MSB-to-LSB order and Huffman codes in
308 bit-reversed order (i.e., with the first bit of the code in the
309 relative LSB position).
310
311 3.2. Compressed block format
312
313 3.2.1. Synopsis of prefix and Huffman coding
314
315 Prefix coding represents symbols from an a priori known
316 alphabet by bit sequences (codes), one code for each symbol, in
317 a manner such that different symbols may be represented by bit
318 sequences of different lengths, but a parser can always parse
319 an encoded string unambiguously symbol-by-symbol.
320
321 We define a prefix code in terms of a binary tree in which the
322 two edges descending from each non-leaf node are labeled 0 and
323 1 and in which the leaf nodes correspond one-for-one with (are
324 labeled with) the symbols of the alphabet; then the code for a
325 symbol is the sequence of 0's and 1's on the edges leading from
326 the root to the leaf labeled with that symbol. For example:
327
328
329
330
331
332
333
334
335
336
337
338Deutsch Informational [Page 6]
339
340RFC 1951 DEFLATE Compressed Data Format Specification May 1996
341
342
343 /\ Symbol Code
344 0 1 ------ ----
345 / \ A 00
346 /\ B B 1
347 0 1 C 011
348 / \ D 010
349 A /\
350 0 1
351 / \
352 D C
353
354 A parser can decode the next symbol from an encoded input
355 stream by walking down the tree from the root, at each step
356 choosing the edge corresponding to the next input bit.
357
358 Given an alphabet with known symbol frequencies, the Huffman
359 algorithm allows the construction of an optimal prefix code
360 (one which represents strings with those symbol frequencies
361 using the fewest bits of any possible prefix codes for that
362 alphabet). Such a code is called a Huffman code. (See
363 reference [1] in Chapter 5, references for additional
364 information on Huffman codes.)
365
366 Note that in the "deflate" format, the Huffman codes for the
367 various alphabets must not exceed certain maximum code lengths.
368 This constraint complicates the algorithm for computing code
369 lengths from symbol frequencies. Again, see Chapter 5,
370 references for details.
371
372 3.2.2. Use of Huffman coding in the "deflate" format
373
374 The Huffman codes used for each alphabet in the "deflate"
375 format have two additional rules:
376
377 * All codes of a given bit length have lexicographically
378 consecutive values, in the same order as the symbols
379 they represent;
380
381 * Shorter codes lexicographically precede longer codes.
382
383
384
385
386
387
388
389
390
391
392
393
394Deutsch Informational [Page 7]
395
396RFC 1951 DEFLATE Compressed Data Format Specification May 1996
397
398
399 We could recode the example above to follow this rule as
400 follows, assuming that the order of the alphabet is ABCD:
401
402 Symbol Code
403 ------ ----
404 A 10
405 B 0
406 C 110
407 D 111
408
409 I.e., 0 precedes 10 which precedes 11x, and 110 and 111 are
410 lexicographically consecutive.
411
412 Given this rule, we can define the Huffman code for an alphabet
413 just by giving the bit lengths of the codes for each symbol of
414 the alphabet in order; this is sufficient to determine the
415 actual codes. In our example, the code is completely defined
416 by the sequence of bit lengths (2, 1, 3, 3). The following
417 algorithm generates the codes as integers, intended to be read
418 from most- to least-significant bit. The code lengths are
419 initially in tree[I].Len; the codes are produced in
420 tree[I].Code.
421
422 1) Count the number of codes for each code length. Let
423 bl_count[N] be the number of codes of length N, N >= 1.
424
425 2) Find the numerical value of the smallest code for each
426 code length:
427
428 code = 0;
429 bl_count[0] = 0;
430 for (bits = 1; bits <= MAX_BITS; bits++) {
431 code = (code + bl_count[bits-1]) << 1;
432 next_code[bits] = code;
433 }
434
435 3) Assign numerical values to all codes, using consecutive
436 values for all codes of the same length with the base
437 values determined at step 2. Codes that are never used
438 (which have a bit length of zero) must not be assigned a
439 value.
440
441 for (n = 0; n <= max_code; n++) {
442 len = tree[n].Len;
443 if (len != 0) {
444 tree[n].Code = next_code[len];
445 next_code[len]++;
446 }
447
448
449
450Deutsch Informational [Page 8]
451
452RFC 1951 DEFLATE Compressed Data Format Specification May 1996
453
454
455 }
456
457 Example:
458
459 Consider the alphabet ABCDEFGH, with bit lengths (3, 3, 3, 3,
460 3, 2, 4, 4). After step 1, we have:
461
462 N bl_count[N]
463 - -----------
464 2 1
465 3 5
466 4 2
467
468 Step 2 computes the following next_code values:
469
470 N next_code[N]
471 - ------------
472 1 0
473 2 0
474 3 2
475 4 14
476
477 Step 3 produces the following code values:
478
479 Symbol Length Code
480 ------ ------ ----
481 A 3 010
482 B 3 011
483 C 3 100
484 D 3 101
485 E 3 110
486 F 2 00
487 G 4 1110
488 H 4 1111
489
490 3.2.3. Details of block format
491
492 Each block of compressed data begins with 3 header bits
493 containing the following data:
494
495 first bit BFINAL
496 next 2 bits BTYPE
497
498 Note that the header bits do not necessarily begin on a byte
499 boundary, since a block does not necessarily occupy an integral
500 number of bytes.
501
502
503
504
505
506Deutsch Informational [Page 9]
507
508RFC 1951 DEFLATE Compressed Data Format Specification May 1996
509
510
511 BFINAL is set if and only if this is the last block of the data
512 set.
513
514 BTYPE specifies how the data are compressed, as follows:
515
516 00 - no compression
517 01 - compressed with fixed Huffman codes
518 10 - compressed with dynamic Huffman codes
519 11 - reserved (error)
520
521 The only difference between the two compressed cases is how the
522 Huffman codes for the literal/length and distance alphabets are
523 defined.
524
525 In all cases, the decoding algorithm for the actual data is as
526 follows:
527
528 do
529 read block header from input stream.
530 if stored with no compression
531 skip any remaining bits in current partially
532 processed byte
533 read LEN and NLEN (see next section)
534 copy LEN bytes of data to output
535 otherwise
536 if compressed with dynamic Huffman codes
537 read representation of code trees (see
538 subsection below)
539 loop (until end of block code recognized)
540 decode literal/length value from input stream
541 if value < 256
542 copy value (literal byte) to output stream
543 otherwise
544 if value = end of block (256)
545 break from loop
546 otherwise (value = 257..285)
547 decode distance from input stream
548
549 move backwards distance bytes in the output
550 stream, and copy length bytes from this
551 position to the output stream.
552 end loop
553 while not last block
554
555 Note that a duplicated string reference may refer to a string
556 in a previous block; i.e., the backward distance may cross one
557 or more block boundaries. However a distance cannot refer past
558 the beginning of the output stream. (An application using a
559
560
561
562Deutsch Informational [Page 10]
563
564RFC 1951 DEFLATE Compressed Data Format Specification May 1996
565
566
567 preset dictionary might discard part of the output stream; a
568 distance can refer to that part of the output stream anyway)
569 Note also that the referenced string may overlap the current
570 position; for example, if the last 2 bytes decoded have values
571 X and Y, a string reference with <length = 5, distance = 2>
572 adds X,Y,X,Y,X to the output stream.
573
574 We now specify each compression method in turn.
575
576 3.2.4. Non-compressed blocks (BTYPE=00)
577
578 Any bits of input up to the next byte boundary are ignored.
579 The rest of the block consists of the following information:
580
581 0 1 2 3 4...
582 +---+---+---+---+================================+
583 | LEN | NLEN |... LEN bytes of literal data...|
584 +---+---+---+---+================================+
585
586 LEN is the number of data bytes in the block. NLEN is the
587 one's complement of LEN.
588
589 3.2.5. Compressed blocks (length and distance codes)
590
591 As noted above, encoded data blocks in the "deflate" format
592 consist of sequences of symbols drawn from three conceptually
593 distinct alphabets: either literal bytes, from the alphabet of
594 byte values (0..255), or <length, backward distance> pairs,
595 where the length is drawn from (3..258) and the distance is
596 drawn from (1..32,768). In fact, the literal and length
597 alphabets are merged into a single alphabet (0..285), where
598 values 0..255 represent literal bytes, the value 256 indicates
599 end-of-block, and values 257..285 represent length codes
600 (possibly in conjunction with extra bits following the symbol
601 code) as follows:
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618Deutsch Informational [Page 11]
619
620RFC 1951 DEFLATE Compressed Data Format Specification May 1996
621
622
623 Extra Extra Extra
624 Code Bits Length(s) Code Bits Lengths Code Bits Length(s)
625 ---- ---- ------ ---- ---- ------- ---- ---- -------
626 257 0 3 267 1 15,16 277 4 67-82
627 258 0 4 268 1 17,18 278 4 83-98
628 259 0 5 269 2 19-22 279 4 99-114
629 260 0 6 270 2 23-26 280 4 115-130
630 261 0 7 271 2 27-30 281 5 131-162
631 262 0 8 272 2 31-34 282 5 163-194
632 263 0 9 273 3 35-42 283 5 195-226
633 264 0 10 274 3 43-50 284 5 227-257
634 265 1 11,12 275 3 51-58 285 0 258
635 266 1 13,14 276 3 59-66
636
637 The extra bits should be interpreted as a machine integer
638 stored with the most-significant bit first, e.g., bits 1110
639 represent the value 14.
640
641 Extra Extra Extra
642 Code Bits Dist Code Bits Dist Code Bits Distance
643 ---- ---- ---- ---- ---- ------ ---- ---- --------
644 0 0 1 10 4 33-48 20 9 1025-1536
645 1 0 2 11 4 49-64 21 9 1537-2048
646 2 0 3 12 5 65-96 22 10 2049-3072
647 3 0 4 13 5 97-128 23 10 3073-4096
648 4 1 5,6 14 6 129-192 24 11 4097-6144
649 5 1 7,8 15 6 193-256 25 11 6145-8192
650 6 2 9-12 16 7 257-384 26 12 8193-12288
651 7 2 13-16 17 7 385-512 27 12 12289-16384
652 8 3 17-24 18 8 513-768 28 13 16385-24576
653 9 3 25-32 19 8 769-1024 29 13 24577-32768
654
655 3.2.6. Compression with fixed Huffman codes (BTYPE=01)
656
657 The Huffman codes for the two alphabets are fixed, and are not
658 represented explicitly in the data. The Huffman code lengths
659 for the literal/length alphabet are:
660
661 Lit Value Bits Codes
662 --------- ---- -----
663 0 - 143 8 00110000 through
664 10111111
665 144 - 255 9 110010000 through
666 111111111
667 256 - 279 7 0000000 through
668 0010111
669 280 - 287 8 11000000 through
670 11000111
671
672
673
674Deutsch Informational [Page 12]
675
676RFC 1951 DEFLATE Compressed Data Format Specification May 1996
677
678
679 The code lengths are sufficient to generate the actual codes,
680 as described above; we show the codes in the table for added
681 clarity. Literal/length values 286-287 will never actually
682 occur in the compressed data, but participate in the code
683 construction.
684
685 Distance codes 0-31 are represented by (fixed-length) 5-bit
686 codes, with possible additional bits as shown in the table
687 shown in Paragraph 3.2.5, above. Note that distance codes 30-
688 31 will never actually occur in the compressed data.
689
690 3.2.7. Compression with dynamic Huffman codes (BTYPE=10)
691
692 The Huffman codes for the two alphabets appear in the block
693 immediately after the header bits and before the actual
694 compressed data, first the literal/length code and then the
695 distance code. Each code is defined by a sequence of code
696 lengths, as discussed in Paragraph 3.2.2, above. For even
697 greater compactness, the code length sequences themselves are
698 compressed using a Huffman code. The alphabet for code lengths
699 is as follows:
700
701 0 - 15: Represent code lengths of 0 - 15
702 16: Copy the previous code length 3 - 6 times.
703 The next 2 bits indicate repeat length
704 (0 = 3, ... , 3 = 6)
705 Example: Codes 8, 16 (+2 bits 11),
706 16 (+2 bits 10) will expand to
707 12 code lengths of 8 (1 + 6 + 5)
708 17: Repeat a code length of 0 for 3 - 10 times.
709 (3 bits of length)
710 18: Repeat a code length of 0 for 11 - 138 times
711 (7 bits of length)
712
713 A code length of 0 indicates that the corresponding symbol in
714 the literal/length or distance alphabet will not occur in the
715 block, and should not participate in the Huffman code
716 construction algorithm given earlier. If only one distance
717 code is used, it is encoded using one bit, not zero bits; in
718 this case there is a single code length of one, with one unused
719 code. One distance code of zero bits means that there are no
720 distance codes used at all (the data is all literals).
721
722 We can now define the format of the block:
723
724 5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286)
725 5 Bits: HDIST, # of Distance codes - 1 (1 - 32)
726 4 Bits: HCLEN, # of Code Length codes - 4 (4 - 19)
727
728
729
730Deutsch Informational [Page 13]
731
732RFC 1951 DEFLATE Compressed Data Format Specification May 1996
733
734
735 (HCLEN + 4) x 3 bits: code lengths for the code length
736 alphabet given just above, in the order: 16, 17, 18,
737 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
738
739 These code lengths are interpreted as 3-bit integers
740 (0-7); as above, a code length of 0 means the
741 corresponding symbol (literal/length or distance code
742 length) is not used.
743
744 HLIT + 257 code lengths for the literal/length alphabet,
745 encoded using the code length Huffman code
746
747 HDIST + 1 code lengths for the distance alphabet,
748 encoded using the code length Huffman code
749
750 The actual compressed data of the block,
751 encoded using the literal/length and distance Huffman
752 codes
753
754 The literal/length symbol 256 (end of data),
755 encoded using the literal/length Huffman code
756
757 The code length repeat codes can cross from HLIT + 257 to the
758 HDIST + 1 code lengths. In other words, all code lengths form
759 a single sequence of HLIT + HDIST + 258 values.
760
761 3.3. Compliance
762
763 A compressor may limit further the ranges of values specified in
764 the previous section and still be compliant; for example, it may
765 limit the range of backward pointers to some value smaller than
766 32K. Similarly, a compressor may limit the size of blocks so that
767 a compressible block fits in memory.
768
769 A compliant decompressor must accept the full range of possible
770 values defined in the previous section, and must accept blocks of
771 arbitrary size.
772
7734. Compression algorithm details
774
775 While it is the intent of this document to define the "deflate"
776 compressed data format without reference to any particular
777 compression algorithm, the format is related to the compressed
778 formats produced by LZ77 (Lempel-Ziv 1977, see reference [2] below);
779 since many variations of LZ77 are patented, it is strongly
780 recommended that the implementor of a compressor follow the general
781 algorithm presented here, which is known not to be patented per se.
782 The material in this section is not part of the definition of the
783
784
785
786Deutsch Informational [Page 14]
787
788RFC 1951 DEFLATE Compressed Data Format Specification May 1996
789
790
791 specification per se, and a compressor need not follow it in order to
792 be compliant.
793
794 The compressor terminates a block when it determines that starting a
795 new block with fresh trees would be useful, or when the block size
796 fills up the compressor's block buffer.
797
798 The compressor uses a chained hash table to find duplicated strings,
799 using a hash function that operates on 3-byte sequences. At any
800 given point during compression, let XYZ be the next 3 input bytes to
801 be examined (not necessarily all different, of course). First, the
802 compressor examines the hash chain for XYZ. If the chain is empty,
803 the compressor simply writes out X as a literal byte and advances one
804 byte in the input. If the hash chain is not empty, indicating that
805 the sequence XYZ (or, if we are unlucky, some other 3 bytes with the
806 same hash function value) has occurred recently, the compressor
807 compares all strings on the XYZ hash chain with the actual input data
808 sequence starting at the current point, and selects the longest
809 match.
810
811 The compressor searches the hash chains starting with the most recent
812 strings, to favor small distances and thus take advantage of the
813 Huffman encoding. The hash chains are singly linked. There are no
814 deletions from the hash chains; the algorithm simply discards matches
815 that are too old. To avoid a worst-case situation, very long hash
816 chains are arbitrarily truncated at a certain length, determined by a
817 run-time parameter.
818
819 To improve overall compression, the compressor optionally defers the
820 selection of matches ("lazy matching"): after a match of length N has
821 been found, the compressor searches for a longer match starting at
822 the next input byte. If it finds a longer match, it truncates the
823 previous match to a length of one (thus producing a single literal
824 byte) and then emits the longer match. Otherwise, it emits the
825 original match, and, as described above, advances N bytes before
826 continuing.
827
828 Run-time parameters also control this "lazy match" procedure. If
829 compression ratio is most important, the compressor attempts a
830 complete second search regardless of the length of the first match.
831 In the normal case, if the current match is "long enough", the
832 compressor reduces the search for a longer match, thus speeding up
833 the process. If speed is most important, the compressor inserts new
834 strings in the hash table only when no match was found, or when the
835 match is not "too long". This degrades the compression ratio but
836 saves time since there are both fewer insertions and fewer searches.
837
838
839
840
841
842Deutsch Informational [Page 15]
843
844RFC 1951 DEFLATE Compressed Data Format Specification May 1996
845
846
8475. References
848
849 [1] Huffman, D. A., "A Method for the Construction of Minimum
850 Redundancy Codes", Proceedings of the Institute of Radio
851 Engineers, September 1952, Volume 40, Number 9, pp. 1098-1101.
852
853 [2] Ziv J., Lempel A., "A Universal Algorithm for Sequential Data
854 Compression", IEEE Transactions on Information Theory, Vol. 23,
855 No. 3, pp. 337-343.
856
857 [3] Gailly, J.-L., and Adler, M., ZLIB documentation and sources,
858 available in ftp://ftp.uu.net/pub/archiving/zip/doc/
859
860 [4] Gailly, J.-L., and Adler, M., GZIP documentation and sources,
861 available as gzip-*.tar in ftp://prep.ai.mit.edu/pub/gnu/
862
863 [5] Schwartz, E. S., and Kallick, B. "Generating a canonical prefix
864 encoding." Comm. ACM, 7,3 (Mar. 1964), pp. 166-169.
865
866 [6] Hirschberg and Lelewer, "Efficient decoding of prefix codes,"
867 Comm. ACM, 33,4, April 1990, pp. 449-459.
868
8696. Security Considerations
870
871 Any data compression method involves the reduction of redundancy in
872 the data. Consequently, any corruption of the data is likely to have
873 severe effects and be difficult to correct. Uncompressed text, on
874 the other hand, will probably still be readable despite the presence
875 of some corrupted bytes.
876
877 It is recommended that systems using this data format provide some
878 means of validating the integrity of the compressed data. See
879 reference [3], for example.
880
8817. Source code
882
883 Source code for a C language implementation of a "deflate" compliant
884 compressor and decompressor is available within the zlib package at
885 ftp://ftp.uu.net/pub/archiving/zip/zlib/.
886
8878. Acknowledgements
888
889 Trademarks cited in this document are the property of their
890 respective owners.
891
892 Phil Katz designed the deflate format. Jean-Loup Gailly and Mark
893 Adler wrote the related software described in this specification.
894 Glenn Randers-Pehrson converted this document to RFC and HTML format.
895
896
897
898Deutsch Informational [Page 16]
899
900RFC 1951 DEFLATE Compressed Data Format Specification May 1996
901
902
9039. Author's Address
904
905 L. Peter Deutsch
906 Aladdin Enterprises
907 203 Santa Margarita Ave.
908 Menlo Park, CA 94025
909
910 Phone: (415) 322-0103 (AM only)
911 FAX: (415) 322-1734
912 EMail: <ghost@aladdin.com>
913
914 Questions about the technical content of this specification can be
915 sent by email to:
916
917 Jean-Loup Gailly <gzip@prep.ai.mit.edu> and
918 Mark Adler <madler@alumni.caltech.edu>
919
920 Editorial comments on this specification can be sent by email to:
921
922 L. Peter Deutsch <ghost@aladdin.com> and
923 Glenn Randers-Pehrson <randeg@alumni.rpi.edu>
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954Deutsch Informational [Page 17]
955
lib/std/compress/rfc1951.txt.fixed.z.9 created
Binary files /dev/null and b/lib/std/compress/rfc1951.txt.fixed.z.9 differ
lib/std/compress/rfc1951.txt.z.0 created
Binary files /dev/null and b/lib/std/compress/rfc1951.txt.z.0 differ
lib/std/compress/rfc1951.txt.z.9 created
Binary files /dev/null and b/lib/std/compress/rfc1951.txt.z.9 differ
lib/std/compress/zlib.zig created+178
......@@ -0,0 +1,178 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 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// Decompressor for ZLIB data streams (RFC1950)
8
9const std = @import("std");
10const io = std.io;
11const fs = std.fs;
12const testing = std.testing;
13const mem = std.mem;
14const deflate = std.compress.deflate;
15
16pub fn ZlibStream(comptime ReaderType: type) type {
17 return struct {
18 const Self = @This();
19
20 pub const Error = ReaderType.Error ||
21 deflate.InflateStream(ReaderType).Error ||
22 error{ WrongChecksum, Unsupported };
23 pub const Reader = io.Reader(*Self, Error, read);
24
25 allocator: *mem.Allocator,
26 inflater: deflate.InflateStream(ReaderType),
27 in_reader: ReaderType,
28 hasher: std.hash.Adler32,
29 window_slice: []u8,
30
31 fn init(allocator: *mem.Allocator, source: ReaderType) !Self {
32 // Zlib header format is specified in RFC1950
33 const header = try source.readBytesNoEof(2);
34
35 const CM = @truncate(u4, header[0]);
36 const CINFO = @truncate(u4, header[0] >> 4);
37 const FCHECK = @truncate(u5, header[1]);
38 const FDICT = @truncate(u1, header[1] >> 5);
39
40 if ((@as(u16, header[0]) << 8 | header[1]) % 31 != 0)
41 return error.BadHeader;
42
43 // The CM field must be 8 to indicate the use of DEFLATE
44 if (CM != 8) return error.InvalidCompression;
45 // CINFO is the base-2 logarithm of the window size, minus 8.
46 // Values above 7 are unspecified and therefore rejected.
47 if (CINFO > 7) return error.InvalidWindowSize;
48 const window_size: u16 = @as(u16, 1) << (CINFO + 8);
49
50 // TODO: Support this case
51 if (FDICT != 0)
52 return error.Unsupported;
53
54 var window_slice = try allocator.alloc(u8, window_size);
55
56 return Self{
57 .allocator = allocator,
58 .inflater = deflate.inflateStream(source, window_slice),
59 .in_reader = source,
60 .hasher = std.hash.Adler32.init(),
61 .window_slice = window_slice,
62 };
63 }
64
65 fn deinit(self: *Self) void {
66 self.allocator.free(self.window_slice);
67 }
68
69 // Implements the io.Reader interface
70 pub fn read(self: *Self, buffer: []u8) Error!usize {
71 if (buffer.len == 0)
72 return 0;
73
74 // Read from the compressed stream and update the computed checksum
75 const r = try self.inflater.read(buffer);
76 if (r != 0) {
77 self.hasher.update(buffer[0..r]);
78 return r;
79 }
80
81 // We've reached the end of stream, check if the checksum matches
82 const hash = try self.in_reader.readIntBig(u32);
83 if (hash != self.hasher.final())
84 return error.WrongChecksum;
85
86 return 0;
87 }
88
89 pub fn reader(self: *Self) Reader {
90 return .{ .context = self };
91 }
92 };
93}
94
95pub fn zlibStream(allocator: *mem.Allocator, reader: anytype) !ZlibStream(@TypeOf(reader)) {
96 return ZlibStream(@TypeOf(reader)).init(allocator, reader);
97}
98
99fn testReader(data: []const u8, comptime expected: []const u8) !void {
100 var in_stream = io.fixedBufferStream(data);
101
102 var zlib_stream = try zlibStream(testing.allocator, in_stream.reader());
103 defer zlib_stream.deinit();
104
105 // Read and decompress the whole file
106 const buf = try zlib_stream.reader().readAllAlloc(testing.allocator, std.math.maxInt(usize));
107 defer testing.allocator.free(buf);
108 // Calculate its SHA256 hash and check it against the reference
109 var hash: [32]u8 = undefined;
110 std.crypto.hash.sha2.Sha256.hash(buf, hash[0..], .{});
111
112 assertEqual(expected, &hash);
113}
114
115// Assert `expected` == `input` where `input` is a bytestring.
116pub fn assertEqual(comptime expected: []const u8, input: []const u8) void {
117 var expected_bytes: [expected.len / 2]u8 = undefined;
118 for (expected_bytes) |*r, i| {
119 r.* = std.fmt.parseInt(u8, expected[2 * i .. 2 * i + 2], 16) catch unreachable;
120 }
121
122 testing.expectEqualSlices(u8, &expected_bytes, input);
123}
124
125// All the test cases are obtained by compressing the RFC1950 text
126//
127// https://tools.ietf.org/rfc/rfc1950.txt length=36944 bytes
128// SHA256=5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009
129test "compressed data" {
130 // Compressed with compression level = 0
131 try testReader(
132 @embedFile("rfc1951.txt.z.0"),
133 "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009",
134 );
135 // Compressed with compression level = 9
136 try testReader(
137 @embedFile("rfc1951.txt.z.9"),
138 "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009",
139 );
140 // Compressed with compression level = 9 and fixed Huffman codes
141 try testReader(
142 @embedFile("rfc1951.txt.fixed.z.9"),
143 "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009",
144 );
145}
146
147test "sanity checks" {
148 // Truncated header
149 testing.expectError(
150 error.EndOfStream,
151 testReader(&[_]u8{0x78}, ""),
152 );
153 // Failed FCHECK check
154 testing.expectError(
155 error.BadHeader,
156 testReader(&[_]u8{ 0x78, 0x9D }, ""),
157 );
158 // Wrong CM
159 testing.expectError(
160 error.InvalidCompression,
161 testReader(&[_]u8{ 0x79, 0x94 }, ""),
162 );
163 // Wrong CINFO
164 testing.expectError(
165 error.InvalidWindowSize,
166 testReader(&[_]u8{ 0x88, 0x98 }, ""),
167 );
168 // Wrong checksum
169 testing.expectError(
170 error.WrongChecksum,
171 testReader(&[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, ""),
172 );
173 // Truncated checksum
174 testing.expectError(
175 error.EndOfStream,
176 testReader(&[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00 }, ""),
177 );
178}
lib/std/debug/leb128.zig+24-23
......@@ -9,10 +9,10 @@ const testing = std.testing;
99/// Read a single unsigned LEB128 value from the given reader as type T,
1010/// or error.Overflow if the value cannot fit.
1111pub fn readULEB128(comptime T: type, reader: anytype) !T {
12 const U = if (T.bit_count < 8) u8 else T;
12 const U = if (@typeInfo(T).Int.bits < 8) u8 else T;
1313 const ShiftT = std.math.Log2Int(U);
1414
15 const max_group = (U.bit_count + 6) / 7;
15 const max_group = (@typeInfo(U).Int.bits + 6) / 7;
1616
1717 var value = @as(U, 0);
1818 var group = @as(ShiftT, 0);
......@@ -40,7 +40,7 @@ pub fn readULEB128(comptime T: type, reader: anytype) !T {
4040/// Write a single unsigned integer as unsigned LEB128 to the given writer.
4141pub fn writeULEB128(writer: anytype, uint_value: anytype) !void {
4242 const T = @TypeOf(uint_value);
43 const U = if (T.bit_count < 8) u8 else T;
43 const U = if (@typeInfo(T).Int.bits < 8) u8 else T;
4444 var value = @intCast(U, uint_value);
4545
4646 while (true) {
......@@ -68,7 +68,7 @@ pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T {
6868/// returning the number of bytes written.
6969pub fn writeULEB128Mem(ptr: []u8, uint_value: anytype) !usize {
7070 const T = @TypeOf(uint_value);
71 const max_group = (T.bit_count + 6) / 7;
71 const max_group = (@typeInfo(T).Int.bits + 6) / 7;
7272 var buf = std.io.fixedBufferStream(ptr);
7373 try writeULEB128(buf.writer(), uint_value);
7474 return buf.pos;
......@@ -77,11 +77,11 @@ pub fn writeULEB128Mem(ptr: []u8, uint_value: anytype) !usize {
7777/// Read a single signed LEB128 value from the given reader as type T,
7878/// or error.Overflow if the value cannot fit.
7979pub fn readILEB128(comptime T: type, reader: anytype) !T {
80 const S = if (T.bit_count < 8) i8 else T;
81 const U = std.meta.Int(false, S.bit_count);
80 const S = if (@typeInfo(T).Int.bits < 8) i8 else T;
81 const U = std.meta.Int(false, @typeInfo(S).Int.bits);
8282 const ShiftU = std.math.Log2Int(U);
8383
84 const max_group = (U.bit_count + 6) / 7;
84 const max_group = (@typeInfo(U).Int.bits + 6) / 7;
8585
8686 var value = @as(U, 0);
8787 var group = @as(ShiftU, 0);
......@@ -97,7 +97,7 @@ pub fn readILEB128(comptime T: type, reader: anytype) !T {
9797 if (@bitCast(S, temp) >= 0) return error.Overflow;
9898
9999 // and all the overflowed bits are 1
100 const remaining_shift = @intCast(u3, U.bit_count - @as(u16, shift));
100 const remaining_shift = @intCast(u3, @typeInfo(U).Int.bits - @as(u16, shift));
101101 const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift;
102102 if (remaining_bits != -1) return error.Overflow;
103103 }
......@@ -127,8 +127,8 @@ pub fn readILEB128(comptime T: type, reader: anytype) !T {
127127/// Write a single signed integer as signed LEB128 to the given writer.
128128pub fn writeILEB128(writer: anytype, int_value: anytype) !void {
129129 const T = @TypeOf(int_value);
130 const S = if (T.bit_count < 8) i8 else T;
131 const U = std.meta.Int(false, S.bit_count);
130 const S = if (@typeInfo(T).Int.bits < 8) i8 else T;
131 const U = std.meta.Int(false, @typeInfo(S).Int.bits);
132132
133133 var value = @intCast(S, int_value);
134134
......@@ -173,7 +173,7 @@ pub fn writeILEB128Mem(ptr: []u8, int_value: anytype) !usize {
173173/// different value without shifting all the following code.
174174pub fn writeUnsignedFixed(comptime l: usize, ptr: *[l]u8, int: std.meta.Int(false, l * 7)) void {
175175 const T = @TypeOf(int);
176 const U = if (T.bit_count < 8) u8 else T;
176 const U = if (@typeInfo(T).Int.bits < 8) u8 else T;
177177 var value = @intCast(U, int);
178178
179179 comptime var i = 0;
......@@ -346,28 +346,29 @@ test "deserialize unsigned LEB128" {
346346
347347fn test_write_leb128(value: anytype) !void {
348348 const T = @TypeOf(value);
349 const t_signed = @typeInfo(T).Int.is_signed;
349350
350 const writeStream = if (T.is_signed) writeILEB128 else writeULEB128;
351 const writeMem = if (T.is_signed) writeILEB128Mem else writeULEB128Mem;
352 const readStream = if (T.is_signed) readILEB128 else readULEB128;
353 const readMem = if (T.is_signed) readILEB128Mem else readULEB128Mem;
351 const writeStream = if (t_signed) writeILEB128 else writeULEB128;
352 const writeMem = if (t_signed) writeILEB128Mem else writeULEB128Mem;
353 const readStream = if (t_signed) readILEB128 else readULEB128;
354 const readMem = if (t_signed) readILEB128Mem else readULEB128Mem;
354355
355356 // decode to a larger bit size too, to ensure sign extension
356357 // is working as expected
357 const larger_type_bits = ((T.bit_count + 8) / 8) * 8;
358 const B = std.meta.Int(T.is_signed, larger_type_bits);
358 const larger_type_bits = ((@typeInfo(T).Int.bits + 8) / 8) * 8;
359 const B = std.meta.Int(t_signed, larger_type_bits);
359360
360361 const bytes_needed = bn: {
361 const S = std.meta.Int(T.is_signed, @sizeOf(T) * 8);
362 if (T.bit_count <= 7) break :bn @as(u16, 1);
362 const S = std.meta.Int(t_signed, @sizeOf(T) * 8);
363 if (@typeInfo(T).Int.bits <= 7) break :bn @as(u16, 1);
363364
364365 const unused_bits = if (value < 0) @clz(T, ~value) else @clz(T, value);
365 const used_bits: u16 = (T.bit_count - unused_bits) + @boolToInt(T.is_signed);
366 const used_bits: u16 = (@typeInfo(T).Int.bits - unused_bits) + @boolToInt(t_signed);
366367 if (used_bits <= 7) break :bn @as(u16, 1);
367368 break :bn ((used_bits + 6) / 7);
368369 };
369370
370 const max_groups = if (T.bit_count == 0) 1 else (T.bit_count + 6) / 7;
371 const max_groups = if (@typeInfo(T).Int.bits == 0) 1 else (@typeInfo(T).Int.bits + 6) / 7;
371372
372373 var buf: [max_groups]u8 = undefined;
373374 var fbs = std.io.fixedBufferStream(&buf);
......@@ -414,7 +415,7 @@ test "serialize unsigned LEB128" {
414415 const T = std.meta.Int(false, t);
415416 const min = std.math.minInt(T);
416417 const max = std.math.maxInt(T);
417 var i = @as(std.meta.Int(false, T.bit_count + 1), min);
418 var i = @as(std.meta.Int(false, @typeInfo(T).Int.bits + 1), min);
418419
419420 while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i));
420421 }
......@@ -432,7 +433,7 @@ test "serialize signed LEB128" {
432433 const T = std.meta.Int(true, t);
433434 const min = std.math.minInt(T);
434435 const max = std.math.maxInt(T);
435 var i = @as(std.meta.Int(true, T.bit_count + 1), min);
436 var i = @as(std.meta.Int(true, @typeInfo(T).Int.bits + 1), min);
436437
437438 while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i));
438439 }
lib/std/fmt.zig+30-10
......@@ -66,6 +66,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
6666/// - output numeric value in hexadecimal notation
6767/// - `s`: print a pointer-to-many as a c-string, use zero-termination
6868/// - `B` and `Bi`: output a memory size in either metric (1000) or power-of-two (1024) based notation. works for both float and integer values.
69/// - `e` and `E`: if printing a string, escape non-printable characters
6970/// - `e`: output floating point value in scientific notation
7071/// - `d`: output numeric value in decimal notation
7172/// - `b`: output integer value in binary notation
......@@ -81,6 +82,8 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
8182/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type.
8283///
8384/// A user type may be a `struct`, `vector`, `union` or `enum` type.
85///
86/// To print literal curly braces, escape them by writing them twice, e.g. `{{` or `}}`.
8487pub fn format(
8588 writer: anytype,
8689 comptime fmt: []const u8,
......@@ -90,7 +93,7 @@ pub fn format(
9093 if (@typeInfo(@TypeOf(args)) != .Struct) {
9194 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
9295 }
93 if (args.len > ArgSetType.bit_count) {
96 if (args.len > @typeInfo(ArgSetType).Int.bits) {
9497 @compileError("32 arguments max are supported per format call");
9598 }
9699
......@@ -324,7 +327,7 @@ pub fn formatType(
324327 max_depth: usize,
325328) @TypeOf(writer).Error!void {
326329 if (comptime std.mem.eql(u8, fmt, "*")) {
327 try writer.writeAll(@typeName(@TypeOf(value).Child));
330 try writer.writeAll(@typeName(@typeInfo(@TypeOf(value)).Pointer.child));
328331 try writer.writeAll("@");
329332 try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
330333 return;
......@@ -429,12 +432,12 @@ pub fn formatType(
429432 if (info.child == u8) {
430433 return formatText(value, fmt, options, writer);
431434 }
432 return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
435 return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) });
433436 },
434437 .Enum, .Union, .Struct => {
435438 return formatType(value.*, fmt, options, writer, max_depth);
436439 },
437 else => return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }),
440 else => return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) }),
438441 },
439442 .Many, .C => {
440443 if (ptr_info.sentinel) |sentinel| {
......@@ -445,7 +448,7 @@ pub fn formatType(
445448 return formatText(mem.span(value), fmt, options, writer);
446449 }
447450 }
448 return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
451 return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) });
449452 },
450453 .Slice => {
451454 if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {
......@@ -535,7 +538,7 @@ pub fn formatIntValue(
535538 radix = 10;
536539 uppercase = false;
537540 } else if (comptime std.mem.eql(u8, fmt, "c")) {
538 if (@TypeOf(int_value).bit_count <= 8) {
541 if (@typeInfo(@TypeOf(int_value)).Int.bits <= 8) {
539542 return formatAsciiChar(@as(u8, int_value), options, writer);
540543 } else {
541544 @compileError("Cannot print integer that is larger than 8 bits as a ascii");
......@@ -599,6 +602,16 @@ pub fn formatText(
599602 try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, writer);
600603 }
601604 return;
605 } else if (comptime (std.mem.eql(u8, fmt, "e") or std.mem.eql(u8, fmt, "E"))) {
606 for (bytes) |c| {
607 if (std.ascii.isPrint(c)) {
608 try writer.writeByte(c);
609 } else {
610 try writer.writeAll("\\x");
611 try formatInt(c, 16, fmt[0] == 'E', FormatOptions{ .width = 2, .fill = '0' }, writer);
612 }
613 }
614 return;
602615 } else {
603616 @compileError("Unknown format string: '" ++ fmt ++ "'");
604617 }
......@@ -934,7 +947,7 @@ pub fn formatInt(
934947 } else
935948 value;
936949
937 if (@TypeOf(int_value).is_signed) {
950 if (@typeInfo(@TypeOf(int_value)).Int.is_signed) {
938951 return formatIntSigned(int_value, base, uppercase, options, writer);
939952 } else {
940953 return formatIntUnsigned(int_value, base, uppercase, options, writer);
......@@ -976,9 +989,10 @@ fn formatIntUnsigned(
976989 writer: anytype,
977990) !void {
978991 assert(base >= 2);
979 var buf: [math.max(@TypeOf(value).bit_count, 1)]u8 = undefined;
980 const min_int_bits = comptime math.max(@TypeOf(value).bit_count, @TypeOf(base).bit_count);
981 const MinInt = std.meta.Int(@TypeOf(value).is_signed, min_int_bits);
992 const value_info = @typeInfo(@TypeOf(value)).Int;
993 var buf: [math.max(value_info.bits, 1)]u8 = undefined;
994 const min_int_bits = comptime math.max(value_info.bits, @typeInfo(@TypeOf(base)).Int.bits);
995 const MinInt = std.meta.Int(value_info.is_signed, min_int_bits);
982996 var a: MinInt = value;
983997 var index: usize = buf.len;
984998
......@@ -1319,6 +1333,12 @@ test "slice" {
13191333 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});
13201334}
13211335
1336test "escape non-printable" {
1337 try testFmt("abc", "{e}", .{"abc"});
1338 try testFmt("ab\\xffc", "{e}", .{"ab\xffc"});
1339 try testFmt("ab\\xFFc", "{E}", .{"ab\xffc"});
1340}
1341
13221342test "pointer" {
13231343 {
13241344 const value = @intToPtr(*align(1) i32, 0xdeadbeef);
lib/std/fmt/parse_float.zig+5-2
......@@ -37,7 +37,9 @@
3737const std = @import("../std.zig");
3838const ascii = std.ascii;
3939
40const max_digits = 25;
40// The mantissa field in FloatRepr is 64bit wide and holds only 19 digits
41// without overflowing
42const max_digits = 19;
4143
4244const f64_plus_zero: u64 = 0x0000000000000000;
4345const f64_minus_zero: u64 = 0x8000000000000000;
......@@ -372,7 +374,7 @@ test "fmt.parseFloat" {
372374 const epsilon = 1e-7;
373375
374376 inline for ([_]type{ f16, f32, f64, f128 }) |T| {
375 const Z = std.meta.Int(false, T.bit_count);
377 const Z = std.meta.Int(false, @typeInfo(T).Float.bits);
376378
377379 testing.expectError(error.InvalidCharacter, parseFloat(T, ""));
378380 testing.expectError(error.InvalidCharacter, parseFloat(T, " 1"));
......@@ -409,6 +411,7 @@ test "fmt.parseFloat" {
409411 expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon));
410412 expect(approxEq(T, try parseFloat(T, "-123142.1124"), @as(T, -123142.1124), epsilon));
411413 expect(approxEq(T, try parseFloat(T, "0.7062146892655368"), @as(T, 0.7062146892655368), epsilon));
414 expect(approxEq(T, try parseFloat(T, "2.71828182845904523536"), @as(T, 2.718281828459045), epsilon));
412415 }
413416 }
414417}
lib/std/fs.zig+9-3
......@@ -1437,26 +1437,32 @@ pub const Dir = struct {
14371437 /// On success, caller owns returned buffer.
14381438 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
14391439 pub fn readFileAlloc(self: Dir, allocator: *mem.Allocator, file_path: []const u8, max_bytes: usize) ![]u8 {
1440 return self.readFileAllocOptions(allocator, file_path, max_bytes, @alignOf(u8), null);
1440 return self.readFileAllocOptions(allocator, file_path, max_bytes, null, @alignOf(u8), null);
14411441 }
14421442
14431443 /// On success, caller owns returned buffer.
14441444 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
1445 /// If `size_hint` is specified the initial buffer size is calculated using
1446 /// that value, otherwise the effective file size is used instead.
14451447 /// Allows specifying alignment and a sentinel value.
14461448 pub fn readFileAllocOptions(
14471449 self: Dir,
14481450 allocator: *mem.Allocator,
14491451 file_path: []const u8,
14501452 max_bytes: usize,
1453 size_hint: ?usize,
14511454 comptime alignment: u29,
14521455 comptime optional_sentinel: ?u8,
14531456 ) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {
14541457 var file = try self.openFile(file_path, .{});
14551458 defer file.close();
14561459
1457 const stat_size = try file.getEndPos();
1460 // If the file size doesn't fit a usize it'll be certainly greater than
1461 // `max_bytes`
1462 const stat_size = size_hint orelse math.cast(usize, try file.getEndPos()) catch
1463 return error.FileTooBig;
14581464
1459 return file.readAllAllocOptions(allocator, stat_size, max_bytes, alignment, optional_sentinel);
1465 return file.readToEndAllocOptions(allocator, max_bytes, stat_size, alignment, optional_sentinel);
14601466 }
14611467
14621468 pub const DeleteTreeError = error{
lib/std/fs/file.zig+29-11
......@@ -363,31 +363,49 @@ pub const File = struct {
363363 try os.futimens(self.handle, &times);
364364 }
365365
366 /// Reads all the bytes from the current position to the end of the file.
366367 /// On success, caller owns returned buffer.
367368 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
368 pub fn readAllAlloc(self: File, allocator: *mem.Allocator, stat_size: u64, max_bytes: usize) ![]u8 {
369 return self.readAllAllocOptions(allocator, stat_size, max_bytes, @alignOf(u8), null);
369 pub fn readToEndAlloc(self: File, allocator: *mem.Allocator, max_bytes: usize) ![]u8 {
370 return self.readToEndAllocOptions(allocator, max_bytes, null, @alignOf(u8), null);
370371 }
371372
373 /// Reads all the bytes from the current position to the end of the file.
372374 /// On success, caller owns returned buffer.
373375 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
376 /// If `size_hint` is specified the initial buffer size is calculated using
377 /// that value, otherwise an arbitrary value is used instead.
374378 /// Allows specifying alignment and a sentinel value.
375 pub fn readAllAllocOptions(
379 pub fn readToEndAllocOptions(
376380 self: File,
377381 allocator: *mem.Allocator,
378 stat_size: u64,
379382 max_bytes: usize,
383 size_hint: ?usize,
380384 comptime alignment: u29,
381385 comptime optional_sentinel: ?u8,
382386 ) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {
383 const size = math.cast(usize, stat_size) catch math.maxInt(usize);
384 if (size > max_bytes) return error.FileTooBig;
385
386 const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);
387 errdefer allocator.free(buf);
387 // If no size hint is provided fall back to the size=0 code path
388 const size = size_hint orelse 0;
389
390 // The file size returned by stat is used as hint to set the buffer
391 // size. If the reported size is zero, as it happens on Linux for files
392 // in /proc, a small buffer is allocated instead.
393 const initial_cap = (if (size > 0) size else 1024) + @boolToInt(optional_sentinel != null);
394 var array_list = try std.ArrayListAligned(u8, alignment).initCapacity(allocator, initial_cap);
395 defer array_list.deinit();
396
397 self.reader().readAllArrayList(&array_list, max_bytes) catch |err| switch (err) {
398 error.StreamTooLong => return error.FileTooBig,
399 else => |e| return e,
400 };
388401
389 try self.reader().readNoEof(buf);
390 return buf;
402 if (optional_sentinel) |sentinel| {
403 try array_list.append(sentinel);
404 const buf = array_list.toOwnedSlice();
405 return buf[0 .. buf.len - 1 :sentinel];
406 } else {
407 return array_list.toOwnedSlice();
408 }
391409 }
392410
393411 pub const ReadError = os.ReadError;
lib/std/fs/test.zig+5-5
......@@ -188,30 +188,30 @@ test "readAllAlloc" {
188188 var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });
189189 defer file.close();
190190
191 const buf1 = try file.readAllAlloc(testing.allocator, 0, 1024);
191 const buf1 = try file.readToEndAlloc(testing.allocator, 1024);
192192 defer testing.allocator.free(buf1);
193193 testing.expect(buf1.len == 0);
194194
195195 const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";
196196 try file.writeAll(write_buf);
197197 try file.seekTo(0);
198 const file_size = try file.getEndPos();
199198
200199 // max_bytes > file_size
201 const buf2 = try file.readAllAlloc(testing.allocator, file_size, 1024);
200 const buf2 = try file.readToEndAlloc(testing.allocator, 1024);
202201 defer testing.allocator.free(buf2);
203202 testing.expectEqual(write_buf.len, buf2.len);
204203 testing.expect(std.mem.eql(u8, write_buf, buf2));
205204 try file.seekTo(0);
206205
207206 // max_bytes == file_size
208 const buf3 = try file.readAllAlloc(testing.allocator, file_size, write_buf.len);
207 const buf3 = try file.readToEndAlloc(testing.allocator, write_buf.len);
209208 defer testing.allocator.free(buf3);
210209 testing.expectEqual(write_buf.len, buf3.len);
211210 testing.expect(std.mem.eql(u8, write_buf, buf3));
211 try file.seekTo(0);
212212
213213 // max_bytes < file_size
214 testing.expectError(error.FileTooBig, file.readAllAlloc(testing.allocator, file_size, write_buf.len - 1));
214 testing.expectError(error.FileTooBig, file.readToEndAlloc(testing.allocator, write_buf.len - 1));
215215}
216216
217217test "directory operations on files" {
lib/std/hash/auto_hash.zig+1-1
......@@ -113,7 +113,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
113113 .Array => hashArray(hasher, key, strat),
114114
115115 .Vector => |info| {
116 if (info.child.bit_count % 8 == 0) {
116 if (std.meta.bitCount(info.child) % 8 == 0) {
117117 // If there's no unused bits in the child type, we can just hash
118118 // this as an array of bytes.
119119 hasher.update(mem.asBytes(&key));
lib/std/heap.zig+5-1
......@@ -915,6 +915,10 @@ pub fn testAllocator(base_allocator: *mem.Allocator) !void {
915915 testing.expect(slice.len == 10);
916916
917917 allocator.free(slice);
918
919 const zero_bit_ptr = try allocator.create(u0);
920 zero_bit_ptr.* = 0;
921 allocator.destroy(zero_bit_ptr);
918922}
919923
920924pub fn testAllocatorAligned(base_allocator: *mem.Allocator, comptime alignment: u29) !void {
......@@ -952,7 +956,7 @@ pub fn testAllocatorLargeAlignment(base_allocator: *mem.Allocator) mem.Allocator
952956 // very near usize?
953957 if (mem.page_size << 2 > maxInt(usize)) return;
954958
955 const USizeShift = std.meta.Int(false, std.math.log2(usize.bit_count));
959 const USizeShift = std.meta.Int(false, std.math.log2(std.meta.bitCount(usize)));
956960 const large_align = @as(u29, mem.page_size << 2);
957961
958962 var align_mask: usize = undefined;
lib/std/io.zig+9
......@@ -169,6 +169,15 @@ pub const BitOutStream = BitWriter;
169169/// Deprecated: use `bitWriter`
170170pub const bitOutStream = bitWriter;
171171
172pub const AutoIndentingStream = @import("io/auto_indenting_stream.zig").AutoIndentingStream;
173pub const autoIndentingStream = @import("io/auto_indenting_stream.zig").autoIndentingStream;
174
175pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream;
176pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream;
177
178pub const FindByteOutStream = @import("io/find_byte_out_stream.zig").FindByteOutStream;
179pub const findByteOutStream = @import("io/find_byte_out_stream.zig").findByteOutStream;
180
172181pub const Packing = @import("io/serialization.zig").Packing;
173182
174183pub const Serializer = @import("io/serialization.zig").Serializer;
lib/std/io/auto_indenting_stream.zig created+148
......@@ -0,0 +1,148 @@
1const std = @import("../std.zig");
2const io = std.io;
3const mem = std.mem;
4const assert = std.debug.assert;
5
6/// Automatically inserts indentation of written data by keeping
7/// track of the current indentation level
8pub fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
9 return struct {
10 const Self = @This();
11 pub const Error = UnderlyingWriter.Error;
12 pub const Writer = io.Writer(*Self, Error, write);
13
14 underlying_writer: UnderlyingWriter,
15
16 indent_count: usize = 0,
17 indent_delta: usize,
18 current_line_empty: bool = true,
19 indent_one_shot_count: usize = 0, // automatically popped when applied
20 applied_indent: usize = 0, // the most recently applied indent
21 indent_next_line: usize = 0, // not used until the next line
22
23 pub fn writer(self: *Self) Writer {
24 return .{ .context = self };
25 }
26
27 pub fn write(self: *Self, bytes: []const u8) Error!usize {
28 if (bytes.len == 0)
29 return @as(usize, 0);
30
31 try self.applyIndent();
32 return self.writeNoIndent(bytes);
33 }
34
35 // Change the indent delta without changing the final indentation level
36 pub fn setIndentDelta(self: *Self, indent_delta: usize) void {
37 if (self.indent_delta == indent_delta) {
38 return;
39 } else if (self.indent_delta > indent_delta) {
40 assert(self.indent_delta % indent_delta == 0);
41 self.indent_count = self.indent_count * (self.indent_delta / indent_delta);
42 } else {
43 // assert that the current indentation (in spaces) in a multiple of the new delta
44 assert((self.indent_count * self.indent_delta) % indent_delta == 0);
45 self.indent_count = self.indent_count / (indent_delta / self.indent_delta);
46 }
47 self.indent_delta = indent_delta;
48 }
49
50 fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
51 if (bytes.len == 0)
52 return @as(usize, 0);
53
54 try self.underlying_writer.writeAll(bytes);
55 if (bytes[bytes.len - 1] == '\n')
56 self.resetLine();
57 return bytes.len;
58 }
59
60 pub fn insertNewline(self: *Self) Error!void {
61 _ = try self.writeNoIndent("\n");
62 }
63
64 fn resetLine(self: *Self) void {
65 self.current_line_empty = true;
66 self.indent_next_line = 0;
67 }
68
69 /// Insert a newline unless the current line is blank
70 pub fn maybeInsertNewline(self: *Self) Error!void {
71 if (!self.current_line_empty)
72 try self.insertNewline();
73 }
74
75 /// Push default indentation
76 pub fn pushIndent(self: *Self) void {
77 // Doesn't actually write any indentation.
78 // Just primes the stream to be able to write the correct indentation if it needs to.
79 self.indent_count += 1;
80 }
81
82 /// Push an indent that is automatically popped after being applied
83 pub fn pushIndentOneShot(self: *Self) void {
84 self.indent_one_shot_count += 1;
85 self.pushIndent();
86 }
87
88 /// Turns all one-shot indents into regular indents
89 /// Returns number of indents that must now be manually popped
90 pub fn lockOneShotIndent(self: *Self) usize {
91 var locked_count = self.indent_one_shot_count;
92 self.indent_one_shot_count = 0;
93 return locked_count;
94 }
95
96 /// Push an indent that should not take effect until the next line
97 pub fn pushIndentNextLine(self: *Self) void {
98 self.indent_next_line += 1;
99 self.pushIndent();
100 }
101
102 pub fn popIndent(self: *Self) void {
103 assert(self.indent_count != 0);
104 self.indent_count -= 1;
105
106 if (self.indent_next_line > 0)
107 self.indent_next_line -= 1;
108 }
109
110 /// Writes ' ' bytes if the current line is empty
111 fn applyIndent(self: *Self) Error!void {
112 const current_indent = self.currentIndent();
113 if (self.current_line_empty and current_indent > 0) {
114 try self.underlying_writer.writeByteNTimes(' ', current_indent);
115 self.applied_indent = current_indent;
116 }
117
118 self.indent_count -= self.indent_one_shot_count;
119 self.indent_one_shot_count = 0;
120 self.current_line_empty = false;
121 }
122
123 /// Checks to see if the most recent indentation exceeds the currently pushed indents
124 pub fn isLineOverIndented(self: *Self) bool {
125 if (self.current_line_empty) return false;
126 return self.applied_indent > self.currentIndent();
127 }
128
129 fn currentIndent(self: *Self) usize {
130 var indent_current: usize = 0;
131 if (self.indent_count > 0) {
132 const indent_count = self.indent_count - self.indent_next_line;
133 indent_current = indent_count * self.indent_delta;
134 }
135 return indent_current;
136 }
137 };
138}
139
140pub fn autoIndentingStream(
141 indent_delta: usize,
142 underlying_writer: anytype,
143) AutoIndentingStream(@TypeOf(underlying_writer)) {
144 return AutoIndentingStream(@TypeOf(underlying_writer)){
145 .underlying_writer = underlying_writer,
146 .indent_delta = indent_delta,
147 };
148}
lib/std/io/change_detection_stream.zig created+55
......@@ -0,0 +1,55 @@
1const std = @import("../std.zig");
2const io = std.io;
3const mem = std.mem;
4const assert = std.debug.assert;
5
6/// Used to detect if the data written to a stream differs from a source buffer
7pub fn ChangeDetectionStream(comptime WriterType: type) type {
8 return struct {
9 const Self = @This();
10 pub const Error = WriterType.Error;
11 pub const Writer = io.Writer(*Self, Error, write);
12
13 anything_changed: bool,
14 underlying_writer: WriterType,
15 source_index: usize,
16 source: []const u8,
17
18 pub fn writer(self: *Self) Writer {
19 return .{ .context = self };
20 }
21
22 fn write(self: *Self, bytes: []const u8) Error!usize {
23 if (!self.anything_changed) {
24 const end = self.source_index + bytes.len;
25 if (end > self.source.len) {
26 self.anything_changed = true;
27 } else {
28 const src_slice = self.source[self.source_index..end];
29 self.source_index += bytes.len;
30 if (!mem.eql(u8, bytes, src_slice)) {
31 self.anything_changed = true;
32 }
33 }
34 }
35
36 return self.underlying_writer.write(bytes);
37 }
38
39 pub fn changeDetected(self: *Self) bool {
40 return self.anything_changed or (self.source_index != self.source.len);
41 }
42 };
43}
44
45pub fn changeDetectionStream(
46 source: []const u8,
47 underlying_writer: anytype,
48) ChangeDetectionStream(@TypeOf(underlying_writer)) {
49 return ChangeDetectionStream(@TypeOf(underlying_writer)){
50 .anything_changed = false,
51 .underlying_writer = underlying_writer,
52 .source_index = 0,
53 .source = source,
54 };
55}
lib/std/io/find_byte_out_stream.zig created+40
......@@ -0,0 +1,40 @@
1const std = @import("../std.zig");
2const io = std.io;
3const assert = std.debug.assert;
4
5/// An OutStream that returns whether the given character has been written to it.
6/// The contents are not written to anything.
7pub fn FindByteOutStream(comptime UnderlyingWriter: type) type {
8 return struct {
9 const Self = @This();
10 pub const Error = UnderlyingWriter.Error;
11 pub const Writer = io.Writer(*Self, Error, write);
12
13 underlying_writer: UnderlyingWriter,
14 byte_found: bool,
15 byte: u8,
16
17 pub fn writer(self: *Self) Writer {
18 return .{ .context = self };
19 }
20
21 fn write(self: *Self, bytes: []const u8) Error!usize {
22 if (!self.byte_found) {
23 self.byte_found = blk: {
24 for (bytes) |b|
25 if (b == self.byte) break :blk true;
26 break :blk false;
27 };
28 }
29 return self.underlying_writer.write(bytes);
30 }
31 };
32}
33
34pub fn findByteOutStream(byte: u8, underlying_writer: anytype) FindByteOutStream(@TypeOf(underlying_writer)) {
35 return FindByteOutStream(@TypeOf(underlying_writer)){
36 .underlying_writer = underlying_writer,
37 .byte = byte,
38 .byte_found = false,
39 };
40}
lib/std/io/reader.zig+5-5
......@@ -198,28 +198,28 @@ pub fn Reader(
198198
199199 /// Reads a native-endian integer
200200 pub fn readIntNative(self: Self, comptime T: type) !T {
201 const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8);
201 const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8);
202202 return mem.readIntNative(T, &bytes);
203203 }
204204
205205 /// Reads a foreign-endian integer
206206 pub fn readIntForeign(self: Self, comptime T: type) !T {
207 const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8);
207 const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8);
208208 return mem.readIntForeign(T, &bytes);
209209 }
210210
211211 pub fn readIntLittle(self: Self, comptime T: type) !T {
212 const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8);
212 const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8);
213213 return mem.readIntLittle(T, &bytes);
214214 }
215215
216216 pub fn readIntBig(self: Self, comptime T: type) !T {
217 const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8);
217 const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8);
218218 return mem.readIntBig(T, &bytes);
219219 }
220220
221221 pub fn readInt(self: Self, comptime T: type, endian: builtin.Endian) !T {
222 const bytes = try self.readBytesNoEof((T.bit_count + 7) / 8);
222 const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8);
223223 return mem.readInt(T, &bytes, endian);
224224 }
225225
lib/std/io/serialization.zig+3-3
......@@ -60,7 +60,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
6060
6161 const U = std.meta.Int(false, t_bit_count);
6262 const Log2U = math.Log2Int(U);
63 const int_size = (U.bit_count + 7) / 8;
63 const int_size = (t_bit_count + 7) / 8;
6464
6565 if (packing == .Bit) {
6666 const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
......@@ -73,7 +73,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
7373
7474 if (int_size == 1) {
7575 if (t_bit_count == 8) return @bitCast(T, buffer[0]);
76 const PossiblySignedByte = std.meta.Int(T.is_signed, 8);
76 const PossiblySignedByte = std.meta.Int(@typeInfo(T).Int.is_signed, 8);
7777 return @truncate(T, @bitCast(PossiblySignedByte, buffer[0]));
7878 }
7979
......@@ -247,7 +247,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
247247
248248 const U = std.meta.Int(false, t_bit_count);
249249 const Log2U = math.Log2Int(U);
250 const int_size = (U.bit_count + 7) / 8;
250 const int_size = (t_bit_count + 7) / 8;
251251
252252 const u_value = @bitCast(U, value);
253253
lib/std/io/writer.zig+5-5
......@@ -53,7 +53,7 @@ pub fn Writer(
5353 /// Write a native-endian integer.
5454 /// TODO audit non-power-of-two int sizes
5555 pub fn writeIntNative(self: Self, comptime T: type, value: T) Error!void {
56 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
56 var bytes: [(@typeInfo(T).Int.bits + 7) / 8]u8 = undefined;
5757 mem.writeIntNative(T, &bytes, value);
5858 return self.writeAll(&bytes);
5959 }
......@@ -61,28 +61,28 @@ pub fn Writer(
6161 /// Write a foreign-endian integer.
6262 /// TODO audit non-power-of-two int sizes
6363 pub fn writeIntForeign(self: Self, comptime T: type, value: T) Error!void {
64 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
64 var bytes: [(@typeInfo(T).Int.bits + 7) / 8]u8 = undefined;
6565 mem.writeIntForeign(T, &bytes, value);
6666 return self.writeAll(&bytes);
6767 }
6868
6969 /// TODO audit non-power-of-two int sizes
7070 pub fn writeIntLittle(self: Self, comptime T: type, value: T) Error!void {
71 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
71 var bytes: [(@typeInfo(T).Int.bits + 7) / 8]u8 = undefined;
7272 mem.writeIntLittle(T, &bytes, value);
7373 return self.writeAll(&bytes);
7474 }
7575
7676 /// TODO audit non-power-of-two int sizes
7777 pub fn writeIntBig(self: Self, comptime T: type, value: T) Error!void {
78 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
78 var bytes: [(@typeInfo(T).Int.bits + 7) / 8]u8 = undefined;
7979 mem.writeIntBig(T, &bytes, value);
8080 return self.writeAll(&bytes);
8181 }
8282
8383 /// TODO audit non-power-of-two int sizes
8484 pub fn writeInt(self: Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {
85 var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
85 var bytes: [(@typeInfo(T).Int.bits + 7) / 8]u8 = undefined;
8686 mem.writeInt(T, &bytes, value, endian);
8787 return self.writeAll(&bytes);
8888 }
lib/std/log.zig+4
......@@ -127,6 +127,10 @@ fn log(
127127 if (@enumToInt(message_level) <= @enumToInt(level)) {
128128 if (@hasDecl(root, "log")) {
129129 root.log(message_level, scope, format, args);
130 } else if (std.Target.current.os.tag == .freestanding) {
131 // On freestanding one must provide a log function; we do not have
132 // any I/O configured.
133 return;
130134 } else if (builtin.mode != .ReleaseSmall) {
131135 const held = std.debug.getStderrMutex().acquire();
132136 defer held.release();
lib/std/math.zig+32-31
......@@ -195,7 +195,7 @@ test "" {
195195pub fn floatMantissaBits(comptime T: type) comptime_int {
196196 assert(@typeInfo(T) == .Float);
197197
198 return switch (T.bit_count) {
198 return switch (@typeInfo(T).Float.bits) {
199199 16 => 10,
200200 32 => 23,
201201 64 => 52,
......@@ -208,7 +208,7 @@ pub fn floatMantissaBits(comptime T: type) comptime_int {
208208pub fn floatExponentBits(comptime T: type) comptime_int {
209209 assert(@typeInfo(T) == .Float);
210210
211 return switch (T.bit_count) {
211 return switch (@typeInfo(T).Float.bits) {
212212 16 => 5,
213213 32 => 8,
214214 64 => 11,
......@@ -347,9 +347,9 @@ pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {
347347/// A negative shift amount results in a right shift.
348348pub fn shl(comptime T: type, a: T, shift_amt: anytype) T {
349349 const abs_shift_amt = absCast(shift_amt);
350 const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt);
350 const casted_shift_amt = if (abs_shift_amt >= @typeInfo(T).Int.bits) return 0 else @intCast(Log2Int(T), abs_shift_amt);
351351
352 if (@TypeOf(shift_amt) == comptime_int or @TypeOf(shift_amt).is_signed) {
352 if (@TypeOf(shift_amt) == comptime_int or @typeInfo(@TypeOf(shift_amt)).Int.is_signed) {
353353 if (shift_amt < 0) {
354354 return a >> casted_shift_amt;
355355 }
......@@ -373,9 +373,9 @@ test "math.shl" {
373373/// A negative shift amount results in a left shift.
374374pub fn shr(comptime T: type, a: T, shift_amt: anytype) T {
375375 const abs_shift_amt = absCast(shift_amt);
376 const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt);
376 const casted_shift_amt = if (abs_shift_amt >= @typeInfo(T).Int.bits) return 0 else @intCast(Log2Int(T), abs_shift_amt);
377377
378 if (@TypeOf(shift_amt) == comptime_int or @TypeOf(shift_amt).is_signed) {
378 if (@TypeOf(shift_amt) == comptime_int or @typeInfo(@TypeOf(shift_amt)).Int.is_signed) {
379379 if (shift_amt >= 0) {
380380 return a >> casted_shift_amt;
381381 } else {
......@@ -400,11 +400,11 @@ test "math.shr" {
400400/// Rotates right. Only unsigned values can be rotated.
401401/// Negative shift values results in shift modulo the bit count.
402402pub fn rotr(comptime T: type, x: T, r: anytype) T {
403 if (T.is_signed) {
403 if (@typeInfo(T).Int.is_signed) {
404404 @compileError("cannot rotate signed integer");
405405 } else {
406 const ar = @mod(r, T.bit_count);
407 return shr(T, x, ar) | shl(T, x, T.bit_count - ar);
406 const ar = @mod(r, @typeInfo(T).Int.bits);
407 return shr(T, x, ar) | shl(T, x, @typeInfo(T).Int.bits - ar);
408408 }
409409}
410410
......@@ -419,11 +419,11 @@ test "math.rotr" {
419419/// Rotates left. Only unsigned values can be rotated.
420420/// Negative shift values results in shift modulo the bit count.
421421pub fn rotl(comptime T: type, x: T, r: anytype) T {
422 if (T.is_signed) {
422 if (@typeInfo(T).Int.is_signed) {
423423 @compileError("cannot rotate signed integer");
424424 } else {
425 const ar = @mod(r, T.bit_count);
426 return shl(T, x, ar) | shr(T, x, T.bit_count - ar);
425 const ar = @mod(r, @typeInfo(T).Int.bits);
426 return shl(T, x, ar) | shr(T, x, @typeInfo(T).Int.bits - ar);
427427 }
428428}
429429
......@@ -438,7 +438,7 @@ test "math.rotl" {
438438pub fn Log2Int(comptime T: type) type {
439439 // comptime ceil log2
440440 comptime var count = 0;
441 comptime var s = T.bit_count - 1;
441 comptime var s = @typeInfo(T).Int.bits - 1;
442442 inline while (s != 0) : (s >>= 1) {
443443 count += 1;
444444 }
......@@ -524,7 +524,7 @@ fn testOverflow() void {
524524pub fn absInt(x: anytype) !@TypeOf(x) {
525525 const T = @TypeOf(x);
526526 comptime assert(@typeInfo(T) == .Int); // must pass an integer to absInt
527 comptime assert(T.is_signed); // must pass a signed integer to absInt
527 comptime assert(@typeInfo(T).Int.is_signed); // must pass a signed integer to absInt
528528
529529 if (x == minInt(@TypeOf(x))) {
530530 return error.Overflow;
......@@ -557,7 +557,7 @@ fn testAbsFloat() void {
557557pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T {
558558 @setRuntimeSafety(false);
559559 if (denominator == 0) return error.DivisionByZero;
560 if (@typeInfo(T) == .Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow;
560 if (@typeInfo(T) == .Int and @typeInfo(T).Int.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow;
561561 return @divTrunc(numerator, denominator);
562562}
563563
......@@ -578,7 +578,7 @@ fn testDivTrunc() void {
578578pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T {
579579 @setRuntimeSafety(false);
580580 if (denominator == 0) return error.DivisionByZero;
581 if (@typeInfo(T) == .Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow;
581 if (@typeInfo(T) == .Int and @typeInfo(T).Int.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow;
582582 return @divFloor(numerator, denominator);
583583}
584584
......@@ -652,7 +652,7 @@ fn testDivCeil() void {
652652pub fn divExact(comptime T: type, numerator: T, denominator: T) !T {
653653 @setRuntimeSafety(false);
654654 if (denominator == 0) return error.DivisionByZero;
655 if (@typeInfo(T) == .Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow;
655 if (@typeInfo(T) == .Int and @typeInfo(T).Int.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow;
656656 const result = @divTrunc(numerator, denominator);
657657 if (result * denominator != numerator) return error.UnexpectedRemainder;
658658 return result;
......@@ -757,10 +757,10 @@ test "math.absCast" {
757757
758758/// Returns the negation of the integer parameter.
759759/// Result is a signed integer.
760pub fn negateCast(x: anytype) !std.meta.Int(true, @TypeOf(x).bit_count) {
761 if (@TypeOf(x).is_signed) return negate(x);
760pub fn negateCast(x: anytype) !std.meta.Int(true, std.meta.bitCount(@TypeOf(x))) {
761 if (@typeInfo(@TypeOf(x)).Int.is_signed) return negate(x);
762762
763 const int = std.meta.Int(true, @TypeOf(x).bit_count);
763 const int = std.meta.Int(true, std.meta.bitCount(@TypeOf(x)));
764764 if (x > -minInt(int)) return error.Overflow;
765765
766766 if (x == -minInt(int)) return minInt(int);
......@@ -823,7 +823,7 @@ pub fn floorPowerOfTwo(comptime T: type, value: T) T {
823823 var x = value;
824824
825825 comptime var i = 1;
826 inline while (T.bit_count > i) : (i *= 2) {
826 inline while (@typeInfo(T).Int.bits > i) : (i *= 2) {
827827 x |= (x >> i);
828828 }
829829
......@@ -847,13 +847,13 @@ fn testFloorPowerOfTwo() void {
847847/// Returns the next power of two (if the value is not already a power of two).
848848/// Only unsigned integers can be used. Zero is not an allowed input.
849849/// Result is a type with 1 more bit than the input type.
850pub fn ceilPowerOfTwoPromote(comptime T: type, value: T) std.meta.Int(T.is_signed, T.bit_count + 1) {
850pub fn ceilPowerOfTwoPromote(comptime T: type, value: T) std.meta.Int(@typeInfo(T).Int.is_signed, @typeInfo(T).Int.bits + 1) {
851851 comptime assert(@typeInfo(T) == .Int);
852 comptime assert(!T.is_signed);
852 comptime assert(!@typeInfo(T).Int.is_signed);
853853 assert(value != 0);
854 comptime const PromotedType = std.meta.Int(T.is_signed, T.bit_count + 1);
854 comptime const PromotedType = std.meta.Int(@typeInfo(T).Int.is_signed, @typeInfo(T).Int.bits + 1);
855855 comptime const shiftType = std.math.Log2Int(PromotedType);
856 return @as(PromotedType, 1) << @intCast(shiftType, T.bit_count - @clz(T, value - 1));
856 return @as(PromotedType, 1) << @intCast(shiftType, @typeInfo(T).Int.bits - @clz(T, value - 1));
857857}
858858
859859/// Returns the next power of two (if the value is not already a power of two).
......@@ -861,9 +861,10 @@ pub fn ceilPowerOfTwoPromote(comptime T: type, value: T) std.meta.Int(T.is_signe
861861/// If the value doesn't fit, returns an error.
862862pub fn ceilPowerOfTwo(comptime T: type, value: T) (error{Overflow}!T) {
863863 comptime assert(@typeInfo(T) == .Int);
864 comptime assert(!T.is_signed);
865 comptime const PromotedType = std.meta.Int(T.is_signed, T.bit_count + 1);
866 comptime const overflowBit = @as(PromotedType, 1) << T.bit_count;
864 const info = @typeInfo(T).Int;
865 comptime assert(!info.is_signed);
866 comptime const PromotedType = std.meta.Int(info.is_signed, info.bits + 1);
867 comptime const overflowBit = @as(PromotedType, 1) << info.bits;
867868 var x = ceilPowerOfTwoPromote(T, value);
868869 if (overflowBit & x != 0) {
869870 return error.Overflow;
......@@ -911,7 +912,7 @@ fn testCeilPowerOfTwo() !void {
911912
912913pub fn log2_int(comptime T: type, x: T) Log2Int(T) {
913914 assert(x != 0);
914 return @intCast(Log2Int(T), T.bit_count - 1 - @clz(T, x));
915 return @intCast(Log2Int(T), @typeInfo(T).Int.bits - 1 - @clz(T, x));
915916}
916917
917918pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) {
......@@ -1008,8 +1009,8 @@ test "max value type" {
10081009 testing.expect(x == 2147483647);
10091010}
10101011
1011pub fn mulWide(comptime T: type, a: T, b: T) std.meta.Int(T.is_signed, T.bit_count * 2) {
1012 const ResultInt = std.meta.Int(T.is_signed, T.bit_count * 2);
1012pub fn mulWide(comptime T: type, a: T, b: T) std.meta.Int(@typeInfo(T).Int.is_signed, @typeInfo(T).Int.bits * 2) {
1013 const ResultInt = std.meta.Int(@typeInfo(T).Int.is_signed, @typeInfo(T).Int.bits * 2);
10131014 return @as(ResultInt, a) * @as(ResultInt, b);
10141015}
10151016
lib/std/math/big.zig+6-5
......@@ -9,14 +9,15 @@ const assert = std.debug.assert;
99pub const Rational = @import("big/rational.zig").Rational;
1010pub const int = @import("big/int.zig");
1111pub const Limb = usize;
12pub const DoubleLimb = std.meta.IntType(false, 2 * Limb.bit_count);
13pub const SignedDoubleLimb = std.meta.IntType(true, DoubleLimb.bit_count);
12const limb_info = @typeInfo(Limb).Int;
13pub const DoubleLimb = std.meta.IntType(false, 2 * limb_info.bits);
14pub const SignedDoubleLimb = std.meta.IntType(true, 2 * limb_info.bits);
1415pub const Log2Limb = std.math.Log2Int(Limb);
1516
1617comptime {
17 assert(std.math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count);
18 assert(Limb.bit_count <= 64); // u128 set is unsupported
19 assert(Limb.is_signed == false);
18 assert(std.math.floorPowerOfTwo(usize, limb_info.bits) == limb_info.bits);
19 assert(limb_info.bits <= 64); // u128 set is unsupported
20 assert(limb_info.is_signed == false);
2021}
2122
2223test "" {
lib/std/math/big/int.zig+44-43
......@@ -6,6 +6,7 @@
66const std = @import("../../std.zig");
77const math = std.math;
88const Limb = std.math.big.Limb;
9const limb_bits = @typeInfo(Limb).Int.bits;
910const DoubleLimb = std.math.big.DoubleLimb;
1011const SignedDoubleLimb = std.math.big.SignedDoubleLimb;
1112const Log2Limb = std.math.big.Log2Limb;
......@@ -28,7 +29,7 @@ pub fn calcLimbLen(scalar: anytype) usize {
2829 },
2930 .ComptimeInt => {
3031 const w_value = if (scalar < 0) -scalar else scalar;
31 return @divFloor(math.log2(w_value), Limb.bit_count) + 1;
32 return @divFloor(math.log2(w_value), limb_bits) + 1;
3233 },
3334 else => @compileError("parameter must be a primitive integer type"),
3435 }
......@@ -54,7 +55,7 @@ pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize {
5455}
5556
5657pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize {
57 return (string_len + (Limb.bit_count / base - 1)) / (Limb.bit_count / base);
58 return (string_len + (limb_bits / base - 1)) / (limb_bits / base);
5859}
5960
6061/// a + b * c + *carry, sets carry to the overflow bits
......@@ -68,7 +69,7 @@ pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
6869 // r2 = b * c
6970 const bc = @as(DoubleLimb, math.mulWide(Limb, b, c));
7071 const r2 = @truncate(Limb, bc);
71 const c2 = @truncate(Limb, bc >> Limb.bit_count);
72 const c2 = @truncate(Limb, bc >> limb_bits);
7273
7374 // r1 = r1 + r2
7475 const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1));
......@@ -181,7 +182,7 @@ pub const Mutable = struct {
181182
182183 switch (@typeInfo(T)) {
183184 .Int => |info| {
184 const UT = if (T.is_signed) std.meta.Int(false, T.bit_count - 1) else T;
185 const UT = if (info.is_signed) std.meta.Int(false, info.bits - 1) else T;
185186
186187 const needed_limbs = @sizeOf(UT) / @sizeOf(Limb);
187188 assert(needed_limbs <= self.limbs.len); // value too big
......@@ -190,7 +191,7 @@ pub const Mutable = struct {
190191
191192 var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value);
192193
193 if (info.bits <= Limb.bit_count) {
194 if (info.bits <= limb_bits) {
194195 self.limbs[0] = @as(Limb, w_value);
195196 self.len += 1;
196197 } else {
......@@ -200,15 +201,15 @@ pub const Mutable = struct {
200201 self.len += 1;
201202
202203 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
203 w_value >>= Limb.bit_count / 2;
204 w_value >>= Limb.bit_count / 2;
204 w_value >>= limb_bits / 2;
205 w_value >>= limb_bits / 2;
205206 }
206207 }
207208 },
208209 .ComptimeInt => {
209210 comptime var w_value = if (value < 0) -value else value;
210211
211 const req_limbs = @divFloor(math.log2(w_value), Limb.bit_count) + 1;
212 const req_limbs = @divFloor(math.log2(w_value), limb_bits) + 1;
212213 assert(req_limbs <= self.limbs.len); // value too big
213214
214215 self.len = req_limbs;
......@@ -217,14 +218,14 @@ pub const Mutable = struct {
217218 if (w_value <= maxInt(Limb)) {
218219 self.limbs[0] = w_value;
219220 } else {
220 const mask = (1 << Limb.bit_count) - 1;
221 const mask = (1 << limb_bits) - 1;
221222
222223 comptime var i = 0;
223224 inline while (w_value != 0) : (i += 1) {
224225 self.limbs[i] = w_value & mask;
225226
226 w_value >>= Limb.bit_count / 2;
227 w_value >>= Limb.bit_count / 2;
227 w_value >>= limb_bits / 2;
228 w_value >>= limb_bits / 2;
228229 }
229230 }
230231 },
......@@ -506,7 +507,7 @@ pub const Mutable = struct {
506507 /// `a.limbs.len + (shift / (@sizeOf(Limb) * 8))`.
507508 pub fn shiftLeft(r: *Mutable, a: Const, shift: usize) void {
508509 llshl(r.limbs[0..], a.limbs[0..a.limbs.len], shift);
509 r.normalize(a.limbs.len + (shift / Limb.bit_count) + 1);
510 r.normalize(a.limbs.len + (shift / limb_bits) + 1);
510511 r.positive = a.positive;
511512 }
512513
......@@ -516,7 +517,7 @@ pub const Mutable = struct {
516517 /// Asserts there is enough memory to fit the result. The upper bound Limb count is
517518 /// `a.limbs.len - (shift / (@sizeOf(Limb) * 8))`.
518519 pub fn shiftRight(r: *Mutable, a: Const, shift: usize) void {
519 if (a.limbs.len <= shift / Limb.bit_count) {
520 if (a.limbs.len <= shift / limb_bits) {
520521 r.len = 1;
521522 r.positive = true;
522523 r.limbs[0] = 0;
......@@ -524,7 +525,7 @@ pub const Mutable = struct {
524525 }
525526
526527 const r_len = llshr(r.limbs[0..], a.limbs[0..a.limbs.len], shift);
527 r.len = a.limbs.len - (shift / Limb.bit_count);
528 r.len = a.limbs.len - (shift / limb_bits);
528529 r.positive = a.positive;
529530 }
530531
......@@ -772,7 +773,7 @@ pub const Mutable = struct {
772773 }
773774
774775 if (ab_zero_limb_count != 0) {
775 rem.shiftLeft(rem.toConst(), ab_zero_limb_count * Limb.bit_count);
776 rem.shiftLeft(rem.toConst(), ab_zero_limb_count * limb_bits);
776777 }
777778 }
778779
......@@ -803,10 +804,10 @@ pub const Mutable = struct {
803804 };
804805 tmp.limbs[0] = 0;
805806
806 // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even
807 // Normalize so y > limb_bits / 2 (i.e. leading bit is set) and even
807808 var norm_shift = @clz(Limb, y.limbs[y.len - 1]);
808809 if (norm_shift == 0 and y.toConst().isOdd()) {
809 norm_shift = Limb.bit_count;
810 norm_shift = limb_bits;
810811 }
811812 x.shiftLeft(x.toConst(), norm_shift);
812813 y.shiftLeft(y.toConst(), norm_shift);
......@@ -820,7 +821,7 @@ pub const Mutable = struct {
820821 mem.set(Limb, q.limbs[0..q.len], 0);
821822
822823 // 2.
823 tmp.shiftLeft(y.toConst(), Limb.bit_count * (n - t));
824 tmp.shiftLeft(y.toConst(), limb_bits * (n - t));
824825 while (x.toConst().order(tmp.toConst()) != .lt) {
825826 q.limbs[n - t] += 1;
826827 x.sub(x.toConst(), tmp.toConst());
......@@ -833,7 +834,7 @@ pub const Mutable = struct {
833834 if (x.limbs[i] == y.limbs[t]) {
834835 q.limbs[i - t - 1] = maxInt(Limb);
835836 } else {
836 const num = (@as(DoubleLimb, x.limbs[i]) << Limb.bit_count) | @as(DoubleLimb, x.limbs[i - 1]);
837 const num = (@as(DoubleLimb, x.limbs[i]) << limb_bits) | @as(DoubleLimb, x.limbs[i - 1]);
837838 const z = @intCast(Limb, num / @as(DoubleLimb, y.limbs[t]));
838839 q.limbs[i - t - 1] = if (z > maxInt(Limb)) maxInt(Limb) else @as(Limb, z);
839840 }
......@@ -862,11 +863,11 @@ pub const Mutable = struct {
862863 // 3.3
863864 tmp.set(q.limbs[i - t - 1]);
864865 tmp.mul(tmp.toConst(), y.toConst(), mul_limb_buf, allocator);
865 tmp.shiftLeft(tmp.toConst(), Limb.bit_count * (i - t - 1));
866 tmp.shiftLeft(tmp.toConst(), limb_bits * (i - t - 1));
866867 x.sub(x.toConst(), tmp.toConst());
867868
868869 if (!x.positive) {
869 tmp.shiftLeft(y.toConst(), Limb.bit_count * (i - t - 1));
870 tmp.shiftLeft(y.toConst(), limb_bits * (i - t - 1));
870871 x.add(x.toConst(), tmp.toConst());
871872 q.limbs[i - t - 1] -= 1;
872873 }
......@@ -949,7 +950,7 @@ pub const Const = struct {
949950
950951 /// Returns the number of bits required to represent the absolute value of an integer.
951952 pub fn bitCountAbs(self: Const) usize {
952 return (self.limbs.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(Limb, self.limbs[self.limbs.len - 1]));
953 return (self.limbs.len - 1) * limb_bits + (limb_bits - @clz(Limb, self.limbs[self.limbs.len - 1]));
953954 }
954955
955956 /// Returns the number of bits required to represent the integer in twos-complement form.
......@@ -1019,10 +1020,10 @@ pub const Const = struct {
10191020 /// Returns an error if self cannot be narrowed into the requested type without truncation.
10201021 pub fn to(self: Const, comptime T: type) ConvertError!T {
10211022 switch (@typeInfo(T)) {
1022 .Int => {
1023 const UT = std.meta.Int(false, T.bit_count);
1023 .Int => |info| {
1024 const UT = std.meta.Int(false, info.bits);
10241025
1025 if (self.bitCountTwosComp() > T.bit_count) {
1026 if (self.bitCountTwosComp() > info.bits) {
10261027 return error.TargetTooSmall;
10271028 }
10281029
......@@ -1033,12 +1034,12 @@ pub const Const = struct {
10331034 } else {
10341035 for (self.limbs[0..self.limbs.len]) |_, ri| {
10351036 const limb = self.limbs[self.limbs.len - ri - 1];
1036 r <<= Limb.bit_count;
1037 r <<= limb_bits;
10371038 r |= limb;
10381039 }
10391040 }
10401041
1041 if (!T.is_signed) {
1042 if (!info.is_signed) {
10421043 return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned;
10431044 } else {
10441045 if (self.positive) {
......@@ -1149,7 +1150,7 @@ pub const Const = struct {
11491150
11501151 outer: for (self.limbs[0..self.limbs.len]) |limb| {
11511152 var shift: usize = 0;
1152 while (shift < Limb.bit_count) : (shift += base_shift) {
1153 while (shift < limb_bits) : (shift += base_shift) {
11531154 const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & @as(Limb, base - 1));
11541155 const ch = std.fmt.digitToChar(r, uppercase);
11551156 string[digits_len] = ch;
......@@ -1295,7 +1296,7 @@ pub const Const = struct {
12951296/// Memory is allocated as needed to ensure operations never overflow. The range
12961297/// is bounded only by available memory.
12971298pub const Managed = struct {
1298 pub const sign_bit: usize = 1 << (usize.bit_count - 1);
1299 pub const sign_bit: usize = 1 << (@typeInfo(usize).Int.bits - 1);
12991300
13001301 /// Default number of limbs to allocate on creation of a `Managed`.
13011302 pub const default_capacity = 4;
......@@ -1448,7 +1449,7 @@ pub const Managed = struct {
14481449 for (self.limbs[0..self.len()]) |limb| {
14491450 std.debug.warn("{x} ", .{limb});
14501451 }
1451 std.debug.warn("capacity={} positive={}\n", .{ self.limbs.len, self.positive });
1452 std.debug.warn("capacity={} positive={}\n", .{ self.limbs.len, self.isPositive() });
14521453 }
14531454
14541455 /// Negate the sign.
......@@ -1716,7 +1717,7 @@ pub const Managed = struct {
17161717
17171718 /// r = a << shift, in other words, r = a * 2^shift
17181719 pub fn shiftLeft(r: *Managed, a: Managed, shift: usize) !void {
1719 try r.ensureCapacity(a.len() + (shift / Limb.bit_count) + 1);
1720 try r.ensureCapacity(a.len() + (shift / limb_bits) + 1);
17201721 var m = r.toMutable();
17211722 m.shiftLeft(a.toConst(), shift);
17221723 r.setMetadata(m.positive, m.len);
......@@ -1724,13 +1725,13 @@ pub const Managed = struct {
17241725
17251726 /// r = a >> shift
17261727 pub fn shiftRight(r: *Managed, a: Managed, shift: usize) !void {
1727 if (a.len() <= shift / Limb.bit_count) {
1728 if (a.len() <= shift / limb_bits) {
17281729 r.metadata = 1;
17291730 r.limbs[0] = 0;
17301731 return;
17311732 }
17321733
1733 try r.ensureCapacity(a.len() - (shift / Limb.bit_count));
1734 try r.ensureCapacity(a.len() - (shift / limb_bits));
17341735 var m = r.toMutable();
17351736 m.shiftRight(a.toConst(), shift);
17361737 r.setMetadata(m.positive, m.len);
......@@ -2021,7 +2022,7 @@ fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {
20212022 rem.* = 0;
20222023 for (a) |_, ri| {
20232024 const i = a.len - ri - 1;
2024 const pdiv = ((@as(DoubleLimb, rem.*) << Limb.bit_count) | a[i]);
2025 const pdiv = ((@as(DoubleLimb, rem.*) << limb_bits) | a[i]);
20252026
20262027 if (pdiv == 0) {
20272028 quo[i] = 0;
......@@ -2042,10 +2043,10 @@ fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {
20422043fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
20432044 @setRuntimeSafety(debug_safety);
20442045 assert(a.len >= 1);
2045 assert(r.len >= a.len + (shift / Limb.bit_count) + 1);
2046 assert(r.len >= a.len + (shift / limb_bits) + 1);
20462047
2047 const limb_shift = shift / Limb.bit_count + 1;
2048 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);
2048 const limb_shift = shift / limb_bits + 1;
2049 const interior_limb_shift = @intCast(Log2Limb, shift % limb_bits);
20492050
20502051 var carry: Limb = 0;
20512052 var i: usize = 0;
......@@ -2057,7 +2058,7 @@ fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
20572058 r[dst_i] = carry | @call(.{ .modifier = .always_inline }, math.shr, .{
20582059 Limb,
20592060 src_digit,
2060 Limb.bit_count - @intCast(Limb, interior_limb_shift),
2061 limb_bits - @intCast(Limb, interior_limb_shift),
20612062 });
20622063 carry = (src_digit << interior_limb_shift);
20632064 }
......@@ -2069,10 +2070,10 @@ fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
20692070fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
20702071 @setRuntimeSafety(debug_safety);
20712072 assert(a.len >= 1);
2072 assert(r.len >= a.len - (shift / Limb.bit_count));
2073 assert(r.len >= a.len - (shift / limb_bits));
20732074
2074 const limb_shift = shift / Limb.bit_count;
2075 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);
2075 const limb_shift = shift / limb_bits;
2076 const interior_limb_shift = @intCast(Log2Limb, shift % limb_bits);
20762077
20772078 var carry: Limb = 0;
20782079 var i: usize = 0;
......@@ -2085,7 +2086,7 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
20852086 carry = @call(.{ .modifier = .always_inline }, math.shl, .{
20862087 Limb,
20872088 src_digit,
2088 Limb.bit_count - @intCast(Limb, interior_limb_shift),
2089 limb_bits - @intCast(Limb, interior_limb_shift),
20892090 });
20902091 }
20912092}
......@@ -2135,7 +2136,7 @@ fn fixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Mutable {
21352136 const A_is_positive = A >= 0;
21362137 const Au = @intCast(DoubleLimb, if (A < 0) -A else A);
21372138 storage[0] = @truncate(Limb, Au);
2138 storage[1] = @truncate(Limb, Au >> Limb.bit_count);
2139 storage[1] = @truncate(Limb, Au >> limb_bits);
21392140 return .{
21402141 .limbs = storage[0..2],
21412142 .positive = A_is_positive,
lib/std/math/big/int_test.zig+3-3
......@@ -23,13 +23,13 @@ test "big.int comptime_int set" {
2323 var a = try Managed.initSet(testing.allocator, s);
2424 defer a.deinit();
2525
26 const s_limb_count = 128 / Limb.bit_count;
26 const s_limb_count = 128 / @typeInfo(Limb).Int.bits;
2727
2828 comptime var i: usize = 0;
2929 inline while (i < s_limb_count) : (i += 1) {
3030 const result = @as(Limb, s & maxInt(Limb));
31 s >>= Limb.bit_count / 2;
32 s >>= Limb.bit_count / 2;
31 s >>= @typeInfo(Limb).Int.bits / 2;
32 s >>= @typeInfo(Limb).Int.bits / 2;
3333 testing.expect(a.limbs[i] == result);
3434 }
3535}
lib/std/math/big/rational.zig+9-7
......@@ -136,7 +136,7 @@ pub const Rational = struct {
136136 // Translated from golang.go/src/math/big/rat.go.
137137 debug.assert(@typeInfo(T) == .Float);
138138
139 const UnsignedInt = std.meta.Int(false, T.bit_count);
139 const UnsignedInt = std.meta.Int(false, @typeInfo(T).Float.bits);
140140 const f_bits = @bitCast(UnsignedInt, f);
141141
142142 const exponent_bits = math.floatExponentBits(T);
......@@ -194,8 +194,8 @@ pub const Rational = struct {
194194 // TODO: Indicate whether the result is not exact.
195195 debug.assert(@typeInfo(T) == .Float);
196196
197 const fsize = T.bit_count;
198 const BitReprType = std.meta.Int(false, T.bit_count);
197 const fsize = @typeInfo(T).Float.bits;
198 const BitReprType = std.meta.Int(false, fsize);
199199
200200 const msize = math.floatMantissaBits(T);
201201 const msize1 = msize + 1;
......@@ -475,16 +475,18 @@ pub const Rational = struct {
475475fn extractLowBits(a: Int, comptime T: type) T {
476476 testing.expect(@typeInfo(T) == .Int);
477477
478 if (T.bit_count <= Limb.bit_count) {
478 const t_bits = @typeInfo(T).Int.bits;
479 const limb_bits = @typeInfo(Limb).Int.bits;
480 if (t_bits <= limb_bits) {
479481 return @truncate(T, a.limbs[0]);
480482 } else {
481483 var r: T = 0;
482484 comptime var i: usize = 0;
483485
484 // Remainder is always 0 since if T.bit_count >= Limb.bit_count -> Limb | T and both
486 // Remainder is always 0 since if t_bits >= limb_bits -> Limb | T and both
485487 // are powers of two.
486 inline while (i < T.bit_count / Limb.bit_count) : (i += 1) {
487 r |= math.shl(T, a.limbs[i], i * Limb.bit_count);
488 inline while (i < t_bits / limb_bits) : (i += 1) {
489 r |= math.shl(T, a.limbs[i], i * limb_bits);
488490 }
489491
490492 return r;
lib/std/math/cos.zig+1-1
......@@ -49,7 +49,7 @@ const pi4c = 2.69515142907905952645E-15;
4949const m4pi = 1.273239544735162542821171882678754627704620361328125;
5050
5151fn cos_(comptime T: type, x_: T) T {
52 const I = std.meta.Int(true, T.bit_count);
52 const I = std.meta.Int(true, @typeInfo(T).Float.bits);
5353
5454 var x = x_;
5555 if (math.isNan(x) or math.isInf(x)) {
lib/std/math/pow.zig+2-2
......@@ -128,7 +128,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
128128 if (yf != 0 and x < 0) {
129129 return math.nan(T);
130130 }
131 if (yi >= 1 << (T.bit_count - 1)) {
131 if (yi >= 1 << (@typeInfo(T).Float.bits - 1)) {
132132 return math.exp(y * math.ln(x));
133133 }
134134
......@@ -150,7 +150,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
150150 var xe = r2.exponent;
151151 var x1 = r2.significand;
152152
153 var i = @floatToInt(std.meta.Int(true, T.bit_count), yi);
153 var i = @floatToInt(std.meta.Int(true, @typeInfo(T).Float.bits), yi);
154154 while (i != 0) : (i >>= 1) {
155155 const overflow_shift = math.floatExponentBits(T) + 1;
156156 if (xe < -(1 << overflow_shift) or (1 << overflow_shift) < xe) {
lib/std/math/sin.zig+1-1
......@@ -50,7 +50,7 @@ const pi4c = 2.69515142907905952645E-15;
5050const m4pi = 1.273239544735162542821171882678754627704620361328125;
5151
5252fn sin_(comptime T: type, x_: T) T {
53 const I = std.meta.Int(true, T.bit_count);
53 const I = std.meta.Int(true, @typeInfo(T).Float.bits);
5454
5555 var x = x_;
5656 if (x == 0 or math.isNan(x)) {
lib/std/math/sqrt.zig+3-3
......@@ -36,10 +36,10 @@ pub fn sqrt(x: anytype) Sqrt(@TypeOf(x)) {
3636 }
3737}
3838
39fn sqrt_int(comptime T: type, value: T) std.meta.Int(false, T.bit_count / 2) {
39fn sqrt_int(comptime T: type, value: T) std.meta.Int(false, @typeInfo(T).Int.bits / 2) {
4040 var op = value;
4141 var res: T = 0;
42 var one: T = 1 << (T.bit_count - 2);
42 var one: T = 1 << (@typeInfo(T).Int.bits - 2);
4343
4444 // "one" starts at the highest power of four <= than the argument.
4545 while (one > op) {
......@@ -55,7 +55,7 @@ fn sqrt_int(comptime T: type, value: T) std.meta.Int(false, T.bit_count / 2) {
5555 one >>= 2;
5656 }
5757
58 const ResultType = std.meta.Int(false, T.bit_count / 2);
58 const ResultType = std.meta.Int(false, @typeInfo(T).Int.bits / 2);
5959 return @intCast(ResultType, res);
6060}
6161
lib/std/math/tan.zig+1-1
......@@ -43,7 +43,7 @@ const pi4c = 2.69515142907905952645E-15;
4343const m4pi = 1.273239544735162542821171882678754627704620361328125;
4444
4545fn tan_(comptime T: type, x_: T) T {
46 const I = std.meta.Int(true, T.bit_count);
46 const I = std.meta.Int(true, @typeInfo(T).Float.bits);
4747
4848 var x = x_;
4949 if (x == 0 or math.isNan(x)) {
lib/std/mem.zig+21-21
......@@ -949,7 +949,7 @@ pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: builtin.
949949/// This function cannot fail and cannot cause undefined behavior.
950950/// Assumes the endianness of memory is native. This means the function can
951951/// simply pointer cast memory.
952pub fn readIntNative(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8) T {
952pub fn readIntNative(comptime T: type, bytes: *const [@divExact(@typeInfo(T).Int.bits, 8)]u8) T {
953953 return @ptrCast(*align(1) const T, bytes).*;
954954}
955955
......@@ -957,7 +957,7 @@ pub fn readIntNative(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]
957957/// The bit count of T must be evenly divisible by 8.
958958/// This function cannot fail and cannot cause undefined behavior.
959959/// Assumes the endianness of memory is foreign, so it must byte-swap.
960pub fn readIntForeign(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8) T {
960pub fn readIntForeign(comptime T: type, bytes: *const [@divExact(@typeInfo(T).Int.bits, 8)]u8) T {
961961 return @byteSwap(T, readIntNative(T, bytes));
962962}
963963
......@@ -971,18 +971,18 @@ pub const readIntBig = switch (builtin.endian) {
971971 .Big => readIntNative,
972972};
973973
974/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0
974/// Asserts that bytes.len >= @typeInfo(T).Int.bits / 8. Reads the integer starting from index 0
975975/// and ignores extra bytes.
976976/// The bit count of T must be evenly divisible by 8.
977977/// Assumes the endianness of memory is native. This means the function can
978978/// simply pointer cast memory.
979979pub fn readIntSliceNative(comptime T: type, bytes: []const u8) T {
980 const n = @divExact(T.bit_count, 8);
980 const n = @divExact(@typeInfo(T).Int.bits, 8);
981981 assert(bytes.len >= n);
982982 return readIntNative(T, bytes[0..n]);
983983}
984984
985/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0
985/// Asserts that bytes.len >= @typeInfo(T).Int.bits / 8. Reads the integer starting from index 0
986986/// and ignores extra bytes.
987987/// The bit count of T must be evenly divisible by 8.
988988/// Assumes the endianness of memory is foreign, so it must byte-swap.
......@@ -1003,7 +1003,7 @@ pub const readIntSliceBig = switch (builtin.endian) {
10031003/// Reads an integer from memory with bit count specified by T.
10041004/// The bit count of T must be evenly divisible by 8.
10051005/// This function cannot fail and cannot cause undefined behavior.
1006pub fn readInt(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8, endian: builtin.Endian) T {
1006pub fn readInt(comptime T: type, bytes: *const [@divExact(@typeInfo(T).Int.bits, 8)]u8, endian: builtin.Endian) T {
10071007 if (endian == builtin.endian) {
10081008 return readIntNative(T, bytes);
10091009 } else {
......@@ -1011,11 +1011,11 @@ pub fn readInt(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8, en
10111011 }
10121012}
10131013
1014/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0
1014/// Asserts that bytes.len >= @typeInfo(T).Int.bits / 8. Reads the integer starting from index 0
10151015/// and ignores extra bytes.
10161016/// The bit count of T must be evenly divisible by 8.
10171017pub fn readIntSlice(comptime T: type, bytes: []const u8, endian: builtin.Endian) T {
1018 const n = @divExact(T.bit_count, 8);
1018 const n = @divExact(@typeInfo(T).Int.bits, 8);
10191019 assert(bytes.len >= n);
10201020 return readInt(T, bytes[0..n], endian);
10211021}
......@@ -1060,7 +1060,7 @@ test "readIntBig and readIntLittle" {
10601060/// accepts any integer bit width.
10611061/// This function stores in native endian, which means it is implemented as a simple
10621062/// memory store.
1063pub fn writeIntNative(comptime T: type, buf: *[(T.bit_count + 7) / 8]u8, value: T) void {
1063pub fn writeIntNative(comptime T: type, buf: *[(@typeInfo(T).Int.bits + 7) / 8]u8, value: T) void {
10641064 @ptrCast(*align(1) T, buf).* = value;
10651065}
10661066
......@@ -1068,7 +1068,7 @@ pub fn writeIntNative(comptime T: type, buf: *[(T.bit_count + 7) / 8]u8, value:
10681068/// This function always succeeds, has defined behavior for all inputs, but
10691069/// the integer bit width must be divisible by 8.
10701070/// This function stores in foreign endian, which means it does a @byteSwap first.
1071pub fn writeIntForeign(comptime T: type, buf: *[@divExact(T.bit_count, 8)]u8, value: T) void {
1071pub fn writeIntForeign(comptime T: type, buf: *[@divExact(@typeInfo(T).Int.bits, 8)]u8, value: T) void {
10721072 writeIntNative(T, buf, @byteSwap(T, value));
10731073}
10741074
......@@ -1085,7 +1085,7 @@ pub const writeIntBig = switch (builtin.endian) {
10851085/// Writes an integer to memory, storing it in twos-complement.
10861086/// This function always succeeds, has defined behavior for all inputs, but
10871087/// the integer bit width must be divisible by 8.
1088pub fn writeInt(comptime T: type, buffer: *[@divExact(T.bit_count, 8)]u8, value: T, endian: builtin.Endian) void {
1088pub fn writeInt(comptime T: type, buffer: *[@divExact(@typeInfo(T).Int.bits, 8)]u8, value: T, endian: builtin.Endian) void {
10891089 if (endian == builtin.endian) {
10901090 return writeIntNative(T, buffer, value);
10911091 } else {
......@@ -1094,19 +1094,19 @@ pub fn writeInt(comptime T: type, buffer: *[@divExact(T.bit_count, 8)]u8, value:
10941094}
10951095
10961096/// Writes a twos-complement little-endian integer to memory.
1097/// Asserts that buf.len >= T.bit_count / 8.
1097/// Asserts that buf.len >= @typeInfo(T).Int.bits / 8.
10981098/// The bit count of T must be divisible by 8.
10991099/// Any extra bytes in buffer after writing the integer are set to zero. To
11001100/// avoid the branch to check for extra buffer bytes, use writeIntLittle
11011101/// instead.
11021102pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void {
1103 assert(buffer.len >= @divExact(T.bit_count, 8));
1103 assert(buffer.len >= @divExact(@typeInfo(T).Int.bits, 8));
11041104
1105 if (T.bit_count == 0)
1105 if (@typeInfo(T).Int.bits == 0)
11061106 return set(u8, buffer, 0);
11071107
11081108 // TODO I want to call writeIntLittle here but comptime eval facilities aren't good enough
1109 const uint = std.meta.Int(false, T.bit_count);
1109 const uint = std.meta.Int(false, @typeInfo(T).Int.bits);
11101110 var bits = @truncate(uint, value);
11111111 for (buffer) |*b| {
11121112 b.* = @truncate(u8, bits);
......@@ -1115,18 +1115,18 @@ pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void {
11151115}
11161116
11171117/// Writes a twos-complement big-endian integer to memory.
1118/// Asserts that buffer.len >= T.bit_count / 8.
1118/// Asserts that buffer.len >= @typeInfo(T).Int.bits / 8.
11191119/// The bit count of T must be divisible by 8.
11201120/// Any extra bytes in buffer before writing the integer are set to zero. To
11211121/// avoid the branch to check for extra buffer bytes, use writeIntBig instead.
11221122pub fn writeIntSliceBig(comptime T: type, buffer: []u8, value: T) void {
1123 assert(buffer.len >= @divExact(T.bit_count, 8));
1123 assert(buffer.len >= @divExact(@typeInfo(T).Int.bits, 8));
11241124
1125 if (T.bit_count == 0)
1125 if (@typeInfo(T).Int.bits == 0)
11261126 return set(u8, buffer, 0);
11271127
11281128 // TODO I want to call writeIntBig here but comptime eval facilities aren't good enough
1129 const uint = std.meta.Int(false, T.bit_count);
1129 const uint = std.meta.Int(false, @typeInfo(T).Int.bits);
11301130 var bits = @truncate(uint, value);
11311131 var index: usize = buffer.len;
11321132 while (index != 0) {
......@@ -1147,13 +1147,13 @@ pub const writeIntSliceForeign = switch (builtin.endian) {
11471147};
11481148
11491149/// Writes a twos-complement integer to memory, with the specified endianness.
1150/// Asserts that buf.len >= T.bit_count / 8.
1150/// Asserts that buf.len >= @typeInfo(T).Int.bits / 8.
11511151/// The bit count of T must be evenly divisible by 8.
11521152/// Any extra bytes in buffer not part of the integer are set to zero, with
11531153/// respect to endianness. To avoid the branch to check for extra buffer bytes,
11541154/// use writeInt instead.
11551155pub fn writeIntSlice(comptime T: type, buffer: []u8, value: T, endian: builtin.Endian) void {
1156 comptime assert(T.bit_count % 8 == 0);
1156 comptime assert(@typeInfo(T).Int.bits % 8 == 0);
11571157 return switch (endian) {
11581158 .Little => writeIntSliceLittle(T, buffer, value),
11591159 .Big => writeIntSliceBig(T, buffer, value),
lib/std/mem/Allocator.zig+4-4
......@@ -159,7 +159,7 @@ fn moveBytes(
159159/// Returns a pointer to undefined memory.
160160/// Call `destroy` with the result to free the memory.
161161pub fn create(self: *Allocator, comptime T: type) Error!*T {
162 if (@sizeOf(T) == 0) return &(T{});
162 if (@sizeOf(T) == 0) return @as(*T, undefined);
163163 const slice = try self.allocAdvancedWithRetAddr(T, null, 1, .exact, @returnAddress());
164164 return &slice[0];
165165}
......@@ -167,11 +167,11 @@ pub fn create(self: *Allocator, comptime T: type) Error!*T {
167167/// `ptr` should be the return value of `create`, or otherwise
168168/// have the same address and alignment property.
169169pub fn destroy(self: *Allocator, ptr: anytype) void {
170 const T = @TypeOf(ptr).Child;
170 const info = @typeInfo(@TypeOf(ptr)).Pointer;
171 const T = info.child;
171172 if (@sizeOf(T) == 0) return;
172173 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
173 const ptr_align = @typeInfo(@TypeOf(ptr)).Pointer.alignment;
174 _ = self.shrinkBytes(non_const_ptr[0..@sizeOf(T)], ptr_align, 0, 0, @returnAddress());
174 _ = self.shrinkBytes(non_const_ptr[0..@sizeOf(T)], info.alignment, 0, 0, @returnAddress());
175175}
176176
177177/// Allocates an array of `n` items of type `T` and sets all the
lib/std/meta.zig+8-8
......@@ -705,34 +705,34 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
705705pub fn cast(comptime DestType: type, target: anytype) DestType {
706706 const TargetType = @TypeOf(target);
707707 switch (@typeInfo(DestType)) {
708 .Pointer => {
708 .Pointer => |dest_ptr| {
709709 switch (@typeInfo(TargetType)) {
710710 .Int, .ComptimeInt => {
711711 return @intToPtr(DestType, target);
712712 },
713713 .Pointer => |ptr| {
714 return @ptrCast(DestType, @alignCast(ptr.alignment, target));
714 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
715715 },
716716 .Optional => |opt| {
717717 if (@typeInfo(opt.child) == .Pointer) {
718 return @ptrCast(DestType, @alignCast(@alignOf(opt.child.Child), target));
718 return @ptrCast(DestType, @alignCast(dest_ptr, target));
719719 }
720720 },
721721 else => {},
722722 }
723723 },
724 .Optional => |opt| {
725 if (@typeInfo(opt.child) == .Pointer) {
724 .Optional => |dest_opt| {
725 if (@typeInfo(dest_opt.child) == .Pointer) {
726726 switch (@typeInfo(TargetType)) {
727727 .Int, .ComptimeInt => {
728728 return @intToPtr(DestType, target);
729729 },
730 .Pointer => |ptr| {
731 return @ptrCast(DestType, @alignCast(ptr.alignment, target));
730 .Pointer => {
731 return @ptrCast(DestType, @alignCast(@alignOf(dest_opt.child.Child), target));
732732 },
733733 .Optional => |target_opt| {
734734 if (@typeInfo(target_opt.child) == .Pointer) {
735 return @ptrCast(DestType, @alignCast(@alignOf(target_opt.child.Child), target));
735 return @ptrCast(DestType, @alignCast(@alignOf(dest_opt.child.Child), target));
736736 }
737737 },
738738 else => {},
lib/std/net.zig+4-1
......@@ -1164,7 +1164,7 @@ fn linuxLookupNameFromDnsSearch(
11641164 }
11651165
11661166 const search = if (rc.search.isNull() or dots >= rc.ndots or mem.endsWith(u8, name, "."))
1167 &[_]u8{}
1167 ""
11681168 else
11691169 rc.search.span();
11701170
......@@ -1641,6 +1641,9 @@ pub const StreamServer = struct {
16411641 /// by the socket buffer limits, not by the system memory.
16421642 SystemResources,
16431643
1644 /// Socket is not listening for new connections.
1645 SocketNotListening,
1646
16441647 ProtocolFailure,
16451648
16461649 /// Firewall rules forbid connection.
lib/std/os.zig+113-23
......@@ -2512,13 +2512,14 @@ pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) Read
25122512 }
25132513}
25142514
2515pub const SetIdError = error{
2516 ResourceLimitReached,
2515pub const SetEidError = error{
25172516 InvalidUserId,
25182517 PermissionDenied,
2519} || UnexpectedError;
2518};
2519
2520pub const SetIdError = error{ResourceLimitReached} || SetEidError || UnexpectedError;
25202521
2521pub fn setuid(uid: u32) SetIdError!void {
2522pub fn setuid(uid: uid_t) SetIdError!void {
25222523 switch (errno(system.setuid(uid))) {
25232524 0 => return,
25242525 EAGAIN => return error.ResourceLimitReached,
......@@ -2528,7 +2529,16 @@ pub fn setuid(uid: u32) SetIdError!void {
25282529 }
25292530}
25302531
2531pub fn setreuid(ruid: u32, euid: u32) SetIdError!void {
2532pub fn seteuid(uid: uid_t) SetEidError!void {
2533 switch (errno(system.seteuid(uid))) {
2534 0 => return,
2535 EINVAL => return error.InvalidUserId,
2536 EPERM => return error.PermissionDenied,
2537 else => |err| return unexpectedErrno(err),
2538 }
2539}
2540
2541pub fn setreuid(ruid: uid_t, euid: uid_t) SetIdError!void {
25322542 switch (errno(system.setreuid(ruid, euid))) {
25332543 0 => return,
25342544 EAGAIN => return error.ResourceLimitReached,
......@@ -2538,7 +2548,7 @@ pub fn setreuid(ruid: u32, euid: u32) SetIdError!void {
25382548 }
25392549}
25402550
2541pub fn setgid(gid: u32) SetIdError!void {
2551pub fn setgid(gid: gid_t) SetIdError!void {
25422552 switch (errno(system.setgid(gid))) {
25432553 0 => return,
25442554 EAGAIN => return error.ResourceLimitReached,
......@@ -2548,7 +2558,16 @@ pub fn setgid(gid: u32) SetIdError!void {
25482558 }
25492559}
25502560
2551pub fn setregid(rgid: u32, egid: u32) SetIdError!void {
2561pub fn setegid(uid: uid_t) SetEidError!void {
2562 switch (errno(system.setegid(uid))) {
2563 0 => return,
2564 EINVAL => return error.InvalidUserId,
2565 EPERM => return error.PermissionDenied,
2566 else => |err| return unexpectedErrno(err),
2567 }
2568}
2569
2570pub fn setregid(rgid: gid_t, egid: gid_t) SetIdError!void {
25522571 switch (errno(system.setregid(rgid, egid))) {
25532572 0 => return,
25542573 EAGAIN => return error.ResourceLimitReached,
......@@ -2815,6 +2834,9 @@ pub const AcceptError = error{
28152834 /// by the socket buffer limits, not by the system memory.
28162835 SystemResources,
28172836
2837 /// Socket is not listening for new connections.
2838 SocketNotListening,
2839
28182840 ProtocolFailure,
28192841
28202842 /// Firewall rules forbid connection.
......@@ -2884,21 +2906,21 @@ pub fn accept(
28842906 loop.waitUntilFdReadable(sock);
28852907 continue;
28862908 } else {
2887 return error.WouldBlock;
2888 },
2889 EBADF => unreachable, // always a race condition
2890 ECONNABORTED => return error.ConnectionAborted,
2891 EFAULT => unreachable,
2892 EINVAL => unreachable,
2893 ENOTSOCK => unreachable,
2894 EMFILE => return error.ProcessFdQuotaExceeded,
2895 ENFILE => return error.SystemFdQuotaExceeded,
2896 ENOBUFS => return error.SystemResources,
2897 ENOMEM => return error.SystemResources,
2898 EOPNOTSUPP => unreachable,
2899 EPROTO => return error.ProtocolFailure,
2900 EPERM => return error.BlockedByFirewall,
2901 else => |err| return unexpectedErrno(err),
2909 return error.WouldBlock;
2910 },
2911 EBADF => unreachable, // always a race condition
2912 ECONNABORTED => return error.ConnectionAborted,
2913 EFAULT => unreachable,
2914 EINVAL => return error.SocketNotListening,
2915 ENOTSOCK => unreachable,
2916 EMFILE => return error.ProcessFdQuotaExceeded,
2917 ENFILE => return error.SystemFdQuotaExceeded,
2918 ENOBUFS => return error.SystemResources,
2919 ENOMEM => return error.SystemResources,
2920 EOPNOTSUPP => unreachable,
2921 EPROTO => return error.ProtocolFailure,
2922 EPERM => return error.BlockedByFirewall,
2923 else => |err| return unexpectedErrno(err),
29022924 }
29032925 }
29042926 } else unreachable;
......@@ -4554,7 +4576,7 @@ pub fn res_mkquery(
45544576 // Make a reasonably unpredictable id
45554577 var ts: timespec = undefined;
45564578 clock_gettime(CLOCK_REALTIME, &ts) catch {};
4557 const UInt = std.meta.Int(false, @TypeOf(ts.tv_nsec).bit_count);
4579 const UInt = std.meta.Int(false, std.meta.bitCount(@TypeOf(ts.tv_nsec)));
45584580 const unsec = @bitCast(UInt, ts.tv_nsec);
45594581 const id = @truncate(u32, unsec + unsec / 65536);
45604582 q[0] = @truncate(u8, id / 256);
......@@ -5404,3 +5426,71 @@ pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
54045426 else => |err| return std.os.unexpectedErrno(err),
54055427 }
54065428}
5429
5430pub const SyncError = error{
5431 InputOutput,
5432 NoSpaceLeft,
5433 DiskQuota,
5434 AccessDenied,
5435} || UnexpectedError;
5436
5437/// Write all pending file contents and metadata modifications to all filesystems.
5438pub fn sync() void {
5439 system.sync();
5440}
5441
5442/// Write all pending file contents and metadata modifications to the filesystem which contains the specified file.
5443pub fn syncfs(fd: fd_t) SyncError!void {
5444 const rc = system.syncfs(fd);
5445 switch (errno(rc)) {
5446 0 => return,
5447 EBADF, EINVAL, EROFS => unreachable,
5448 EIO => return error.InputOutput,
5449 ENOSPC => return error.NoSpaceLeft,
5450 EDQUOT => return error.DiskQuota,
5451 else => |err| return std.os.unexpectedErrno(err),
5452 }
5453}
5454
5455/// Write all pending file contents and metadata modifications for the specified file descriptor to the underlying filesystem.
5456pub fn fsync(fd: fd_t) SyncError!void {
5457 if (std.Target.current.os.tag == .windows) {
5458 if (windows.kernel32.FlushFileBuffers(fd) != 0)
5459 return;
5460 switch (windows.kernel32.GetLastError()) {
5461 .SUCCESS => return,
5462 .INVALID_HANDLE => unreachable,
5463 .ACCESS_DENIED => return error.AccessDenied, // a sync was performed but the system couldn't update the access time
5464 .UNEXP_NET_ERR => return error.InputOutput,
5465 else => return error.InputOutput,
5466 }
5467 }
5468 const rc = system.fsync(fd);
5469 switch (errno(rc)) {
5470 0 => return,
5471 EBADF, EINVAL, EROFS => unreachable,
5472 EIO => return error.InputOutput,
5473 ENOSPC => return error.NoSpaceLeft,
5474 EDQUOT => return error.DiskQuota,
5475 else => |err| return std.os.unexpectedErrno(err),
5476 }
5477}
5478
5479/// Write all pending file contents for the specified file descriptor to the underlying filesystem, but not necessarily the metadata.
5480pub fn fdatasync(fd: fd_t) SyncError!void {
5481 if (std.Target.current.os.tag == .windows) {
5482 return fsync(fd) catch |err| switch (err) {
5483 SyncError.AccessDenied => return, // fdatasync doesn't promise that the access time was synced
5484 else => return err,
5485 };
5486 }
5487 const rc = system.fdatasync(fd);
5488 switch (errno(rc)) {
5489 0 => return,
5490 EBADF, EINVAL, EROFS => unreachable,
5491 EIO => return error.InputOutput,
5492 ENOSPC => return error.NoSpaceLeft,
5493 EDQUOT => return error.DiskQuota,
5494 else => |err| return std.os.unexpectedErrno(err),
5495 }
5496}
lib/std/os/bits/darwin.zig+6-2
......@@ -7,9 +7,13 @@ const std = @import("../../std.zig");
77const assert = std.debug.assert;
88const maxInt = std.math.maxInt;
99
10// See: https://opensource.apple.com/source/xnu/xnu-6153.141.1/bsd/sys/_types.h.auto.html
11// TODO: audit mode_t/pid_t, should likely be u16/i32
1012pub const fd_t = c_int;
1113pub const pid_t = c_int;
1214pub const mode_t = c_uint;
15pub const uid_t = u32;
16pub const gid_t = u32;
1317
1418pub const in_port_t = u16;
1519pub const sa_family_t = u8;
......@@ -79,8 +83,8 @@ pub const Stat = extern struct {
7983 mode: u16,
8084 nlink: u16,
8185 ino: ino_t,
82 uid: u32,
83 gid: u32,
86 uid: uid_t,
87 gid: gid_t,
8488 rdev: i32,
8589 atimesec: isize,
8690 atimensec: isize,
lib/std/os/bits/dragonfly.zig+10-3
......@@ -9,10 +9,17 @@ const maxInt = std.math.maxInt;
99pub fn S_ISCHR(m: u32) bool {
1010 return m & S_IFMT == S_IFCHR;
1111}
12
13// See:
14// - https://gitweb.dragonflybsd.org/dragonfly.git/blob/HEAD:/include/unistd.h
15// - https://gitweb.dragonflybsd.org/dragonfly.git/blob/HEAD:/sys/sys/types.h
16// TODO: mode_t should probably be changed to a u16, audit pid_t/off_t as well
1217pub const fd_t = c_int;
1318pub const pid_t = c_int;
1419pub const off_t = c_long;
1520pub const mode_t = c_uint;
21pub const uid_t = u32;
22pub const gid_t = u32;
1623
1724pub const ENOTSUP = EOPNOTSUPP;
1825pub const EWOULDBLOCK = EAGAIN;
......@@ -151,8 +158,8 @@ pub const Stat = extern struct {
151158 dev: c_uint,
152159 mode: c_ushort,
153160 padding1: u16,
154 uid: c_uint,
155 gid: c_uint,
161 uid: uid_t,
162 gid: gid_t,
156163 rdev: c_uint,
157164 atim: timespec,
158165 mtim: timespec,
......@@ -511,7 +518,7 @@ pub const siginfo_t = extern struct {
511518 si_errno: c_int,
512519 si_code: c_int,
513520 si_pid: c_int,
514 si_uid: c_uint,
521 si_uid: uid_t,
515522 si_status: c_int,
516523 si_addr: ?*c_void,
517524 si_value: union_sigval,
lib/std/os/bits/freebsd.zig+6-2
......@@ -6,8 +6,12 @@
66const std = @import("../../std.zig");
77const maxInt = std.math.maxInt;
88
9// See https://svnweb.freebsd.org/base/head/sys/sys/_types.h?view=co
10// TODO: audit pid_t/mode_t. They should likely be i32 and u16, respectively
911pub const fd_t = c_int;
1012pub const pid_t = c_int;
13pub const uid_t = u32;
14pub const gid_t = u32;
1115pub const mode_t = c_uint;
1216
1317pub const socklen_t = u32;
......@@ -128,8 +132,8 @@ pub const Stat = extern struct {
128132
129133 mode: u16,
130134 __pad0: u16,
131 uid: u32,
132 gid: u32,
135 uid: uid_t,
136 gid: gid_t,
133137 __pad1: u32,
134138 rdev: u64,
135139
lib/std/os/bits/linux.zig+5-5
......@@ -29,7 +29,7 @@ const is_mips = builtin.arch.isMIPS();
2929
3030pub const pid_t = i32;
3131pub const fd_t = i32;
32pub const uid_t = i32;
32pub const uid_t = u32;
3333pub const gid_t = u32;
3434pub const clock_t = isize;
3535
......@@ -846,14 +846,14 @@ pub const SIG_ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize));
846846pub const SIG_DFL = @intToPtr(?Sigaction.sigaction_fn, 0);
847847pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1);
848848
849pub const empty_sigset = [_]u32{0} ** sigset_t.len;
849pub const empty_sigset = [_]u32{0} ** @typeInfo(sigset_t).Array.len;
850850
851851pub const signalfd_siginfo = extern struct {
852852 signo: u32,
853853 errno: i32,
854854 code: i32,
855855 pid: u32,
856 uid: u32,
856 uid: uid_t,
857857 fd: i32,
858858 tid: u32,
859859 band: u32,
......@@ -1491,10 +1491,10 @@ pub const Statx = extern struct {
14911491 nlink: u32,
14921492
14931493 /// User ID of owner
1494 uid: u32,
1494 uid: uid_t,
14951495
14961496 /// Group ID of owner
1497 gid: u32,
1497 gid: gid_t,
14981498
14991499 /// File type and mode
15001500 mode: u16,
lib/std/os/bits/linux/x86_64.zig+3-2
......@@ -7,6 +7,7 @@
77const std = @import("../../../std.zig");
88const pid_t = linux.pid_t;
99const uid_t = linux.uid_t;
10const gid_t = linux.gid_t;
1011const clock_t = linux.clock_t;
1112const stack_t = linux.stack_t;
1213const sigset_t = linux.sigset_t;
......@@ -523,8 +524,8 @@ pub const Stat = extern struct {
523524 nlink: usize,
524525
525526 mode: u32,
526 uid: u32,
527 gid: u32,
527 uid: uid_t,
528 gid: gid_t,
528529 __pad0: u32,
529530 rdev: u64,
530531 size: off_t,
lib/std/os/linux.zig+61-29
......@@ -655,7 +655,7 @@ pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize {
655655 return syscall2(.nanosleep, @ptrToInt(req), @ptrToInt(rem));
656656}
657657
658pub fn setuid(uid: u32) usize {
658pub fn setuid(uid: uid_t) usize {
659659 if (@hasField(SYS, "setuid32")) {
660660 return syscall1(.setuid32, uid);
661661 } else {
......@@ -663,7 +663,7 @@ pub fn setuid(uid: u32) usize {
663663 }
664664}
665665
666pub fn setgid(gid: u32) usize {
666pub fn setgid(gid: gid_t) usize {
667667 if (@hasField(SYS, "setgid32")) {
668668 return syscall1(.setgid32, gid);
669669 } else {
......@@ -671,7 +671,7 @@ pub fn setgid(gid: u32) usize {
671671 }
672672}
673673
674pub fn setreuid(ruid: u32, euid: u32) usize {
674pub fn setreuid(ruid: uid_t, euid: uid_t) usize {
675675 if (@hasField(SYS, "setreuid32")) {
676676 return syscall2(.setreuid32, ruid, euid);
677677 } else {
......@@ -679,7 +679,7 @@ pub fn setreuid(ruid: u32, euid: u32) usize {
679679 }
680680}
681681
682pub fn setregid(rgid: u32, egid: u32) usize {
682pub fn setregid(rgid: gid_t, egid: gid_t) usize {
683683 if (@hasField(SYS, "setregid32")) {
684684 return syscall2(.setregid32, rgid, egid);
685685 } else {
......@@ -687,47 +687,61 @@ pub fn setregid(rgid: u32, egid: u32) usize {
687687 }
688688}
689689
690pub fn getuid() u32 {
690pub fn getuid() uid_t {
691691 if (@hasField(SYS, "getuid32")) {
692 return @as(u32, syscall0(.getuid32));
692 return @as(uid_t, syscall0(.getuid32));
693693 } else {
694 return @as(u32, syscall0(.getuid));
694 return @as(uid_t, syscall0(.getuid));
695695 }
696696}
697697
698pub fn getgid() u32 {
698pub fn getgid() gid_t {
699699 if (@hasField(SYS, "getgid32")) {
700 return @as(u32, syscall0(.getgid32));
700 return @as(gid_t, syscall0(.getgid32));
701701 } else {
702 return @as(u32, syscall0(.getgid));
702 return @as(gid_t, syscall0(.getgid));
703703 }
704704}
705705
706pub fn geteuid() u32 {
706pub fn geteuid() uid_t {
707707 if (@hasField(SYS, "geteuid32")) {
708 return @as(u32, syscall0(.geteuid32));
708 return @as(uid_t, syscall0(.geteuid32));
709709 } else {
710 return @as(u32, syscall0(.geteuid));
710 return @as(uid_t, syscall0(.geteuid));
711711 }
712712}
713713
714pub fn getegid() u32 {
714pub fn getegid() gid_t {
715715 if (@hasField(SYS, "getegid32")) {
716 return @as(u32, syscall0(.getegid32));
716 return @as(gid_t, syscall0(.getegid32));
717717 } else {
718 return @as(u32, syscall0(.getegid));
718 return @as(gid_t, syscall0(.getegid));
719719 }
720720}
721721
722pub fn seteuid(euid: u32) usize {
723 return setreuid(std.math.maxInt(u32), euid);
722pub fn seteuid(euid: uid_t) usize {
723 // We use setresuid here instead of setreuid to ensure that the saved uid
724 // is not changed. This is what musl and recent glibc versions do as well.
725 //
726 // The setresuid(2) man page says that if -1 is passed the corresponding
727 // id will not be changed. Since uid_t is unsigned, this wraps around to the
728 // max value in C.
729 comptime assert(@typeInfo(uid_t) == .Int and !@typeInfo(uid_t).Int.is_signed);
730 return setresuid(std.math.maxInt(uid_t), euid, std.math.maxInt(uid_t));
724731}
725732
726pub fn setegid(egid: u32) usize {
727 return setregid(std.math.maxInt(u32), egid);
733pub fn setegid(egid: gid_t) usize {
734 // We use setresgid here instead of setregid to ensure that the saved uid
735 // is not changed. This is what musl and recent glibc versions do as well.
736 //
737 // The setresgid(2) man page says that if -1 is passed the corresponding
738 // id will not be changed. Since gid_t is unsigned, this wraps around to the
739 // max value in C.
740 comptime assert(@typeInfo(uid_t) == .Int and !@typeInfo(uid_t).Int.is_signed);
741 return setresgid(std.math.maxInt(gid_t), egid, std.math.maxInt(gid_t));
728742}
729743
730pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize {
744pub fn getresuid(ruid: *uid_t, euid: *uid_t, suid: *uid_t) usize {
731745 if (@hasField(SYS, "getresuid32")) {
732746 return syscall3(.getresuid32, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid));
733747 } else {
......@@ -735,7 +749,7 @@ pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize {
735749 }
736750}
737751
738pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize {
752pub fn getresgid(rgid: *gid_t, egid: *gid_t, sgid: *gid_t) usize {
739753 if (@hasField(SYS, "getresgid32")) {
740754 return syscall3(.getresgid32, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid));
741755 } else {
......@@ -743,7 +757,7 @@ pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize {
743757 }
744758}
745759
746pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {
760pub fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) usize {
747761 if (@hasField(SYS, "setresuid32")) {
748762 return syscall3(.setresuid32, ruid, euid, suid);
749763 } else {
......@@ -751,7 +765,7 @@ pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {
751765 }
752766}
753767
754pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {
768pub fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) usize {
755769 if (@hasField(SYS, "setresgid32")) {
756770 return syscall3(.setresgid32, rgid, egid, sgid);
757771 } else {
......@@ -759,7 +773,7 @@ pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {
759773 }
760774}
761775
762pub fn getgroups(size: usize, list: *u32) usize {
776pub fn getgroups(size: usize, list: *gid_t) usize {
763777 if (@hasField(SYS, "getgroups32")) {
764778 return syscall2(.getgroups32, size, @ptrToInt(list));
765779 } else {
......@@ -767,7 +781,7 @@ pub fn getgroups(size: usize, list: *u32) usize {
767781 }
768782}
769783
770pub fn setgroups(size: usize, list: *const u32) usize {
784pub fn setgroups(size: usize, list: *const gid_t) usize {
771785 if (@hasField(SYS, "setgroups32")) {
772786 return syscall2(.setgroups32, size, @ptrToInt(list));
773787 } else {
......@@ -815,17 +829,19 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti
815829 return 0;
816830}
817831
832const usize_bits = @typeInfo(usize).Int.bits;
833
818834pub fn sigaddset(set: *sigset_t, sig: u6) void {
819835 const s = sig - 1;
820836 // shift in musl: s&8*sizeof *set->__bits-1
821 const shift = @intCast(u5, s & (usize.bit_count - 1));
837 const shift = @intCast(u5, s & (usize_bits - 1));
822838 const val = @intCast(u32, 1) << shift;
823 (set.*)[@intCast(usize, s) / usize.bit_count] |= val;
839 (set.*)[@intCast(usize, s) / usize_bits] |= val;
824840}
825841
826842pub fn sigismember(set: *const sigset_t, sig: u6) bool {
827843 const s = sig - 1;
828 return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0;
844 return ((set.*)[@intCast(usize, s) / usize_bits] & (@intCast(usize, 1) << (s & (usize_bits - 1)))) != 0;
829845}
830846
831847pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
......@@ -1226,6 +1242,22 @@ pub fn bpf(cmd: BPF.Cmd, attr: *BPF.Attr, size: u32) usize {
12261242 return syscall3(.bpf, @enumToInt(cmd), @ptrToInt(attr), size);
12271243}
12281244
1245pub fn sync() void {
1246 _ = syscall0(.sync);
1247}
1248
1249pub fn syncfs(fd: fd_t) usize {
1250 return syscall1(.syncfs, @bitCast(usize, @as(isize, fd)));
1251}
1252
1253pub fn fsync(fd: fd_t) usize {
1254 return syscall1(.fsync, @bitCast(usize, @as(isize, fd)));
1255}
1256
1257pub fn fdatasync(fd: fd_t) usize {
1258 return syscall1(.fdatasync, @bitCast(usize, @as(isize, fd)));
1259}
1260
12291261test "" {
12301262 if (builtin.os.tag == .linux) {
12311263 _ = @import("linux/test.zig");
lib/std/os/linux/bpf.zig+758-68
......@@ -3,9 +3,13 @@
33// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
6usingnamespace std.os;
6usingnamespace std.os.linux;
77const std = @import("../../std.zig");
8const errno = getErrno;
9const unexpectedErrno = std.os.unexpectedErrno;
810const expectEqual = std.testing.expectEqual;
11const expectError = std.testing.expectError;
12const expect = std.testing.expect;
913
1014// instruction classes
1115pub const LD = 0x00;
......@@ -62,6 +66,7 @@ pub const MAXINSNS = 4096;
6266// instruction classes
6367/// jmp mode in word width
6468pub const JMP32 = 0x06;
69
6570/// alu mode in double word width
6671pub const ALU64 = 0x07;
6772
......@@ -72,14 +77,17 @@ pub const XADD = 0xc0;
7277// alu/jmp fields
7378/// mov reg to reg
7479pub const MOV = 0xb0;
80
7581/// sign extending arithmetic shift right */
7682pub const ARSH = 0xc0;
7783
7884// change endianness of a register
7985/// flags for endianness conversion:
8086pub const END = 0xd0;
87
8188/// convert to little-endian */
8289pub const TO_LE = 0x00;
90
8391/// convert to big-endian
8492pub const TO_BE = 0x08;
8593pub const FROM_LE = TO_LE;
......@@ -88,29 +96,39 @@ pub const FROM_BE = TO_BE;
8896// jmp encodings
8997/// jump != *
9098pub const JNE = 0x50;
99
91100/// LT is unsigned, '<'
92101pub const JLT = 0xa0;
102
93103/// LE is unsigned, '<=' *
94104pub const JLE = 0xb0;
105
95106/// SGT is signed '>', GT in x86
96107pub const JSGT = 0x60;
108
97109/// SGE is signed '>=', GE in x86
98110pub const JSGE = 0x70;
111
99112/// SLT is signed, '<'
100113pub const JSLT = 0xc0;
114
101115/// SLE is signed, '<='
102116pub const JSLE = 0xd0;
117
103118/// function call
104119pub const CALL = 0x80;
120
105121/// function return
106122pub const EXIT = 0x90;
107123
108124/// Flag for prog_attach command. If a sub-cgroup installs some bpf program, the
109125/// program in this cgroup yields to sub-cgroup program.
110126pub const F_ALLOW_OVERRIDE = 0x1;
127
111128/// Flag for prog_attach command. If a sub-cgroup installs some bpf program,
112129/// that cgroup program gets run in addition to the program in this cgroup.
113130pub const F_ALLOW_MULTI = 0x2;
131
114132/// Flag for prog_attach command.
115133pub const F_REPLACE = 0x4;
116134
......@@ -164,47 +182,61 @@ pub const PSEUDO_CALL = 1;
164182
165183/// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing
166184pub const ANY = 0;
185
167186/// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist
168187pub const NOEXIST = 1;
188
169189/// flag for BPF_MAP_UPDATE_ELEM command. update existing element
170190pub const EXIST = 2;
191
171192/// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update
172193pub const F_LOCK = 4;
173194
174195/// flag for BPF_MAP_CREATE command */
175196pub const BPF_F_NO_PREALLOC = 0x1;
197
176198/// flag for BPF_MAP_CREATE command. Instead of having one common LRU list in
177199/// the BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list which can
178200/// scale and perform better. Note, the LRU nodes (including free nodes) cannot
179201/// be moved across different LRU lists.
180202pub const BPF_F_NO_COMMON_LRU = 0x2;
203
181204/// flag for BPF_MAP_CREATE command. Specify numa node during map creation
182205pub const BPF_F_NUMA_NODE = 0x4;
206
183207/// flag for BPF_MAP_CREATE command. Flags for BPF object read access from
184208/// syscall side
185209pub const BPF_F_RDONLY = 0x8;
210
186211/// flag for BPF_MAP_CREATE command. Flags for BPF object write access from
187212/// syscall side
188213pub const BPF_F_WRONLY = 0x10;
214
189215/// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset
190216/// instead of pointer
191217pub const BPF_F_STACK_BUILD_ID = 0x20;
218
192219/// flag for BPF_MAP_CREATE command. Zero-initialize hash function seed. This
193220/// should only be used for testing.
194221pub const BPF_F_ZERO_SEED = 0x40;
222
195223/// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program
196224/// side.
197225pub const BPF_F_RDONLY_PROG = 0x80;
226
198227/// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program
199228/// side.
200229pub const BPF_F_WRONLY_PROG = 0x100;
230
201231/// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted
202232/// socket
203233pub const BPF_F_CLONE = 0x200;
234
204235/// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map
205236pub const BPF_F_MMAPABLE = 0x400;
206237
207/// These values correspond to "syscalls" within the BPF program's environment
238/// These values correspond to "syscalls" within the BPF program's environment,
239/// each one is documented in std.os.linux.BPF.kern
208240pub const Helper = enum(i32) {
209241 unspec,
210242 map_lookup_elem,
......@@ -325,9 +357,34 @@ pub const Helper = enum(i32) {
325357 tcp_send_ack,
326358 send_signal_thread,
327359 jiffies64,
360 read_branch_records,
361 get_ns_current_pid_tgid,
362 xdp_output,
363 get_netns_cookie,
364 get_current_ancestor_cgroup_id,
365 sk_assign,
366 ktime_get_boot_ns,
367 seq_printf,
368 seq_write,
369 sk_cgroup_id,
370 sk_ancestor_cgroup_id,
371 ringbuf_output,
372 ringbuf_reserve,
373 ringbuf_submit,
374 ringbuf_discard,
375 ringbuf_query,
376 csum_level,
377 skc_to_tcp6_sock,
378 skc_to_tcp_sock,
379 skc_to_tcp_timewait_sock,
380 skc_to_tcp_request_sock,
381 skc_to_udp6_sock,
382 get_task_stack,
328383 _,
329384};
330385
386// TODO: determine that this is the expected bit layout for both little and big
387// endian systems
331388/// a single BPF instruction
332389pub const Insn = packed struct {
333390 code: u8,
......@@ -340,19 +397,30 @@ pub const Insn = packed struct {
340397 /// frame
341398 pub const Reg = packed enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 };
342399 const Source = packed enum(u1) { reg, imm };
400
401 const Mode = packed enum(u8) {
402 imm = IMM,
403 abs = ABS,
404 ind = IND,
405 mem = MEM,
406 len = LEN,
407 msh = MSH,
408 };
409
343410 const AluOp = packed enum(u8) {
344411 add = ADD,
345412 sub = SUB,
346413 mul = MUL,
347414 div = DIV,
348 op_or = OR,
349 op_and = AND,
415 alu_or = OR,
416 alu_and = AND,
350417 lsh = LSH,
351418 rsh = RSH,
352419 neg = NEG,
353420 mod = MOD,
354421 xor = XOR,
355422 mov = MOV,
423 arsh = ARSH,
356424 };
357425
358426 pub const Size = packed enum(u8) {
......@@ -368,6 +436,13 @@ pub const Insn = packed struct {
368436 jgt = JGT,
369437 jge = JGE,
370438 jset = JSET,
439 jlt = JLT,
440 jle = JLE,
441 jne = JNE,
442 jsgt = JSGT,
443 jsge = JSGE,
444 jslt = JSLT,
445 jsle = JSLE,
371446 };
372447
373448 const ImmOrReg = union(Source) {
......@@ -419,22 +494,100 @@ pub const Insn = packed struct {
419494 return alu(64, .add, dst, src);
420495 }
421496
497 pub fn sub(dst: Reg, src: anytype) Insn {
498 return alu(64, .sub, dst, src);
499 }
500
501 pub fn mul(dst: Reg, src: anytype) Insn {
502 return alu(64, .mul, dst, src);
503 }
504
505 pub fn div(dst: Reg, src: anytype) Insn {
506 return alu(64, .div, dst, src);
507 }
508
509 pub fn alu_or(dst: Reg, src: anytype) Insn {
510 return alu(64, .alu_or, dst, src);
511 }
512
513 pub fn alu_and(dst: Reg, src: anytype) Insn {
514 return alu(64, .alu_and, dst, src);
515 }
516
517 pub fn lsh(dst: Reg, src: anytype) Insn {
518 return alu(64, .lsh, dst, src);
519 }
520
521 pub fn rsh(dst: Reg, src: anytype) Insn {
522 return alu(64, .rsh, dst, src);
523 }
524
525 pub fn neg(dst: Reg) Insn {
526 return alu(64, .neg, dst, 0);
527 }
528
529 pub fn mod(dst: Reg, src: anytype) Insn {
530 return alu(64, .mod, dst, src);
531 }
532
533 pub fn xor(dst: Reg, src: anytype) Insn {
534 return alu(64, .xor, dst, src);
535 }
536
537 pub fn arsh(dst: Reg, src: anytype) Insn {
538 return alu(64, .arsh, dst, src);
539 }
540
422541 fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn {
423542 return imm_reg(JMP | @enumToInt(op), dst, src, off);
424543 }
425544
545 pub fn ja(off: i16) Insn {
546 return jmp(.ja, .r0, 0, off);
547 }
548
426549 pub fn jeq(dst: Reg, src: anytype, off: i16) Insn {
427550 return jmp(.jeq, dst, src, off);
428551 }
429552
430 pub fn stx_mem(size: Size, dst: Reg, src: Reg, off: i16) Insn {
431 return Insn{
432 .code = STX | @enumToInt(size) | MEM,
433 .dst = @enumToInt(dst),
434 .src = @enumToInt(src),
435 .off = off,
436 .imm = 0,
437 };
553 pub fn jgt(dst: Reg, src: anytype, off: i16) Insn {
554 return jmp(.jgt, dst, src, off);
555 }
556
557 pub fn jge(dst: Reg, src: anytype, off: i16) Insn {
558 return jmp(.jge, dst, src, off);
559 }
560
561 pub fn jlt(dst: Reg, src: anytype, off: i16) Insn {
562 return jmp(.jlt, dst, src, off);
563 }
564
565 pub fn jle(dst: Reg, src: anytype, off: i16) Insn {
566 return jmp(.jle, dst, src, off);
567 }
568
569 pub fn jset(dst: Reg, src: anytype, off: i16) Insn {
570 return jmp(.jset, dst, src, off);
571 }
572
573 pub fn jne(dst: Reg, src: anytype, off: i16) Insn {
574 return jmp(.jne, dst, src, off);
575 }
576
577 pub fn jsgt(dst: Reg, src: anytype, off: i16) Insn {
578 return jmp(.jsgt, dst, src, off);
579 }
580
581 pub fn jsge(dst: Reg, src: anytype, off: i16) Insn {
582 return jmp(.jsge, dst, src, off);
583 }
584
585 pub fn jslt(dst: Reg, src: anytype, off: i16) Insn {
586 return jmp(.jslt, dst, src, off);
587 }
588
589 pub fn jsle(dst: Reg, src: anytype, off: i16) Insn {
590 return jmp(.jsle, dst, src, off);
438591 }
439592
440593 pub fn xadd(dst: Reg, src: Reg) Insn {
......@@ -447,17 +600,34 @@ pub const Insn = packed struct {
447600 };
448601 }
449602
450 /// direct packet access, R0 = *(uint *)(skb->data + imm32)
451 pub fn ld_abs(size: Size, imm: i32) Insn {
603 fn ld(mode: Mode, size: Size, dst: Reg, src: Reg, imm: i32) Insn {
452604 return Insn{
453 .code = LD | @enumToInt(size) | ABS,
454 .dst = 0,
455 .src = 0,
605 .code = @enumToInt(mode) | @enumToInt(size) | LD,
606 .dst = @enumToInt(dst),
607 .src = @enumToInt(src),
456608 .off = 0,
457609 .imm = imm,
458610 };
459611 }
460612
613 pub fn ld_abs(size: Size, dst: Reg, src: Reg, imm: i32) Insn {
614 return ld(.abs, size, dst, src, imm);
615 }
616
617 pub fn ld_ind(size: Size, dst: Reg, src: Reg, imm: i32) Insn {
618 return ld(.ind, size, dst, src, imm);
619 }
620
621 pub fn ldx(size: Size, dst: Reg, src: Reg, off: i16) Insn {
622 return Insn{
623 .code = MEM | @enumToInt(size) | LDX,
624 .dst = @enumToInt(dst),
625 .src = @enumToInt(src),
626 .off = off,
627 .imm = 0,
628 };
629 }
630
461631 fn ld_imm_impl1(dst: Reg, src: Reg, imm: u64) Insn {
462632 return Insn{
463633 .code = LD | DW | IMM,
......@@ -478,6 +648,14 @@ pub const Insn = packed struct {
478648 };
479649 }
480650
651 pub fn ld_dw1(dst: Reg, imm: u64) Insn {
652 return ld_imm_impl1(dst, .r0, imm);
653 }
654
655 pub fn ld_dw2(imm: u64) Insn {
656 return ld_imm_impl2(imm);
657 }
658
481659 pub fn ld_map_fd1(dst: Reg, map_fd: fd_t) Insn {
482660 return ld_imm_impl1(dst, @intToEnum(Reg, PSEUDO_MAP_FD), @intCast(u64, map_fd));
483661 }
......@@ -486,6 +664,53 @@ pub const Insn = packed struct {
486664 return ld_imm_impl2(@intCast(u64, map_fd));
487665 }
488666
667 pub fn st(comptime size: Size, dst: Reg, off: i16, imm: i32) Insn {
668 if (size == .double_word) @compileError("TODO: need to determine how to correctly handle double words");
669 return Insn{
670 .code = MEM | @enumToInt(size) | ST,
671 .dst = @enumToInt(dst),
672 .src = 0,
673 .off = off,
674 .imm = imm,
675 };
676 }
677
678 pub fn stx(size: Size, dst: Reg, off: i16, src: Reg) Insn {
679 return Insn{
680 .code = MEM | @enumToInt(size) | STX,
681 .dst = @enumToInt(dst),
682 .src = @enumToInt(src),
683 .off = off,
684 .imm = 0,
685 };
686 }
687
688 fn endian_swap(endian: std.builtin.Endian, comptime size: Size, dst: Reg) Insn {
689 return Insn{
690 .code = switch (endian) {
691 .Big => 0xdc,
692 .Little => 0xd4,
693 },
694 .dst = @enumToInt(dst),
695 .src = 0,
696 .off = 0,
697 .imm = switch (size) {
698 .byte => @compileError("can't swap a single byte"),
699 .half_word => 16,
700 .word => 32,
701 .double_word => 64,
702 },
703 };
704 }
705
706 pub fn le(comptime size: Size, dst: Reg) Insn {
707 return endian_swap(.Little, size, dst);
708 }
709
710 pub fn be(comptime size: Size, dst: Reg) Insn {
711 return endian_swap(.Big, size, dst);
712 }
713
489714 pub fn call(helper: Helper) Insn {
490715 return Insn{
491716 .code = JMP | CALL,
......@@ -508,95 +733,242 @@ pub const Insn = packed struct {
508733 }
509734};
510735
511fn expect_insn(insn: Insn, val: u64) void {
512 expectEqual(@bitCast(u64, insn), val);
513}
514
515736test "insn bitsize" {
516737 expectEqual(@bitSizeOf(Insn), 64);
517738}
518739
519// mov instructions
520test "mov imm" {
521 expect_insn(Insn.mov(.r1, 1), 0x00000001000001b7);
522}
523
524test "mov reg" {
525 expect_insn(Insn.mov(.r6, .r1), 0x00000000000016bf);
526}
527
528// alu instructions
529test "add imm" {
530 expect_insn(Insn.add(.r2, -4), 0xfffffffc00000207);
740fn expect_opcode(code: u8, insn: Insn) void {
741 expectEqual(code, insn.code);
531742}
532743
533// ld instructions
534test "ld_abs" {
535 expect_insn(Insn.ld_abs(.byte, 42), 0x0000002a00000030);
536}
537
538test "ld_map_fd" {
539 expect_insn(Insn.ld_map_fd1(.r1, 42), 0x0000002a00001118);
540 expect_insn(Insn.ld_map_fd2(42), 0x0000000000000000);
541}
542
543// st instructions
544test "stx_mem" {
545 expect_insn(Insn.stx_mem(.word, .r10, .r0, -4), 0x00000000fffc0a63);
546}
547
548test "xadd" {
549 expect_insn(Insn.xadd(.r0, .r1), 0x00000000000010db);
550}
551
552// jmp instructions
553test "jeq imm" {
554 expect_insn(Insn.jeq(.r0, 0, 2), 0x0000000000020015);
555}
556
557// other instructions
558test "call" {
559 expect_insn(Insn.call(.map_lookup_elem), 0x0000000100000085);
560}
561
562test "exit" {
563 expect_insn(Insn.exit(), 0x0000000000000095);
744// The opcodes were grabbed from https://github.com/iovisor/bpf-docs/blob/master/eBPF.md
745test "opcodes" {
746 // instructions that have a name that end with 1 or 2 are consecutive for
747 // loading 64-bit immediates (imm is only 32 bits wide)
748
749 // alu instructions
750 expect_opcode(0x07, Insn.add(.r1, 0));
751 expect_opcode(0x0f, Insn.add(.r1, .r2));
752 expect_opcode(0x17, Insn.sub(.r1, 0));
753 expect_opcode(0x1f, Insn.sub(.r1, .r2));
754 expect_opcode(0x27, Insn.mul(.r1, 0));
755 expect_opcode(0x2f, Insn.mul(.r1, .r2));
756 expect_opcode(0x37, Insn.div(.r1, 0));
757 expect_opcode(0x3f, Insn.div(.r1, .r2));
758 expect_opcode(0x47, Insn.alu_or(.r1, 0));
759 expect_opcode(0x4f, Insn.alu_or(.r1, .r2));
760 expect_opcode(0x57, Insn.alu_and(.r1, 0));
761 expect_opcode(0x5f, Insn.alu_and(.r1, .r2));
762 expect_opcode(0x67, Insn.lsh(.r1, 0));
763 expect_opcode(0x6f, Insn.lsh(.r1, .r2));
764 expect_opcode(0x77, Insn.rsh(.r1, 0));
765 expect_opcode(0x7f, Insn.rsh(.r1, .r2));
766 expect_opcode(0x87, Insn.neg(.r1));
767 expect_opcode(0x97, Insn.mod(.r1, 0));
768 expect_opcode(0x9f, Insn.mod(.r1, .r2));
769 expect_opcode(0xa7, Insn.xor(.r1, 0));
770 expect_opcode(0xaf, Insn.xor(.r1, .r2));
771 expect_opcode(0xb7, Insn.mov(.r1, 0));
772 expect_opcode(0xbf, Insn.mov(.r1, .r2));
773 expect_opcode(0xc7, Insn.arsh(.r1, 0));
774 expect_opcode(0xcf, Insn.arsh(.r1, .r2));
775
776 // atomic instructions: might be more of these not documented in the wild
777 expect_opcode(0xdb, Insn.xadd(.r1, .r2));
778
779 // TODO: byteswap instructions
780 expect_opcode(0xd4, Insn.le(.half_word, .r1));
781 expectEqual(@intCast(i32, 16), Insn.le(.half_word, .r1).imm);
782 expect_opcode(0xd4, Insn.le(.word, .r1));
783 expectEqual(@intCast(i32, 32), Insn.le(.word, .r1).imm);
784 expect_opcode(0xd4, Insn.le(.double_word, .r1));
785 expectEqual(@intCast(i32, 64), Insn.le(.double_word, .r1).imm);
786 expect_opcode(0xdc, Insn.be(.half_word, .r1));
787 expectEqual(@intCast(i32, 16), Insn.be(.half_word, .r1).imm);
788 expect_opcode(0xdc, Insn.be(.word, .r1));
789 expectEqual(@intCast(i32, 32), Insn.be(.word, .r1).imm);
790 expect_opcode(0xdc, Insn.be(.double_word, .r1));
791 expectEqual(@intCast(i32, 64), Insn.be(.double_word, .r1).imm);
792
793 // memory instructions
794 expect_opcode(0x18, Insn.ld_dw1(.r1, 0));
795 expect_opcode(0x00, Insn.ld_dw2(0));
796
797 // loading a map fd
798 expect_opcode(0x18, Insn.ld_map_fd1(.r1, 0));
799 expectEqual(@intCast(u4, PSEUDO_MAP_FD), Insn.ld_map_fd1(.r1, 0).src);
800 expect_opcode(0x00, Insn.ld_map_fd2(0));
801
802 expect_opcode(0x38, Insn.ld_abs(.double_word, .r1, .r2, 0));
803 expect_opcode(0x20, Insn.ld_abs(.word, .r1, .r2, 0));
804 expect_opcode(0x28, Insn.ld_abs(.half_word, .r1, .r2, 0));
805 expect_opcode(0x30, Insn.ld_abs(.byte, .r1, .r2, 0));
806
807 expect_opcode(0x58, Insn.ld_ind(.double_word, .r1, .r2, 0));
808 expect_opcode(0x40, Insn.ld_ind(.word, .r1, .r2, 0));
809 expect_opcode(0x48, Insn.ld_ind(.half_word, .r1, .r2, 0));
810 expect_opcode(0x50, Insn.ld_ind(.byte, .r1, .r2, 0));
811
812 expect_opcode(0x79, Insn.ldx(.double_word, .r1, .r2, 0));
813 expect_opcode(0x61, Insn.ldx(.word, .r1, .r2, 0));
814 expect_opcode(0x69, Insn.ldx(.half_word, .r1, .r2, 0));
815 expect_opcode(0x71, Insn.ldx(.byte, .r1, .r2, 0));
816
817 expect_opcode(0x62, Insn.st(.word, .r1, 0, 0));
818 expect_opcode(0x6a, Insn.st(.half_word, .r1, 0, 0));
819 expect_opcode(0x72, Insn.st(.byte, .r1, 0, 0));
820
821 expect_opcode(0x63, Insn.stx(.word, .r1, 0, .r2));
822 expect_opcode(0x6b, Insn.stx(.half_word, .r1, 0, .r2));
823 expect_opcode(0x73, Insn.stx(.byte, .r1, 0, .r2));
824 expect_opcode(0x7b, Insn.stx(.double_word, .r1, 0, .r2));
825
826 // branch instructions
827 expect_opcode(0x05, Insn.ja(0));
828 expect_opcode(0x15, Insn.jeq(.r1, 0, 0));
829 expect_opcode(0x1d, Insn.jeq(.r1, .r2, 0));
830 expect_opcode(0x25, Insn.jgt(.r1, 0, 0));
831 expect_opcode(0x2d, Insn.jgt(.r1, .r2, 0));
832 expect_opcode(0x35, Insn.jge(.r1, 0, 0));
833 expect_opcode(0x3d, Insn.jge(.r1, .r2, 0));
834 expect_opcode(0xa5, Insn.jlt(.r1, 0, 0));
835 expect_opcode(0xad, Insn.jlt(.r1, .r2, 0));
836 expect_opcode(0xb5, Insn.jle(.r1, 0, 0));
837 expect_opcode(0xbd, Insn.jle(.r1, .r2, 0));
838 expect_opcode(0x45, Insn.jset(.r1, 0, 0));
839 expect_opcode(0x4d, Insn.jset(.r1, .r2, 0));
840 expect_opcode(0x55, Insn.jne(.r1, 0, 0));
841 expect_opcode(0x5d, Insn.jne(.r1, .r2, 0));
842 expect_opcode(0x65, Insn.jsgt(.r1, 0, 0));
843 expect_opcode(0x6d, Insn.jsgt(.r1, .r2, 0));
844 expect_opcode(0x75, Insn.jsge(.r1, 0, 0));
845 expect_opcode(0x7d, Insn.jsge(.r1, .r2, 0));
846 expect_opcode(0xc5, Insn.jslt(.r1, 0, 0));
847 expect_opcode(0xcd, Insn.jslt(.r1, .r2, 0));
848 expect_opcode(0xd5, Insn.jsle(.r1, 0, 0));
849 expect_opcode(0xdd, Insn.jsle(.r1, .r2, 0));
850 expect_opcode(0x85, Insn.call(.unspec));
851 expect_opcode(0x95, Insn.exit());
564852}
565853
566854pub const Cmd = extern enum(usize) {
855 /// Create a map and return a file descriptor that refers to the map. The
856 /// close-on-exec file descriptor flag is automatically enabled for the new
857 /// file descriptor.
858 ///
859 /// uses MapCreateAttr
567860 map_create,
861
862 /// Look up an element by key in a specified map and return its value.
863 ///
864 /// uses MapElemAttr
568865 map_lookup_elem,
866
867 /// Create or update an element (key/value pair) in a specified map.
868 ///
869 /// uses MapElemAttr
569870 map_update_elem,
871
872 /// Look up and delete an element by key in a specified map.
873 ///
874 /// uses MapElemAttr
570875 map_delete_elem,
876
877 /// Look up an element by key in a specified map and return the key of the
878 /// next element.
571879 map_get_next_key,
880
881 /// Verify and load an eBPF program, returning a new file descriptor
882 /// associated with the program. The close-on-exec file descriptor flag
883 /// is automatically enabled for the new file descriptor.
884 ///
885 /// uses ProgLoadAttr
572886 prog_load,
887
888 /// Pin a map or eBPF program to a path within the minimal BPF filesystem
889 ///
890 /// uses ObjAttr
573891 obj_pin,
892
893 /// Get the file descriptor of a BPF object pinned to a certain path
894 ///
895 /// uses ObjAttr
574896 obj_get,
897
898 /// uses ProgAttachAttr
575899 prog_attach,
900
901 /// uses ProgAttachAttr
576902 prog_detach,
903
904 /// uses TestRunAttr
577905 prog_test_run,
906
907 /// uses GetIdAttr
578908 prog_get_next_id,
909
910 /// uses GetIdAttr
579911 map_get_next_id,
912
913 /// uses GetIdAttr
580914 prog_get_fd_by_id,
915
916 /// uses GetIdAttr
581917 map_get_fd_by_id,
918
919 /// uses InfoAttr
582920 obj_get_info_by_fd,
921
922 /// uses QueryAttr
583923 prog_query,
924
925 /// uses RawTracepointAttr
584926 raw_tracepoint_open,
927
928 /// uses BtfLoadAttr
585929 btf_load,
930
931 /// uses GetIdAttr
586932 btf_get_fd_by_id,
933
934 /// uses TaskFdQueryAttr
587935 task_fd_query,
936
937 /// uses MapElemAttr
588938 map_lookup_and_delete_elem,
589939 map_freeze,
940
941 /// uses GetIdAttr
590942 btf_get_next_id,
943
944 /// uses MapBatchAttr
591945 map_lookup_batch,
946
947 /// uses MapBatchAttr
592948 map_lookup_and_delete_batch,
949
950 /// uses MapBatchAttr
593951 map_update_batch,
952
953 /// uses MapBatchAttr
594954 map_delete_batch,
955
956 /// uses LinkCreateAttr
595957 link_create,
958
959 /// uses LinkUpdateAttr
596960 link_update,
961
962 /// uses GetIdAttr
597963 link_get_fd_by_id,
964
965 /// uses GetIdAttr
598966 link_get_next_id,
967
968 /// uses EnableStatsAttr
599969 enable_stats,
970
971 /// uses IterCreateAttr
600972 iter_create,
601973 link_detach,
602974 _,
......@@ -630,42 +1002,138 @@ pub const MapType = extern enum(u32) {
6301002 sk_storage,
6311003 devmap_hash,
6321004 struct_ops,
1005
1006 /// An ordered and shared CPU version of perf_event_array. They have
1007 /// similar semantics:
1008 /// - variable length records
1009 /// - no blocking: when full, reservation fails
1010 /// - memory mappable for ease and speed
1011 /// - epoll notifications for new data, but can busy poll
1012 ///
1013 /// Ringbufs give BPF programs two sets of APIs:
1014 /// - ringbuf_output() allows copy data from one place to a ring
1015 /// buffer, similar to bpf_perf_event_output()
1016 /// - ringbuf_reserve()/ringbuf_commit()/ringbuf_discard() split the
1017 /// process into two steps. First a fixed amount of space is reserved,
1018 /// if that is successful then the program gets a pointer to a chunk of
1019 /// memory and can be submitted with commit() or discarded with
1020 /// discard()
1021 ///
1022 /// ringbuf_output() will incurr an extra memory copy, but allows to submit
1023 /// records of the length that's not known beforehand, and is an easy
1024 /// replacement for perf_event_outptu().
1025 ///
1026 /// ringbuf_reserve() avoids the extra memory copy but requires a known size
1027 /// of memory beforehand.
1028 ///
1029 /// ringbuf_query() allows to query properties of the map, 4 are currently
1030 /// supported:
1031 /// - BPF_RB_AVAIL_DATA: amount of unconsumed data in ringbuf
1032 /// - BPF_RB_RING_SIZE: returns size of ringbuf
1033 /// - BPF_RB_CONS_POS/BPF_RB_PROD_POS returns current logical position
1034 /// of consumer and producer respectively
1035 ///
1036 /// key size: 0
1037 /// value size: 0
1038 /// max entries: size of ringbuf, must be power of 2
6331039 ringbuf,
1040
6341041 _,
6351042};
6361043
6371044pub const ProgType = extern enum(u32) {
6381045 unspec,
1046
1047 /// context type: __sk_buff
6391048 socket_filter,
1049
1050 /// context type: bpf_user_pt_regs_t
6401051 kprobe,
1052
1053 /// context type: __sk_buff
6411054 sched_cls,
1055
1056 /// context type: __sk_buff
6421057 sched_act,
1058
1059 /// context type: u64
6431060 tracepoint,
1061
1062 /// context type: xdp_md
6441063 xdp,
1064
1065 /// context type: bpf_perf_event_data
6451066 perf_event,
1067
1068 /// context type: __sk_buff
6461069 cgroup_skb,
1070
1071 /// context type: bpf_sock
6471072 cgroup_sock,
1073
1074 /// context type: __sk_buff
6481075 lwt_in,
1076
1077 /// context type: __sk_buff
6491078 lwt_out,
1079
1080 /// context type: __sk_buff
6501081 lwt_xmit,
1082
1083 /// context type: bpf_sock_ops
6511084 sock_ops,
1085
1086 /// context type: __sk_buff
6521087 sk_skb,
1088
1089 /// context type: bpf_cgroup_dev_ctx
6531090 cgroup_device,
1091
1092 /// context type: sk_msg_md
6541093 sk_msg,
1094
1095 /// context type: bpf_raw_tracepoint_args
6551096 raw_tracepoint,
1097
1098 /// context type: bpf_sock_addr
6561099 cgroup_sock_addr,
1100
1101 /// context type: __sk_buff
6571102 lwt_seg6local,
1103
1104 /// context type: u32
6581105 lirc_mode2,
1106
1107 /// context type: sk_reuseport_md
6591108 sk_reuseport,
1109
1110 /// context type: __sk_buff
6601111 flow_dissector,
1112
1113 /// context type: bpf_sysctl
6611114 cgroup_sysctl,
1115
1116 /// context type: bpf_raw_tracepoint_args
6621117 raw_tracepoint_writable,
1118
1119 /// context type: bpf_sockopt
6631120 cgroup_sockopt,
1121
1122 /// context type: void *
6641123 tracing,
1124
1125 /// context type: void *
6651126 struct_ops,
1127
1128 /// context type: void *
6661129 ext,
1130
1131 /// context type: void *
6671132 lsm,
1133
1134 /// context type: bpf_sk_lookup
6681135 sk_lookup,
1136 _,
6691137};
6701138
6711139pub const AttachType = extern enum(u32) {
......@@ -715,27 +1183,38 @@ const obj_name_len = 16;
7151183pub const MapCreateAttr = extern struct {
7161184 /// one of MapType
7171185 map_type: u32,
1186
7181187 /// size of key in bytes
7191188 key_size: u32,
1189
7201190 /// size of value in bytes
7211191 value_size: u32,
1192
7221193 /// max number of entries in a map
7231194 max_entries: u32,
1195
7241196 /// .map_create related flags
7251197 map_flags: u32,
1198
7261199 /// fd pointing to the inner map
7271200 inner_map_fd: fd_t,
1201
7281202 /// numa node (effective only if MapCreateFlags.numa_node is set)
7291203 numa_node: u32,
7301204 map_name: [obj_name_len]u8,
1205
7311206 /// ifindex of netdev to create on
7321207 map_ifindex: u32,
1208
7331209 /// fd pointing to a BTF type data
7341210 btf_fd: fd_t,
1211
7351212 /// BTF type_id of the key
7361213 btf_key_type_id: u32,
1214
7371215 /// BTF type_id of the value
7381216 bpf_value_type_id: u32,
1217
7391218 /// BTF type_id of a kernel struct stored as the map value
7401219 btf_vmlinux_value_type_id: u32,
7411220};
......@@ -755,10 +1234,12 @@ pub const MapElemAttr = extern struct {
7551234pub const MapBatchAttr = extern struct {
7561235 /// start batch, NULL to start from beginning
7571236 in_batch: u64,
1237
7581238 /// output: next start batch
7591239 out_batch: u64,
7601240 keys: u64,
7611241 values: u64,
1242
7621243 /// input/output:
7631244 /// input: # of key/value elements
7641245 /// output: # of filled elements
......@@ -775,35 +1256,49 @@ pub const ProgLoadAttr = extern struct {
7751256 insn_cnt: u32,
7761257 insns: u64,
7771258 license: u64,
1259
7781260 /// verbosity level of verifier
7791261 log_level: u32,
1262
7801263 /// size of user buffer
7811264 log_size: u32,
1265
7821266 /// user supplied buffer
7831267 log_buf: u64,
1268
7841269 /// not used
7851270 kern_version: u32,
7861271 prog_flags: u32,
7871272 prog_name: [obj_name_len]u8,
788 /// ifindex of netdev to prep for. For some prog types expected attach
789 /// type must be known at load time to verify attach type specific parts
790 /// of prog (context accesses, allowed helpers, etc).
1273
1274 /// ifindex of netdev to prep for.
7911275 prog_ifindex: u32,
1276
1277 /// For some prog types expected attach type must be known at load time to
1278 /// verify attach type specific parts of prog (context accesses, allowed
1279 /// helpers, etc).
7921280 expected_attach_type: u32,
1281
7931282 /// fd pointing to BTF type data
7941283 prog_btf_fd: fd_t,
1284
7951285 /// userspace bpf_func_info size
7961286 func_info_rec_size: u32,
7971287 func_info: u64,
1288
7981289 /// number of bpf_func_info records
7991290 func_info_cnt: u32,
1291
8001292 /// userspace bpf_line_info size
8011293 line_info_rec_size: u32,
8021294 line_info: u64,
1295
8031296 /// number of bpf_line_info records
8041297 line_info_cnt: u32,
1298
8051299 /// in-kernel BTF type id to attach to
8061300 attact_btf_id: u32,
1301
8071302 /// 0 to attach to vmlinux
8081303 attach_prog_id: u32,
8091304};
......@@ -819,29 +1314,36 @@ pub const ObjAttr = extern struct {
8191314pub const ProgAttachAttr = extern struct {
8201315 /// container object to attach to
8211316 target_fd: fd_t,
1317
8221318 /// eBPF program to attach
8231319 attach_bpf_fd: fd_t,
1320
8241321 attach_type: u32,
8251322 attach_flags: u32,
1323
8261324 // TODO: BPF_F_REPLACE flags
8271325 /// previously attached eBPF program to replace if .replace is used
8281326 replace_bpf_fd: fd_t,
8291327};
8301328
8311329/// struct used by Cmd.prog_test_run command
832pub const TestAttr = extern struct {
1330pub const TestRunAttr = extern struct {
8331331 prog_fd: fd_t,
8341332 retval: u32,
1333
8351334 /// input: len of data_in
8361335 data_size_in: u32,
1336
8371337 /// input/output: len of data_out. returns ENOSPC if data_out is too small.
8381338 data_size_out: u32,
8391339 data_in: u64,
8401340 data_out: u64,
8411341 repeat: u32,
8421342 duration: u32,
1343
8431344 /// input: len of ctx_in
8441345 ctx_size_in: u32,
1346
8451347 /// input/output: len of ctx_out. returns ENOSPC if ctx_out is too small.
8461348 ctx_size_out: u32,
8471349 ctx_in: u64,
......@@ -894,26 +1396,35 @@ pub const BtfLoadAttr = extern struct {
8941396 btf_log_level: u32,
8951397};
8961398
1399/// struct used by Cmd.task_fd_query
8971400pub const TaskFdQueryAttr = extern struct {
8981401 /// input: pid
8991402 pid: pid_t,
1403
9001404 /// input: fd
9011405 fd: fd_t,
1406
9021407 /// input: flags
9031408 flags: u32,
1409
9041410 /// input/output: buf len
9051411 buf_len: u32,
1412
9061413 /// input/output:
9071414 /// tp_name for tracepoint
9081415 /// symbol for kprobe
9091416 /// filename for uprobe
9101417 buf: u64,
1418
9111419 /// output: prod_id
9121420 prog_id: u32,
1421
9131422 /// output: BPF_FD_TYPE
9141423 fd_type: u32,
1424
9151425 /// output: probe_offset
9161426 probe_offset: u64,
1427
9171428 /// output: probe_addr
9181429 probe_addr: u64,
9191430};
......@@ -922,9 +1433,11 @@ pub const TaskFdQueryAttr = extern struct {
9221433pub const LinkCreateAttr = extern struct {
9231434 /// eBPF program to attach
9241435 prog_fd: fd_t,
1436
9251437 /// object to attach to
9261438 target_fd: fd_t,
9271439 attach_type: u32,
1440
9281441 /// extra flags
9291442 flags: u32,
9301443};
......@@ -932,10 +1445,13 @@ pub const LinkCreateAttr = extern struct {
9321445/// struct used by Cmd.link_update command
9331446pub const LinkUpdateAttr = extern struct {
9341447 link_fd: fd_t,
1448
9351449 /// new program to update link with
9361450 new_prog_fd: fd_t,
1451
9371452 /// extra flags
9381453 flags: u32,
1454
9391455 /// expected link's program fd, it is specified only if BPF_F_REPLACE is
9401456 /// set in flags
9411457 old_prog_fd: fd_t,
......@@ -952,6 +1468,7 @@ pub const IterCreateAttr = extern struct {
9521468 flags: u32,
9531469};
9541470
1471/// Mega struct that is passed to the bpf() syscall
9551472pub const Attr = extern union {
9561473 map_create: MapCreateAttr,
9571474 map_elem: MapElemAttr,
......@@ -971,3 +1488,176 @@ pub const Attr = extern union {
9711488 enable_stats: EnableStatsAttr,
9721489 iter_create: IterCreateAttr,
9731490};
1491
1492pub const Log = struct {
1493 level: u32,
1494 buf: []u8,
1495};
1496
1497pub fn map_create(map_type: MapType, key_size: u32, value_size: u32, max_entries: u32) !fd_t {
1498 var attr = Attr{
1499 .map_create = std.mem.zeroes(MapCreateAttr),
1500 };
1501
1502 attr.map_create.map_type = @enumToInt(map_type);
1503 attr.map_create.key_size = key_size;
1504 attr.map_create.value_size = value_size;
1505 attr.map_create.max_entries = max_entries;
1506
1507 const rc = bpf(.map_create, &attr, @sizeOf(MapCreateAttr));
1508 return switch (errno(rc)) {
1509 0 => @intCast(fd_t, rc),
1510 EINVAL => error.MapTypeOrAttrInvalid,
1511 ENOMEM => error.SystemResources,
1512 EPERM => error.AccessDenied,
1513 else => |err| unexpectedErrno(rc),
1514 };
1515}
1516
1517test "map_create" {
1518 const map = try map_create(.hash, 4, 4, 32);
1519 defer std.os.close(map);
1520}
1521
1522pub fn map_lookup_elem(fd: fd_t, key: []const u8, value: []u8) !void {
1523 var attr = Attr{
1524 .map_elem = std.mem.zeroes(MapElemAttr),
1525 };
1526
1527 attr.map_elem.map_fd = fd;
1528 attr.map_elem.key = @ptrToInt(key.ptr);
1529 attr.map_elem.result.value = @ptrToInt(value.ptr);
1530
1531 const rc = bpf(.map_lookup_elem, &attr, @sizeOf(MapElemAttr));
1532 switch (errno(rc)) {
1533 0 => return,
1534 EBADF => return error.BadFd,
1535 EFAULT => unreachable,
1536 EINVAL => return error.FieldInAttrNeedsZeroing,
1537 ENOENT => return error.NotFound,
1538 EPERM => return error.AccessDenied,
1539 else => |err| return unexpectedErrno(rc),
1540 }
1541}
1542
1543pub fn map_update_elem(fd: fd_t, key: []const u8, value: []const u8, flags: u64) !void {
1544 var attr = Attr{
1545 .map_elem = std.mem.zeroes(MapElemAttr),
1546 };
1547
1548 attr.map_elem.map_fd = fd;
1549 attr.map_elem.key = @ptrToInt(key.ptr);
1550 attr.map_elem.result = .{ .value = @ptrToInt(value.ptr) };
1551 attr.map_elem.flags = flags;
1552
1553 const rc = bpf(.map_update_elem, &attr, @sizeOf(MapElemAttr));
1554 switch (errno(rc)) {
1555 0 => return,
1556 E2BIG => return error.ReachedMaxEntries,
1557 EBADF => return error.BadFd,
1558 EFAULT => unreachable,
1559 EINVAL => return error.FieldInAttrNeedsZeroing,
1560 ENOMEM => return error.SystemResources,
1561 EPERM => return error.AccessDenied,
1562 else => |err| return unexpectedErrno(err),
1563 }
1564}
1565
1566pub fn map_delete_elem(fd: fd_t, key: []const u8) !void {
1567 var attr = Attr{
1568 .map_elem = std.mem.zeroes(MapElemAttr),
1569 };
1570
1571 attr.map_elem.map_fd = fd;
1572 attr.map_elem.key = @ptrToInt(key.ptr);
1573
1574 const rc = bpf(.map_delete_elem, &attr, @sizeOf(MapElemAttr));
1575 switch (errno(rc)) {
1576 0 => return,
1577 EBADF => return error.BadFd,
1578 EFAULT => unreachable,
1579 EINVAL => return error.FieldInAttrNeedsZeroing,
1580 ENOENT => return error.NotFound,
1581 EPERM => return error.AccessDenied,
1582 else => |err| return unexpectedErrno(err),
1583 }
1584}
1585
1586test "map lookup, update, and delete" {
1587 const key_size = 4;
1588 const value_size = 4;
1589 const map = try map_create(.hash, key_size, value_size, 1);
1590 defer std.os.close(map);
1591
1592 const key = std.mem.zeroes([key_size]u8);
1593 var value = std.mem.zeroes([value_size]u8);
1594
1595 // fails looking up value that doesn't exist
1596 expectError(error.NotFound, map_lookup_elem(map, &key, &value));
1597
1598 // succeed at updating and looking up element
1599 try map_update_elem(map, &key, &value, 0);
1600 try map_lookup_elem(map, &key, &value);
1601
1602 // fails inserting more than max entries
1603 const second_key = [key_size]u8{ 0, 0, 0, 1 };
1604 expectError(error.ReachedMaxEntries, map_update_elem(map, &second_key, &value, 0));
1605
1606 // succeed at deleting an existing elem
1607 try map_delete_elem(map, &key);
1608 expectError(error.NotFound, map_lookup_elem(map, &key, &value));
1609
1610 // fail at deleting a non-existing elem
1611 expectError(error.NotFound, map_delete_elem(map, &key));
1612}
1613
1614pub fn prog_load(
1615 prog_type: ProgType,
1616 insns: []const Insn,
1617 log: ?*Log,
1618 license: []const u8,
1619 kern_version: u32,
1620) !fd_t {
1621 var attr = Attr{
1622 .prog_load = std.mem.zeroes(ProgLoadAttr),
1623 };
1624
1625 attr.prog_load.prog_type = @enumToInt(prog_type);
1626 attr.prog_load.insns = @ptrToInt(insns.ptr);
1627 attr.prog_load.insn_cnt = @intCast(u32, insns.len);
1628 attr.prog_load.license = @ptrToInt(license.ptr);
1629 attr.prog_load.kern_version = kern_version;
1630
1631 if (log) |l| {
1632 attr.prog_load.log_buf = @ptrToInt(l.buf.ptr);
1633 attr.prog_load.log_size = @intCast(u32, l.buf.len);
1634 attr.prog_load.log_level = l.level;
1635 }
1636
1637 const rc = bpf(.prog_load, &attr, @sizeOf(ProgLoadAttr));
1638 return switch (errno(rc)) {
1639 0 => @intCast(fd_t, rc),
1640 EACCES => error.UnsafeProgram,
1641 EFAULT => unreachable,
1642 EINVAL => error.InvalidProgram,
1643 EPERM => error.AccessDenied,
1644 else => |err| unexpectedErrno(err),
1645 };
1646}
1647
1648test "prog_load" {
1649 // this should fail because it does not set r0 before exiting
1650 const bad_prog = [_]Insn{
1651 Insn.exit(),
1652 };
1653
1654 const good_prog = [_]Insn{
1655 Insn.mov(.r0, 0),
1656 Insn.exit(),
1657 };
1658
1659 const prog = try prog_load(.socket_filter, &good_prog, null, "MIT", 0);
1660 defer std.os.close(prog);
1661
1662 expectError(error.UnsafeProgram, prog_load(.socket_filter, &bad_prog, null, "MIT", 0));
1663}
lib/std/os/test.zig+36
......@@ -555,3 +555,39 @@ test "signalfd" {
555555 return error.SkipZigTest;
556556 _ = std.os.signalfd;
557557}
558
559test "sync" {
560 if (builtin.os.tag != .linux)
561 return error.SkipZigTest;
562
563 var tmp = tmpDir(.{});
564 defer tmp.cleanup();
565
566 const test_out_file = "os_tmp_test";
567 const file = try tmp.dir.createFile(test_out_file, .{});
568 defer {
569 file.close();
570 tmp.dir.deleteFile(test_out_file) catch {};
571 }
572
573 os.sync();
574 try os.syncfs(file.handle);
575}
576
577test "fsync" {
578 if (builtin.os.tag != .linux and builtin.os.tag != .windows)
579 return error.SkipZigTest;
580
581 var tmp = tmpDir(.{});
582 defer tmp.cleanup();
583
584 const test_out_file = "os_tmp_test";
585 const file = try tmp.dir.createFile(test_out_file, .{});
586 defer {
587 file.close();
588 tmp.dir.deleteFile(test_out_file) catch {};
589 }
590
591 try os.fsync(file.handle);
592 try os.fdatasync(file.handle);
593}
lib/std/os/windows/kernel32.zig+2
......@@ -287,3 +287,5 @@ pub extern "kernel32" fn K32GetWsChangesEx(hProcess: HANDLE, lpWatchInfoEx: PPSA
287287pub extern "kernel32" fn K32InitializeProcessForWsWatch(hProcess: HANDLE) callconv(.Stdcall) BOOL;
288288pub extern "kernel32" fn K32QueryWorkingSet(hProcess: HANDLE, pv: PVOID, cb: DWORD) callconv(.Stdcall) BOOL;
289289pub extern "kernel32" fn K32QueryWorkingSetEx(hProcess: HANDLE, pv: PVOID, cb: DWORD) callconv(.Stdcall) BOOL;
290
291pub extern "kernel32" fn FlushFileBuffers(hFile: HANDLE) callconv(.Stdcall) BOOL;
lib/std/os/windows/ws2_32.zig+1-1
......@@ -12,7 +12,7 @@ pub const SOCKET_ERROR = -1;
1212pub const WSADESCRIPTION_LEN = 256;
1313pub const WSASYS_STATUS_LEN = 128;
1414
15pub const WSADATA = if (usize.bit_count == u64.bit_count)
15pub const WSADATA = if (@sizeOf(usize) == @sizeOf(u64))
1616 extern struct {
1717 wVersion: WORD,
1818 wHighVersion: WORD,
lib/std/pdb.zig+1-1
......@@ -636,7 +636,7 @@ const MsfStream = struct {
636636 blocks: []u32 = undefined,
637637 block_size: u32 = undefined,
638638
639 pub const Error = @TypeOf(read).ReturnType.ErrorSet;
639 pub const Error = @typeInfo(@typeInfo(@TypeOf(read)).Fn.return_type.?).ErrorUnion.error_set;
640640
641641 fn init(block_size: u32, file: File, blocks: []u32) MsfStream {
642642 const stream = MsfStream{
lib/std/process.zig+4-4
......@@ -578,8 +578,8 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons
578578}
579579
580580pub const UserInfo = struct {
581 uid: u32,
582 gid: u32,
581 uid: os.uid_t,
582 gid: os.gid_t,
583583};
584584
585585/// POSIX function which gets a uid from username.
......@@ -607,8 +607,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
607607 var buf: [std.mem.page_size]u8 = undefined;
608608 var name_index: usize = 0;
609609 var state = State.Start;
610 var uid: u32 = 0;
611 var gid: u32 = 0;
610 var uid: os.uid_t = 0;
611 var gid: os.gid_t = 0;
612612
613613 while (true) {
614614 const amt_read = try reader.read(buf[0..]);
lib/std/progress.zig+3-3
......@@ -197,7 +197,7 @@ pub const Progress = struct {
197197 var maybe_node: ?*Node = &self.root;
198198 while (maybe_node) |node| {
199199 if (need_ellipse) {
200 self.bufWrite(&end, "...", .{});
200 self.bufWrite(&end, "... ", .{});
201201 }
202202 need_ellipse = false;
203203 if (node.name.len != 0 or node.estimated_total_items != null) {
......@@ -218,7 +218,7 @@ pub const Progress = struct {
218218 maybe_node = node.recently_updated_child;
219219 }
220220 if (need_ellipse) {
221 self.bufWrite(&end, "...", .{});
221 self.bufWrite(&end, "... ", .{});
222222 }
223223 }
224224
......@@ -253,7 +253,7 @@ pub const Progress = struct {
253253 const bytes_needed_for_esc_codes_at_end = if (std.builtin.os.tag == .windows) 0 else 11;
254254 const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
255255 if (end.* > max_end) {
256 const suffix = "...";
256 const suffix = "... ";
257257 self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;
258258 std.mem.copy(u8, self.output_buffer[max_end..], suffix);
259259 end.* = max_end + suffix.len;
lib/std/rand.zig+33-24
......@@ -51,8 +51,9 @@ pub const Random = struct {
5151 /// Returns a random int `i` such that `0 <= i <= maxInt(T)`.
5252 /// `i` is evenly distributed.
5353 pub fn int(r: *Random, comptime T: type) T {
54 const UnsignedT = std.meta.Int(false, T.bit_count);
55 const ByteAlignedT = std.meta.Int(false, @divTrunc(T.bit_count + 7, 8) * 8);
54 const bits = @typeInfo(T).Int.bits;
55 const UnsignedT = std.meta.Int(false, bits);
56 const ByteAlignedT = std.meta.Int(false, @divTrunc(bits + 7, 8) * 8);
5657
5758 var rand_bytes: [@sizeOf(ByteAlignedT)]u8 = undefined;
5859 r.bytes(rand_bytes[0..]);
......@@ -68,10 +69,11 @@ pub const Random = struct {
6869 /// Constant-time implementation off `uintLessThan`.
6970 /// The results of this function may be biased.
7071 pub fn uintLessThanBiased(r: *Random, comptime T: type, less_than: T) T {
71 comptime assert(T.is_signed == false);
72 comptime assert(T.bit_count <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation!
72 comptime assert(@typeInfo(T).Int.is_signed == false);
73 const bits = @typeInfo(T).Int.bits;
74 comptime assert(bits <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation!
7375 assert(0 < less_than);
74 if (T.bit_count <= 32) {
76 if (bits <= 32) {
7577 return @intCast(T, limitRangeBiased(u32, r.int(u32), less_than));
7678 } else {
7779 return @intCast(T, limitRangeBiased(u64, r.int(u64), less_than));
......@@ -87,13 +89,15 @@ pub const Random = struct {
8789 /// this function is guaranteed to return.
8890 /// If you need deterministic runtime bounds, use `uintLessThanBiased`.
8991 pub fn uintLessThan(r: *Random, comptime T: type, less_than: T) T {
90 comptime assert(T.is_signed == false);
91 comptime assert(T.bit_count <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation!
92 comptime assert(@typeInfo(T).Int.is_signed == false);
93 const bits = @typeInfo(T).Int.bits;
94 comptime assert(bits <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation!
9295 assert(0 < less_than);
9396 // Small is typically u32
94 const Small = std.meta.Int(false, @divTrunc(T.bit_count + 31, 32) * 32);
97 const small_bits = @divTrunc(bits + 31, 32) * 32;
98 const Small = std.meta.Int(false, small_bits);
9599 // Large is typically u64
96 const Large = std.meta.Int(false, Small.bit_count * 2);
100 const Large = std.meta.Int(false, small_bits * 2);
97101
98102 // adapted from:
99103 // http://www.pcg-random.org/posts/bounded-rands.html
......@@ -105,7 +109,7 @@ pub const Random = struct {
105109 // TODO: workaround for https://github.com/ziglang/zig/issues/1770
106110 // should be:
107111 // var t: Small = -%less_than;
108 var t: Small = @bitCast(Small, -%@bitCast(std.meta.Int(true, Small.bit_count), @as(Small, less_than)));
112 var t: Small = @bitCast(Small, -%@bitCast(std.meta.Int(true, small_bits), @as(Small, less_than)));
109113
110114 if (t >= less_than) {
111115 t -= less_than;
......@@ -119,13 +123,13 @@ pub const Random = struct {
119123 l = @truncate(Small, m);
120124 }
121125 }
122 return @intCast(T, m >> Small.bit_count);
126 return @intCast(T, m >> small_bits);
123127 }
124128
125129 /// Constant-time implementation off `uintAtMost`.
126130 /// The results of this function may be biased.
127131 pub fn uintAtMostBiased(r: *Random, comptime T: type, at_most: T) T {
128 assert(T.is_signed == false);
132 assert(@typeInfo(T).Int.is_signed == false);
129133 if (at_most == maxInt(T)) {
130134 // have the full range
131135 return r.int(T);
......@@ -137,7 +141,7 @@ pub const Random = struct {
137141 /// See `uintLessThan`, which this function uses in most cases,
138142 /// for commentary on the runtime of this function.
139143 pub fn uintAtMost(r: *Random, comptime T: type, at_most: T) T {
140 assert(T.is_signed == false);
144 assert(@typeInfo(T).Int.is_signed == false);
141145 if (at_most == maxInt(T)) {
142146 // have the full range
143147 return r.int(T);
......@@ -149,9 +153,10 @@ pub const Random = struct {
149153 /// The results of this function may be biased.
150154 pub fn intRangeLessThanBiased(r: *Random, comptime T: type, at_least: T, less_than: T) T {
151155 assert(at_least < less_than);
152 if (T.is_signed) {
156 const info = @typeInfo(T).Int;
157 if (info.is_signed) {
153158 // Two's complement makes this math pretty easy.
154 const UnsignedT = std.meta.Int(false, T.bit_count);
159 const UnsignedT = std.meta.Int(false, info.bits);
155160 const lo = @bitCast(UnsignedT, at_least);
156161 const hi = @bitCast(UnsignedT, less_than);
157162 const result = lo +% r.uintLessThanBiased(UnsignedT, hi -% lo);
......@@ -167,9 +172,10 @@ pub const Random = struct {
167172 /// for commentary on the runtime of this function.
168173 pub fn intRangeLessThan(r: *Random, comptime T: type, at_least: T, less_than: T) T {
169174 assert(at_least < less_than);
170 if (T.is_signed) {
175 const info = @typeInfo(T).Int;
176 if (info.is_signed) {
171177 // Two's complement makes this math pretty easy.
172 const UnsignedT = std.meta.Int(false, T.bit_count);
178 const UnsignedT = std.meta.Int(false, info.bits);
173179 const lo = @bitCast(UnsignedT, at_least);
174180 const hi = @bitCast(UnsignedT, less_than);
175181 const result = lo +% r.uintLessThan(UnsignedT, hi -% lo);
......@@ -184,9 +190,10 @@ pub const Random = struct {
184190 /// The results of this function may be biased.
185191 pub fn intRangeAtMostBiased(r: *Random, comptime T: type, at_least: T, at_most: T) T {
186192 assert(at_least <= at_most);
187 if (T.is_signed) {
193 const info = @typeInfo(T).Int;
194 if (info.is_signed) {
188195 // Two's complement makes this math pretty easy.
189 const UnsignedT = std.meta.Int(false, T.bit_count);
196 const UnsignedT = std.meta.Int(false, info.bits);
190197 const lo = @bitCast(UnsignedT, at_least);
191198 const hi = @bitCast(UnsignedT, at_most);
192199 const result = lo +% r.uintAtMostBiased(UnsignedT, hi -% lo);
......@@ -202,9 +209,10 @@ pub const Random = struct {
202209 /// for commentary on the runtime of this function.
203210 pub fn intRangeAtMost(r: *Random, comptime T: type, at_least: T, at_most: T) T {
204211 assert(at_least <= at_most);
205 if (T.is_signed) {
212 const info = @typeInfo(T).Int;
213 if (info.is_signed) {
206214 // Two's complement makes this math pretty easy.
207 const UnsignedT = std.meta.Int(false, T.bit_count);
215 const UnsignedT = std.meta.Int(false, info.bits);
208216 const lo = @bitCast(UnsignedT, at_least);
209217 const hi = @bitCast(UnsignedT, at_most);
210218 const result = lo +% r.uintAtMost(UnsignedT, hi -% lo);
......@@ -280,14 +288,15 @@ pub const Random = struct {
280288/// into an integer 0 <= result < less_than.
281289/// This function introduces a minor bias.
282290pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T {
283 comptime assert(T.is_signed == false);
284 const T2 = std.meta.Int(false, T.bit_count * 2);
291 comptime assert(@typeInfo(T).Int.is_signed == false);
292 const bits = @typeInfo(T).Int.bits;
293 const T2 = std.meta.Int(false, bits * 2);
285294
286295 // adapted from:
287296 // http://www.pcg-random.org/posts/bounded-rands.html
288297 // "Integer Multiplication (Biased)"
289298 var m: T2 = @as(T2, random_int) * @as(T2, less_than);
290 return @intCast(T, m >> T.bit_count);
299 return @intCast(T, m >> bits);
291300}
292301
293302const SequentialPrng = struct {
lib/std/special/build_runner.zig+1-1
......@@ -133,7 +133,7 @@ pub fn main() !void {
133133}
134134
135135fn runBuild(builder: *Builder) anyerror!void {
136 switch (@typeInfo(@TypeOf(root.build).ReturnType)) {
136 switch (@typeInfo(@typeInfo(@TypeOf(root.build)).Fn.return_type.?)) {
137137 .Void => root.build(builder),
138138 .ErrorUnion => try root.build(builder),
139139 else => @compileError("expected return type of build to be 'void' or '!void'"),
lib/std/special/c.zig+3-2
......@@ -516,11 +516,12 @@ export fn roundf(a: f32) f32 {
516516fn generic_fmod(comptime T: type, x: T, y: T) T {
517517 @setRuntimeSafety(false);
518518
519 const uint = std.meta.Int(false, T.bit_count);
519 const bits = @typeInfo(T).Float.bits;
520 const uint = std.meta.Int(false, bits);
520521 const log2uint = math.Log2Int(uint);
521522 const digits = if (T == f32) 23 else 52;
522523 const exp_bits = if (T == f32) 9 else 12;
523 const bits_minus_1 = T.bit_count - 1;
524 const bits_minus_1 = bits - 1;
524525 const mask = if (T == f32) 0xff else 0x7ff;
525526 var ux = @bitCast(uint, x);
526527 var uy = @bitCast(uint, y);
lib/std/special/compiler_rt/addXf3.zig+10-8
......@@ -59,23 +59,25 @@ pub fn __aeabi_dsub(a: f64, b: f64) callconv(.AAPCS) f64 {
5959}
6060
6161// TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154
62fn normalize(comptime T: type, significand: *std.meta.Int(false, T.bit_count)) i32 {
63 const Z = std.meta.Int(false, T.bit_count);
64 const S = std.meta.Int(false, T.bit_count - @clz(Z, @as(Z, T.bit_count) - 1));
62fn normalize(comptime T: type, significand: *std.meta.Int(false, @typeInfo(T).Float.bits)) i32 {
63 const bits = @typeInfo(T).Float.bits;
64 const Z = std.meta.Int(false, bits);
65 const S = std.meta.Int(false, bits - @clz(Z, @as(Z, bits) - 1));
6566 const significandBits = std.math.floatMantissaBits(T);
6667 const implicitBit = @as(Z, 1) << significandBits;
6768
68 const shift = @clz(std.meta.Int(false, T.bit_count), significand.*) - @clz(Z, implicitBit);
69 const shift = @clz(std.meta.Int(false, bits), significand.*) - @clz(Z, implicitBit);
6970 significand.* <<= @intCast(S, shift);
7071 return 1 - shift;
7172}
7273
7374// TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154
7475fn addXf3(comptime T: type, a: T, b: T) T {
75 const Z = std.meta.Int(false, T.bit_count);
76 const S = std.meta.Int(false, T.bit_count - @clz(Z, @as(Z, T.bit_count) - 1));
76 const bits = @typeInfo(T).Float.bits;
77 const Z = std.meta.Int(false, bits);
78 const S = std.meta.Int(false, bits - @clz(Z, @as(Z, bits) - 1));
7779
78 const typeWidth = T.bit_count;
80 const typeWidth = bits;
7981 const significandBits = std.math.floatMantissaBits(T);
8082 const exponentBits = std.math.floatExponentBits(T);
8183
......@@ -187,7 +189,7 @@ fn addXf3(comptime T: type, a: T, b: T) T {
187189 // If partial cancellation occured, we need to left-shift the result
188190 // and adjust the exponent:
189191 if (aSignificand < implicitBit << 3) {
190 const shift = @intCast(i32, @clz(Z, aSignificand)) - @intCast(i32, @clz(std.meta.Int(false, T.bit_count), implicitBit << 3));
192 const shift = @intCast(i32, @clz(Z, aSignificand)) - @intCast(i32, @clz(std.meta.Int(false, bits), implicitBit << 3));
191193 aSignificand <<= @intCast(S, shift);
192194 aExponent -= shift;
193195 }
lib/std/special/compiler_rt/aulldiv.zig+2-2
......@@ -7,8 +7,8 @@ const builtin = @import("builtin");
77
88pub fn _alldiv(a: i64, b: i64) callconv(.Stdcall) i64 {
99 @setRuntimeSafety(builtin.is_test);
10 const s_a = a >> (i64.bit_count - 1);
11 const s_b = b >> (i64.bit_count - 1);
10 const s_a = a >> (64 - 1);
11 const s_b = b >> (64 - 1);
1212
1313 const an = (a ^ s_a) -% s_a;
1414 const bn = (b ^ s_b) -% s_b;
lib/std/special/compiler_rt/aullrem.zig+2-2
......@@ -7,8 +7,8 @@ const builtin = @import("builtin");
77
88pub fn _allrem(a: i64, b: i64) callconv(.Stdcall) i64 {
99 @setRuntimeSafety(builtin.is_test);
10 const s_a = a >> (i64.bit_count - 1);
11 const s_b = b >> (i64.bit_count - 1);
10 const s_a = a >> (64 - 1);
11 const s_b = b >> (64 - 1);
1212
1313 const an = (a ^ s_a) -% s_a;
1414 const bn = (b ^ s_b) -% s_b;
lib/std/special/compiler_rt/compareXf2.zig+4-3
......@@ -27,8 +27,9 @@ const GE = extern enum(i32) {
2727pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {
2828 @setRuntimeSafety(builtin.is_test);
2929
30 const srep_t = std.meta.Int(true, T.bit_count);
31 const rep_t = std.meta.Int(false, T.bit_count);
30 const bits = @typeInfo(T).Float.bits;
31 const srep_t = std.meta.Int(true, bits);
32 const rep_t = std.meta.Int(false, bits);
3233
3334 const significandBits = std.math.floatMantissaBits(T);
3435 const exponentBits = std.math.floatExponentBits(T);
......@@ -73,7 +74,7 @@ pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {
7374pub fn unordcmp(comptime T: type, a: T, b: T) i32 {
7475 @setRuntimeSafety(builtin.is_test);
7576
76 const rep_t = std.meta.Int(false, T.bit_count);
77 const rep_t = std.meta.Int(false, @typeInfo(T).Float.bits);
7778
7879 const significandBits = std.math.floatMantissaBits(T);
7980 const exponentBits = std.math.floatExponentBits(T);
lib/std/special/compiler_rt/divdf3.zig+4-5
......@@ -12,10 +12,9 @@ const builtin = @import("builtin");
1212
1313pub fn __divdf3(a: f64, b: f64) callconv(.C) f64 {
1414 @setRuntimeSafety(builtin.is_test);
15 const Z = std.meta.Int(false, f64.bit_count);
16 const SignedZ = std.meta.Int(true, f64.bit_count);
15 const Z = std.meta.Int(false, 64);
16 const SignedZ = std.meta.Int(true, 64);
1717
18 const typeWidth = f64.bit_count;
1918 const significandBits = std.math.floatMantissaBits(f64);
2019 const exponentBits = std.math.floatExponentBits(f64);
2120
......@@ -317,9 +316,9 @@ pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void {
317316 }
318317}
319318
320pub fn normalize(comptime T: type, significand: *std.meta.Int(false, T.bit_count)) i32 {
319pub fn normalize(comptime T: type, significand: *std.meta.Int(false, @typeInfo(T).Float.bits)) i32 {
321320 @setRuntimeSafety(builtin.is_test);
322 const Z = std.meta.Int(false, T.bit_count);
321 const Z = std.meta.Int(false, @typeInfo(T).Float.bits);
323322 const significandBits = std.math.floatMantissaBits(T);
324323 const implicitBit = @as(Z, 1) << significandBits;
325324
lib/std/special/compiler_rt/divsf3.zig+3-4
......@@ -12,9 +12,8 @@ const builtin = @import("builtin");
1212
1313pub fn __divsf3(a: f32, b: f32) callconv(.C) f32 {
1414 @setRuntimeSafety(builtin.is_test);
15 const Z = std.meta.Int(false, f32.bit_count);
15 const Z = std.meta.Int(false, 32);
1616
17 const typeWidth = f32.bit_count;
1817 const significandBits = std.math.floatMantissaBits(f32);
1918 const exponentBits = std.math.floatExponentBits(f32);
2019
......@@ -190,9 +189,9 @@ pub fn __divsf3(a: f32, b: f32) callconv(.C) f32 {
190189 }
191190}
192191
193fn normalize(comptime T: type, significand: *std.meta.Int(false, T.bit_count)) i32 {
192fn normalize(comptime T: type, significand: *std.meta.Int(false, @typeInfo(T).Float.bits)) i32 {
194193 @setRuntimeSafety(builtin.is_test);
195 const Z = std.meta.Int(false, T.bit_count);
194 const Z = std.meta.Int(false, @typeInfo(T).Float.bits);
196195 const significandBits = std.math.floatMantissaBits(T);
197196 const implicitBit = @as(Z, 1) << significandBits;
198197
lib/std/special/compiler_rt/divtf3.zig+2-3
......@@ -11,10 +11,9 @@ const wideMultiply = @import("divdf3.zig").wideMultiply;
1111
1212pub fn __divtf3(a: f128, b: f128) callconv(.C) f128 {
1313 @setRuntimeSafety(builtin.is_test);
14 const Z = std.meta.Int(false, f128.bit_count);
15 const SignedZ = std.meta.Int(true, f128.bit_count);
14 const Z = std.meta.Int(false, 128);
15 const SignedZ = std.meta.Int(true, 128);
1616
17 const typeWidth = f128.bit_count;
1817 const significandBits = std.math.floatMantissaBits(f128);
1918 const exponentBits = std.math.floatExponentBits(f128);
2019
lib/std/special/compiler_rt/divti3.zig+2-2
......@@ -9,8 +9,8 @@ const builtin = @import("builtin");
99pub fn __divti3(a: i128, b: i128) callconv(.C) i128 {
1010 @setRuntimeSafety(builtin.is_test);
1111
12 const s_a = a >> (i128.bit_count - 1);
13 const s_b = b >> (i128.bit_count - 1);
12 const s_a = a >> (128 - 1);
13 const s_b = b >> (128 - 1);
1414
1515 const an = (a ^ s_a) -% s_a;
1616 const bn = (b ^ s_b) -% s_b;
lib/std/special/compiler_rt/fixint.zig+5-4
......@@ -28,7 +28,7 @@ pub fn fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t) fixint_t {
2828 else => unreachable,
2929 };
3030
31 const typeWidth = rep_t.bit_count;
31 const typeWidth = @typeInfo(rep_t).Int.bits;
3232 const exponentBits = (typeWidth - significandBits - 1);
3333 const signBit = (@as(rep_t, 1) << (significandBits + exponentBits));
3434 const maxExponent = ((1 << exponentBits) - 1);
......@@ -50,12 +50,13 @@ pub fn fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t) fixint_t {
5050 if (exponent < 0) return 0;
5151
5252 // The unsigned result needs to be large enough to handle an fixint_t or rep_t
53 const fixuint_t = std.meta.Int(false, fixint_t.bit_count);
54 const UintResultType = if (fixint_t.bit_count > rep_t.bit_count) fixuint_t else rep_t;
53 const fixint_bits = @typeInfo(fixint_t).Int.bits;
54 const fixuint_t = std.meta.Int(false, fixint_bits);
55 const UintResultType = if (fixint_bits > typeWidth) fixuint_t else rep_t;
5556 var uint_result: UintResultType = undefined;
5657
5758 // If the value is too large for the integer type, saturate.
58 if (@intCast(usize, exponent) >= fixint_t.bit_count) {
59 if (@intCast(usize, exponent) >= fixint_bits) {
5960 return if (negative) @as(fixint_t, minInt(fixint_t)) else @as(fixint_t, maxInt(fixint_t));
6061 }
6162
lib/std/special/compiler_rt/fixuint.zig+3-3
......@@ -15,14 +15,14 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t
1515 f128 => u128,
1616 else => unreachable,
1717 };
18 const srep_t = @import("std").meta.Int(true, rep_t.bit_count);
18 const typeWidth = @typeInfo(rep_t).Int.bits;
19 const srep_t = @import("std").meta.Int(true, typeWidth);
1920 const significandBits = switch (fp_t) {
2021 f32 => 23,
2122 f64 => 52,
2223 f128 => 112,
2324 else => unreachable,
2425 };
25 const typeWidth = rep_t.bit_count;
2626 const exponentBits = (typeWidth - significandBits - 1);
2727 const signBit = (@as(rep_t, 1) << (significandBits + exponentBits));
2828 const maxExponent = ((1 << exponentBits) - 1);
......@@ -44,7 +44,7 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t
4444 if (sign == -1 or exponent < 0) return 0;
4545
4646 // If the value is too large for the integer type, saturate.
47 if (@intCast(c_uint, exponent) >= fixuint_t.bit_count) return ~@as(fixuint_t, 0);
47 if (@intCast(c_uint, exponent) >= @typeInfo(fixuint_t).Int.bits) return ~@as(fixuint_t, 0);
4848
4949 // If 0 <= exponent < significandBits, right shift to get the result.
5050 // Otherwise, shift left.
lib/std/special/compiler_rt/floatXisf.zig+5-4
......@@ -12,15 +12,16 @@ const FLT_MANT_DIG = 24;
1212fn __floatXisf(comptime T: type, arg: T) f32 {
1313 @setRuntimeSafety(builtin.is_test);
1414
15 const Z = std.meta.Int(false, T.bit_count);
16 const S = std.meta.Int(false, T.bit_count - @clz(Z, @as(Z, T.bit_count) - 1));
15 const bits = @typeInfo(T).Int.bits;
16 const Z = std.meta.Int(false, bits);
17 const S = std.meta.Int(false, bits - @clz(Z, @as(Z, bits) - 1));
1718
1819 if (arg == 0) {
1920 return @as(f32, 0.0);
2021 }
2122
2223 var ai = arg;
23 const N: u32 = T.bit_count;
24 const N: u32 = bits;
2425 const si = ai >> @intCast(S, (N - 1));
2526 ai = ((ai ^ si) -% si);
2627 var a = @bitCast(Z, ai);
......@@ -66,7 +67,7 @@ fn __floatXisf(comptime T: type, arg: T) f32 {
6667 // a is now rounded to FLT_MANT_DIG bits
6768 }
6869
69 const s = @bitCast(Z, arg) >> (T.bit_count - 32);
70 const s = @bitCast(Z, arg) >> (@typeInfo(T).Int.bits - 32);
7071 const r = (@intCast(u32, s) & 0x80000000) | // sign
7172 (@intCast(u32, (e + 127)) << 23) | // exponent
7273 (@truncate(u32, a) & 0x007fffff); // mantissa-high
lib/std/special/compiler_rt/floatsiXf.zig+4-3
......@@ -10,8 +10,9 @@ const maxInt = std.math.maxInt;
1010fn floatsiXf(comptime T: type, a: i32) T {
1111 @setRuntimeSafety(builtin.is_test);
1212
13 const Z = std.meta.Int(false, T.bit_count);
14 const S = std.meta.Int(false, T.bit_count - @clz(Z, @as(Z, T.bit_count) - 1));
13 const bits = @typeInfo(T).Float.bits;
14 const Z = std.meta.Int(false, bits);
15 const S = std.meta.Int(false, bits - @clz(Z, @as(Z, bits) - 1));
1516
1617 if (a == 0) {
1718 return @as(T, 0.0);
......@@ -22,7 +23,7 @@ fn floatsiXf(comptime T: type, a: i32) T {
2223 const exponentBias = ((1 << exponentBits - 1) - 1);
2324
2425 const implicitBit = @as(Z, 1) << significandBits;
25 const signBit = @as(Z, 1 << Z.bit_count - 1);
26 const signBit = @as(Z, 1 << bits - 1);
2627
2728 const sign = a >> 31;
2829 // Take absolute value of a via abs(x) = (x^(x >> 31)) - (x >> 31).
lib/std/special/compiler_rt/floatundisf.zig+1-1
......@@ -15,7 +15,7 @@ pub fn __floatundisf(arg: u64) callconv(.C) f32 {
1515 if (arg == 0) return 0;
1616
1717 var a = arg;
18 const N: usize = @TypeOf(a).bit_count;
18 const N: usize = @typeInfo(@TypeOf(a)).Int.bits;
1919 // Number of significant digits
2020 const sd = N - @clz(u64, a);
2121 // 8 exponent
lib/std/special/compiler_rt/floatunditf.zig+1-1
......@@ -19,7 +19,7 @@ pub fn __floatunditf(a: u64) callconv(.C) f128 {
1919 const exponent_bias = (1 << (exponent_bits - 1)) - 1;
2020 const implicit_bit = 1 << mantissa_bits;
2121
22 const exp: u128 = (u64.bit_count - 1) - @clz(u64, a);
22 const exp: u128 = (64 - 1) - @clz(u64, a);
2323 const shift: u7 = mantissa_bits - @intCast(u7, exp);
2424
2525 var result: u128 = (@intCast(u128, a) << shift) ^ implicit_bit;
lib/std/special/compiler_rt/floatunsitf.zig+1-1
......@@ -19,7 +19,7 @@ pub fn __floatunsitf(a: u64) callconv(.C) f128 {
1919 const exponent_bias = (1 << (exponent_bits - 1)) - 1;
2020 const implicit_bit = 1 << mantissa_bits;
2121
22 const exp = (u64.bit_count - 1) - @clz(u64, a);
22 const exp = (64 - 1) - @clz(u64, a);
2323 const shift = mantissa_bits - @intCast(u7, exp);
2424
2525 // TODO(#1148): @bitCast alignment error
lib/std/special/compiler_rt/int.zig+1-1
......@@ -219,7 +219,7 @@ fn test_one_divsi3(a: i32, b: i32, expected_q: i32) void {
219219pub fn __udivsi3(n: u32, d: u32) callconv(.C) u32 {
220220 @setRuntimeSafety(builtin.is_test);
221221
222 const n_uword_bits: c_uint = u32.bit_count;
222 const n_uword_bits: c_uint = 32;
223223 // special cases
224224 if (d == 0) return 0; // ?!
225225 if (n == 0) return 0;
lib/std/special/compiler_rt/modti3.zig+2-2
......@@ -14,8 +14,8 @@ const compiler_rt = @import("../compiler_rt.zig");
1414pub fn __modti3(a: i128, b: i128) callconv(.C) i128 {
1515 @setRuntimeSafety(builtin.is_test);
1616
17 const s_a = a >> (i128.bit_count - 1); // s = a < 0 ? -1 : 0
18 const s_b = b >> (i128.bit_count - 1); // s = b < 0 ? -1 : 0
17 const s_a = a >> (128 - 1); // s = a < 0 ? -1 : 0
18 const s_b = b >> (128 - 1); // s = b < 0 ? -1 : 0
1919
2020 const an = (a ^ s_a) -% s_a; // negate if s == -1
2121 const bn = (b ^ s_b) -% s_b; // negate if s == -1
lib/std/special/compiler_rt/mulXf3.zig+5-5
......@@ -33,9 +33,9 @@ pub fn __aeabi_dmul(a: f64, b: f64) callconv(.C) f64 {
3333
3434fn mulXf3(comptime T: type, a: T, b: T) T {
3535 @setRuntimeSafety(builtin.is_test);
36 const Z = std.meta.Int(false, T.bit_count);
36 const typeWidth = @typeInfo(T).Float.bits;
37 const Z = std.meta.Int(false, typeWidth);
3738
38 const typeWidth = T.bit_count;
3939 const significandBits = std.math.floatMantissaBits(T);
4040 const exponentBits = std.math.floatExponentBits(T);
4141
......@@ -269,9 +269,9 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void {
269269 }
270270}
271271
272fn normalize(comptime T: type, significand: *std.meta.Int(false, T.bit_count)) i32 {
272fn normalize(comptime T: type, significand: *std.meta.Int(false, @typeInfo(T).Float.bits)) i32 {
273273 @setRuntimeSafety(builtin.is_test);
274 const Z = std.meta.Int(false, T.bit_count);
274 const Z = std.meta.Int(false, @typeInfo(T).Float.bits);
275275 const significandBits = std.math.floatMantissaBits(T);
276276 const implicitBit = @as(Z, 1) << significandBits;
277277
......@@ -282,7 +282,7 @@ fn normalize(comptime T: type, significand: *std.meta.Int(false, T.bit_count)) i
282282
283283fn wideRightShiftWithSticky(comptime Z: type, hi: *Z, lo: *Z, count: u32) void {
284284 @setRuntimeSafety(builtin.is_test);
285 const typeWidth = Z.bit_count;
285 const typeWidth = @typeInfo(Z).Int.bits;
286286 const S = std.math.Log2Int(Z);
287287 if (count < typeWidth) {
288288 const sticky = @truncate(u8, lo.* << @intCast(S, typeWidth -% count));
lib/std/special/compiler_rt/mulodi4.zig+1-1
......@@ -11,7 +11,7 @@ const minInt = std.math.minInt;
1111pub fn __mulodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 {
1212 @setRuntimeSafety(builtin.is_test);
1313
14 const min = @bitCast(i64, @as(u64, 1 << (i64.bit_count - 1)));
14 const min = @bitCast(i64, @as(u64, 1 << (64 - 1)));
1515 const max = ~min;
1616
1717 overflow.* = 0;
lib/std/special/compiler_rt/muloti4.zig+3-3
......@@ -9,7 +9,7 @@ const compiler_rt = @import("../compiler_rt.zig");
99pub fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 {
1010 @setRuntimeSafety(builtin.is_test);
1111
12 const min = @bitCast(i128, @as(u128, 1 << (i128.bit_count - 1)));
12 const min = @bitCast(i128, @as(u128, 1 << (128 - 1)));
1313 const max = ~min;
1414 overflow.* = 0;
1515
......@@ -27,9 +27,9 @@ pub fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 {
2727 return r;
2828 }
2929
30 const sa = a >> (i128.bit_count - 1);
30 const sa = a >> (128 - 1);
3131 const abs_a = (a ^ sa) -% sa;
32 const sb = b >> (i128.bit_count - 1);
32 const sb = b >> (128 - 1);
3333 const abs_b = (b ^ sb) -% sb;
3434
3535 if (abs_a < 2 or abs_b < 2) {
lib/std/special/compiler_rt/negXf2.zig+1-2
......@@ -24,9 +24,8 @@ pub fn __aeabi_dneg(arg: f64) callconv(.AAPCS) f64 {
2424}
2525
2626fn negXf2(comptime T: type, a: T) T {
27 const Z = std.meta.Int(false, T.bit_count);
27 const Z = std.meta.Int(false, @typeInfo(T).Float.bits);
2828
29 const typeWidth = T.bit_count;
3029 const significandBits = std.math.floatMantissaBits(T);
3130 const exponentBits = std.math.floatExponentBits(T);
3231
lib/std/special/compiler_rt/shift.zig+13-12
......@@ -9,8 +9,9 @@ const Log2Int = std.math.Log2Int;
99
1010fn Dwords(comptime T: type, comptime signed_half: bool) type {
1111 return extern union {
12 pub const HalfTU = std.meta.Int(false, @divExact(T.bit_count, 2));
13 pub const HalfTS = std.meta.Int(true, @divExact(T.bit_count, 2));
12 pub const bits = @divExact(@typeInfo(T).Int.bits, 2);
13 pub const HalfTU = std.meta.Int(false, bits);
14 pub const HalfTS = std.meta.Int(true, bits);
1415 pub const HalfT = if (signed_half) HalfTS else HalfTU;
1516
1617 all: T,
......@@ -30,15 +31,15 @@ pub fn ashlXi3(comptime T: type, a: T, b: i32) T {
3031 const input = dwords{ .all = a };
3132 var output: dwords = undefined;
3233
33 if (b >= dwords.HalfT.bit_count) {
34 if (b >= dwords.bits) {
3435 output.s.low = 0;
35 output.s.high = input.s.low << @intCast(S, b - dwords.HalfT.bit_count);
36 output.s.high = input.s.low << @intCast(S, b - dwords.bits);
3637 } else if (b == 0) {
3738 return a;
3839 } else {
3940 output.s.low = input.s.low << @intCast(S, b);
4041 output.s.high = input.s.high << @intCast(S, b);
41 output.s.high |= input.s.low >> @intCast(S, dwords.HalfT.bit_count - b);
42 output.s.high |= input.s.low >> @intCast(S, dwords.bits - b);
4243 }
4344
4445 return output.all;
......@@ -53,14 +54,14 @@ pub fn ashrXi3(comptime T: type, a: T, b: i32) T {
5354 const input = dwords{ .all = a };
5455 var output: dwords = undefined;
5556
56 if (b >= dwords.HalfT.bit_count) {
57 output.s.high = input.s.high >> (dwords.HalfT.bit_count - 1);
58 output.s.low = input.s.high >> @intCast(S, b - dwords.HalfT.bit_count);
57 if (b >= dwords.bits) {
58 output.s.high = input.s.high >> (dwords.bits - 1);
59 output.s.low = input.s.high >> @intCast(S, b - dwords.bits);
5960 } else if (b == 0) {
6061 return a;
6162 } else {
6263 output.s.high = input.s.high >> @intCast(S, b);
63 output.s.low = input.s.high << @intCast(S, dwords.HalfT.bit_count - b);
64 output.s.low = input.s.high << @intCast(S, dwords.bits - b);
6465 // Avoid sign-extension here
6566 output.s.low |= @bitCast(
6667 dwords.HalfT,
......@@ -80,14 +81,14 @@ pub fn lshrXi3(comptime T: type, a: T, b: i32) T {
8081 const input = dwords{ .all = a };
8182 var output: dwords = undefined;
8283
83 if (b >= dwords.HalfT.bit_count) {
84 if (b >= dwords.bits) {
8485 output.s.high = 0;
85 output.s.low = input.s.high >> @intCast(S, b - dwords.HalfT.bit_count);
86 output.s.low = input.s.high >> @intCast(S, b - dwords.bits);
8687 } else if (b == 0) {
8788 return a;
8889 } else {
8990 output.s.high = input.s.high >> @intCast(S, b);
90 output.s.low = input.s.high << @intCast(S, dwords.HalfT.bit_count - b);
91 output.s.low = input.s.high << @intCast(S, dwords.bits - b);
9192 output.s.low |= input.s.low >> @intCast(S, b);
9293 }
9394
lib/std/special/compiler_rt/truncXfYf2.zig+2-2
......@@ -50,7 +50,7 @@ fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
5050
5151 // Various constants whose values follow from the type parameters.
5252 // Any reasonable optimizer will fold and propagate all of these.
53 const srcBits = src_t.bit_count;
53 const srcBits = @typeInfo(src_t).Float.bits;
5454 const srcExpBits = srcBits - srcSigBits - 1;
5555 const srcInfExp = (1 << srcExpBits) - 1;
5656 const srcExpBias = srcInfExp >> 1;
......@@ -65,7 +65,7 @@ fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
6565 const srcQNaN = 1 << (srcSigBits - 1);
6666 const srcNaNCode = srcQNaN - 1;
6767
68 const dstBits = dst_t.bit_count;
68 const dstBits = @typeInfo(dst_t).Float.bits;
6969 const dstExpBits = dstBits - dstSigBits - 1;
7070 const dstInfExp = (1 << dstExpBits) - 1;
7171 const dstExpBias = dstInfExp >> 1;
lib/std/special/compiler_rt/udivmod.zig+36-34
......@@ -15,8 +15,10 @@ const high = 1 - low;
1515pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: ?*DoubleInt) DoubleInt {
1616 @setRuntimeSafety(is_test);
1717
18 const SingleInt = @import("std").meta.Int(false, @divExact(DoubleInt.bit_count, 2));
19 const SignedDoubleInt = @import("std").meta.Int(true, DoubleInt.bit_count);
18 const double_int_bits = @typeInfo(DoubleInt).Int.bits;
19 const single_int_bits = @divExact(double_int_bits, 2);
20 const SingleInt = @import("std").meta.Int(false, single_int_bits);
21 const SignedDoubleInt = @import("std").meta.Int(true, double_int_bits);
2022 const Log2SingleInt = @import("std").math.Log2Int(SingleInt);
2123
2224 const n = @ptrCast(*const [2]SingleInt, &a).*; // TODO issue #421
......@@ -82,21 +84,21 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:
8284 // ---
8385 // K 0
8486 sr = @bitCast(c_uint, @as(c_int, @clz(SingleInt, d[high])) - @as(c_int, @clz(SingleInt, n[high])));
85 // 0 <= sr <= SingleInt.bit_count - 2 or sr large
86 if (sr > SingleInt.bit_count - 2) {
87 // 0 <= sr <= single_int_bits - 2 or sr large
88 if (sr > single_int_bits - 2) {
8789 if (maybe_rem) |rem| {
8890 rem.* = a;
8991 }
9092 return 0;
9193 }
9294 sr += 1;
93 // 1 <= sr <= SingleInt.bit_count - 1
94 // q.all = a << (DoubleInt.bit_count - sr);
95 // 1 <= sr <= single_int_bits - 1
96 // q.all = a << (double_int_bits - sr);
9597 q[low] = 0;
96 q[high] = n[low] << @intCast(Log2SingleInt, SingleInt.bit_count - sr);
98 q[high] = n[low] << @intCast(Log2SingleInt, single_int_bits - sr);
9799 // r.all = a >> sr;
98100 r[high] = n[high] >> @intCast(Log2SingleInt, sr);
99 r[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
101 r[low] = (n[high] << @intCast(Log2SingleInt, single_int_bits - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
100102 } else {
101103 // d[low] != 0
102104 if (d[high] == 0) {
......@@ -113,74 +115,74 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:
113115 }
114116 sr = @ctz(SingleInt, d[low]);
115117 q[high] = n[high] >> @intCast(Log2SingleInt, sr);
116 q[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
118 q[low] = (n[high] << @intCast(Log2SingleInt, single_int_bits - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
117119 return @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &q[0]).*; // TODO issue #421
118120 }
119121 // K X
120122 // ---
121123 // 0 K
122 sr = 1 + SingleInt.bit_count + @as(c_uint, @clz(SingleInt, d[low])) - @as(c_uint, @clz(SingleInt, n[high]));
123 // 2 <= sr <= DoubleInt.bit_count - 1
124 // q.all = a << (DoubleInt.bit_count - sr);
124 sr = 1 + single_int_bits + @as(c_uint, @clz(SingleInt, d[low])) - @as(c_uint, @clz(SingleInt, n[high]));
125 // 2 <= sr <= double_int_bits - 1
126 // q.all = a << (double_int_bits - sr);
125127 // r.all = a >> sr;
126 if (sr == SingleInt.bit_count) {
128 if (sr == single_int_bits) {
127129 q[low] = 0;
128130 q[high] = n[low];
129131 r[high] = 0;
130132 r[low] = n[high];
131 } else if (sr < SingleInt.bit_count) {
132 // 2 <= sr <= SingleInt.bit_count - 1
133 } else if (sr < single_int_bits) {
134 // 2 <= sr <= single_int_bits - 1
133135 q[low] = 0;
134 q[high] = n[low] << @intCast(Log2SingleInt, SingleInt.bit_count - sr);
136 q[high] = n[low] << @intCast(Log2SingleInt, single_int_bits - sr);
135137 r[high] = n[high] >> @intCast(Log2SingleInt, sr);
136 r[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
138 r[low] = (n[high] << @intCast(Log2SingleInt, single_int_bits - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
137139 } else {
138 // SingleInt.bit_count + 1 <= sr <= DoubleInt.bit_count - 1
139 q[low] = n[low] << @intCast(Log2SingleInt, DoubleInt.bit_count - sr);
140 q[high] = (n[high] << @intCast(Log2SingleInt, DoubleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr - SingleInt.bit_count));
140 // single_int_bits + 1 <= sr <= double_int_bits - 1
141 q[low] = n[low] << @intCast(Log2SingleInt, double_int_bits - sr);
142 q[high] = (n[high] << @intCast(Log2SingleInt, double_int_bits - sr)) | (n[low] >> @intCast(Log2SingleInt, sr - single_int_bits));
141143 r[high] = 0;
142 r[low] = n[high] >> @intCast(Log2SingleInt, sr - SingleInt.bit_count);
144 r[low] = n[high] >> @intCast(Log2SingleInt, sr - single_int_bits);
143145 }
144146 } else {
145147 // K X
146148 // ---
147149 // K K
148150 sr = @bitCast(c_uint, @as(c_int, @clz(SingleInt, d[high])) - @as(c_int, @clz(SingleInt, n[high])));
149 // 0 <= sr <= SingleInt.bit_count - 1 or sr large
150 if (sr > SingleInt.bit_count - 1) {
151 // 0 <= sr <= single_int_bits - 1 or sr large
152 if (sr > single_int_bits - 1) {
151153 if (maybe_rem) |rem| {
152154 rem.* = a;
153155 }
154156 return 0;
155157 }
156158 sr += 1;
157 // 1 <= sr <= SingleInt.bit_count
158 // q.all = a << (DoubleInt.bit_count - sr);
159 // 1 <= sr <= single_int_bits
160 // q.all = a << (double_int_bits - sr);
159161 // r.all = a >> sr;
160162 q[low] = 0;
161 if (sr == SingleInt.bit_count) {
163 if (sr == single_int_bits) {
162164 q[high] = n[low];
163165 r[high] = 0;
164166 r[low] = n[high];
165167 } else {
166168 r[high] = n[high] >> @intCast(Log2SingleInt, sr);
167 r[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
168 q[high] = n[low] << @intCast(Log2SingleInt, SingleInt.bit_count - sr);
169 r[low] = (n[high] << @intCast(Log2SingleInt, single_int_bits - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
170 q[high] = n[low] << @intCast(Log2SingleInt, single_int_bits - sr);
169171 }
170172 }
171173 }
172174 // Not a special case
173175 // q and r are initialized with:
174 // q.all = a << (DoubleInt.bit_count - sr);
176 // q.all = a << (double_int_bits - sr);
175177 // r.all = a >> sr;
176 // 1 <= sr <= DoubleInt.bit_count - 1
178 // 1 <= sr <= double_int_bits - 1
177179 var carry: u32 = 0;
178180 var r_all: DoubleInt = undefined;
179181 while (sr > 0) : (sr -= 1) {
180182 // r:q = ((r:q) << 1) | carry
181 r[high] = (r[high] << 1) | (r[low] >> (SingleInt.bit_count - 1));
182 r[low] = (r[low] << 1) | (q[high] >> (SingleInt.bit_count - 1));
183 q[high] = (q[high] << 1) | (q[low] >> (SingleInt.bit_count - 1));
183 r[high] = (r[high] << 1) | (r[low] >> (single_int_bits - 1));
184 r[low] = (r[low] << 1) | (q[high] >> (single_int_bits - 1));
185 q[high] = (q[high] << 1) | (q[low] >> (single_int_bits - 1));
184186 q[low] = (q[low] << 1) | carry;
185187 // carry = 0;
186188 // if (r.all >= b)
......@@ -189,7 +191,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:
189191 // carry = 1;
190192 // }
191193 r_all = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421
192 const s: SignedDoubleInt = @bitCast(SignedDoubleInt, b -% r_all -% 1) >> (DoubleInt.bit_count - 1);
194 const s: SignedDoubleInt = @bitCast(SignedDoubleInt, b -% r_all -% 1) >> (double_int_bits - 1);
193195 carry = @intCast(u32, s & 1);
194196 r_all -= b & @bitCast(DoubleInt, s);
195197 r = @ptrCast(*[2]SingleInt, &r_all).*; // TODO issue #421
lib/std/special/test_runner.zig+1-1
......@@ -40,7 +40,7 @@ pub fn main() anyerror!void {
4040 test_node.activate();
4141 progress.refresh();
4242 if (progress.terminal == null) {
43 std.debug.print("{}/{} {}...", .{ i + 1, test_fn_list.len, test_fn.name });
43 std.debug.print("{}/{} {}... ", .{ i + 1, test_fn_list.len, test_fn.name });
4444 }
4545 const result = if (test_fn.async_frame_size) |size| switch (io_mode) {
4646 .evented => blk: {
lib/std/start.zig+2-2
......@@ -67,7 +67,7 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv
6767 uefi.handle = handle;
6868 uefi.system_table = system_table;
6969
70 switch (@TypeOf(root.main).ReturnType) {
70 switch (@typeInfo(@TypeOf(root.main)).Fn.return_type.?) {
7171 noreturn => {
7272 root.main();
7373 },
......@@ -239,7 +239,7 @@ fn callMainAsync(loop: *std.event.Loop) callconv(.Async) u8 {
239239// This is not marked inline because it is called with @asyncCall when
240240// there is an event loop.
241241pub fn callMain() u8 {
242 switch (@typeInfo(@TypeOf(root.main).ReturnType)) {
242 switch (@typeInfo(@typeInfo(@TypeOf(root.main)).Fn.return_type.?)) {
243243 .NoReturn => {
244244 root.main();
245245 },
lib/std/std.zig+1
......@@ -50,6 +50,7 @@ pub const builtin = @import("builtin.zig");
5050pub const c = @import("c.zig");
5151pub const cache_hash = @import("cache_hash.zig");
5252pub const coff = @import("coff.zig");
53pub const compress = @import("compress.zig");
5354pub const crypto = @import("crypto.zig");
5455pub const cstr = @import("cstr.zig");
5556pub const debug = @import("debug.zig");
lib/std/target.zig+59-1
......@@ -101,7 +101,7 @@ pub const Target = struct {
101101
102102 /// Latest Windows version that the Zig Standard Library is aware of
103103 pub const latest = WindowsVersion.win10_20h1;
104
104
105105 pub const Range = struct {
106106 min: WindowsVersion,
107107 max: WindowsVersion,
......@@ -468,6 +468,7 @@ pub const Target = struct {
468468 /// TODO Get rid of this one.
469469 unknown,
470470 coff,
471 pe,
471472 elf,
472473 macho,
473474 wasm,
......@@ -771,6 +772,63 @@ pub const Target = struct {
771772 };
772773 }
773774
775 pub fn toCoffMachine(arch: Arch) std.coff.MachineType {
776 return switch (arch) {
777 .avr => .Unknown,
778 .msp430 => .Unknown,
779 .arc => .Unknown,
780 .arm => .ARM,
781 .armeb => .Unknown,
782 .hexagon => .Unknown,
783 .le32 => .Unknown,
784 .mips => .Unknown,
785 .mipsel => .Unknown,
786 .powerpc => .POWERPC,
787 .r600 => .Unknown,
788 .riscv32 => .RISCV32,
789 .sparc => .Unknown,
790 .sparcel => .Unknown,
791 .tce => .Unknown,
792 .tcele => .Unknown,
793 .thumb => .Thumb,
794 .thumbeb => .Thumb,
795 .i386 => .I386,
796 .xcore => .Unknown,
797 .nvptx => .Unknown,
798 .amdil => .Unknown,
799 .hsail => .Unknown,
800 .spir => .Unknown,
801 .kalimba => .Unknown,
802 .shave => .Unknown,
803 .lanai => .Unknown,
804 .wasm32 => .Unknown,
805 .renderscript32 => .Unknown,
806 .aarch64_32 => .ARM64,
807 .aarch64 => .ARM64,
808 .aarch64_be => .Unknown,
809 .mips64 => .Unknown,
810 .mips64el => .Unknown,
811 .powerpc64 => .Unknown,
812 .powerpc64le => .Unknown,
813 .riscv64 => .RISCV64,
814 .x86_64 => .X64,
815 .nvptx64 => .Unknown,
816 .le64 => .Unknown,
817 .amdil64 => .Unknown,
818 .hsail64 => .Unknown,
819 .spir64 => .Unknown,
820 .wasm64 => .Unknown,
821 .renderscript64 => .Unknown,
822 .amdgcn => .Unknown,
823 .bpfel => .Unknown,
824 .bpfeb => .Unknown,
825 .sparcv9 => .Unknown,
826 .s390x => .Unknown,
827 .ve => .Unknown,
828 .spu_2 => .Unknown,
829 };
830 }
831
774832 pub fn endian(arch: Arch) builtin.Endian {
775833 return switch (arch) {
776834 .avr,
lib/std/thread.zig+3-3
......@@ -166,7 +166,7 @@ pub const Thread = struct {
166166 fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD {
167167 const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
168168
169 switch (@typeInfo(@TypeOf(startFn).ReturnType)) {
169 switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
170170 .NoReturn => {
171171 startFn(arg);
172172 },
......@@ -227,7 +227,7 @@ pub const Thread = struct {
227227 fn linuxThreadMain(ctx_addr: usize) callconv(.C) u8 {
228228 const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;
229229
230 switch (@typeInfo(@TypeOf(startFn).ReturnType)) {
230 switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
231231 .NoReturn => {
232232 startFn(arg);
233233 },
......@@ -259,7 +259,7 @@ pub const Thread = struct {
259259 fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void {
260260 const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), ctx)).*;
261261
262 switch (@typeInfo(@TypeOf(startFn).ReturnType)) {
262 switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
263263 .NoReturn => {
264264 startFn(arg);
265265 },
lib/std/zig.zig+1-1
......@@ -22,7 +22,7 @@ pub const SrcHash = [16]u8;
2222/// If it is long, blake3 hash is computed.
2323pub fn hashSrc(src: []const u8) SrcHash {
2424 var out: SrcHash = undefined;
25 if (src.len <= SrcHash.len) {
25 if (src.len <= @typeInfo(SrcHash).Array.len) {
2626 std.mem.copy(u8, &out, src);
2727 std.mem.set(u8, out[src.len..], 0);
2828 } else {
lib/std/zig/parser_test.zig+114-6
......@@ -615,6 +615,17 @@ test "zig fmt: infix operator and then multiline string literal" {
615615 );
616616}
617617
618test "zig fmt: infix operator and then multiline string literal" {
619 try testCanonical(
620 \\const x = "" ++
621 \\ \\ hi0
622 \\ \\ hi1
623 \\ \\ hi2
624 \\;
625 \\
626 );
627}
628
618629test "zig fmt: C pointers" {
619630 try testCanonical(
620631 \\const Ptr = [*c]i32;
......@@ -885,6 +896,28 @@ test "zig fmt: 2nd arg multiline string" {
885896 );
886897}
887898
899test "zig fmt: 2nd arg multiline string many args" {
900 try testCanonical(
901 \\comptime {
902 \\ cases.addAsm("hello world linux x86_64",
903 \\ \\.text
904 \\ , "Hello, world!\n", "Hello, world!\n");
905 \\}
906 \\
907 );
908}
909
910test "zig fmt: final arg multiline string" {
911 try testCanonical(
912 \\comptime {
913 \\ cases.addAsm("hello world linux x86_64", "Hello, world!\n",
914 \\ \\.text
915 \\ );
916 \\}
917 \\
918 );
919}
920
888921test "zig fmt: if condition wraps" {
889922 try testTransform(
890923 \\comptime {
......@@ -915,6 +948,11 @@ test "zig fmt: if condition wraps" {
915948 \\ var a = if (a) |*f| x: {
916949 \\ break :x &a.b;
917950 \\ } else |err| err;
951 \\ var a = if (cond and
952 \\ cond) |*f|
953 \\ x: {
954 \\ break :x &a.b;
955 \\ } else |err| err;
918956 \\}
919957 ,
920958 \\comptime {
......@@ -951,6 +989,35 @@ test "zig fmt: if condition wraps" {
951989 \\ var a = if (a) |*f| x: {
952990 \\ break :x &a.b;
953991 \\ } else |err| err;
992 \\ var a = if (cond and
993 \\ cond) |*f|
994 \\ x: {
995 \\ break :x &a.b;
996 \\ } else |err| err;
997 \\}
998 \\
999 );
1000}
1001
1002test "zig fmt: if condition has line break but must not wrap" {
1003 try testCanonical(
1004 \\comptime {
1005 \\ if (self.user_input_options.put(
1006 \\ name,
1007 \\ UserInputOption{
1008 \\ .name = name,
1009 \\ .used = false,
1010 \\ },
1011 \\ ) catch unreachable) |*prev_value| {
1012 \\ foo();
1013 \\ bar();
1014 \\ }
1015 \\ if (put(
1016 \\ a,
1017 \\ b,
1018 \\ )) {
1019 \\ foo();
1020 \\ }
9541021 \\}
9551022 \\
9561023 );
......@@ -977,6 +1044,18 @@ test "zig fmt: if condition has line break but must not wrap" {
9771044 );
9781045}
9791046
1047test "zig fmt: function call with multiline argument" {
1048 try testCanonical(
1049 \\comptime {
1050 \\ self.user_input_options.put(name, UserInputOption{
1051 \\ .name = name,
1052 \\ .used = false,
1053 \\ });
1054 \\}
1055 \\
1056 );
1057}
1058
9801059test "zig fmt: same-line doc comment on variable declaration" {
9811060 try testTransform(
9821061 \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space
......@@ -1228,7 +1307,7 @@ test "zig fmt: array literal with hint" {
12281307 \\const a = []u8{
12291308 \\ 1, 2,
12301309 \\ 3, //
1231 \\ 4,
1310 \\ 4,
12321311 \\ 5, 6,
12331312 \\ 7,
12341313 \\};
......@@ -1293,7 +1372,7 @@ test "zig fmt: multiline string parameter in fn call with trailing comma" {
12931372 \\ \\ZIG_C_HEADER_FILES {}
12941373 \\ \\ZIG_DIA_GUIDS_LIB {}
12951374 \\ \\
1296 \\ ,
1375 \\ ,
12971376 \\ std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),
12981377 \\ std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),
12991378 \\ std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),
......@@ -2885,20 +2964,20 @@ test "zig fmt: multiline string in array" {
28852964 try testCanonical(
28862965 \\const Foo = [][]const u8{
28872966 \\ \\aaa
2888 \\,
2967 \\ ,
28892968 \\ \\bbb
28902969 \\};
28912970 \\
28922971 \\fn bar() void {
28932972 \\ const Foo = [][]const u8{
28942973 \\ \\aaa
2895 \\ ,
2974 \\ ,
28962975 \\ \\bbb
28972976 \\ };
28982977 \\ const Bar = [][]const u8{ // comment here
28992978 \\ \\aaa
29002979 \\ \\
2901 \\ , // and another comment can go here
2980 \\ , // and another comment can go here
29022981 \\ \\bbb
29032982 \\ };
29042983 \\}
......@@ -3214,6 +3293,34 @@ test "zig fmt: C var args" {
32143293 );
32153294}
32163295
3296test "zig fmt: Only indent multiline string literals in function calls" {
3297 try testCanonical(
3298 \\test "zig fmt:" {
3299 \\ try testTransform(
3300 \\ \\const X = struct {
3301 \\ \\ foo: i32, bar: i8 };
3302 \\ ,
3303 \\ \\const X = struct {
3304 \\ \\ foo: i32, bar: i8
3305 \\ \\};
3306 \\ \\
3307 \\ );
3308 \\}
3309 \\
3310 );
3311}
3312
3313test "zig fmt: Don't add extra newline after if" {
3314 try testCanonical(
3315 \\pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {
3316 \\ if (cwd().symLink(existing_path, new_path, .{})) {
3317 \\ return;
3318 \\ }
3319 \\}
3320 \\
3321 );
3322}
3323
32173324const std = @import("std");
32183325const mem = std.mem;
32193326const warn = std.debug.warn;
......@@ -3256,7 +3363,8 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
32563363 var buffer = std.ArrayList(u8).init(allocator);
32573364 errdefer buffer.deinit();
32583365
3259 anything_changed.* = try std.zig.render(allocator, buffer.outStream(), tree);
3366 const outStream = buffer.outStream();
3367 anything_changed.* = try std.zig.render(allocator, outStream, tree);
32603368 return buffer.toOwnedSlice();
32613369}
32623370fn testTransform(source: []const u8, expected_source: []const u8) !void {
lib/std/zig/render.zig+763-912
......@@ -6,10 +6,12 @@
66const std = @import("../std.zig");
77const assert = std.debug.assert;
88const mem = std.mem;
9const meta = std.meta;
910const ast = std.zig.ast;
1011const Token = std.zig.Token;
1112
1213const indent_delta = 4;
14const asm_indent_delta = 2;
1315
1416pub const Error = error{
1517 /// Ran out of memory allocating call stack frames to complete rendering.
......@@ -21,70 +23,32 @@ pub fn render(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree) (@Typ
2123 // cannot render an invalid tree
2224 std.debug.assert(tree.errors.len == 0);
2325
24 // make a passthrough stream that checks whether something changed
25 const MyStream = struct {
26 const MyStream = @This();
27 const StreamError = @TypeOf(stream).Error;
28
29 child_stream: @TypeOf(stream),
30 anything_changed: bool,
31 source_index: usize,
32 source: []const u8,
33
34 fn write(self: *MyStream, bytes: []const u8) StreamError!usize {
35 if (!self.anything_changed) {
36 const end = self.source_index + bytes.len;
37 if (end > self.source.len) {
38 self.anything_changed = true;
39 } else {
40 const src_slice = self.source[self.source_index..end];
41 self.source_index += bytes.len;
42 if (!mem.eql(u8, bytes, src_slice)) {
43 self.anything_changed = true;
44 }
45 }
46 }
47
48 return self.child_stream.write(bytes);
49 }
50 };
51 var my_stream = MyStream{
52 .child_stream = stream,
53 .anything_changed = false,
54 .source_index = 0,
55 .source = tree.source,
56 };
57 const my_stream_stream: std.io.Writer(*MyStream, MyStream.StreamError, MyStream.write) = .{
58 .context = &my_stream,
59 };
26 var change_detection_stream = std.io.changeDetectionStream(tree.source, stream);
27 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, change_detection_stream.writer());
6028
61 try renderRoot(allocator, my_stream_stream, tree);
29 try renderRoot(allocator, &auto_indenting_stream, tree);
6230
63 if (my_stream.source_index != my_stream.source.len) {
64 my_stream.anything_changed = true;
65 }
66
67 return my_stream.anything_changed;
31 return change_detection_stream.changeDetected();
6832}
6933
7034fn renderRoot(
7135 allocator: *mem.Allocator,
72 stream: anytype,
36 ais: anytype,
7337 tree: *ast.Tree,
74) (@TypeOf(stream).Error || Error)!void {
38) (@TypeOf(ais.*).Error || Error)!void {
39
7540 // render all the line comments at the beginning of the file
7641 for (tree.token_ids) |token_id, i| {
7742 if (token_id != .LineComment) break;
7843 const token_loc = tree.token_locs[i];
79 try stream.print("{}\n", .{mem.trimRight(u8, tree.tokenSliceLoc(token_loc), " ")});
44 try ais.writer().print("{}\n", .{mem.trimRight(u8, tree.tokenSliceLoc(token_loc), " ")});
8045 const next_token = tree.token_locs[i + 1];
8146 const loc = tree.tokenLocationLoc(token_loc.end, next_token);
8247 if (loc.line >= 2) {
83 try stream.writeByte('\n');
48 try ais.insertNewline();
8449 }
8550 }
8651
87 var start_col: usize = 0;
8852 var decl_i: ast.NodeIndex = 0;
8953 const root_decls = tree.root_node.decls();
9054
......@@ -145,7 +109,7 @@ fn renderRoot(
145109 // If there's no next reformatted `decl`, just copy the
146110 // remaining input tokens and bail out.
147111 const start = tree.token_locs[copy_start_token_index].start;
148 try copyFixingWhitespace(stream, tree.source[start..]);
112 try copyFixingWhitespace(ais, tree.source[start..]);
149113 return;
150114 }
151115 decl = root_decls[decl_i];
......@@ -186,26 +150,25 @@ fn renderRoot(
186150
187151 const start = tree.token_locs[copy_start_token_index].start;
188152 const end = tree.token_locs[copy_end_token_index].start;
189 try copyFixingWhitespace(stream, tree.source[start..end]);
153 try copyFixingWhitespace(ais, tree.source[start..end]);
190154 }
191155
192 try renderTopLevelDecl(allocator, stream, tree, 0, &start_col, decl);
156 try renderTopLevelDecl(allocator, ais, tree, decl);
193157 decl_i += 1;
194158 if (decl_i >= root_decls.len) return;
195 try renderExtraNewline(tree, stream, &start_col, root_decls[decl_i]);
159 try renderExtraNewline(tree, ais, root_decls[decl_i]);
196160 }
197161}
198162
199fn renderExtraNewline(tree: *ast.Tree, stream: anytype, start_col: *usize, node: *ast.Node) @TypeOf(stream).Error!void {
200 return renderExtraNewlineToken(tree, stream, start_col, node.firstToken());
163fn renderExtraNewline(tree: *ast.Tree, ais: anytype, node: *ast.Node) @TypeOf(ais.*).Error!void {
164 return renderExtraNewlineToken(tree, ais, node.firstToken());
201165}
202166
203167fn renderExtraNewlineToken(
204168 tree: *ast.Tree,
205 stream: anytype,
206 start_col: *usize,
169 ais: anytype,
207170 first_token: ast.TokenIndex,
208) @TypeOf(stream).Error!void {
171) @TypeOf(ais.*).Error!void {
209172 var prev_token = first_token;
210173 if (prev_token == 0) return;
211174 var newline_threshold: usize = 2;
......@@ -218,28 +181,27 @@ fn renderExtraNewlineToken(
218181 const prev_token_end = tree.token_locs[prev_token - 1].end;
219182 const loc = tree.tokenLocation(prev_token_end, first_token);
220183 if (loc.line >= newline_threshold) {
221 try stream.writeByte('\n');
222 start_col.* = 0;
184 try ais.insertNewline();
223185 }
224186}
225187
226fn renderTopLevelDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Error || Error)!void {
227 try renderContainerDecl(allocator, stream, tree, indent, start_col, decl, .Newline);
188fn renderTopLevelDecl(allocator: *mem.Allocator, ais: anytype, tree: *ast.Tree, decl: *ast.Node) (@TypeOf(ais.*).Error || Error)!void {
189 try renderContainerDecl(allocator, ais, tree, decl, .Newline);
228190}
229191
230fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Error || Error)!void {
192fn renderContainerDecl(allocator: *mem.Allocator, ais: anytype, tree: *ast.Tree, decl: *ast.Node, space: Space) (@TypeOf(ais.*).Error || Error)!void {
231193 switch (decl.tag) {
232194 .FnProto => {
233195 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
234196
235 try renderDocComments(tree, stream, fn_proto, fn_proto.getDocComments(), indent, start_col);
197 try renderDocComments(tree, ais, fn_proto, fn_proto.getDocComments());
236198
237199 if (fn_proto.getBodyNode()) |body_node| {
238 try renderExpression(allocator, stream, tree, indent, start_col, decl, .Space);
239 try renderExpression(allocator, stream, tree, indent, start_col, body_node, space);
200 try renderExpression(allocator, ais, tree, decl, .Space);
201 try renderExpression(allocator, ais, tree, body_node, space);
240202 } else {
241 try renderExpression(allocator, stream, tree, indent, start_col, decl, .None);
242 try renderToken(tree, stream, tree.nextToken(decl.lastToken()), indent, start_col, space);
203 try renderExpression(allocator, ais, tree, decl, .None);
204 try renderToken(tree, ais, tree.nextToken(decl.lastToken()), space);
243205 }
244206 },
245207
......@@ -247,35 +209,35 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tr
247209 const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);
248210
249211 if (use_decl.visib_token) |visib_token| {
250 try renderToken(tree, stream, visib_token, indent, start_col, .Space); // pub
212 try renderToken(tree, ais, visib_token, .Space); // pub
251213 }
252 try renderToken(tree, stream, use_decl.use_token, indent, start_col, .Space); // usingnamespace
253 try renderExpression(allocator, stream, tree, indent, start_col, use_decl.expr, .None);
254 try renderToken(tree, stream, use_decl.semicolon_token, indent, start_col, space); // ;
214 try renderToken(tree, ais, use_decl.use_token, .Space); // usingnamespace
215 try renderExpression(allocator, ais, tree, use_decl.expr, .None);
216 try renderToken(tree, ais, use_decl.semicolon_token, space); // ;
255217 },
256218
257219 .VarDecl => {
258220 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
259221
260 try renderDocComments(tree, stream, var_decl, var_decl.getDocComments(), indent, start_col);
261 try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl);
222 try renderDocComments(tree, ais, var_decl, var_decl.getDocComments());
223 try renderVarDecl(allocator, ais, tree, var_decl);
262224 },
263225
264226 .TestDecl => {
265227 const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
266228
267 try renderDocComments(tree, stream, test_decl, test_decl.doc_comments, indent, start_col);
268 try renderToken(tree, stream, test_decl.test_token, indent, start_col, .Space);
269 try renderExpression(allocator, stream, tree, indent, start_col, test_decl.name, .Space);
270 try renderExpression(allocator, stream, tree, indent, start_col, test_decl.body_node, space);
229 try renderDocComments(tree, ais, test_decl, test_decl.doc_comments);
230 try renderToken(tree, ais, test_decl.test_token, .Space);
231 try renderExpression(allocator, ais, tree, test_decl.name, .Space);
232 try renderExpression(allocator, ais, tree, test_decl.body_node, space);
271233 },
272234
273235 .ContainerField => {
274236 const field = @fieldParentPtr(ast.Node.ContainerField, "base", decl);
275237
276 try renderDocComments(tree, stream, field, field.doc_comments, indent, start_col);
238 try renderDocComments(tree, ais, field, field.doc_comments);
277239 if (field.comptime_token) |t| {
278 try renderToken(tree, stream, t, indent, start_col, .Space); // comptime
240 try renderToken(tree, ais, t, .Space); // comptime
279241 }
280242
281243 const src_has_trailing_comma = blk: {
......@@ -288,68 +250,67 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tr
288250 const last_token_space: Space = if (src_has_trailing_comma) .None else space;
289251
290252 if (field.type_expr == null and field.value_expr == null) {
291 try renderToken(tree, stream, field.name_token, indent, start_col, last_token_space); // name
253 try renderToken(tree, ais, field.name_token, last_token_space); // name
292254 } else if (field.type_expr != null and field.value_expr == null) {
293 try renderToken(tree, stream, field.name_token, indent, start_col, .None); // name
294 try renderToken(tree, stream, tree.nextToken(field.name_token), indent, start_col, .Space); // :
255 try renderToken(tree, ais, field.name_token, .None); // name
256 try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // :
295257
296258 if (field.align_expr) |align_value_expr| {
297 try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, .Space); // type
259 try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type
298260 const lparen_token = tree.prevToken(align_value_expr.firstToken());
299261 const align_kw = tree.prevToken(lparen_token);
300262 const rparen_token = tree.nextToken(align_value_expr.lastToken());
301 try renderToken(tree, stream, align_kw, indent, start_col, .None); // align
302 try renderToken(tree, stream, lparen_token, indent, start_col, .None); // (
303 try renderExpression(allocator, stream, tree, indent, start_col, align_value_expr, .None); // alignment
304 try renderToken(tree, stream, rparen_token, indent, start_col, last_token_space); // )
263 try renderToken(tree, ais, align_kw, .None); // align
264 try renderToken(tree, ais, lparen_token, .None); // (
265 try renderExpression(allocator, ais, tree, align_value_expr, .None); // alignment
266 try renderToken(tree, ais, rparen_token, last_token_space); // )
305267 } else {
306 try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, last_token_space); // type
268 try renderExpression(allocator, ais, tree, field.type_expr.?, last_token_space); // type
307269 }
308270 } else if (field.type_expr == null and field.value_expr != null) {
309 try renderToken(tree, stream, field.name_token, indent, start_col, .Space); // name
310 try renderToken(tree, stream, tree.nextToken(field.name_token), indent, start_col, .Space); // =
311 try renderExpression(allocator, stream, tree, indent, start_col, field.value_expr.?, last_token_space); // value
271 try renderToken(tree, ais, field.name_token, .Space); // name
272 try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // =
273 try renderExpression(allocator, ais, tree, field.value_expr.?, last_token_space); // value
312274 } else {
313 try renderToken(tree, stream, field.name_token, indent, start_col, .None); // name
314 try renderToken(tree, stream, tree.nextToken(field.name_token), indent, start_col, .Space); // :
275 try renderToken(tree, ais, field.name_token, .None); // name
276 try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // :
315277
316278 if (field.align_expr) |align_value_expr| {
317 try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, .Space); // type
279 try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type
318280 const lparen_token = tree.prevToken(align_value_expr.firstToken());
319281 const align_kw = tree.prevToken(lparen_token);
320282 const rparen_token = tree.nextToken(align_value_expr.lastToken());
321 try renderToken(tree, stream, align_kw, indent, start_col, .None); // align
322 try renderToken(tree, stream, lparen_token, indent, start_col, .None); // (
323 try renderExpression(allocator, stream, tree, indent, start_col, align_value_expr, .None); // alignment
324 try renderToken(tree, stream, rparen_token, indent, start_col, .Space); // )
283 try renderToken(tree, ais, align_kw, .None); // align
284 try renderToken(tree, ais, lparen_token, .None); // (
285 try renderExpression(allocator, ais, tree, align_value_expr, .None); // alignment
286 try renderToken(tree, ais, rparen_token, .Space); // )
325287 } else {
326 try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, .Space); // type
288 try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type
327289 }
328 try renderToken(tree, stream, tree.prevToken(field.value_expr.?.firstToken()), indent, start_col, .Space); // =
329 try renderExpression(allocator, stream, tree, indent, start_col, field.value_expr.?, last_token_space); // value
290 try renderToken(tree, ais, tree.prevToken(field.value_expr.?.firstToken()), .Space); // =
291 try renderExpression(allocator, ais, tree, field.value_expr.?, last_token_space); // value
330292 }
331293
332294 if (src_has_trailing_comma) {
333295 const comma = tree.nextToken(field.lastToken());
334 try renderToken(tree, stream, comma, indent, start_col, space);
296 try renderToken(tree, ais, comma, space);
335297 }
336298 },
337299
338300 .Comptime => {
339301 assert(!decl.requireSemiColon());
340 try renderExpression(allocator, stream, tree, indent, start_col, decl, space);
302 try renderExpression(allocator, ais, tree, decl, space);
341303 },
342304
343305 .DocComment => {
344306 const comment = @fieldParentPtr(ast.Node.DocComment, "base", decl);
345307 const kind = tree.token_ids[comment.first_line];
346 try renderToken(tree, stream, comment.first_line, indent, start_col, .Newline);
308 try renderToken(tree, ais, comment.first_line, .Newline);
347309 var tok_i = comment.first_line + 1;
348310 while (true) : (tok_i += 1) {
349311 const tok_id = tree.token_ids[tok_i];
350312 if (tok_id == kind) {
351 try stream.writeByteNTimes(' ', indent);
352 try renderToken(tree, stream, tok_i, indent, start_col, .Newline);
313 try renderToken(tree, ais, tok_i, .Newline);
353314 } else if (tok_id == .LineComment) {
354315 continue;
355316 } else {
......@@ -363,13 +324,11 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tr
363324
364325fn renderExpression(
365326 allocator: *mem.Allocator,
366 stream: anytype,
327 ais: anytype,
367328 tree: *ast.Tree,
368 indent: usize,
369 start_col: *usize,
370329 base: *ast.Node,
371330 space: Space,
372) (@TypeOf(stream).Error || Error)!void {
331) (@TypeOf(ais.*).Error || Error)!void {
373332 switch (base.tag) {
374333 .Identifier,
375334 .IntegerLiteral,
......@@ -383,18 +342,18 @@ fn renderExpression(
383342 .UndefinedLiteral,
384343 => {
385344 const casted_node = base.cast(ast.Node.OneToken).?;
386 return renderToken(tree, stream, casted_node.token, indent, start_col, space);
345 return renderToken(tree, ais, casted_node.token, space);
387346 },
388347
389348 .AnyType => {
390349 const any_type = base.castTag(.AnyType).?;
391350 if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) {
392351 // TODO remove in next release cycle
393 try stream.writeAll("anytype");
394 if (space == .Comma) try stream.writeAll(",\n");
352 try ais.writer().writeAll("anytype");
353 if (space == .Comma) try ais.writer().writeAll(",\n");
395354 return;
396355 }
397 return renderToken(tree, stream, any_type.token, indent, start_col, space);
356 return renderToken(tree, ais, any_type.token, space);
398357 },
399358
400359 .Block, .LabeledBlock => {
......@@ -424,65 +383,65 @@ fn renderExpression(
424383 };
425384
426385 if (block.label) |label| {
427 try renderToken(tree, stream, label, indent, start_col, Space.None);
428 try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space);
386 try renderToken(tree, ais, label, Space.None);
387 try renderToken(tree, ais, tree.nextToken(label), Space.Space);
429388 }
430389
431390 if (block.statements.len == 0) {
432 try renderToken(tree, stream, block.lbrace, indent + indent_delta, start_col, Space.None);
433 return renderToken(tree, stream, block.rbrace, indent, start_col, space);
391 ais.pushIndentNextLine();
392 defer ais.popIndent();
393 try renderToken(tree, ais, block.lbrace, Space.None);
434394 } else {
435 const block_indent = indent + indent_delta;
436 try renderToken(tree, stream, block.lbrace, block_indent, start_col, Space.Newline);
395 ais.pushIndentNextLine();
396 defer ais.popIndent();
397
398 try renderToken(tree, ais, block.lbrace, Space.Newline);
437399
438400 for (block.statements) |statement, i| {
439 try stream.writeByteNTimes(' ', block_indent);
440 try renderStatement(allocator, stream, tree, block_indent, start_col, statement);
401 try renderStatement(allocator, ais, tree, statement);
441402
442403 if (i + 1 < block.statements.len) {
443 try renderExtraNewline(tree, stream, start_col, block.statements[i + 1]);
404 try renderExtraNewline(tree, ais, block.statements[i + 1]);
444405 }
445406 }
446
447 try stream.writeByteNTimes(' ', indent);
448 return renderToken(tree, stream, block.rbrace, indent, start_col, space);
449407 }
408 return renderToken(tree, ais, block.rbrace, space);
450409 },
451410
452411 .Defer => {
453412 const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
454413
455 try renderToken(tree, stream, defer_node.defer_token, indent, start_col, Space.Space);
414 try renderToken(tree, ais, defer_node.defer_token, Space.Space);
456415 if (defer_node.payload) |payload| {
457 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
416 try renderExpression(allocator, ais, tree, payload, Space.Space);
458417 }
459 return renderExpression(allocator, stream, tree, indent, start_col, defer_node.expr, space);
418 return renderExpression(allocator, ais, tree, defer_node.expr, space);
460419 },
461420 .Comptime => {
462421 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
463422
464 try renderToken(tree, stream, comptime_node.comptime_token, indent, start_col, Space.Space);
465 return renderExpression(allocator, stream, tree, indent, start_col, comptime_node.expr, space);
423 try renderToken(tree, ais, comptime_node.comptime_token, Space.Space);
424 return renderExpression(allocator, ais, tree, comptime_node.expr, space);
466425 },
467426 .Nosuspend => {
468427 const nosuspend_node = @fieldParentPtr(ast.Node.Nosuspend, "base", base);
469428 if (mem.eql(u8, tree.tokenSlice(nosuspend_node.nosuspend_token), "noasync")) {
470429 // TODO: remove this
471 try stream.writeAll("nosuspend ");
430 try ais.writer().writeAll("nosuspend ");
472431 } else {
473 try renderToken(tree, stream, nosuspend_node.nosuspend_token, indent, start_col, Space.Space);
432 try renderToken(tree, ais, nosuspend_node.nosuspend_token, Space.Space);
474433 }
475 return renderExpression(allocator, stream, tree, indent, start_col, nosuspend_node.expr, space);
434 return renderExpression(allocator, ais, tree, nosuspend_node.expr, space);
476435 },
477436
478437 .Suspend => {
479438 const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
480439
481440 if (suspend_node.body) |body| {
482 try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space);
483 return renderExpression(allocator, stream, tree, indent, start_col, body, space);
441 try renderToken(tree, ais, suspend_node.suspend_token, Space.Space);
442 return renderExpression(allocator, ais, tree, body, space);
484443 } else {
485 return renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, space);
444 return renderToken(tree, ais, suspend_node.suspend_token, space);
486445 }
487446 },
488447
......@@ -490,26 +449,21 @@ fn renderExpression(
490449 const infix_op_node = @fieldParentPtr(ast.Node.Catch, "base", base);
491450
492451 const op_space = Space.Space;
493 try renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.lhs, op_space);
452 try renderExpression(allocator, ais, tree, infix_op_node.lhs, op_space);
494453
495454 const after_op_space = blk: {
496 const loc = tree.tokenLocation(tree.token_locs[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token));
497 break :blk if (loc.line == 0) op_space else Space.Newline;
455 const same_line = tree.tokensOnSameLine(infix_op_node.op_token, tree.nextToken(infix_op_node.op_token));
456 break :blk if (same_line) op_space else Space.Newline;
498457 };
499458
500 try renderToken(tree, stream, infix_op_node.op_token, indent, start_col, after_op_space);
501 if (after_op_space == Space.Newline and
502 tree.token_ids[tree.nextToken(infix_op_node.op_token)] != .MultilineStringLiteralLine)
503 {
504 try stream.writeByteNTimes(' ', indent + indent_delta);
505 start_col.* = indent + indent_delta;
506 }
459 try renderToken(tree, ais, infix_op_node.op_token, after_op_space);
507460
508461 if (infix_op_node.payload) |payload| {
509 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
462 try renderExpression(allocator, ais, tree, payload, Space.Space);
510463 }
511464
512 return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);
465 ais.pushIndentOneShot();
466 return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);
513467 },
514468
515469 .Add,
......@@ -561,22 +515,16 @@ fn renderExpression(
561515 .Period, .ErrorUnion, .Range => Space.None,
562516 else => Space.Space,
563517 };
564 try renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.lhs, op_space);
518 try renderExpression(allocator, ais, tree, infix_op_node.lhs, op_space);
565519
566520 const after_op_space = blk: {
567521 const loc = tree.tokenLocation(tree.token_locs[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token));
568522 break :blk if (loc.line == 0) op_space else Space.Newline;
569523 };
570524
571 try renderToken(tree, stream, infix_op_node.op_token, indent, start_col, after_op_space);
572 if (after_op_space == Space.Newline and
573 tree.token_ids[tree.nextToken(infix_op_node.op_token)] != .MultilineStringLiteralLine)
574 {
575 try stream.writeByteNTimes(' ', indent + indent_delta);
576 start_col.* = indent + indent_delta;
577 }
578
579 return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);
525 try renderToken(tree, ais, infix_op_node.op_token, after_op_space);
526 ais.pushIndentOneShot();
527 return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);
580528 },
581529
582530 .BitNot,
......@@ -587,8 +535,8 @@ fn renderExpression(
587535 .AddressOf,
588536 => {
589537 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
590 try renderToken(tree, stream, casted_node.op_token, indent, start_col, Space.None);
591 return renderExpression(allocator, stream, tree, indent, start_col, casted_node.rhs, space);
538 try renderToken(tree, ais, casted_node.op_token, Space.None);
539 return renderExpression(allocator, ais, tree, casted_node.rhs, space);
592540 },
593541
594542 .Try,
......@@ -596,18 +544,16 @@ fn renderExpression(
596544 .Await,
597545 => {
598546 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
599 try renderToken(tree, stream, casted_node.op_token, indent, start_col, Space.Space);
600 return renderExpression(allocator, stream, tree, indent, start_col, casted_node.rhs, space);
547 try renderToken(tree, ais, casted_node.op_token, Space.Space);
548 return renderExpression(allocator, ais, tree, casted_node.rhs, space);
601549 },
602550
603551 .ArrayType => {
604552 const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base);
605553 return renderArrayType(
606554 allocator,
607 stream,
555 ais,
608556 tree,
609 indent,
610 start_col,
611557 array_type.op_token,
612558 array_type.rhs,
613559 array_type.len_expr,
......@@ -619,10 +565,8 @@ fn renderExpression(
619565 const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base);
620566 return renderArrayType(
621567 allocator,
622 stream,
568 ais,
623569 tree,
624 indent,
625 start_col,
626570 array_type.op_token,
627571 array_type.rhs,
628572 array_type.len_expr,
......@@ -635,111 +579,111 @@ fn renderExpression(
635579 const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base);
636580 const op_tok_id = tree.token_ids[ptr_type.op_token];
637581 switch (op_tok_id) {
638 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
582 .Asterisk, .AsteriskAsterisk => try ais.writer().writeByte('*'),
639583 .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier)
640 try stream.writeAll("[*c")
584 try ais.writer().writeAll("[*c")
641585 else
642 try stream.writeAll("[*"),
586 try ais.writer().writeAll("[*"),
643587 else => unreachable,
644588 }
645589 if (ptr_type.ptr_info.sentinel) |sentinel| {
646590 const colon_token = tree.prevToken(sentinel.firstToken());
647 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
591 try renderToken(tree, ais, colon_token, Space.None); // :
648592 const sentinel_space = switch (op_tok_id) {
649593 .LBracket => Space.None,
650594 else => Space.Space,
651595 };
652 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space);
596 try renderExpression(allocator, ais, tree, sentinel, sentinel_space);
653597 }
654598 switch (op_tok_id) {
655599 .Asterisk, .AsteriskAsterisk => {},
656 .LBracket => try stream.writeByte(']'),
600 .LBracket => try ais.writer().writeByte(']'),
657601 else => unreachable,
658602 }
659603 if (ptr_type.ptr_info.allowzero_token) |allowzero_token| {
660 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
604 try renderToken(tree, ais, allowzero_token, Space.Space); // allowzero
661605 }
662606 if (ptr_type.ptr_info.align_info) |align_info| {
663607 const lparen_token = tree.prevToken(align_info.node.firstToken());
664608 const align_token = tree.prevToken(lparen_token);
665609
666 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
667 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
610 try renderToken(tree, ais, align_token, Space.None); // align
611 try renderToken(tree, ais, lparen_token, Space.None); // (
668612
669 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
613 try renderExpression(allocator, ais, tree, align_info.node, Space.None);
670614
671615 if (align_info.bit_range) |bit_range| {
672616 const colon1 = tree.prevToken(bit_range.start.firstToken());
673617 const colon2 = tree.prevToken(bit_range.end.firstToken());
674618
675 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
676 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
677 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
678 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
619 try renderToken(tree, ais, colon1, Space.None); // :
620 try renderExpression(allocator, ais, tree, bit_range.start, Space.None);
621 try renderToken(tree, ais, colon2, Space.None); // :
622 try renderExpression(allocator, ais, tree, bit_range.end, Space.None);
679623
680624 const rparen_token = tree.nextToken(bit_range.end.lastToken());
681 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
625 try renderToken(tree, ais, rparen_token, Space.Space); // )
682626 } else {
683627 const rparen_token = tree.nextToken(align_info.node.lastToken());
684 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
628 try renderToken(tree, ais, rparen_token, Space.Space); // )
685629 }
686630 }
687631 if (ptr_type.ptr_info.const_token) |const_token| {
688 try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const
632 try renderToken(tree, ais, const_token, Space.Space); // const
689633 }
690634 if (ptr_type.ptr_info.volatile_token) |volatile_token| {
691 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile
635 try renderToken(tree, ais, volatile_token, Space.Space); // volatile
692636 }
693 return renderExpression(allocator, stream, tree, indent, start_col, ptr_type.rhs, space);
637 return renderExpression(allocator, ais, tree, ptr_type.rhs, space);
694638 },
695639
696640 .SliceType => {
697641 const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base);
698 try renderToken(tree, stream, slice_type.op_token, indent, start_col, Space.None); // [
642 try renderToken(tree, ais, slice_type.op_token, Space.None); // [
699643 if (slice_type.ptr_info.sentinel) |sentinel| {
700644 const colon_token = tree.prevToken(sentinel.firstToken());
701 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
702 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
703 try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ]
645 try renderToken(tree, ais, colon_token, Space.None); // :
646 try renderExpression(allocator, ais, tree, sentinel, Space.None);
647 try renderToken(tree, ais, tree.nextToken(sentinel.lastToken()), Space.None); // ]
704648 } else {
705 try renderToken(tree, stream, tree.nextToken(slice_type.op_token), indent, start_col, Space.None); // ]
649 try renderToken(tree, ais, tree.nextToken(slice_type.op_token), Space.None); // ]
706650 }
707651
708652 if (slice_type.ptr_info.allowzero_token) |allowzero_token| {
709 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
653 try renderToken(tree, ais, allowzero_token, Space.Space); // allowzero
710654 }
711655 if (slice_type.ptr_info.align_info) |align_info| {
712656 const lparen_token = tree.prevToken(align_info.node.firstToken());
713657 const align_token = tree.prevToken(lparen_token);
714658
715 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
716 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
659 try renderToken(tree, ais, align_token, Space.None); // align
660 try renderToken(tree, ais, lparen_token, Space.None); // (
717661
718 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
662 try renderExpression(allocator, ais, tree, align_info.node, Space.None);
719663
720664 if (align_info.bit_range) |bit_range| {
721665 const colon1 = tree.prevToken(bit_range.start.firstToken());
722666 const colon2 = tree.prevToken(bit_range.end.firstToken());
723667
724 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
725 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
726 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
727 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
668 try renderToken(tree, ais, colon1, Space.None); // :
669 try renderExpression(allocator, ais, tree, bit_range.start, Space.None);
670 try renderToken(tree, ais, colon2, Space.None); // :
671 try renderExpression(allocator, ais, tree, bit_range.end, Space.None);
728672
729673 const rparen_token = tree.nextToken(bit_range.end.lastToken());
730 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
674 try renderToken(tree, ais, rparen_token, Space.Space); // )
731675 } else {
732676 const rparen_token = tree.nextToken(align_info.node.lastToken());
733 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
677 try renderToken(tree, ais, rparen_token, Space.Space); // )
734678 }
735679 }
736680 if (slice_type.ptr_info.const_token) |const_token| {
737 try renderToken(tree, stream, const_token, indent, start_col, Space.Space);
681 try renderToken(tree, ais, const_token, Space.Space);
738682 }
739683 if (slice_type.ptr_info.volatile_token) |volatile_token| {
740 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space);
684 try renderToken(tree, ais, volatile_token, Space.Space);
741685 }
742 return renderExpression(allocator, stream, tree, indent, start_col, slice_type.rhs, space);
686 return renderExpression(allocator, ais, tree, slice_type.rhs, space);
743687 },
744688
745689 .ArrayInitializer, .ArrayInitializerDot => {
......@@ -768,27 +712,33 @@ fn renderExpression(
768712
769713 if (exprs.len == 0) {
770714 switch (lhs) {
771 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
772 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
715 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
716 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
717 }
718
719 {
720 ais.pushIndent();
721 defer ais.popIndent();
722 try renderToken(tree, ais, lbrace, Space.None);
773723 }
774 try renderToken(tree, stream, lbrace, indent, start_col, Space.None);
775 return renderToken(tree, stream, rtoken, indent, start_col, space);
776 }
777724
778 if (exprs.len == 1 and tree.token_ids[exprs[0].lastToken() + 1] == .RBrace) {
725 return renderToken(tree, ais, rtoken, space);
726 }
727 if (exprs.len == 1 and tree.token_ids[exprs[0].*.lastToken() + 1] == .RBrace) {
779728 const expr = exprs[0];
729
780730 switch (lhs) {
781 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
782 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
731 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
732 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
783733 }
784 try renderToken(tree, stream, lbrace, indent, start_col, Space.None);
785 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None);
786 return renderToken(tree, stream, rtoken, indent, start_col, space);
734 try renderToken(tree, ais, lbrace, Space.None);
735 try renderExpression(allocator, ais, tree, expr, Space.None);
736 return renderToken(tree, ais, rtoken, space);
787737 }
788738
789739 switch (lhs) {
790 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
791 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
740 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
741 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
792742 }
793743
794744 // scan to find row size
......@@ -830,79 +780,70 @@ fn renderExpression(
830780 var expr_widths = widths[0 .. widths.len - row_size];
831781 var column_widths = widths[widths.len - row_size ..];
832782
833 // Null stream for counting the printed length of each expression
783 // Null ais for counting the printed length of each expression
834784 var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
785 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer());
835786
836787 for (exprs) |expr, i| {
837788 counting_stream.bytes_written = 0;
838 var dummy_col: usize = 0;
839 try renderExpression(allocator, counting_stream.outStream(), tree, indent, &dummy_col, expr, Space.None);
789 try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
840790 const width = @intCast(usize, counting_stream.bytes_written);
841791 const col = i % row_size;
842792 column_widths[col] = std.math.max(column_widths[col], width);
843793 expr_widths[i] = width;
844794 }
845795
846 var new_indent = indent + indent_delta;
796 {
797 ais.pushIndentNextLine();
798 defer ais.popIndent();
799 try renderToken(tree, ais, lbrace, Space.Newline);
847800
848 if (tree.token_ids[tree.nextToken(lbrace)] != .MultilineStringLiteralLine) {
849 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline);
850 try stream.writeByteNTimes(' ', new_indent);
851 } else {
852 new_indent -= indent_delta;
853 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.None);
854 }
801 var col: usize = 1;
802 for (exprs) |expr, i| {
803 if (i + 1 < exprs.len) {
804 const next_expr = exprs[i + 1];
805 try renderExpression(allocator, ais, tree, expr, Space.None);
855806
856 var col: usize = 1;
857 for (exprs) |expr, i| {
858 if (i + 1 < exprs.len) {
859 const next_expr = exprs[i + 1];
860 try renderExpression(allocator, stream, tree, new_indent, start_col, expr, Space.None);
807 const comma = tree.nextToken(expr.*.lastToken());
861808
862 const comma = tree.nextToken(expr.lastToken());
809 if (col != row_size) {
810 try renderToken(tree, ais, comma, Space.Space); // ,
863811
864 if (col != row_size) {
865 try renderToken(tree, stream, comma, new_indent, start_col, Space.Space); // ,
812 const padding = column_widths[i % row_size] - expr_widths[i];
813 try ais.writer().writeByteNTimes(' ', padding);
866814
867 const padding = column_widths[i % row_size] - expr_widths[i];
868 try stream.writeByteNTimes(' ', padding);
815 col += 1;
816 continue;
817 }
818 col = 1;
869819
870 col += 1;
871 continue;
872 }
873 col = 1;
820 if (tree.token_ids[tree.nextToken(comma)] != .MultilineStringLiteralLine) {
821 try renderToken(tree, ais, comma, Space.Newline); // ,
822 } else {
823 try renderToken(tree, ais, comma, Space.None); // ,
824 }
874825
875 if (tree.token_ids[tree.nextToken(comma)] != .MultilineStringLiteralLine) {
876 try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline); // ,
826 try renderExtraNewline(tree, ais, next_expr);
877827 } else {
878 try renderToken(tree, stream, comma, new_indent, start_col, Space.None); // ,
879 }
880
881 try renderExtraNewline(tree, stream, start_col, next_expr);
882 if (next_expr.tag != .MultilineStringLiteral) {
883 try stream.writeByteNTimes(' ', new_indent);
828 try renderExpression(allocator, ais, tree, expr, Space.Comma); // ,
884829 }
885 } else {
886 try renderExpression(allocator, stream, tree, new_indent, start_col, expr, Space.Comma); // ,
887830 }
888831 }
889 if (exprs[exprs.len - 1].tag != .MultilineStringLiteral) {
890 try stream.writeByteNTimes(' ', indent);
891 }
892 return renderToken(tree, stream, rtoken, indent, start_col, space);
832 return renderToken(tree, ais, rtoken, space);
893833 } else {
894 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
834 try renderToken(tree, ais, lbrace, Space.Space);
895835 for (exprs) |expr, i| {
896836 if (i + 1 < exprs.len) {
897 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None);
898 const comma = tree.nextToken(expr.lastToken());
899 try renderToken(tree, stream, comma, indent, start_col, Space.Space); // ,
837 const next_expr = exprs[i + 1];
838 try renderExpression(allocator, ais, tree, expr, Space.None);
839 const comma = tree.nextToken(expr.*.lastToken());
840 try renderToken(tree, ais, comma, Space.Space); // ,
900841 } else {
901 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.Space);
842 try renderExpression(allocator, ais, tree, expr, Space.Space);
902843 }
903844 }
904845
905 return renderToken(tree, stream, rtoken, indent, start_col, space);
846 return renderToken(tree, ais, rtoken, space);
906847 }
907848 },
908849
......@@ -932,11 +873,17 @@ fn renderExpression(
932873
933874 if (field_inits.len == 0) {
934875 switch (lhs) {
935 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
936 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
876 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
877 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
937878 }
938 try renderToken(tree, stream, lbrace, indent + indent_delta, start_col, Space.None);
939 return renderToken(tree, stream, rtoken, indent, start_col, space);
879
880 {
881 ais.pushIndentNextLine();
882 defer ais.popIndent();
883 try renderToken(tree, ais, lbrace, Space.None);
884 }
885
886 return renderToken(tree, ais, rtoken, space);
940887 }
941888
942889 const src_has_trailing_comma = blk: {
......@@ -952,9 +899,10 @@ fn renderExpression(
952899 const expr_outputs_one_line = blk: {
953900 // render field expressions until a LF is found
954901 for (field_inits) |field_init| {
955 var find_stream = FindByteOutStream.init('\n');
956 var dummy_col: usize = 0;
957 try renderExpression(allocator, find_stream.outStream(), tree, 0, &dummy_col, field_init, Space.None);
902 var find_stream = std.io.findByteOutStream('\n', std.io.null_out_stream);
903 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, find_stream.writer());
904
905 try renderExpression(allocator, &auto_indenting_stream, tree, field_init, Space.None);
958906 if (find_stream.byte_found) break :blk false;
959907 }
960908 break :blk true;
......@@ -967,7 +915,6 @@ fn renderExpression(
967915 .StructInitializer,
968916 .StructInitializerDot,
969917 => break :blk,
970
971918 else => {},
972919 }
973920
......@@ -977,76 +924,78 @@ fn renderExpression(
977924 }
978925
979926 switch (lhs) {
980 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
981 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
927 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
928 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
982929 }
983 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
984 try renderExpression(allocator, stream, tree, indent, start_col, &field_init.base, Space.Space);
985 return renderToken(tree, stream, rtoken, indent, start_col, space);
930 try renderToken(tree, ais, lbrace, Space.Space);
931 try renderExpression(allocator, ais, tree, &field_init.base, Space.Space);
932 return renderToken(tree, ais, rtoken, space);
986933 }
987934
988935 if (!src_has_trailing_comma and src_same_line and expr_outputs_one_line) {
989936 // render all on one line, no trailing comma
990937 switch (lhs) {
991 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
992 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
938 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
939 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
993940 }
994 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
941 try renderToken(tree, ais, lbrace, Space.Space);
995942
996943 for (field_inits) |field_init, i| {
997944 if (i + 1 < field_inits.len) {
998 try renderExpression(allocator, stream, tree, indent, start_col, field_init, Space.None);
945 try renderExpression(allocator, ais, tree, field_init, Space.None);
999946
1000947 const comma = tree.nextToken(field_init.lastToken());
1001 try renderToken(tree, stream, comma, indent, start_col, Space.Space);
948 try renderToken(tree, ais, comma, Space.Space);
1002949 } else {
1003 try renderExpression(allocator, stream, tree, indent, start_col, field_init, Space.Space);
950 try renderExpression(allocator, ais, tree, field_init, Space.Space);
1004951 }
1005952 }
1006953
1007 return renderToken(tree, stream, rtoken, indent, start_col, space);
954 return renderToken(tree, ais, rtoken, space);
1008955 }
1009956
1010 const new_indent = indent + indent_delta;
957 {
958 switch (lhs) {
959 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
960 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
961 }
1011962
1012 switch (lhs) {
1013 .dot => |dot| try renderToken(tree, stream, dot, new_indent, start_col, Space.None),
1014 .node => |node| try renderExpression(allocator, stream, tree, new_indent, start_col, node, Space.None),
1015 }
1016 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline);
963 ais.pushIndentNextLine();
964 defer ais.popIndent();
1017965
1018 for (field_inits) |field_init, i| {
1019 try stream.writeByteNTimes(' ', new_indent);
966 try renderToken(tree, ais, lbrace, Space.Newline);
1020967
1021 if (i + 1 < field_inits.len) {
1022 try renderExpression(allocator, stream, tree, new_indent, start_col, field_init, Space.None);
968 for (field_inits) |field_init, i| {
969 if (i + 1 < field_inits.len) {
970 const next_field_init = field_inits[i + 1];
971 try renderExpression(allocator, ais, tree, field_init, Space.None);
1023972
1024 const comma = tree.nextToken(field_init.lastToken());
1025 try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline);
973 const comma = tree.nextToken(field_init.lastToken());
974 try renderToken(tree, ais, comma, Space.Newline);
1026975
1027 try renderExtraNewline(tree, stream, start_col, field_inits[i + 1]);
1028 } else {
1029 try renderExpression(allocator, stream, tree, new_indent, start_col, field_init, Space.Comma);
976 try renderExtraNewline(tree, ais, next_field_init);
977 } else {
978 try renderExpression(allocator, ais, tree, field_init, Space.Comma);
979 }
1030980 }
1031981 }
1032982
1033 try stream.writeByteNTimes(' ', indent);
1034 return renderToken(tree, stream, rtoken, indent, start_col, space);
983 return renderToken(tree, ais, rtoken, space);
1035984 },
1036985
1037986 .Call => {
1038987 const call = @fieldParentPtr(ast.Node.Call, "base", base);
1039988 if (call.async_token) |async_token| {
1040 try renderToken(tree, stream, async_token, indent, start_col, Space.Space);
989 try renderToken(tree, ais, async_token, Space.Space);
1041990 }
1042991
1043 try renderExpression(allocator, stream, tree, indent, start_col, call.lhs, Space.None);
992 try renderExpression(allocator, ais, tree, call.lhs, Space.None);
1044993
1045994 const lparen = tree.nextToken(call.lhs.lastToken());
1046995
1047996 if (call.params_len == 0) {
1048 try renderToken(tree, stream, lparen, indent, start_col, Space.None);
1049 return renderToken(tree, stream, call.rtoken, indent, start_col, space);
997 try renderToken(tree, ais, lparen, Space.None);
998 return renderToken(tree, ais, call.rtoken, space);
1050999 }
10511000
10521001 const src_has_trailing_comma = blk: {
......@@ -1055,43 +1004,41 @@ fn renderExpression(
10551004 };
10561005
10571006 if (src_has_trailing_comma) {
1058 const new_indent = indent + indent_delta;
1059 try renderToken(tree, stream, lparen, new_indent, start_col, Space.Newline);
1007 try renderToken(tree, ais, lparen, Space.Newline);
10601008
10611009 const params = call.params();
10621010 for (params) |param_node, i| {
1063 const param_node_new_indent = if (param_node.tag == .MultilineStringLiteral) blk: {
1064 break :blk indent;
1065 } else blk: {
1066 try stream.writeByteNTimes(' ', new_indent);
1067 break :blk new_indent;
1068 };
1011 ais.pushIndent();
1012 defer ais.popIndent();
10691013
10701014 if (i + 1 < params.len) {
1071 try renderExpression(allocator, stream, tree, param_node_new_indent, start_col, param_node, Space.None);
1015 const next_node = params[i + 1];
1016 try renderExpression(allocator, ais, tree, param_node, Space.None);
10721017 const comma = tree.nextToken(param_node.lastToken());
1073 try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline); // ,
1074 try renderExtraNewline(tree, stream, start_col, params[i + 1]);
1018 try renderToken(tree, ais, comma, Space.Newline); // ,
1019 try renderExtraNewline(tree, ais, next_node);
10751020 } else {
1076 try renderExpression(allocator, stream, tree, param_node_new_indent, start_col, param_node, Space.Comma);
1077 try stream.writeByteNTimes(' ', indent);
1078 return renderToken(tree, stream, call.rtoken, indent, start_col, space);
1021 try renderExpression(allocator, ais, tree, param_node, Space.Comma);
10791022 }
10801023 }
1024 return renderToken(tree, ais, call.rtoken, space);
10811025 }
10821026
1083 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
1027 try renderToken(tree, ais, lparen, Space.None); // (
10841028
10851029 const params = call.params();
10861030 for (params) |param_node, i| {
1087 try renderExpression(allocator, stream, tree, indent, start_col, param_node, Space.None);
1031 if (param_node.*.tag == .MultilineStringLiteral) ais.pushIndentOneShot();
1032
1033 try renderExpression(allocator, ais, tree, param_node, Space.None);
10881034
10891035 if (i + 1 < params.len) {
1036 const next_param = params[i + 1];
10901037 const comma = tree.nextToken(param_node.lastToken());
1091 try renderToken(tree, stream, comma, indent, start_col, Space.Space);
1038 try renderToken(tree, ais, comma, Space.Space);
10921039 }
10931040 }
1094 return renderToken(tree, stream, call.rtoken, indent, start_col, space);
1041 return renderToken(tree, ais, call.rtoken, space);
10951042 },
10961043
10971044 .ArrayAccess => {
......@@ -1100,26 +1047,25 @@ fn renderExpression(
11001047 const lbracket = tree.nextToken(suffix_op.lhs.lastToken());
11011048 const rbracket = tree.nextToken(suffix_op.index_expr.lastToken());
11021049
1103 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
1104 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
1050 try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
1051 try renderToken(tree, ais, lbracket, Space.None); // [
11051052
11061053 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
11071054 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
1108 const new_indent = if (ends_with_comment) indent + indent_delta else indent;
1109 const new_space = if (ends_with_comment) Space.Newline else Space.None;
1110 try renderExpression(allocator, stream, tree, new_indent, start_col, suffix_op.index_expr, new_space);
1111 if (starts_with_comment) {
1112 try stream.writeByte('\n');
1113 }
1114 if (ends_with_comment or starts_with_comment) {
1115 try stream.writeByteNTimes(' ', indent);
1055 {
1056 const new_space = if (ends_with_comment) Space.Newline else Space.None;
1057
1058 ais.pushIndent();
1059 defer ais.popIndent();
1060 try renderExpression(allocator, ais, tree, suffix_op.index_expr, new_space);
11161061 }
1117 return renderToken(tree, stream, rbracket, indent, start_col, space); // ]
1062 if (starts_with_comment) try ais.maybeInsertNewline();
1063 return renderToken(tree, ais, rbracket, space); // ]
11181064 },
1065
11191066 .Slice => {
11201067 const suffix_op = base.castTag(.Slice).?;
1121
1122 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
1068 try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
11231069
11241070 const lbracket = tree.prevToken(suffix_op.start.firstToken());
11251071 const dotdot = tree.nextToken(suffix_op.start.lastToken());
......@@ -1129,32 +1075,33 @@ fn renderExpression(
11291075 const after_start_space = if (after_start_space_bool) Space.Space else Space.None;
11301076 const after_op_space = if (suffix_op.end != null) after_start_space else Space.None;
11311077
1132 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
1133 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.start, after_start_space);
1134 try renderToken(tree, stream, dotdot, indent, start_col, after_op_space); // ..
1078 try renderToken(tree, ais, lbracket, Space.None); // [
1079 try renderExpression(allocator, ais, tree, suffix_op.start, after_start_space);
1080 try renderToken(tree, ais, dotdot, after_op_space); // ..
11351081 if (suffix_op.end) |end| {
11361082 const after_end_space = if (suffix_op.sentinel != null) Space.Space else Space.None;
1137 try renderExpression(allocator, stream, tree, indent, start_col, end, after_end_space);
1083 try renderExpression(allocator, ais, tree, end, after_end_space);
11381084 }
11391085 if (suffix_op.sentinel) |sentinel| {
11401086 const colon = tree.prevToken(sentinel.firstToken());
1141 try renderToken(tree, stream, colon, indent, start_col, Space.None); // :
1142 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
1087 try renderToken(tree, ais, colon, Space.None); // :
1088 try renderExpression(allocator, ais, tree, sentinel, Space.None);
11431089 }
1144 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ]
1090 return renderToken(tree, ais, suffix_op.rtoken, space); // ]
11451091 },
1092
11461093 .Deref => {
11471094 const suffix_op = base.castTag(.Deref).?;
11481095
1149 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
1150 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // .*
1096 try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
1097 return renderToken(tree, ais, suffix_op.rtoken, space); // .*
11511098 },
11521099 .UnwrapOptional => {
11531100 const suffix_op = base.castTag(.UnwrapOptional).?;
11541101
1155 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
1156 try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // .
1157 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ?
1102 try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
1103 try renderToken(tree, ais, tree.prevToken(suffix_op.rtoken), Space.None); // .
1104 return renderToken(tree, ais, suffix_op.rtoken, space); // ?
11581105 },
11591106
11601107 .Break => {
......@@ -1163,145 +1110,152 @@ fn renderExpression(
11631110 const maybe_label = flow_expr.getLabel();
11641111
11651112 if (maybe_label == null and maybe_rhs == null) {
1166 return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // break
1113 return renderToken(tree, ais, flow_expr.ltoken, space); // break
11671114 }
11681115
1169 try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // break
1116 try renderToken(tree, ais, flow_expr.ltoken, Space.Space); // break
11701117 if (maybe_label) |label| {
11711118 const colon = tree.nextToken(flow_expr.ltoken);
1172 try renderToken(tree, stream, colon, indent, start_col, Space.None); // :
1119 try renderToken(tree, ais, colon, Space.None); // :
11731120
11741121 if (maybe_rhs == null) {
1175 return renderToken(tree, stream, label, indent, start_col, space); // label
1122 return renderToken(tree, ais, label, space); // label
11761123 }
1177 try renderToken(tree, stream, label, indent, start_col, Space.Space); // label
1124 try renderToken(tree, ais, label, Space.Space); // label
11781125 }
1179 return renderExpression(allocator, stream, tree, indent, start_col, maybe_rhs.?, space);
1126 return renderExpression(allocator, ais, tree, maybe_rhs.?, space);
11801127 },
11811128
11821129 .Continue => {
11831130 const flow_expr = base.castTag(.Continue).?;
11841131 if (flow_expr.getLabel()) |label| {
1185 try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // continue
1132 try renderToken(tree, ais, flow_expr.ltoken, Space.Space); // continue
11861133 const colon = tree.nextToken(flow_expr.ltoken);
1187 try renderToken(tree, stream, colon, indent, start_col, Space.None); // :
1188 return renderToken(tree, stream, label, indent, start_col, space); // label
1134 try renderToken(tree, ais, colon, Space.None); // :
1135 return renderToken(tree, ais, label, space); // label
11891136 } else {
1190 return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // continue
1137 return renderToken(tree, ais, flow_expr.ltoken, space); // continue
11911138 }
11921139 },
11931140
11941141 .Return => {
11951142 const flow_expr = base.castTag(.Return).?;
11961143 if (flow_expr.getRHS()) |rhs| {
1197 try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space);
1198 return renderExpression(allocator, stream, tree, indent, start_col, rhs, space);
1144 try renderToken(tree, ais, flow_expr.ltoken, Space.Space);
1145 return renderExpression(allocator, ais, tree, rhs, space);
11991146 } else {
1200 return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space);
1147 return renderToken(tree, ais, flow_expr.ltoken, space);
12011148 }
12021149 },
12031150
12041151 .Payload => {
12051152 const payload = @fieldParentPtr(ast.Node.Payload, "base", base);
12061153
1207 try renderToken(tree, stream, payload.lpipe, indent, start_col, Space.None);
1208 try renderExpression(allocator, stream, tree, indent, start_col, payload.error_symbol, Space.None);
1209 return renderToken(tree, stream, payload.rpipe, indent, start_col, space);
1154 try renderToken(tree, ais, payload.lpipe, Space.None);
1155 try renderExpression(allocator, ais, tree, payload.error_symbol, Space.None);
1156 return renderToken(tree, ais, payload.rpipe, space);
12101157 },
12111158
12121159 .PointerPayload => {
12131160 const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base);
12141161
1215 try renderToken(tree, stream, payload.lpipe, indent, start_col, Space.None);
1162 try renderToken(tree, ais, payload.lpipe, Space.None);
12161163 if (payload.ptr_token) |ptr_token| {
1217 try renderToken(tree, stream, ptr_token, indent, start_col, Space.None);
1164 try renderToken(tree, ais, ptr_token, Space.None);
12181165 }
1219 try renderExpression(allocator, stream, tree, indent, start_col, payload.value_symbol, Space.None);
1220 return renderToken(tree, stream, payload.rpipe, indent, start_col, space);
1166 try renderExpression(allocator, ais, tree, payload.value_symbol, Space.None);
1167 return renderToken(tree, ais, payload.rpipe, space);
12211168 },
12221169
12231170 .PointerIndexPayload => {
12241171 const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base);
12251172
1226 try renderToken(tree, stream, payload.lpipe, indent, start_col, Space.None);
1173 try renderToken(tree, ais, payload.lpipe, Space.None);
12271174 if (payload.ptr_token) |ptr_token| {
1228 try renderToken(tree, stream, ptr_token, indent, start_col, Space.None);
1175 try renderToken(tree, ais, ptr_token, Space.None);
12291176 }
1230 try renderExpression(allocator, stream, tree, indent, start_col, payload.value_symbol, Space.None);
1177 try renderExpression(allocator, ais, tree, payload.value_symbol, Space.None);
12311178
12321179 if (payload.index_symbol) |index_symbol| {
12331180 const comma = tree.nextToken(payload.value_symbol.lastToken());
12341181
1235 try renderToken(tree, stream, comma, indent, start_col, Space.Space);
1236 try renderExpression(allocator, stream, tree, indent, start_col, index_symbol, Space.None);
1182 try renderToken(tree, ais, comma, Space.Space);
1183 try renderExpression(allocator, ais, tree, index_symbol, Space.None);
12371184 }
12381185
1239 return renderToken(tree, stream, payload.rpipe, indent, start_col, space);
1186 return renderToken(tree, ais, payload.rpipe, space);
12401187 },
12411188
12421189 .GroupedExpression => {
12431190 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);
12441191
1245 try renderToken(tree, stream, grouped_expr.lparen, indent, start_col, Space.None);
1246 try renderExpression(allocator, stream, tree, indent, start_col, grouped_expr.expr, Space.None);
1247 return renderToken(tree, stream, grouped_expr.rparen, indent, start_col, space);
1192 try renderToken(tree, ais, grouped_expr.lparen, Space.None);
1193 {
1194 ais.pushIndentOneShot();
1195 try renderExpression(allocator, ais, tree, grouped_expr.expr, Space.None);
1196 }
1197 return renderToken(tree, ais, grouped_expr.rparen, space);
12481198 },
12491199
12501200 .FieldInitializer => {
12511201 const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);
12521202
1253 try renderToken(tree, stream, field_init.period_token, indent, start_col, Space.None); // .
1254 try renderToken(tree, stream, field_init.name_token, indent, start_col, Space.Space); // name
1255 try renderToken(tree, stream, tree.nextToken(field_init.name_token), indent, start_col, Space.Space); // =
1256 return renderExpression(allocator, stream, tree, indent, start_col, field_init.expr, space);
1203 try renderToken(tree, ais, field_init.period_token, Space.None); // .
1204 try renderToken(tree, ais, field_init.name_token, Space.Space); // name
1205 try renderToken(tree, ais, tree.nextToken(field_init.name_token), Space.Space); // =
1206 return renderExpression(allocator, ais, tree, field_init.expr, space);
12571207 },
12581208
12591209 .ContainerDecl => {
12601210 const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
12611211
12621212 if (container_decl.layout_token) |layout_token| {
1263 try renderToken(tree, stream, layout_token, indent, start_col, Space.Space);
1213 try renderToken(tree, ais, layout_token, Space.Space);
12641214 }
12651215
12661216 switch (container_decl.init_arg_expr) {
12671217 .None => {
1268 try renderToken(tree, stream, container_decl.kind_token, indent, start_col, Space.Space); // union
1218 try renderToken(tree, ais, container_decl.kind_token, Space.Space); // union
12691219 },
12701220 .Enum => |enum_tag_type| {
1271 try renderToken(tree, stream, container_decl.kind_token, indent, start_col, Space.None); // union
1221 try renderToken(tree, ais, container_decl.kind_token, Space.None); // union
12721222
12731223 const lparen = tree.nextToken(container_decl.kind_token);
12741224 const enum_token = tree.nextToken(lparen);
12751225
1276 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
1277 try renderToken(tree, stream, enum_token, indent, start_col, Space.None); // enum
1226 try renderToken(tree, ais, lparen, Space.None); // (
1227 try renderToken(tree, ais, enum_token, Space.None); // enum
12781228
12791229 if (enum_tag_type) |expr| {
1280 try renderToken(tree, stream, tree.nextToken(enum_token), indent, start_col, Space.None); // (
1281 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None);
1230 try renderToken(tree, ais, tree.nextToken(enum_token), Space.None); // (
1231 try renderExpression(allocator, ais, tree, expr, Space.None);
12821232
12831233 const rparen = tree.nextToken(expr.lastToken());
1284 try renderToken(tree, stream, rparen, indent, start_col, Space.None); // )
1285 try renderToken(tree, stream, tree.nextToken(rparen), indent, start_col, Space.Space); // )
1234 try renderToken(tree, ais, rparen, Space.None); // )
1235 try renderToken(tree, ais, tree.nextToken(rparen), Space.Space); // )
12861236 } else {
1287 try renderToken(tree, stream, tree.nextToken(enum_token), indent, start_col, Space.Space); // )
1237 try renderToken(tree, ais, tree.nextToken(enum_token), Space.Space); // )
12881238 }
12891239 },
12901240 .Type => |type_expr| {
1291 try renderToken(tree, stream, container_decl.kind_token, indent, start_col, Space.None); // union
1241 try renderToken(tree, ais, container_decl.kind_token, Space.None); // union
12921242
12931243 const lparen = tree.nextToken(container_decl.kind_token);
12941244 const rparen = tree.nextToken(type_expr.lastToken());
12951245
1296 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
1297 try renderExpression(allocator, stream, tree, indent, start_col, type_expr, Space.None);
1298 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )
1246 try renderToken(tree, ais, lparen, Space.None); // (
1247 try renderExpression(allocator, ais, tree, type_expr, Space.None);
1248 try renderToken(tree, ais, rparen, Space.Space); // )
12991249 },
13001250 }
13011251
13021252 if (container_decl.fields_and_decls_len == 0) {
1303 try renderToken(tree, stream, container_decl.lbrace_token, indent + indent_delta, start_col, Space.None); // {
1304 return renderToken(tree, stream, container_decl.rbrace_token, indent, start_col, space); // }
1253 {
1254 ais.pushIndentNextLine();
1255 defer ais.popIndent();
1256 try renderToken(tree, ais, container_decl.lbrace_token, Space.None); // {
1257 }
1258 return renderToken(tree, ais, container_decl.rbrace_token, space); // }
13051259 }
13061260
13071261 const src_has_trailing_comma = blk: {
......@@ -1332,43 +1286,39 @@ fn renderExpression(
13321286
13331287 if (src_has_trailing_comma or !src_has_only_fields) {
13341288 // One declaration per line
1335 const new_indent = indent + indent_delta;
1336 try renderToken(tree, stream, container_decl.lbrace_token, new_indent, start_col, .Newline); // {
1289 ais.pushIndentNextLine();
1290 defer ais.popIndent();
1291 try renderToken(tree, ais, container_decl.lbrace_token, .Newline); // {
13371292
13381293 for (fields_and_decls) |decl, i| {
1339 try stream.writeByteNTimes(' ', new_indent);
1340 try renderContainerDecl(allocator, stream, tree, new_indent, start_col, decl, .Newline);
1294 try renderContainerDecl(allocator, ais, tree, decl, .Newline);
13411295
13421296 if (i + 1 < fields_and_decls.len) {
1343 try renderExtraNewline(tree, stream, start_col, fields_and_decls[i + 1]);
1297 try renderExtraNewline(tree, ais, fields_and_decls[i + 1]);
13441298 }
13451299 }
1346
1347 try stream.writeByteNTimes(' ', indent);
13481300 } else if (src_has_newline) {
13491301 // All the declarations on the same line, but place the items on
13501302 // their own line
1351 try renderToken(tree, stream, container_decl.lbrace_token, indent, start_col, .Newline); // {
1303 try renderToken(tree, ais, container_decl.lbrace_token, .Newline); // {
13521304
1353 const new_indent = indent + indent_delta;
1354 try stream.writeByteNTimes(' ', new_indent);
1305 ais.pushIndent();
1306 defer ais.popIndent();
13551307
13561308 for (fields_and_decls) |decl, i| {
13571309 const space_after_decl: Space = if (i + 1 >= fields_and_decls.len) .Newline else .Space;
1358 try renderContainerDecl(allocator, stream, tree, new_indent, start_col, decl, space_after_decl);
1310 try renderContainerDecl(allocator, ais, tree, decl, space_after_decl);
13591311 }
1360
1361 try stream.writeByteNTimes(' ', indent);
13621312 } else {
13631313 // All the declarations on the same line
1364 try renderToken(tree, stream, container_decl.lbrace_token, indent, start_col, .Space); // {
1314 try renderToken(tree, ais, container_decl.lbrace_token, .Space); // {
13651315
13661316 for (fields_and_decls) |decl| {
1367 try renderContainerDecl(allocator, stream, tree, indent, start_col, decl, .Space);
1317 try renderContainerDecl(allocator, ais, tree, decl, .Space);
13681318 }
13691319 }
13701320
1371 return renderToken(tree, stream, container_decl.rbrace_token, indent, start_col, space); // }
1321 return renderToken(tree, ais, container_decl.rbrace_token, space); // }
13721322 },
13731323
13741324 .ErrorSetDecl => {
......@@ -1377,9 +1327,9 @@ fn renderExpression(
13771327 const lbrace = tree.nextToken(err_set_decl.error_token);
13781328
13791329 if (err_set_decl.decls_len == 0) {
1380 try renderToken(tree, stream, err_set_decl.error_token, indent, start_col, Space.None);
1381 try renderToken(tree, stream, lbrace, indent, start_col, Space.None);
1382 return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space);
1330 try renderToken(tree, ais, err_set_decl.error_token, Space.None);
1331 try renderToken(tree, ais, lbrace, Space.None);
1332 return renderToken(tree, ais, err_set_decl.rbrace_token, space);
13831333 }
13841334
13851335 if (err_set_decl.decls_len == 1) blk: {
......@@ -1393,13 +1343,13 @@ fn renderExpression(
13931343 break :blk;
13941344 }
13951345
1396 try renderToken(tree, stream, err_set_decl.error_token, indent, start_col, Space.None); // error
1397 try renderToken(tree, stream, lbrace, indent, start_col, Space.None); // {
1398 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None);
1399 return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // }
1346 try renderToken(tree, ais, err_set_decl.error_token, Space.None); // error
1347 try renderToken(tree, ais, lbrace, Space.None); // {
1348 try renderExpression(allocator, ais, tree, node, Space.None);
1349 return renderToken(tree, ais, err_set_decl.rbrace_token, space); // }
14001350 }
14011351
1402 try renderToken(tree, stream, err_set_decl.error_token, indent, start_col, Space.None); // error
1352 try renderToken(tree, ais, err_set_decl.error_token, Space.None); // error
14031353
14041354 const src_has_trailing_comma = blk: {
14051355 const maybe_comma = tree.prevToken(err_set_decl.rbrace_token);
......@@ -1407,72 +1357,66 @@ fn renderExpression(
14071357 };
14081358
14091359 if (src_has_trailing_comma) {
1410 try renderToken(tree, stream, lbrace, indent, start_col, Space.Newline); // {
1411 const new_indent = indent + indent_delta;
1412
1413 const decls = err_set_decl.decls();
1414 for (decls) |node, i| {
1415 try stream.writeByteNTimes(' ', new_indent);
1416
1417 if (i + 1 < decls.len) {
1418 try renderExpression(allocator, stream, tree, new_indent, start_col, node, Space.None);
1419 try renderToken(tree, stream, tree.nextToken(node.lastToken()), new_indent, start_col, Space.Newline); // ,
1420
1421 try renderExtraNewline(tree, stream, start_col, decls[i + 1]);
1422 } else {
1423 try renderExpression(allocator, stream, tree, new_indent, start_col, node, Space.Comma);
1360 {
1361 ais.pushIndent();
1362 defer ais.popIndent();
1363
1364 try renderToken(tree, ais, lbrace, Space.Newline); // {
1365 const decls = err_set_decl.decls();
1366 for (decls) |node, i| {
1367 if (i + 1 < decls.len) {
1368 try renderExpression(allocator, ais, tree, node, Space.None);
1369 try renderToken(tree, ais, tree.nextToken(node.lastToken()), Space.Newline); // ,
1370
1371 try renderExtraNewline(tree, ais, decls[i + 1]);
1372 } else {
1373 try renderExpression(allocator, ais, tree, node, Space.Comma);
1374 }
14241375 }
14251376 }
14261377
1427 try stream.writeByteNTimes(' ', indent);
1428 return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // }
1378 return renderToken(tree, ais, err_set_decl.rbrace_token, space); // }
14291379 } else {
1430 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space); // {
1380 try renderToken(tree, ais, lbrace, Space.Space); // {
14311381
14321382 const decls = err_set_decl.decls();
14331383 for (decls) |node, i| {
14341384 if (i + 1 < decls.len) {
1435 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None);
1385 try renderExpression(allocator, ais, tree, node, Space.None);
14361386
14371387 const comma_token = tree.nextToken(node.lastToken());
14381388 assert(tree.token_ids[comma_token] == .Comma);
1439 try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // ,
1440 try renderExtraNewline(tree, stream, start_col, decls[i + 1]);
1389 try renderToken(tree, ais, comma_token, Space.Space); // ,
1390 try renderExtraNewline(tree, ais, decls[i + 1]);
14411391 } else {
1442 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.Space);
1392 try renderExpression(allocator, ais, tree, node, Space.Space);
14431393 }
14441394 }
14451395
1446 return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // }
1396 return renderToken(tree, ais, err_set_decl.rbrace_token, space); // }
14471397 }
14481398 },
14491399
14501400 .ErrorTag => {
14511401 const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", base);
14521402
1453 try renderDocComments(tree, stream, tag, tag.doc_comments, indent, start_col);
1454 return renderToken(tree, stream, tag.name_token, indent, start_col, space); // name
1403 try renderDocComments(tree, ais, tag, tag.doc_comments);
1404 return renderToken(tree, ais, tag.name_token, space); // name
14551405 },
14561406
14571407 .MultilineStringLiteral => {
1458 // TODO: Don't indent in this function, but let the caller indent.
1459 // If this has been implemented, a lot of hacky solutions in i.e. ArrayInit and FunctionCall can be removed
14601408 const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
14611409
1462 var skip_first_indent = true;
1463 if (tree.token_ids[multiline_str_literal.firstToken() - 1] != .LineComment) {
1464 try stream.print("\n", .{});
1465 skip_first_indent = false;
1466 }
1467
1468 for (multiline_str_literal.lines()) |t| {
1469 if (!skip_first_indent) {
1470 try stream.writeByteNTimes(' ', indent + indent_delta);
1410 {
1411 const locked_indents = ais.lockOneShotIndent();
1412 defer {
1413 var i: u8 = 0;
1414 while (i < locked_indents) : (i += 1) ais.popIndent();
14711415 }
1472 try renderToken(tree, stream, t, indent, start_col, Space.None);
1473 skip_first_indent = false;
1416 try ais.maybeInsertNewline();
1417
1418 for (multiline_str_literal.lines()) |t| try renderToken(tree, ais, t, Space.None);
14741419 }
1475 try stream.writeByteNTimes(' ', indent);
14761420 },
14771421
14781422 .BuiltinCall => {
......@@ -1480,9 +1424,9 @@ fn renderExpression(
14801424
14811425 // TODO remove after 0.7.0 release
14821426 if (mem.eql(u8, tree.tokenSlice(builtin_call.builtin_token), "@OpaqueType"))
1483 return stream.writeAll("@Type(.Opaque)");
1427 return ais.writer().writeAll("@Type(.Opaque)");
14841428
1485 try renderToken(tree, stream, builtin_call.builtin_token, indent, start_col, Space.None); // @name
1429 try renderToken(tree, ais, builtin_call.builtin_token, Space.None); // @name
14861430
14871431 const src_params_trailing_comma = blk: {
14881432 if (builtin_call.params_len < 2) break :blk false;
......@@ -1494,31 +1438,30 @@ fn renderExpression(
14941438 const lparen = tree.nextToken(builtin_call.builtin_token);
14951439
14961440 if (!src_params_trailing_comma) {
1497 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
1441 try renderToken(tree, ais, lparen, Space.None); // (
14981442
14991443 // render all on one line, no trailing comma
15001444 const params = builtin_call.params();
15011445 for (params) |param_node, i| {
1502 try renderExpression(allocator, stream, tree, indent, start_col, param_node, Space.None);
1446 try renderExpression(allocator, ais, tree, param_node, Space.None);
15031447
15041448 if (i + 1 < params.len) {
15051449 const comma_token = tree.nextToken(param_node.lastToken());
1506 try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // ,
1450 try renderToken(tree, ais, comma_token, Space.Space); // ,
15071451 }
15081452 }
15091453 } else {
15101454 // one param per line
1511 const new_indent = indent + indent_delta;
1512 try renderToken(tree, stream, lparen, new_indent, start_col, Space.Newline); // (
1455 ais.pushIndent();
1456 defer ais.popIndent();
1457 try renderToken(tree, ais, lparen, Space.Newline); // (
15131458
15141459 for (builtin_call.params()) |param_node| {
1515 try stream.writeByteNTimes(' ', new_indent);
1516 try renderExpression(allocator, stream, tree, indent, start_col, param_node, Space.Comma);
1460 try renderExpression(allocator, ais, tree, param_node, Space.Comma);
15171461 }
1518 try stream.writeByteNTimes(' ', indent);
15191462 }
15201463
1521 return renderToken(tree, stream, builtin_call.rparen_token, indent, start_col, space); // )
1464 return renderToken(tree, ais, builtin_call.rparen_token, space); // )
15221465 },
15231466
15241467 .FnProto => {
......@@ -1528,24 +1471,24 @@ fn renderExpression(
15281471 const visib_token = tree.token_ids[visib_token_index];
15291472 assert(visib_token == .Keyword_pub or visib_token == .Keyword_export);
15301473
1531 try renderToken(tree, stream, visib_token_index, indent, start_col, Space.Space); // pub
1474 try renderToken(tree, ais, visib_token_index, Space.Space); // pub
15321475 }
15331476
15341477 if (fn_proto.getExternExportInlineToken()) |extern_export_inline_token| {
15351478 if (fn_proto.getIsExternPrototype() == null)
1536 try renderToken(tree, stream, extern_export_inline_token, indent, start_col, Space.Space); // extern/export/inline
1479 try renderToken(tree, ais, extern_export_inline_token, Space.Space); // extern/export/inline
15371480 }
15381481
15391482 if (fn_proto.getLibName()) |lib_name| {
1540 try renderExpression(allocator, stream, tree, indent, start_col, lib_name, Space.Space);
1483 try renderExpression(allocator, ais, tree, lib_name, Space.Space);
15411484 }
15421485
15431486 const lparen = if (fn_proto.getNameToken()) |name_token| blk: {
1544 try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.Space); // fn
1545 try renderToken(tree, stream, name_token, indent, start_col, Space.None); // name
1487 try renderToken(tree, ais, fn_proto.fn_token, Space.Space); // fn
1488 try renderToken(tree, ais, name_token, Space.None); // name
15461489 break :blk tree.nextToken(name_token);
15471490 } else blk: {
1548 try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.Space); // fn
1491 try renderToken(tree, ais, fn_proto.fn_token, Space.Space); // fn
15491492 break :blk tree.nextToken(fn_proto.fn_token);
15501493 };
15511494 assert(tree.token_ids[lparen] == .LParen);
......@@ -1572,47 +1515,45 @@ fn renderExpression(
15721515 };
15731516
15741517 if (!src_params_trailing_comma) {
1575 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
1518 try renderToken(tree, ais, lparen, Space.None); // (
15761519
15771520 // render all on one line, no trailing comma
15781521 for (fn_proto.params()) |param_decl, i| {
1579 try renderParamDecl(allocator, stream, tree, indent, start_col, param_decl, Space.None);
1522 try renderParamDecl(allocator, ais, tree, param_decl, Space.None);
15801523
15811524 if (i + 1 < fn_proto.params_len or fn_proto.getVarArgsToken() != null) {
15821525 const comma = tree.nextToken(param_decl.lastToken());
1583 try renderToken(tree, stream, comma, indent, start_col, Space.Space); // ,
1526 try renderToken(tree, ais, comma, Space.Space); // ,
15841527 }
15851528 }
15861529 if (fn_proto.getVarArgsToken()) |var_args_token| {
1587 try renderToken(tree, stream, var_args_token, indent, start_col, Space.None);
1530 try renderToken(tree, ais, var_args_token, Space.None);
15881531 }
15891532 } else {
15901533 // one param per line
1591 const new_indent = indent + indent_delta;
1592 try renderToken(tree, stream, lparen, new_indent, start_col, Space.Newline); // (
1534 ais.pushIndent();
1535 defer ais.popIndent();
1536 try renderToken(tree, ais, lparen, Space.Newline); // (
15931537
15941538 for (fn_proto.params()) |param_decl| {
1595 try stream.writeByteNTimes(' ', new_indent);
1596 try renderParamDecl(allocator, stream, tree, new_indent, start_col, param_decl, Space.Comma);
1539 try renderParamDecl(allocator, ais, tree, param_decl, Space.Comma);
15971540 }
15981541 if (fn_proto.getVarArgsToken()) |var_args_token| {
1599 try stream.writeByteNTimes(' ', new_indent);
1600 try renderToken(tree, stream, var_args_token, new_indent, start_col, Space.Comma);
1542 try renderToken(tree, ais, var_args_token, Space.Comma);
16011543 }
1602 try stream.writeByteNTimes(' ', indent);
16031544 }
16041545
1605 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )
1546 try renderToken(tree, ais, rparen, Space.Space); // )
16061547
16071548 if (fn_proto.getAlignExpr()) |align_expr| {
16081549 const align_rparen = tree.nextToken(align_expr.lastToken());
16091550 const align_lparen = tree.prevToken(align_expr.firstToken());
16101551 const align_kw = tree.prevToken(align_lparen);
16111552
1612 try renderToken(tree, stream, align_kw, indent, start_col, Space.None); // align
1613 try renderToken(tree, stream, align_lparen, indent, start_col, Space.None); // (
1614 try renderExpression(allocator, stream, tree, indent, start_col, align_expr, Space.None);
1615 try renderToken(tree, stream, align_rparen, indent, start_col, Space.Space); // )
1553 try renderToken(tree, ais, align_kw, Space.None); // align
1554 try renderToken(tree, ais, align_lparen, Space.None); // (
1555 try renderExpression(allocator, ais, tree, align_expr, Space.None);
1556 try renderToken(tree, ais, align_rparen, Space.Space); // )
16161557 }
16171558
16181559 if (fn_proto.getSectionExpr()) |section_expr| {
......@@ -1620,10 +1561,10 @@ fn renderExpression(
16201561 const section_lparen = tree.prevToken(section_expr.firstToken());
16211562 const section_kw = tree.prevToken(section_lparen);
16221563
1623 try renderToken(tree, stream, section_kw, indent, start_col, Space.None); // section
1624 try renderToken(tree, stream, section_lparen, indent, start_col, Space.None); // (
1625 try renderExpression(allocator, stream, tree, indent, start_col, section_expr, Space.None);
1626 try renderToken(tree, stream, section_rparen, indent, start_col, Space.Space); // )
1564 try renderToken(tree, ais, section_kw, Space.None); // section
1565 try renderToken(tree, ais, section_lparen, Space.None); // (
1566 try renderExpression(allocator, ais, tree, section_expr, Space.None);
1567 try renderToken(tree, ais, section_rparen, Space.Space); // )
16271568 }
16281569
16291570 if (fn_proto.getCallconvExpr()) |callconv_expr| {
......@@ -1631,23 +1572,23 @@ fn renderExpression(
16311572 const callconv_lparen = tree.prevToken(callconv_expr.firstToken());
16321573 const callconv_kw = tree.prevToken(callconv_lparen);
16331574
1634 try renderToken(tree, stream, callconv_kw, indent, start_col, Space.None); // callconv
1635 try renderToken(tree, stream, callconv_lparen, indent, start_col, Space.None); // (
1636 try renderExpression(allocator, stream, tree, indent, start_col, callconv_expr, Space.None);
1637 try renderToken(tree, stream, callconv_rparen, indent, start_col, Space.Space); // )
1575 try renderToken(tree, ais, callconv_kw, Space.None); // callconv
1576 try renderToken(tree, ais, callconv_lparen, Space.None); // (
1577 try renderExpression(allocator, ais, tree, callconv_expr, Space.None);
1578 try renderToken(tree, ais, callconv_rparen, Space.Space); // )
16381579 } else if (fn_proto.getIsExternPrototype() != null) {
1639 try stream.writeAll("callconv(.C) ");
1580 try ais.writer().writeAll("callconv(.C) ");
16401581 } else if (fn_proto.getIsAsync() != null) {
1641 try stream.writeAll("callconv(.Async) ");
1582 try ais.writer().writeAll("callconv(.Async) ");
16421583 }
16431584
16441585 switch (fn_proto.return_type) {
16451586 .Explicit => |node| {
1646 return renderExpression(allocator, stream, tree, indent, start_col, node, space);
1587 return renderExpression(allocator, ais, tree, node, space);
16471588 },
16481589 .InferErrorSet => |node| {
1649 try renderToken(tree, stream, tree.prevToken(node.firstToken()), indent, start_col, Space.None); // !
1650 return renderExpression(allocator, stream, tree, indent, start_col, node, space);
1590 try renderToken(tree, ais, tree.prevToken(node.firstToken()), Space.None); // !
1591 return renderExpression(allocator, ais, tree, node, space);
16511592 },
16521593 .Invalid => unreachable,
16531594 }
......@@ -1657,11 +1598,11 @@ fn renderExpression(
16571598 const anyframe_type = @fieldParentPtr(ast.Node.AnyFrameType, "base", base);
16581599
16591600 if (anyframe_type.result) |result| {
1660 try renderToken(tree, stream, anyframe_type.anyframe_token, indent, start_col, Space.None); // anyframe
1661 try renderToken(tree, stream, result.arrow_token, indent, start_col, Space.None); // ->
1662 return renderExpression(allocator, stream, tree, indent, start_col, result.return_type, space);
1601 try renderToken(tree, ais, anyframe_type.anyframe_token, Space.None); // anyframe
1602 try renderToken(tree, ais, result.arrow_token, Space.None); // ->
1603 return renderExpression(allocator, ais, tree, result.return_type, space);
16631604 } else {
1664 return renderToken(tree, stream, anyframe_type.anyframe_token, indent, start_col, space); // anyframe
1605 return renderToken(tree, ais, anyframe_type.anyframe_token, space); // anyframe
16651606 }
16661607 },
16671608
......@@ -1670,38 +1611,38 @@ fn renderExpression(
16701611 .Switch => {
16711612 const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
16721613
1673 try renderToken(tree, stream, switch_node.switch_token, indent, start_col, Space.Space); // switch
1674 try renderToken(tree, stream, tree.nextToken(switch_node.switch_token), indent, start_col, Space.None); // (
1614 try renderToken(tree, ais, switch_node.switch_token, Space.Space); // switch
1615 try renderToken(tree, ais, tree.nextToken(switch_node.switch_token), Space.None); // (
16751616
16761617 const rparen = tree.nextToken(switch_node.expr.lastToken());
16771618 const lbrace = tree.nextToken(rparen);
16781619
16791620 if (switch_node.cases_len == 0) {
1680 try renderExpression(allocator, stream, tree, indent, start_col, switch_node.expr, Space.None);
1681 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )
1682 try renderToken(tree, stream, lbrace, indent, start_col, Space.None); // {
1683 return renderToken(tree, stream, switch_node.rbrace, indent, start_col, space); // }
1621 try renderExpression(allocator, ais, tree, switch_node.expr, Space.None);
1622 try renderToken(tree, ais, rparen, Space.Space); // )
1623 try renderToken(tree, ais, lbrace, Space.None); // {
1624 return renderToken(tree, ais, switch_node.rbrace, space); // }
16841625 }
16851626
1686 try renderExpression(allocator, stream, tree, indent, start_col, switch_node.expr, Space.None);
1687
1688 const new_indent = indent + indent_delta;
1627 try renderExpression(allocator, ais, tree, switch_node.expr, Space.None);
1628 try renderToken(tree, ais, rparen, Space.Space); // )
16891629
1690 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )
1691 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline); // {
1630 {
1631 ais.pushIndentNextLine();
1632 defer ais.popIndent();
1633 try renderToken(tree, ais, lbrace, Space.Newline); // {
16921634
1693 const cases = switch_node.cases();
1694 for (cases) |node, i| {
1695 try stream.writeByteNTimes(' ', new_indent);
1696 try renderExpression(allocator, stream, tree, new_indent, start_col, node, Space.Comma);
1635 const cases = switch_node.cases();
1636 for (cases) |node, i| {
1637 try renderExpression(allocator, ais, tree, node, Space.Comma);
16971638
1698 if (i + 1 < cases.len) {
1699 try renderExtraNewline(tree, stream, start_col, cases[i + 1]);
1639 if (i + 1 < cases.len) {
1640 try renderExtraNewline(tree, ais, cases[i + 1]);
1641 }
17001642 }
17011643 }
17021644
1703 try stream.writeByteNTimes(' ', indent);
1704 return renderToken(tree, stream, switch_node.rbrace, indent, start_col, space); // }
1645 return renderToken(tree, ais, switch_node.rbrace, space); // }
17051646 },
17061647
17071648 .SwitchCase => {
......@@ -1718,43 +1659,41 @@ fn renderExpression(
17181659 const items = switch_case.items();
17191660 for (items) |node, i| {
17201661 if (i + 1 < items.len) {
1721 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None);
1662 try renderExpression(allocator, ais, tree, node, Space.None);
17221663
17231664 const comma_token = tree.nextToken(node.lastToken());
1724 try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // ,
1725 try renderExtraNewline(tree, stream, start_col, items[i + 1]);
1665 try renderToken(tree, ais, comma_token, Space.Space); // ,
1666 try renderExtraNewline(tree, ais, items[i + 1]);
17261667 } else {
1727 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.Space);
1668 try renderExpression(allocator, ais, tree, node, Space.Space);
17281669 }
17291670 }
17301671 } else {
17311672 const items = switch_case.items();
17321673 for (items) |node, i| {
17331674 if (i + 1 < items.len) {
1734 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None);
1675 try renderExpression(allocator, ais, tree, node, Space.None);
17351676
17361677 const comma_token = tree.nextToken(node.lastToken());
1737 try renderToken(tree, stream, comma_token, indent, start_col, Space.Newline); // ,
1738 try renderExtraNewline(tree, stream, start_col, items[i + 1]);
1739 try stream.writeByteNTimes(' ', indent);
1678 try renderToken(tree, ais, comma_token, Space.Newline); // ,
1679 try renderExtraNewline(tree, ais, items[i + 1]);
17401680 } else {
1741 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.Comma);
1742 try stream.writeByteNTimes(' ', indent);
1681 try renderExpression(allocator, ais, tree, node, Space.Comma);
17431682 }
17441683 }
17451684 }
17461685
1747 try renderToken(tree, stream, switch_case.arrow_token, indent, start_col, Space.Space); // =>
1686 try renderToken(tree, ais, switch_case.arrow_token, Space.Space); // =>
17481687
17491688 if (switch_case.payload) |payload| {
1750 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
1689 try renderExpression(allocator, ais, tree, payload, Space.Space);
17511690 }
17521691
1753 return renderExpression(allocator, stream, tree, indent, start_col, switch_case.expr, space);
1692 return renderExpression(allocator, ais, tree, switch_case.expr, space);
17541693 },
17551694 .SwitchElse => {
17561695 const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base);
1757 return renderToken(tree, stream, switch_else.token, indent, start_col, space);
1696 return renderToken(tree, ais, switch_else.token, space);
17581697 },
17591698 .Else => {
17601699 const else_node = @fieldParentPtr(ast.Node.Else, "base", base);
......@@ -1763,37 +1702,37 @@ fn renderExpression(
17631702 const same_line = body_is_block or tree.tokensOnSameLine(else_node.else_token, else_node.body.lastToken());
17641703
17651704 const after_else_space = if (same_line or else_node.payload != null) Space.Space else Space.Newline;
1766 try renderToken(tree, stream, else_node.else_token, indent, start_col, after_else_space);
1705 try renderToken(tree, ais, else_node.else_token, after_else_space);
17671706
17681707 if (else_node.payload) |payload| {
17691708 const payload_space = if (same_line) Space.Space else Space.Newline;
1770 try renderExpression(allocator, stream, tree, indent, start_col, payload, payload_space);
1709 try renderExpression(allocator, ais, tree, payload, payload_space);
17711710 }
17721711
17731712 if (same_line) {
1774 return renderExpression(allocator, stream, tree, indent, start_col, else_node.body, space);
1713 return renderExpression(allocator, ais, tree, else_node.body, space);
1714 } else {
1715 ais.pushIndent();
1716 defer ais.popIndent();
1717 return renderExpression(allocator, ais, tree, else_node.body, space);
17751718 }
1776
1777 try stream.writeByteNTimes(' ', indent + indent_delta);
1778 start_col.* = indent + indent_delta;
1779 return renderExpression(allocator, stream, tree, indent, start_col, else_node.body, space);
17801719 },
17811720
17821721 .While => {
17831722 const while_node = @fieldParentPtr(ast.Node.While, "base", base);
17841723
17851724 if (while_node.label) |label| {
1786 try renderToken(tree, stream, label, indent, start_col, Space.None); // label
1787 try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); // :
1725 try renderToken(tree, ais, label, Space.None); // label
1726 try renderToken(tree, ais, tree.nextToken(label), Space.Space); // :
17881727 }
17891728
17901729 if (while_node.inline_token) |inline_token| {
1791 try renderToken(tree, stream, inline_token, indent, start_col, Space.Space); // inline
1730 try renderToken(tree, ais, inline_token, Space.Space); // inline
17921731 }
17931732
1794 try renderToken(tree, stream, while_node.while_token, indent, start_col, Space.Space); // while
1795 try renderToken(tree, stream, tree.nextToken(while_node.while_token), indent, start_col, Space.None); // (
1796 try renderExpression(allocator, stream, tree, indent, start_col, while_node.condition, Space.None);
1733 try renderToken(tree, ais, while_node.while_token, Space.Space); // while
1734 try renderToken(tree, ais, tree.nextToken(while_node.while_token), Space.None); // (
1735 try renderExpression(allocator, ais, tree, while_node.condition, Space.None);
17971736
17981737 const cond_rparen = tree.nextToken(while_node.condition.lastToken());
17991738
......@@ -1815,12 +1754,12 @@ fn renderExpression(
18151754
18161755 {
18171756 const rparen_space = if (while_node.payload != null or while_node.continue_expr != null) Space.Space else block_start_space;
1818 try renderToken(tree, stream, cond_rparen, indent, start_col, rparen_space); // )
1757 try renderToken(tree, ais, cond_rparen, rparen_space); // )
18191758 }
18201759
18211760 if (while_node.payload) |payload| {
1822 const payload_space = if (while_node.continue_expr != null) Space.Space else block_start_space;
1823 try renderExpression(allocator, stream, tree, indent, start_col, payload, payload_space);
1761 const payload_space = Space.Space; //if (while_node.continue_expr != null) Space.Space else block_start_space;
1762 try renderExpression(allocator, ais, tree, payload, payload_space);
18241763 }
18251764
18261765 if (while_node.continue_expr) |continue_expr| {
......@@ -1828,29 +1767,22 @@ fn renderExpression(
18281767 const lparen = tree.prevToken(continue_expr.firstToken());
18291768 const colon = tree.prevToken(lparen);
18301769
1831 try renderToken(tree, stream, colon, indent, start_col, Space.Space); // :
1832 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
1770 try renderToken(tree, ais, colon, Space.Space); // :
1771 try renderToken(tree, ais, lparen, Space.None); // (
18331772
1834 try renderExpression(allocator, stream, tree, indent, start_col, continue_expr, Space.None);
1773 try renderExpression(allocator, ais, tree, continue_expr, Space.None);
18351774
1836 try renderToken(tree, stream, rparen, indent, start_col, block_start_space); // )
1775 try renderToken(tree, ais, rparen, block_start_space); // )
18371776 }
18381777
1839 var new_indent = indent;
1840 if (block_start_space == Space.Newline) {
1841 new_indent += indent_delta;
1842 try stream.writeByteNTimes(' ', new_indent);
1843 start_col.* = new_indent;
1778 {
1779 if (!body_is_block) ais.pushIndent();
1780 defer if (!body_is_block) ais.popIndent();
1781 try renderExpression(allocator, ais, tree, while_node.body, after_body_space);
18441782 }
18451783
1846 try renderExpression(allocator, stream, tree, indent, start_col, while_node.body, after_body_space);
1847
18481784 if (while_node.@"else") |@"else"| {
1849 if (after_body_space == Space.Newline) {
1850 try stream.writeByteNTimes(' ', indent);
1851 start_col.* = indent;
1852 }
1853 return renderExpression(allocator, stream, tree, indent, start_col, &@"else".base, space);
1785 return renderExpression(allocator, ais, tree, &@"else".base, space);
18541786 }
18551787 },
18561788
......@@ -1858,17 +1790,17 @@ fn renderExpression(
18581790 const for_node = @fieldParentPtr(ast.Node.For, "base", base);
18591791
18601792 if (for_node.label) |label| {
1861 try renderToken(tree, stream, label, indent, start_col, Space.None); // label
1862 try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); // :
1793 try renderToken(tree, ais, label, Space.None); // label
1794 try renderToken(tree, ais, tree.nextToken(label), Space.Space); // :
18631795 }
18641796
18651797 if (for_node.inline_token) |inline_token| {
1866 try renderToken(tree, stream, inline_token, indent, start_col, Space.Space); // inline
1798 try renderToken(tree, ais, inline_token, Space.Space); // inline
18671799 }
18681800
1869 try renderToken(tree, stream, for_node.for_token, indent, start_col, Space.Space); // for
1870 try renderToken(tree, stream, tree.nextToken(for_node.for_token), indent, start_col, Space.None); // (
1871 try renderExpression(allocator, stream, tree, indent, start_col, for_node.array_expr, Space.None);
1801 try renderToken(tree, ais, for_node.for_token, Space.Space); // for
1802 try renderToken(tree, ais, tree.nextToken(for_node.for_token), Space.None); // (
1803 try renderExpression(allocator, ais, tree, for_node.array_expr, Space.None);
18721804
18731805 const rparen = tree.nextToken(for_node.array_expr.lastToken());
18741806
......@@ -1876,10 +1808,10 @@ fn renderExpression(
18761808 const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());
18771809 const body_on_same_line = body_is_block or src_one_line_to_body;
18781810
1879 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )
1811 try renderToken(tree, ais, rparen, Space.Space); // )
18801812
18811813 const space_after_payload = if (body_on_same_line) Space.Space else Space.Newline;
1882 try renderExpression(allocator, stream, tree, indent, start_col, for_node.payload, space_after_payload); // |x|
1814 try renderExpression(allocator, ais, tree, for_node.payload, space_after_payload); // |x|
18831815
18841816 const space_after_body = blk: {
18851817 if (for_node.@"else") |@"else"| {
......@@ -1894,13 +1826,14 @@ fn renderExpression(
18941826 }
18951827 };
18961828
1897 const body_indent = if (body_on_same_line) indent else indent + indent_delta;
1898 if (!body_on_same_line) try stream.writeByteNTimes(' ', body_indent);
1899 try renderExpression(allocator, stream, tree, body_indent, start_col, for_node.body, space_after_body); // { body }
1829 {
1830 if (!body_on_same_line) ais.pushIndent();
1831 defer if (!body_on_same_line) ais.popIndent();
1832 try renderExpression(allocator, ais, tree, for_node.body, space_after_body); // { body }
1833 }
19001834
19011835 if (for_node.@"else") |@"else"| {
1902 if (space_after_body == Space.Newline) try stream.writeByteNTimes(' ', indent);
1903 return renderExpression(allocator, stream, tree, indent, start_col, &@"else".base, space); // else
1836 return renderExpression(allocator, ais, tree, &@"else".base, space); // else
19041837 }
19051838 },
19061839
......@@ -1910,29 +1843,29 @@ fn renderExpression(
19101843 const lparen = tree.nextToken(if_node.if_token);
19111844 const rparen = tree.nextToken(if_node.condition.lastToken());
19121845
1913 try renderToken(tree, stream, if_node.if_token, indent, start_col, Space.Space); // if
1914 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
1846 try renderToken(tree, ais, if_node.if_token, Space.Space); // if
1847 try renderToken(tree, ais, lparen, Space.None); // (
19151848
1916 try renderExpression(allocator, stream, tree, indent, start_col, if_node.condition, Space.None); // condition
1849 try renderExpression(allocator, ais, tree, if_node.condition, Space.None); // condition
19171850
19181851 const body_is_if_block = if_node.body.tag == .If;
19191852 const body_is_block = nodeIsBlock(if_node.body);
19201853
19211854 if (body_is_if_block) {
1922 try renderExtraNewline(tree, stream, start_col, if_node.body);
1855 try renderExtraNewline(tree, ais, if_node.body);
19231856 } else if (body_is_block) {
19241857 const after_rparen_space = if (if_node.payload == null) Space.BlockStart else Space.Space;
1925 try renderToken(tree, stream, rparen, indent, start_col, after_rparen_space); // )
1858 try renderToken(tree, ais, rparen, after_rparen_space); // )
19261859
19271860 if (if_node.payload) |payload| {
1928 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.BlockStart); // |x|
1861 try renderExpression(allocator, ais, tree, payload, Space.BlockStart); // |x|
19291862 }
19301863
19311864 if (if_node.@"else") |@"else"| {
1932 try renderExpression(allocator, stream, tree, indent, start_col, if_node.body, Space.SpaceOrOutdent);
1933 return renderExpression(allocator, stream, tree, indent, start_col, &@"else".base, space);
1865 try renderExpression(allocator, ais, tree, if_node.body, Space.SpaceOrOutdent);
1866 return renderExpression(allocator, ais, tree, &@"else".base, space);
19341867 } else {
1935 return renderExpression(allocator, stream, tree, indent, start_col, if_node.body, space);
1868 return renderExpression(allocator, ais, tree, if_node.body, space);
19361869 }
19371870 }
19381871
......@@ -1940,186 +1873,184 @@ fn renderExpression(
19401873
19411874 if (src_has_newline) {
19421875 const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space;
1943 try renderToken(tree, stream, rparen, indent, start_col, after_rparen_space); // )
1876 try renderToken(tree, ais, rparen, after_rparen_space); // )
19441877
19451878 if (if_node.payload) |payload| {
1946 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Newline);
1879 try renderExpression(allocator, ais, tree, payload, Space.Newline);
19471880 }
19481881
1949 const new_indent = indent + indent_delta;
1950 try stream.writeByteNTimes(' ', new_indent);
1951
19521882 if (if_node.@"else") |@"else"| {
19531883 const else_is_block = nodeIsBlock(@"else".body);
1954 try renderExpression(allocator, stream, tree, new_indent, start_col, if_node.body, Space.Newline);
1955 try stream.writeByteNTimes(' ', indent);
1884
1885 {
1886 ais.pushIndent();
1887 defer ais.popIndent();
1888 try renderExpression(allocator, ais, tree, if_node.body, Space.Newline);
1889 }
19561890
19571891 if (else_is_block) {
1958 try renderToken(tree, stream, @"else".else_token, indent, start_col, Space.Space); // else
1892 try renderToken(tree, ais, @"else".else_token, Space.Space); // else
19591893
19601894 if (@"else".payload) |payload| {
1961 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
1895 try renderExpression(allocator, ais, tree, payload, Space.Space);
19621896 }
19631897
1964 return renderExpression(allocator, stream, tree, indent, start_col, @"else".body, space);
1898 return renderExpression(allocator, ais, tree, @"else".body, space);
19651899 } else {
19661900 const after_else_space = if (@"else".payload == null) Space.Newline else Space.Space;
1967 try renderToken(tree, stream, @"else".else_token, indent, start_col, after_else_space); // else
1901 try renderToken(tree, ais, @"else".else_token, after_else_space); // else
19681902
19691903 if (@"else".payload) |payload| {
1970 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Newline);
1904 try renderExpression(allocator, ais, tree, payload, Space.Newline);
19711905 }
1972 try stream.writeByteNTimes(' ', new_indent);
19731906
1974 return renderExpression(allocator, stream, tree, new_indent, start_col, @"else".body, space);
1907 ais.pushIndent();
1908 defer ais.popIndent();
1909 return renderExpression(allocator, ais, tree, @"else".body, space);
19751910 }
19761911 } else {
1977 return renderExpression(allocator, stream, tree, new_indent, start_col, if_node.body, space);
1912 ais.pushIndent();
1913 defer ais.popIndent();
1914 return renderExpression(allocator, ais, tree, if_node.body, space);
19781915 }
19791916 }
19801917
1981 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )
1918 // Single line if statement
1919
1920 try renderToken(tree, ais, rparen, Space.Space); // )
19821921
19831922 if (if_node.payload) |payload| {
1984 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
1923 try renderExpression(allocator, ais, tree, payload, Space.Space);
19851924 }
19861925
19871926 if (if_node.@"else") |@"else"| {
1988 try renderExpression(allocator, stream, tree, indent, start_col, if_node.body, Space.Space);
1989 try renderToken(tree, stream, @"else".else_token, indent, start_col, Space.Space);
1927 try renderExpression(allocator, ais, tree, if_node.body, Space.Space);
1928 try renderToken(tree, ais, @"else".else_token, Space.Space);
19901929
19911930 if (@"else".payload) |payload| {
1992 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
1931 try renderExpression(allocator, ais, tree, payload, Space.Space);
19931932 }
19941933
1995 return renderExpression(allocator, stream, tree, indent, start_col, @"else".body, space);
1934 return renderExpression(allocator, ais, tree, @"else".body, space);
19961935 } else {
1997 return renderExpression(allocator, stream, tree, indent, start_col, if_node.body, space);
1936 return renderExpression(allocator, ais, tree, if_node.body, space);
19981937 }
19991938 },
20001939
20011940 .Asm => {
20021941 const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);
20031942
2004 try renderToken(tree, stream, asm_node.asm_token, indent, start_col, Space.Space); // asm
1943 try renderToken(tree, ais, asm_node.asm_token, Space.Space); // asm
20051944
20061945 if (asm_node.volatile_token) |volatile_token| {
2007 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile
2008 try renderToken(tree, stream, tree.nextToken(volatile_token), indent, start_col, Space.None); // (
1946 try renderToken(tree, ais, volatile_token, Space.Space); // volatile
1947 try renderToken(tree, ais, tree.nextToken(volatile_token), Space.None); // (
20091948 } else {
2010 try renderToken(tree, stream, tree.nextToken(asm_node.asm_token), indent, start_col, Space.None); // (
1949 try renderToken(tree, ais, tree.nextToken(asm_node.asm_token), Space.None); // (
20111950 }
20121951
2013 if (asm_node.outputs.len == 0 and asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {
2014 try renderExpression(allocator, stream, tree, indent, start_col, asm_node.template, Space.None);
2015 return renderToken(tree, stream, asm_node.rparen, indent, start_col, space);
2016 }
1952 asmblk: {
1953 ais.pushIndent();
1954 defer ais.popIndent();
20171955
2018 try renderExpression(allocator, stream, tree, indent, start_col, asm_node.template, Space.Newline);
1956 if (asm_node.outputs.len == 0 and asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {
1957 try renderExpression(allocator, ais, tree, asm_node.template, Space.None);
1958 break :asmblk;
1959 }
20191960
2020 const indent_once = indent + indent_delta;
1961 try renderExpression(allocator, ais, tree, asm_node.template, Space.Newline);
20211962
2022 if (asm_node.template.tag == .MultilineStringLiteral) {
2023 // After rendering a multiline string literal the cursor is
2024 // already offset by indent
2025 try stream.writeByteNTimes(' ', indent_delta);
2026 } else {
2027 try stream.writeByteNTimes(' ', indent_once);
2028 }
1963 ais.setIndentDelta(asm_indent_delta);
1964 defer ais.setIndentDelta(indent_delta);
20291965
2030 const colon1 = tree.nextToken(asm_node.template.lastToken());
2031 const indent_extra = indent_once + 2;
1966 const colon1 = tree.nextToken(asm_node.template.lastToken());
20321967
2033 const colon2 = if (asm_node.outputs.len == 0) blk: {
2034 try renderToken(tree, stream, colon1, indent, start_col, Space.Newline); // :
2035 try stream.writeByteNTimes(' ', indent_once);
1968 const colon2 = if (asm_node.outputs.len == 0) blk: {
1969 try renderToken(tree, ais, colon1, Space.Newline); // :
20361970
2037 break :blk tree.nextToken(colon1);
2038 } else blk: {
2039 try renderToken(tree, stream, colon1, indent, start_col, Space.Space); // :
2040
2041 for (asm_node.outputs) |*asm_output, i| {
2042 if (i + 1 < asm_node.outputs.len) {
2043 const next_asm_output = asm_node.outputs[i + 1];
2044 try renderAsmOutput(allocator, stream, tree, indent_extra, start_col, asm_output, Space.None);
2045
2046 const comma = tree.prevToken(next_asm_output.firstToken());
2047 try renderToken(tree, stream, comma, indent_extra, start_col, Space.Newline); // ,
2048 try renderExtraNewlineToken(tree, stream, start_col, next_asm_output.firstToken());
2049
2050 try stream.writeByteNTimes(' ', indent_extra);
2051 } else if (asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {
2052 try renderAsmOutput(allocator, stream, tree, indent_extra, start_col, asm_output, Space.Newline);
2053 try stream.writeByteNTimes(' ', indent);
2054 return renderToken(tree, stream, asm_node.rparen, indent, start_col, space);
2055 } else {
2056 try renderAsmOutput(allocator, stream, tree, indent_extra, start_col, asm_output, Space.Newline);
2057 try stream.writeByteNTimes(' ', indent_once);
2058 const comma_or_colon = tree.nextToken(asm_output.lastToken());
2059 break :blk switch (tree.token_ids[comma_or_colon]) {
2060 .Comma => tree.nextToken(comma_or_colon),
2061 else => comma_or_colon,
2062 };
2063 }
2064 }
2065 unreachable;
2066 };
1971 break :blk tree.nextToken(colon1);
1972 } else blk: {
1973 try renderToken(tree, ais, colon1, Space.Space); // :
20671974
2068 const colon3 = if (asm_node.inputs.len == 0) blk: {
2069 try renderToken(tree, stream, colon2, indent, start_col, Space.Newline); // :
2070 try stream.writeByteNTimes(' ', indent_once);
1975 ais.pushIndent();
1976 defer ais.popIndent();
20711977
2072 break :blk tree.nextToken(colon2);
2073 } else blk: {
2074 try renderToken(tree, stream, colon2, indent, start_col, Space.Space); // :
2075
2076 for (asm_node.inputs) |*asm_input, i| {
2077 if (i + 1 < asm_node.inputs.len) {
2078 const next_asm_input = &asm_node.inputs[i + 1];
2079 try renderAsmInput(allocator, stream, tree, indent_extra, start_col, asm_input, Space.None);
2080
2081 const comma = tree.prevToken(next_asm_input.firstToken());
2082 try renderToken(tree, stream, comma, indent_extra, start_col, Space.Newline); // ,
2083 try renderExtraNewlineToken(tree, stream, start_col, next_asm_input.firstToken());
2084
2085 try stream.writeByteNTimes(' ', indent_extra);
2086 } else if (asm_node.clobbers.len == 0) {
2087 try renderAsmInput(allocator, stream, tree, indent_extra, start_col, asm_input, Space.Newline);
2088 try stream.writeByteNTimes(' ', indent);
2089 return renderToken(tree, stream, asm_node.rparen, indent, start_col, space); // )
2090 } else {
2091 try renderAsmInput(allocator, stream, tree, indent_extra, start_col, asm_input, Space.Newline);
2092 try stream.writeByteNTimes(' ', indent_once);
2093 const comma_or_colon = tree.nextToken(asm_input.lastToken());
2094 break :blk switch (tree.token_ids[comma_or_colon]) {
2095 .Comma => tree.nextToken(comma_or_colon),
2096 else => comma_or_colon,
2097 };
1978 for (asm_node.outputs) |*asm_output, i| {
1979 if (i + 1 < asm_node.outputs.len) {
1980 const next_asm_output = asm_node.outputs[i + 1];
1981 try renderAsmOutput(allocator, ais, tree, asm_output, Space.None);
1982
1983 const comma = tree.prevToken(next_asm_output.firstToken());
1984 try renderToken(tree, ais, comma, Space.Newline); // ,
1985 try renderExtraNewlineToken(tree, ais, next_asm_output.firstToken());
1986 } else if (asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {
1987 try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline);
1988 break :asmblk;
1989 } else {
1990 try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline);
1991 const comma_or_colon = tree.nextToken(asm_output.lastToken());
1992 break :blk switch (tree.token_ids[comma_or_colon]) {
1993 .Comma => tree.nextToken(comma_or_colon),
1994 else => comma_or_colon,
1995 };
1996 }
20981997 }
2099 }
2100 unreachable;
2101 };
1998 unreachable;
1999 };
21022000
2103 try renderToken(tree, stream, colon3, indent, start_col, Space.Space); // :
2001 const colon3 = if (asm_node.inputs.len == 0) blk: {
2002 try renderToken(tree, ais, colon2, Space.Newline); // :
2003 break :blk tree.nextToken(colon2);
2004 } else blk: {
2005 try renderToken(tree, ais, colon2, Space.Space); // :
2006 ais.pushIndent();
2007 defer ais.popIndent();
2008 for (asm_node.inputs) |*asm_input, i| {
2009 if (i + 1 < asm_node.inputs.len) {
2010 const next_asm_input = &asm_node.inputs[i + 1];
2011 try renderAsmInput(allocator, ais, tree, asm_input, Space.None);
2012
2013 const comma = tree.prevToken(next_asm_input.firstToken());
2014 try renderToken(tree, ais, comma, Space.Newline); // ,
2015 try renderExtraNewlineToken(tree, ais, next_asm_input.firstToken());
2016 } else if (asm_node.clobbers.len == 0) {
2017 try renderAsmInput(allocator, ais, tree, asm_input, Space.Newline);
2018 break :asmblk;
2019 } else {
2020 try renderAsmInput(allocator, ais, tree, asm_input, Space.Newline);
2021 const comma_or_colon = tree.nextToken(asm_input.lastToken());
2022 break :blk switch (tree.token_ids[comma_or_colon]) {
2023 .Comma => tree.nextToken(comma_or_colon),
2024 else => comma_or_colon,
2025 };
2026 }
2027 }
2028 unreachable;
2029 };
21042030
2105 for (asm_node.clobbers) |clobber_node, i| {
2106 if (i + 1 >= asm_node.clobbers.len) {
2107 try renderExpression(allocator, stream, tree, indent_extra, start_col, clobber_node, Space.Newline);
2108 try stream.writeByteNTimes(' ', indent);
2109 return renderToken(tree, stream, asm_node.rparen, indent, start_col, space);
2110 } else {
2111 try renderExpression(allocator, stream, tree, indent_extra, start_col, clobber_node, Space.None);
2112 const comma = tree.nextToken(clobber_node.lastToken());
2113 try renderToken(tree, stream, comma, indent_once, start_col, Space.Space); // ,
2031 try renderToken(tree, ais, colon3, Space.Space); // :
2032 ais.pushIndent();
2033 defer ais.popIndent();
2034 for (asm_node.clobbers) |clobber_node, i| {
2035 if (i + 1 >= asm_node.clobbers.len) {
2036 try renderExpression(allocator, ais, tree, clobber_node, Space.Newline);
2037 break :asmblk;
2038 } else {
2039 try renderExpression(allocator, ais, tree, clobber_node, Space.None);
2040 const comma = tree.nextToken(clobber_node.lastToken());
2041 try renderToken(tree, ais, comma, Space.Space); // ,
2042 }
21142043 }
21152044 }
2045
2046 return renderToken(tree, ais, asm_node.rparen, space);
21162047 },
21172048
21182049 .EnumLiteral => {
21192050 const enum_literal = @fieldParentPtr(ast.Node.EnumLiteral, "base", base);
21202051
2121 try renderToken(tree, stream, enum_literal.dot, indent, start_col, Space.None); // .
2122 return renderToken(tree, stream, enum_literal.name, indent, start_col, space); // name
2052 try renderToken(tree, ais, enum_literal.dot, Space.None); // .
2053 return renderToken(tree, ais, enum_literal.name, space); // name
21232054 },
21242055
21252056 .ContainerField,
......@@ -2133,118 +2064,115 @@ fn renderExpression(
21332064
21342065fn renderArrayType(
21352066 allocator: *mem.Allocator,
2136 stream: anytype,
2067 ais: anytype,
21372068 tree: *ast.Tree,
2138 indent: usize,
2139 start_col: *usize,
21402069 lbracket: ast.TokenIndex,
21412070 rhs: *ast.Node,
21422071 len_expr: *ast.Node,
21432072 opt_sentinel: ?*ast.Node,
21442073 space: Space,
2145) (@TypeOf(stream).Error || Error)!void {
2074) (@TypeOf(ais.*).Error || Error)!void {
21462075 const rbracket = tree.nextToken(if (opt_sentinel) |sentinel|
21472076 sentinel.lastToken()
21482077 else
21492078 len_expr.lastToken());
21502079
2151 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
2152
21532080 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
21542081 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
2155 const new_indent = if (ends_with_comment) indent + indent_delta else indent;
21562082 const new_space = if (ends_with_comment) Space.Newline else Space.None;
2157 try renderExpression(allocator, stream, tree, new_indent, start_col, len_expr, new_space);
2158 if (starts_with_comment) {
2159 try stream.writeByte('\n');
2160 }
2161 if (ends_with_comment or starts_with_comment) {
2162 try stream.writeByteNTimes(' ', indent);
2163 }
2164 if (opt_sentinel) |sentinel| {
2165 const colon_token = tree.prevToken(sentinel.firstToken());
2166 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
2167 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
2083 {
2084 const do_indent = (starts_with_comment or ends_with_comment);
2085 if (do_indent) ais.pushIndent();
2086 defer if (do_indent) ais.popIndent();
2087
2088 try renderToken(tree, ais, lbracket, Space.None); // [
2089 try renderExpression(allocator, ais, tree, len_expr, new_space);
2090
2091 if (starts_with_comment) {
2092 try ais.maybeInsertNewline();
2093 }
2094 if (opt_sentinel) |sentinel| {
2095 const colon_token = tree.prevToken(sentinel.firstToken());
2096 try renderToken(tree, ais, colon_token, Space.None); // :
2097 try renderExpression(allocator, ais, tree, sentinel, Space.None);
2098 }
2099 if (starts_with_comment) {
2100 try ais.maybeInsertNewline();
2101 }
21682102 }
2169 try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ]
2103 try renderToken(tree, ais, rbracket, Space.None); // ]
21702104
2171 return renderExpression(allocator, stream, tree, indent, start_col, rhs, space);
2105 return renderExpression(allocator, ais, tree, rhs, space);
21722106}
21732107
21742108fn renderAsmOutput(
21752109 allocator: *mem.Allocator,
2176 stream: anytype,
2110 ais: anytype,
21772111 tree: *ast.Tree,
2178 indent: usize,
2179 start_col: *usize,
21802112 asm_output: *const ast.Node.Asm.Output,
21812113 space: Space,
2182) (@TypeOf(stream).Error || Error)!void {
2183 try stream.writeAll("[");
2184 try renderExpression(allocator, stream, tree, indent, start_col, asm_output.symbolic_name, Space.None);
2185 try stream.writeAll("] ");
2186 try renderExpression(allocator, stream, tree, indent, start_col, asm_output.constraint, Space.None);
2187 try stream.writeAll(" (");
2114) (@TypeOf(ais.*).Error || Error)!void {
2115 try ais.writer().writeAll("[");
2116 try renderExpression(allocator, ais, tree, asm_output.symbolic_name, Space.None);
2117 try ais.writer().writeAll("] ");
2118 try renderExpression(allocator, ais, tree, asm_output.constraint, Space.None);
2119 try ais.writer().writeAll(" (");
21882120
21892121 switch (asm_output.kind) {
21902122 ast.Node.Asm.Output.Kind.Variable => |variable_name| {
2191 try renderExpression(allocator, stream, tree, indent, start_col, &variable_name.base, Space.None);
2123 try renderExpression(allocator, ais, tree, &variable_name.base, Space.None);
21922124 },
21932125 ast.Node.Asm.Output.Kind.Return => |return_type| {
2194 try stream.writeAll("-> ");
2195 try renderExpression(allocator, stream, tree, indent, start_col, return_type, Space.None);
2126 try ais.writer().writeAll("-> ");
2127 try renderExpression(allocator, ais, tree, return_type, Space.None);
21962128 },
21972129 }
21982130
2199 return renderToken(tree, stream, asm_output.lastToken(), indent, start_col, space); // )
2131 return renderToken(tree, ais, asm_output.lastToken(), space); // )
22002132}
22012133
22022134fn renderAsmInput(
22032135 allocator: *mem.Allocator,
2204 stream: anytype,
2136 ais: anytype,
22052137 tree: *ast.Tree,
2206 indent: usize,
2207 start_col: *usize,
22082138 asm_input: *const ast.Node.Asm.Input,
22092139 space: Space,
2210) (@TypeOf(stream).Error || Error)!void {
2211 try stream.writeAll("[");
2212 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.symbolic_name, Space.None);
2213 try stream.writeAll("] ");
2214 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.constraint, Space.None);
2215 try stream.writeAll(" (");
2216 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.expr, Space.None);
2217 return renderToken(tree, stream, asm_input.lastToken(), indent, start_col, space); // )
2140) (@TypeOf(ais.*).Error || Error)!void {
2141 try ais.writer().writeAll("[");
2142 try renderExpression(allocator, ais, tree, asm_input.symbolic_name, Space.None);
2143 try ais.writer().writeAll("] ");
2144 try renderExpression(allocator, ais, tree, asm_input.constraint, Space.None);
2145 try ais.writer().writeAll(" (");
2146 try renderExpression(allocator, ais, tree, asm_input.expr, Space.None);
2147 return renderToken(tree, ais, asm_input.lastToken(), space); // )
22182148}
22192149
22202150fn renderVarDecl(
22212151 allocator: *mem.Allocator,
2222 stream: anytype,
2152 ais: anytype,
22232153 tree: *ast.Tree,
2224 indent: usize,
2225 start_col: *usize,
22262154 var_decl: *ast.Node.VarDecl,
2227) (@TypeOf(stream).Error || Error)!void {
2155) (@TypeOf(ais.*).Error || Error)!void {
22282156 if (var_decl.getVisibToken()) |visib_token| {
2229 try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub
2157 try renderToken(tree, ais, visib_token, Space.Space); // pub
22302158 }
22312159
22322160 if (var_decl.getExternExportToken()) |extern_export_token| {
2233 try renderToken(tree, stream, extern_export_token, indent, start_col, Space.Space); // extern
2161 try renderToken(tree, ais, extern_export_token, Space.Space); // extern
22342162
22352163 if (var_decl.getLibName()) |lib_name| {
2236 try renderExpression(allocator, stream, tree, indent, start_col, lib_name, Space.Space); // "lib"
2164 try renderExpression(allocator, ais, tree, lib_name, Space.Space); // "lib"
22372165 }
22382166 }
22392167
22402168 if (var_decl.getComptimeToken()) |comptime_token| {
2241 try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space); // comptime
2169 try renderToken(tree, ais, comptime_token, Space.Space); // comptime
22422170 }
22432171
22442172 if (var_decl.getThreadLocalToken()) |thread_local_token| {
2245 try renderToken(tree, stream, thread_local_token, indent, start_col, Space.Space); // threadlocal
2173 try renderToken(tree, ais, thread_local_token, Space.Space); // threadlocal
22462174 }
2247 try renderToken(tree, stream, var_decl.mut_token, indent, start_col, Space.Space); // var
2175 try renderToken(tree, ais, var_decl.mut_token, Space.Space); // var
22482176
22492177 const name_space = if (var_decl.getTypeNode() == null and
22502178 (var_decl.getAlignNode() != null or
......@@ -2253,95 +2181,92 @@ fn renderVarDecl(
22532181 Space.Space
22542182 else
22552183 Space.None;
2256 try renderToken(tree, stream, var_decl.name_token, indent, start_col, name_space);
2184 try renderToken(tree, ais, var_decl.name_token, name_space);
22572185
22582186 if (var_decl.getTypeNode()) |type_node| {
2259 try renderToken(tree, stream, tree.nextToken(var_decl.name_token), indent, start_col, Space.Space);
2187 try renderToken(tree, ais, tree.nextToken(var_decl.name_token), Space.Space);
22602188 const s = if (var_decl.getAlignNode() != null or
22612189 var_decl.getSectionNode() != null or
22622190 var_decl.getInitNode() != null) Space.Space else Space.None;
2263 try renderExpression(allocator, stream, tree, indent, start_col, type_node, s);
2191 try renderExpression(allocator, ais, tree, type_node, s);
22642192 }
22652193
22662194 if (var_decl.getAlignNode()) |align_node| {
22672195 const lparen = tree.prevToken(align_node.firstToken());
22682196 const align_kw = tree.prevToken(lparen);
22692197 const rparen = tree.nextToken(align_node.lastToken());
2270 try renderToken(tree, stream, align_kw, indent, start_col, Space.None); // align
2271 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
2272 try renderExpression(allocator, stream, tree, indent, start_col, align_node, Space.None);
2198 try renderToken(tree, ais, align_kw, Space.None); // align
2199 try renderToken(tree, ais, lparen, Space.None); // (
2200 try renderExpression(allocator, ais, tree, align_node, Space.None);
22732201 const s = if (var_decl.getSectionNode() != null or var_decl.getInitNode() != null) Space.Space else Space.None;
2274 try renderToken(tree, stream, rparen, indent, start_col, s); // )
2202 try renderToken(tree, ais, rparen, s); // )
22752203 }
22762204
22772205 if (var_decl.getSectionNode()) |section_node| {
22782206 const lparen = tree.prevToken(section_node.firstToken());
22792207 const section_kw = tree.prevToken(lparen);
22802208 const rparen = tree.nextToken(section_node.lastToken());
2281 try renderToken(tree, stream, section_kw, indent, start_col, Space.None); // linksection
2282 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
2283 try renderExpression(allocator, stream, tree, indent, start_col, section_node, Space.None);
2209 try renderToken(tree, ais, section_kw, Space.None); // linksection
2210 try renderToken(tree, ais, lparen, Space.None); // (
2211 try renderExpression(allocator, ais, tree, section_node, Space.None);
22842212 const s = if (var_decl.getInitNode() != null) Space.Space else Space.None;
2285 try renderToken(tree, stream, rparen, indent, start_col, s); // )
2213 try renderToken(tree, ais, rparen, s); // )
22862214 }
22872215
22882216 if (var_decl.getInitNode()) |init_node| {
22892217 const s = if (init_node.tag == .MultilineStringLiteral) Space.None else Space.Space;
2290 try renderToken(tree, stream, var_decl.getEqToken().?, indent, start_col, s); // =
2291 try renderExpression(allocator, stream, tree, indent, start_col, init_node, Space.None);
2218 try renderToken(tree, ais, var_decl.getEqToken().?, s); // =
2219 ais.pushIndentOneShot();
2220 try renderExpression(allocator, ais, tree, init_node, Space.None);
22922221 }
22932222
2294 try renderToken(tree, stream, var_decl.semicolon_token, indent, start_col, Space.Newline);
2223 try renderToken(tree, ais, var_decl.semicolon_token, Space.Newline);
22952224}
22962225
22972226fn renderParamDecl(
22982227 allocator: *mem.Allocator,
2299 stream: anytype,
2228 ais: anytype,
23002229 tree: *ast.Tree,
2301 indent: usize,
2302 start_col: *usize,
23032230 param_decl: ast.Node.FnProto.ParamDecl,
23042231 space: Space,
2305) (@TypeOf(stream).Error || Error)!void {
2306 try renderDocComments(tree, stream, param_decl, param_decl.doc_comments, indent, start_col);
2232) (@TypeOf(ais.*).Error || Error)!void {
2233 try renderDocComments(tree, ais, param_decl, param_decl.doc_comments);
23072234
23082235 if (param_decl.comptime_token) |comptime_token| {
2309 try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space);
2236 try renderToken(tree, ais, comptime_token, Space.Space);
23102237 }
23112238 if (param_decl.noalias_token) |noalias_token| {
2312 try renderToken(tree, stream, noalias_token, indent, start_col, Space.Space);
2239 try renderToken(tree, ais, noalias_token, Space.Space);
23132240 }
23142241 if (param_decl.name_token) |name_token| {
2315 try renderToken(tree, stream, name_token, indent, start_col, Space.None);
2316 try renderToken(tree, stream, tree.nextToken(name_token), indent, start_col, Space.Space); // :
2242 try renderToken(tree, ais, name_token, Space.None);
2243 try renderToken(tree, ais, tree.nextToken(name_token), Space.Space); // :
23172244 }
23182245 switch (param_decl.param_type) {
2319 .any_type, .type_expr => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, space),
2246 .any_type, .type_expr => |node| try renderExpression(allocator, ais, tree, node, space),
23202247 }
23212248}
23222249
23232250fn renderStatement(
23242251 allocator: *mem.Allocator,
2325 stream: anytype,
2252 ais: anytype,
23262253 tree: *ast.Tree,
2327 indent: usize,
2328 start_col: *usize,
23292254 base: *ast.Node,
2330) (@TypeOf(stream).Error || Error)!void {
2255) (@TypeOf(ais.*).Error || Error)!void {
23312256 switch (base.tag) {
23322257 .VarDecl => {
23332258 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
2334 try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl);
2259 try renderVarDecl(allocator, ais, tree, var_decl);
23352260 },
23362261 else => {
23372262 if (base.requireSemiColon()) {
2338 try renderExpression(allocator, stream, tree, indent, start_col, base, Space.None);
2263 try renderExpression(allocator, ais, tree, base, Space.None);
23392264
23402265 const semicolon_index = tree.nextToken(base.lastToken());
23412266 assert(tree.token_ids[semicolon_index] == .Semicolon);
2342 try renderToken(tree, stream, semicolon_index, indent, start_col, Space.Newline);
2267 try renderToken(tree, ais, semicolon_index, Space.Newline);
23432268 } else {
2344 try renderExpression(allocator, stream, tree, indent, start_col, base, Space.Newline);
2269 try renderExpression(allocator, ais, tree, base, Space.Newline);
23452270 }
23462271 },
23472272 }
......@@ -2360,24 +2285,19 @@ const Space = enum {
23602285
23612286fn renderTokenOffset(
23622287 tree: *ast.Tree,
2363 stream: anytype,
2288 ais: anytype,
23642289 token_index: ast.TokenIndex,
2365 indent: usize,
2366 start_col: *usize,
23672290 space: Space,
23682291 token_skip_bytes: usize,
2369) (@TypeOf(stream).Error || Error)!void {
2292) (@TypeOf(ais.*).Error || Error)!void {
23702293 if (space == Space.BlockStart) {
2371 if (start_col.* < indent + indent_delta)
2372 return renderToken(tree, stream, token_index, indent, start_col, Space.Space);
2373 try renderToken(tree, stream, token_index, indent, start_col, Space.Newline);
2374 try stream.writeByteNTimes(' ', indent);
2375 start_col.* = indent;
2376 return;
2294 // If placing the lbrace on the current line would cause an uggly gap then put the lbrace on the next line
2295 const new_space = if (ais.isLineOverIndented()) Space.Newline else Space.Space;
2296 return renderToken(tree, ais, token_index, new_space);
23772297 }
23782298
23792299 var token_loc = tree.token_locs[token_index];
2380 try stream.writeAll(mem.trimRight(u8, tree.tokenSliceLoc(token_loc)[token_skip_bytes..], " "));
2300 try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(token_loc)[token_skip_bytes..], " "));
23812301
23822302 if (space == Space.NoComment)
23832303 return;
......@@ -2386,20 +2306,20 @@ fn renderTokenOffset(
23862306 var next_token_loc = tree.token_locs[token_index + 1];
23872307
23882308 if (space == Space.Comma) switch (next_token_id) {
2389 .Comma => return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline),
2309 .Comma => return renderToken(tree, ais, token_index + 1, Space.Newline),
23902310 .LineComment => {
2391 try stream.writeAll(", ");
2392 return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline);
2311 try ais.writer().writeAll(", ");
2312 return renderToken(tree, ais, token_index + 1, Space.Newline);
23932313 },
23942314 else => {
23952315 if (token_index + 2 < tree.token_ids.len and
23962316 tree.token_ids[token_index + 2] == .MultilineStringLiteralLine)
23972317 {
2398 try stream.writeAll(",");
2318 try ais.writer().writeAll(",");
23992319 return;
24002320 } else {
2401 try stream.writeAll(",\n");
2402 start_col.* = 0;
2321 try ais.writer().writeAll(",");
2322 try ais.insertNewline();
24032323 return;
24042324 }
24052325 },
......@@ -2423,15 +2343,14 @@ fn renderTokenOffset(
24232343 if (next_token_id == .MultilineStringLiteralLine) {
24242344 return;
24252345 } else {
2426 try stream.writeAll("\n");
2427 start_col.* = 0;
2346 try ais.insertNewline();
24282347 return;
24292348 }
24302349 },
24312350 Space.Space, Space.SpaceOrOutdent => {
24322351 if (next_token_id == .MultilineStringLiteralLine)
24332352 return;
2434 try stream.writeByte(' ');
2353 try ais.writer().writeByte(' ');
24352354 return;
24362355 },
24372356 Space.NoComment, Space.Comma, Space.BlockStart => unreachable,
......@@ -2448,8 +2367,7 @@ fn renderTokenOffset(
24482367 next_token_id = tree.token_ids[token_index + offset];
24492368 next_token_loc = tree.token_locs[token_index + offset];
24502369 if (next_token_id != .LineComment) {
2451 try stream.writeByte('\n');
2452 start_col.* = 0;
2370 try ais.insertNewline();
24532371 return;
24542372 }
24552373 },
......@@ -2462,7 +2380,7 @@ fn renderTokenOffset(
24622380
24632381 var loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
24642382 if (loc.line == 0) {
2465 try stream.print(" {}", .{mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " ")});
2383 try ais.writer().print(" {}", .{mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " ")});
24662384 offset = 2;
24672385 token_loc = next_token_loc;
24682386 next_token_loc = tree.token_locs[token_index + offset];
......@@ -2470,26 +2388,16 @@ fn renderTokenOffset(
24702388 if (next_token_id != .LineComment) {
24712389 switch (space) {
24722390 Space.None, Space.Space => {
2473 try stream.writeByte('\n');
2474 const after_comment_token = tree.token_ids[token_index + offset];
2475 const next_line_indent = switch (after_comment_token) {
2476 .RParen, .RBrace, .RBracket => indent,
2477 else => indent + indent_delta,
2478 };
2479 try stream.writeByteNTimes(' ', next_line_indent);
2480 start_col.* = next_line_indent;
2391 try ais.insertNewline();
24812392 },
24822393 Space.SpaceOrOutdent => {
2483 try stream.writeByte('\n');
2484 try stream.writeByteNTimes(' ', indent);
2485 start_col.* = indent;
2394 try ais.insertNewline();
24862395 },
24872396 Space.Newline => {
24882397 if (next_token_id == .MultilineStringLiteralLine) {
24892398 return;
24902399 } else {
2491 try stream.writeAll("\n");
2492 start_col.* = 0;
2400 try ais.insertNewline();
24932401 return;
24942402 }
24952403 },
......@@ -2505,10 +2413,9 @@ fn renderTokenOffset(
25052413 // translate-c doesn't generate correct newlines
25062414 // in generated code (loc.line == 0) so treat that case
25072415 // as though there was meant to be a newline between the tokens
2508 const newline_count = if (loc.line <= 1) @as(u8, 1) else @as(u8, 2);
2509 try stream.writeByteNTimes('\n', newline_count);
2510 try stream.writeByteNTimes(' ', indent);
2511 try stream.writeAll(mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " "));
2416 var newline_count = if (loc.line <= 1) @as(u8, 1) else @as(u8, 2);
2417 while (newline_count > 0) : (newline_count -= 1) try ais.insertNewline();
2418 try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " "));
25122419
25132420 offset += 1;
25142421 token_loc = next_token_loc;
......@@ -2520,32 +2427,15 @@ fn renderTokenOffset(
25202427 if (next_token_id == .MultilineStringLiteralLine) {
25212428 return;
25222429 } else {
2523 try stream.writeAll("\n");
2524 start_col.* = 0;
2430 try ais.insertNewline();
25252431 return;
25262432 }
25272433 },
25282434 Space.None, Space.Space => {
2529 try stream.writeByte('\n');
2530
2531 const after_comment_token = tree.token_ids[token_index + offset];
2532 const next_line_indent = switch (after_comment_token) {
2533 .RParen, .RBrace, .RBracket => blk: {
2534 if (indent > indent_delta) {
2535 break :blk indent - indent_delta;
2536 } else {
2537 break :blk 0;
2538 }
2539 },
2540 else => indent,
2541 };
2542 try stream.writeByteNTimes(' ', next_line_indent);
2543 start_col.* = next_line_indent;
2435 try ais.insertNewline();
25442436 },
25452437 Space.SpaceOrOutdent => {
2546 try stream.writeByte('\n');
2547 try stream.writeByteNTimes(' ', indent);
2548 start_col.* = indent;
2438 try ais.insertNewline();
25492439 },
25502440 Space.NoNewline => {},
25512441 Space.NoComment, Space.Comma, Space.BlockStart => unreachable,
......@@ -2558,46 +2448,38 @@ fn renderTokenOffset(
25582448
25592449fn renderToken(
25602450 tree: *ast.Tree,
2561 stream: anytype,
2451 ais: anytype,
25622452 token_index: ast.TokenIndex,
2563 indent: usize,
2564 start_col: *usize,
25652453 space: Space,
2566) (@TypeOf(stream).Error || Error)!void {
2567 return renderTokenOffset(tree, stream, token_index, indent, start_col, space, 0);
2454) (@TypeOf(ais.*).Error || Error)!void {
2455 return renderTokenOffset(tree, ais, token_index, space, 0);
25682456}
25692457
25702458fn renderDocComments(
25712459 tree: *ast.Tree,
2572 stream: anytype,
2460 ais: anytype,
25732461 node: anytype,
25742462 doc_comments: ?*ast.Node.DocComment,
2575 indent: usize,
2576 start_col: *usize,
2577) (@TypeOf(stream).Error || Error)!void {
2463) (@TypeOf(ais.*).Error || Error)!void {
25782464 const comment = doc_comments orelse return;
2579 return renderDocCommentsToken(tree, stream, comment, node.firstToken(), indent, start_col);
2465 return renderDocCommentsToken(tree, ais, comment, node.firstToken());
25802466}
25812467
25822468fn renderDocCommentsToken(
25832469 tree: *ast.Tree,
2584 stream: anytype,
2470 ais: anytype,
25852471 comment: *ast.Node.DocComment,
25862472 first_token: ast.TokenIndex,
2587 indent: usize,
2588 start_col: *usize,
2589) (@TypeOf(stream).Error || Error)!void {
2473) (@TypeOf(ais.*).Error || Error)!void {
25902474 var tok_i = comment.first_line;
25912475 while (true) : (tok_i += 1) {
25922476 switch (tree.token_ids[tok_i]) {
25932477 .DocComment, .ContainerDocComment => {
25942478 if (comment.first_line < first_token) {
2595 try renderToken(tree, stream, tok_i, indent, start_col, Space.Newline);
2596 try stream.writeByteNTimes(' ', indent);
2479 try renderToken(tree, ais, tok_i, Space.Newline);
25972480 } else {
2598 try renderToken(tree, stream, tok_i, indent, start_col, Space.NoComment);
2599 try stream.writeAll("\n");
2600 try stream.writeByteNTimes(' ', indent);
2481 try renderToken(tree, ais, tok_i, Space.NoComment);
2482 try ais.insertNewline();
26012483 }
26022484 },
26032485 .LineComment => continue,
......@@ -2669,41 +2551,10 @@ fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
26692551 };
26702552}
26712553
2672/// A `std.io.OutStream` that returns whether the given character has been written to it.
2673/// The contents are not written to anything.
2674const FindByteOutStream = struct {
2675 byte_found: bool,
2676 byte: u8,
2677
2678 pub const Error = error{};
2679 pub const OutStream = std.io.OutStream(*FindByteOutStream, Error, write);
2680
2681 pub fn init(byte: u8) FindByteOutStream {
2682 return FindByteOutStream{
2683 .byte = byte,
2684 .byte_found = false,
2685 };
2686 }
2687
2688 pub fn write(self: *FindByteOutStream, bytes: []const u8) Error!usize {
2689 if (self.byte_found) return bytes.len;
2690 self.byte_found = blk: {
2691 for (bytes) |b|
2692 if (b == self.byte) break :blk true;
2693 break :blk false;
2694 };
2695 return bytes.len;
2696 }
2697
2698 pub fn outStream(self: *FindByteOutStream) OutStream {
2699 return .{ .context = self };
2700 }
2701};
2702
2703fn copyFixingWhitespace(stream: anytype, slice: []const u8) @TypeOf(stream).Error!void {
2554fn copyFixingWhitespace(ais: anytype, slice: []const u8) @TypeOf(ais.*).Error!void {
27042555 for (slice) |byte| switch (byte) {
2705 '\t' => try stream.writeAll(" "),
2556 '\t' => try ais.writer().writeAll(" "),
27062557 '\r' => {},
2707 else => try stream.writeByte(byte),
2558 else => try ais.writer().writeByte(byte),
27082559 };
27092560}
lib/std/zig/tokenizer.zig+2-1
......@@ -1175,6 +1175,7 @@ pub const Tokenizer = struct {
11751175 },
11761176 .num_dot_dec => switch (c) {
11771177 '.' => {
1178 result.id = .IntegerLiteral;
11781179 self.index -= 1;
11791180 state = .start;
11801181 break;
......@@ -1183,7 +1184,6 @@ pub const Tokenizer = struct {
11831184 state = .float_exponent_unsigned;
11841185 },
11851186 '0'...'9' => {
1186 result.id = .FloatLiteral;
11871187 state = .float_fraction_dec;
11881188 },
11891189 else => {
......@@ -1769,6 +1769,7 @@ test "tokenizer - number literals decimal" {
17691769 testTokenize("7", &[_]Token.Id{.IntegerLiteral});
17701770 testTokenize("8", &[_]Token.Id{.IntegerLiteral});
17711771 testTokenize("9", &[_]Token.Id{.IntegerLiteral});
1772 testTokenize("1..", &[_]Token.Id{ .IntegerLiteral, .Ellipsis2 });
17721773 testTokenize("0a", &[_]Token.Id{ .Invalid, .Identifier });
17731774 testTokenize("9b", &[_]Token.Id{ .Invalid, .Identifier });
17741775 testTokenize("1z", &[_]Token.Id{ .Invalid, .Identifier });
src-self-hosted/Module.zig+165-54
......@@ -125,7 +125,7 @@ pub const Decl = struct {
125125 /// mapping them to an address in the output file.
126126 /// Memory owned by this decl, using Module's allocator.
127127 name: [*:0]const u8,
128 /// The direct parent container of the Decl. This is either a `Scope.File` or `Scope.ZIRModule`.
128 /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`.
129129 /// Reference to externally owned memory.
130130 scope: *Scope,
131131 /// The AST Node decl index or ZIR Inst index that contains this declaration.
......@@ -217,9 +217,10 @@ pub const Decl = struct {
217217
218218 pub fn src(self: Decl) usize {
219219 switch (self.scope.tag) {
220 .file => {
221 const file = @fieldParentPtr(Scope.File, "base", self.scope);
222 const tree = file.contents.tree;
220 .container => {
221 const container = @fieldParentPtr(Scope.Container, "base", self.scope);
222 const tree = container.file_scope.contents.tree;
223 // TODO Container should have it's own decls()
223224 const decl_node = tree.root_node.decls()[self.src_index];
224225 return tree.token_locs[decl_node.firstToken()].start;
225226 },
......@@ -229,7 +230,7 @@ pub const Decl = struct {
229230 const src_decl = module.decls[self.src_index];
230231 return src_decl.inst.src;
231232 },
232 .block => unreachable,
233 .file, .block => unreachable,
233234 .gen_zir => unreachable,
234235 .local_val => unreachable,
235236 .local_ptr => unreachable,
......@@ -359,6 +360,7 @@ pub const Scope = struct {
359360 .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
360361 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
361362 .file => unreachable,
363 .container => unreachable,
362364 }
363365 }
364366
......@@ -368,15 +370,16 @@ pub const Scope = struct {
368370 return switch (self.tag) {
369371 .block => self.cast(Block).?.decl,
370372 .gen_zir => self.cast(GenZIR).?.decl,
371 .local_val => return self.cast(LocalVal).?.gen_zir.decl,
372 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl,
373 .local_val => self.cast(LocalVal).?.gen_zir.decl,
374 .local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
373375 .decl => self.cast(DeclAnalysis).?.decl,
374376 .zir_module => null,
375377 .file => null,
378 .container => null,
376379 };
377380 }
378381
379 /// Asserts the scope has a parent which is a ZIRModule or File and
382 /// Asserts the scope has a parent which is a ZIRModule or Container and
380383 /// returns it.
381384 pub fn namespace(self: *Scope) *Scope {
382385 switch (self.tag) {
......@@ -385,7 +388,8 @@ pub const Scope = struct {
385388 .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,
386389 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,
387390 .decl => return self.cast(DeclAnalysis).?.decl.scope,
388 .zir_module, .file => return self,
391 .file => return &self.cast(File).?.root_container.base,
392 .zir_module, .container => return self,
389393 }
390394 }
391395
......@@ -399,8 +403,9 @@ pub const Scope = struct {
399403 .local_val => unreachable,
400404 .local_ptr => unreachable,
401405 .decl => unreachable,
406 .file => unreachable,
402407 .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
403 .file => return self.cast(File).?.fullyQualifiedNameHash(name),
408 .container => return self.cast(Container).?.fullyQualifiedNameHash(name),
404409 }
405410 }
406411
......@@ -409,11 +414,12 @@ pub const Scope = struct {
409414 switch (self.tag) {
410415 .file => return self.cast(File).?.contents.tree,
411416 .zir_module => unreachable,
412 .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(File).?.contents.tree,
413 .block => return self.cast(Block).?.decl.scope.cast(File).?.contents.tree,
414 .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(File).?.contents.tree,
415 .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(File).?.contents.tree,
416 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(File).?.contents.tree,
417 .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,
418 .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,
419 .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,
420 .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
421 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
422 .container => return self.cast(Container).?.file_scope.contents.tree,
417423 }
418424 }
419425
......@@ -427,13 +433,15 @@ pub const Scope = struct {
427433 .decl => unreachable,
428434 .zir_module => unreachable,
429435 .file => unreachable,
436 .container => unreachable,
430437 };
431438 }
432439
433 /// Asserts the scope has a parent which is a ZIRModule or File and
440 /// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and
434441 /// returns the sub_file_path field.
435442 pub fn subFilePath(base: *Scope) []const u8 {
436443 switch (base.tag) {
444 .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,
437445 .file => return @fieldParentPtr(File, "base", base).sub_file_path,
438446 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
439447 .block => unreachable,
......@@ -453,11 +461,13 @@ pub const Scope = struct {
453461 .local_val => unreachable,
454462 .local_ptr => unreachable,
455463 .decl => unreachable,
464 .container => unreachable,
456465 }
457466 }
458467
459468 pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {
460469 switch (base.tag) {
470 .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),
461471 .file => return @fieldParentPtr(File, "base", base).getSource(module),
462472 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
463473 .gen_zir => unreachable,
......@@ -471,8 +481,9 @@ pub const Scope = struct {
471481 /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
472482 pub fn removeDecl(base: *Scope, child: *Decl) void {
473483 switch (base.tag) {
474 .file => return @fieldParentPtr(File, "base", base).removeDecl(child),
484 .container => return @fieldParentPtr(Container, "base", base).removeDecl(child),
475485 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
486 .file => unreachable,
476487 .block => unreachable,
477488 .gen_zir => unreachable,
478489 .local_val => unreachable,
......@@ -499,6 +510,7 @@ pub const Scope = struct {
499510 .local_val => unreachable,
500511 .local_ptr => unreachable,
501512 .decl => unreachable,
513 .container => unreachable,
502514 }
503515 }
504516
......@@ -515,6 +527,8 @@ pub const Scope = struct {
515527 zir_module,
516528 /// .zig source code.
517529 file,
530 /// struct, enum or union, every .file contains one of these.
531 container,
518532 block,
519533 decl,
520534 gen_zir,
......@@ -522,6 +536,33 @@ pub const Scope = struct {
522536 local_ptr,
523537 };
524538
539 pub const Container = struct {
540 pub const base_tag: Tag = .container;
541 base: Scope = Scope{ .tag = base_tag },
542
543 file_scope: *Scope.File,
544
545 /// Direct children of the file.
546 decls: std.AutoArrayHashMapUnmanaged(*Decl, void),
547
548 // TODO implement container types and put this in a status union
549 // ty: Type
550
551 pub fn deinit(self: *Container, gpa: *Allocator) void {
552 self.decls.deinit(gpa);
553 self.* = undefined;
554 }
555
556 pub fn removeDecl(self: *Container, child: *Decl) void {
557 _ = self.decls.remove(child);
558 }
559
560 pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash {
561 // TODO container scope qualified names.
562 return std.zig.hashSrc(name);
563 }
564 };
565
525566 pub const File = struct {
526567 pub const base_tag: Tag = .file;
527568 base: Scope = Scope{ .tag = base_tag },
......@@ -544,8 +585,7 @@ pub const Scope = struct {
544585 loaded_success,
545586 },
546587
547 /// Direct children of the file.
548 decls: ArrayListUnmanaged(*Decl),
588 root_container: Container,
549589
550590 pub fn unload(self: *File, gpa: *Allocator) void {
551591 switch (self.status) {
......@@ -569,20 +609,11 @@ pub const Scope = struct {
569609 }
570610
571611 pub fn deinit(self: *File, gpa: *Allocator) void {
572 self.decls.deinit(gpa);
612 self.root_container.deinit(gpa);
573613 self.unload(gpa);
574614 self.* = undefined;
575615 }
576616
577 pub fn removeDecl(self: *File, child: *Decl) void {
578 for (self.decls.items) |item, i| {
579 if (item == child) {
580 _ = self.decls.swapRemove(i);
581 return;
582 }
583 }
584 }
585
586617 pub fn dumpSrc(self: *File, src: usize) void {
587618 const loc = std.zig.findLineColumn(self.source.bytes, src);
588619 std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
......@@ -595,6 +626,7 @@ pub const Scope = struct {
595626 module.gpa,
596627 self.sub_file_path,
597628 std.math.maxInt(u32),
629 null,
598630 1,
599631 0,
600632 );
......@@ -604,11 +636,6 @@ pub const Scope = struct {
604636 .bytes => |bytes| return bytes,
605637 }
606638 }
607
608 pub fn fullyQualifiedNameHash(self: *File, name: []const u8) NameHash {
609 // We don't have struct scopes yet so this is currently just a simple name hash.
610 return std.zig.hashSrc(name);
611 }
612639 };
613640
614641 pub const ZIRModule = struct {
......@@ -697,6 +724,7 @@ pub const Scope = struct {
697724 module.gpa,
698725 self.sub_file_path,
699726 std.math.maxInt(u32),
727 null,
700728 1,
701729 0,
702730 );
......@@ -861,7 +889,10 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
861889 .source = .{ .unloaded = {} },
862890 .contents = .{ .not_available = {} },
863891 .status = .never_loaded,
864 .decls = .{},
892 .root_container = .{
893 .file_scope = root_scope,
894 .decls = .{},
895 },
865896 };
866897 break :blk &root_scope.base;
867898 } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) {
......@@ -969,7 +1000,7 @@ pub fn update(self: *Module) !void {
9691000 // to force a refresh we unload now.
9701001 if (self.root_scope.cast(Scope.File)) |zig_file| {
9711002 zig_file.unload(self.gpa);
972 self.analyzeRootSrcFile(zig_file) catch |err| switch (err) {
1003 self.analyzeContainer(&zig_file.root_container) catch |err| switch (err) {
9731004 error.AnalysisFail => {
9741005 assert(self.totalErrorCount() != 0);
9751006 },
......@@ -1237,8 +1268,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12371268 const tracy = trace(@src());
12381269 defer tracy.end();
12391270
1240 const file_scope = decl.scope.cast(Scope.File).?;
1241 const tree = try self.getAstTree(file_scope);
1271 const container_scope = decl.scope.cast(Scope.Container).?;
1272 const tree = try self.getAstTree(container_scope);
12421273 const ast_node = tree.root_node.decls()[decl.src_index];
12431274 switch (ast_node.tag) {
12441275 .FnProto => {
......@@ -1698,10 +1729,12 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
16981729 }
16991730}
17001731
1701fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1732fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
17021733 const tracy = trace(@src());
17031734 defer tracy.end();
17041735
1736 const root_scope = container_scope.file_scope;
1737
17051738 switch (root_scope.status) {
17061739 .never_loaded, .unloaded_success => {
17071740 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
......@@ -1743,25 +1776,25 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
17431776 }
17441777}
17451778
1746fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1779fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
17471780 const tracy = trace(@src());
17481781 defer tracy.end();
17491782
17501783 // We may be analyzing it for the first time, or this may be
17511784 // an incremental update. This code handles both cases.
1752 const tree = try self.getAstTree(root_scope);
1785 const tree = try self.getAstTree(container_scope);
17531786 const decls = tree.root_node.decls();
17541787
17551788 try self.work_queue.ensureUnusedCapacity(decls.len);
1756 try root_scope.decls.ensureCapacity(self.gpa, decls.len);
1789 try container_scope.decls.ensureCapacity(self.gpa, decls.len);
17571790
17581791 // Keep track of the decls that we expect to see in this file so that
17591792 // we know which ones have been deleted.
17601793 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
17611794 defer deleted_decls.deinit();
1762 try deleted_decls.ensureCapacity(root_scope.decls.items.len);
1763 for (root_scope.decls.items) |file_decl| {
1764 deleted_decls.putAssumeCapacityNoClobber(file_decl, {});
1795 try deleted_decls.ensureCapacity(container_scope.decls.items().len);
1796 for (container_scope.decls.items()) |entry| {
1797 deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
17651798 }
17661799
17671800 for (decls) |src_decl, decl_i| {
......@@ -1773,7 +1806,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
17731806
17741807 const name_loc = tree.token_locs[name_tok];
17751808 const name = tree.tokenSliceLoc(name_loc);
1776 const name_hash = root_scope.fullyQualifiedNameHash(name);
1809 const name_hash = container_scope.fullyQualifiedNameHash(name);
17771810 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
17781811 if (self.decl_table.get(name_hash)) |decl| {
17791812 // Update the AST Node index of the decl, even if its contents are unchanged, it may
......@@ -1789,6 +1822,9 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
17891822 try self.markOutdatedDecl(decl);
17901823 decl.contents_hash = contents_hash;
17911824 } else switch (self.bin_file.tag) {
1825 .coff => {
1826 // TODO Implement for COFF
1827 },
17921828 .elf => if (decl.fn_link.elf.len != 0) {
17931829 // TODO Look into detecting when this would be unnecessary by storing enough state
17941830 // in `Decl` to notice that the line number did not change.
......@@ -1801,8 +1837,8 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
18011837 }
18021838 }
18031839 } else {
1804 const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
1805 root_scope.decls.appendAssumeCapacity(new_decl);
1840 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1841 container_scope.decls.putAssumeCapacity(new_decl, {});
18061842 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
18071843 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
18081844 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
......@@ -1812,7 +1848,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
18121848 } else if (src_decl.castTag(.VarDecl)) |var_decl| {
18131849 const name_loc = tree.token_locs[var_decl.name_token];
18141850 const name = tree.tokenSliceLoc(name_loc);
1815 const name_hash = root_scope.fullyQualifiedNameHash(name);
1851 const name_hash = container_scope.fullyQualifiedNameHash(name);
18161852 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
18171853 if (self.decl_table.get(name_hash)) |decl| {
18181854 // Update the AST Node index of the decl, even if its contents are unchanged, it may
......@@ -1828,8 +1864,8 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
18281864 decl.contents_hash = contents_hash;
18291865 }
18301866 } else {
1831 const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
1832 root_scope.decls.appendAssumeCapacity(new_decl);
1867 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1868 container_scope.decls.putAssumeCapacity(new_decl, {});
18331869 if (var_decl.getExternExportToken()) |maybe_export_token| {
18341870 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
18351871 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
......@@ -1841,11 +1877,11 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
18411877 const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index});
18421878 defer self.gpa.free(name);
18431879
1844 const name_hash = root_scope.fullyQualifiedNameHash(name);
1880 const name_hash = container_scope.fullyQualifiedNameHash(name);
18451881 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
18461882
1847 const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
1848 root_scope.decls.appendAssumeCapacity(new_decl);
1883 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1884 container_scope.decls.putAssumeCapacity(new_decl, {});
18491885 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
18501886 } else if (src_decl.castTag(.ContainerField)) |container_field| {
18511887 log.err("TODO: analyze container field", .{});
......@@ -2047,12 +2083,14 @@ fn allocateNewDecl(
20472083 .deletion_flag = false,
20482084 .contents_hash = contents_hash,
20492085 .link = switch (self.bin_file.tag) {
2086 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
20502087 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
20512088 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
20522089 .c => .{ .c = {} },
20532090 .wasm => .{ .wasm = {} },
20542091 },
20552092 .fn_link = switch (self.bin_file.tag) {
2093 .coff => .{ .coff = {} },
20562094 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
20572095 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
20582096 .c => .{ .c = {} },
......@@ -2591,6 +2629,72 @@ pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) In
25912629 return self.fail(scope, src, "TODO implement analysis of iserr", .{});
25922630}
25932631
2632pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {
2633 const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
2634 .Pointer => array_ptr.ty.elemType(),
2635 else => return self.fail(scope, src, "expected pointer, found '{}'", .{array_ptr.ty}),
2636 };
2637
2638 var array_type = ptr_child;
2639 const elem_type = switch (ptr_child.zigTypeTag()) {
2640 .Array => ptr_child.elemType(),
2641 .Pointer => blk: {
2642 if (ptr_child.isSinglePointer()) {
2643 if (ptr_child.elemType().zigTypeTag() == .Array) {
2644 array_type = ptr_child.elemType();
2645 break :blk ptr_child.elemType().elemType();
2646 }
2647
2648 return self.fail(scope, src, "slice of single-item pointer", .{});
2649 }
2650 break :blk ptr_child.elemType();
2651 },
2652 else => return self.fail(scope, src, "slice of non-array type '{}'", .{ptr_child}),
2653 };
2654
2655 const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
2656 const casted = try self.coerce(scope, elem_type, sentinel);
2657 break :blk try self.resolveConstValue(scope, casted);
2658 } else null;
2659
2660 var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
2661 var return_elem_type = elem_type;
2662 if (end_opt) |end| {
2663 if (end.value()) |end_val| {
2664 if (start.value()) |start_val| {
2665 const start_u64 = start_val.toUnsignedInt();
2666 const end_u64 = end_val.toUnsignedInt();
2667 if (start_u64 > end_u64) {
2668 return self.fail(scope, src, "out of bounds slice", .{});
2669 }
2670
2671 const len = end_u64 - start_u64;
2672 const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
2673 array_type.sentinel()
2674 else
2675 slice_sentinel;
2676 return_elem_type = try self.arrayType(scope, len, array_sentinel, elem_type);
2677 return_ptr_size = .One;
2678 }
2679 }
2680 }
2681 const return_type = try self.ptrType(
2682 scope,
2683 src,
2684 return_elem_type,
2685 if (end_opt == null) slice_sentinel else null,
2686 0, // TODO alignment
2687 0,
2688 0,
2689 !ptr_child.isConstPtr(),
2690 ptr_child.isAllowzeroPtr(),
2691 ptr_child.isVolatilePtr(),
2692 return_ptr_size,
2693 );
2694
2695 return self.fail(scope, src, "TODO implement analysis of slice", .{});
2696}
2697
25942698/// Asserts that lhs and rhs types are both numeric.
25952699pub fn cmpNumeric(
25962700 self: *Module,
......@@ -2801,6 +2905,12 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty
28012905 prev_inst = next_inst;
28022906 continue;
28032907 }
2908 if (next_inst.ty.zigTypeTag() == .Undefined)
2909 continue;
2910 if (prev_inst.ty.zigTypeTag() == .Undefined) {
2911 prev_inst = next_inst;
2912 continue;
2913 }
28042914 if (prev_inst.ty.isInt() and
28052915 next_inst.ty.isInt() and
28062916 prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())
......@@ -3052,6 +3162,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err
30523162 self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
30533163 },
30543164 .file => unreachable,
3165 .container => unreachable,
30553166 }
30563167 return error.AnalysisFail;
30573168}
src-self-hosted/astgen.zig+74-26
......@@ -275,16 +275,16 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
275275 .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
276276 .For => return forExpr(mod, scope, rl, node.castTag(.For).?),
277277 .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
278 .Slice => return rlWrap(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)),
278279 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
279280 .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
281 .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
280282
281283 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
282284 .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
283 .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}),
284285 .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
285286 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
286287 .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
287 .Slice => return mod.failNode(scope, node, "TODO implement astgen.expr for .Slice", .{}),
288288 .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),
289289 .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
290290 .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
......@@ -790,13 +790,31 @@ fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*
790790}
791791
792792fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst {
793 return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .iserr, .unwrap_err_unsafe, node.rhs, node.payload);
794}
795
796fn orelseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
797 return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .isnull, .unwrap_optional_unsafe, node.rhs, null);
798}
799
800fn orelseCatchExpr(
801 mod: *Module,
802 scope: *Scope,
803 rl: ResultLoc,
804 lhs: *ast.Node,
805 op_token: ast.TokenIndex,
806 cond_op: zir.Inst.Tag,
807 unwrap_op: zir.Inst.Tag,
808 rhs: *ast.Node,
809 payload_node: ?*ast.Node,
810) InnerError!*zir.Inst {
793811 const tree = scope.tree();
794 const src = tree.token_locs[node.op_token].start;
812 const src = tree.token_locs[op_token].start;
795813
796 const err_union_ptr = try expr(mod, scope, .ref, node.lhs);
797 // TODO we could avoid an unnecessary copy if .iserr took a pointer
798 const err_union = try addZIRUnOp(mod, scope, src, .deref, err_union_ptr);
799 const cond = try addZIRUnOp(mod, scope, src, .iserr, err_union);
814 const operand_ptr = try expr(mod, scope, .ref, lhs);
815 // TODO we could avoid an unnecessary copy if .iserr, .isnull took a pointer
816 const err_union = try addZIRUnOp(mod, scope, src, .deref, operand_ptr);
817 const cond = try addZIRUnOp(mod, scope, src, cond_op, err_union);
800818
801819 var block_scope: Scope.GenZIR = .{
802820 .parent = scope,
......@@ -825,55 +843,55 @@ fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch)
825843 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
826844 };
827845
828 var err_scope: Scope.GenZIR = .{
846 var then_scope: Scope.GenZIR = .{
829847 .parent = scope,
830848 .decl = block_scope.decl,
831849 .arena = block_scope.arena,
832850 .instructions = .{},
833851 };
834 defer err_scope.instructions.deinit(mod.gpa);
852 defer then_scope.instructions.deinit(mod.gpa);
835853
836854 var err_val_scope: Scope.LocalVal = undefined;
837 const err_sub_scope = blk: {
838 const payload = node.payload orelse
839 break :blk &err_scope.base;
855 const then_sub_scope = blk: {
856 const payload = payload_node orelse
857 break :blk &then_scope.base;
840858
841859 const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());
842860 if (mem.eql(u8, err_name, "_"))
843 break :blk &err_scope.base;
861 break :blk &then_scope.base;
844862
845 const unwrapped_err_ptr = try addZIRUnOp(mod, &err_scope.base, src, .unwrap_err_code, err_union_ptr);
863 const unwrapped_err_ptr = try addZIRUnOp(mod, &then_scope.base, src, .unwrap_err_code, operand_ptr);
846864 err_val_scope = .{
847 .parent = &err_scope.base,
848 .gen_zir = &err_scope,
865 .parent = &then_scope.base,
866 .gen_zir = &then_scope,
849867 .name = err_name,
850 .inst = try addZIRUnOp(mod, &err_scope.base, src, .deref, unwrapped_err_ptr),
868 .inst = try addZIRUnOp(mod, &then_scope.base, src, .deref, unwrapped_err_ptr),
851869 };
852870 break :blk &err_val_scope.base;
853871 };
854872
855 _ = try addZIRInst(mod, &err_scope.base, src, zir.Inst.Break, .{
873 _ = try addZIRInst(mod, &then_scope.base, src, zir.Inst.Break, .{
856874 .block = block,
857 .operand = try expr(mod, err_sub_scope, branch_rl, node.rhs),
875 .operand = try expr(mod, then_sub_scope, branch_rl, rhs),
858876 }, .{});
859877
860 var not_err_scope: Scope.GenZIR = .{
878 var else_scope: Scope.GenZIR = .{
861879 .parent = scope,
862880 .decl = block_scope.decl,
863881 .arena = block_scope.arena,
864882 .instructions = .{},
865883 };
866 defer not_err_scope.instructions.deinit(mod.gpa);
884 defer else_scope.instructions.deinit(mod.gpa);
867885
868 const unwrapped_payload = try addZIRUnOp(mod, &not_err_scope.base, src, .unwrap_err_unsafe, err_union_ptr);
869 _ = try addZIRInst(mod, &not_err_scope.base, src, zir.Inst.Break, .{
886 const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand_ptr);
887 _ = try addZIRInst(mod, &else_scope.base, src, zir.Inst.Break, .{
870888 .block = block,
871889 .operand = unwrapped_payload,
872890 }, .{});
873891
874 condbr.positionals.then_body = .{ .instructions = try err_scope.arena.dupe(*zir.Inst, err_scope.instructions.items) };
875 condbr.positionals.else_body = .{ .instructions = try not_err_scope.arena.dupe(*zir.Inst, not_err_scope.instructions.items) };
876 return rlWrap(mod, scope, rl, &block.base);
892 condbr.positionals.then_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items) };
893 condbr.positionals.else_body = .{ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items) };
894 return rlWrapPtr(mod, scope, rl, &block.base);
877895}
878896
879897/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
......@@ -933,6 +951,36 @@ fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Array
933951 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{}));
934952}
935953
954fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.Slice) InnerError!*zir.Inst {
955 const tree = scope.tree();
956 const src = tree.token_locs[node.rtoken].start;
957
958 const usize_type = try addZIRInstConst(mod, scope, src, .{
959 .ty = Type.initTag(.type),
960 .val = Value.initTag(.usize_type),
961 });
962
963 const array_ptr = try expr(mod, scope, .ref, node.lhs);
964 const start = try expr(mod, scope, .{ .ty = usize_type }, node.start);
965
966 if (node.end == null and node.sentinel == null) {
967 return try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start);
968 }
969
970 const end = if (node.end) |end| try expr(mod, scope, .{ .ty = usize_type }, end) else null;
971 // we could get the child type here, but it is easier to just do it in semantic analysis.
972 const sentinel = if (node.sentinel) |sentinel| try expr(mod, scope, .none, sentinel) else null;
973
974 return try addZIRInst(
975 mod,
976 scope,
977 src,
978 zir.Inst.Slice,
979 .{ .array_ptr = array_ptr, .start = start },
980 .{ .end = end, .sentinel = sentinel },
981 );
982}
983
936984fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
937985 const tree = scope.tree();
938986 const src = tree.token_locs[node.rtoken].start;
src-self-hosted/codegen.zig+225-116
......@@ -59,14 +59,21 @@ pub const GenerateSymbolError = error{
5959 AnalysisFail,
6060};
6161
62pub const DebugInfoOutput = union(enum) {
63 dwarf: struct {
64 dbg_line: *std.ArrayList(u8),
65 dbg_info: *std.ArrayList(u8),
66 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
67 },
68 none,
69};
70
6271pub fn generateSymbol(
6372 bin_file: *link.File,
6473 src: usize,
6574 typed_value: TypedValue,
6675 code: *std.ArrayList(u8),
67 dbg_line: *std.ArrayList(u8),
68 dbg_info: *std.ArrayList(u8),
69 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
76 debug_output: DebugInfoOutput,
7077) GenerateSymbolError!Result {
7178 const tracy = trace(@src());
7279 defer tracy.end();
......@@ -76,70 +83,70 @@ pub fn generateSymbol(
7683 switch (bin_file.options.target.cpu.arch) {
7784 .wasm32 => unreachable, // has its own code path
7885 .wasm64 => unreachable, // has its own code path
79 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
80 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
81 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
82 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
83 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
84 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
85 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
86 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
87 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
88 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
89 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
90 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
91 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
92 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
93 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
94 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
95 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
96 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
97 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
98 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
99 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
100 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
101 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
102 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
103 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
104 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
105 .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
106 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
107 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
108 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
109 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
110 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
111 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
112 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
113 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
114 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
115 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
116 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
117 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
118 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
119 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
120 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
121 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
122 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
123 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
124 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
125 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
126 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
127 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
128 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
86 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, debug_output),
87 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
88 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, debug_output),
89 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, debug_output),
90 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, debug_output),
91 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, debug_output),
92 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, debug_output),
93 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, debug_output),
94 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
95 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, debug_output),
96 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, debug_output),
97 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, debug_output),
98 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, debug_output),
99 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, debug_output),
100 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, debug_output),
101 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, debug_output),
102 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, debug_output),
103 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, debug_output),
104 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, debug_output),
105 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, debug_output),
106 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, debug_output),
107 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, debug_output),
108 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, debug_output),
109 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, debug_output),
110 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, debug_output),
111 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, debug_output),
112 .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, debug_output),
113 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, debug_output),
114 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, debug_output),
115 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, debug_output),
116 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
117 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, debug_output),
118 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, debug_output),
119 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, debug_output),
120 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, debug_output),
121 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, debug_output),
122 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, debug_output),
123 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, debug_output),
124 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, debug_output),
125 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, debug_output),
126 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, debug_output),
127 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, debug_output),
128 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, debug_output),
129 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, debug_output),
130 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, debug_output),
131 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, debug_output),
132 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, debug_output),
133 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, debug_output),
134 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, debug_output),
135 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, debug_output),
129136 else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
130137 }
131138 },
132139 .Array => {
133140 // TODO populate .debug_info for the array
134141 if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {
135 if (typed_value.ty.arraySentinel()) |sentinel| {
142 if (typed_value.ty.sentinel()) |sentinel| {
136143 try code.ensureCapacity(code.items.len + payload.data.len + 1);
137144 code.appendSliceAssumeCapacity(payload.data);
138145 const prev_len = code.items.len;
139146 switch (try generateSymbol(bin_file, src, .{
140147 .ty = typed_value.ty.elemType(),
141148 .val = sentinel,
142 }, code, dbg_line, dbg_info, dbg_info_type_relocs)) {
149 }, code, debug_output)) {
143150 .appended => return Result{ .appended = {} },
144151 .externally_managed => |slice| {
145152 code.appendSliceAssumeCapacity(slice);
......@@ -239,9 +246,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
239246 target: *const std.Target,
240247 mod_fn: *const Module.Fn,
241248 code: *std.ArrayList(u8),
242 dbg_line: *std.ArrayList(u8),
243 dbg_info: *std.ArrayList(u8),
244 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
249 debug_output: DebugInfoOutput,
245250 err_msg: ?*ErrorMsg,
246251 args: []MCValue,
247252 ret_mcv: MCValue,
......@@ -419,9 +424,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
419424 src: usize,
420425 typed_value: TypedValue,
421426 code: *std.ArrayList(u8),
422 dbg_line: *std.ArrayList(u8),
423 dbg_info: *std.ArrayList(u8),
424 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
427 debug_output: DebugInfoOutput,
425428 ) GenerateSymbolError!Result {
426429 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
427430
......@@ -436,8 +439,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
436439 try branch_stack.append(.{});
437440
438441 const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: {
439 if (module_fn.owner_decl.scope.cast(Module.Scope.File)) |scope_file| {
440 const tree = scope_file.contents.tree;
442 if (module_fn.owner_decl.scope.cast(Module.Scope.Container)) |container_scope| {
443 const tree = container_scope.file_scope.contents.tree;
441444 const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?;
442445 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
443446 const lbrace_src = tree.token_locs[block.lbrace].start;
......@@ -457,9 +460,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
457460 .bin_file = bin_file,
458461 .mod_fn = module_fn,
459462 .code = code,
460 .dbg_line = dbg_line,
461 .dbg_info = dbg_info,
462 .dbg_info_type_relocs = dbg_info_type_relocs,
463 .debug_output = debug_output,
463464 .err_msg = null,
464465 .args = undefined, // populated after `resolveCallingConventionValues`
465466 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
......@@ -598,35 +599,50 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
598599 }
599600
600601 fn dbgSetPrologueEnd(self: *Self) InnerError!void {
601 try self.dbg_line.append(DW.LNS_set_prologue_end);
602 try self.dbgAdvancePCAndLine(self.prev_di_src);
602 switch (self.debug_output) {
603 .dwarf => |dbg_out| {
604 try dbg_out.dbg_line.append(DW.LNS_set_prologue_end);
605 try self.dbgAdvancePCAndLine(self.prev_di_src);
606 },
607 .none => {},
608 }
603609 }
604610
605611 fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
606 try self.dbg_line.append(DW.LNS_set_epilogue_begin);
607 try self.dbgAdvancePCAndLine(self.prev_di_src);
612 switch (self.debug_output) {
613 .dwarf => |dbg_out| {
614 try dbg_out.dbg_line.append(DW.LNS_set_epilogue_begin);
615 try self.dbgAdvancePCAndLine(self.prev_di_src);
616 },
617 .none => {},
618 }
608619 }
609620
610621 fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void {
611 // TODO Look into improving the performance here by adding a token-index-to-line
612 // lookup table, and changing ir.Inst from storing byte offset to token. Currently
613 // this involves scanning over the source code for newlines
614 // (but only from the previous byte offset to the new one).
615 const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src);
616 const delta_pc = self.code.items.len - self.prev_di_pc;
617622 self.prev_di_src = src;
618623 self.prev_di_pc = self.code.items.len;
619 // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit
620 // single-byte opcodes that add different numbers to both the PC and the line number
621 // at the same time.
622 try self.dbg_line.ensureCapacity(self.dbg_line.items.len + 11);
623 self.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc);
624 leb128.writeULEB128(self.dbg_line.writer(), delta_pc) catch unreachable;
625 if (delta_line != 0) {
626 self.dbg_line.appendAssumeCapacity(DW.LNS_advance_line);
627 leb128.writeILEB128(self.dbg_line.writer(), delta_line) catch unreachable;
624 switch (self.debug_output) {
625 .dwarf => |dbg_out| {
626 // TODO Look into improving the performance here by adding a token-index-to-line
627 // lookup table, and changing ir.Inst from storing byte offset to token. Currently
628 // this involves scanning over the source code for newlines
629 // (but only from the previous byte offset to the new one).
630 const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src);
631 const delta_pc = self.code.items.len - self.prev_di_pc;
632 // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit
633 // single-byte opcodes that add different numbers to both the PC and the line number
634 // at the same time.
635 try dbg_out.dbg_line.ensureCapacity(dbg_out.dbg_line.items.len + 11);
636 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc);
637 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
638 if (delta_line != 0) {
639 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_line);
640 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
641 }
642 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_copy);
643 },
644 .none => {},
628645 }
629 self.dbg_line.appendAssumeCapacity(DW.LNS_copy);
630646 }
631647
632648 /// Asserts there is already capacity to insert into top branch inst_table.
......@@ -654,18 +670,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
654670 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
655671 /// after codegen for this symbol is done.
656672 fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
657 assert(ty.hasCodeGenBits());
658 const index = self.dbg_info.items.len;
659 try self.dbg_info.resize(index + 4); // DW.AT_type, DW.FORM_ref4
660
661 const gop = try self.dbg_info_type_relocs.getOrPut(self.gpa, ty);
662 if (!gop.found_existing) {
663 gop.entry.value = .{
664 .off = undefined,
665 .relocs = .{},
666 };
673 switch (self.debug_output) {
674 .dwarf => |dbg_out| {
675 assert(ty.hasCodeGenBits());
676 const index = dbg_out.dbg_info.items.len;
677 try dbg_out.dbg_info.resize(index + 4); // DW.AT_type, DW.FORM_ref4
678
679 const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty);
680 if (!gop.found_existing) {
681 gop.entry.value = .{
682 .off = undefined,
683 .relocs = .{},
684 };
685 }
686 try gop.entry.value.relocs.append(self.gpa, @intCast(u32, index));
687 },
688 .none => {},
667689 }
668 try gop.entry.value.relocs.append(self.gpa, @intCast(u32, index));
669690 }
670691
671692 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
......@@ -1258,14 +1279,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12581279 self.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), &inst.base);
12591280 self.markRegUsed(reg);
12601281
1261 try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len);
1262 self.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
1263 self.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT_location, DW.FORM_exprloc
1264 1, // ULEB128 dwarf expression length
1265 reg.dwarfLocOp(),
1266 });
1267 try self.addDbgInfoTypeReloc(inst.base.ty); // DW.AT_type, DW.FORM_ref4
1268 self.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
1282 switch (self.debug_output) {
1283 .dwarf => |dbg_out| {
1284 try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 8 + name_with_null.len);
1285 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
1286 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT_location, DW.FORM_exprloc
1287 1, // ULEB128 dwarf expression length
1288 reg.dwarfLocOp(),
1289 });
1290 try self.addDbgInfoTypeReloc(inst.base.ty); // DW.AT_type, DW.FORM_ref4
1291 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
1292 },
1293 .none => {},
1294 }
12691295 },
12701296 else => {},
12711297 }
......@@ -1302,7 +1328,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13021328
13031329 // Due to incremental compilation, how function calls are generated depends
13041330 // on linking.
1305 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
1331 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
13061332 switch (arch) {
13071333 .x86_64 => {
13081334 for (info.args) |mc_arg, arg_i| {
......@@ -1341,10 +1367,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13411367 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
13421368 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
13431369 const func = func_val.func;
1344 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1370
13451371 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
13461372 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1347 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1373 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1374 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1375 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1376 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
1377 @intCast(u32, coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes)
1378 else
1379 unreachable;
1380
13481381 // ff 14 25 xx xx xx xx call [addr]
13491382 try self.code.ensureCapacity(self.code.items.len + 7);
13501383 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
......@@ -1362,10 +1395,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13621395 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
13631396 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
13641397 const func = func_val.func;
1365 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1398
13661399 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
13671400 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1368 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1401 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1402 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1403 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1404 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
1405 coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
1406 else
1407 unreachable;
13691408
13701409 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
13711410 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
......@@ -1383,8 +1422,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13831422 }
13841423 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
13851424 const func = func_val.func;
1386 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1387 const got_addr = @intCast(u16, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * 2);
1425 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1426 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1427 break :blk @intCast(u16, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * 2);
1428 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
1429 @intCast(u16, coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * 2)
1430 else
1431 unreachable;
1432
13881433 const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType();
13891434 // First, push the return address, then jump; if noreturn, don't bother with the first step
13901435 // TODO: implement packed struct -> u16 at comptime and move the bitcast here
......@@ -1420,10 +1465,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14201465 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
14211466 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
14221467 const func = func_val.func;
1423 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
14241468 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
14251469 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1426 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1470 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1471 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1472 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1473 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
1474 coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
1475 else
1476 unreachable;
14271477
14281478 // TODO only works with leaf functions
14291479 // at the moment, which works fine for
......@@ -1443,7 +1493,57 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14431493 }
14441494 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
14451495 switch (arch) {
1446 .x86_64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for x86_64 arch", .{}),
1496 .x86_64 => {
1497 for (info.args) |mc_arg, arg_i| {
1498 const arg = inst.args[arg_i];
1499 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
1500 // Here we do not use setRegOrMem even though the logic is similar, because
1501 // the function call will move the stack pointer, so the offsets are different.
1502 switch (mc_arg) {
1503 .none => continue,
1504 .register => |reg| {
1505 try self.genSetReg(arg.src, reg, arg_mcv);
1506 // TODO interact with the register allocator to mark the instruction as moved.
1507 },
1508 .stack_offset => {
1509 // Here we need to emit instructions like this:
1510 // mov qword ptr [rsp + stack_offset], x
1511 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
1512 },
1513 .ptr_stack_offset => {
1514 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1515 },
1516 .ptr_embedded_in_code => {
1517 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1518 },
1519 .undef => unreachable,
1520 .immediate => unreachable,
1521 .unreach => unreachable,
1522 .dead => unreachable,
1523 .embedded_in_code => unreachable,
1524 .memory => unreachable,
1525 .compare_flags_signed => unreachable,
1526 .compare_flags_unsigned => unreachable,
1527 }
1528 }
1529
1530 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1531 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1532 const func = func_val.func;
1533 const got = &macho_file.sections.items[macho_file.got_section_index.?];
1534 const ptr_bytes = 8;
1535 const got_addr = @intCast(u32, got.addr + func.owner_decl.link.macho.offset_table_index.? * ptr_bytes);
1536 // ff 14 25 xx xx xx xx call [addr]
1537 try self.code.ensureCapacity(self.code.items.len + 7);
1538 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
1539 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
1540 } else {
1541 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1542 }
1543 } else {
1544 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1545 }
1546 },
14471547 .aarch64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for aarch64 arch", .{}),
14481548 else => unreachable,
14491549 }
......@@ -1933,7 +2033,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19332033
19342034 if (mem.eql(u8, inst.asm_source, "syscall")) {
19352035 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
1936 } else {
2036 } else if (inst.asm_source.len != 0) {
19372037 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
19382038 }
19392039
......@@ -2486,6 +2586,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24862586 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
24872587 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
24882588 return MCValue{ .memory = got_addr };
2589 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
2590 const decl = payload.decl;
2591 const got = &macho_file.sections.items[macho_file.got_section_index.?];
2592 const got_addr = got.addr + decl.link.macho.offset_table_index.? * ptr_bytes;
2593 return MCValue{ .memory = got_addr };
2594 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
2595 const decl = payload.decl;
2596 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
2597 return MCValue{ .memory = got_addr };
24892598 } else {
24902599 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
24912600 }
src-self-hosted/codegen/c.zig+1-1
......@@ -85,7 +85,7 @@ fn genArray(file: *C, decl: *Decl) !void {
8585 const name = try map(file.base.allocator, mem.span(decl.name));
8686 defer file.base.allocator.free(name);
8787 if (tv.val.cast(Value.Payload.Bytes)) |payload|
88 if (tv.ty.arraySentinel()) |sentinel|
88 if (tv.ty.sentinel()) |sentinel|
8989 if (sentinel.toUnsignedInt() == 0)
9090 try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
9191 else
src-self-hosted/link.zig+20-2
......@@ -34,6 +34,7 @@ pub const File = struct {
3434
3535 pub const LinkBlock = union {
3636 elf: Elf.TextBlock,
37 coff: Coff.TextBlock,
3738 macho: MachO.TextBlock,
3839 c: void,
3940 wasm: void,
......@@ -41,6 +42,7 @@ pub const File = struct {
4142
4243 pub const LinkFn = union {
4344 elf: Elf.SrcFn,
45 coff: Coff.SrcFn,
4446 macho: MachO.SrcFn,
4547 c: void,
4648 wasm: ?Wasm.FnData,
......@@ -66,7 +68,7 @@ pub const File = struct {
6668 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
6769 switch (options.object_format) {
6870 .unknown => unreachable,
69 .coff => return error.TODOImplementCoff,
71 .coff, .pe => return Coff.openPath(allocator, dir, sub_path, options),
7072 .elf => return Elf.openPath(allocator, dir, sub_path, options),
7173 .macho => return MachO.openPath(allocator, dir, sub_path, options),
7274 .wasm => return Wasm.openPath(allocator, dir, sub_path, options),
......@@ -85,7 +87,7 @@ pub const File = struct {
8587
8688 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
8789 switch (base.tag) {
88 .elf, .macho => {
90 .coff, .elf, .macho => {
8991 if (base.file != null) return;
9092 base.file = try dir.createFile(sub_path, .{
9193 .truncate = false,
......@@ -112,6 +114,7 @@ pub const File = struct {
112114 /// after allocateDeclIndexes for any given Decl.
113115 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
114116 switch (base.tag) {
117 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
115118 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
116119 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
117120 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
......@@ -121,6 +124,7 @@ pub const File = struct {
121124
122125 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
123126 switch (base.tag) {
127 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
124128 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
125129 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
126130 .c, .wasm => {},
......@@ -131,6 +135,7 @@ pub const File = struct {
131135 /// any given Decl.
132136 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
133137 switch (base.tag) {
138 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
134139 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
135140 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
136141 .c, .wasm => {},
......@@ -140,6 +145,7 @@ pub const File = struct {
140145 pub fn deinit(base: *File) void {
141146 if (base.file) |f| f.close();
142147 switch (base.tag) {
148 .coff => @fieldParentPtr(Coff, "base", base).deinit(),
143149 .elf => @fieldParentPtr(Elf, "base", base).deinit(),
144150 .macho => @fieldParentPtr(MachO, "base", base).deinit(),
145151 .c => @fieldParentPtr(C, "base", base).deinit(),
......@@ -149,6 +155,11 @@ pub const File = struct {
149155
150156 pub fn destroy(base: *File) void {
151157 switch (base.tag) {
158 .coff => {
159 const parent = @fieldParentPtr(Coff, "base", base);
160 parent.deinit();
161 base.allocator.destroy(parent);
162 },
152163 .elf => {
153164 const parent = @fieldParentPtr(Elf, "base", base);
154165 parent.deinit();
......@@ -177,6 +188,7 @@ pub const File = struct {
177188 defer tracy.end();
178189
179190 try switch (base.tag) {
191 .coff => @fieldParentPtr(Coff, "base", base).flush(module),
180192 .elf => @fieldParentPtr(Elf, "base", base).flush(module),
181193 .macho => @fieldParentPtr(MachO, "base", base).flush(module),
182194 .c => @fieldParentPtr(C, "base", base).flush(module),
......@@ -186,6 +198,7 @@ pub const File = struct {
186198
187199 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
188200 switch (base.tag) {
201 .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
189202 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
190203 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
191204 .c => unreachable,
......@@ -195,6 +208,7 @@ pub const File = struct {
195208
196209 pub fn errorFlags(base: *File) ErrorFlags {
197210 return switch (base.tag) {
211 .coff => @fieldParentPtr(Coff, "base", base).error_flags,
198212 .elf => @fieldParentPtr(Elf, "base", base).error_flags,
199213 .macho => @fieldParentPtr(MachO, "base", base).error_flags,
200214 .c => return .{ .no_entry_point_found = false },
......@@ -211,6 +225,7 @@ pub const File = struct {
211225 exports: []const *Module.Export,
212226 ) !void {
213227 switch (base.tag) {
228 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
214229 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
215230 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
216231 .c => return {},
......@@ -220,6 +235,7 @@ pub const File = struct {
220235
221236 pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
222237 switch (base.tag) {
238 .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
223239 .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
224240 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
225241 .c => unreachable,
......@@ -228,6 +244,7 @@ pub const File = struct {
228244 }
229245
230246 pub const Tag = enum {
247 coff,
231248 elf,
232249 macho,
233250 c,
......@@ -239,6 +256,7 @@ pub const File = struct {
239256 };
240257
241258 pub const C = @import("link/C.zig");
259 pub const Coff = @import("link/Coff.zig");
242260 pub const Elf = @import("link/Elf.zig");
243261 pub const MachO = @import("link/MachO.zig");
244262 pub const Wasm = @import("link/Wasm.zig");
src-self-hosted/link/Coff.zig created+792
......@@ -0,0 +1,792 @@
1const Coff = @This();
2
3const std = @import("std");
4const log = std.log.scoped(.link);
5const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;
7const fs = std.fs;
8
9const trace = @import("../tracy.zig").trace;
10const Module = @import("../Module.zig");
11const codegen = @import("../codegen.zig");
12const link = @import("../link.zig");
13
14const allocation_padding = 4 / 3;
15const minimum_text_block_size = 64 * allocation_padding;
16
17const section_alignment = 4096;
18const file_alignment = 512;
19const image_base = 0x400_000;
20const section_table_size = 2 * 40;
21comptime {
22 std.debug.assert(std.mem.isAligned(image_base, section_alignment));
23}
24
25pub const base_tag: link.File.Tag = .coff;
26
27const msdos_stub = @embedFile("msdos-stub.bin");
28
29base: link.File,
30ptr_width: enum { p32, p64 },
31error_flags: link.File.ErrorFlags = .{},
32
33text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
34last_text_block: ?*TextBlock = null,
35
36/// Section table file pointer.
37section_table_offset: u32 = 0,
38/// Section data file pointer.
39section_data_offset: u32 = 0,
40/// Optiona header file pointer.
41optional_header_offset: u32 = 0,
42
43/// Absolute virtual address of the offset table when the executable is loaded in memory.
44offset_table_virtual_address: u32 = 0,
45/// Current size of the offset table on disk, must be a multiple of `file_alignment`
46offset_table_size: u32 = 0,
47/// Contains absolute virtual addresses
48offset_table: std.ArrayListUnmanaged(u64) = .{},
49/// Free list of offset table indices
50offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
51
52/// Virtual address of the entry point procedure relative to `image_base`
53entry_addr: ?u32 = null,
54
55/// Absolute virtual address of the text section when the executable is loaded in memory.
56text_section_virtual_address: u32 = 0,
57/// Current size of the `.text` section on disk, must be a multiple of `file_alignment`
58text_section_size: u32 = 0,
59
60offset_table_size_dirty: bool = false,
61text_section_size_dirty: bool = false,
62/// This flag is set when the virtual size of the whole image file when loaded in memory has changed
63/// and needs to be updated in the optional header.
64size_of_image_dirty: bool = false,
65
66pub const TextBlock = struct {
67 /// Offset of the code relative to the start of the text section
68 text_offset: u32,
69 /// Used size of the text block
70 size: u32,
71 /// This field is undefined for symbols with size = 0.
72 offset_table_index: u32,
73 /// Points to the previous and next neighbors, based on the `text_offset`.
74 /// This can be used to find, for example, the capacity of this `TextBlock`.
75 prev: ?*TextBlock,
76 next: ?*TextBlock,
77
78 pub const empty = TextBlock{
79 .text_offset = 0,
80 .size = 0,
81 .offset_table_index = undefined,
82 .prev = null,
83 .next = null,
84 };
85
86 /// Returns how much room there is to grow in virtual address space.
87 fn capacity(self: TextBlock) u64 {
88 if (self.next) |next| {
89 return next.text_offset - self.text_offset;
90 }
91 // This is the last block, the capacity is only limited by the address space.
92 return std.math.maxInt(u32) - self.text_offset;
93 }
94
95 fn freeListEligible(self: TextBlock) bool {
96 // No need to keep a free list node for the last block.
97 const next = self.next orelse return false;
98 const cap = next.text_offset - self.text_offset;
99 const ideal_cap = self.size * allocation_padding;
100 if (cap <= ideal_cap) return false;
101 const surplus = cap - ideal_cap;
102 return surplus >= minimum_text_block_size;
103 }
104
105 /// Absolute virtual address of the text block when the file is loaded in memory.
106 fn getVAddr(self: TextBlock, coff: Coff) u32 {
107 return coff.text_section_virtual_address + self.text_offset;
108 }
109};
110
111pub const SrcFn = void;
112
113pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {
114 assert(options.object_format == .coff);
115
116 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
117 errdefer file.close();
118
119 var coff_file = try allocator.create(Coff);
120 errdefer allocator.destroy(coff_file);
121
122 coff_file.* = openFile(allocator, file, options) catch |err| switch (err) {
123 error.IncrFailed => try createFile(allocator, file, options),
124 else => |e| return e,
125 };
126
127 return &coff_file.base;
128}
129
130/// Returns error.IncrFailed if incremental update could not be performed.
131fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
132 switch (options.output_mode) {
133 .Exe => {},
134 .Obj => return error.IncrFailed,
135 .Lib => return error.IncrFailed,
136 }
137 var self: Coff = .{
138 .base = .{
139 .file = file,
140 .tag = .coff,
141 .options = options,
142 .allocator = allocator,
143 },
144 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
145 32 => .p32,
146 64 => .p64,
147 else => return error.UnsupportedELFArchitecture,
148 },
149 };
150 errdefer self.deinit();
151
152 // TODO implement reading the PE/COFF file
153 return error.IncrFailed;
154}
155
156/// Truncates the existing file contents and overwrites the contents.
157/// Returns an error if `file` is not already open with +read +write +seek abilities.
158fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
159 // TODO Write object specific relocations, COFF symbol table, then enable object file output.
160 switch (options.output_mode) {
161 .Exe => {},
162 .Obj => return error.TODOImplementWritingObjFiles,
163 .Lib => return error.TODOImplementWritingLibFiles,
164 }
165 var self: Coff = .{
166 .base = .{
167 .tag = .coff,
168 .options = options,
169 .allocator = allocator,
170 .file = file,
171 },
172 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
173 32 => .p32,
174 64 => .p64,
175 else => return error.UnsupportedCOFFArchitecture,
176 },
177 };
178 errdefer self.deinit();
179
180 var coff_file_header_offset: u32 = 0;
181 if (options.output_mode == .Exe) {
182 // Write the MS-DOS stub and the PE signature
183 try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0);
184 coff_file_header_offset = msdos_stub.len + 4;
185 }
186
187 // COFF file header
188 const data_directory_count = 0;
189 var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined;
190 var index: usize = 0;
191
192 const machine = self.base.options.target.cpu.arch.toCoffMachine();
193 if (machine == .Unknown) {
194 return error.UnsupportedCOFFArchitecture;
195 }
196 std.mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine));
197 index += 2;
198
199 // Number of sections (we only use .got, .text)
200 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 2);
201 index += 2;
202 // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32)
203 std.mem.set(u8, hdr_data[index..][0..12], 0);
204 index += 12;
205
206 const optional_header_size = switch (options.output_mode) {
207 .Exe => data_directory_count * 8 + switch (self.ptr_width) {
208 .p32 => @as(u16, 96),
209 .p64 => 112,
210 },
211 else => 0,
212 };
213
214 const section_table_offset = coff_file_header_offset + 20 + optional_header_size;
215 const default_offset_table_size = file_alignment;
216 const default_size_of_code = 0;
217
218 self.section_data_offset = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
219 const section_data_relative_virtual_address = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
220 self.offset_table_virtual_address = image_base + section_data_relative_virtual_address;
221 self.offset_table_size = default_offset_table_size;
222 self.section_table_offset = section_table_offset;
223 self.text_section_virtual_address = image_base + section_data_relative_virtual_address + section_alignment;
224 self.text_section_size = default_size_of_code;
225
226 // Size of file when loaded in memory
227 const size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment);
228
229 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
230 index += 2;
231
232 // Characteristics
233 var characteristics: u16 = std.coff.IMAGE_FILE_DEBUG_STRIPPED | std.coff.IMAGE_FILE_RELOCS_STRIPPED; // TODO Remove debug info stripped flag when necessary
234 if (options.output_mode == .Exe) {
235 characteristics |= std.coff.IMAGE_FILE_EXECUTABLE_IMAGE;
236 }
237 switch (self.ptr_width) {
238 .p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE,
239 .p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE,
240 }
241 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics);
242 index += 2;
243
244 assert(index == 20);
245 try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset);
246
247 if (options.output_mode == .Exe) {
248 self.optional_header_offset = coff_file_header_offset + 20;
249 // Optional header
250 index = 0;
251 std.mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) {
252 .p32 => @as(u16, 0x10b),
253 .p64 => 0x20b,
254 });
255 index += 2;
256
257 // Linker version (u8 + u8)
258 std.mem.set(u8, hdr_data[index..][0..2], 0);
259 index += 2;
260
261 // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32)
262 std.mem.set(u8, hdr_data[index..][0..20], 0);
263 index += 20;
264
265 if (self.ptr_width == .p32) {
266 // Base of data relative to the image base (UNUSED)
267 std.mem.set(u8, hdr_data[index..][0..4], 0);
268 index += 4;
269
270 // Image base address
271 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base);
272 index += 4;
273 } else {
274 // Image base address
275 std.mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base);
276 index += 8;
277 }
278
279 // Section alignment
280 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment);
281 index += 4;
282 // File alignment
283 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment);
284 index += 4;
285 // Required OS version, 6.0 is vista
286 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
287 index += 2;
288 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
289 index += 2;
290 // Image version
291 std.mem.set(u8, hdr_data[index..][0..4], 0);
292 index += 4;
293 // Required subsystem version, same as OS version
294 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
295 index += 2;
296 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
297 index += 2;
298 // Reserved zeroes (u32)
299 std.mem.set(u8, hdr_data[index..][0..4], 0);
300 index += 4;
301 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image);
302 index += 4;
303 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
304 index += 4;
305 // CheckSum (u32)
306 std.mem.set(u8, hdr_data[index..][0..4], 0);
307 index += 4;
308 // Subsystem, TODO: Let users specify the subsystem, always CUI for now
309 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 3);
310 index += 2;
311 // DLL characteristics
312 std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0);
313 index += 2;
314
315 switch (self.ptr_width) {
316 .p32 => {
317 // Size of stack reserve + commit
318 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000);
319 index += 4;
320 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
321 index += 4;
322 // Size of heap reserve + commit
323 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000);
324 index += 4;
325 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
326 index += 4;
327 },
328 .p64 => {
329 // Size of stack reserve + commit
330 std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000);
331 index += 8;
332 std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
333 index += 8;
334 // Size of heap reserve + commit
335 std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000);
336 index += 8;
337 std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
338 index += 8;
339 },
340 }
341
342 // Reserved zeroes
343 std.mem.set(u8, hdr_data[index..][0..4], 0);
344 index += 4;
345
346 // Number of data directories
347 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count);
348 index += 4;
349 // Initialize data directories to zero
350 std.mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0);
351 index += data_directory_count * 8;
352
353 assert(index == optional_header_size);
354 }
355
356 // Write section table.
357 // First, the .got section
358 hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*;
359 index += 8;
360 if (options.output_mode == .Exe) {
361 // Virtual size (u32)
362 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
363 index += 4;
364 // Virtual address (u32)
365 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base);
366 index += 4;
367 } else {
368 std.mem.set(u8, hdr_data[index..][0..8], 0);
369 index += 8;
370 }
371 // Size of raw data (u32)
372 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
373 index += 4;
374 // File pointer to the start of the section
375 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
376 index += 4;
377 // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
378 std.mem.set(u8, hdr_data[index..][0..12], 0);
379 index += 12;
380 // Section flags
381 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ);
382 index += 4;
383 // Then, the .text section
384 hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
385 index += 8;
386 if (options.output_mode == .Exe) {
387 // Virtual size (u32)
388 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
389 index += 4;
390 // Virtual address (u32)
391 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base);
392 index += 4;
393 } else {
394 std.mem.set(u8, hdr_data[index..][0..8], 0);
395 index += 8;
396 }
397 // Size of raw data (u32)
398 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
399 index += 4;
400 // File pointer to the start of the section
401 std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size);
402 index += 4;
403 // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
404 std.mem.set(u8, hdr_data[index..][0..12], 0);
405 index += 12;
406 // Section flags
407 std.mem.writeIntLittle(
408 u32,
409 hdr_data[index..][0..4],
410 std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE,
411 );
412 index += 4;
413
414 assert(index == optional_header_size + section_table_size);
415 try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset);
416 try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code);
417
418 return self;
419}
420
421pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
422 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
423
424 if (self.offset_table_free_list.popOrNull()) |i| {
425 decl.link.coff.offset_table_index = i;
426 } else {
427 decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len);
428 _ = self.offset_table.addOneAssumeCapacity();
429
430 const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
431 if (self.offset_table.items.len > self.offset_table_size / entry_size) {
432 self.offset_table_size_dirty = true;
433 }
434 }
435
436 self.offset_table.items[decl.link.coff.offset_table_index] = 0;
437}
438
439fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
440 const new_block_min_capacity = new_block_size * allocation_padding;
441
442 // We use these to indicate our intention to update metadata, placing the new block,
443 // and possibly removing a free list node.
444 // It would be simpler to do it inside the for loop below, but that would cause a
445 // problem if an error was returned later in the function. So this action
446 // is actually carried out at the end of the function, when errors are no longer possible.
447 var block_placement: ?*TextBlock = null;
448 var free_list_removal: ?usize = null;
449
450 const vaddr = blk: {
451 var i: usize = 0;
452 while (i < self.text_block_free_list.items.len) {
453 const free_block = self.text_block_free_list.items[i];
454
455 const next_block_text_offset = free_block.text_offset + free_block.capacity();
456 const new_block_text_offset = std.mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address;
457 if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) {
458 block_placement = free_block;
459
460 const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity;
461 if (remaining_capacity < minimum_text_block_size) {
462 free_list_removal = i;
463 }
464
465 break :blk new_block_text_offset + self.text_section_virtual_address;
466 } else {
467 if (!free_block.freeListEligible()) {
468 _ = self.text_block_free_list.swapRemove(i);
469 } else {
470 i += 1;
471 }
472 continue;
473 }
474 } else if (self.last_text_block) |last| {
475 const new_block_vaddr = std.mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment);
476 block_placement = last;
477 break :blk new_block_vaddr;
478 } else {
479 break :blk self.text_section_virtual_address;
480 }
481 };
482
483 const expand_text_section = block_placement == null or block_placement.?.next == null;
484 if (expand_text_section) {
485 const needed_size = @intCast(u32, std.mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment));
486 if (needed_size > self.text_section_size) {
487 const current_text_section_virtual_size = std.mem.alignForwardGeneric(u32, self.text_section_size, section_alignment);
488 const new_text_section_virtual_size = std.mem.alignForwardGeneric(u32, needed_size, section_alignment);
489 if (current_text_section_virtual_size != new_text_section_virtual_size) {
490 self.size_of_image_dirty = true;
491 // Write new virtual size
492 var buf: [4]u8 = undefined;
493 std.mem.writeIntLittle(u32, &buf, new_text_section_virtual_size);
494 try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8);
495 }
496
497 self.text_section_size = needed_size;
498 self.text_section_size_dirty = true;
499 }
500 self.last_text_block = text_block;
501 }
502 text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address);
503 text_block.size = @intCast(u32, new_block_size);
504
505 // This function can also reallocate a text block.
506 // In this case we need to "unplug" it from its previous location before
507 // plugging it in to its new location.
508 if (text_block.prev) |prev| {
509 prev.next = text_block.next;
510 }
511 if (text_block.next) |next| {
512 next.prev = text_block.prev;
513 }
514
515 if (block_placement) |big_block| {
516 text_block.prev = big_block;
517 text_block.next = big_block.next;
518 big_block.next = text_block;
519 } else {
520 text_block.prev = null;
521 text_block.next = null;
522 }
523 if (free_list_removal) |i| {
524 _ = self.text_block_free_list.swapRemove(i);
525 }
526 return vaddr;
527}
528
529fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
530 const block_vaddr = text_block.getVAddr(self.*);
531 const align_ok = std.mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr;
532 const need_realloc = !align_ok or new_block_size > text_block.capacity();
533 if (!need_realloc) return @as(u64, block_vaddr);
534 return self.allocateTextBlock(text_block, new_block_size, alignment);
535}
536
537fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void {
538 text_block.size = @intCast(u32, new_block_size);
539 if (text_block.capacity() - text_block.size >= minimum_text_block_size) {
540 self.text_block_free_list.append(self.base.allocator, text_block) catch {};
541 }
542}
543
544fn freeTextBlock(self: *Coff, text_block: *TextBlock) void {
545 var already_have_free_list_node = false;
546 {
547 var i: usize = 0;
548 // TODO turn text_block_free_list into a hash map
549 while (i < self.text_block_free_list.items.len) {
550 if (self.text_block_free_list.items[i] == text_block) {
551 _ = self.text_block_free_list.swapRemove(i);
552 continue;
553 }
554 if (self.text_block_free_list.items[i] == text_block.prev) {
555 already_have_free_list_node = true;
556 }
557 i += 1;
558 }
559 }
560 if (self.last_text_block == text_block) {
561 self.last_text_block = text_block.prev;
562 }
563 if (text_block.prev) |prev| {
564 prev.next = text_block.next;
565
566 if (!already_have_free_list_node and prev.freeListEligible()) {
567 // The free list is heuristics, it doesn't have to be perfect, so we can
568 // ignore the OOM here.
569 self.text_block_free_list.append(self.base.allocator, prev) catch {};
570 }
571 }
572
573 if (text_block.next) |next| {
574 next.prev = text_block.prev;
575 }
576}
577
578fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
579 const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
580 const endian = self.base.options.target.cpu.arch.endian();
581
582 const offset_table_start = self.section_data_offset;
583 if (self.offset_table_size_dirty) {
584 const current_raw_size = self.offset_table_size;
585 const new_raw_size = self.offset_table_size * 2;
586 log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size });
587
588 // Move the text section to a new place in the executable
589 const current_text_section_start = self.section_data_offset + current_raw_size;
590 const new_text_section_start = self.section_data_offset + new_raw_size;
591
592 const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size);
593 if (amt != self.text_section_size) return error.InputOutput;
594
595 // Write the new raw size in the .got header
596 var buf: [8]u8 = undefined;
597 std.mem.writeIntLittle(u32, buf[0..4], new_raw_size);
598 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16);
599 // Write the new .text section file offset in the .text section header
600 std.mem.writeIntLittle(u32, buf[0..4], new_text_section_start);
601 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20);
602
603 const current_virtual_size = std.mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
604 const new_virtual_size = std.mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
605 // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section
606 // and the virutal size of the `.got` section
607
608 if (new_virtual_size != current_virtual_size) {
609 log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size });
610 self.size_of_image_dirty = true;
611 const va_offset = new_virtual_size - current_virtual_size;
612
613 // Write .got virtual size
614 std.mem.writeIntLittle(u32, buf[0..4], new_virtual_size);
615 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8);
616
617 // Write .text new virtual address
618 self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
619 std.mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base);
620 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
621
622 // Fix the VAs in the offset table
623 for (self.offset_table.items) |*va, idx| {
624 if (va.* != 0) {
625 va.* += va_offset;
626
627 switch (entry_size) {
628 4 => {
629 std.mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian);
630 try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size);
631 },
632 8 => {
633 std.mem.writeInt(u64, &buf, va.*, endian);
634 try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size);
635 },
636 else => unreachable,
637 }
638 }
639 }
640 }
641 self.offset_table_size = new_raw_size;
642 self.offset_table_size_dirty = false;
643 }
644 // Write the new entry
645 switch (entry_size) {
646 4 => {
647 var buf: [4]u8 = undefined;
648 std.mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
649 try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
650 },
651 8 => {
652 var buf: [8]u8 = undefined;
653 std.mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
654 try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
655 },
656 else => unreachable,
657 }
658}
659
660pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
661 // TODO COFF/PE debug information
662 // TODO Implement exports
663 const tracy = trace(@src());
664 defer tracy.end();
665
666 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
667 defer code_buffer.deinit();
668
669 const typed_value = decl.typed_value.most_recent.typed_value;
670 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
671 const code = switch (res) {
672 .externally_managed => |x| x,
673 .appended => code_buffer.items,
674 .fail => |em| {
675 decl.analysis = .codegen_failure;
676 try module.failed_decls.put(module.gpa, decl, em);
677 return;
678 },
679 };
680
681 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
682 const curr_size = decl.link.coff.size;
683 if (curr_size != 0) {
684 const capacity = decl.link.coff.capacity();
685 const need_realloc = code.len > capacity or
686 !std.mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment);
687 if (need_realloc) {
688 const curr_vaddr = self.getDeclVAddr(decl);
689 const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment);
690 log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr });
691 if (vaddr != curr_vaddr) {
692 log.debug(" (writing new offset table entry)\n", .{});
693 self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
694 try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
695 }
696 } else if (code.len < curr_size) {
697 self.shrinkTextBlock(&decl.link.coff, code.len);
698 }
699 } else {
700 const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment);
701 log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ std.mem.spanZ(decl.name), vaddr, code.len });
702 errdefer self.freeTextBlock(&decl.link.coff);
703 self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
704 try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
705 }
706
707 // Write the code into the file
708 try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset);
709
710 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
711 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
712 return self.updateDeclExports(module, decl, decl_exports);
713}
714
715pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
716 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
717 self.freeTextBlock(&decl.link.coff);
718 self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {};
719}
720
721pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void {
722 for (exports) |exp| {
723 if (exp.options.section) |section_name| {
724 if (!std.mem.eql(u8, section_name, ".text")) {
725 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
726 module.failed_exports.putAssumeCapacityNoClobber(
727 exp,
728 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
729 );
730 continue;
731 }
732 }
733 if (std.mem.eql(u8, exp.options.name, "_start")) {
734 self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base;
735 } else {
736 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
737 module.failed_exports.putAssumeCapacityNoClobber(
738 exp,
739 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}),
740 );
741 continue;
742 }
743 }
744}
745
746pub fn flush(self: *Coff, module: *Module) !void {
747 if (self.text_section_size_dirty) {
748 // Write the new raw size in the .text header
749 var buf: [4]u8 = undefined;
750 std.mem.writeIntLittle(u32, &buf, self.text_section_size);
751 try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16);
752 try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size);
753 self.text_section_size_dirty = false;
754 }
755
756 if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
757 const new_size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment);
758 var buf: [4]u8 = undefined;
759 std.mem.writeIntLittle(u32, &buf, new_size_of_image);
760 try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
761 self.size_of_image_dirty = false;
762 }
763
764 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
765 log.debug("flushing. no_entry_point_found = true\n", .{});
766 self.error_flags.no_entry_point_found = true;
767 } else {
768 log.debug("flushing. no_entry_point_found = false\n", .{});
769 self.error_flags.no_entry_point_found = false;
770
771 if (self.base.options.output_mode == .Exe) {
772 // Write AddressOfEntryPoint
773 var buf: [4]u8 = undefined;
774 std.mem.writeIntLittle(u32, &buf, self.entry_addr.?);
775 try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16);
776 }
777 }
778}
779
780pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
781 return self.text_section_virtual_address + decl.link.coff.text_offset;
782}
783
784pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
785 // TODO Implement this
786}
787
788pub fn deinit(self: *Coff) void {
789 self.text_block_free_list.deinit(self.base.allocator);
790 self.offset_table.deinit(self.base.allocator);
791 self.offset_table_free_list.deinit(self.base.allocator);
792}
src-self-hosted/link/Elf.zig+11-5
......@@ -1656,8 +1656,8 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
16561656 try dbg_line_buffer.ensureCapacity(26);
16571657
16581658 const line_off: u28 = blk: {
1659 if (decl.scope.cast(Module.Scope.File)) |scope_file| {
1660 const tree = scope_file.contents.tree;
1659 if (decl.scope.cast(Module.Scope.Container)) |container_scope| {
1660 const tree = container_scope.file_scope.contents.tree;
16611661 const file_ast_decls = tree.root_node.decls();
16621662 // TODO Look into improving the performance here by adding a token-index-to-line
16631663 // lookup table. Currently this involves scanning over the source code for newlines.
......@@ -1735,7 +1735,13 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
17351735 } else {
17361736 // TODO implement .debug_info for global variables
17371737 }
1738 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, &dbg_line_buffer, &dbg_info_buffer, &dbg_info_type_relocs);
1738 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .{
1739 .dwarf = .{
1740 .dbg_line = &dbg_line_buffer,
1741 .dbg_info = &dbg_info_buffer,
1742 .dbg_info_type_relocs = &dbg_info_type_relocs,
1743 },
1744 });
17391745 const code = switch (res) {
17401746 .externally_managed => |x| x,
17411747 .appended => code_buffer.items,
......@@ -2157,8 +2163,8 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
21572163 const tracy = trace(@src());
21582164 defer tracy.end();
21592165
2160 const scope_file = decl.scope.cast(Module.Scope.File).?;
2161 const tree = scope_file.contents.tree;
2166 const container_scope = decl.scope.cast(Module.Scope.Container).?;
2167 const tree = container_scope.file_scope.contents.tree;
21622168 const file_ast_decls = tree.root_node.decls();
21632169 // TODO Look into improving the performance here by adding a token-index-to-line
21642170 // lookup table. Currently this involves scanning over the source code for newlines.
src-self-hosted/link/MachO.zig+502-159
......@@ -18,36 +18,66 @@ const File = link.File;
1818
1919pub const base_tag: File.Tag = File.Tag.macho;
2020
21const LoadCommand = union(enum) {
22 Segment: macho.segment_command_64,
23 LinkeditData: macho.linkedit_data_command,
24 Symtab: macho.symtab_command,
25 Dysymtab: macho.dysymtab_command,
26
27 pub fn cmdsize(self: LoadCommand) u32 {
28 return switch (self) {
29 .Segment => |x| x.cmdsize,
30 .LinkeditData => |x| x.cmdsize,
31 .Symtab => |x| x.cmdsize,
32 .Dysymtab => |x| x.cmdsize,
33 };
34 }
35};
36
2137base: File,
2238
23/// List of all load command headers that are in the file.
24/// We use it to track number and size of all commands needed by the header.
25commands: std.ArrayListUnmanaged(macho.load_command) = std.ArrayListUnmanaged(macho.load_command){},
26command_file_offset: ?u64 = null,
39/// Table of all load commands
40load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
41segment_cmd_index: ?u16 = null,
42symtab_cmd_index: ?u16 = null,
43dysymtab_cmd_index: ?u16 = null,
44data_in_code_cmd_index: ?u16 = null,
2745
28/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
29/// Same order as in the file.
30segments: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){},
31/// Section (headers) *always* follow segment (load commands) directly!
32sections: std.ArrayListUnmanaged(macho.section_64) = std.ArrayListUnmanaged(macho.section_64){},
46/// Table of all sections
47sections: std.ArrayListUnmanaged(macho.section_64) = .{},
3348
34/// Offset (index) into __TEXT segment load command.
35text_segment_offset: ?u64 = null,
36/// Offset (index) into __LINKEDIT segment load command.
37linkedit_segment_offset: ?u664 = null,
49/// __TEXT segment sections
50text_section_index: ?u16 = null,
51cstring_section_index: ?u16 = null,
52const_text_section_index: ?u16 = null,
53stubs_section_index: ?u16 = null,
54stub_helper_section_index: ?u16 = null,
55
56/// __DATA segment sections
57got_section_index: ?u16 = null,
58const_data_section_index: ?u16 = null,
3859
39/// Entry point load command
40entry_point_cmd: ?macho.entry_point_command = null,
4160entry_addr: ?u64 = null,
4261
43/// The first 4GB of process' memory is reserved for the null (__PAGEZERO) segment.
44/// This is also the start address for our binary.
45vm_start_address: u64 = 0x100000000,
62/// Table of all symbols used.
63/// Internally references string table for names (which are optional).
64symbol_table: std.ArrayListUnmanaged(macho.nlist_64) = .{},
65
66/// Table of symbol names aka the string table.
67string_table: std.ArrayListUnmanaged(u8) = .{},
4668
47seg_table_dirty: bool = false,
69/// Table of symbol vaddr values. The values is the absolute vaddr value.
70/// If the vaddr of the executable __TEXT segment vaddr changes, the entire offset
71/// table needs to be rewritten.
72offset_table: std.ArrayListUnmanaged(u64) = .{},
4873
4974error_flags: File.ErrorFlags = File.ErrorFlags{},
5075
76cmd_table_dirty: bool = false,
77
78/// Pointer to the last allocated text block
79last_text_block: ?*TextBlock = null,
80
5181/// `alloc_num / alloc_den` is the factor of padding when allocating.
5282const alloc_num = 4;
5383const alloc_den = 3;
......@@ -67,7 +97,23 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
6797const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
6898
6999pub const TextBlock = struct {
70 pub const empty = TextBlock{};
100 /// Index into the symbol table
101 symbol_table_index: ?u32,
102 /// Index into offset table
103 offset_table_index: ?u32,
104 /// Size of this text block
105 size: u64,
106 /// Points to the previous and next neighbours
107 prev: ?*TextBlock,
108 next: ?*TextBlock,
109
110 pub const empty = TextBlock{
111 .symbol_table_index = null,
112 .offset_table_index = null,
113 .size = 0,
114 .prev = null,
115 .next = null,
116 };
71117};
72118
73119pub const SrcFn = struct {
......@@ -117,6 +163,12 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO
117163/// Truncates the existing file contents and overwrites the contents.
118164/// Returns an error if `file` is not already open with +read +write +seek abilities.
119165fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO {
166 switch (options.output_mode) {
167 .Exe => {},
168 .Obj => {},
169 .Lib => return error.TODOImplementWritingLibFiles,
170 }
171
120172 var self: MachO = .{
121173 .base = .{
122174 .file = file,
......@@ -127,104 +179,15 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach
127179 };
128180 errdefer self.deinit();
129181
130 switch (options.output_mode) {
131 .Exe => {
132 // The first segment command for executables is always a __PAGEZERO segment.
133 const pagezero = .{
134 .cmd = macho.LC_SEGMENT_64,
135 .cmdsize = commandSize(@sizeOf(macho.segment_command_64)),
136 .segname = makeString("__PAGEZERO"),
137 .vmaddr = 0,
138 .vmsize = self.vm_start_address,
139 .fileoff = 0,
140 .filesize = 0,
141 .maxprot = macho.VM_PROT_NONE,
142 .initprot = macho.VM_PROT_NONE,
143 .nsects = 0,
144 .flags = 0,
145 };
146 try self.commands.append(allocator, .{
147 .cmd = pagezero.cmd,
148 .cmdsize = pagezero.cmdsize,
149 });
150 try self.segments.append(allocator, pagezero);
151 },
152 .Obj => return error.TODOImplementWritingObjFiles,
153 .Lib => return error.TODOImplementWritingLibFiles,
154 }
155
156182 try self.populateMissingMetadata();
157183
158184 return self;
159185}
160186
161fn writeMachOHeader(self: *MachO) !void {
162 var hdr: macho.mach_header_64 = undefined;
163 hdr.magic = macho.MH_MAGIC_64;
164
165 const CpuInfo = struct {
166 cpu_type: macho.cpu_type_t,
167 cpu_subtype: macho.cpu_subtype_t,
168 };
169
170 const cpu_info: CpuInfo = switch (self.base.options.target.cpu.arch) {
171 .aarch64 => .{
172 .cpu_type = macho.CPU_TYPE_ARM64,
173 .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL,
174 },
175 .x86_64 => .{
176 .cpu_type = macho.CPU_TYPE_X86_64,
177 .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL,
178 },
179 else => return error.UnsupportedMachOArchitecture,
180 };
181 hdr.cputype = cpu_info.cpu_type;
182 hdr.cpusubtype = cpu_info.cpu_subtype;
183
184 const filetype: u32 = switch (self.base.options.output_mode) {
185 .Exe => macho.MH_EXECUTE,
186 .Obj => macho.MH_OBJECT,
187 .Lib => switch (self.base.options.link_mode) {
188 .Static => return error.TODOStaticLibMachOType,
189 .Dynamic => macho.MH_DYLIB,
190 },
191 };
192 hdr.filetype = filetype;
193
194 const ncmds = try math.cast(u32, self.commands.items.len);
195 hdr.ncmds = ncmds;
196
197 var sizeof_cmds: u32 = 0;
198 for (self.commands.items) |cmd| {
199 sizeof_cmds += cmd.cmdsize;
200 }
201 hdr.sizeofcmds = sizeof_cmds;
202
203 // TODO should these be set to something else?
204 hdr.flags = 0;
205 hdr.reserved = 0;
206
207 try self.base.file.?.pwriteAll(@ptrCast([*]const u8, &hdr)[0..@sizeOf(macho.mach_header_64)], 0);
208}
209
210187pub fn flush(self: *MachO, module: *Module) !void {
211 // Save segments first
212 {
213 const buf = try self.base.allocator.alloc(macho.segment_command_64, self.segments.items.len);
214 defer self.base.allocator.free(buf);
215
216 self.command_file_offset = @sizeOf(macho.mach_header_64);
217
218 for (buf) |*seg, i| {
219 seg.* = self.segments.items[i];
220 self.command_file_offset.? += self.segments.items[i].cmdsize;
221 }
222
223 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), @sizeOf(macho.mach_header_64));
224 }
225
226188 switch (self.base.options.output_mode) {
227189 .Exe => {
190 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
228191 {
229192 // Specify path to dynamic linker dyld
230193 const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH));
......@@ -235,18 +198,14 @@ pub fn flush(self: *MachO, module: *Module) !void {
235198 .name = @sizeOf(macho.dylinker_command),
236199 },
237200 };
238 try self.commands.append(self.base.allocator, .{
239 .cmd = macho.LC_LOAD_DYLINKER,
240 .cmdsize = cmdsize,
241 });
242201
243 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?);
202 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), last_cmd_offset);
244203
245 const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command);
204 const file_offset = last_cmd_offset + @sizeOf(macho.dylinker_command);
246205 try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);
247206
248207 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);
249 self.command_file_offset.? += cmdsize;
208 last_cmd_offset += cmdsize;
250209 }
251210
252211 {
......@@ -268,21 +227,44 @@ pub fn flush(self: *MachO, module: *Module) !void {
268227 .dylib = dylib,
269228 },
270229 };
271 try self.commands.append(self.base.allocator, .{
272 .cmd = macho.LC_LOAD_DYLIB,
273 .cmdsize = cmdsize,
274 });
275230
276 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?);
231 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), last_cmd_offset);
277232
278 const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command);
233 const file_offset = last_cmd_offset + @sizeOf(macho.dylib_command);
279234 try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);
280235
281236 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);
282 self.command_file_offset.? += cmdsize;
237 last_cmd_offset += cmdsize;
238 }
239 },
240 .Obj => {
241 {
242 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
243 symtab.nsyms = @intCast(u32, self.symbol_table.items.len);
244 const allocated_size = self.allocatedSize(symtab.stroff);
245 const needed_size = self.string_table.items.len;
246 log.debug("allocated_size = 0x{x}, needed_size = 0x{x}\n", .{ allocated_size, needed_size });
247
248 if (needed_size > allocated_size) {
249 symtab.strsize = 0;
250 symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1));
251 }
252 symtab.strsize = @intCast(u32, needed_size);
253
254 log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize });
255
256 try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);
257 }
258
259 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
260 for (self.load_commands.items) |cmd| {
261 const cmd_to_write = [1]@TypeOf(cmd){cmd};
262 try self.base.file.?.pwriteAll(mem.sliceAsBytes(cmd_to_write[0..1]), last_cmd_offset);
263 last_cmd_offset += cmd.cmdsize();
283264 }
265 const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
266 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items), off);
284267 },
285 .Obj => return error.TODOImplementWritingObjFiles,
286268 .Lib => return error.TODOImplementWritingLibFiles,
287269 }
288270
......@@ -297,14 +279,87 @@ pub fn flush(self: *MachO, module: *Module) !void {
297279}
298280
299281pub fn deinit(self: *MachO) void {
300 self.commands.deinit(self.base.allocator);
301 self.segments.deinit(self.base.allocator);
282 self.offset_table.deinit(self.base.allocator);
283 self.string_table.deinit(self.base.allocator);
284 self.symbol_table.deinit(self.base.allocator);
302285 self.sections.deinit(self.base.allocator);
286 self.load_commands.deinit(self.base.allocator);
287}
288
289pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
290 if (decl.link.macho.symbol_table_index) |_| return;
291
292 try self.symbol_table.ensureCapacity(self.base.allocator, self.symbol_table.items.len + 1);
293 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
294
295 log.debug("allocating symbol index {} for {}\n", .{ self.symbol_table.items.len, decl.name });
296 decl.link.macho.symbol_table_index = @intCast(u32, self.symbol_table.items.len);
297 _ = self.symbol_table.addOneAssumeCapacity();
298
299 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
300 _ = self.offset_table.addOneAssumeCapacity();
301
302 self.symbol_table.items[decl.link.macho.symbol_table_index.?] = .{
303 .n_strx = 0,
304 .n_type = 0,
305 .n_sect = 0,
306 .n_desc = 0,
307 .n_value = 0,
308 };
309 self.offset_table.items[decl.link.macho.offset_table_index.?] = 0;
303310}
304311
305pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {}
312pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
313 const tracy = trace(@src());
314 defer tracy.end();
315
316 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
317 defer code_buffer.deinit();
306318
307pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {}
319 const typed_value = decl.typed_value.most_recent.typed_value;
320 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
321
322 const code = switch (res) {
323 .externally_managed => |x| x,
324 .appended => code_buffer.items,
325 .fail => |em| {
326 decl.analysis = .codegen_failure;
327 try module.failed_decls.put(module.gpa, decl, em);
328 return;
329 },
330 };
331 log.debug("generated code {}\n", .{code});
332
333 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
334 const symbol = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];
335
336 const decl_name = mem.spanZ(decl.name);
337 const name_str_index = try self.makeString(decl_name);
338 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
339 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr });
340 log.debug("updated text section {}\n", .{self.sections.items[self.text_section_index.?]});
341
342 symbol.* = .{
343 .n_strx = name_str_index,
344 .n_type = macho.N_SECT,
345 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
346 .n_desc = 0,
347 .n_value = addr,
348 };
349 self.offset_table.items[decl.link.macho.offset_table_index.?] = addr;
350
351 try self.writeSymbol(decl.link.macho.symbol_table_index.?);
352
353 const text_section = self.sections.items[self.text_section_index.?];
354 const section_offset = symbol.n_value - text_section.addr;
355 const file_offset = text_section.offset + section_offset;
356 log.debug("file_offset 0x{x}\n", .{file_offset});
357 try self.base.file.?.pwriteAll(code, file_offset);
358
359 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
360 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
361 return self.updateDeclExports(module, decl, decl_exports);
362}
308363
309364pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
310365
......@@ -313,51 +368,191 @@ pub fn updateDeclExports(
313368 module: *Module,
314369 decl: *const Module.Decl,
315370 exports: []const *Module.Export,
316) !void {}
371) !void {
372 const tracy = trace(@src());
373 defer tracy.end();
374
375 if (decl.link.macho.symbol_table_index == null) return;
376
377 var decl_sym = self.symbol_table.items[decl.link.macho.symbol_table_index.?];
378 // TODO implement
379 if (exports.len == 0) return;
380
381 const exp = exports[0];
382 self.entry_addr = decl_sym.n_value;
383 decl_sym.n_type |= macho.N_EXT;
384 exp.link.sym_index = 0;
385}
317386
318387pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
319388
320389pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
321 @panic("TODO implement getDeclVAddr for MachO");
390 return self.symbol_table.items[decl.link.macho.symbol_table_index.?].n_value;
322391}
323392
324393pub fn populateMissingMetadata(self: *MachO) !void {
325 if (self.text_segment_offset == null) {
326 self.text_segment_offset = @intCast(u64, self.segments.items.len);
327 const file_size = alignSize(u64, self.base.options.program_code_size_hint, 0x1000);
328 log.debug("vmsize/filesize = {}", .{file_size});
329 const file_offset = 0;
330 const vm_address = self.vm_start_address; // the end of __PAGEZERO segment in VM
331 const protection = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;
332 const cmdsize = commandSize(@sizeOf(macho.segment_command_64));
333 const text_segment = .{
334 .cmd = macho.LC_SEGMENT_64,
335 .cmdsize = cmdsize,
336 .segname = makeString("__TEXT"),
337 .vmaddr = vm_address,
338 .vmsize = file_size,
339 .fileoff = 0, // __TEXT segment *always* starts at 0 file offset
340 .filesize = 0, //file_size,
341 .maxprot = protection,
342 .initprot = protection,
343 .nsects = 0,
344 .flags = 0,
345 };
346 try self.commands.append(self.base.allocator, .{
347 .cmd = macho.LC_SEGMENT_64,
348 .cmdsize = cmdsize,
394 if (self.segment_cmd_index == null) {
395 self.segment_cmd_index = @intCast(u16, self.load_commands.items.len);
396 try self.load_commands.append(self.base.allocator, .{
397 .Segment = .{
398 .cmd = macho.LC_SEGMENT_64,
399 .cmdsize = @sizeOf(macho.segment_command_64),
400 .segname = makeStaticString(""),
401 .vmaddr = 0,
402 .vmsize = 0,
403 .fileoff = 0,
404 .filesize = 0,
405 .maxprot = 0,
406 .initprot = 0,
407 .nsects = 0,
408 .flags = 0,
409 },
410 });
411 self.cmd_table_dirty = true;
412 }
413 if (self.symtab_cmd_index == null) {
414 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
415 try self.load_commands.append(self.base.allocator, .{
416 .Symtab = .{
417 .cmd = macho.LC_SYMTAB,
418 .cmdsize = @sizeOf(macho.symtab_command),
419 .symoff = 0,
420 .nsyms = 0,
421 .stroff = 0,
422 .strsize = 0,
423 },
424 });
425 self.cmd_table_dirty = true;
426 }
427 if (self.text_section_index == null) {
428 self.text_section_index = @intCast(u16, self.sections.items.len);
429 const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
430 segment.cmdsize += @sizeOf(macho.section_64);
431 segment.nsects += 1;
432
433 const file_size = self.base.options.program_code_size_hint;
434 const off = @intCast(u32, self.findFreeSpace(file_size, 1));
435 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
436
437 log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
438
439 try self.sections.append(self.base.allocator, .{
440 .sectname = makeStaticString("__text"),
441 .segname = makeStaticString("__TEXT"),
442 .addr = 0,
443 .size = file_size,
444 .offset = off,
445 .@"align" = 0x1000,
446 .reloff = 0,
447 .nreloc = 0,
448 .flags = flags,
449 .reserved1 = 0,
450 .reserved2 = 0,
451 .reserved3 = 0,
349452 });
350 try self.segments.append(self.base.allocator, text_segment);
453
454 segment.vmsize += file_size;
455 segment.filesize += file_size;
456 segment.fileoff = off;
457
458 log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]});
459 }
460 {
461 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
462 if (symtab.symoff == 0) {
463 const p_align = @sizeOf(macho.nlist_64);
464 const nsyms = self.base.options.symbol_count_hint;
465 const file_size = p_align * nsyms;
466 const off = @intCast(u32, self.findFreeSpace(file_size, p_align));
467 log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
468 symtab.symoff = off;
469 symtab.nsyms = @intCast(u32, nsyms);
470 }
471 if (symtab.stroff == 0) {
472 try self.string_table.append(self.base.allocator, 0);
473 const file_size = @intCast(u32, self.string_table.items.len);
474 const off = @intCast(u32, self.findFreeSpace(file_size, 1));
475 log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
476 symtab.stroff = off;
477 symtab.strsize = file_size;
478 }
479 }
480}
481
482fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
483 const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
484 const text_section = &self.sections.items[self.text_section_index.?];
485 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
486
487 var block_placement: ?*TextBlock = null;
488 const addr = blk: {
489 if (self.last_text_block) |last| {
490 const last_symbol = self.symbol_table.items[last.symbol_table_index.?];
491 const ideal_capacity = last.size * alloc_num / alloc_den;
492 const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity;
493 const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment);
494 block_placement = last;
495 break :blk new_start_addr;
496 } else {
497 break :blk text_section.addr;
498 }
499 };
500 log.debug("computed symbol address 0x{x}\n", .{addr});
501
502 const expand_text_section = block_placement == null or block_placement.?.next == null;
503 if (expand_text_section) {
504 const text_capacity = self.allocatedSize(text_section.offset);
505 const needed_size = (addr + new_block_size) - text_section.addr;
506 log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size });
507
508 if (needed_size > text_capacity) {
509 // TODO handle growth
510 }
511
512 self.last_text_block = text_block;
513 text_section.size = needed_size;
514 segment.vmsize = needed_size;
515 segment.filesize = needed_size;
516 if (alignment < text_section.@"align") {
517 text_section.@"align" = @intCast(u32, alignment);
518 }
519 }
520 text_block.size = new_block_size;
521
522 if (text_block.prev) |prev| {
523 prev.next = text_block.next;
524 }
525 if (text_block.next) |next| {
526 next.prev = text_block.prev;
527 }
528
529 if (block_placement) |big_block| {
530 text_block.prev = big_block;
531 text_block.next = big_block.next;
532 big_block.next = text_block;
533 } else {
534 text_block.prev = null;
535 text_block.next = null;
351536 }
537
538 return addr;
352539}
353540
354fn makeString(comptime bytes: []const u8) [16]u8 {
541fn makeStaticString(comptime bytes: []const u8) [16]u8 {
355542 var buf = [_]u8{0} ** 16;
356 if (bytes.len > buf.len) @compileError("MachO segment/section name too long");
543 if (bytes.len > buf.len) @compileError("string too long; max 16 bytes");
357544 mem.copy(u8, buf[0..], bytes);
358545 return buf;
359546}
360547
548fn makeString(self: *MachO, bytes: []const u8) !u32 {
549 try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1);
550 const result = self.string_table.items.len;
551 self.string_table.appendSliceAssumeCapacity(bytes);
552 self.string_table.appendAssumeCapacity(0);
553 return @intCast(u32, result);
554}
555
361556fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int {
362557 const size = @intCast(Int, min_size);
363558 if (size % alignment == 0) return size;
......@@ -370,7 +565,7 @@ fn commandSize(min_size: anytype) u32 {
370565 return alignSize(u32, min_size, @sizeOf(u64));
371566}
372567
373fn addPadding(self: *MachO, size: u32, file_offset: u64) !void {
568fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
374569 if (size == 0) return;
375570
376571 const buf = try self.base.allocator.alloc(u8, size);
......@@ -380,3 +575,151 @@ fn addPadding(self: *MachO, size: u32, file_offset: u64) !void {
380575
381576 try self.base.file.?.pwriteAll(buf, file_offset);
382577}
578
579fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
580 const hdr_size: u64 = @sizeOf(macho.mach_header_64);
581 if (start < hdr_size)
582 return hdr_size;
583
584 const end = start + satMul(size, alloc_num) / alloc_den;
585
586 {
587 const off = @sizeOf(macho.mach_header_64);
588 var tight_size: u64 = 0;
589 for (self.load_commands.items) |cmd| {
590 tight_size += cmd.cmdsize();
591 }
592 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
593 const test_end = off + increased_size;
594 if (end > off and start < test_end) {
595 return test_end;
596 }
597 }
598
599 for (self.sections.items) |section| {
600 const increased_size = satMul(section.size, alloc_num) / alloc_den;
601 const test_end = section.offset + increased_size;
602 if (end > section.offset and start < test_end) {
603 return test_end;
604 }
605 }
606
607 if (self.symtab_cmd_index) |symtab_index| {
608 const symtab = self.load_commands.items[symtab_index].Symtab;
609 {
610 const tight_size = @sizeOf(macho.nlist_64) * symtab.nsyms;
611 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
612 const test_end = symtab.symoff + increased_size;
613 if (end > symtab.symoff and start < test_end) {
614 return test_end;
615 }
616 }
617 {
618 const increased_size = satMul(symtab.strsize, alloc_num) / alloc_den;
619 const test_end = symtab.stroff + increased_size;
620 if (end > symtab.stroff and start < test_end) {
621 return test_end;
622 }
623 }
624 }
625
626 return null;
627}
628
629fn allocatedSize(self: *MachO, start: u64) u64 {
630 if (start == 0)
631 return 0;
632 var min_pos: u64 = std.math.maxInt(u64);
633 {
634 const off = @sizeOf(macho.mach_header_64);
635 if (off > start and off < min_pos) min_pos = off;
636 }
637 for (self.sections.items) |section| {
638 if (section.offset <= start) continue;
639 if (section.offset < min_pos) min_pos = section.offset;
640 }
641 if (self.symtab_cmd_index) |symtab_index| {
642 const symtab = self.load_commands.items[symtab_index].Symtab;
643 if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff;
644 if (symtab.stroff > start and symtab.stroff < min_pos) min_pos = symtab.stroff;
645 }
646 return min_pos - start;
647}
648
649fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 {
650 var start: u64 = 0;
651 while (self.detectAllocCollision(start, object_size)) |item_end| {
652 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
653 }
654 return start;
655}
656
657fn writeSymbol(self: *MachO, index: usize) !void {
658 const tracy = trace(@src());
659 defer tracy.end();
660
661 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
662 var sym = [1]macho.nlist_64{self.symbol_table.items[index]};
663 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
664 log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off });
665 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
666}
667
668/// Writes Mach-O file header.
669/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping
670/// variables.
671fn writeMachOHeader(self: *MachO) !void {
672 var hdr: macho.mach_header_64 = undefined;
673 hdr.magic = macho.MH_MAGIC_64;
674
675 const CpuInfo = struct {
676 cpu_type: macho.cpu_type_t,
677 cpu_subtype: macho.cpu_subtype_t,
678 };
679
680 const cpu_info: CpuInfo = switch (self.base.options.target.cpu.arch) {
681 .aarch64 => .{
682 .cpu_type = macho.CPU_TYPE_ARM64,
683 .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL,
684 },
685 .x86_64 => .{
686 .cpu_type = macho.CPU_TYPE_X86_64,
687 .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL,
688 },
689 else => return error.UnsupportedMachOArchitecture,
690 };
691 hdr.cputype = cpu_info.cpu_type;
692 hdr.cpusubtype = cpu_info.cpu_subtype;
693
694 const filetype: u32 = switch (self.base.options.output_mode) {
695 .Exe => macho.MH_EXECUTE,
696 .Obj => macho.MH_OBJECT,
697 .Lib => switch (self.base.options.link_mode) {
698 .Static => return error.TODOStaticLibMachOType,
699 .Dynamic => macho.MH_DYLIB,
700 },
701 };
702 hdr.filetype = filetype;
703 hdr.ncmds = @intCast(u32, self.load_commands.items.len);
704
705 var sizeofcmds: u32 = 0;
706 for (self.load_commands.items) |cmd| {
707 sizeofcmds += cmd.cmdsize();
708 }
709
710 hdr.sizeofcmds = sizeofcmds;
711
712 // TODO should these be set to something else?
713 hdr.flags = 0;
714 hdr.reserved = 0;
715
716 log.debug("writing Mach-O header {}\n", .{hdr});
717
718 try self.base.file.?.pwriteAll(@ptrCast([*]const u8, &hdr)[0..@sizeOf(macho.mach_header_64)], 0);
719}
720
721/// Saturating multiplication
722fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
723 const T = @TypeOf(a, b);
724 return std.math.mul(T, a, b) catch std.math.maxInt(T);
725}
src-self-hosted/link/msdos-stub.bin created
Binary files /dev/null and b/src-self-hosted/link/msdos-stub.bin differ
src-self-hosted/main.zig+18-8
......@@ -153,8 +153,8 @@ const usage_build_generic =
153153 \\ elf Executable and Linking Format
154154 \\ c Compile to C source code
155155 \\ wasm WebAssembly
156 \\ pe Portable Executable (Windows)
156157 \\ coff (planned) Common Object File Format (Windows)
157 \\ pe (planned) Portable Executable (Windows)
158158 \\ macho (planned) macOS relocatables
159159 \\ hex (planned) Intel IHEX
160160 \\ raw (planned) Dump machine code directly
......@@ -451,7 +451,7 @@ fn buildOutputType(
451451 } else if (mem.eql(u8, ofmt, "coff")) {
452452 break :blk .coff;
453453 } else if (mem.eql(u8, ofmt, "pe")) {
454 break :blk .coff;
454 break :blk .pe;
455455 } else if (mem.eql(u8, ofmt, "macho")) {
456456 break :blk .macho;
457457 } else if (mem.eql(u8, ofmt, "wasm")) {
......@@ -524,17 +524,19 @@ fn buildOutputType(
524524 try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});
525525 continue;
526526 }) |line| {
527 if (mem.eql(u8, line, "update")) {
527 const actual_line = mem.trimRight(u8, line, "\r\n ");
528
529 if (mem.eql(u8, actual_line, "update")) {
528530 if (output_mode == .Exe) {
529531 try module.makeBinFileWritable();
530532 }
531533 try updateModule(gpa, &module, zir_out_path);
532 } else if (mem.eql(u8, line, "exit")) {
534 } else if (mem.eql(u8, actual_line, "exit")) {
533535 break;
534 } else if (mem.eql(u8, line, "help")) {
536 } else if (mem.eql(u8, actual_line, "help")) {
535537 try stderr.writeAll(repl_help);
536538 } else {
537 try stderr.print("unknown command: {}\n", .{line});
539 try stderr.print("unknown command: {}\n", .{actual_line});
538540 }
539541 } else {
540542 break;
......@@ -742,6 +744,7 @@ const FmtError = error{
742744 LinkQuotaExceeded,
743745 FileBusy,
744746 EndOfStream,
747 Unseekable,
745748 NotOpenForWriting,
746749} || fs.File.OpenError;
747750
......@@ -805,7 +808,13 @@ fn fmtPathFile(
805808 if (stat.kind == .Directory)
806809 return error.IsDir;
807810
808 const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| switch (err) {
811 const source_code = source_file.readToEndAllocOptions(
812 fmt.gpa,
813 max_src_size,
814 stat.size,
815 @alignOf(u8),
816 null,
817 ) catch |err| switch (err) {
809818 error.ConnectionResetByPeer => unreachable,
810819 error.ConnectionTimedOut => unreachable,
811820 error.NotOpenForReading => unreachable,
......@@ -839,7 +848,8 @@ fn fmtPathFile(
839848 // As a heuristic, we make enough capacity for the same as the input source.
840849 try fmt.out_buffer.ensureCapacity(source_code.len);
841850 fmt.out_buffer.items.len = 0;
842 const anything_changed = try std.zig.render(fmt.gpa, fmt.out_buffer.writer(), tree);
851 const writer = fmt.out_buffer.writer();
852 const anything_changed = try std.zig.render(fmt.gpa, writer, tree);
843853 if (!anything_changed)
844854 return; // Good thing we didn't waste any file system access on this.
845855
src-self-hosted/stage2.zig-1
......@@ -615,7 +615,6 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [
615615 error.NotOpenForWriting => unreachable,
616616 error.NotOpenForReading => unreachable,
617617 error.Unexpected => return .Unexpected,
618 error.EndOfStream => return .EndOfFile,
619618 error.IsDir => return .IsDir,
620619 error.ConnectionResetByPeer => unreachable,
621620 error.ConnectionTimedOut => unreachable,
src-self-hosted/type.zig+98-20
......@@ -163,7 +163,7 @@ pub const Type = extern union {
163163 // Hot path for common case:
164164 if (a.castPointer()) |a_payload| {
165165 if (b.castPointer()) |b_payload| {
166 return eql(a_payload.pointee_type, b_payload.pointee_type);
166 return a.tag() == b.tag() and eql(a_payload.pointee_type, b_payload.pointee_type);
167167 }
168168 }
169169 const is_slice_a = isSlice(a);
......@@ -189,10 +189,10 @@ pub const Type = extern union {
189189 .Array => {
190190 if (a.arrayLen() != b.arrayLen())
191191 return false;
192 if (a.elemType().eql(b.elemType()))
192 if (!a.elemType().eql(b.elemType()))
193193 return false;
194 const sentinel_a = a.arraySentinel();
195 const sentinel_b = b.arraySentinel();
194 const sentinel_a = a.sentinel();
195 const sentinel_b = b.sentinel();
196196 if (sentinel_a) |sa| {
197197 if (sentinel_b) |sb| {
198198 return sa.eql(sb);
......@@ -501,9 +501,9 @@ pub const Type = extern union {
501501 .noreturn,
502502 => return out_stream.writeAll(@tagName(t)),
503503
504 .enum_literal => return out_stream.writeAll("@TypeOf(.EnumLiteral)"),
505 .@"null" => return out_stream.writeAll("@TypeOf(null)"),
506 .@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"),
504 .enum_literal => return out_stream.writeAll("@Type(.EnumLiteral)"),
505 .@"null" => return out_stream.writeAll("@Type(.Null)"),
506 .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"),
507507
508508 .@"anyframe" => return out_stream.writeAll("anyframe"),
509509 .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
......@@ -630,8 +630,8 @@ pub const Type = extern union {
630630 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
631631 if (payload.sentinel) |some| switch (payload.size) {
632632 .One, .C => unreachable,
633 .Many => try out_stream.writeAll("[*:{}]"),
634 .Slice => try out_stream.writeAll("[:{}]"),
633 .Many => try out_stream.print("[*:{}]", .{some}),
634 .Slice => try out_stream.print("[:{}]", .{some}),
635635 } else switch (payload.size) {
636636 .One => try out_stream.writeAll("*"),
637637 .Many => try out_stream.writeAll("[*]"),
......@@ -1341,6 +1341,81 @@ pub const Type = extern union {
13411341 };
13421342 }
13431343
1344 pub fn isAllowzeroPtr(self: Type) bool {
1345 return switch (self.tag()) {
1346 .u8,
1347 .i8,
1348 .u16,
1349 .i16,
1350 .u32,
1351 .i32,
1352 .u64,
1353 .i64,
1354 .usize,
1355 .isize,
1356 .c_short,
1357 .c_ushort,
1358 .c_int,
1359 .c_uint,
1360 .c_long,
1361 .c_ulong,
1362 .c_longlong,
1363 .c_ulonglong,
1364 .c_longdouble,
1365 .f16,
1366 .f32,
1367 .f64,
1368 .f128,
1369 .c_void,
1370 .bool,
1371 .void,
1372 .type,
1373 .anyerror,
1374 .comptime_int,
1375 .comptime_float,
1376 .noreturn,
1377 .@"null",
1378 .@"undefined",
1379 .array,
1380 .array_sentinel,
1381 .array_u8,
1382 .array_u8_sentinel_0,
1383 .fn_noreturn_no_args,
1384 .fn_void_no_args,
1385 .fn_naked_noreturn_no_args,
1386 .fn_ccc_void_no_args,
1387 .function,
1388 .int_unsigned,
1389 .int_signed,
1390 .single_mut_pointer,
1391 .single_const_pointer,
1392 .many_const_pointer,
1393 .many_mut_pointer,
1394 .c_const_pointer,
1395 .c_mut_pointer,
1396 .const_slice,
1397 .mut_slice,
1398 .single_const_pointer_to_comptime_int,
1399 .const_slice_u8,
1400 .optional,
1401 .optional_single_mut_pointer,
1402 .optional_single_const_pointer,
1403 .enum_literal,
1404 .error_union,
1405 .@"anyframe",
1406 .anyframe_T,
1407 .anyerror_void_error_union,
1408 .error_set,
1409 .error_set_single,
1410 => false,
1411
1412 .pointer => {
1413 const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
1414 return payload.@"allowzero";
1415 },
1416 };
1417 }
1418
13441419 /// Asserts that the type is an optional
13451420 pub fn isPtrLikeOptional(self: Type) bool {
13461421 switch (self.tag()) {
......@@ -1585,8 +1660,8 @@ pub const Type = extern union {
15851660 };
15861661 }
15871662
1588 /// Asserts the type is an array or vector.
1589 pub fn arraySentinel(self: Type) ?Value {
1663 /// Asserts the type is an array, pointer or vector.
1664 pub fn sentinel(self: Type) ?Value {
15901665 return switch (self.tag()) {
15911666 .u8,
15921667 .i8,
......@@ -1626,16 +1701,8 @@ pub const Type = extern union {
16261701 .fn_naked_noreturn_no_args,
16271702 .fn_ccc_void_no_args,
16281703 .function,
1629 .pointer,
1630 .single_const_pointer,
1631 .single_mut_pointer,
1632 .many_const_pointer,
1633 .many_mut_pointer,
1634 .c_const_pointer,
1635 .c_mut_pointer,
16361704 .const_slice,
16371705 .mut_slice,
1638 .single_const_pointer_to_comptime_int,
16391706 .const_slice_u8,
16401707 .int_unsigned,
16411708 .int_signed,
......@@ -1651,7 +1718,18 @@ pub const Type = extern union {
16511718 .error_set_single,
16521719 => unreachable,
16531720
1654 .array, .array_u8 => return null,
1721 .single_const_pointer,
1722 .single_mut_pointer,
1723 .many_const_pointer,
1724 .many_mut_pointer,
1725 .c_const_pointer,
1726 .c_mut_pointer,
1727 .single_const_pointer_to_comptime_int,
1728 .array,
1729 .array_u8,
1730 => return null,
1731
1732 .pointer => return self.cast(Payload.Pointer).?.sentinel,
16551733 .array_sentinel => return self.cast(Payload.ArraySentinel).?.sentinel,
16561734 .array_u8_sentinel_0 => return Value.initTag(.zero),
16571735 };
src-self-hosted/value.zig+3-3
......@@ -301,15 +301,15 @@ pub const Value = extern union {
301301 .comptime_int_type => return out_stream.writeAll("comptime_int"),
302302 .comptime_float_type => return out_stream.writeAll("comptime_float"),
303303 .noreturn_type => return out_stream.writeAll("noreturn"),
304 .null_type => return out_stream.writeAll("@TypeOf(null)"),
305 .undefined_type => return out_stream.writeAll("@TypeOf(undefined)"),
304 .null_type => return out_stream.writeAll("@Type(.Null)"),
305 .undefined_type => return out_stream.writeAll("@Type(.Undefined)"),
306306 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
307307 .fn_void_no_args_type => return out_stream.writeAll("fn() void"),
308308 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
309309 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
310310 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
311311 .const_slice_u8_type => return out_stream.writeAll("[]const u8"),
312 .enum_literal_type => return out_stream.writeAll("@TypeOf(.EnumLiteral)"),
312 .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"),
313313 .anyframe_type => return out_stream.writeAll("anyframe"),
314314
315315 .null_value => return out_stream.writeAll("null"),
src-self-hosted/zir.zig+23-1
......@@ -231,6 +231,10 @@ pub const Inst = struct {
231231 const_slice_type,
232232 /// Create a pointer type with attributes
233233 ptr_type,
234 /// Slice operation `array_ptr[start..end:sentinel]`
235 slice,
236 /// Slice operation with just start `lhs[rhs..]`
237 slice_start,
234238 /// Write a value to a pointer. For loading, see `deref`.
235239 store,
236240 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
......@@ -343,6 +347,7 @@ pub const Inst = struct {
343347 .xor,
344348 .error_union_type,
345349 .merge_error_sets,
350 .slice_start,
346351 => BinOp,
347352
348353 .block,
......@@ -380,6 +385,7 @@ pub const Inst = struct {
380385 .ptr_type => PtrType,
381386 .enum_literal => EnumLiteral,
382387 .error_set => ErrorSet,
388 .slice => Slice,
383389 };
384390 }
385391
......@@ -481,6 +487,8 @@ pub const Inst = struct {
481487 .error_union_type,
482488 .bitnot,
483489 .error_set,
490 .slice,
491 .slice_start,
484492 => false,
485493
486494 .@"break",
......@@ -961,6 +969,20 @@ pub const Inst = struct {
961969 },
962970 kw_args: struct {},
963971 };
972
973 pub const Slice = struct {
974 pub const base_tag = Tag.slice;
975 base: Inst,
976
977 positionals: struct {
978 array_ptr: *Inst,
979 start: *Inst,
980 },
981 kw_args: struct {
982 end: ?*Inst = null,
983 sentinel: ?*Inst = null,
984 },
985 };
964986};
965987
966988pub const ErrorMsg = struct {
......@@ -2574,7 +2596,7 @@ const EmitZIR = struct {
25742596 var len_pl = Value.Payload.Int_u64{ .int = ty.arrayLen() };
25752597 const len = Value.initPayload(&len_pl.base);
25762598
2577 const inst = if (ty.arraySentinel()) |sentinel| blk: {
2599 const inst = if (ty.sentinel()) |sentinel| blk: {
25782600 const inst = try self.arena.allocator.create(Inst.ArrayTypeSentinel);
25792601 inst.* = .{
25802602 .base = .{
src-self-hosted/zir_sema.zig+24
......@@ -132,6 +132,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
132132 .error_union_type => return analyzeInstErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?),
133133 .anyframe_type => return analyzeInstAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?),
134134 .error_set => return analyzeInstErrorSet(mod, scope, old_inst.castTag(.error_set).?),
135 .slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?),
136 .slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?),
135137 }
136138}
137139
......@@ -1172,6 +1174,22 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
11721174 return mod.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
11731175}
11741176
1177fn analyzeInstSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerError!*Inst {
1178 const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
1179 const start = try resolveInst(mod, scope, inst.positionals.start);
1180 const end = if (inst.kw_args.end) |end| try resolveInst(mod, scope, end) else null;
1181 const sentinel = if (inst.kw_args.sentinel) |sentinel| try resolveInst(mod, scope, sentinel) else null;
1182
1183 return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, end, sentinel);
1184}
1185
1186fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1187 const array_ptr = try resolveInst(mod, scope, inst.positionals.lhs);
1188 const start = try resolveInst(mod, scope, inst.positionals.rhs);
1189
1190 return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null);
1191}
1192
11751193fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
11761194 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{});
11771195}
......@@ -1239,6 +1257,12 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn
12391257
12401258 if (casted_lhs.value()) |lhs_val| {
12411259 if (casted_rhs.value()) |rhs_val| {
1260 if (lhs_val.isUndef() or rhs_val.isUndef()) {
1261 return mod.constInst(scope, inst.base.src, .{
1262 .ty = resolved_type,
1263 .val = Value.initTag(.undef),
1264 });
1265 }
12421266 return analyzeInstComptimeOp(mod, scope, scalar_type, inst, lhs_val, rhs_val);
12431267 }
12441268 }
src/analyze.cpp+1-1
......@@ -1810,7 +1810,7 @@ Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) {
18101810ZigType *get_auto_err_set_type(CodeGen *g, ZigFn *fn_entry) {
18111811 ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
18121812 buf_resize(&err_set_type->name, 0);
1813 buf_appendf(&err_set_type->name, "@TypeOf(%s).ReturnType.ErrorSet", buf_ptr(&fn_entry->symbol_name));
1813 buf_appendf(&err_set_type->name, "@typeInfo(@typeInfo(@TypeOf(%s)).Fn.return_type.?).ErrorUnion.error_set", buf_ptr(&fn_entry->symbol_name));
18141814 err_set_type->data.error_set.err_count = 0;
18151815 err_set_type->data.error_set.errors = nullptr;
18161816 err_set_type->data.error_set.infer_fn = fn_entry;
src/ir.cpp+9-162
......@@ -15341,9 +15341,14 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
1534115341 ZigType *array_type = actual_type->data.pointer.child_type;
1534215342 bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0
1534315343 || !actual_type->data.pointer.is_const);
15344
1534415345 if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
1534515346 array_type->data.array.child_type, source_node,
15346 !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
15347 !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk &&
15348 (slice_ptr_type->data.pointer.sentinel == nullptr ||
15349 (array_type->data.array.sentinel != nullptr &&
15350 const_values_equal(ira->codegen, array_type->data.array.sentinel,
15351 slice_ptr_type->data.pointer.sentinel))))
1534715352 {
1534815353 // If the pointers both have ABI align, it works.
1534915354 // Or if the array length is 0, alignment doesn't matter.
......@@ -22830,167 +22835,9 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
2283022835 bool ptr_is_volatile = false;
2283122836 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, const_val,
2283222837 err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22833 } else if (child_type->id == ZigTypeIdInt) {
22834 if (buf_eql_str(field_name, "bit_count")) {
22835 bool ptr_is_const = true;
22836 bool ptr_is_volatile = false;
22837 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22838 create_const_unsigned_negative(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int,
22839 child_type->data.integral.bit_count, false),
22840 ira->codegen->builtin_types.entry_num_lit_int,
22841 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22842 } else if (buf_eql_str(field_name, "is_signed")) {
22843 bool ptr_is_const = true;
22844 bool ptr_is_volatile = false;
22845 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22846 create_const_bool(ira->codegen, child_type->data.integral.is_signed),
22847 ira->codegen->builtin_types.entry_bool,
22848 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22849 } else {
22850 ir_add_error(ira, &field_ptr_instruction->base.base,
22851 buf_sprintf("type '%s' has no member called '%s'",
22852 buf_ptr(&child_type->name), buf_ptr(field_name)));
22853 return ira->codegen->invalid_inst_gen;
22854 }
22855 } else if (child_type->id == ZigTypeIdFloat) {
22856 if (buf_eql_str(field_name, "bit_count")) {
22857 bool ptr_is_const = true;
22858 bool ptr_is_volatile = false;
22859 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22860 create_const_unsigned_negative(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int,
22861 child_type->data.floating.bit_count, false),
22862 ira->codegen->builtin_types.entry_num_lit_int,
22863 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22864 } else {
22865 ir_add_error(ira, &field_ptr_instruction->base.base,
22866 buf_sprintf("type '%s' has no member called '%s'",
22867 buf_ptr(&child_type->name), buf_ptr(field_name)));
22868 return ira->codegen->invalid_inst_gen;
22869 }
22870 } else if (child_type->id == ZigTypeIdPointer) {
22871 if (buf_eql_str(field_name, "Child")) {
22872 bool ptr_is_const = true;
22873 bool ptr_is_volatile = false;
22874 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22875 create_const_type(ira->codegen, child_type->data.pointer.child_type),
22876 ira->codegen->builtin_types.entry_type,
22877 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22878 } else if (buf_eql_str(field_name, "alignment")) {
22879 bool ptr_is_const = true;
22880 bool ptr_is_volatile = false;
22881 if ((err = type_resolve(ira->codegen, child_type->data.pointer.child_type,
22882 ResolveStatusAlignmentKnown)))
22883 {
22884 return ira->codegen->invalid_inst_gen;
22885 }
22886 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22887 create_const_unsigned_negative(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int,
22888 get_ptr_align(ira->codegen, child_type), false),
22889 ira->codegen->builtin_types.entry_num_lit_int,
22890 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22891 } else {
22892 ir_add_error(ira, &field_ptr_instruction->base.base,
22893 buf_sprintf("type '%s' has no member called '%s'",
22894 buf_ptr(&child_type->name), buf_ptr(field_name)));
22895 return ira->codegen->invalid_inst_gen;
22896 }
22897 } else if (child_type->id == ZigTypeIdArray) {
22898 if (buf_eql_str(field_name, "Child")) {
22899 bool ptr_is_const = true;
22900 bool ptr_is_volatile = false;
22901 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22902 create_const_type(ira->codegen, child_type->data.array.child_type),
22903 ira->codegen->builtin_types.entry_type,
22904 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22905 } else if (buf_eql_str(field_name, "len")) {
22906 bool ptr_is_const = true;
22907 bool ptr_is_volatile = false;
22908 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22909 create_const_unsigned_negative(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int,
22910 child_type->data.array.len, false),
22911 ira->codegen->builtin_types.entry_num_lit_int,
22912 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22913 } else {
22914 ir_add_error(ira, &field_ptr_instruction->base.base,
22915 buf_sprintf("type '%s' has no member called '%s'",
22916 buf_ptr(&child_type->name), buf_ptr(field_name)));
22917 return ira->codegen->invalid_inst_gen;
22918 }
22919 } else if (child_type->id == ZigTypeIdErrorUnion) {
22920 if (buf_eql_str(field_name, "Payload")) {
22921 bool ptr_is_const = true;
22922 bool ptr_is_volatile = false;
22923 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22924 create_const_type(ira->codegen, child_type->data.error_union.payload_type),
22925 ira->codegen->builtin_types.entry_type,
22926 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22927 } else if (buf_eql_str(field_name, "ErrorSet")) {
22928 bool ptr_is_const = true;
22929 bool ptr_is_volatile = false;
22930 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22931 create_const_type(ira->codegen, child_type->data.error_union.err_set_type),
22932 ira->codegen->builtin_types.entry_type,
22933 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22934 } else {
22935 ir_add_error(ira, &field_ptr_instruction->base.base,
22936 buf_sprintf("type '%s' has no member called '%s'",
22937 buf_ptr(&child_type->name), buf_ptr(field_name)));
22938 return ira->codegen->invalid_inst_gen;
22939 }
22940 } else if (child_type->id == ZigTypeIdOptional) {
22941 if (buf_eql_str(field_name, "Child")) {
22942 bool ptr_is_const = true;
22943 bool ptr_is_volatile = false;
22944 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22945 create_const_type(ira->codegen, child_type->data.maybe.child_type),
22946 ira->codegen->builtin_types.entry_type,
22947 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22948 } else {
22949 ir_add_error(ira, &field_ptr_instruction->base.base,
22950 buf_sprintf("type '%s' has no member called '%s'",
22951 buf_ptr(&child_type->name), buf_ptr(field_name)));
22952 return ira->codegen->invalid_inst_gen;
22953 }
22954 } else if (child_type->id == ZigTypeIdFn) {
22955 if (buf_eql_str(field_name, "ReturnType")) {
22956 if (child_type->data.fn.fn_type_id.return_type == nullptr) {
22957 // Return type can only ever be null, if the function is generic
22958 assert(child_type->data.fn.is_generic);
22959
22960 ir_add_error(ira, &field_ptr_instruction->base.base,
22961 buf_sprintf("ReturnType has not been resolved because '%s' is generic", buf_ptr(&child_type->name)));
22962 return ira->codegen->invalid_inst_gen;
22963 }
22964
22965 bool ptr_is_const = true;
22966 bool ptr_is_volatile = false;
22967 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22968 create_const_type(ira->codegen, child_type->data.fn.fn_type_id.return_type),
22969 ira->codegen->builtin_types.entry_type,
22970 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22971 } else if (buf_eql_str(field_name, "is_var_args")) {
22972 bool ptr_is_const = true;
22973 bool ptr_is_volatile = false;
22974 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22975 create_const_bool(ira->codegen, child_type->data.fn.fn_type_id.is_var_args),
22976 ira->codegen->builtin_types.entry_bool,
22977 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22978 } else if (buf_eql_str(field_name, "arg_count")) {
22979 bool ptr_is_const = true;
22980 bool ptr_is_volatile = false;
22981 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base,
22982 create_const_usize(ira->codegen, child_type->data.fn.fn_type_id.param_count),
22983 ira->codegen->builtin_types.entry_usize,
22984 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22985 } else {
22986 ir_add_error(ira, &field_ptr_instruction->base.base,
22987 buf_sprintf("type '%s' has no member called '%s'",
22988 buf_ptr(&child_type->name), buf_ptr(field_name)));
22989 return ira->codegen->invalid_inst_gen;
22990 }
2299122838 } else {
2299222839 ir_add_error(ira, &field_ptr_instruction->base.base,
22993 buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name)));
22840 buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
2299422841 return ira->codegen->invalid_inst_gen;
2299522842 }
2299622843 } else if (field_ptr_instruction->initializing) {
......@@ -26747,7 +26594,7 @@ static IrInstGen *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstSrcCmpxch
2674726594
2674826595 if (operand_type->id == ZigTypeIdFloat) {
2674926596 ir_add_error(ira, &instruction->type_value->child->base,
26750 buf_sprintf("expected integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name)));
26597 buf_sprintf("expected bool, integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name)));
2675126598 return ira->codegen->invalid_inst_gen;
2675226599 }
2675326600
......@@ -30402,7 +30249,7 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstGen *op) {
3040230249 return ira->codegen->builtin_types.entry_invalid;
3040330250 if (operand_ptr_type == nullptr) {
3040430251 ir_add_error(ira, &op->base,
30405 buf_sprintf("expected integer, float, enum or pointer type, found '%s'",
30252 buf_sprintf("expected bool, integer, float, enum or pointer type, found '%s'",
3040630253 buf_ptr(&operand_type->name)));
3040730254 return ira->codegen->builtin_types.entry_invalid;
3040830255 }
test/compile_errors.zig+16-17
......@@ -2,6 +2,14 @@ const tests = @import("tests.zig");
22const std = @import("std");
33
44pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.add("slice sentinel mismatch",
6 \\export fn entry() void {
7 \\ const y: [:1]const u8 = &[_:2]u8{ 1, 2 };
8 \\}
9 , &[_][]const u8{
10 "tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8'",
11 });
12
513 cases.add("@Type with undefined",
614 \\comptime {
715 \\ _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } });
......@@ -168,11 +176,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
168176 , &[_][]const u8{
169177 "tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'",
170178 "tmp.zig:1:17: note: function cannot return an error",
171 "tmp.zig:8:5: error: expected type 'void', found '@TypeOf(bar).ReturnType.ErrorSet'",
179 "tmp.zig:8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set'",
172180 "tmp.zig:7:17: note: function cannot return an error",
173 "tmp.zig:11:15: error: expected type 'u32', found '@TypeOf(bar).ReturnType.ErrorSet!u32'",
181 "tmp.zig:11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32'",
174182 "tmp.zig:10:17: note: function cannot return an error",
175 "tmp.zig:15:14: error: expected type 'u32', found '@TypeOf(bar).ReturnType.ErrorSet!u32'",
183 "tmp.zig:15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32'",
176184 "tmp.zig:14:5: note: cannot store an error in type 'u32'",
177185 });
178186
......@@ -891,7 +899,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
891899 \\ _ = @cmpxchgWeak(f32, &x, 1, 2, .SeqCst, .SeqCst);
892900 \\}
893901 , &[_][]const u8{
894 "tmp.zig:3:22: error: expected integer, enum or pointer type, found 'f32'",
902 "tmp.zig:3:22: error: expected bool, integer, enum or pointer type, found 'f32'",
895903 });
896904
897905 cases.add("atomicrmw with float op not .Xchg, .Add or .Sub",
......@@ -1216,7 +1224,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
12161224 \\ };
12171225 \\}
12181226 , &[_][]const u8{
1219 "tmp.zig:11:25: error: expected type 'u32', found '@TypeOf(get_uval).ReturnType.ErrorSet!u32'",
1227 "tmp.zig:11:25: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32'",
12201228 });
12211229
12221230 cases.add("assigning to struct or union fields that are not optionals with a function that returns an optional",
......@@ -1921,7 +1929,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
19211929 \\ const info = @TypeOf(slice).unknown;
19221930 \\}
19231931 , &[_][]const u8{
1924 "tmp.zig:3:32: error: type '[]i32' does not support field access",
1932 "tmp.zig:3:32: error: type 'type' does not support field access",
19251933 });
19261934
19271935 cases.add("peer cast then implicit cast const pointer to mutable C pointer",
......@@ -3534,7 +3542,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
35343542 \\ }
35353543 \\}
35363544 , &[_][]const u8{
3537 "tmp.zig:5:14: error: duplicate switch value: '@TypeOf(foo).ReturnType.ErrorSet.Foo'",
3545 "tmp.zig:5:14: error: duplicate switch value: '@typeInfo(@typeInfo(@TypeOf(foo)).Fn.return_type.?).ErrorUnion.error_set.Foo'",
35383546 "tmp.zig:3:14: note: other value is here",
35393547 });
35403548
......@@ -3666,7 +3674,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
36663674 \\ try foo();
36673675 \\}
36683676 , &[_][]const u8{
3669 "tmp.zig:5:5: error: cannot resolve inferred error set '@TypeOf(foo).ReturnType.ErrorSet': function 'foo' not fully analyzed yet",
3677 "tmp.zig:5:5: error: cannot resolve inferred error set '@typeInfo(@typeInfo(@TypeOf(foo)).Fn.return_type.?).ErrorUnion.error_set': function 'foo' not fully analyzed yet",
36703678 });
36713679
36723680 cases.add("implicit cast of error set not a subset",
......@@ -7198,15 +7206,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
71987206 "tmp.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set",
71997207 });
72007208
7201 cases.add("getting return type of generic function",
7202 \\fn generic(a: anytype) void {}
7203 \\comptime {
7204 \\ _ = @TypeOf(generic).ReturnType;
7205 \\}
7206 , &[_][]const u8{
7207 "tmp.zig:3:25: error: ReturnType has not been resolved because 'fn(anytype) anytype' is generic",
7208 });
7209
72107209 cases.add("unsupported modifier at start of asm output constraint",
72117210 \\export fn foo() void {
72127211 \\ var bar: u32 = 3;
test/stage1/behavior/align.zig+1-1
......@@ -5,7 +5,7 @@ const builtin = @import("builtin");
55var foo: u8 align(4) = 100;
66
77test "global variable alignment" {
8 comptime expect(@TypeOf(&foo).alignment == 4);
8 comptime expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
99 comptime expect(@TypeOf(&foo) == *align(4) u8);
1010 {
1111 const slice = @as(*[1]u8, &foo)[0..];
test/stage1/behavior/array.zig-10
......@@ -136,16 +136,6 @@ test "array literal with specified size" {
136136 expect(array[1] == 2);
137137}
138138
139test "array child property" {
140 var x: [5]i32 = undefined;
141 expect(@TypeOf(x).Child == i32);
142}
143
144test "array len property" {
145 var x: [5]i32 = undefined;
146 expect(@TypeOf(x).len == 5);
147}
148
149139test "array len field" {
150140 var arr = [4]u8{ 0, 0, 0, 0 };
151141 var ptr = &arr;
test/stage1/behavior/async_fn.zig+3-3
......@@ -331,7 +331,7 @@ test "async fn with inferred error set" {
331331 fn doTheTest() void {
332332 var frame: [1]@Frame(middle) = undefined;
333333 var fn_ptr = middle;
334 var result: @TypeOf(fn_ptr).ReturnType.ErrorSet!void = undefined;
334 var result: @typeInfo(@typeInfo(@TypeOf(fn_ptr)).Fn.return_type.?).ErrorUnion.error_set!void = undefined;
335335 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, fn_ptr, .{});
336336 resume global_frame;
337337 std.testing.expectError(error.Fail, result);
......@@ -950,7 +950,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" {
950950
951951 fn doTheTest() void {
952952 var frame: [1]@Frame(middle) = undefined;
953 var result: @TypeOf(middle).ReturnType.ErrorSet!void = undefined;
953 var result: @typeInfo(@typeInfo(@TypeOf(middle)).Fn.return_type.?).ErrorUnion.error_set!void = undefined;
954954 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, middle, .{});
955955 resume global_frame;
956956 std.testing.expectError(error.Fail, result);
......@@ -1018,7 +1018,7 @@ test "@TypeOf an async function call of generic fn with error union type" {
10181018 const S = struct {
10191019 fn func(comptime x: anytype) anyerror!i32 {
10201020 const T = @TypeOf(async func(x));
1021 comptime expect(T == @TypeOf(@frame()).Child);
1021 comptime expect(T == @typeInfo(@TypeOf(@frame())).Pointer.child);
10221022 return undefined;
10231023 }
10241024 };
test/stage1/behavior/bit_shifting.zig+7-5
......@@ -2,16 +2,18 @@ const std = @import("std");
22const expect = std.testing.expect;
33
44fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime V: type) type {
5 expect(Key == std.meta.Int(false, Key.bit_count));
6 expect(Key.bit_count >= mask_bit_count);
5 const key_bits = @typeInfo(Key).Int.bits;
6 expect(Key == std.meta.Int(false, key_bits));
7 expect(key_bits >= mask_bit_count);
8 const shard_key_bits = mask_bit_count;
79 const ShardKey = std.meta.Int(false, mask_bit_count);
8 const shift_amount = Key.bit_count - ShardKey.bit_count;
10 const shift_amount = key_bits - shard_key_bits;
911 return struct {
1012 const Self = @This();
11 shards: [1 << ShardKey.bit_count]?*Node,
13 shards: [1 << shard_key_bits]?*Node,
1214
1315 pub fn create() Self {
14 return Self{ .shards = [_]?*Node{null} ** (1 << ShardKey.bit_count) };
16 return Self{ .shards = [_]?*Node{null} ** (1 << shard_key_bits) };
1517 }
1618
1719 fn getShardKey(key: Key) ShardKey {
test/stage1/behavior/bugs/5487.zig+2-2
......@@ -3,8 +3,8 @@ const io = @import("std").io;
33pub fn write(_: void, bytes: []const u8) !usize {
44 return 0;
55}
6pub fn outStream() io.OutStream(void, @TypeOf(write).ReturnType.ErrorSet, write) {
7 return io.OutStream(void, @TypeOf(write).ReturnType.ErrorSet, write){ .context = {} };
6pub fn outStream() io.OutStream(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_type.?).ErrorUnion.error_set, write) {
7 return io.OutStream(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_type.?).ErrorUnion.error_set, write){ .context = {} };
88}
99
1010test "crash" {
test/stage1/behavior/error.zig+2-2
......@@ -84,8 +84,8 @@ fn testErrorUnionType() void {
8484 const x: anyerror!i32 = 1234;
8585 if (x) |value| expect(value == 1234) else |_| unreachable;
8686 expect(@typeInfo(@TypeOf(x)) == .ErrorUnion);
87 expect(@typeInfo(@TypeOf(x).ErrorSet) == .ErrorSet);
88 expect(@TypeOf(x).ErrorSet == anyerror);
87 expect(@typeInfo(@typeInfo(@TypeOf(x)).ErrorUnion.error_set) == .ErrorSet);
88 expect(@typeInfo(@TypeOf(x)).ErrorUnion.error_set == anyerror);
8989}
9090
9191test "error set type" {
test/stage1/behavior/misc.zig-10
......@@ -24,12 +24,6 @@ test "call disabled extern fn" {
2424 disabledExternFn();
2525}
2626
27test "floating point primitive bit counts" {
28 expect(f16.bit_count == 16);
29 expect(f32.bit_count == 32);
30 expect(f64.bit_count == 64);
31}
32
3327test "short circuit" {
3428 testShortCircuit(false, true);
3529 comptime testShortCircuit(false, true);
......@@ -577,10 +571,6 @@ test "slice string literal has correct type" {
577571 comptime expect(@TypeOf(array[runtime_zero..]) == []const i32);
578572}
579573
580test "pointer child field" {
581 expect((*u32).Child == u32);
582}
583
584574test "struct inside function" {
585575 testStructInFn();
586576 comptime testStructInFn();
test/stage1/behavior/reflection.zig+7-15
......@@ -2,23 +2,15 @@ const expect = @import("std").testing.expect;
22const mem = @import("std").mem;
33const reflection = @This();
44
5test "reflection: array, pointer, optional, error union type child" {
6 comptime {
7 expect(([10]u8).Child == u8);
8 expect((*u8).Child == u8);
9 expect((anyerror!u8).Payload == u8);
10 expect((?u8).Child == u8);
11 }
12}
13
145test "reflection: function return type, var args, and param types" {
156 comptime {
16 expect(@TypeOf(dummy).ReturnType == i32);
17 expect(!@TypeOf(dummy).is_var_args);
18 expect(@TypeOf(dummy).arg_count == 3);
19 expect(@typeInfo(@TypeOf(dummy)).Fn.args[0].arg_type.? == bool);
20 expect(@typeInfo(@TypeOf(dummy)).Fn.args[1].arg_type.? == i32);
21 expect(@typeInfo(@TypeOf(dummy)).Fn.args[2].arg_type.? == f32);
7 const info = @typeInfo(@TypeOf(dummy)).Fn;
8 expect(info.return_type.? == i32);
9 expect(!info.is_var_args);
10 expect(info.args.len == 3);
11 expect(info.args[0].arg_type.? == bool);
12 expect(info.args[1].arg_type.? == i32);
13 expect(info.args[2].arg_type.? == f32);
2214 }
2315}
2416