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
signaturebadge-question-mark 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 {...@@ -123,7 +123,13 @@ pub fn build(b: *Builder) !void {
123 .source_dir = "lib",123 .source_dir = "lib",
124 .install_dir = .Lib,124 .install_dir = .Lib,
125 .install_subdir = "zig",125 .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 },
127 });133 });
128134
129 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");135 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" {...@@ -2156,7 +2156,7 @@ test "pointer casting" {
21562156
2157test "pointer child type" {2157test "pointer child type" {
2158 // pointer types have a `child` field which tells you the type they point to.2158 // 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);
2160}2160}
2161 {#code_end#}2161 {#code_end#}
2162 {#header_open|Alignment#}2162 {#header_open|Alignment#}
...@@ -2184,7 +2184,7 @@ test "variable alignment" {...@@ -2184,7 +2184,7 @@ test "variable alignment" {
2184 assert(@TypeOf(&x) == *i32);2184 assert(@TypeOf(&x) == *i32);
2185 assert(*i32 == *align(align_of_i32) i32);2185 assert(*i32 == *align(align_of_i32) i32);
2186 if (std.Target.current.cpu.arch == .x86_64) {2186 if (std.Target.current.cpu.arch == .x86_64) {
2187 assert((*i32).alignment == 4);2187 assert(@typeInfo(*i32).Pointer.alignment == 4);
2188 }2188 }
2189}2189}
2190 {#code_end#}2190 {#code_end#}
...@@ -2202,7 +2202,7 @@ const assert = @import("std").debug.assert;...@@ -2202,7 +2202,7 @@ const assert = @import("std").debug.assert;
2202var foo: u8 align(4) = 100;2202var foo: u8 align(4) = 100;
22032203
2204test "global variable alignment" {2204test "global variable alignment" {
2205 assert(@TypeOf(&foo).alignment == 4);2205 assert(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
2206 assert(@TypeOf(&foo) == *align(4) u8);2206 assert(@TypeOf(&foo) == *align(4) u8);
2207 const as_pointer_to_array: *[1]u8 = &foo;2207 const as_pointer_to_array: *[1]u8 = &foo;
2208 const as_slice: []u8 = as_pointer_to_array;2208 const as_slice: []u8 = as_pointer_to_array;
...@@ -4310,8 +4310,8 @@ test "fn type inference" {...@@ -4310,8 +4310,8 @@ test "fn type inference" {
4310const assert = @import("std").debug.assert;4310const assert = @import("std").debug.assert;
43114311
4312test "fn reflection" {4312test "fn reflection" {
4313 assert(@TypeOf(assert).ReturnType == void);4313 assert(@typeInfo(@TypeOf(assert)).Fn.return_type.? == void);
4314 assert(@TypeOf(assert).is_var_args == false);4314 assert(@typeInfo(@TypeOf(assert)).Fn.is_var_args == false);
4315}4315}
4316 {#code_end#}4316 {#code_end#}
4317 {#header_close#}4317 {#header_close#}
...@@ -4611,10 +4611,10 @@ test "error union" {...@@ -4611,10 +4611,10 @@ test "error union" {
4611 foo = error.SomeError;4611 foo = error.SomeError;
46124612
4613 // Use compile-time reflection to access the payload type of an error union:4613 // 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
4616 // Use compile-time reflection to access the error set type of an error union:4616 // 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);
4618}4618}
4619 {#code_end#}4619 {#code_end#}
4620 {#header_open|Merging Error Sets#}4620 {#header_open|Merging Error Sets#}
...@@ -4991,7 +4991,7 @@ test "optional type" {...@@ -4991,7 +4991,7 @@ test "optional type" {
4991 foo = 1234;4991 foo = 1234;
49924992
4993 // Use compile-time reflection to access the child type of the optional:4993 // 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);
4995}4995}
4996 {#code_end#}4996 {#code_end#}
4997 {#header_close#}4997 {#header_close#}
...@@ -6889,7 +6889,7 @@ fn func(y: *i32) void {...@@ -6889,7 +6889,7 @@ fn func(y: *i32) void {
6889 This builtin function atomically dereferences a pointer and returns the value.6889 This builtin function atomically dereferences a pointer and returns the value.
6890 </p>6890 </p>
6891 <p>6891 <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,
6893 an integer or an enum.6893 an integer or an enum.
6894 </p>6894 </p>
6895 {#header_close#}6895 {#header_close#}
...@@ -6899,7 +6899,7 @@ fn func(y: *i32) void {...@@ -6899,7 +6899,7 @@ fn func(y: *i32) void {
6899 This builtin function atomically modifies memory and then returns the previous value.6899 This builtin function atomically modifies memory and then returns the previous value.
6900 </p>6900 </p>
6901 <p>6901 <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,
6903 an integer or an enum.6903 an integer or an enum.
6904 </p>6904 </p>
6905 <p>6905 <p>
...@@ -6925,7 +6925,7 @@ fn func(y: *i32) void {...@@ -6925,7 +6925,7 @@ fn func(y: *i32) void {
6925 This builtin function atomically stores a value.6925 This builtin function atomically stores a value.
6926 </p>6926 </p>
6927 <p>6927 <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,
6929 an integer or an enum.6929 an integer or an enum.
6930 </p>6930 </p>
6931 {#header_close#}6931 {#header_close#}
...@@ -7208,10 +7208,10 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_v...@@ -7208,10 +7208,10 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_v
7208 more efficiently in machine instructions.7208 more efficiently in machine instructions.
7209 </p>7209 </p>
7210 <p>7210 <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,
7212 an integer or an enum.7212 an integer or an enum.
7213 </p>7213 </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>
7215 {#see_also|Compile Variables|cmpxchgWeak#}7215 {#see_also|Compile Variables|cmpxchgWeak#}
7216 {#header_close#}7216 {#header_close#}
7217 {#header_open|@cmpxchgWeak#}7217 {#header_open|@cmpxchgWeak#}
...@@ -7237,10 +7237,10 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -7237,10 +7237,10 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
7237 However if you need a stronger guarantee, use {#link|@cmpxchgStrong#}.7237 However if you need a stronger guarantee, use {#link|@cmpxchgStrong#}.
7238 </p>7238 </p>
7239 <p>7239 <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,
7241 an integer or an enum.7241 an integer or an enum.
7242 </p>7242 </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>
7244 {#see_also|Compile Variables|cmpxchgStrong#}7244 {#see_also|Compile Variables|cmpxchgStrong#}
7245 {#header_close#}7245 {#header_close#}
72467246
lib/std/array_list.zig+10-2
...@@ -46,7 +46,11 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -46,7 +46,11 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
46 /// Deinitialize with `deinit` or use `toOwnedSlice`.46 /// Deinitialize with `deinit` or use `toOwnedSlice`.
47 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {47 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
48 var self = Self.init(allocator);48 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
50 return self;54 return self;
51 }55 }
5256
...@@ -366,7 +370,11 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -366,7 +370,11 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
366 /// Deinitialize with `deinit` or use `toOwnedSlice`.370 /// Deinitialize with `deinit` or use `toOwnedSlice`.
367 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {371 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
368 var self = Self{};372 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
370 return self;378 return self;
371 }379 }
372380
lib/std/c.zig+5
...@@ -330,3 +330,8 @@ pub const FILE = @Type(.Opaque);...@@ -330,3 +330,8 @@ pub const FILE = @Type(.Opaque);
330pub extern "c" fn dlopen(path: [*:0]const u8, mode: c_int) ?*c_void;330pub extern "c" fn dlopen(path: [*:0]const u8, mode: c_int) ?*c_void;
331pub extern "c" fn dlclose(handle: *c_void) c_int;331pub extern "c" fn dlclose(handle: *c_void) c_int;
332pub extern "c" fn dlsym(handle: ?*c_void, symbol: [*:0]const u8) ?*c_void;332pub 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 {...@@ -44,10 +44,10 @@ pub const ChildProcess = struct {
44 stderr_behavior: StdIo,44 stderr_behavior: StdIo,
4545
46 /// Set to change the user id when spawning the child process.46 /// 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
49 /// Set to change the group id when spawning the child process.49 /// 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
52 /// Set to change the current working directory when spawning the child process.52 /// Set to change the current working directory when spawning the child process.
53 cwd: ?[]const u8,53 cwd: ?[]const u8,
...@@ -275,9 +275,7 @@ pub const ChildProcess = struct {...@@ -275,9 +275,7 @@ pub const ChildProcess = struct {
275 }275 }
276276
277 fn handleWaitResult(self: *ChildProcess, status: u32) void {277 fn handleWaitResult(self: *ChildProcess, status: u32) void {
278 // TODO https://github.com/ziglang/zig/issues/3190278 self.term = self.cleanupAfterWait(status);
279 var term = self.cleanupAfterWait(status);
280 self.term = term;
281 }279 }
282280
283 fn cleanupStreams(self: *ChildProcess) void {281 fn cleanupStreams(self: *ChildProcess) void {
...@@ -487,8 +485,8 @@ pub const ChildProcess = struct {...@@ -487,8 +485,8 @@ pub const ChildProcess = struct {
487 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);485 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
488486
489 const nul_handle = if (any_ignore)487 const nul_handle = if (any_ignore)
490 windows.OpenFile(&[_]u16{ 'N', 'U', 'L' }, .{488 // "\Device\Null" or "\??\NUL"
491 .dir = std.fs.cwd().fd,489 windows.OpenFile(&[_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }, .{
492 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,490 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
493 .share_access = windows.FILE_SHARE_READ,491 .share_access = windows.FILE_SHARE_READ,
494 .creation = windows.OPEN_EXISTING,492 .creation = windows.OPEN_EXISTING,
lib/std/coff.zig+66
...@@ -18,11 +18,77 @@ const IMAGE_FILE_MACHINE_I386 = 0x014c;...@@ -18,11 +18,77 @@ const IMAGE_FILE_MACHINE_I386 = 0x014c;
18const IMAGE_FILE_MACHINE_IA64 = 0x0200;18const IMAGE_FILE_MACHINE_IA64 = 0x0200;
19const IMAGE_FILE_MACHINE_AMD64 = 0x8664;19const 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
21// OptionalHeader.magic values73// OptionalHeader.magic values
22// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx74// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
23const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;75const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
24const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;76const 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
26const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;92const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
27const IMAGE_DEBUG_TYPE_CODEVIEW = 2;93const IMAGE_DEBUG_TYPE_CODEVIEW = 2;
28const DEBUG_DIRECTORY = 6;94const 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;...@@ -9,10 +9,10 @@ const testing = std.testing;
9/// Read a single unsigned LEB128 value from the given reader as type T,9/// Read a single unsigned LEB128 value from the given reader as type T,
10/// or error.Overflow if the value cannot fit.10/// or error.Overflow if the value cannot fit.
11pub fn readULEB128(comptime T: type, reader: anytype) !T {11pub 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;
13 const ShiftT = std.math.Log2Int(U);13 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
17 var value = @as(U, 0);17 var value = @as(U, 0);
18 var group = @as(ShiftT, 0);18 var group = @as(ShiftT, 0);
...@@ -40,7 +40,7 @@ pub fn readULEB128(comptime T: type, reader: anytype) !T {...@@ -40,7 +40,7 @@ pub fn readULEB128(comptime T: type, reader: anytype) !T {
40/// Write a single unsigned integer as unsigned LEB128 to the given writer.40/// Write a single unsigned integer as unsigned LEB128 to the given writer.
41pub fn writeULEB128(writer: anytype, uint_value: anytype) !void {41pub fn writeULEB128(writer: anytype, uint_value: anytype) !void {
42 const T = @TypeOf(uint_value);42 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;
44 var value = @intCast(U, uint_value);44 var value = @intCast(U, uint_value);
4545
46 while (true) {46 while (true) {
...@@ -68,7 +68,7 @@ pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T {...@@ -68,7 +68,7 @@ pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T {
68/// returning the number of bytes written.68/// returning the number of bytes written.
69pub fn writeULEB128Mem(ptr: []u8, uint_value: anytype) !usize {69pub fn writeULEB128Mem(ptr: []u8, uint_value: anytype) !usize {
70 const T = @TypeOf(uint_value);70 const T = @TypeOf(uint_value);
71 const max_group = (T.bit_count + 6) / 7;71 const max_group = (@typeInfo(T).Int.bits + 6) / 7;
72 var buf = std.io.fixedBufferStream(ptr);72 var buf = std.io.fixedBufferStream(ptr);
73 try writeULEB128(buf.writer(), uint_value);73 try writeULEB128(buf.writer(), uint_value);
74 return buf.pos;74 return buf.pos;
...@@ -77,11 +77,11 @@ pub fn writeULEB128Mem(ptr: []u8, uint_value: anytype) !usize {...@@ -77,11 +77,11 @@ pub fn writeULEB128Mem(ptr: []u8, uint_value: anytype) !usize {
77/// Read a single signed LEB128 value from the given reader as type T,77/// Read a single signed LEB128 value from the given reader as type T,
78/// or error.Overflow if the value cannot fit.78/// or error.Overflow if the value cannot fit.
79pub fn readILEB128(comptime T: type, reader: anytype) !T {79pub fn readILEB128(comptime T: type, reader: anytype) !T {
80 const S = if (T.bit_count < 8) i8 else T;80 const S = if (@typeInfo(T).Int.bits < 8) i8 else T;
81 const U = std.meta.Int(false, S.bit_count);81 const U = std.meta.Int(false, @typeInfo(S).Int.bits);
82 const ShiftU = std.math.Log2Int(U);82 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
86 var value = @as(U, 0);86 var value = @as(U, 0);
87 var group = @as(ShiftU, 0);87 var group = @as(ShiftU, 0);
...@@ -97,7 +97,7 @@ pub fn readILEB128(comptime T: type, reader: anytype) !T {...@@ -97,7 +97,7 @@ pub fn readILEB128(comptime T: type, reader: anytype) !T {
97 if (@bitCast(S, temp) >= 0) return error.Overflow;97 if (@bitCast(S, temp) >= 0) return error.Overflow;
9898
99 // and all the overflowed bits are 199 // 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));
101 const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift;101 const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift;
102 if (remaining_bits != -1) return error.Overflow;102 if (remaining_bits != -1) return error.Overflow;
103 }103 }
...@@ -127,8 +127,8 @@ pub fn readILEB128(comptime T: type, reader: anytype) !T {...@@ -127,8 +127,8 @@ pub fn readILEB128(comptime T: type, reader: anytype) !T {
127/// Write a single signed integer as signed LEB128 to the given writer.127/// Write a single signed integer as signed LEB128 to the given writer.
128pub fn writeILEB128(writer: anytype, int_value: anytype) !void {128pub fn writeILEB128(writer: anytype, int_value: anytype) !void {
129 const T = @TypeOf(int_value);129 const T = @TypeOf(int_value);
130 const S = if (T.bit_count < 8) i8 else T;130 const S = if (@typeInfo(T).Int.bits < 8) i8 else T;
131 const U = std.meta.Int(false, S.bit_count);131 const U = std.meta.Int(false, @typeInfo(S).Int.bits);
132132
133 var value = @intCast(S, int_value);133 var value = @intCast(S, int_value);
134134
...@@ -173,7 +173,7 @@ pub fn writeILEB128Mem(ptr: []u8, int_value: anytype) !usize {...@@ -173,7 +173,7 @@ pub fn writeILEB128Mem(ptr: []u8, int_value: anytype) !usize {
173/// different value without shifting all the following code.173/// different value without shifting all the following code.
174pub fn writeUnsignedFixed(comptime l: usize, ptr: *[l]u8, int: std.meta.Int(false, l * 7)) void {174pub fn writeUnsignedFixed(comptime l: usize, ptr: *[l]u8, int: std.meta.Int(false, l * 7)) void {
175 const T = @TypeOf(int);175 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;
177 var value = @intCast(U, int);177 var value = @intCast(U, int);
178178
179 comptime var i = 0;179 comptime var i = 0;
...@@ -346,28 +346,29 @@ test "deserialize unsigned LEB128" {...@@ -346,28 +346,29 @@ test "deserialize unsigned LEB128" {
346346
347fn test_write_leb128(value: anytype) !void {347fn test_write_leb128(value: anytype) !void {
348 const T = @TypeOf(value);348 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 writeStream = if (t_signed) writeILEB128 else writeULEB128;
351 const writeMem = if (T.is_signed) writeILEB128Mem else writeULEB128Mem;352 const writeMem = if (t_signed) writeILEB128Mem else writeULEB128Mem;
352 const readStream = if (T.is_signed) readILEB128 else readULEB128;353 const readStream = if (t_signed) readILEB128 else readULEB128;
353 const readMem = if (T.is_signed) readILEB128Mem else readULEB128Mem;354 const readMem = if (t_signed) readILEB128Mem else readULEB128Mem;
354355
355 // decode to a larger bit size too, to ensure sign extension356 // decode to a larger bit size too, to ensure sign extension
356 // is working as expected357 // is working as expected
357 const larger_type_bits = ((T.bit_count + 8) / 8) * 8;358 const larger_type_bits = ((@typeInfo(T).Int.bits + 8) / 8) * 8;
358 const B = std.meta.Int(T.is_signed, larger_type_bits);359 const B = std.meta.Int(t_signed, larger_type_bits);
359360
360 const bytes_needed = bn: {361 const bytes_needed = bn: {
361 const S = std.meta.Int(T.is_signed, @sizeOf(T) * 8);362 const S = std.meta.Int(t_signed, @sizeOf(T) * 8);
362 if (T.bit_count <= 7) break :bn @as(u16, 1);363 if (@typeInfo(T).Int.bits <= 7) break :bn @as(u16, 1);
363364
364 const unused_bits = if (value < 0) @clz(T, ~value) else @clz(T, value);365 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);
366 if (used_bits <= 7) break :bn @as(u16, 1);367 if (used_bits <= 7) break :bn @as(u16, 1);
367 break :bn ((used_bits + 6) / 7);368 break :bn ((used_bits + 6) / 7);
368 };369 };
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
372 var buf: [max_groups]u8 = undefined;373 var buf: [max_groups]u8 = undefined;
373 var fbs = std.io.fixedBufferStream(&buf);374 var fbs = std.io.fixedBufferStream(&buf);
...@@ -414,7 +415,7 @@ test "serialize unsigned LEB128" {...@@ -414,7 +415,7 @@ test "serialize unsigned LEB128" {
414 const T = std.meta.Int(false, t);415 const T = std.meta.Int(false, t);
415 const min = std.math.minInt(T);416 const min = std.math.minInt(T);
416 const max = std.math.maxInt(T);417 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
419 while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i));420 while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i));
420 }421 }
...@@ -432,7 +433,7 @@ test "serialize signed LEB128" {...@@ -432,7 +433,7 @@ test "serialize signed LEB128" {
432 const T = std.meta.Int(true, t);433 const T = std.meta.Int(true, t);
433 const min = std.math.minInt(T);434 const min = std.math.minInt(T);
434 const max = std.math.maxInt(T);435 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
437 while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i));438 while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i));
438 }439 }
lib/std/fmt.zig+30-10
...@@ -66,6 +66,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool {...@@ -66,6 +66,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
66/// - output numeric value in hexadecimal notation66/// - output numeric value in hexadecimal notation
67/// - `s`: print a pointer-to-many as a c-string, use zero-termination67/// - `s`: print a pointer-to-many as a c-string, use zero-termination
68/// - `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.68/// - `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
69/// - `e`: output floating point value in scientific notation70/// - `e`: output floating point value in scientific notation
70/// - `d`: output numeric value in decimal notation71/// - `d`: output numeric value in decimal notation
71/// - `b`: output integer value in binary notation72/// - `b`: output integer value in binary notation
...@@ -81,6 +82,8 @@ fn peekIsAlign(comptime fmt: []const u8) bool {...@@ -81,6 +82,8 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
81/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type.82/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type.
82///83///
83/// A user type may be a `struct`, `vector`, `union` or `enum` type.84/// 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 `}}`.
84pub fn format(87pub fn format(
85 writer: anytype,88 writer: anytype,
86 comptime fmt: []const u8,89 comptime fmt: []const u8,
...@@ -90,7 +93,7 @@ pub fn format(...@@ -90,7 +93,7 @@ pub fn format(
90 if (@typeInfo(@TypeOf(args)) != .Struct) {93 if (@typeInfo(@TypeOf(args)) != .Struct) {
91 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));94 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
92 }95 }
93 if (args.len > ArgSetType.bit_count) {96 if (args.len > @typeInfo(ArgSetType).Int.bits) {
94 @compileError("32 arguments max are supported per format call");97 @compileError("32 arguments max are supported per format call");
95 }98 }
9699
...@@ -324,7 +327,7 @@ pub fn formatType(...@@ -324,7 +327,7 @@ pub fn formatType(
324 max_depth: usize,327 max_depth: usize,
325) @TypeOf(writer).Error!void {328) @TypeOf(writer).Error!void {
326 if (comptime std.mem.eql(u8, fmt, "*")) {329 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));
328 try writer.writeAll("@");331 try writer.writeAll("@");
329 try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);332 try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
330 return;333 return;
...@@ -429,12 +432,12 @@ pub fn formatType(...@@ -429,12 +432,12 @@ pub fn formatType(
429 if (info.child == u8) {432 if (info.child == u8) {
430 return formatText(value, fmt, options, writer);433 return formatText(value, fmt, options, writer);
431 }434 }
432 return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });435 return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) });
433 },436 },
434 .Enum, .Union, .Struct => {437 .Enum, .Union, .Struct => {
435 return formatType(value.*, fmt, options, writer, max_depth);438 return formatType(value.*, fmt, options, writer, max_depth);
436 },439 },
437 else => return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }),440 else => return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) }),
438 },441 },
439 .Many, .C => {442 .Many, .C => {
440 if (ptr_info.sentinel) |sentinel| {443 if (ptr_info.sentinel) |sentinel| {
...@@ -445,7 +448,7 @@ pub fn formatType(...@@ -445,7 +448,7 @@ pub fn formatType(
445 return formatText(mem.span(value), fmt, options, writer);448 return formatText(mem.span(value), fmt, options, writer);
446 }449 }
447 }450 }
448 return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });451 return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) });
449 },452 },
450 .Slice => {453 .Slice => {
451 if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {454 if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {
...@@ -535,7 +538,7 @@ pub fn formatIntValue(...@@ -535,7 +538,7 @@ pub fn formatIntValue(
535 radix = 10;538 radix = 10;
536 uppercase = false;539 uppercase = false;
537 } else if (comptime std.mem.eql(u8, fmt, "c")) {540 } 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) {
539 return formatAsciiChar(@as(u8, int_value), options, writer);542 return formatAsciiChar(@as(u8, int_value), options, writer);
540 } else {543 } else {
541 @compileError("Cannot print integer that is larger than 8 bits as a ascii");544 @compileError("Cannot print integer that is larger than 8 bits as a ascii");
...@@ -599,6 +602,16 @@ pub fn formatText(...@@ -599,6 +602,16 @@ pub fn formatText(
599 try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, writer);602 try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, writer);
600 }603 }
601 return;604 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;
602 } else {615 } else {
603 @compileError("Unknown format string: '" ++ fmt ++ "'");616 @compileError("Unknown format string: '" ++ fmt ++ "'");
604 }617 }
...@@ -934,7 +947,7 @@ pub fn formatInt(...@@ -934,7 +947,7 @@ pub fn formatInt(
934 } else947 } else
935 value;948 value;
936949
937 if (@TypeOf(int_value).is_signed) {950 if (@typeInfo(@TypeOf(int_value)).Int.is_signed) {
938 return formatIntSigned(int_value, base, uppercase, options, writer);951 return formatIntSigned(int_value, base, uppercase, options, writer);
939 } else {952 } else {
940 return formatIntUnsigned(int_value, base, uppercase, options, writer);953 return formatIntUnsigned(int_value, base, uppercase, options, writer);
...@@ -976,9 +989,10 @@ fn formatIntUnsigned(...@@ -976,9 +989,10 @@ fn formatIntUnsigned(
976 writer: anytype,989 writer: anytype,
977) !void {990) !void {
978 assert(base >= 2);991 assert(base >= 2);
979 var buf: [math.max(@TypeOf(value).bit_count, 1)]u8 = undefined;992 const value_info = @typeInfo(@TypeOf(value)).Int;
980 const min_int_bits = comptime math.max(@TypeOf(value).bit_count, @TypeOf(base).bit_count);993 var buf: [math.max(value_info.bits, 1)]u8 = undefined;
981 const MinInt = std.meta.Int(@TypeOf(value).is_signed, min_int_bits);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);
982 var a: MinInt = value;996 var a: MinInt = value;
983 var index: usize = buf.len;997 var index: usize = buf.len;
984998
...@@ -1319,6 +1333,12 @@ test "slice" {...@@ -1319,6 +1333,12 @@ test "slice" {
1319 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});1333 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});
1320}1334}
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
1322test "pointer" {1342test "pointer" {
1323 {1343 {
1324 const value = @intToPtr(*align(1) i32, 0xdeadbeef);1344 const value = @intToPtr(*align(1) i32, 0xdeadbeef);
lib/std/fmt/parse_float.zig+5-2
...@@ -37,7 +37,9 @@...@@ -37,7 +37,9 @@
37const std = @import("../std.zig");37const std = @import("../std.zig");
38const ascii = std.ascii;38const 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
42const f64_plus_zero: u64 = 0x0000000000000000;44const f64_plus_zero: u64 = 0x0000000000000000;
43const f64_minus_zero: u64 = 0x8000000000000000;45const f64_minus_zero: u64 = 0x8000000000000000;
...@@ -372,7 +374,7 @@ test "fmt.parseFloat" {...@@ -372,7 +374,7 @@ test "fmt.parseFloat" {
372 const epsilon = 1e-7;374 const epsilon = 1e-7;
373375
374 inline for ([_]type{ f16, f32, f64, f128 }) |T| {376 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
377 testing.expectError(error.InvalidCharacter, parseFloat(T, ""));379 testing.expectError(error.InvalidCharacter, parseFloat(T, ""));
378 testing.expectError(error.InvalidCharacter, parseFloat(T, " 1"));380 testing.expectError(error.InvalidCharacter, parseFloat(T, " 1"));
...@@ -409,6 +411,7 @@ test "fmt.parseFloat" {...@@ -409,6 +411,7 @@ test "fmt.parseFloat" {
409 expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon));411 expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon));
410 expect(approxEq(T, try parseFloat(T, "-123142.1124"), @as(T, -123142.1124), epsilon));412 expect(approxEq(T, try parseFloat(T, "-123142.1124"), @as(T, -123142.1124), epsilon));
411 expect(approxEq(T, try parseFloat(T, "0.7062146892655368"), @as(T, 0.7062146892655368), epsilon));413 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));
412 }415 }
413 }416 }
414}417}
lib/std/fs.zig+9-3
...@@ -1437,26 +1437,32 @@ pub const Dir = struct {...@@ -1437,26 +1437,32 @@ pub const Dir = struct {
1437 /// On success, caller owns returned buffer.1437 /// On success, caller owns returned buffer.
1438 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.1438 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
1439 pub fn readFileAlloc(self: Dir, allocator: *mem.Allocator, file_path: []const u8, max_bytes: usize) ![]u8 {1439 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);
1441 }1441 }
14421442
1443 /// On success, caller owns returned buffer.1443 /// On success, caller owns returned buffer.
1444 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.1444 /// 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.
1445 /// Allows specifying alignment and a sentinel value.1447 /// Allows specifying alignment and a sentinel value.
1446 pub fn readFileAllocOptions(1448 pub fn readFileAllocOptions(
1447 self: Dir,1449 self: Dir,
1448 allocator: *mem.Allocator,1450 allocator: *mem.Allocator,
1449 file_path: []const u8,1451 file_path: []const u8,
1450 max_bytes: usize,1452 max_bytes: usize,
1453 size_hint: ?usize,
1451 comptime alignment: u29,1454 comptime alignment: u29,
1452 comptime optional_sentinel: ?u8,1455 comptime optional_sentinel: ?u8,
1453 ) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {1456 ) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {
1454 var file = try self.openFile(file_path, .{});1457 var file = try self.openFile(file_path, .{});
1455 defer file.close();1458 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);
1460 }1466 }
14611467
1462 pub const DeleteTreeError = error{1468 pub const DeleteTreeError = error{
lib/std/fs/file.zig+29-11
...@@ -363,31 +363,49 @@ pub const File = struct {...@@ -363,31 +363,49 @@ pub const File = struct {
363 try os.futimens(self.handle, &times);363 try os.futimens(self.handle, &times);
364 }364 }
365365
366 /// Reads all the bytes from the current position to the end of the file.
366 /// On success, caller owns returned buffer.367 /// On success, caller owns returned buffer.
367 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.368 /// 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 pub fn readToEndAlloc(self: File, allocator: *mem.Allocator, max_bytes: usize) ![]u8 {
369 return self.readAllAllocOptions(allocator, stat_size, max_bytes, @alignOf(u8), null);370 return self.readToEndAllocOptions(allocator, max_bytes, null, @alignOf(u8), null);
370 }371 }
371372
373 /// Reads all the bytes from the current position to the end of the file.
372 /// On success, caller owns returned buffer.374 /// On success, caller owns returned buffer.
373 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.375 /// 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.
374 /// Allows specifying alignment and a sentinel value.378 /// Allows specifying alignment and a sentinel value.
375 pub fn readAllAllocOptions(379 pub fn readToEndAllocOptions(
376 self: File,380 self: File,
377 allocator: *mem.Allocator,381 allocator: *mem.Allocator,
378 stat_size: u64,
379 max_bytes: usize,382 max_bytes: usize,
383 size_hint: ?usize,
380 comptime alignment: u29,384 comptime alignment: u29,
381 comptime optional_sentinel: ?u8,385 comptime optional_sentinel: ?u8,
382 ) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {386 ) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {
383 const size = math.cast(usize, stat_size) catch math.maxInt(usize);387 // If no size hint is provided fall back to the size=0 code path
384 if (size > max_bytes) return error.FileTooBig;388 const size = size_hint orelse 0;
385389
386 const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);390 // The file size returned by stat is used as hint to set the buffer
387 errdefer allocator.free(buf);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);402 if (optional_sentinel) |sentinel| {
390 return buf;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 }
391 }409 }
392410
393 pub const ReadError = os.ReadError;411 pub const ReadError = os.ReadError;
lib/std/fs/test.zig+5-5
...@@ -188,30 +188,30 @@ test "readAllAlloc" {...@@ -188,30 +188,30 @@ test "readAllAlloc" {
188 var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });188 var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });
189 defer file.close();189 defer file.close();
190190
191 const buf1 = try file.readAllAlloc(testing.allocator, 0, 1024);191 const buf1 = try file.readToEndAlloc(testing.allocator, 1024);
192 defer testing.allocator.free(buf1);192 defer testing.allocator.free(buf1);
193 testing.expect(buf1.len == 0);193 testing.expect(buf1.len == 0);
194194
195 const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";195 const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";
196 try file.writeAll(write_buf);196 try file.writeAll(write_buf);
197 try file.seekTo(0);197 try file.seekTo(0);
198 const file_size = try file.getEndPos();
199198
200 // max_bytes > file_size199 // max_bytes > file_size
201 const buf2 = try file.readAllAlloc(testing.allocator, file_size, 1024);200 const buf2 = try file.readToEndAlloc(testing.allocator, 1024);
202 defer testing.allocator.free(buf2);201 defer testing.allocator.free(buf2);
203 testing.expectEqual(write_buf.len, buf2.len);202 testing.expectEqual(write_buf.len, buf2.len);
204 testing.expect(std.mem.eql(u8, write_buf, buf2));203 testing.expect(std.mem.eql(u8, write_buf, buf2));
205 try file.seekTo(0);204 try file.seekTo(0);
206205
207 // max_bytes == file_size206 // 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);
209 defer testing.allocator.free(buf3);208 defer testing.allocator.free(buf3);
210 testing.expectEqual(write_buf.len, buf3.len);209 testing.expectEqual(write_buf.len, buf3.len);
211 testing.expect(std.mem.eql(u8, write_buf, buf3));210 testing.expect(std.mem.eql(u8, write_buf, buf3));
211 try file.seekTo(0);
212212
213 // max_bytes < file_size213 // 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));
215}215}
216216
217test "directory operations on files" {217test "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 {...@@ -113,7 +113,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
113 .Array => hashArray(hasher, key, strat),113 .Array => hashArray(hasher, key, strat),
114114
115 .Vector => |info| {115 .Vector => |info| {
116 if (info.child.bit_count % 8 == 0) {116 if (std.meta.bitCount(info.child) % 8 == 0) {
117 // If there's no unused bits in the child type, we can just hash117 // If there's no unused bits in the child type, we can just hash
118 // this as an array of bytes.118 // this as an array of bytes.
119 hasher.update(mem.asBytes(&key));119 hasher.update(mem.asBytes(&key));
lib/std/heap.zig+5-1
...@@ -915,6 +915,10 @@ pub fn testAllocator(base_allocator: *mem.Allocator) !void {...@@ -915,6 +915,10 @@ pub fn testAllocator(base_allocator: *mem.Allocator) !void {
915 testing.expect(slice.len == 10);915 testing.expect(slice.len == 10);
916916
917 allocator.free(slice);917 allocator.free(slice);
918
919 const zero_bit_ptr = try allocator.create(u0);
920 zero_bit_ptr.* = 0;
921 allocator.destroy(zero_bit_ptr);
918}922}
919923
920pub fn testAllocatorAligned(base_allocator: *mem.Allocator, comptime alignment: u29) !void {924pub fn testAllocatorAligned(base_allocator: *mem.Allocator, comptime alignment: u29) !void {
...@@ -952,7 +956,7 @@ pub fn testAllocatorLargeAlignment(base_allocator: *mem.Allocator) mem.Allocator...@@ -952,7 +956,7 @@ pub fn testAllocatorLargeAlignment(base_allocator: *mem.Allocator) mem.Allocator
952 // very near usize?956 // very near usize?
953 if (mem.page_size << 2 > maxInt(usize)) return;957 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)));
956 const large_align = @as(u29, mem.page_size << 2);960 const large_align = @as(u29, mem.page_size << 2);
957961
958 var align_mask: usize = undefined;962 var align_mask: usize = undefined;
lib/std/io.zig+9
...@@ -169,6 +169,15 @@ pub const BitOutStream = BitWriter;...@@ -169,6 +169,15 @@ pub const BitOutStream = BitWriter;
169/// Deprecated: use `bitWriter`169/// Deprecated: use `bitWriter`
170pub const bitOutStream = bitWriter;170pub 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
172pub const Packing = @import("io/serialization.zig").Packing;181pub const Packing = @import("io/serialization.zig").Packing;
173182
174pub const Serializer = @import("io/serialization.zig").Serializer;183pub 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(...@@ -198,28 +198,28 @@ pub fn Reader(
198198
199 /// Reads a native-endian integer199 /// Reads a native-endian integer
200 pub fn readIntNative(self: Self, comptime T: type) !T {200 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);
202 return mem.readIntNative(T, &bytes);202 return mem.readIntNative(T, &bytes);
203 }203 }
204204
205 /// Reads a foreign-endian integer205 /// Reads a foreign-endian integer
206 pub fn readIntForeign(self: Self, comptime T: type) !T {206 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);
208 return mem.readIntForeign(T, &bytes);208 return mem.readIntForeign(T, &bytes);
209 }209 }
210210
211 pub fn readIntLittle(self: Self, comptime T: type) !T {211 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);
213 return mem.readIntLittle(T, &bytes);213 return mem.readIntLittle(T, &bytes);
214 }214 }
215215
216 pub fn readIntBig(self: Self, comptime T: type) !T {216 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);
218 return mem.readIntBig(T, &bytes);218 return mem.readIntBig(T, &bytes);
219 }219 }
220220
221 pub fn readInt(self: Self, comptime T: type, endian: builtin.Endian) !T {221 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);
223 return mem.readInt(T, &bytes, endian);223 return mem.readInt(T, &bytes, endian);
224 }224 }
225225
lib/std/io/serialization.zig+3-3
...@@ -60,7 +60,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,...@@ -60,7 +60,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
6060
61 const U = std.meta.Int(false, t_bit_count);61 const U = std.meta.Int(false, t_bit_count);
62 const Log2U = math.Log2Int(U);62 const Log2U = math.Log2Int(U);
63 const int_size = (U.bit_count + 7) / 8;63 const int_size = (t_bit_count + 7) / 8;
6464
65 if (packing == .Bit) {65 if (packing == .Bit) {
66 const result = try self.in_stream.readBitsNoEof(U, t_bit_count);66 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,...@@ -73,7 +73,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
7373
74 if (int_size == 1) {74 if (int_size == 1) {
75 if (t_bit_count == 8) return @bitCast(T, buffer[0]);75 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);
77 return @truncate(T, @bitCast(PossiblySignedByte, buffer[0]));77 return @truncate(T, @bitCast(PossiblySignedByte, buffer[0]));
78 }78 }
7979
...@@ -247,7 +247,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co...@@ -247,7 +247,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
247247
248 const U = std.meta.Int(false, t_bit_count);248 const U = std.meta.Int(false, t_bit_count);
249 const Log2U = math.Log2Int(U);249 const Log2U = math.Log2Int(U);
250 const int_size = (U.bit_count + 7) / 8;250 const int_size = (t_bit_count + 7) / 8;
251251
252 const u_value = @bitCast(U, value);252 const u_value = @bitCast(U, value);
253253
lib/std/io/writer.zig+5-5
...@@ -53,7 +53,7 @@ pub fn Writer(...@@ -53,7 +53,7 @@ pub fn Writer(
53 /// Write a native-endian integer.53 /// Write a native-endian integer.
54 /// TODO audit non-power-of-two int sizes54 /// TODO audit non-power-of-two int sizes
55 pub fn writeIntNative(self: Self, comptime T: type, value: T) Error!void {55 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;
57 mem.writeIntNative(T, &bytes, value);57 mem.writeIntNative(T, &bytes, value);
58 return self.writeAll(&bytes);58 return self.writeAll(&bytes);
59 }59 }
...@@ -61,28 +61,28 @@ pub fn Writer(...@@ -61,28 +61,28 @@ pub fn Writer(
61 /// Write a foreign-endian integer.61 /// Write a foreign-endian integer.
62 /// TODO audit non-power-of-two int sizes62 /// TODO audit non-power-of-two int sizes
63 pub fn writeIntForeign(self: Self, comptime T: type, value: T) Error!void {63 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;
65 mem.writeIntForeign(T, &bytes, value);65 mem.writeIntForeign(T, &bytes, value);
66 return self.writeAll(&bytes);66 return self.writeAll(&bytes);
67 }67 }
6868
69 /// TODO audit non-power-of-two int sizes69 /// TODO audit non-power-of-two int sizes
70 pub fn writeIntLittle(self: Self, comptime T: type, value: T) Error!void {70 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;
72 mem.writeIntLittle(T, &bytes, value);72 mem.writeIntLittle(T, &bytes, value);
73 return self.writeAll(&bytes);73 return self.writeAll(&bytes);
74 }74 }
7575
76 /// TODO audit non-power-of-two int sizes76 /// TODO audit non-power-of-two int sizes
77 pub fn writeIntBig(self: Self, comptime T: type, value: T) Error!void {77 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;
79 mem.writeIntBig(T, &bytes, value);79 mem.writeIntBig(T, &bytes, value);
80 return self.writeAll(&bytes);80 return self.writeAll(&bytes);
81 }81 }
8282
83 /// TODO audit non-power-of-two int sizes83 /// TODO audit non-power-of-two int sizes
84 pub fn writeInt(self: Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {84 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;
86 mem.writeInt(T, &bytes, value, endian);86 mem.writeInt(T, &bytes, value, endian);
87 return self.writeAll(&bytes);87 return self.writeAll(&bytes);
88 }88 }
lib/std/log.zig+4
...@@ -127,6 +127,10 @@ fn log(...@@ -127,6 +127,10 @@ fn log(
127 if (@enumToInt(message_level) <= @enumToInt(level)) {127 if (@enumToInt(message_level) <= @enumToInt(level)) {
128 if (@hasDecl(root, "log")) {128 if (@hasDecl(root, "log")) {
129 root.log(message_level, scope, format, args);129 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;
130 } else if (builtin.mode != .ReleaseSmall) {134 } else if (builtin.mode != .ReleaseSmall) {
131 const held = std.debug.getStderrMutex().acquire();135 const held = std.debug.getStderrMutex().acquire();
132 defer held.release();136 defer held.release();
lib/std/math.zig+32-31
...@@ -195,7 +195,7 @@ test "" {...@@ -195,7 +195,7 @@ test "" {
195pub fn floatMantissaBits(comptime T: type) comptime_int {195pub fn floatMantissaBits(comptime T: type) comptime_int {
196 assert(@typeInfo(T) == .Float);196 assert(@typeInfo(T) == .Float);
197197
198 return switch (T.bit_count) {198 return switch (@typeInfo(T).Float.bits) {
199 16 => 10,199 16 => 10,
200 32 => 23,200 32 => 23,
201 64 => 52,201 64 => 52,
...@@ -208,7 +208,7 @@ pub fn floatMantissaBits(comptime T: type) comptime_int {...@@ -208,7 +208,7 @@ pub fn floatMantissaBits(comptime T: type) comptime_int {
208pub fn floatExponentBits(comptime T: type) comptime_int {208pub fn floatExponentBits(comptime T: type) comptime_int {
209 assert(@typeInfo(T) == .Float);209 assert(@typeInfo(T) == .Float);
210210
211 return switch (T.bit_count) {211 return switch (@typeInfo(T).Float.bits) {
212 16 => 5,212 16 => 5,
213 32 => 8,213 32 => 8,
214 64 => 11,214 64 => 11,
...@@ -347,9 +347,9 @@ pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {...@@ -347,9 +347,9 @@ pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {
347/// A negative shift amount results in a right shift.347/// A negative shift amount results in a right shift.
348pub fn shl(comptime T: type, a: T, shift_amt: anytype) T {348pub fn shl(comptime T: type, a: T, shift_amt: anytype) T {
349 const abs_shift_amt = absCast(shift_amt);349 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) {
353 if (shift_amt < 0) {353 if (shift_amt < 0) {
354 return a >> casted_shift_amt;354 return a >> casted_shift_amt;
355 }355 }
...@@ -373,9 +373,9 @@ test "math.shl" {...@@ -373,9 +373,9 @@ test "math.shl" {
373/// A negative shift amount results in a left shift.373/// A negative shift amount results in a left shift.
374pub fn shr(comptime T: type, a: T, shift_amt: anytype) T {374pub fn shr(comptime T: type, a: T, shift_amt: anytype) T {
375 const abs_shift_amt = absCast(shift_amt);375 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) {
379 if (shift_amt >= 0) {379 if (shift_amt >= 0) {
380 return a >> casted_shift_amt;380 return a >> casted_shift_amt;
381 } else {381 } else {
...@@ -400,11 +400,11 @@ test "math.shr" {...@@ -400,11 +400,11 @@ test "math.shr" {
400/// Rotates right. Only unsigned values can be rotated.400/// Rotates right. Only unsigned values can be rotated.
401/// Negative shift values results in shift modulo the bit count.401/// Negative shift values results in shift modulo the bit count.
402pub fn rotr(comptime T: type, x: T, r: anytype) T {402pub fn rotr(comptime T: type, x: T, r: anytype) T {
403 if (T.is_signed) {403 if (@typeInfo(T).Int.is_signed) {
404 @compileError("cannot rotate signed integer");404 @compileError("cannot rotate signed integer");
405 } else {405 } else {
406 const ar = @mod(r, T.bit_count);406 const ar = @mod(r, @typeInfo(T).Int.bits);
407 return shr(T, x, ar) | shl(T, x, T.bit_count - ar);407 return shr(T, x, ar) | shl(T, x, @typeInfo(T).Int.bits - ar);
408 }408 }
409}409}
410410
...@@ -419,11 +419,11 @@ test "math.rotr" {...@@ -419,11 +419,11 @@ test "math.rotr" {
419/// Rotates left. Only unsigned values can be rotated.419/// Rotates left. Only unsigned values can be rotated.
420/// Negative shift values results in shift modulo the bit count.420/// Negative shift values results in shift modulo the bit count.
421pub fn rotl(comptime T: type, x: T, r: anytype) T {421pub fn rotl(comptime T: type, x: T, r: anytype) T {
422 if (T.is_signed) {422 if (@typeInfo(T).Int.is_signed) {
423 @compileError("cannot rotate signed integer");423 @compileError("cannot rotate signed integer");
424 } else {424 } else {
425 const ar = @mod(r, T.bit_count);425 const ar = @mod(r, @typeInfo(T).Int.bits);
426 return shl(T, x, ar) | shr(T, x, T.bit_count - ar);426 return shl(T, x, ar) | shr(T, x, @typeInfo(T).Int.bits - ar);
427 }427 }
428}428}
429429
...@@ -438,7 +438,7 @@ test "math.rotl" {...@@ -438,7 +438,7 @@ test "math.rotl" {
438pub fn Log2Int(comptime T: type) type {438pub fn Log2Int(comptime T: type) type {
439 // comptime ceil log2439 // comptime ceil log2
440 comptime var count = 0;440 comptime var count = 0;
441 comptime var s = T.bit_count - 1;441 comptime var s = @typeInfo(T).Int.bits - 1;
442 inline while (s != 0) : (s >>= 1) {442 inline while (s != 0) : (s >>= 1) {
443 count += 1;443 count += 1;
444 }444 }
...@@ -524,7 +524,7 @@ fn testOverflow() void {...@@ -524,7 +524,7 @@ fn testOverflow() void {
524pub fn absInt(x: anytype) !@TypeOf(x) {524pub fn absInt(x: anytype) !@TypeOf(x) {
525 const T = @TypeOf(x);525 const T = @TypeOf(x);
526 comptime assert(@typeInfo(T) == .Int); // must pass an integer to absInt526 comptime assert(@typeInfo(T) == .Int); // must pass an integer to absInt
527 comptime assert(T.is_signed); // must pass a signed integer to absInt527 comptime assert(@typeInfo(T).Int.is_signed); // must pass a signed integer to absInt
528528
529 if (x == minInt(@TypeOf(x))) {529 if (x == minInt(@TypeOf(x))) {
530 return error.Overflow;530 return error.Overflow;
...@@ -557,7 +557,7 @@ fn testAbsFloat() void {...@@ -557,7 +557,7 @@ fn testAbsFloat() void {
557pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T {557pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T {
558 @setRuntimeSafety(false);558 @setRuntimeSafety(false);
559 if (denominator == 0) return error.DivisionByZero;559 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;
561 return @divTrunc(numerator, denominator);561 return @divTrunc(numerator, denominator);
562}562}
563563
...@@ -578,7 +578,7 @@ fn testDivTrunc() void {...@@ -578,7 +578,7 @@ fn testDivTrunc() void {
578pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T {578pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T {
579 @setRuntimeSafety(false);579 @setRuntimeSafety(false);
580 if (denominator == 0) return error.DivisionByZero;580 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;
582 return @divFloor(numerator, denominator);582 return @divFloor(numerator, denominator);
583}583}
584584
...@@ -652,7 +652,7 @@ fn testDivCeil() void {...@@ -652,7 +652,7 @@ fn testDivCeil() void {
652pub fn divExact(comptime T: type, numerator: T, denominator: T) !T {652pub fn divExact(comptime T: type, numerator: T, denominator: T) !T {
653 @setRuntimeSafety(false);653 @setRuntimeSafety(false);
654 if (denominator == 0) return error.DivisionByZero;654 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;
656 const result = @divTrunc(numerator, denominator);656 const result = @divTrunc(numerator, denominator);
657 if (result * denominator != numerator) return error.UnexpectedRemainder;657 if (result * denominator != numerator) return error.UnexpectedRemainder;
658 return result;658 return result;
...@@ -757,10 +757,10 @@ test "math.absCast" {...@@ -757,10 +757,10 @@ test "math.absCast" {
757757
758/// Returns the negation of the integer parameter.758/// Returns the negation of the integer parameter.
759/// Result is a signed integer.759/// Result is a signed integer.
760pub fn negateCast(x: anytype) !std.meta.Int(true, @TypeOf(x).bit_count) {760pub fn negateCast(x: anytype) !std.meta.Int(true, std.meta.bitCount(@TypeOf(x))) {
761 if (@TypeOf(x).is_signed) return negate(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)));
764 if (x > -minInt(int)) return error.Overflow;764 if (x > -minInt(int)) return error.Overflow;
765765
766 if (x == -minInt(int)) return minInt(int);766 if (x == -minInt(int)) return minInt(int);
...@@ -823,7 +823,7 @@ pub fn floorPowerOfTwo(comptime T: type, value: T) T {...@@ -823,7 +823,7 @@ pub fn floorPowerOfTwo(comptime T: type, value: T) T {
823 var x = value;823 var x = value;
824824
825 comptime var i = 1;825 comptime var i = 1;
826 inline while (T.bit_count > i) : (i *= 2) {826 inline while (@typeInfo(T).Int.bits > i) : (i *= 2) {
827 x |= (x >> i);827 x |= (x >> i);
828 }828 }
829829
...@@ -847,13 +847,13 @@ fn testFloorPowerOfTwo() void {...@@ -847,13 +847,13 @@ fn testFloorPowerOfTwo() void {
847/// Returns the next power of two (if the value is not already a power of two).847/// Returns the next power of two (if the value is not already a power of two).
848/// Only unsigned integers can be used. Zero is not an allowed input.848/// Only unsigned integers can be used. Zero is not an allowed input.
849/// Result is a type with 1 more bit than the input type.849/// 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) {
851 comptime assert(@typeInfo(T) == .Int);851 comptime assert(@typeInfo(T) == .Int);
852 comptime assert(!T.is_signed);852 comptime assert(!@typeInfo(T).Int.is_signed);
853 assert(value != 0);853 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);
855 comptime const shiftType = std.math.Log2Int(PromotedType);855 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));
857}857}
858858
859/// Returns the next power of two (if the value is not already a power of two).859/// 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...@@ -861,9 +861,10 @@ pub fn ceilPowerOfTwoPromote(comptime T: type, value: T) std.meta.Int(T.is_signe
861/// If the value doesn't fit, returns an error.861/// If the value doesn't fit, returns an error.
862pub fn ceilPowerOfTwo(comptime T: type, value: T) (error{Overflow}!T) {862pub fn ceilPowerOfTwo(comptime T: type, value: T) (error{Overflow}!T) {
863 comptime assert(@typeInfo(T) == .Int);863 comptime assert(@typeInfo(T) == .Int);
864 comptime assert(!T.is_signed);864 const info = @typeInfo(T).Int;
865 comptime const PromotedType = std.meta.Int(T.is_signed, T.bit_count + 1);865 comptime assert(!info.is_signed);
866 comptime const overflowBit = @as(PromotedType, 1) << T.bit_count;866 comptime const PromotedType = std.meta.Int(info.is_signed, info.bits + 1);
867 comptime const overflowBit = @as(PromotedType, 1) << info.bits;
867 var x = ceilPowerOfTwoPromote(T, value);868 var x = ceilPowerOfTwoPromote(T, value);
868 if (overflowBit & x != 0) {869 if (overflowBit & x != 0) {
869 return error.Overflow;870 return error.Overflow;
...@@ -911,7 +912,7 @@ fn testCeilPowerOfTwo() !void {...@@ -911,7 +912,7 @@ fn testCeilPowerOfTwo() !void {
911912
912pub fn log2_int(comptime T: type, x: T) Log2Int(T) {913pub fn log2_int(comptime T: type, x: T) Log2Int(T) {
913 assert(x != 0);914 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));
915}916}
916917
917pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) {918pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) {
...@@ -1008,8 +1009,8 @@ test "max value type" {...@@ -1008,8 +1009,8 @@ test "max value type" {
1008 testing.expect(x == 2147483647);1009 testing.expect(x == 2147483647);
1009}1010}
10101011
1011pub fn mulWide(comptime T: type, a: T, b: T) 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) {
1012 const ResultInt = std.meta.Int(T.is_signed, T.bit_count * 2);1013 const ResultInt = std.meta.Int(@typeInfo(T).Int.is_signed, @typeInfo(T).Int.bits * 2);
1013 return @as(ResultInt, a) * @as(ResultInt, b);1014 return @as(ResultInt, a) * @as(ResultInt, b);
1014}1015}
10151016
lib/std/math/big.zig+6-5
...@@ -9,14 +9,15 @@ const assert = std.debug.assert;...@@ -9,14 +9,15 @@ const assert = std.debug.assert;
9pub const Rational = @import("big/rational.zig").Rational;9pub const Rational = @import("big/rational.zig").Rational;
10pub const int = @import("big/int.zig");10pub const int = @import("big/int.zig");
11pub const Limb = usize;11pub const Limb = usize;
12pub const DoubleLimb = std.meta.IntType(false, 2 * Limb.bit_count);12const limb_info = @typeInfo(Limb).Int;
13pub const SignedDoubleLimb = std.meta.IntType(true, DoubleLimb.bit_count);13pub const DoubleLimb = std.meta.IntType(false, 2 * limb_info.bits);
14pub const SignedDoubleLimb = std.meta.IntType(true, 2 * limb_info.bits);
14pub const Log2Limb = std.math.Log2Int(Limb);15pub const Log2Limb = std.math.Log2Int(Limb);
1516
16comptime {17comptime {
17 assert(std.math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count);18 assert(std.math.floorPowerOfTwo(usize, limb_info.bits) == limb_info.bits);
18 assert(Limb.bit_count <= 64); // u128 set is unsupported19 assert(limb_info.bits <= 64); // u128 set is unsupported
19 assert(Limb.is_signed == false);20 assert(limb_info.is_signed == false);
20}21}
2122
22test "" {23test "" {
lib/std/math/big/int.zig+44-43
...@@ -6,6 +6,7 @@...@@ -6,6 +6,7 @@
6const std = @import("../../std.zig");6const std = @import("../../std.zig");
7const math = std.math;7const math = std.math;
8const Limb = std.math.big.Limb;8const Limb = std.math.big.Limb;
9const limb_bits = @typeInfo(Limb).Int.bits;
9const DoubleLimb = std.math.big.DoubleLimb;10const DoubleLimb = std.math.big.DoubleLimb;
10const SignedDoubleLimb = std.math.big.SignedDoubleLimb;11const SignedDoubleLimb = std.math.big.SignedDoubleLimb;
11const Log2Limb = std.math.big.Log2Limb;12const Log2Limb = std.math.big.Log2Limb;
...@@ -28,7 +29,7 @@ pub fn calcLimbLen(scalar: anytype) usize {...@@ -28,7 +29,7 @@ pub fn calcLimbLen(scalar: anytype) usize {
28 },29 },
29 .ComptimeInt => {30 .ComptimeInt => {
30 const w_value = if (scalar < 0) -scalar else scalar;31 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;
32 },33 },
33 else => @compileError("parameter must be a primitive integer type"),34 else => @compileError("parameter must be a primitive integer type"),
34 }35 }
...@@ -54,7 +55,7 @@ pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize {...@@ -54,7 +55,7 @@ pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize {
54}55}
5556
56pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize {57pub 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);
58}59}
5960
60/// a + b * c + *carry, sets carry to the overflow bits61/// 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 {...@@ -68,7 +69,7 @@ pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
68 // r2 = b * c69 // r2 = b * c
69 const bc = @as(DoubleLimb, math.mulWide(Limb, b, c));70 const bc = @as(DoubleLimb, math.mulWide(Limb, b, c));
70 const r2 = @truncate(Limb, bc);71 const r2 = @truncate(Limb, bc);
71 const c2 = @truncate(Limb, bc >> Limb.bit_count);72 const c2 = @truncate(Limb, bc >> limb_bits);
7273
73 // r1 = r1 + r274 // r1 = r1 + r2
74 const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1));75 const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1));
...@@ -181,7 +182,7 @@ pub const Mutable = struct {...@@ -181,7 +182,7 @@ pub const Mutable = struct {
181182
182 switch (@typeInfo(T)) {183 switch (@typeInfo(T)) {
183 .Int => |info| {184 .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
186 const needed_limbs = @sizeOf(UT) / @sizeOf(Limb);187 const needed_limbs = @sizeOf(UT) / @sizeOf(Limb);
187 assert(needed_limbs <= self.limbs.len); // value too big188 assert(needed_limbs <= self.limbs.len); // value too big
...@@ -190,7 +191,7 @@ pub const Mutable = struct {...@@ -190,7 +191,7 @@ pub const Mutable = struct {
190191
191 var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value);192 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) {
194 self.limbs[0] = @as(Limb, w_value);195 self.limbs[0] = @as(Limb, w_value);
195 self.len += 1;196 self.len += 1;
196 } else {197 } else {
...@@ -200,15 +201,15 @@ pub const Mutable = struct {...@@ -200,15 +201,15 @@ pub const Mutable = struct {
200 self.len += 1;201 self.len += 1;
201202
202 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.203 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
203 w_value >>= Limb.bit_count / 2;204 w_value >>= limb_bits / 2;
204 w_value >>= Limb.bit_count / 2;205 w_value >>= limb_bits / 2;
205 }206 }
206 }207 }
207 },208 },
208 .ComptimeInt => {209 .ComptimeInt => {
209 comptime var w_value = if (value < 0) -value else value;210 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;
212 assert(req_limbs <= self.limbs.len); // value too big213 assert(req_limbs <= self.limbs.len); // value too big
213214
214 self.len = req_limbs;215 self.len = req_limbs;
...@@ -217,14 +218,14 @@ pub const Mutable = struct {...@@ -217,14 +218,14 @@ pub const Mutable = struct {
217 if (w_value <= maxInt(Limb)) {218 if (w_value <= maxInt(Limb)) {
218 self.limbs[0] = w_value;219 self.limbs[0] = w_value;
219 } else {220 } else {
220 const mask = (1 << Limb.bit_count) - 1;221 const mask = (1 << limb_bits) - 1;
221222
222 comptime var i = 0;223 comptime var i = 0;
223 inline while (w_value != 0) : (i += 1) {224 inline while (w_value != 0) : (i += 1) {
224 self.limbs[i] = w_value & mask;225 self.limbs[i] = w_value & mask;
225226
226 w_value >>= Limb.bit_count / 2;227 w_value >>= limb_bits / 2;
227 w_value >>= Limb.bit_count / 2;228 w_value >>= limb_bits / 2;
228 }229 }
229 }230 }
230 },231 },
...@@ -506,7 +507,7 @@ pub const Mutable = struct {...@@ -506,7 +507,7 @@ pub const Mutable = struct {
506 /// `a.limbs.len + (shift / (@sizeOf(Limb) * 8))`.507 /// `a.limbs.len + (shift / (@sizeOf(Limb) * 8))`.
507 pub fn shiftLeft(r: *Mutable, a: Const, shift: usize) void {508 pub fn shiftLeft(r: *Mutable, a: Const, shift: usize) void {
508 llshl(r.limbs[0..], a.limbs[0..a.limbs.len], shift);509 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);
510 r.positive = a.positive;511 r.positive = a.positive;
511 }512 }
512513
...@@ -516,7 +517,7 @@ pub const Mutable = struct {...@@ -516,7 +517,7 @@ pub const Mutable = struct {
516 /// Asserts there is enough memory to fit the result. The upper bound Limb count is517 /// Asserts there is enough memory to fit the result. The upper bound Limb count is
517 /// `a.limbs.len - (shift / (@sizeOf(Limb) * 8))`.518 /// `a.limbs.len - (shift / (@sizeOf(Limb) * 8))`.
518 pub fn shiftRight(r: *Mutable, a: Const, shift: usize) void {519 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) {
520 r.len = 1;521 r.len = 1;
521 r.positive = true;522 r.positive = true;
522 r.limbs[0] = 0;523 r.limbs[0] = 0;
...@@ -524,7 +525,7 @@ pub const Mutable = struct {...@@ -524,7 +525,7 @@ pub const Mutable = struct {
524 }525 }
525526
526 const r_len = llshr(r.limbs[0..], a.limbs[0..a.limbs.len], shift);527 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);
528 r.positive = a.positive;529 r.positive = a.positive;
529 }530 }
530531
...@@ -772,7 +773,7 @@ pub const Mutable = struct {...@@ -772,7 +773,7 @@ pub const Mutable = struct {
772 }773 }
773774
774 if (ab_zero_limb_count != 0) {775 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);
776 }777 }
777 }778 }
778779
...@@ -803,10 +804,10 @@ pub const Mutable = struct {...@@ -803,10 +804,10 @@ pub const Mutable = struct {
803 };804 };
804 tmp.limbs[0] = 0;805 tmp.limbs[0] = 0;
805806
806 // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even807 // Normalize so y > limb_bits / 2 (i.e. leading bit is set) and even
807 var norm_shift = @clz(Limb, y.limbs[y.len - 1]);808 var norm_shift = @clz(Limb, y.limbs[y.len - 1]);
808 if (norm_shift == 0 and y.toConst().isOdd()) {809 if (norm_shift == 0 and y.toConst().isOdd()) {
809 norm_shift = Limb.bit_count;810 norm_shift = limb_bits;
810 }811 }
811 x.shiftLeft(x.toConst(), norm_shift);812 x.shiftLeft(x.toConst(), norm_shift);
812 y.shiftLeft(y.toConst(), norm_shift);813 y.shiftLeft(y.toConst(), norm_shift);
...@@ -820,7 +821,7 @@ pub const Mutable = struct {...@@ -820,7 +821,7 @@ pub const Mutable = struct {
820 mem.set(Limb, q.limbs[0..q.len], 0);821 mem.set(Limb, q.limbs[0..q.len], 0);
821822
822 // 2.823 // 2.
823 tmp.shiftLeft(y.toConst(), Limb.bit_count * (n - t));824 tmp.shiftLeft(y.toConst(), limb_bits * (n - t));
824 while (x.toConst().order(tmp.toConst()) != .lt) {825 while (x.toConst().order(tmp.toConst()) != .lt) {
825 q.limbs[n - t] += 1;826 q.limbs[n - t] += 1;
826 x.sub(x.toConst(), tmp.toConst());827 x.sub(x.toConst(), tmp.toConst());
...@@ -833,7 +834,7 @@ pub const Mutable = struct {...@@ -833,7 +834,7 @@ pub const Mutable = struct {
833 if (x.limbs[i] == y.limbs[t]) {834 if (x.limbs[i] == y.limbs[t]) {
834 q.limbs[i - t - 1] = maxInt(Limb);835 q.limbs[i - t - 1] = maxInt(Limb);
835 } else {836 } 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]);
837 const z = @intCast(Limb, num / @as(DoubleLimb, y.limbs[t]));838 const z = @intCast(Limb, num / @as(DoubleLimb, y.limbs[t]));
838 q.limbs[i - t - 1] = if (z > maxInt(Limb)) maxInt(Limb) else @as(Limb, z);839 q.limbs[i - t - 1] = if (z > maxInt(Limb)) maxInt(Limb) else @as(Limb, z);
839 }840 }
...@@ -862,11 +863,11 @@ pub const Mutable = struct {...@@ -862,11 +863,11 @@ pub const Mutable = struct {
862 // 3.3863 // 3.3
863 tmp.set(q.limbs[i - t - 1]);864 tmp.set(q.limbs[i - t - 1]);
864 tmp.mul(tmp.toConst(), y.toConst(), mul_limb_buf, allocator);865 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));
866 x.sub(x.toConst(), tmp.toConst());867 x.sub(x.toConst(), tmp.toConst());
867868
868 if (!x.positive) {869 if (!x.positive) {
869 tmp.shiftLeft(y.toConst(), Limb.bit_count * (i - t - 1));870 tmp.shiftLeft(y.toConst(), limb_bits * (i - t - 1));
870 x.add(x.toConst(), tmp.toConst());871 x.add(x.toConst(), tmp.toConst());
871 q.limbs[i - t - 1] -= 1;872 q.limbs[i - t - 1] -= 1;
872 }873 }
...@@ -949,7 +950,7 @@ pub const Const = struct {...@@ -949,7 +950,7 @@ pub const Const = struct {
949950
950 /// Returns the number of bits required to represent the absolute value of an integer.951 /// Returns the number of bits required to represent the absolute value of an integer.
951 pub fn bitCountAbs(self: Const) usize {952 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]));
953 }954 }
954955
955 /// Returns the number of bits required to represent the integer in twos-complement form.956 /// Returns the number of bits required to represent the integer in twos-complement form.
...@@ -1019,10 +1020,10 @@ pub const Const = struct {...@@ -1019,10 +1020,10 @@ pub const Const = struct {
1019 /// Returns an error if self cannot be narrowed into the requested type without truncation.1020 /// Returns an error if self cannot be narrowed into the requested type without truncation.
1020 pub fn to(self: Const, comptime T: type) ConvertError!T {1021 pub fn to(self: Const, comptime T: type) ConvertError!T {
1021 switch (@typeInfo(T)) {1022 switch (@typeInfo(T)) {
1022 .Int => {1023 .Int => |info| {
1023 const UT = std.meta.Int(false, T.bit_count);1024 const UT = std.meta.Int(false, info.bits);
10241025
1025 if (self.bitCountTwosComp() > T.bit_count) {1026 if (self.bitCountTwosComp() > info.bits) {
1026 return error.TargetTooSmall;1027 return error.TargetTooSmall;
1027 }1028 }
10281029
...@@ -1033,12 +1034,12 @@ pub const Const = struct {...@@ -1033,12 +1034,12 @@ pub const Const = struct {
1033 } else {1034 } else {
1034 for (self.limbs[0..self.limbs.len]) |_, ri| {1035 for (self.limbs[0..self.limbs.len]) |_, ri| {
1035 const limb = self.limbs[self.limbs.len - ri - 1];1036 const limb = self.limbs[self.limbs.len - ri - 1];
1036 r <<= Limb.bit_count;1037 r <<= limb_bits;
1037 r |= limb;1038 r |= limb;
1038 }1039 }
1039 }1040 }
10401041
1041 if (!T.is_signed) {1042 if (!info.is_signed) {
1042 return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned;1043 return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned;
1043 } else {1044 } else {
1044 if (self.positive) {1045 if (self.positive) {
...@@ -1149,7 +1150,7 @@ pub const Const = struct {...@@ -1149,7 +1150,7 @@ pub const Const = struct {
11491150
1150 outer: for (self.limbs[0..self.limbs.len]) |limb| {1151 outer: for (self.limbs[0..self.limbs.len]) |limb| {
1151 var shift: usize = 0;1152 var shift: usize = 0;
1152 while (shift < Limb.bit_count) : (shift += base_shift) {1153 while (shift < limb_bits) : (shift += base_shift) {
1153 const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & @as(Limb, base - 1));1154 const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & @as(Limb, base - 1));
1154 const ch = std.fmt.digitToChar(r, uppercase);1155 const ch = std.fmt.digitToChar(r, uppercase);
1155 string[digits_len] = ch;1156 string[digits_len] = ch;
...@@ -1295,7 +1296,7 @@ pub const Const = struct {...@@ -1295,7 +1296,7 @@ pub const Const = struct {
1295/// Memory is allocated as needed to ensure operations never overflow. The range1296/// Memory is allocated as needed to ensure operations never overflow. The range
1296/// is bounded only by available memory.1297/// is bounded only by available memory.
1297pub const Managed = struct {1298pub 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
1300 /// Default number of limbs to allocate on creation of a `Managed`.1301 /// Default number of limbs to allocate on creation of a `Managed`.
1301 pub const default_capacity = 4;1302 pub const default_capacity = 4;
...@@ -1448,7 +1449,7 @@ pub const Managed = struct {...@@ -1448,7 +1449,7 @@ pub const Managed = struct {
1448 for (self.limbs[0..self.len()]) |limb| {1449 for (self.limbs[0..self.len()]) |limb| {
1449 std.debug.warn("{x} ", .{limb});1450 std.debug.warn("{x} ", .{limb});
1450 }1451 }
1451 std.debug.warn("capacity={} positive={}\n", .{ self.limbs.len, self.positive });1452 std.debug.warn("capacity={} positive={}\n", .{ self.limbs.len, self.isPositive() });
1452 }1453 }
14531454
1454 /// Negate the sign.1455 /// Negate the sign.
...@@ -1716,7 +1717,7 @@ pub const Managed = struct {...@@ -1716,7 +1717,7 @@ pub const Managed = struct {
17161717
1717 /// r = a << shift, in other words, r = a * 2^shift1718 /// r = a << shift, in other words, r = a * 2^shift
1718 pub fn shiftLeft(r: *Managed, a: Managed, shift: usize) !void {1719 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);
1720 var m = r.toMutable();1721 var m = r.toMutable();
1721 m.shiftLeft(a.toConst(), shift);1722 m.shiftLeft(a.toConst(), shift);
1722 r.setMetadata(m.positive, m.len);1723 r.setMetadata(m.positive, m.len);
...@@ -1724,13 +1725,13 @@ pub const Managed = struct {...@@ -1724,13 +1725,13 @@ pub const Managed = struct {
17241725
1725 /// r = a >> shift1726 /// r = a >> shift
1726 pub fn shiftRight(r: *Managed, a: Managed, shift: usize) !void {1727 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) {
1728 r.metadata = 1;1729 r.metadata = 1;
1729 r.limbs[0] = 0;1730 r.limbs[0] = 0;
1730 return;1731 return;
1731 }1732 }
17321733
1733 try r.ensureCapacity(a.len() - (shift / Limb.bit_count));1734 try r.ensureCapacity(a.len() - (shift / limb_bits));
1734 var m = r.toMutable();1735 var m = r.toMutable();
1735 m.shiftRight(a.toConst(), shift);1736 m.shiftRight(a.toConst(), shift);
1736 r.setMetadata(m.positive, m.len);1737 r.setMetadata(m.positive, m.len);
...@@ -2021,7 +2022,7 @@ fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {...@@ -2021,7 +2022,7 @@ fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {
2021 rem.* = 0;2022 rem.* = 0;
2022 for (a) |_, ri| {2023 for (a) |_, ri| {
2023 const i = a.len - ri - 1;2024 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
2026 if (pdiv == 0) {2027 if (pdiv == 0) {
2027 quo[i] = 0;2028 quo[i] = 0;
...@@ -2042,10 +2043,10 @@ fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {...@@ -2042,10 +2043,10 @@ fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {
2042fn llshl(r: []Limb, a: []const Limb, shift: usize) void {2043fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
2043 @setRuntimeSafety(debug_safety);2044 @setRuntimeSafety(debug_safety);
2044 assert(a.len >= 1);2045 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 limb_shift = shift / limb_bits + 1;
2048 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);2049 const interior_limb_shift = @intCast(Log2Limb, shift % limb_bits);
20492050
2050 var carry: Limb = 0;2051 var carry: Limb = 0;
2051 var i: usize = 0;2052 var i: usize = 0;
...@@ -2057,7 +2058,7 @@ fn llshl(r: []Limb, a: []const Limb, shift: usize) void {...@@ -2057,7 +2058,7 @@ fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
2057 r[dst_i] = carry | @call(.{ .modifier = .always_inline }, math.shr, .{2058 r[dst_i] = carry | @call(.{ .modifier = .always_inline }, math.shr, .{
2058 Limb,2059 Limb,
2059 src_digit,2060 src_digit,
2060 Limb.bit_count - @intCast(Limb, interior_limb_shift),2061 limb_bits - @intCast(Limb, interior_limb_shift),
2061 });2062 });
2062 carry = (src_digit << interior_limb_shift);2063 carry = (src_digit << interior_limb_shift);
2063 }2064 }
...@@ -2069,10 +2070,10 @@ fn llshl(r: []Limb, a: []const Limb, shift: usize) void {...@@ -2069,10 +2070,10 @@ fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
2069fn llshr(r: []Limb, a: []const Limb, shift: usize) void {2070fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
2070 @setRuntimeSafety(debug_safety);2071 @setRuntimeSafety(debug_safety);
2071 assert(a.len >= 1);2072 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 limb_shift = shift / limb_bits;
2075 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);2076 const interior_limb_shift = @intCast(Log2Limb, shift % limb_bits);
20762077
2077 var carry: Limb = 0;2078 var carry: Limb = 0;
2078 var i: usize = 0;2079 var i: usize = 0;
...@@ -2085,7 +2086,7 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void {...@@ -2085,7 +2086,7 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
2085 carry = @call(.{ .modifier = .always_inline }, math.shl, .{2086 carry = @call(.{ .modifier = .always_inline }, math.shl, .{
2086 Limb,2087 Limb,
2087 src_digit,2088 src_digit,
2088 Limb.bit_count - @intCast(Limb, interior_limb_shift),2089 limb_bits - @intCast(Limb, interior_limb_shift),
2089 });2090 });
2090 }2091 }
2091}2092}
...@@ -2135,7 +2136,7 @@ fn fixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Mutable {...@@ -2135,7 +2136,7 @@ fn fixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Mutable {
2135 const A_is_positive = A >= 0;2136 const A_is_positive = A >= 0;
2136 const Au = @intCast(DoubleLimb, if (A < 0) -A else A);2137 const Au = @intCast(DoubleLimb, if (A < 0) -A else A);
2137 storage[0] = @truncate(Limb, Au);2138 storage[0] = @truncate(Limb, Au);
2138 storage[1] = @truncate(Limb, Au >> Limb.bit_count);2139 storage[1] = @truncate(Limb, Au >> limb_bits);
2139 return .{2140 return .{
2140 .limbs = storage[0..2],2141 .limbs = storage[0..2],
2141 .positive = A_is_positive,2142 .positive = A_is_positive,
lib/std/math/big/int_test.zig+3-3
...@@ -23,13 +23,13 @@ test "big.int comptime_int set" {...@@ -23,13 +23,13 @@ test "big.int comptime_int set" {
23 var a = try Managed.initSet(testing.allocator, s);23 var a = try Managed.initSet(testing.allocator, s);
24 defer a.deinit();24 defer a.deinit();
2525
26 const s_limb_count = 128 / Limb.bit_count;26 const s_limb_count = 128 / @typeInfo(Limb).Int.bits;
2727
28 comptime var i: usize = 0;28 comptime var i: usize = 0;
29 inline while (i < s_limb_count) : (i += 1) {29 inline while (i < s_limb_count) : (i += 1) {
30 const result = @as(Limb, s & maxInt(Limb));30 const result = @as(Limb, s & maxInt(Limb));
31 s >>= Limb.bit_count / 2;31 s >>= @typeInfo(Limb).Int.bits / 2;
32 s >>= Limb.bit_count / 2;32 s >>= @typeInfo(Limb).Int.bits / 2;
33 testing.expect(a.limbs[i] == result);33 testing.expect(a.limbs[i] == result);
34 }34 }
35}35}
lib/std/math/big/rational.zig+9-7
...@@ -136,7 +136,7 @@ pub const Rational = struct {...@@ -136,7 +136,7 @@ pub const Rational = struct {
136 // Translated from golang.go/src/math/big/rat.go.136 // Translated from golang.go/src/math/big/rat.go.
137 debug.assert(@typeInfo(T) == .Float);137 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);
140 const f_bits = @bitCast(UnsignedInt, f);140 const f_bits = @bitCast(UnsignedInt, f);
141141
142 const exponent_bits = math.floatExponentBits(T);142 const exponent_bits = math.floatExponentBits(T);
...@@ -194,8 +194,8 @@ pub const Rational = struct {...@@ -194,8 +194,8 @@ pub const Rational = struct {
194 // TODO: Indicate whether the result is not exact.194 // TODO: Indicate whether the result is not exact.
195 debug.assert(@typeInfo(T) == .Float);195 debug.assert(@typeInfo(T) == .Float);
196196
197 const fsize = T.bit_count;197 const fsize = @typeInfo(T).Float.bits;
198 const BitReprType = std.meta.Int(false, T.bit_count);198 const BitReprType = std.meta.Int(false, fsize);
199199
200 const msize = math.floatMantissaBits(T);200 const msize = math.floatMantissaBits(T);
201 const msize1 = msize + 1;201 const msize1 = msize + 1;
...@@ -475,16 +475,18 @@ pub const Rational = struct {...@@ -475,16 +475,18 @@ pub const Rational = struct {
475fn extractLowBits(a: Int, comptime T: type) T {475fn extractLowBits(a: Int, comptime T: type) T {
476 testing.expect(@typeInfo(T) == .Int);476 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) {
479 return @truncate(T, a.limbs[0]);481 return @truncate(T, a.limbs[0]);
480 } else {482 } else {
481 var r: T = 0;483 var r: T = 0;
482 comptime var i: usize = 0;484 comptime var i: usize = 0;
483485
484 // Remainder is always 0 since if T.bit_count >= Limb.bit_count -> Limb | T and both486 // Remainder is always 0 since if t_bits >= limb_bits -> Limb | T and both
485 // are powers of two.487 // are powers of two.
486 inline while (i < T.bit_count / Limb.bit_count) : (i += 1) {488 inline while (i < t_bits / limb_bits) : (i += 1) {
487 r |= math.shl(T, a.limbs[i], i * Limb.bit_count);489 r |= math.shl(T, a.limbs[i], i * limb_bits);
488 }490 }
489491
490 return r;492 return r;
lib/std/math/cos.zig+1-1
...@@ -49,7 +49,7 @@ const pi4c = 2.69515142907905952645E-15;...@@ -49,7 +49,7 @@ const pi4c = 2.69515142907905952645E-15;
49const m4pi = 1.273239544735162542821171882678754627704620361328125;49const m4pi = 1.273239544735162542821171882678754627704620361328125;
5050
51fn cos_(comptime T: type, x_: T) T {51fn 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
54 var x = x_;54 var x = x_;
55 if (math.isNan(x) or math.isInf(x)) {55 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 {...@@ -128,7 +128,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
128 if (yf != 0 and x < 0) {128 if (yf != 0 and x < 0) {
129 return math.nan(T);129 return math.nan(T);
130 }130 }
131 if (yi >= 1 << (T.bit_count - 1)) {131 if (yi >= 1 << (@typeInfo(T).Float.bits - 1)) {
132 return math.exp(y * math.ln(x));132 return math.exp(y * math.ln(x));
133 }133 }
134134
...@@ -150,7 +150,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {...@@ -150,7 +150,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
150 var xe = r2.exponent;150 var xe = r2.exponent;
151 var x1 = r2.significand;151 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);
154 while (i != 0) : (i >>= 1) {154 while (i != 0) : (i >>= 1) {
155 const overflow_shift = math.floatExponentBits(T) + 1;155 const overflow_shift = math.floatExponentBits(T) + 1;
156 if (xe < -(1 << overflow_shift) or (1 << overflow_shift) < xe) {156 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;...@@ -50,7 +50,7 @@ const pi4c = 2.69515142907905952645E-15;
50const m4pi = 1.273239544735162542821171882678754627704620361328125;50const m4pi = 1.273239544735162542821171882678754627704620361328125;
5151
52fn sin_(comptime T: type, x_: T) T {52fn 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
55 var x = x_;55 var x = x_;
56 if (x == 0 or math.isNan(x)) {56 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)) {...@@ -36,10 +36,10 @@ pub fn sqrt(x: anytype) Sqrt(@TypeOf(x)) {
36 }36 }
37}37}
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) {
40 var op = value;40 var op = value;
41 var res: T = 0;41 var res: T = 0;
42 var one: T = 1 << (T.bit_count - 2);42 var one: T = 1 << (@typeInfo(T).Int.bits - 2);
4343
44 // "one" starts at the highest power of four <= than the argument.44 // "one" starts at the highest power of four <= than the argument.
45 while (one > op) {45 while (one > op) {
...@@ -55,7 +55,7 @@ fn sqrt_int(comptime T: type, value: T) std.meta.Int(false, T.bit_count / 2) {...@@ -55,7 +55,7 @@ fn sqrt_int(comptime T: type, value: T) std.meta.Int(false, T.bit_count / 2) {
55 one >>= 2;55 one >>= 2;
56 }56 }
5757
58 const ResultType = std.meta.Int(false, T.bit_count / 2);58 const ResultType = std.meta.Int(false, @typeInfo(T).Int.bits / 2);
59 return @intCast(ResultType, res);59 return @intCast(ResultType, res);
60}60}
6161
lib/std/math/tan.zig+1-1
...@@ -43,7 +43,7 @@ const pi4c = 2.69515142907905952645E-15;...@@ -43,7 +43,7 @@ const pi4c = 2.69515142907905952645E-15;
43const m4pi = 1.273239544735162542821171882678754627704620361328125;43const m4pi = 1.273239544735162542821171882678754627704620361328125;
4444
45fn tan_(comptime T: type, x_: T) T {45fn 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
48 var x = x_;48 var x = x_;
49 if (x == 0 or math.isNan(x)) {49 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....@@ -949,7 +949,7 @@ pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: builtin.
949/// This function cannot fail and cannot cause undefined behavior.949/// This function cannot fail and cannot cause undefined behavior.
950/// Assumes the endianness of memory is native. This means the function can950/// Assumes the endianness of memory is native. This means the function can
951/// simply pointer cast memory.951/// 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 {
953 return @ptrCast(*align(1) const T, bytes).*;953 return @ptrCast(*align(1) const T, bytes).*;
954}954}
955955
...@@ -957,7 +957,7 @@ pub fn readIntNative(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]...@@ -957,7 +957,7 @@ pub fn readIntNative(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]
957/// The bit count of T must be evenly divisible by 8.957/// The bit count of T must be evenly divisible by 8.
958/// This function cannot fail and cannot cause undefined behavior.958/// This function cannot fail and cannot cause undefined behavior.
959/// Assumes the endianness of memory is foreign, so it must byte-swap.959/// 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 {
961 return @byteSwap(T, readIntNative(T, bytes));961 return @byteSwap(T, readIntNative(T, bytes));
962}962}
963963
...@@ -971,18 +971,18 @@ pub const readIntBig = switch (builtin.endian) {...@@ -971,18 +971,18 @@ pub const readIntBig = switch (builtin.endian) {
971 .Big => readIntNative,971 .Big => readIntNative,
972};972};
973973
974/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0974/// Asserts that bytes.len >= @typeInfo(T).Int.bits / 8. Reads the integer starting from index 0
975/// and ignores extra bytes.975/// and ignores extra bytes.
976/// The bit count of T must be evenly divisible by 8.976/// The bit count of T must be evenly divisible by 8.
977/// Assumes the endianness of memory is native. This means the function can977/// Assumes the endianness of memory is native. This means the function can
978/// simply pointer cast memory.978/// simply pointer cast memory.
979pub fn readIntSliceNative(comptime T: type, bytes: []const u8) T {979pub 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);
981 assert(bytes.len >= n);981 assert(bytes.len >= n);
982 return readIntNative(T, bytes[0..n]);982 return readIntNative(T, bytes[0..n]);
983}983}
984984
985/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0985/// Asserts that bytes.len >= @typeInfo(T).Int.bits / 8. Reads the integer starting from index 0
986/// and ignores extra bytes.986/// and ignores extra bytes.
987/// The bit count of T must be evenly divisible by 8.987/// The bit count of T must be evenly divisible by 8.
988/// Assumes the endianness of memory is foreign, so it must byte-swap.988/// Assumes the endianness of memory is foreign, so it must byte-swap.
...@@ -1003,7 +1003,7 @@ pub const readIntSliceBig = switch (builtin.endian) {...@@ -1003,7 +1003,7 @@ pub const readIntSliceBig = switch (builtin.endian) {
1003/// Reads an integer from memory with bit count specified by T.1003/// Reads an integer from memory with bit count specified by T.
1004/// The bit count of T must be evenly divisible by 8.1004/// The bit count of T must be evenly divisible by 8.
1005/// This function cannot fail and cannot cause undefined behavior.1005/// 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 {
1007 if (endian == builtin.endian) {1007 if (endian == builtin.endian) {
1008 return readIntNative(T, bytes);1008 return readIntNative(T, bytes);
1009 } else {1009 } else {
...@@ -1011,11 +1011,11 @@ pub fn readInt(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8, en...@@ -1011,11 +1011,11 @@ pub fn readInt(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8, en
1011 }1011 }
1012}1012}
10131013
1014/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 01014/// Asserts that bytes.len >= @typeInfo(T).Int.bits / 8. Reads the integer starting from index 0
1015/// and ignores extra bytes.1015/// and ignores extra bytes.
1016/// The bit count of T must be evenly divisible by 8.1016/// The bit count of T must be evenly divisible by 8.
1017pub fn readIntSlice(comptime T: type, bytes: []const u8, endian: builtin.Endian) T {1017pub 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);
1019 assert(bytes.len >= n);1019 assert(bytes.len >= n);
1020 return readInt(T, bytes[0..n], endian);1020 return readInt(T, bytes[0..n], endian);
1021}1021}
...@@ -1060,7 +1060,7 @@ test "readIntBig and readIntLittle" {...@@ -1060,7 +1060,7 @@ test "readIntBig and readIntLittle" {
1060/// accepts any integer bit width.1060/// accepts any integer bit width.
1061/// This function stores in native endian, which means it is implemented as a simple1061/// This function stores in native endian, which means it is implemented as a simple
1062/// memory store.1062/// 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 {
1064 @ptrCast(*align(1) T, buf).* = value;1064 @ptrCast(*align(1) T, buf).* = value;
1065}1065}
10661066
...@@ -1068,7 +1068,7 @@ pub fn writeIntNative(comptime T: type, buf: *[(T.bit_count + 7) / 8]u8, value:...@@ -1068,7 +1068,7 @@ pub fn writeIntNative(comptime T: type, buf: *[(T.bit_count + 7) / 8]u8, value:
1068/// This function always succeeds, has defined behavior for all inputs, but1068/// This function always succeeds, has defined behavior for all inputs, but
1069/// the integer bit width must be divisible by 8.1069/// the integer bit width must be divisible by 8.
1070/// This function stores in foreign endian, which means it does a @byteSwap first.1070/// 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 {
1072 writeIntNative(T, buf, @byteSwap(T, value));1072 writeIntNative(T, buf, @byteSwap(T, value));
1073}1073}
10741074
...@@ -1085,7 +1085,7 @@ pub const writeIntBig = switch (builtin.endian) {...@@ -1085,7 +1085,7 @@ pub const writeIntBig = switch (builtin.endian) {
1085/// Writes an integer to memory, storing it in twos-complement.1085/// Writes an integer to memory, storing it in twos-complement.
1086/// This function always succeeds, has defined behavior for all inputs, but1086/// This function always succeeds, has defined behavior for all inputs, but
1087/// the integer bit width must be divisible by 8.1087/// 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 {
1089 if (endian == builtin.endian) {1089 if (endian == builtin.endian) {
1090 return writeIntNative(T, buffer, value);1090 return writeIntNative(T, buffer, value);
1091 } else {1091 } else {
...@@ -1094,19 +1094,19 @@ pub fn writeInt(comptime T: type, buffer: *[@divExact(T.bit_count, 8)]u8, value:...@@ -1094,19 +1094,19 @@ pub fn writeInt(comptime T: type, buffer: *[@divExact(T.bit_count, 8)]u8, value:
1094}1094}
10951095
1096/// Writes a twos-complement little-endian integer to memory.1096/// 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.
1098/// The bit count of T must be divisible by 8.1098/// The bit count of T must be divisible by 8.
1099/// Any extra bytes in buffer after writing the integer are set to zero. To1099/// Any extra bytes in buffer after writing the integer are set to zero. To
1100/// avoid the branch to check for extra buffer bytes, use writeIntLittle1100/// avoid the branch to check for extra buffer bytes, use writeIntLittle
1101/// instead.1101/// instead.
1102pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void {1102pub 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)
1106 return set(u8, buffer, 0);1106 return set(u8, buffer, 0);
11071107
1108 // TODO I want to call writeIntLittle here but comptime eval facilities aren't good enough1108 // 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);
1110 var bits = @truncate(uint, value);1110 var bits = @truncate(uint, value);
1111 for (buffer) |*b| {1111 for (buffer) |*b| {
1112 b.* = @truncate(u8, bits);1112 b.* = @truncate(u8, bits);
...@@ -1115,18 +1115,18 @@ pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void {...@@ -1115,18 +1115,18 @@ pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void {
1115}1115}
11161116
1117/// Writes a twos-complement big-endian integer to memory.1117/// 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.
1119/// The bit count of T must be divisible by 8.1119/// The bit count of T must be divisible by 8.
1120/// Any extra bytes in buffer before writing the integer are set to zero. To1120/// Any extra bytes in buffer before writing the integer are set to zero. To
1121/// avoid the branch to check for extra buffer bytes, use writeIntBig instead.1121/// avoid the branch to check for extra buffer bytes, use writeIntBig instead.
1122pub fn writeIntSliceBig(comptime T: type, buffer: []u8, value: T) void {1122pub 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)
1126 return set(u8, buffer, 0);1126 return set(u8, buffer, 0);
11271127
1128 // TODO I want to call writeIntBig here but comptime eval facilities aren't good enough1128 // 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);
1130 var bits = @truncate(uint, value);1130 var bits = @truncate(uint, value);
1131 var index: usize = buffer.len;1131 var index: usize = buffer.len;
1132 while (index != 0) {1132 while (index != 0) {
...@@ -1147,13 +1147,13 @@ pub const writeIntSliceForeign = switch (builtin.endian) {...@@ -1147,13 +1147,13 @@ pub const writeIntSliceForeign = switch (builtin.endian) {
1147};1147};
11481148
1149/// Writes a twos-complement integer to memory, with the specified endianness.1149/// 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.
1151/// The bit count of T must be evenly divisible by 8.1151/// The bit count of T must be evenly divisible by 8.
1152/// Any extra bytes in buffer not part of the integer are set to zero, with1152/// Any extra bytes in buffer not part of the integer are set to zero, with
1153/// respect to endianness. To avoid the branch to check for extra buffer bytes,1153/// respect to endianness. To avoid the branch to check for extra buffer bytes,
1154/// use writeInt instead.1154/// use writeInt instead.
1155pub fn writeIntSlice(comptime T: type, buffer: []u8, value: T, endian: builtin.Endian) void {1155pub 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);
1157 return switch (endian) {1157 return switch (endian) {
1158 .Little => writeIntSliceLittle(T, buffer, value),1158 .Little => writeIntSliceLittle(T, buffer, value),
1159 .Big => writeIntSliceBig(T, buffer, value),1159 .Big => writeIntSliceBig(T, buffer, value),
lib/std/mem/Allocator.zig+4-4
...@@ -159,7 +159,7 @@ fn moveBytes(...@@ -159,7 +159,7 @@ fn moveBytes(
159/// Returns a pointer to undefined memory.159/// Returns a pointer to undefined memory.
160/// Call `destroy` with the result to free the memory.160/// Call `destroy` with the result to free the memory.
161pub fn create(self: *Allocator, comptime T: type) Error!*T {161pub 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);
163 const slice = try self.allocAdvancedWithRetAddr(T, null, 1, .exact, @returnAddress());163 const slice = try self.allocAdvancedWithRetAddr(T, null, 1, .exact, @returnAddress());
164 return &slice[0];164 return &slice[0];
165}165}
...@@ -167,11 +167,11 @@ pub fn create(self: *Allocator, comptime T: type) Error!*T {...@@ -167,11 +167,11 @@ pub fn create(self: *Allocator, comptime T: type) Error!*T {
167/// `ptr` should be the return value of `create`, or otherwise167/// `ptr` should be the return value of `create`, or otherwise
168/// have the same address and alignment property.168/// have the same address and alignment property.
169pub fn destroy(self: *Allocator, ptr: anytype) void {169pub 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;
171 if (@sizeOf(T) == 0) return;172 if (@sizeOf(T) == 0) return;
172 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));173 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)], info.alignment, 0, 0, @returnAddress());
174 _ = self.shrinkBytes(non_const_ptr[0..@sizeOf(T)], ptr_align, 0, 0, @returnAddress());
175}175}
176176
177/// Allocates an array of `n` items of type `T` and sets all the177/// 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 {...@@ -705,34 +705,34 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
705pub fn cast(comptime DestType: type, target: anytype) DestType {705pub fn cast(comptime DestType: type, target: anytype) DestType {
706 const TargetType = @TypeOf(target);706 const TargetType = @TypeOf(target);
707 switch (@typeInfo(DestType)) {707 switch (@typeInfo(DestType)) {
708 .Pointer => {708 .Pointer => |dest_ptr| {
709 switch (@typeInfo(TargetType)) {709 switch (@typeInfo(TargetType)) {
710 .Int, .ComptimeInt => {710 .Int, .ComptimeInt => {
711 return @intToPtr(DestType, target);711 return @intToPtr(DestType, target);
712 },712 },
713 .Pointer => |ptr| {713 .Pointer => |ptr| {
714 return @ptrCast(DestType, @alignCast(ptr.alignment, target));714 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
715 },715 },
716 .Optional => |opt| {716 .Optional => |opt| {
717 if (@typeInfo(opt.child) == .Pointer) {717 if (@typeInfo(opt.child) == .Pointer) {
718 return @ptrCast(DestType, @alignCast(@alignOf(opt.child.Child), target));718 return @ptrCast(DestType, @alignCast(dest_ptr, target));
719 }719 }
720 },720 },
721 else => {},721 else => {},
722 }722 }
723 },723 },
724 .Optional => |opt| {724 .Optional => |dest_opt| {
725 if (@typeInfo(opt.child) == .Pointer) {725 if (@typeInfo(dest_opt.child) == .Pointer) {
726 switch (@typeInfo(TargetType)) {726 switch (@typeInfo(TargetType)) {
727 .Int, .ComptimeInt => {727 .Int, .ComptimeInt => {
728 return @intToPtr(DestType, target);728 return @intToPtr(DestType, target);
729 },729 },
730 .Pointer => |ptr| {730 .Pointer => {
731 return @ptrCast(DestType, @alignCast(ptr.alignment, target));731 return @ptrCast(DestType, @alignCast(@alignOf(dest_opt.child.Child), target));
732 },732 },
733 .Optional => |target_opt| {733 .Optional => |target_opt| {
734 if (@typeInfo(target_opt.child) == .Pointer) {734 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));
736 }736 }
737 },737 },
738 else => {},738 else => {},
lib/std/net.zig+4-1
...@@ -1164,7 +1164,7 @@ fn linuxLookupNameFromDnsSearch(...@@ -1164,7 +1164,7 @@ fn linuxLookupNameFromDnsSearch(
1164 }1164 }
11651165
1166 const search = if (rc.search.isNull() or dots >= rc.ndots or mem.endsWith(u8, name, "."))1166 const search = if (rc.search.isNull() or dots >= rc.ndots or mem.endsWith(u8, name, "."))
1167 &[_]u8{}1167 ""
1168 else1168 else
1169 rc.search.span();1169 rc.search.span();
11701170
...@@ -1641,6 +1641,9 @@ pub const StreamServer = struct {...@@ -1641,6 +1641,9 @@ pub const StreamServer = struct {
1641 /// by the socket buffer limits, not by the system memory.1641 /// by the socket buffer limits, not by the system memory.
1642 SystemResources,1642 SystemResources,
16431643
1644 /// Socket is not listening for new connections.
1645 SocketNotListening,
1646
1644 ProtocolFailure,1647 ProtocolFailure,
16451648
1646 /// Firewall rules forbid connection.1649 /// 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...@@ -2512,13 +2512,14 @@ pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) Read
2512 }2512 }
2513}2513}
25142514
2515pub const SetIdError = error{2515pub const SetEidError = error{
2516 ResourceLimitReached,
2517 InvalidUserId,2516 InvalidUserId,
2518 PermissionDenied,2517 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 {
2522 switch (errno(system.setuid(uid))) {2523 switch (errno(system.setuid(uid))) {
2523 0 => return,2524 0 => return,
2524 EAGAIN => return error.ResourceLimitReached,2525 EAGAIN => return error.ResourceLimitReached,
...@@ -2528,7 +2529,16 @@ pub fn setuid(uid: u32) SetIdError!void {...@@ -2528,7 +2529,16 @@ pub fn setuid(uid: u32) SetIdError!void {
2528 }2529 }
2529}2530}
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 {
2532 switch (errno(system.setreuid(ruid, euid))) {2542 switch (errno(system.setreuid(ruid, euid))) {
2533 0 => return,2543 0 => return,
2534 EAGAIN => return error.ResourceLimitReached,2544 EAGAIN => return error.ResourceLimitReached,
...@@ -2538,7 +2548,7 @@ pub fn setreuid(ruid: u32, euid: u32) SetIdError!void {...@@ -2538,7 +2548,7 @@ pub fn setreuid(ruid: u32, euid: u32) SetIdError!void {
2538 }2548 }
2539}2549}
25402550
2541pub fn setgid(gid: u32) SetIdError!void {2551pub fn setgid(gid: gid_t) SetIdError!void {
2542 switch (errno(system.setgid(gid))) {2552 switch (errno(system.setgid(gid))) {
2543 0 => return,2553 0 => return,
2544 EAGAIN => return error.ResourceLimitReached,2554 EAGAIN => return error.ResourceLimitReached,
...@@ -2548,7 +2558,16 @@ pub fn setgid(gid: u32) SetIdError!void {...@@ -2548,7 +2558,16 @@ pub fn setgid(gid: u32) SetIdError!void {
2548 }2558 }
2549}2559}
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 {
2552 switch (errno(system.setregid(rgid, egid))) {2571 switch (errno(system.setregid(rgid, egid))) {
2553 0 => return,2572 0 => return,
2554 EAGAIN => return error.ResourceLimitReached,2573 EAGAIN => return error.ResourceLimitReached,
...@@ -2815,6 +2834,9 @@ pub const AcceptError = error{...@@ -2815,6 +2834,9 @@ pub const AcceptError = error{
2815 /// by the socket buffer limits, not by the system memory.2834 /// by the socket buffer limits, not by the system memory.
2816 SystemResources,2835 SystemResources,
28172836
2837 /// Socket is not listening for new connections.
2838 SocketNotListening,
2839
2818 ProtocolFailure,2840 ProtocolFailure,
28192841
2820 /// Firewall rules forbid connection.2842 /// Firewall rules forbid connection.
...@@ -2884,21 +2906,21 @@ pub fn accept(...@@ -2884,21 +2906,21 @@ pub fn accept(
2884 loop.waitUntilFdReadable(sock);2906 loop.waitUntilFdReadable(sock);
2885 continue;2907 continue;
2886 } else {2908 } else {
2887 return error.WouldBlock;2909 return error.WouldBlock;
2888 },2910 },
2889 EBADF => unreachable, // always a race condition2911 EBADF => unreachable, // always a race condition
2890 ECONNABORTED => return error.ConnectionAborted,2912 ECONNABORTED => return error.ConnectionAborted,
2891 EFAULT => unreachable,2913 EFAULT => unreachable,
2892 EINVAL => unreachable,2914 EINVAL => return error.SocketNotListening,
2893 ENOTSOCK => unreachable,2915 ENOTSOCK => unreachable,
2894 EMFILE => return error.ProcessFdQuotaExceeded,2916 EMFILE => return error.ProcessFdQuotaExceeded,
2895 ENFILE => return error.SystemFdQuotaExceeded,2917 ENFILE => return error.SystemFdQuotaExceeded,
2896 ENOBUFS => return error.SystemResources,2918 ENOBUFS => return error.SystemResources,
2897 ENOMEM => return error.SystemResources,2919 ENOMEM => return error.SystemResources,
2898 EOPNOTSUPP => unreachable,2920 EOPNOTSUPP => unreachable,
2899 EPROTO => return error.ProtocolFailure,2921 EPROTO => return error.ProtocolFailure,
2900 EPERM => return error.BlockedByFirewall,2922 EPERM => return error.BlockedByFirewall,
2901 else => |err| return unexpectedErrno(err),2923 else => |err| return unexpectedErrno(err),
2902 }2924 }
2903 }2925 }
2904 } else unreachable;2926 } else unreachable;
...@@ -4554,7 +4576,7 @@ pub fn res_mkquery(...@@ -4554,7 +4576,7 @@ pub fn res_mkquery(
4554 // Make a reasonably unpredictable id4576 // Make a reasonably unpredictable id
4555 var ts: timespec = undefined;4577 var ts: timespec = undefined;
4556 clock_gettime(CLOCK_REALTIME, &ts) catch {};4578 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)));
4558 const unsec = @bitCast(UInt, ts.tv_nsec);4580 const unsec = @bitCast(UInt, ts.tv_nsec);
4559 const id = @truncate(u32, unsec + unsec / 65536);4581 const id = @truncate(u32, unsec + unsec / 65536);
4560 q[0] = @truncate(u8, id / 256);4582 q[0] = @truncate(u8, id / 256);
...@@ -5404,3 +5426,71 @@ pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {...@@ -5404,3 +5426,71 @@ pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
5404 else => |err| return std.os.unexpectedErrno(err),5426 else => |err| return std.os.unexpectedErrno(err),
5405 }5427 }
5406}5428}
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");...@@ -7,9 +7,13 @@ const std = @import("../../std.zig");
7const assert = std.debug.assert;7const assert = std.debug.assert;
8const maxInt = std.math.maxInt;8const 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
10pub const fd_t = c_int;12pub const fd_t = c_int;
11pub const pid_t = c_int;13pub const pid_t = c_int;
12pub const mode_t = c_uint;14pub const mode_t = c_uint;
15pub const uid_t = u32;
16pub const gid_t = u32;
1317
14pub const in_port_t = u16;18pub const in_port_t = u16;
15pub const sa_family_t = u8;19pub const sa_family_t = u8;
...@@ -79,8 +83,8 @@ pub const Stat = extern struct {...@@ -79,8 +83,8 @@ pub const Stat = extern struct {
79 mode: u16,83 mode: u16,
80 nlink: u16,84 nlink: u16,
81 ino: ino_t,85 ino: ino_t,
82 uid: u32,86 uid: uid_t,
83 gid: u32,87 gid: gid_t,
84 rdev: i32,88 rdev: i32,
85 atimesec: isize,89 atimesec: isize,
86 atimensec: isize,90 atimensec: isize,
lib/std/os/bits/dragonfly.zig+10-3
...@@ -9,10 +9,17 @@ const maxInt = std.math.maxInt;...@@ -9,10 +9,17 @@ const maxInt = std.math.maxInt;
9pub fn S_ISCHR(m: u32) bool {9pub fn S_ISCHR(m: u32) bool {
10 return m & S_IFMT == S_IFCHR;10 return m & S_IFMT == S_IFCHR;
11}11}
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
12pub const fd_t = c_int;17pub const fd_t = c_int;
13pub const pid_t = c_int;18pub const pid_t = c_int;
14pub const off_t = c_long;19pub const off_t = c_long;
15pub const mode_t = c_uint;20pub const mode_t = c_uint;
21pub const uid_t = u32;
22pub const gid_t = u32;
1623
17pub const ENOTSUP = EOPNOTSUPP;24pub const ENOTSUP = EOPNOTSUPP;
18pub const EWOULDBLOCK = EAGAIN;25pub const EWOULDBLOCK = EAGAIN;
...@@ -151,8 +158,8 @@ pub const Stat = extern struct {...@@ -151,8 +158,8 @@ pub const Stat = extern struct {
151 dev: c_uint,158 dev: c_uint,
152 mode: c_ushort,159 mode: c_ushort,
153 padding1: u16,160 padding1: u16,
154 uid: c_uint,161 uid: uid_t,
155 gid: c_uint,162 gid: gid_t,
156 rdev: c_uint,163 rdev: c_uint,
157 atim: timespec,164 atim: timespec,
158 mtim: timespec,165 mtim: timespec,
...@@ -511,7 +518,7 @@ pub const siginfo_t = extern struct {...@@ -511,7 +518,7 @@ pub const siginfo_t = extern struct {
511 si_errno: c_int,518 si_errno: c_int,
512 si_code: c_int,519 si_code: c_int,
513 si_pid: c_int,520 si_pid: c_int,
514 si_uid: c_uint,521 si_uid: uid_t,
515 si_status: c_int,522 si_status: c_int,
516 si_addr: ?*c_void,523 si_addr: ?*c_void,
517 si_value: union_sigval,524 si_value: union_sigval,
lib/std/os/bits/freebsd.zig+6-2
...@@ -6,8 +6,12 @@...@@ -6,8 +6,12 @@
6const std = @import("../../std.zig");6const std = @import("../../std.zig");
7const maxInt = std.math.maxInt;7const 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
9pub const fd_t = c_int;11pub const fd_t = c_int;
10pub const pid_t = c_int;12pub const pid_t = c_int;
13pub const uid_t = u32;
14pub const gid_t = u32;
11pub const mode_t = c_uint;15pub const mode_t = c_uint;
1216
13pub const socklen_t = u32;17pub const socklen_t = u32;
...@@ -128,8 +132,8 @@ pub const Stat = extern struct {...@@ -128,8 +132,8 @@ pub const Stat = extern struct {
128132
129 mode: u16,133 mode: u16,
130 __pad0: u16,134 __pad0: u16,
131 uid: u32,135 uid: uid_t,
132 gid: u32,136 gid: gid_t,
133 __pad1: u32,137 __pad1: u32,
134 rdev: u64,138 rdev: u64,
135139
lib/std/os/bits/linux.zig+5-5
...@@ -29,7 +29,7 @@ const is_mips = builtin.arch.isMIPS();...@@ -29,7 +29,7 @@ const is_mips = builtin.arch.isMIPS();
2929
30pub const pid_t = i32;30pub const pid_t = i32;
31pub const fd_t = i32;31pub const fd_t = i32;
32pub const uid_t = i32;32pub const uid_t = u32;
33pub const gid_t = u32;33pub const gid_t = u32;
34pub const clock_t = isize;34pub const clock_t = isize;
3535
...@@ -846,14 +846,14 @@ pub const SIG_ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize));...@@ -846,14 +846,14 @@ pub const SIG_ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize));
846pub const SIG_DFL = @intToPtr(?Sigaction.sigaction_fn, 0);846pub const SIG_DFL = @intToPtr(?Sigaction.sigaction_fn, 0);
847pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1);847pub 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
851pub const signalfd_siginfo = extern struct {851pub const signalfd_siginfo = extern struct {
852 signo: u32,852 signo: u32,
853 errno: i32,853 errno: i32,
854 code: i32,854 code: i32,
855 pid: u32,855 pid: u32,
856 uid: u32,856 uid: uid_t,
857 fd: i32,857 fd: i32,
858 tid: u32,858 tid: u32,
859 band: u32,859 band: u32,
...@@ -1491,10 +1491,10 @@ pub const Statx = extern struct {...@@ -1491,10 +1491,10 @@ pub const Statx = extern struct {
1491 nlink: u32,1491 nlink: u32,
14921492
1493 /// User ID of owner1493 /// User ID of owner
1494 uid: u32,1494 uid: uid_t,
14951495
1496 /// Group ID of owner1496 /// Group ID of owner
1497 gid: u32,1497 gid: gid_t,
14981498
1499 /// File type and mode1499 /// File type and mode
1500 mode: u16,1500 mode: u16,
lib/std/os/bits/linux/x86_64.zig+3-2
...@@ -7,6 +7,7 @@...@@ -7,6 +7,7 @@
7const std = @import("../../../std.zig");7const std = @import("../../../std.zig");
8const pid_t = linux.pid_t;8const pid_t = linux.pid_t;
9const uid_t = linux.uid_t;9const uid_t = linux.uid_t;
10const gid_t = linux.gid_t;
10const clock_t = linux.clock_t;11const clock_t = linux.clock_t;
11const stack_t = linux.stack_t;12const stack_t = linux.stack_t;
12const sigset_t = linux.sigset_t;13const sigset_t = linux.sigset_t;
...@@ -523,8 +524,8 @@ pub const Stat = extern struct {...@@ -523,8 +524,8 @@ pub const Stat = extern struct {
523 nlink: usize,524 nlink: usize,
524525
525 mode: u32,526 mode: u32,
526 uid: u32,527 uid: uid_t,
527 gid: u32,528 gid: gid_t,
528 __pad0: u32,529 __pad0: u32,
529 rdev: u64,530 rdev: u64,
530 size: off_t,531 size: off_t,
lib/std/os/linux.zig+61-29
...@@ -655,7 +655,7 @@ pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize {...@@ -655,7 +655,7 @@ pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize {
655 return syscall2(.nanosleep, @ptrToInt(req), @ptrToInt(rem));655 return syscall2(.nanosleep, @ptrToInt(req), @ptrToInt(rem));
656}656}
657657
658pub fn setuid(uid: u32) usize {658pub fn setuid(uid: uid_t) usize {
659 if (@hasField(SYS, "setuid32")) {659 if (@hasField(SYS, "setuid32")) {
660 return syscall1(.setuid32, uid);660 return syscall1(.setuid32, uid);
661 } else {661 } else {
...@@ -663,7 +663,7 @@ pub fn setuid(uid: u32) usize {...@@ -663,7 +663,7 @@ pub fn setuid(uid: u32) usize {
663 }663 }
664}664}
665665
666pub fn setgid(gid: u32) usize {666pub fn setgid(gid: gid_t) usize {
667 if (@hasField(SYS, "setgid32")) {667 if (@hasField(SYS, "setgid32")) {
668 return syscall1(.setgid32, gid);668 return syscall1(.setgid32, gid);
669 } else {669 } else {
...@@ -671,7 +671,7 @@ pub fn setgid(gid: u32) usize {...@@ -671,7 +671,7 @@ pub fn setgid(gid: u32) usize {
671 }671 }
672}672}
673673
674pub fn setreuid(ruid: u32, euid: u32) usize {674pub fn setreuid(ruid: uid_t, euid: uid_t) usize {
675 if (@hasField(SYS, "setreuid32")) {675 if (@hasField(SYS, "setreuid32")) {
676 return syscall2(.setreuid32, ruid, euid);676 return syscall2(.setreuid32, ruid, euid);
677 } else {677 } else {
...@@ -679,7 +679,7 @@ pub fn setreuid(ruid: u32, euid: u32) usize {...@@ -679,7 +679,7 @@ pub fn setreuid(ruid: u32, euid: u32) usize {
679 }679 }
680}680}
681681
682pub fn setregid(rgid: u32, egid: u32) usize {682pub fn setregid(rgid: gid_t, egid: gid_t) usize {
683 if (@hasField(SYS, "setregid32")) {683 if (@hasField(SYS, "setregid32")) {
684 return syscall2(.setregid32, rgid, egid);684 return syscall2(.setregid32, rgid, egid);
685 } else {685 } else {
...@@ -687,47 +687,61 @@ pub fn setregid(rgid: u32, egid: u32) usize {...@@ -687,47 +687,61 @@ pub fn setregid(rgid: u32, egid: u32) usize {
687 }687 }
688}688}
689689
690pub fn getuid() u32 {690pub fn getuid() uid_t {
691 if (@hasField(SYS, "getuid32")) {691 if (@hasField(SYS, "getuid32")) {
692 return @as(u32, syscall0(.getuid32));692 return @as(uid_t, syscall0(.getuid32));
693 } else {693 } else {
694 return @as(u32, syscall0(.getuid));694 return @as(uid_t, syscall0(.getuid));
695 }695 }
696}696}
697697
698pub fn getgid() u32 {698pub fn getgid() gid_t {
699 if (@hasField(SYS, "getgid32")) {699 if (@hasField(SYS, "getgid32")) {
700 return @as(u32, syscall0(.getgid32));700 return @as(gid_t, syscall0(.getgid32));
701 } else {701 } else {
702 return @as(u32, syscall0(.getgid));702 return @as(gid_t, syscall0(.getgid));
703 }703 }
704}704}
705705
706pub fn geteuid() u32 {706pub fn geteuid() uid_t {
707 if (@hasField(SYS, "geteuid32")) {707 if (@hasField(SYS, "geteuid32")) {
708 return @as(u32, syscall0(.geteuid32));708 return @as(uid_t, syscall0(.geteuid32));
709 } else {709 } else {
710 return @as(u32, syscall0(.geteuid));710 return @as(uid_t, syscall0(.geteuid));
711 }711 }
712}712}
713713
714pub fn getegid() u32 {714pub fn getegid() gid_t {
715 if (@hasField(SYS, "getegid32")) {715 if (@hasField(SYS, "getegid32")) {
716 return @as(u32, syscall0(.getegid32));716 return @as(gid_t, syscall0(.getegid32));
717 } else {717 } else {
718 return @as(u32, syscall0(.getegid));718 return @as(gid_t, syscall0(.getegid));
719 }719 }
720}720}
721721
722pub fn seteuid(euid: u32) usize {722pub fn seteuid(euid: uid_t) usize {
723 return setreuid(std.math.maxInt(u32), euid);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));
724}731}
725732
726pub fn setegid(egid: u32) usize {733pub fn setegid(egid: gid_t) usize {
727 return setregid(std.math.maxInt(u32), egid);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));
728}742}
729743
730pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize {744pub fn getresuid(ruid: *uid_t, euid: *uid_t, suid: *uid_t) usize {
731 if (@hasField(SYS, "getresuid32")) {745 if (@hasField(SYS, "getresuid32")) {
732 return syscall3(.getresuid32, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid));746 return syscall3(.getresuid32, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid));
733 } else {747 } else {
...@@ -735,7 +749,7 @@ pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize {...@@ -735,7 +749,7 @@ pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize {
735 }749 }
736}750}
737751
738pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize {752pub fn getresgid(rgid: *gid_t, egid: *gid_t, sgid: *gid_t) usize {
739 if (@hasField(SYS, "getresgid32")) {753 if (@hasField(SYS, "getresgid32")) {
740 return syscall3(.getresgid32, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid));754 return syscall3(.getresgid32, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid));
741 } else {755 } else {
...@@ -743,7 +757,7 @@ pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize {...@@ -743,7 +757,7 @@ pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize {
743 }757 }
744}758}
745759
746pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {760pub fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) usize {
747 if (@hasField(SYS, "setresuid32")) {761 if (@hasField(SYS, "setresuid32")) {
748 return syscall3(.setresuid32, ruid, euid, suid);762 return syscall3(.setresuid32, ruid, euid, suid);
749 } else {763 } else {
...@@ -751,7 +765,7 @@ pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {...@@ -751,7 +765,7 @@ pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {
751 }765 }
752}766}
753767
754pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {768pub fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) usize {
755 if (@hasField(SYS, "setresgid32")) {769 if (@hasField(SYS, "setresgid32")) {
756 return syscall3(.setresgid32, rgid, egid, sgid);770 return syscall3(.setresgid32, rgid, egid, sgid);
757 } else {771 } else {
...@@ -759,7 +773,7 @@ pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {...@@ -759,7 +773,7 @@ pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {
759 }773 }
760}774}
761775
762pub fn getgroups(size: usize, list: *u32) usize {776pub fn getgroups(size: usize, list: *gid_t) usize {
763 if (@hasField(SYS, "getgroups32")) {777 if (@hasField(SYS, "getgroups32")) {
764 return syscall2(.getgroups32, size, @ptrToInt(list));778 return syscall2(.getgroups32, size, @ptrToInt(list));
765 } else {779 } else {
...@@ -767,7 +781,7 @@ pub fn getgroups(size: usize, list: *u32) usize {...@@ -767,7 +781,7 @@ pub fn getgroups(size: usize, list: *u32) usize {
767 }781 }
768}782}
769783
770pub fn setgroups(size: usize, list: *const u32) usize {784pub fn setgroups(size: usize, list: *const gid_t) usize {
771 if (@hasField(SYS, "setgroups32")) {785 if (@hasField(SYS, "setgroups32")) {
772 return syscall2(.setgroups32, size, @ptrToInt(list));786 return syscall2(.setgroups32, size, @ptrToInt(list));
773 } else {787 } else {
...@@ -815,17 +829,19 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti...@@ -815,17 +829,19 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti
815 return 0;829 return 0;
816}830}
817831
832const usize_bits = @typeInfo(usize).Int.bits;
833
818pub fn sigaddset(set: *sigset_t, sig: u6) void {834pub fn sigaddset(set: *sigset_t, sig: u6) void {
819 const s = sig - 1;835 const s = sig - 1;
820 // shift in musl: s&8*sizeof *set->__bits-1836 // 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));
822 const val = @intCast(u32, 1) << shift;838 const val = @intCast(u32, 1) << shift;
823 (set.*)[@intCast(usize, s) / usize.bit_count] |= val;839 (set.*)[@intCast(usize, s) / usize_bits] |= val;
824}840}
825841
826pub fn sigismember(set: *const sigset_t, sig: u6) bool {842pub fn sigismember(set: *const sigset_t, sig: u6) bool {
827 const s = sig - 1;843 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;
829}845}
830846
831pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {847pub 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 {...@@ -1226,6 +1242,22 @@ pub fn bpf(cmd: BPF.Cmd, attr: *BPF.Attr, size: u32) usize {
1226 return syscall3(.bpf, @enumToInt(cmd), @ptrToInt(attr), size);1242 return syscall3(.bpf, @enumToInt(cmd), @ptrToInt(attr), size);
1227}1243}
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
1229test "" {1261test "" {
1230 if (builtin.os.tag == .linux) {1262 if (builtin.os.tag == .linux) {
1231 _ = @import("linux/test.zig");1263 _ = @import("linux/test.zig");
lib/std/os/linux/bpf.zig+758-68
...@@ -3,9 +3,13 @@...@@ -3,9 +3,13 @@
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.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 copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6usingnamespace std.os;6usingnamespace std.os.linux;
7const std = @import("../../std.zig");7const std = @import("../../std.zig");
8const errno = getErrno;
9const unexpectedErrno = std.os.unexpectedErrno;
8const expectEqual = std.testing.expectEqual;10const expectEqual = std.testing.expectEqual;
11const expectError = std.testing.expectError;
12const expect = std.testing.expect;
913
10// instruction classes14// instruction classes
11pub const LD = 0x00;15pub const LD = 0x00;
...@@ -62,6 +66,7 @@ pub const MAXINSNS = 4096;...@@ -62,6 +66,7 @@ pub const MAXINSNS = 4096;
62// instruction classes66// instruction classes
63/// jmp mode in word width67/// jmp mode in word width
64pub const JMP32 = 0x06;68pub const JMP32 = 0x06;
69
65/// alu mode in double word width70/// alu mode in double word width
66pub const ALU64 = 0x07;71pub const ALU64 = 0x07;
6772
...@@ -72,14 +77,17 @@ pub const XADD = 0xc0;...@@ -72,14 +77,17 @@ pub const XADD = 0xc0;
72// alu/jmp fields77// alu/jmp fields
73/// mov reg to reg78/// mov reg to reg
74pub const MOV = 0xb0;79pub const MOV = 0xb0;
80
75/// sign extending arithmetic shift right */81/// sign extending arithmetic shift right */
76pub const ARSH = 0xc0;82pub const ARSH = 0xc0;
7783
78// change endianness of a register84// change endianness of a register
79/// flags for endianness conversion:85/// flags for endianness conversion:
80pub const END = 0xd0;86pub const END = 0xd0;
87
81/// convert to little-endian */88/// convert to little-endian */
82pub const TO_LE = 0x00;89pub const TO_LE = 0x00;
90
83/// convert to big-endian91/// convert to big-endian
84pub const TO_BE = 0x08;92pub const TO_BE = 0x08;
85pub const FROM_LE = TO_LE;93pub const FROM_LE = TO_LE;
...@@ -88,29 +96,39 @@ pub const FROM_BE = TO_BE;...@@ -88,29 +96,39 @@ pub const FROM_BE = TO_BE;
88// jmp encodings96// jmp encodings
89/// jump != *97/// jump != *
90pub const JNE = 0x50;98pub const JNE = 0x50;
99
91/// LT is unsigned, '<'100/// LT is unsigned, '<'
92pub const JLT = 0xa0;101pub const JLT = 0xa0;
102
93/// LE is unsigned, '<=' *103/// LE is unsigned, '<=' *
94pub const JLE = 0xb0;104pub const JLE = 0xb0;
105
95/// SGT is signed '>', GT in x86106/// SGT is signed '>', GT in x86
96pub const JSGT = 0x60;107pub const JSGT = 0x60;
108
97/// SGE is signed '>=', GE in x86109/// SGE is signed '>=', GE in x86
98pub const JSGE = 0x70;110pub const JSGE = 0x70;
111
99/// SLT is signed, '<'112/// SLT is signed, '<'
100pub const JSLT = 0xc0;113pub const JSLT = 0xc0;
114
101/// SLE is signed, '<='115/// SLE is signed, '<='
102pub const JSLE = 0xd0;116pub const JSLE = 0xd0;
117
103/// function call118/// function call
104pub const CALL = 0x80;119pub const CALL = 0x80;
120
105/// function return121/// function return
106pub const EXIT = 0x90;122pub const EXIT = 0x90;
107123
108/// Flag for prog_attach command. If a sub-cgroup installs some bpf program, the124/// Flag for prog_attach command. If a sub-cgroup installs some bpf program, the
109/// program in this cgroup yields to sub-cgroup program.125/// program in this cgroup yields to sub-cgroup program.
110pub const F_ALLOW_OVERRIDE = 0x1;126pub const F_ALLOW_OVERRIDE = 0x1;
127
111/// Flag for prog_attach command. If a sub-cgroup installs some bpf program,128/// Flag for prog_attach command. If a sub-cgroup installs some bpf program,
112/// that cgroup program gets run in addition to the program in this cgroup.129/// that cgroup program gets run in addition to the program in this cgroup.
113pub const F_ALLOW_MULTI = 0x2;130pub const F_ALLOW_MULTI = 0x2;
131
114/// Flag for prog_attach command.132/// Flag for prog_attach command.
115pub const F_REPLACE = 0x4;133pub const F_REPLACE = 0x4;
116134
...@@ -164,47 +182,61 @@ pub const PSEUDO_CALL = 1;...@@ -164,47 +182,61 @@ pub const PSEUDO_CALL = 1;
164182
165/// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing183/// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing
166pub const ANY = 0;184pub const ANY = 0;
185
167/// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist186/// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist
168pub const NOEXIST = 1;187pub const NOEXIST = 1;
188
169/// flag for BPF_MAP_UPDATE_ELEM command. update existing element189/// flag for BPF_MAP_UPDATE_ELEM command. update existing element
170pub const EXIST = 2;190pub const EXIST = 2;
191
171/// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update192/// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update
172pub const F_LOCK = 4;193pub const F_LOCK = 4;
173194
174/// flag for BPF_MAP_CREATE command */195/// flag for BPF_MAP_CREATE command */
175pub const BPF_F_NO_PREALLOC = 0x1;196pub const BPF_F_NO_PREALLOC = 0x1;
197
176/// flag for BPF_MAP_CREATE command. Instead of having one common LRU list in198/// flag for BPF_MAP_CREATE command. Instead of having one common LRU list in
177/// the BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list which can199/// the BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list which can
178/// scale and perform better. Note, the LRU nodes (including free nodes) cannot200/// scale and perform better. Note, the LRU nodes (including free nodes) cannot
179/// be moved across different LRU lists.201/// be moved across different LRU lists.
180pub const BPF_F_NO_COMMON_LRU = 0x2;202pub const BPF_F_NO_COMMON_LRU = 0x2;
203
181/// flag for BPF_MAP_CREATE command. Specify numa node during map creation204/// flag for BPF_MAP_CREATE command. Specify numa node during map creation
182pub const BPF_F_NUMA_NODE = 0x4;205pub const BPF_F_NUMA_NODE = 0x4;
206
183/// flag for BPF_MAP_CREATE command. Flags for BPF object read access from207/// flag for BPF_MAP_CREATE command. Flags for BPF object read access from
184/// syscall side208/// syscall side
185pub const BPF_F_RDONLY = 0x8;209pub const BPF_F_RDONLY = 0x8;
210
186/// flag for BPF_MAP_CREATE command. Flags for BPF object write access from211/// flag for BPF_MAP_CREATE command. Flags for BPF object write access from
187/// syscall side212/// syscall side
188pub const BPF_F_WRONLY = 0x10;213pub const BPF_F_WRONLY = 0x10;
214
189/// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset215/// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset
190/// instead of pointer216/// instead of pointer
191pub const BPF_F_STACK_BUILD_ID = 0x20;217pub const BPF_F_STACK_BUILD_ID = 0x20;
218
192/// flag for BPF_MAP_CREATE command. Zero-initialize hash function seed. This219/// flag for BPF_MAP_CREATE command. Zero-initialize hash function seed. This
193/// should only be used for testing.220/// should only be used for testing.
194pub const BPF_F_ZERO_SEED = 0x40;221pub const BPF_F_ZERO_SEED = 0x40;
222
195/// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program223/// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program
196/// side.224/// side.
197pub const BPF_F_RDONLY_PROG = 0x80;225pub const BPF_F_RDONLY_PROG = 0x80;
226
198/// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program227/// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program
199/// side.228/// side.
200pub const BPF_F_WRONLY_PROG = 0x100;229pub const BPF_F_WRONLY_PROG = 0x100;
230
201/// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted231/// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted
202/// socket232/// socket
203pub const BPF_F_CLONE = 0x200;233pub const BPF_F_CLONE = 0x200;
234
204/// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map235/// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map
205pub const BPF_F_MMAPABLE = 0x400;236pub const BPF_F_MMAPABLE = 0x400;
206237
207/// These values correspond to "syscalls" within the BPF program's environment238/// These values correspond to "syscalls" within the BPF program's environment,
239/// each one is documented in std.os.linux.BPF.kern
208pub const Helper = enum(i32) {240pub const Helper = enum(i32) {
209 unspec,241 unspec,
210 map_lookup_elem,242 map_lookup_elem,
...@@ -325,9 +357,34 @@ pub const Helper = enum(i32) {...@@ -325,9 +357,34 @@ pub const Helper = enum(i32) {
325 tcp_send_ack,357 tcp_send_ack,
326 send_signal_thread,358 send_signal_thread,
327 jiffies64,359 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,
328 _,383 _,
329};384};
330385
386// TODO: determine that this is the expected bit layout for both little and big
387// endian systems
331/// a single BPF instruction388/// a single BPF instruction
332pub const Insn = packed struct {389pub const Insn = packed struct {
333 code: u8,390 code: u8,
...@@ -340,19 +397,30 @@ pub const Insn = packed struct {...@@ -340,19 +397,30 @@ pub const Insn = packed struct {
340 /// frame397 /// frame
341 pub const Reg = packed enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 };398 pub const Reg = packed enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 };
342 const Source = packed enum(u1) { reg, imm };399 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
343 const AluOp = packed enum(u8) {410 const AluOp = packed enum(u8) {
344 add = ADD,411 add = ADD,
345 sub = SUB,412 sub = SUB,
346 mul = MUL,413 mul = MUL,
347 div = DIV,414 div = DIV,
348 op_or = OR,415 alu_or = OR,
349 op_and = AND,416 alu_and = AND,
350 lsh = LSH,417 lsh = LSH,
351 rsh = RSH,418 rsh = RSH,
352 neg = NEG,419 neg = NEG,
353 mod = MOD,420 mod = MOD,
354 xor = XOR,421 xor = XOR,
355 mov = MOV,422 mov = MOV,
423 arsh = ARSH,
356 };424 };
357425
358 pub const Size = packed enum(u8) {426 pub const Size = packed enum(u8) {
...@@ -368,6 +436,13 @@ pub const Insn = packed struct {...@@ -368,6 +436,13 @@ pub const Insn = packed struct {
368 jgt = JGT,436 jgt = JGT,
369 jge = JGE,437 jge = JGE,
370 jset = JSET,438 jset = JSET,
439 jlt = JLT,
440 jle = JLE,
441 jne = JNE,
442 jsgt = JSGT,
443 jsge = JSGE,
444 jslt = JSLT,
445 jsle = JSLE,
371 };446 };
372447
373 const ImmOrReg = union(Source) {448 const ImmOrReg = union(Source) {
...@@ -419,22 +494,100 @@ pub const Insn = packed struct {...@@ -419,22 +494,100 @@ pub const Insn = packed struct {
419 return alu(64, .add, dst, src);494 return alu(64, .add, dst, src);
420 }495 }
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
422 fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn {541 fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn {
423 return imm_reg(JMP | @enumToInt(op), dst, src, off);542 return imm_reg(JMP | @enumToInt(op), dst, src, off);
424 }543 }
425544
545 pub fn ja(off: i16) Insn {
546 return jmp(.ja, .r0, 0, off);
547 }
548
426 pub fn jeq(dst: Reg, src: anytype, off: i16) Insn {549 pub fn jeq(dst: Reg, src: anytype, off: i16) Insn {
427 return jmp(.jeq, dst, src, off);550 return jmp(.jeq, dst, src, off);
428 }551 }
429552
430 pub fn stx_mem(size: Size, dst: Reg, src: Reg, off: i16) Insn {553 pub fn jgt(dst: Reg, src: anytype, off: i16) Insn {
431 return Insn{554 return jmp(.jgt, dst, src, off);
432 .code = STX | @enumToInt(size) | MEM,555 }
433 .dst = @enumToInt(dst),556
434 .src = @enumToInt(src),557 pub fn jge(dst: Reg, src: anytype, off: i16) Insn {
435 .off = off,558 return jmp(.jge, dst, src, off);
436 .imm = 0,559 }
437 };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);
438 }591 }
439592
440 pub fn xadd(dst: Reg, src: Reg) Insn {593 pub fn xadd(dst: Reg, src: Reg) Insn {
...@@ -447,17 +600,34 @@ pub const Insn = packed struct {...@@ -447,17 +600,34 @@ pub const Insn = packed struct {
447 };600 };
448 }601 }
449602
450 /// direct packet access, R0 = *(uint *)(skb->data + imm32)603 fn ld(mode: Mode, size: Size, dst: Reg, src: Reg, imm: i32) Insn {
451 pub fn ld_abs(size: Size, imm: i32) Insn {
452 return Insn{604 return Insn{
453 .code = LD | @enumToInt(size) | ABS,605 .code = @enumToInt(mode) | @enumToInt(size) | LD,
454 .dst = 0,606 .dst = @enumToInt(dst),
455 .src = 0,607 .src = @enumToInt(src),
456 .off = 0,608 .off = 0,
457 .imm = imm,609 .imm = imm,
458 };610 };
459 }611 }
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
461 fn ld_imm_impl1(dst: Reg, src: Reg, imm: u64) Insn {631 fn ld_imm_impl1(dst: Reg, src: Reg, imm: u64) Insn {
462 return Insn{632 return Insn{
463 .code = LD | DW | IMM,633 .code = LD | DW | IMM,
...@@ -478,6 +648,14 @@ pub const Insn = packed struct {...@@ -478,6 +648,14 @@ pub const Insn = packed struct {
478 };648 };
479 }649 }
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
481 pub fn ld_map_fd1(dst: Reg, map_fd: fd_t) Insn {659 pub fn ld_map_fd1(dst: Reg, map_fd: fd_t) Insn {
482 return ld_imm_impl1(dst, @intToEnum(Reg, PSEUDO_MAP_FD), @intCast(u64, map_fd));660 return ld_imm_impl1(dst, @intToEnum(Reg, PSEUDO_MAP_FD), @intCast(u64, map_fd));
483 }661 }
...@@ -486,6 +664,53 @@ pub const Insn = packed struct {...@@ -486,6 +664,53 @@ pub const Insn = packed struct {
486 return ld_imm_impl2(@intCast(u64, map_fd));664 return ld_imm_impl2(@intCast(u64, map_fd));
487 }665 }
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
489 pub fn call(helper: Helper) Insn {714 pub fn call(helper: Helper) Insn {
490 return Insn{715 return Insn{
491 .code = JMP | CALL,716 .code = JMP | CALL,
...@@ -508,95 +733,242 @@ pub const Insn = packed struct {...@@ -508,95 +733,242 @@ pub const Insn = packed struct {
508 }733 }
509};734};
510735
511fn expect_insn(insn: Insn, val: u64) void {
512 expectEqual(@bitCast(u64, insn), val);
513}
514
515test "insn bitsize" {736test "insn bitsize" {
516 expectEqual(@bitSizeOf(Insn), 64);737 expectEqual(@bitSizeOf(Insn), 64);
517}738}
518739
519// mov instructions740fn expect_opcode(code: u8, insn: Insn) void {
520test "mov imm" {741 expectEqual(code, insn.code);
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);
531}742}
532743
533// ld instructions744// The opcodes were grabbed from https://github.com/iovisor/bpf-docs/blob/master/eBPF.md
534test "ld_abs" {745test "opcodes" {
535 expect_insn(Insn.ld_abs(.byte, 42), 0x0000002a00000030);746 // instructions that have a name that end with 1 or 2 are consecutive for
536}747 // loading 64-bit immediates (imm is only 32 bits wide)
537748
538test "ld_map_fd" {749 // alu instructions
539 expect_insn(Insn.ld_map_fd1(.r1, 42), 0x0000002a00001118);750 expect_opcode(0x07, Insn.add(.r1, 0));
540 expect_insn(Insn.ld_map_fd2(42), 0x0000000000000000);751 expect_opcode(0x0f, Insn.add(.r1, .r2));
541}752 expect_opcode(0x17, Insn.sub(.r1, 0));
542753 expect_opcode(0x1f, Insn.sub(.r1, .r2));
543// st instructions754 expect_opcode(0x27, Insn.mul(.r1, 0));
544test "stx_mem" {755 expect_opcode(0x2f, Insn.mul(.r1, .r2));
545 expect_insn(Insn.stx_mem(.word, .r10, .r0, -4), 0x00000000fffc0a63);756 expect_opcode(0x37, Insn.div(.r1, 0));
546}757 expect_opcode(0x3f, Insn.div(.r1, .r2));
547758 expect_opcode(0x47, Insn.alu_or(.r1, 0));
548test "xadd" {759 expect_opcode(0x4f, Insn.alu_or(.r1, .r2));
549 expect_insn(Insn.xadd(.r0, .r1), 0x00000000000010db);760 expect_opcode(0x57, Insn.alu_and(.r1, 0));
550}761 expect_opcode(0x5f, Insn.alu_and(.r1, .r2));
551762 expect_opcode(0x67, Insn.lsh(.r1, 0));
552// jmp instructions763 expect_opcode(0x6f, Insn.lsh(.r1, .r2));
553test "jeq imm" {764 expect_opcode(0x77, Insn.rsh(.r1, 0));
554 expect_insn(Insn.jeq(.r0, 0, 2), 0x0000000000020015);765 expect_opcode(0x7f, Insn.rsh(.r1, .r2));
555}766 expect_opcode(0x87, Insn.neg(.r1));
556767 expect_opcode(0x97, Insn.mod(.r1, 0));
557// other instructions768 expect_opcode(0x9f, Insn.mod(.r1, .r2));
558test "call" {769 expect_opcode(0xa7, Insn.xor(.r1, 0));
559 expect_insn(Insn.call(.map_lookup_elem), 0x0000000100000085);770 expect_opcode(0xaf, Insn.xor(.r1, .r2));
560}771 expect_opcode(0xb7, Insn.mov(.r1, 0));
561772 expect_opcode(0xbf, Insn.mov(.r1, .r2));
562test "exit" {773 expect_opcode(0xc7, Insn.arsh(.r1, 0));
563 expect_insn(Insn.exit(), 0x0000000000000095);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());
564}852}
565853
566pub const Cmd = extern enum(usize) {854pub 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
567 map_create,860 map_create,
861
862 /// Look up an element by key in a specified map and return its value.
863 ///
864 /// uses MapElemAttr
568 map_lookup_elem,865 map_lookup_elem,
866
867 /// Create or update an element (key/value pair) in a specified map.
868 ///
869 /// uses MapElemAttr
569 map_update_elem,870 map_update_elem,
871
872 /// Look up and delete an element by key in a specified map.
873 ///
874 /// uses MapElemAttr
570 map_delete_elem,875 map_delete_elem,
876
877 /// Look up an element by key in a specified map and return the key of the
878 /// next element.
571 map_get_next_key,879 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
572 prog_load,886 prog_load,
887
888 /// Pin a map or eBPF program to a path within the minimal BPF filesystem
889 ///
890 /// uses ObjAttr
573 obj_pin,891 obj_pin,
892
893 /// Get the file descriptor of a BPF object pinned to a certain path
894 ///
895 /// uses ObjAttr
574 obj_get,896 obj_get,
897
898 /// uses ProgAttachAttr
575 prog_attach,899 prog_attach,
900
901 /// uses ProgAttachAttr
576 prog_detach,902 prog_detach,
903
904 /// uses TestRunAttr
577 prog_test_run,905 prog_test_run,
906
907 /// uses GetIdAttr
578 prog_get_next_id,908 prog_get_next_id,
909
910 /// uses GetIdAttr
579 map_get_next_id,911 map_get_next_id,
912
913 /// uses GetIdAttr
580 prog_get_fd_by_id,914 prog_get_fd_by_id,
915
916 /// uses GetIdAttr
581 map_get_fd_by_id,917 map_get_fd_by_id,
918
919 /// uses InfoAttr
582 obj_get_info_by_fd,920 obj_get_info_by_fd,
921
922 /// uses QueryAttr
583 prog_query,923 prog_query,
924
925 /// uses RawTracepointAttr
584 raw_tracepoint_open,926 raw_tracepoint_open,
927
928 /// uses BtfLoadAttr
585 btf_load,929 btf_load,
930
931 /// uses GetIdAttr
586 btf_get_fd_by_id,932 btf_get_fd_by_id,
933
934 /// uses TaskFdQueryAttr
587 task_fd_query,935 task_fd_query,
936
937 /// uses MapElemAttr
588 map_lookup_and_delete_elem,938 map_lookup_and_delete_elem,
589 map_freeze,939 map_freeze,
940
941 /// uses GetIdAttr
590 btf_get_next_id,942 btf_get_next_id,
943
944 /// uses MapBatchAttr
591 map_lookup_batch,945 map_lookup_batch,
946
947 /// uses MapBatchAttr
592 map_lookup_and_delete_batch,948 map_lookup_and_delete_batch,
949
950 /// uses MapBatchAttr
593 map_update_batch,951 map_update_batch,
952
953 /// uses MapBatchAttr
594 map_delete_batch,954 map_delete_batch,
955
956 /// uses LinkCreateAttr
595 link_create,957 link_create,
958
959 /// uses LinkUpdateAttr
596 link_update,960 link_update,
961
962 /// uses GetIdAttr
597 link_get_fd_by_id,963 link_get_fd_by_id,
964
965 /// uses GetIdAttr
598 link_get_next_id,966 link_get_next_id,
967
968 /// uses EnableStatsAttr
599 enable_stats,969 enable_stats,
970
971 /// uses IterCreateAttr
600 iter_create,972 iter_create,
601 link_detach,973 link_detach,
602 _,974 _,
...@@ -630,42 +1002,138 @@ pub const MapType = extern enum(u32) {...@@ -630,42 +1002,138 @@ pub const MapType = extern enum(u32) {
630 sk_storage,1002 sk_storage,
631 devmap_hash,1003 devmap_hash,
632 struct_ops,1004 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
633 ringbuf,1039 ringbuf,
1040
634 _,1041 _,
635};1042};
6361043
637pub const ProgType = extern enum(u32) {1044pub const ProgType = extern enum(u32) {
638 unspec,1045 unspec,
1046
1047 /// context type: __sk_buff
639 socket_filter,1048 socket_filter,
1049
1050 /// context type: bpf_user_pt_regs_t
640 kprobe,1051 kprobe,
1052
1053 /// context type: __sk_buff
641 sched_cls,1054 sched_cls,
1055
1056 /// context type: __sk_buff
642 sched_act,1057 sched_act,
1058
1059 /// context type: u64
643 tracepoint,1060 tracepoint,
1061
1062 /// context type: xdp_md
644 xdp,1063 xdp,
1064
1065 /// context type: bpf_perf_event_data
645 perf_event,1066 perf_event,
1067
1068 /// context type: __sk_buff
646 cgroup_skb,1069 cgroup_skb,
1070
1071 /// context type: bpf_sock
647 cgroup_sock,1072 cgroup_sock,
1073
1074 /// context type: __sk_buff
648 lwt_in,1075 lwt_in,
1076
1077 /// context type: __sk_buff
649 lwt_out,1078 lwt_out,
1079
1080 /// context type: __sk_buff
650 lwt_xmit,1081 lwt_xmit,
1082
1083 /// context type: bpf_sock_ops
651 sock_ops,1084 sock_ops,
1085
1086 /// context type: __sk_buff
652 sk_skb,1087 sk_skb,
1088
1089 /// context type: bpf_cgroup_dev_ctx
653 cgroup_device,1090 cgroup_device,
1091
1092 /// context type: sk_msg_md
654 sk_msg,1093 sk_msg,
1094
1095 /// context type: bpf_raw_tracepoint_args
655 raw_tracepoint,1096 raw_tracepoint,
1097
1098 /// context type: bpf_sock_addr
656 cgroup_sock_addr,1099 cgroup_sock_addr,
1100
1101 /// context type: __sk_buff
657 lwt_seg6local,1102 lwt_seg6local,
1103
1104 /// context type: u32
658 lirc_mode2,1105 lirc_mode2,
1106
1107 /// context type: sk_reuseport_md
659 sk_reuseport,1108 sk_reuseport,
1109
1110 /// context type: __sk_buff
660 flow_dissector,1111 flow_dissector,
1112
1113 /// context type: bpf_sysctl
661 cgroup_sysctl,1114 cgroup_sysctl,
1115
1116 /// context type: bpf_raw_tracepoint_args
662 raw_tracepoint_writable,1117 raw_tracepoint_writable,
1118
1119 /// context type: bpf_sockopt
663 cgroup_sockopt,1120 cgroup_sockopt,
1121
1122 /// context type: void *
664 tracing,1123 tracing,
1124
1125 /// context type: void *
665 struct_ops,1126 struct_ops,
1127
1128 /// context type: void *
666 ext,1129 ext,
1130
1131 /// context type: void *
667 lsm,1132 lsm,
1133
1134 /// context type: bpf_sk_lookup
668 sk_lookup,1135 sk_lookup,
1136 _,
669};1137};
6701138
671pub const AttachType = extern enum(u32) {1139pub const AttachType = extern enum(u32) {
...@@ -715,27 +1183,38 @@ const obj_name_len = 16;...@@ -715,27 +1183,38 @@ const obj_name_len = 16;
715pub const MapCreateAttr = extern struct {1183pub const MapCreateAttr = extern struct {
716 /// one of MapType1184 /// one of MapType
717 map_type: u32,1185 map_type: u32,
1186
718 /// size of key in bytes1187 /// size of key in bytes
719 key_size: u32,1188 key_size: u32,
1189
720 /// size of value in bytes1190 /// size of value in bytes
721 value_size: u32,1191 value_size: u32,
1192
722 /// max number of entries in a map1193 /// max number of entries in a map
723 max_entries: u32,1194 max_entries: u32,
1195
724 /// .map_create related flags1196 /// .map_create related flags
725 map_flags: u32,1197 map_flags: u32,
1198
726 /// fd pointing to the inner map1199 /// fd pointing to the inner map
727 inner_map_fd: fd_t,1200 inner_map_fd: fd_t,
1201
728 /// numa node (effective only if MapCreateFlags.numa_node is set)1202 /// numa node (effective only if MapCreateFlags.numa_node is set)
729 numa_node: u32,1203 numa_node: u32,
730 map_name: [obj_name_len]u8,1204 map_name: [obj_name_len]u8,
1205
731 /// ifindex of netdev to create on1206 /// ifindex of netdev to create on
732 map_ifindex: u32,1207 map_ifindex: u32,
1208
733 /// fd pointing to a BTF type data1209 /// fd pointing to a BTF type data
734 btf_fd: fd_t,1210 btf_fd: fd_t,
1211
735 /// BTF type_id of the key1212 /// BTF type_id of the key
736 btf_key_type_id: u32,1213 btf_key_type_id: u32,
1214
737 /// BTF type_id of the value1215 /// BTF type_id of the value
738 bpf_value_type_id: u32,1216 bpf_value_type_id: u32,
1217
739 /// BTF type_id of a kernel struct stored as the map value1218 /// BTF type_id of a kernel struct stored as the map value
740 btf_vmlinux_value_type_id: u32,1219 btf_vmlinux_value_type_id: u32,
741};1220};
...@@ -755,10 +1234,12 @@ pub const MapElemAttr = extern struct {...@@ -755,10 +1234,12 @@ pub const MapElemAttr = extern struct {
755pub const MapBatchAttr = extern struct {1234pub const MapBatchAttr = extern struct {
756 /// start batch, NULL to start from beginning1235 /// start batch, NULL to start from beginning
757 in_batch: u64,1236 in_batch: u64,
1237
758 /// output: next start batch1238 /// output: next start batch
759 out_batch: u64,1239 out_batch: u64,
760 keys: u64,1240 keys: u64,
761 values: u64,1241 values: u64,
1242
762 /// input/output:1243 /// input/output:
763 /// input: # of key/value elements1244 /// input: # of key/value elements
764 /// output: # of filled elements1245 /// output: # of filled elements
...@@ -775,35 +1256,49 @@ pub const ProgLoadAttr = extern struct {...@@ -775,35 +1256,49 @@ pub const ProgLoadAttr = extern struct {
775 insn_cnt: u32,1256 insn_cnt: u32,
776 insns: u64,1257 insns: u64,
777 license: u64,1258 license: u64,
1259
778 /// verbosity level of verifier1260 /// verbosity level of verifier
779 log_level: u32,1261 log_level: u32,
1262
780 /// size of user buffer1263 /// size of user buffer
781 log_size: u32,1264 log_size: u32,
1265
782 /// user supplied buffer1266 /// user supplied buffer
783 log_buf: u64,1267 log_buf: u64,
1268
784 /// not used1269 /// not used
785 kern_version: u32,1270 kern_version: u32,
786 prog_flags: u32,1271 prog_flags: u32,
787 prog_name: [obj_name_len]u8,1272 prog_name: [obj_name_len]u8,
788 /// ifindex of netdev to prep for. For some prog types expected attach1273
789 /// type must be known at load time to verify attach type specific parts1274 /// ifindex of netdev to prep for.
790 /// of prog (context accesses, allowed helpers, etc).
791 prog_ifindex: u32,1275 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).
792 expected_attach_type: u32,1280 expected_attach_type: u32,
1281
793 /// fd pointing to BTF type data1282 /// fd pointing to BTF type data
794 prog_btf_fd: fd_t,1283 prog_btf_fd: fd_t,
1284
795 /// userspace bpf_func_info size1285 /// userspace bpf_func_info size
796 func_info_rec_size: u32,1286 func_info_rec_size: u32,
797 func_info: u64,1287 func_info: u64,
1288
798 /// number of bpf_func_info records1289 /// number of bpf_func_info records
799 func_info_cnt: u32,1290 func_info_cnt: u32,
1291
800 /// userspace bpf_line_info size1292 /// userspace bpf_line_info size
801 line_info_rec_size: u32,1293 line_info_rec_size: u32,
802 line_info: u64,1294 line_info: u64,
1295
803 /// number of bpf_line_info records1296 /// number of bpf_line_info records
804 line_info_cnt: u32,1297 line_info_cnt: u32,
1298
805 /// in-kernel BTF type id to attach to1299 /// in-kernel BTF type id to attach to
806 attact_btf_id: u32,1300 attact_btf_id: u32,
1301
807 /// 0 to attach to vmlinux1302 /// 0 to attach to vmlinux
808 attach_prog_id: u32,1303 attach_prog_id: u32,
809};1304};
...@@ -819,29 +1314,36 @@ pub const ObjAttr = extern struct {...@@ -819,29 +1314,36 @@ pub const ObjAttr = extern struct {
819pub const ProgAttachAttr = extern struct {1314pub const ProgAttachAttr = extern struct {
820 /// container object to attach to1315 /// container object to attach to
821 target_fd: fd_t,1316 target_fd: fd_t,
1317
822 /// eBPF program to attach1318 /// eBPF program to attach
823 attach_bpf_fd: fd_t,1319 attach_bpf_fd: fd_t,
1320
824 attach_type: u32,1321 attach_type: u32,
825 attach_flags: u32,1322 attach_flags: u32,
1323
826 // TODO: BPF_F_REPLACE flags1324 // TODO: BPF_F_REPLACE flags
827 /// previously attached eBPF program to replace if .replace is used1325 /// previously attached eBPF program to replace if .replace is used
828 replace_bpf_fd: fd_t,1326 replace_bpf_fd: fd_t,
829};1327};
8301328
831/// struct used by Cmd.prog_test_run command1329/// struct used by Cmd.prog_test_run command
832pub const TestAttr = extern struct {1330pub const TestRunAttr = extern struct {
833 prog_fd: fd_t,1331 prog_fd: fd_t,
834 retval: u32,1332 retval: u32,
1333
835 /// input: len of data_in1334 /// input: len of data_in
836 data_size_in: u32,1335 data_size_in: u32,
1336
837 /// input/output: len of data_out. returns ENOSPC if data_out is too small.1337 /// input/output: len of data_out. returns ENOSPC if data_out is too small.
838 data_size_out: u32,1338 data_size_out: u32,
839 data_in: u64,1339 data_in: u64,
840 data_out: u64,1340 data_out: u64,
841 repeat: u32,1341 repeat: u32,
842 duration: u32,1342 duration: u32,
1343
843 /// input: len of ctx_in1344 /// input: len of ctx_in
844 ctx_size_in: u32,1345 ctx_size_in: u32,
1346
845 /// input/output: len of ctx_out. returns ENOSPC if ctx_out is too small.1347 /// input/output: len of ctx_out. returns ENOSPC if ctx_out is too small.
846 ctx_size_out: u32,1348 ctx_size_out: u32,
847 ctx_in: u64,1349 ctx_in: u64,
...@@ -894,26 +1396,35 @@ pub const BtfLoadAttr = extern struct {...@@ -894,26 +1396,35 @@ pub const BtfLoadAttr = extern struct {
894 btf_log_level: u32,1396 btf_log_level: u32,
895};1397};
8961398
1399/// struct used by Cmd.task_fd_query
897pub const TaskFdQueryAttr = extern struct {1400pub const TaskFdQueryAttr = extern struct {
898 /// input: pid1401 /// input: pid
899 pid: pid_t,1402 pid: pid_t,
1403
900 /// input: fd1404 /// input: fd
901 fd: fd_t,1405 fd: fd_t,
1406
902 /// input: flags1407 /// input: flags
903 flags: u32,1408 flags: u32,
1409
904 /// input/output: buf len1410 /// input/output: buf len
905 buf_len: u32,1411 buf_len: u32,
1412
906 /// input/output:1413 /// input/output:
907 /// tp_name for tracepoint1414 /// tp_name for tracepoint
908 /// symbol for kprobe1415 /// symbol for kprobe
909 /// filename for uprobe1416 /// filename for uprobe
910 buf: u64,1417 buf: u64,
1418
911 /// output: prod_id1419 /// output: prod_id
912 prog_id: u32,1420 prog_id: u32,
1421
913 /// output: BPF_FD_TYPE1422 /// output: BPF_FD_TYPE
914 fd_type: u32,1423 fd_type: u32,
1424
915 /// output: probe_offset1425 /// output: probe_offset
916 probe_offset: u64,1426 probe_offset: u64,
1427
917 /// output: probe_addr1428 /// output: probe_addr
918 probe_addr: u64,1429 probe_addr: u64,
919};1430};
...@@ -922,9 +1433,11 @@ pub const TaskFdQueryAttr = extern struct {...@@ -922,9 +1433,11 @@ pub const TaskFdQueryAttr = extern struct {
922pub const LinkCreateAttr = extern struct {1433pub const LinkCreateAttr = extern struct {
923 /// eBPF program to attach1434 /// eBPF program to attach
924 prog_fd: fd_t,1435 prog_fd: fd_t,
1436
925 /// object to attach to1437 /// object to attach to
926 target_fd: fd_t,1438 target_fd: fd_t,
927 attach_type: u32,1439 attach_type: u32,
1440
928 /// extra flags1441 /// extra flags
929 flags: u32,1442 flags: u32,
930};1443};
...@@ -932,10 +1445,13 @@ pub const LinkCreateAttr = extern struct {...@@ -932,10 +1445,13 @@ pub const LinkCreateAttr = extern struct {
932/// struct used by Cmd.link_update command1445/// struct used by Cmd.link_update command
933pub const LinkUpdateAttr = extern struct {1446pub const LinkUpdateAttr = extern struct {
934 link_fd: fd_t,1447 link_fd: fd_t,
1448
935 /// new program to update link with1449 /// new program to update link with
936 new_prog_fd: fd_t,1450 new_prog_fd: fd_t,
1451
937 /// extra flags1452 /// extra flags
938 flags: u32,1453 flags: u32,
1454
939 /// expected link's program fd, it is specified only if BPF_F_REPLACE is1455 /// expected link's program fd, it is specified only if BPF_F_REPLACE is
940 /// set in flags1456 /// set in flags
941 old_prog_fd: fd_t,1457 old_prog_fd: fd_t,
...@@ -952,6 +1468,7 @@ pub const IterCreateAttr = extern struct {...@@ -952,6 +1468,7 @@ pub const IterCreateAttr = extern struct {
952 flags: u32,1468 flags: u32,
953};1469};
9541470
1471/// Mega struct that is passed to the bpf() syscall
955pub const Attr = extern union {1472pub const Attr = extern union {
956 map_create: MapCreateAttr,1473 map_create: MapCreateAttr,
957 map_elem: MapElemAttr,1474 map_elem: MapElemAttr,
...@@ -971,3 +1488,176 @@ pub const Attr = extern union {...@@ -971,3 +1488,176 @@ pub const Attr = extern union {
971 enable_stats: EnableStatsAttr,1488 enable_stats: EnableStatsAttr,
972 iter_create: IterCreateAttr,1489 iter_create: IterCreateAttr,
973};1490};
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" {...@@ -555,3 +555,39 @@ test "signalfd" {
555 return error.SkipZigTest;555 return error.SkipZigTest;
556 _ = std.os.signalfd;556 _ = std.os.signalfd;
557}557}
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...@@ -287,3 +287,5 @@ pub extern "kernel32" fn K32GetWsChangesEx(hProcess: HANDLE, lpWatchInfoEx: PPSA
287pub extern "kernel32" fn K32InitializeProcessForWsWatch(hProcess: HANDLE) callconv(.Stdcall) BOOL;287pub extern "kernel32" fn K32InitializeProcessForWsWatch(hProcess: HANDLE) callconv(.Stdcall) BOOL;
288pub extern "kernel32" fn K32QueryWorkingSet(hProcess: HANDLE, pv: PVOID, cb: DWORD) callconv(.Stdcall) BOOL;288pub extern "kernel32" fn K32QueryWorkingSet(hProcess: HANDLE, pv: PVOID, cb: DWORD) callconv(.Stdcall) BOOL;
289pub extern "kernel32" fn K32QueryWorkingSetEx(hProcess: HANDLE, pv: PVOID, cb: DWORD) callconv(.Stdcall) BOOL;289pub 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;...@@ -12,7 +12,7 @@ pub const SOCKET_ERROR = -1;
12pub const WSADESCRIPTION_LEN = 256;12pub const WSADESCRIPTION_LEN = 256;
13pub const WSASYS_STATUS_LEN = 128;13pub const WSASYS_STATUS_LEN = 128;
1414
15pub const WSADATA = if (usize.bit_count == u64.bit_count)15pub const WSADATA = if (@sizeOf(usize) == @sizeOf(u64))
16 extern struct {16 extern struct {
17 wVersion: WORD,17 wVersion: WORD,
18 wHighVersion: WORD,18 wHighVersion: WORD,
lib/std/pdb.zig+1-1
...@@ -636,7 +636,7 @@ const MsfStream = struct {...@@ -636,7 +636,7 @@ const MsfStream = struct {
636 blocks: []u32 = undefined,636 blocks: []u32 = undefined,
637 block_size: u32 = undefined,637 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
641 fn init(block_size: u32, file: File, blocks: []u32) MsfStream {641 fn init(block_size: u32, file: File, blocks: []u32) MsfStream {
642 const stream = MsfStream{642 const stream = MsfStream{
lib/std/process.zig+4-4
...@@ -578,8 +578,8 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons...@@ -578,8 +578,8 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons
578}578}
579579
580pub const UserInfo = struct {580pub const UserInfo = struct {
581 uid: u32,581 uid: os.uid_t,
582 gid: u32,582 gid: os.gid_t,
583};583};
584584
585/// POSIX function which gets a uid from username.585/// POSIX function which gets a uid from username.
...@@ -607,8 +607,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {...@@ -607,8 +607,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
607 var buf: [std.mem.page_size]u8 = undefined;607 var buf: [std.mem.page_size]u8 = undefined;
608 var name_index: usize = 0;608 var name_index: usize = 0;
609 var state = State.Start;609 var state = State.Start;
610 var uid: u32 = 0;610 var uid: os.uid_t = 0;
611 var gid: u32 = 0;611 var gid: os.gid_t = 0;
612612
613 while (true) {613 while (true) {
614 const amt_read = try reader.read(buf[0..]);614 const amt_read = try reader.read(buf[0..]);
lib/std/progress.zig+3-3
...@@ -197,7 +197,7 @@ pub const Progress = struct {...@@ -197,7 +197,7 @@ pub const Progress = struct {
197 var maybe_node: ?*Node = &self.root;197 var maybe_node: ?*Node = &self.root;
198 while (maybe_node) |node| {198 while (maybe_node) |node| {
199 if (need_ellipse) {199 if (need_ellipse) {
200 self.bufWrite(&end, "...", .{});200 self.bufWrite(&end, "... ", .{});
201 }201 }
202 need_ellipse = false;202 need_ellipse = false;
203 if (node.name.len != 0 or node.estimated_total_items != null) {203 if (node.name.len != 0 or node.estimated_total_items != null) {
...@@ -218,7 +218,7 @@ pub const Progress = struct {...@@ -218,7 +218,7 @@ pub const Progress = struct {
218 maybe_node = node.recently_updated_child;218 maybe_node = node.recently_updated_child;
219 }219 }
220 if (need_ellipse) {220 if (need_ellipse) {
221 self.bufWrite(&end, "...", .{});221 self.bufWrite(&end, "... ", .{});
222 }222 }
223 }223 }
224224
...@@ -253,7 +253,7 @@ pub const Progress = struct {...@@ -253,7 +253,7 @@ pub const Progress = struct {
253 const bytes_needed_for_esc_codes_at_end = if (std.builtin.os.tag == .windows) 0 else 11;253 const bytes_needed_for_esc_codes_at_end = if (std.builtin.os.tag == .windows) 0 else 11;
254 const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;254 const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
255 if (end.* > max_end) {255 if (end.* > max_end) {
256 const suffix = "...";256 const suffix = "... ";
257 self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;257 self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;
258 std.mem.copy(u8, self.output_buffer[max_end..], suffix);258 std.mem.copy(u8, self.output_buffer[max_end..], suffix);
259 end.* = max_end + suffix.len;259 end.* = max_end + suffix.len;
lib/std/rand.zig+33-24
...@@ -51,8 +51,9 @@ pub const Random = struct {...@@ -51,8 +51,9 @@ pub const Random = struct {
51 /// Returns a random int `i` such that `0 <= i <= maxInt(T)`.51 /// Returns a random int `i` such that `0 <= i <= maxInt(T)`.
52 /// `i` is evenly distributed.52 /// `i` is evenly distributed.
53 pub fn int(r: *Random, comptime T: type) T {53 pub fn int(r: *Random, comptime T: type) T {
54 const UnsignedT = std.meta.Int(false, T.bit_count);54 const bits = @typeInfo(T).Int.bits;
55 const ByteAlignedT = std.meta.Int(false, @divTrunc(T.bit_count + 7, 8) * 8);55 const UnsignedT = std.meta.Int(false, bits);
56 const ByteAlignedT = std.meta.Int(false, @divTrunc(bits + 7, 8) * 8);
5657
57 var rand_bytes: [@sizeOf(ByteAlignedT)]u8 = undefined;58 var rand_bytes: [@sizeOf(ByteAlignedT)]u8 = undefined;
58 r.bytes(rand_bytes[0..]);59 r.bytes(rand_bytes[0..]);
...@@ -68,10 +69,11 @@ pub const Random = struct {...@@ -68,10 +69,11 @@ pub const Random = struct {
68 /// Constant-time implementation off `uintLessThan`.69 /// Constant-time implementation off `uintLessThan`.
69 /// The results of this function may be biased.70 /// The results of this function may be biased.
70 pub fn uintLessThanBiased(r: *Random, comptime T: type, less_than: T) T {71 pub fn uintLessThanBiased(r: *Random, comptime T: type, less_than: T) T {
71 comptime assert(T.is_signed == false);72 comptime assert(@typeInfo(T).Int.is_signed == false);
72 comptime assert(T.bit_count <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation!73 const bits = @typeInfo(T).Int.bits;
74 comptime assert(bits <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation!
73 assert(0 < less_than);75 assert(0 < less_than);
74 if (T.bit_count <= 32) {76 if (bits <= 32) {
75 return @intCast(T, limitRangeBiased(u32, r.int(u32), less_than));77 return @intCast(T, limitRangeBiased(u32, r.int(u32), less_than));
76 } else {78 } else {
77 return @intCast(T, limitRangeBiased(u64, r.int(u64), less_than));79 return @intCast(T, limitRangeBiased(u64, r.int(u64), less_than));
...@@ -87,13 +89,15 @@ pub const Random = struct {...@@ -87,13 +89,15 @@ pub const Random = struct {
87 /// this function is guaranteed to return.89 /// this function is guaranteed to return.
88 /// If you need deterministic runtime bounds, use `uintLessThanBiased`.90 /// If you need deterministic runtime bounds, use `uintLessThanBiased`.
89 pub fn uintLessThan(r: *Random, comptime T: type, less_than: T) T {91 pub fn uintLessThan(r: *Random, comptime T: type, less_than: T) T {
90 comptime assert(T.is_signed == false);92 comptime assert(@typeInfo(T).Int.is_signed == false);
91 comptime assert(T.bit_count <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation!93 const bits = @typeInfo(T).Int.bits;
94 comptime assert(bits <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation!
92 assert(0 < less_than);95 assert(0 < less_than);
93 // Small is typically u3296 // 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);
95 // Large is typically u6499 // 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
98 // adapted from:102 // adapted from:
99 // http://www.pcg-random.org/posts/bounded-rands.html103 // http://www.pcg-random.org/posts/bounded-rands.html
...@@ -105,7 +109,7 @@ pub const Random = struct {...@@ -105,7 +109,7 @@ pub const Random = struct {
105 // TODO: workaround for https://github.com/ziglang/zig/issues/1770109 // TODO: workaround for https://github.com/ziglang/zig/issues/1770
106 // should be:110 // should be:
107 // var t: Small = -%less_than;111 // 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
110 if (t >= less_than) {114 if (t >= less_than) {
111 t -= less_than;115 t -= less_than;
...@@ -119,13 +123,13 @@ pub const Random = struct {...@@ -119,13 +123,13 @@ pub const Random = struct {
119 l = @truncate(Small, m);123 l = @truncate(Small, m);
120 }124 }
121 }125 }
122 return @intCast(T, m >> Small.bit_count);126 return @intCast(T, m >> small_bits);
123 }127 }
124128
125 /// Constant-time implementation off `uintAtMost`.129 /// Constant-time implementation off `uintAtMost`.
126 /// The results of this function may be biased.130 /// The results of this function may be biased.
127 pub fn uintAtMostBiased(r: *Random, comptime T: type, at_most: T) T {131 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);
129 if (at_most == maxInt(T)) {133 if (at_most == maxInt(T)) {
130 // have the full range134 // have the full range
131 return r.int(T);135 return r.int(T);
...@@ -137,7 +141,7 @@ pub const Random = struct {...@@ -137,7 +141,7 @@ pub const Random = struct {
137 /// See `uintLessThan`, which this function uses in most cases,141 /// See `uintLessThan`, which this function uses in most cases,
138 /// for commentary on the runtime of this function.142 /// for commentary on the runtime of this function.
139 pub fn uintAtMost(r: *Random, comptime T: type, at_most: T) T {143 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);
141 if (at_most == maxInt(T)) {145 if (at_most == maxInt(T)) {
142 // have the full range146 // have the full range
143 return r.int(T);147 return r.int(T);
...@@ -149,9 +153,10 @@ pub const Random = struct {...@@ -149,9 +153,10 @@ pub const Random = struct {
149 /// The results of this function may be biased.153 /// The results of this function may be biased.
150 pub fn intRangeLessThanBiased(r: *Random, comptime T: type, at_least: T, less_than: T) T {154 pub fn intRangeLessThanBiased(r: *Random, comptime T: type, at_least: T, less_than: T) T {
151 assert(at_least < less_than);155 assert(at_least < less_than);
152 if (T.is_signed) {156 const info = @typeInfo(T).Int;
157 if (info.is_signed) {
153 // Two's complement makes this math pretty easy.158 // 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);
155 const lo = @bitCast(UnsignedT, at_least);160 const lo = @bitCast(UnsignedT, at_least);
156 const hi = @bitCast(UnsignedT, less_than);161 const hi = @bitCast(UnsignedT, less_than);
157 const result = lo +% r.uintLessThanBiased(UnsignedT, hi -% lo);162 const result = lo +% r.uintLessThanBiased(UnsignedT, hi -% lo);
...@@ -167,9 +172,10 @@ pub const Random = struct {...@@ -167,9 +172,10 @@ pub const Random = struct {
167 /// for commentary on the runtime of this function.172 /// for commentary on the runtime of this function.
168 pub fn intRangeLessThan(r: *Random, comptime T: type, at_least: T, less_than: T) T {173 pub fn intRangeLessThan(r: *Random, comptime T: type, at_least: T, less_than: T) T {
169 assert(at_least < less_than);174 assert(at_least < less_than);
170 if (T.is_signed) {175 const info = @typeInfo(T).Int;
176 if (info.is_signed) {
171 // Two's complement makes this math pretty easy.177 // 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);
173 const lo = @bitCast(UnsignedT, at_least);179 const lo = @bitCast(UnsignedT, at_least);
174 const hi = @bitCast(UnsignedT, less_than);180 const hi = @bitCast(UnsignedT, less_than);
175 const result = lo +% r.uintLessThan(UnsignedT, hi -% lo);181 const result = lo +% r.uintLessThan(UnsignedT, hi -% lo);
...@@ -184,9 +190,10 @@ pub const Random = struct {...@@ -184,9 +190,10 @@ pub const Random = struct {
184 /// The results of this function may be biased.190 /// The results of this function may be biased.
185 pub fn intRangeAtMostBiased(r: *Random, comptime T: type, at_least: T, at_most: T) T {191 pub fn intRangeAtMostBiased(r: *Random, comptime T: type, at_least: T, at_most: T) T {
186 assert(at_least <= at_most);192 assert(at_least <= at_most);
187 if (T.is_signed) {193 const info = @typeInfo(T).Int;
194 if (info.is_signed) {
188 // Two's complement makes this math pretty easy.195 // 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);
190 const lo = @bitCast(UnsignedT, at_least);197 const lo = @bitCast(UnsignedT, at_least);
191 const hi = @bitCast(UnsignedT, at_most);198 const hi = @bitCast(UnsignedT, at_most);
192 const result = lo +% r.uintAtMostBiased(UnsignedT, hi -% lo);199 const result = lo +% r.uintAtMostBiased(UnsignedT, hi -% lo);
...@@ -202,9 +209,10 @@ pub const Random = struct {...@@ -202,9 +209,10 @@ pub const Random = struct {
202 /// for commentary on the runtime of this function.209 /// for commentary on the runtime of this function.
203 pub fn intRangeAtMost(r: *Random, comptime T: type, at_least: T, at_most: T) T {210 pub fn intRangeAtMost(r: *Random, comptime T: type, at_least: T, at_most: T) T {
204 assert(at_least <= at_most);211 assert(at_least <= at_most);
205 if (T.is_signed) {212 const info = @typeInfo(T).Int;
213 if (info.is_signed) {
206 // Two's complement makes this math pretty easy.214 // 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);
208 const lo = @bitCast(UnsignedT, at_least);216 const lo = @bitCast(UnsignedT, at_least);
209 const hi = @bitCast(UnsignedT, at_most);217 const hi = @bitCast(UnsignedT, at_most);
210 const result = lo +% r.uintAtMost(UnsignedT, hi -% lo);218 const result = lo +% r.uintAtMost(UnsignedT, hi -% lo);
...@@ -280,14 +288,15 @@ pub const Random = struct {...@@ -280,14 +288,15 @@ pub const Random = struct {
280/// into an integer 0 <= result < less_than.288/// into an integer 0 <= result < less_than.
281/// This function introduces a minor bias.289/// This function introduces a minor bias.
282pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T {290pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T {
283 comptime assert(T.is_signed == false);291 comptime assert(@typeInfo(T).Int.is_signed == false);
284 const T2 = std.meta.Int(false, T.bit_count * 2);292 const bits = @typeInfo(T).Int.bits;
293 const T2 = std.meta.Int(false, bits * 2);
285294
286 // adapted from:295 // adapted from:
287 // http://www.pcg-random.org/posts/bounded-rands.html296 // http://www.pcg-random.org/posts/bounded-rands.html
288 // "Integer Multiplication (Biased)"297 // "Integer Multiplication (Biased)"
289 var m: T2 = @as(T2, random_int) * @as(T2, less_than);298 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);
291}300}
292301
293const SequentialPrng = struct {302const SequentialPrng = struct {
lib/std/special/build_runner.zig+1-1
...@@ -133,7 +133,7 @@ pub fn main() !void {...@@ -133,7 +133,7 @@ pub fn main() !void {
133}133}
134134
135fn runBuild(builder: *Builder) anyerror!void {135fn runBuild(builder: *Builder) anyerror!void {
136 switch (@typeInfo(@TypeOf(root.build).ReturnType)) {136 switch (@typeInfo(@typeInfo(@TypeOf(root.build)).Fn.return_type.?)) {
137 .Void => root.build(builder),137 .Void => root.build(builder),
138 .ErrorUnion => try root.build(builder),138 .ErrorUnion => try root.build(builder),
139 else => @compileError("expected return type of build to be 'void' or '!void'"),139 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 {...@@ -516,11 +516,12 @@ export fn roundf(a: f32) f32 {
516fn generic_fmod(comptime T: type, x: T, y: T) T {516fn generic_fmod(comptime T: type, x: T, y: T) T {
517 @setRuntimeSafety(false);517 @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);
520 const log2uint = math.Log2Int(uint);521 const log2uint = math.Log2Int(uint);
521 const digits = if (T == f32) 23 else 52;522 const digits = if (T == f32) 23 else 52;
522 const exp_bits = if (T == f32) 9 else 12;523 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;
524 const mask = if (T == f32) 0xff else 0x7ff;525 const mask = if (T == f32) 0xff else 0x7ff;
525 var ux = @bitCast(uint, x);526 var ux = @bitCast(uint, x);
526 var uy = @bitCast(uint, y);527 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 {...@@ -59,23 +59,25 @@ pub fn __aeabi_dsub(a: f64, b: f64) callconv(.AAPCS) f64 {
59}59}
6060
61// TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/215461// 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 {62fn normalize(comptime T: type, significand: *std.meta.Int(false, @typeInfo(T).Float.bits)) i32 {
63 const Z = std.meta.Int(false, T.bit_count);63 const bits = @typeInfo(T).Float.bits;
64 const S = std.meta.Int(false, T.bit_count - @clz(Z, @as(Z, T.bit_count) - 1));64 const Z = std.meta.Int(false, bits);
65 const S = std.meta.Int(false, bits - @clz(Z, @as(Z, bits) - 1));
65 const significandBits = std.math.floatMantissaBits(T);66 const significandBits = std.math.floatMantissaBits(T);
66 const implicitBit = @as(Z, 1) << significandBits;67 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);
69 significand.* <<= @intCast(S, shift);70 significand.* <<= @intCast(S, shift);
70 return 1 - shift;71 return 1 - shift;
71}72}
7273
73// TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/215474// TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154
74fn addXf3(comptime T: type, a: T, b: T) T {75fn addXf3(comptime T: type, a: T, b: T) T {
75 const Z = std.meta.Int(false, T.bit_count);76 const bits = @typeInfo(T).Float.bits;
76 const S = std.meta.Int(false, T.bit_count - @clz(Z, @as(Z, T.bit_count) - 1));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;
79 const significandBits = std.math.floatMantissaBits(T);81 const significandBits = std.math.floatMantissaBits(T);
80 const exponentBits = std.math.floatExponentBits(T);82 const exponentBits = std.math.floatExponentBits(T);
8183
...@@ -187,7 +189,7 @@ fn addXf3(comptime T: type, a: T, b: T) T {...@@ -187,7 +189,7 @@ fn addXf3(comptime T: type, a: T, b: T) T {
187 // If partial cancellation occured, we need to left-shift the result189 // If partial cancellation occured, we need to left-shift the result
188 // and adjust the exponent:190 // and adjust the exponent:
189 if (aSignificand < implicitBit << 3) {191 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));
191 aSignificand <<= @intCast(S, shift);193 aSignificand <<= @intCast(S, shift);
192 aExponent -= shift;194 aExponent -= shift;
193 }195 }
lib/std/special/compiler_rt/aulldiv.zig+2-2
...@@ -7,8 +7,8 @@ const builtin = @import("builtin");...@@ -7,8 +7,8 @@ const builtin = @import("builtin");
77
8pub fn _alldiv(a: i64, b: i64) callconv(.Stdcall) i64 {8pub fn _alldiv(a: i64, b: i64) callconv(.Stdcall) i64 {
9 @setRuntimeSafety(builtin.is_test);9 @setRuntimeSafety(builtin.is_test);
10 const s_a = a >> (i64.bit_count - 1);10 const s_a = a >> (64 - 1);
11 const s_b = b >> (i64.bit_count - 1);11 const s_b = b >> (64 - 1);
1212
13 const an = (a ^ s_a) -% s_a;13 const an = (a ^ s_a) -% s_a;
14 const bn = (b ^ s_b) -% s_b;14 const bn = (b ^ s_b) -% s_b;
lib/std/special/compiler_rt/aullrem.zig+2-2
...@@ -7,8 +7,8 @@ const builtin = @import("builtin");...@@ -7,8 +7,8 @@ const builtin = @import("builtin");
77
8pub fn _allrem(a: i64, b: i64) callconv(.Stdcall) i64 {8pub fn _allrem(a: i64, b: i64) callconv(.Stdcall) i64 {
9 @setRuntimeSafety(builtin.is_test);9 @setRuntimeSafety(builtin.is_test);
10 const s_a = a >> (i64.bit_count - 1);10 const s_a = a >> (64 - 1);
11 const s_b = b >> (i64.bit_count - 1);11 const s_b = b >> (64 - 1);
1212
13 const an = (a ^ s_a) -% s_a;13 const an = (a ^ s_a) -% s_a;
14 const bn = (b ^ s_b) -% s_b;14 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) {...@@ -27,8 +27,9 @@ const GE = extern enum(i32) {
27pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {27pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {
28 @setRuntimeSafety(builtin.is_test);28 @setRuntimeSafety(builtin.is_test);
2929
30 const srep_t = std.meta.Int(true, T.bit_count);30 const bits = @typeInfo(T).Float.bits;
31 const rep_t = std.meta.Int(false, T.bit_count);31 const srep_t = std.meta.Int(true, bits);
32 const rep_t = std.meta.Int(false, bits);
3233
33 const significandBits = std.math.floatMantissaBits(T);34 const significandBits = std.math.floatMantissaBits(T);
34 const exponentBits = std.math.floatExponentBits(T);35 const exponentBits = std.math.floatExponentBits(T);
...@@ -73,7 +74,7 @@ pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {...@@ -73,7 +74,7 @@ pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {
73pub fn unordcmp(comptime T: type, a: T, b: T) i32 {74pub fn unordcmp(comptime T: type, a: T, b: T) i32 {
74 @setRuntimeSafety(builtin.is_test);75 @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
78 const significandBits = std.math.floatMantissaBits(T);79 const significandBits = std.math.floatMantissaBits(T);
79 const exponentBits = std.math.floatExponentBits(T);80 const exponentBits = std.math.floatExponentBits(T);
lib/std/special/compiler_rt/divdf3.zig+4-5
...@@ -12,10 +12,9 @@ const builtin = @import("builtin");...@@ -12,10 +12,9 @@ const builtin = @import("builtin");
1212
13pub fn __divdf3(a: f64, b: f64) callconv(.C) f64 {13pub fn __divdf3(a: f64, b: f64) callconv(.C) f64 {
14 @setRuntimeSafety(builtin.is_test);14 @setRuntimeSafety(builtin.is_test);
15 const Z = std.meta.Int(false, f64.bit_count);15 const Z = std.meta.Int(false, 64);
16 const SignedZ = std.meta.Int(true, f64.bit_count);16 const SignedZ = std.meta.Int(true, 64);
1717
18 const typeWidth = f64.bit_count;
19 const significandBits = std.math.floatMantissaBits(f64);18 const significandBits = std.math.floatMantissaBits(f64);
20 const exponentBits = std.math.floatExponentBits(f64);19 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 {...@@ -317,9 +316,9 @@ pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void {
317 }316 }
318}317}
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 {
321 @setRuntimeSafety(builtin.is_test);320 @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);
323 const significandBits = std.math.floatMantissaBits(T);322 const significandBits = std.math.floatMantissaBits(T);
324 const implicitBit = @as(Z, 1) << significandBits;323 const implicitBit = @as(Z, 1) << significandBits;
325324
lib/std/special/compiler_rt/divsf3.zig+3-4
...@@ -12,9 +12,8 @@ const builtin = @import("builtin");...@@ -12,9 +12,8 @@ const builtin = @import("builtin");
1212
13pub fn __divsf3(a: f32, b: f32) callconv(.C) f32 {13pub fn __divsf3(a: f32, b: f32) callconv(.C) f32 {
14 @setRuntimeSafety(builtin.is_test);14 @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;
18 const significandBits = std.math.floatMantissaBits(f32);17 const significandBits = std.math.floatMantissaBits(f32);
19 const exponentBits = std.math.floatExponentBits(f32);18 const exponentBits = std.math.floatExponentBits(f32);
2019
...@@ -190,9 +189,9 @@ pub fn __divsf3(a: f32, b: f32) callconv(.C) f32 {...@@ -190,9 +189,9 @@ pub fn __divsf3(a: f32, b: f32) callconv(.C) f32 {
190 }189 }
191}190}
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 {
194 @setRuntimeSafety(builtin.is_test);193 @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);
196 const significandBits = std.math.floatMantissaBits(T);195 const significandBits = std.math.floatMantissaBits(T);
197 const implicitBit = @as(Z, 1) << significandBits;196 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;...@@ -11,10 +11,9 @@ const wideMultiply = @import("divdf3.zig").wideMultiply;
1111
12pub fn __divtf3(a: f128, b: f128) callconv(.C) f128 {12pub fn __divtf3(a: f128, b: f128) callconv(.C) f128 {
13 @setRuntimeSafety(builtin.is_test);13 @setRuntimeSafety(builtin.is_test);
14 const Z = std.meta.Int(false, f128.bit_count);14 const Z = std.meta.Int(false, 128);
15 const SignedZ = std.meta.Int(true, f128.bit_count);15 const SignedZ = std.meta.Int(true, 128);
1616
17 const typeWidth = f128.bit_count;
18 const significandBits = std.math.floatMantissaBits(f128);17 const significandBits = std.math.floatMantissaBits(f128);
19 const exponentBits = std.math.floatExponentBits(f128);18 const exponentBits = std.math.floatExponentBits(f128);
2019
lib/std/special/compiler_rt/divti3.zig+2-2
...@@ -9,8 +9,8 @@ const builtin = @import("builtin");...@@ -9,8 +9,8 @@ const builtin = @import("builtin");
9pub fn __divti3(a: i128, b: i128) callconv(.C) i128 {9pub fn __divti3(a: i128, b: i128) callconv(.C) i128 {
10 @setRuntimeSafety(builtin.is_test);10 @setRuntimeSafety(builtin.is_test);
1111
12 const s_a = a >> (i128.bit_count - 1);12 const s_a = a >> (128 - 1);
13 const s_b = b >> (i128.bit_count - 1);13 const s_b = b >> (128 - 1);
1414
15 const an = (a ^ s_a) -% s_a;15 const an = (a ^ s_a) -% s_a;
16 const bn = (b ^ s_b) -% s_b;16 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 {...@@ -28,7 +28,7 @@ pub fn fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t) fixint_t {
28 else => unreachable,28 else => unreachable,
29 };29 };
3030
31 const typeWidth = rep_t.bit_count;31 const typeWidth = @typeInfo(rep_t).Int.bits;
32 const exponentBits = (typeWidth - significandBits - 1);32 const exponentBits = (typeWidth - significandBits - 1);
33 const signBit = (@as(rep_t, 1) << (significandBits + exponentBits));33 const signBit = (@as(rep_t, 1) << (significandBits + exponentBits));
34 const maxExponent = ((1 << exponentBits) - 1);34 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 {...@@ -50,12 +50,13 @@ pub fn fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t) fixint_t {
50 if (exponent < 0) return 0;50 if (exponent < 0) return 0;
5151
52 // The unsigned result needs to be large enough to handle an fixint_t or rep_t52 // 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);53 const fixint_bits = @typeInfo(fixint_t).Int.bits;
54 const UintResultType = if (fixint_t.bit_count > rep_t.bit_count) fixuint_t else rep_t;54 const fixuint_t = std.meta.Int(false, fixint_bits);
55 const UintResultType = if (fixint_bits > typeWidth) fixuint_t else rep_t;
55 var uint_result: UintResultType = undefined;56 var uint_result: UintResultType = undefined;
5657
57 // If the value is too large for the integer type, saturate.58 // 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) {
59 return if (negative) @as(fixint_t, minInt(fixint_t)) else @as(fixint_t, maxInt(fixint_t));60 return if (negative) @as(fixint_t, minInt(fixint_t)) else @as(fixint_t, maxInt(fixint_t));
60 }61 }
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...@@ -15,14 +15,14 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t
15 f128 => u128,15 f128 => u128,
16 else => unreachable,16 else => unreachable,
17 };17 };
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);
19 const significandBits = switch (fp_t) {20 const significandBits = switch (fp_t) {
20 f32 => 23,21 f32 => 23,
21 f64 => 52,22 f64 => 52,
22 f128 => 112,23 f128 => 112,
23 else => unreachable,24 else => unreachable,
24 };25 };
25 const typeWidth = rep_t.bit_count;
26 const exponentBits = (typeWidth - significandBits - 1);26 const exponentBits = (typeWidth - significandBits - 1);
27 const signBit = (@as(rep_t, 1) << (significandBits + exponentBits));27 const signBit = (@as(rep_t, 1) << (significandBits + exponentBits));
28 const maxExponent = ((1 << exponentBits) - 1);28 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...@@ -44,7 +44,7 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t
44 if (sign == -1 or exponent < 0) return 0;44 if (sign == -1 or exponent < 0) return 0;
4545
46 // If the value is too large for the integer type, saturate.46 // 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
49 // If 0 <= exponent < significandBits, right shift to get the result.49 // If 0 <= exponent < significandBits, right shift to get the result.
50 // Otherwise, shift left.50 // Otherwise, shift left.
lib/std/special/compiler_rt/floatXisf.zig+5-4
...@@ -12,15 +12,16 @@ const FLT_MANT_DIG = 24;...@@ -12,15 +12,16 @@ const FLT_MANT_DIG = 24;
12fn __floatXisf(comptime T: type, arg: T) f32 {12fn __floatXisf(comptime T: type, arg: T) f32 {
13 @setRuntimeSafety(builtin.is_test);13 @setRuntimeSafety(builtin.is_test);
1414
15 const Z = std.meta.Int(false, T.bit_count);15 const bits = @typeInfo(T).Int.bits;
16 const S = std.meta.Int(false, T.bit_count - @clz(Z, @as(Z, T.bit_count) - 1));16 const Z = std.meta.Int(false, bits);
17 const S = std.meta.Int(false, bits - @clz(Z, @as(Z, bits) - 1));
1718
18 if (arg == 0) {19 if (arg == 0) {
19 return @as(f32, 0.0);20 return @as(f32, 0.0);
20 }21 }
2122
22 var ai = arg;23 var ai = arg;
23 const N: u32 = T.bit_count;24 const N: u32 = bits;
24 const si = ai >> @intCast(S, (N - 1));25 const si = ai >> @intCast(S, (N - 1));
25 ai = ((ai ^ si) -% si);26 ai = ((ai ^ si) -% si);
26 var a = @bitCast(Z, ai);27 var a = @bitCast(Z, ai);
...@@ -66,7 +67,7 @@ fn __floatXisf(comptime T: type, arg: T) f32 {...@@ -66,7 +67,7 @@ fn __floatXisf(comptime T: type, arg: T) f32 {
66 // a is now rounded to FLT_MANT_DIG bits67 // a is now rounded to FLT_MANT_DIG bits
67 }68 }
6869
69 const s = @bitCast(Z, arg) >> (T.bit_count - 32);70 const s = @bitCast(Z, arg) >> (@typeInfo(T).Int.bits - 32);
70 const r = (@intCast(u32, s) & 0x80000000) | // sign71 const r = (@intCast(u32, s) & 0x80000000) | // sign
71 (@intCast(u32, (e + 127)) << 23) | // exponent72 (@intCast(u32, (e + 127)) << 23) | // exponent
72 (@truncate(u32, a) & 0x007fffff); // mantissa-high73 (@truncate(u32, a) & 0x007fffff); // mantissa-high
lib/std/special/compiler_rt/floatsiXf.zig+4-3
...@@ -10,8 +10,9 @@ const maxInt = std.math.maxInt;...@@ -10,8 +10,9 @@ const maxInt = std.math.maxInt;
10fn floatsiXf(comptime T: type, a: i32) T {10fn floatsiXf(comptime T: type, a: i32) T {
11 @setRuntimeSafety(builtin.is_test);11 @setRuntimeSafety(builtin.is_test);
1212
13 const Z = std.meta.Int(false, T.bit_count);13 const bits = @typeInfo(T).Float.bits;
14 const S = std.meta.Int(false, T.bit_count - @clz(Z, @as(Z, T.bit_count) - 1));14 const Z = std.meta.Int(false, bits);
15 const S = std.meta.Int(false, bits - @clz(Z, @as(Z, bits) - 1));
1516
16 if (a == 0) {17 if (a == 0) {
17 return @as(T, 0.0);18 return @as(T, 0.0);
...@@ -22,7 +23,7 @@ fn floatsiXf(comptime T: type, a: i32) T {...@@ -22,7 +23,7 @@ fn floatsiXf(comptime T: type, a: i32) T {
22 const exponentBias = ((1 << exponentBits - 1) - 1);23 const exponentBias = ((1 << exponentBits - 1) - 1);
2324
24 const implicitBit = @as(Z, 1) << significandBits;25 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
27 const sign = a >> 31;28 const sign = a >> 31;
28 // Take absolute value of a via abs(x) = (x^(x >> 31)) - (x >> 31).29 // 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 {...@@ -15,7 +15,7 @@ pub fn __floatundisf(arg: u64) callconv(.C) f32 {
15 if (arg == 0) return 0;15 if (arg == 0) return 0;
1616
17 var a = arg;17 var a = arg;
18 const N: usize = @TypeOf(a).bit_count;18 const N: usize = @typeInfo(@TypeOf(a)).Int.bits;
19 // Number of significant digits19 // Number of significant digits
20 const sd = N - @clz(u64, a);20 const sd = N - @clz(u64, a);
21 // 8 exponent21 // 8 exponent
lib/std/special/compiler_rt/floatunditf.zig+1-1
...@@ -19,7 +19,7 @@ pub fn __floatunditf(a: u64) callconv(.C) f128 {...@@ -19,7 +19,7 @@ pub fn __floatunditf(a: u64) callconv(.C) f128 {
19 const exponent_bias = (1 << (exponent_bits - 1)) - 1;19 const exponent_bias = (1 << (exponent_bits - 1)) - 1;
20 const implicit_bit = 1 << mantissa_bits;20 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);
23 const shift: u7 = mantissa_bits - @intCast(u7, exp);23 const shift: u7 = mantissa_bits - @intCast(u7, exp);
2424
25 var result: u128 = (@intCast(u128, a) << shift) ^ implicit_bit;25 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 {...@@ -19,7 +19,7 @@ pub fn __floatunsitf(a: u64) callconv(.C) f128 {
19 const exponent_bias = (1 << (exponent_bits - 1)) - 1;19 const exponent_bias = (1 << (exponent_bits - 1)) - 1;
20 const implicit_bit = 1 << mantissa_bits;20 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);
23 const shift = mantissa_bits - @intCast(u7, exp);23 const shift = mantissa_bits - @intCast(u7, exp);
2424
25 // TODO(#1148): @bitCast alignment error25 // 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 {...@@ -219,7 +219,7 @@ fn test_one_divsi3(a: i32, b: i32, expected_q: i32) void {
219pub fn __udivsi3(n: u32, d: u32) callconv(.C) u32 {219pub fn __udivsi3(n: u32, d: u32) callconv(.C) u32 {
220 @setRuntimeSafety(builtin.is_test);220 @setRuntimeSafety(builtin.is_test);
221221
222 const n_uword_bits: c_uint = u32.bit_count;222 const n_uword_bits: c_uint = 32;
223 // special cases223 // special cases
224 if (d == 0) return 0; // ?!224 if (d == 0) return 0; // ?!
225 if (n == 0) return 0;225 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");...@@ -14,8 +14,8 @@ const compiler_rt = @import("../compiler_rt.zig");
14pub fn __modti3(a: i128, b: i128) callconv(.C) i128 {14pub fn __modti3(a: i128, b: i128) callconv(.C) i128 {
15 @setRuntimeSafety(builtin.is_test);15 @setRuntimeSafety(builtin.is_test);
1616
17 const s_a = a >> (i128.bit_count - 1); // s = a < 0 ? -1 : 017 const s_a = a >> (128 - 1); // s = a < 0 ? -1 : 0
18 const s_b = b >> (i128.bit_count - 1); // s = b < 0 ? -1 : 018 const s_b = b >> (128 - 1); // s = b < 0 ? -1 : 0
1919
20 const an = (a ^ s_a) -% s_a; // negate if s == -120 const an = (a ^ s_a) -% s_a; // negate if s == -1
21 const bn = (b ^ s_b) -% s_b; // negate if s == -121 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 {...@@ -33,9 +33,9 @@ pub fn __aeabi_dmul(a: f64, b: f64) callconv(.C) f64 {
3333
34fn mulXf3(comptime T: type, a: T, b: T) T {34fn mulXf3(comptime T: type, a: T, b: T) T {
35 @setRuntimeSafety(builtin.is_test);35 @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;
39 const significandBits = std.math.floatMantissaBits(T);39 const significandBits = std.math.floatMantissaBits(T);
40 const exponentBits = std.math.floatExponentBits(T);40 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 {...@@ -269,9 +269,9 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void {
269 }269 }
270}270}
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 {
273 @setRuntimeSafety(builtin.is_test);273 @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);
275 const significandBits = std.math.floatMantissaBits(T);275 const significandBits = std.math.floatMantissaBits(T);
276 const implicitBit = @as(Z, 1) << significandBits;276 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...@@ -282,7 +282,7 @@ fn normalize(comptime T: type, significand: *std.meta.Int(false, T.bit_count)) i
282282
283fn wideRightShiftWithSticky(comptime Z: type, hi: *Z, lo: *Z, count: u32) void {283fn wideRightShiftWithSticky(comptime Z: type, hi: *Z, lo: *Z, count: u32) void {
284 @setRuntimeSafety(builtin.is_test);284 @setRuntimeSafety(builtin.is_test);
285 const typeWidth = Z.bit_count;285 const typeWidth = @typeInfo(Z).Int.bits;
286 const S = std.math.Log2Int(Z);286 const S = std.math.Log2Int(Z);
287 if (count < typeWidth) {287 if (count < typeWidth) {
288 const sticky = @truncate(u8, lo.* << @intCast(S, typeWidth -% count));288 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;...@@ -11,7 +11,7 @@ const minInt = std.math.minInt;
11pub fn __mulodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 {11pub fn __mulodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 {
12 @setRuntimeSafety(builtin.is_test);12 @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)));
15 const max = ~min;15 const max = ~min;
1616
17 overflow.* = 0;17 overflow.* = 0;
lib/std/special/compiler_rt/muloti4.zig+3-3
...@@ -9,7 +9,7 @@ const compiler_rt = @import("../compiler_rt.zig");...@@ -9,7 +9,7 @@ const compiler_rt = @import("../compiler_rt.zig");
9pub fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 {9pub fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 {
10 @setRuntimeSafety(builtin.is_test);10 @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)));
13 const max = ~min;13 const max = ~min;
14 overflow.* = 0;14 overflow.* = 0;
1515
...@@ -27,9 +27,9 @@ pub fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 {...@@ -27,9 +27,9 @@ pub fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 {
27 return r;27 return r;
28 }28 }
2929
30 const sa = a >> (i128.bit_count - 1);30 const sa = a >> (128 - 1);
31 const abs_a = (a ^ sa) -% sa;31 const abs_a = (a ^ sa) -% sa;
32 const sb = b >> (i128.bit_count - 1);32 const sb = b >> (128 - 1);
33 const abs_b = (b ^ sb) -% sb;33 const abs_b = (b ^ sb) -% sb;
3434
35 if (abs_a < 2 or abs_b < 2) {35 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 {...@@ -24,9 +24,8 @@ pub fn __aeabi_dneg(arg: f64) callconv(.AAPCS) f64 {
24}24}
2525
26fn negXf2(comptime T: type, a: T) T {26fn 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;
30 const significandBits = std.math.floatMantissaBits(T);29 const significandBits = std.math.floatMantissaBits(T);
31 const exponentBits = std.math.floatExponentBits(T);30 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;...@@ -9,8 +9,9 @@ const Log2Int = std.math.Log2Int;
99
10fn Dwords(comptime T: type, comptime signed_half: bool) type {10fn Dwords(comptime T: type, comptime signed_half: bool) type {
11 return extern union {11 return extern union {
12 pub const HalfTU = std.meta.Int(false, @divExact(T.bit_count, 2));12 pub const bits = @divExact(@typeInfo(T).Int.bits, 2);
13 pub const HalfTS = std.meta.Int(true, @divExact(T.bit_count, 2));13 pub const HalfTU = std.meta.Int(false, bits);
14 pub const HalfTS = std.meta.Int(true, bits);
14 pub const HalfT = if (signed_half) HalfTS else HalfTU;15 pub const HalfT = if (signed_half) HalfTS else HalfTU;
1516
16 all: T,17 all: T,
...@@ -30,15 +31,15 @@ pub fn ashlXi3(comptime T: type, a: T, b: i32) T {...@@ -30,15 +31,15 @@ pub fn ashlXi3(comptime T: type, a: T, b: i32) T {
30 const input = dwords{ .all = a };31 const input = dwords{ .all = a };
31 var output: dwords = undefined;32 var output: dwords = undefined;
3233
33 if (b >= dwords.HalfT.bit_count) {34 if (b >= dwords.bits) {
34 output.s.low = 0;35 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);
36 } else if (b == 0) {37 } else if (b == 0) {
37 return a;38 return a;
38 } else {39 } else {
39 output.s.low = input.s.low << @intCast(S, b);40 output.s.low = input.s.low << @intCast(S, b);
40 output.s.high = input.s.high << @intCast(S, b);41 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);
42 }43 }
4344
44 return output.all;45 return output.all;
...@@ -53,14 +54,14 @@ pub fn ashrXi3(comptime T: type, a: T, b: i32) T {...@@ -53,14 +54,14 @@ pub fn ashrXi3(comptime T: type, a: T, b: i32) T {
53 const input = dwords{ .all = a };54 const input = dwords{ .all = a };
54 var output: dwords = undefined;55 var output: dwords = undefined;
5556
56 if (b >= dwords.HalfT.bit_count) {57 if (b >= dwords.bits) {
57 output.s.high = input.s.high >> (dwords.HalfT.bit_count - 1);58 output.s.high = input.s.high >> (dwords.bits - 1);
58 output.s.low = input.s.high >> @intCast(S, b - dwords.HalfT.bit_count);59 output.s.low = input.s.high >> @intCast(S, b - dwords.bits);
59 } else if (b == 0) {60 } else if (b == 0) {
60 return a;61 return a;
61 } else {62 } else {
62 output.s.high = input.s.high >> @intCast(S, b);63 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);
64 // Avoid sign-extension here65 // Avoid sign-extension here
65 output.s.low |= @bitCast(66 output.s.low |= @bitCast(
66 dwords.HalfT,67 dwords.HalfT,
...@@ -80,14 +81,14 @@ pub fn lshrXi3(comptime T: type, a: T, b: i32) T {...@@ -80,14 +81,14 @@ pub fn lshrXi3(comptime T: type, a: T, b: i32) T {
80 const input = dwords{ .all = a };81 const input = dwords{ .all = a };
81 var output: dwords = undefined;82 var output: dwords = undefined;
8283
83 if (b >= dwords.HalfT.bit_count) {84 if (b >= dwords.bits) {
84 output.s.high = 0;85 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);
86 } else if (b == 0) {87 } else if (b == 0) {
87 return a;88 return a;
88 } else {89 } else {
89 output.s.high = input.s.high >> @intCast(S, b);90 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);
91 output.s.low |= input.s.low >> @intCast(S, b);92 output.s.low |= input.s.low >> @intCast(S, b);
92 }93 }
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 {...@@ -50,7 +50,7 @@ fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
5050
51 // Various constants whose values follow from the type parameters.51 // Various constants whose values follow from the type parameters.
52 // Any reasonable optimizer will fold and propagate all of these.52 // 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;
54 const srcExpBits = srcBits - srcSigBits - 1;54 const srcExpBits = srcBits - srcSigBits - 1;
55 const srcInfExp = (1 << srcExpBits) - 1;55 const srcInfExp = (1 << srcExpBits) - 1;
56 const srcExpBias = srcInfExp >> 1;56 const srcExpBias = srcInfExp >> 1;
...@@ -65,7 +65,7 @@ fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {...@@ -65,7 +65,7 @@ fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
65 const srcQNaN = 1 << (srcSigBits - 1);65 const srcQNaN = 1 << (srcSigBits - 1);
66 const srcNaNCode = srcQNaN - 1;66 const srcNaNCode = srcQNaN - 1;
6767
68 const dstBits = dst_t.bit_count;68 const dstBits = @typeInfo(dst_t).Float.bits;
69 const dstExpBits = dstBits - dstSigBits - 1;69 const dstExpBits = dstBits - dstSigBits - 1;
70 const dstInfExp = (1 << dstExpBits) - 1;70 const dstInfExp = (1 << dstExpBits) - 1;
71 const dstExpBias = dstInfExp >> 1;71 const dstExpBias = dstInfExp >> 1;
lib/std/special/compiler_rt/udivmod.zig+36-34
...@@ -15,8 +15,10 @@ const high = 1 - low;...@@ -15,8 +15,10 @@ const high = 1 - low;
15pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: ?*DoubleInt) DoubleInt {15pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: ?*DoubleInt) DoubleInt {
16 @setRuntimeSafety(is_test);16 @setRuntimeSafety(is_test);
1717
18 const SingleInt = @import("std").meta.Int(false, @divExact(DoubleInt.bit_count, 2));18 const double_int_bits = @typeInfo(DoubleInt).Int.bits;
19 const SignedDoubleInt = @import("std").meta.Int(true, DoubleInt.bit_count);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);
20 const Log2SingleInt = @import("std").math.Log2Int(SingleInt);22 const Log2SingleInt = @import("std").math.Log2Int(SingleInt);
2123
22 const n = @ptrCast(*const [2]SingleInt, &a).*; // TODO issue #42124 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:...@@ -82,21 +84,21 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:
82 // ---84 // ---
83 // K 085 // K 0
84 sr = @bitCast(c_uint, @as(c_int, @clz(SingleInt, d[high])) - @as(c_int, @clz(SingleInt, n[high])));86 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 large87 // 0 <= sr <= single_int_bits - 2 or sr large
86 if (sr > SingleInt.bit_count - 2) {88 if (sr > single_int_bits - 2) {
87 if (maybe_rem) |rem| {89 if (maybe_rem) |rem| {
88 rem.* = a;90 rem.* = a;
89 }91 }
90 return 0;92 return 0;
91 }93 }
92 sr += 1;94 sr += 1;
93 // 1 <= sr <= SingleInt.bit_count - 195 // 1 <= sr <= single_int_bits - 1
94 // q.all = a << (DoubleInt.bit_count - sr);96 // q.all = a << (double_int_bits - sr);
95 q[low] = 0;97 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);
97 // r.all = a >> sr;99 // r.all = a >> sr;
98 r[high] = n[high] >> @intCast(Log2SingleInt, sr);100 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));
100 } else {102 } else {
101 // d[low] != 0103 // d[low] != 0
102 if (d[high] == 0) {104 if (d[high] == 0) {
...@@ -113,74 +115,74 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:...@@ -113,74 +115,74 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:
113 }115 }
114 sr = @ctz(SingleInt, d[low]);116 sr = @ctz(SingleInt, d[low]);
115 q[high] = n[high] >> @intCast(Log2SingleInt, sr);117 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));
117 return @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &q[0]).*; // TODO issue #421119 return @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &q[0]).*; // TODO issue #421
118 }120 }
119 // K X121 // K X
120 // ---122 // ---
121 // 0 K123 // 0 K
122 sr = 1 + SingleInt.bit_count + @as(c_uint, @clz(SingleInt, d[low])) - @as(c_uint, @clz(SingleInt, n[high]));124 sr = 1 + single_int_bits + @as(c_uint, @clz(SingleInt, d[low])) - @as(c_uint, @clz(SingleInt, n[high]));
123 // 2 <= sr <= DoubleInt.bit_count - 1125 // 2 <= sr <= double_int_bits - 1
124 // q.all = a << (DoubleInt.bit_count - sr);126 // q.all = a << (double_int_bits - sr);
125 // r.all = a >> sr;127 // r.all = a >> sr;
126 if (sr == SingleInt.bit_count) {128 if (sr == single_int_bits) {
127 q[low] = 0;129 q[low] = 0;
128 q[high] = n[low];130 q[high] = n[low];
129 r[high] = 0;131 r[high] = 0;
130 r[low] = n[high];132 r[low] = n[high];
131 } else if (sr < SingleInt.bit_count) {133 } else if (sr < single_int_bits) {
132 // 2 <= sr <= SingleInt.bit_count - 1134 // 2 <= sr <= single_int_bits - 1
133 q[low] = 0;135 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);
135 r[high] = n[high] >> @intCast(Log2SingleInt, sr);137 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));
137 } else {139 } else {
138 // SingleInt.bit_count + 1 <= sr <= DoubleInt.bit_count - 1140 // single_int_bits + 1 <= sr <= double_int_bits - 1
139 q[low] = n[low] << @intCast(Log2SingleInt, DoubleInt.bit_count - sr);141 q[low] = n[low] << @intCast(Log2SingleInt, double_int_bits - sr);
140 q[high] = (n[high] << @intCast(Log2SingleInt, DoubleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr - SingleInt.bit_count));142 q[high] = (n[high] << @intCast(Log2SingleInt, double_int_bits - sr)) | (n[low] >> @intCast(Log2SingleInt, sr - single_int_bits));
141 r[high] = 0;143 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);
143 }145 }
144 } else {146 } else {
145 // K X147 // K X
146 // ---148 // ---
147 // K K149 // K K
148 sr = @bitCast(c_uint, @as(c_int, @clz(SingleInt, d[high])) - @as(c_int, @clz(SingleInt, n[high])));150 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 large151 // 0 <= sr <= single_int_bits - 1 or sr large
150 if (sr > SingleInt.bit_count - 1) {152 if (sr > single_int_bits - 1) {
151 if (maybe_rem) |rem| {153 if (maybe_rem) |rem| {
152 rem.* = a;154 rem.* = a;
153 }155 }
154 return 0;156 return 0;
155 }157 }
156 sr += 1;158 sr += 1;
157 // 1 <= sr <= SingleInt.bit_count159 // 1 <= sr <= single_int_bits
158 // q.all = a << (DoubleInt.bit_count - sr);160 // q.all = a << (double_int_bits - sr);
159 // r.all = a >> sr;161 // r.all = a >> sr;
160 q[low] = 0;162 q[low] = 0;
161 if (sr == SingleInt.bit_count) {163 if (sr == single_int_bits) {
162 q[high] = n[low];164 q[high] = n[low];
163 r[high] = 0;165 r[high] = 0;
164 r[low] = n[high];166 r[low] = n[high];
165 } else {167 } else {
166 r[high] = n[high] >> @intCast(Log2SingleInt, sr);168 r[high] = n[high] >> @intCast(Log2SingleInt, sr);
167 r[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));169 r[low] = (n[high] << @intCast(Log2SingleInt, single_int_bits - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
168 q[high] = n[low] << @intCast(Log2SingleInt, SingleInt.bit_count - sr);170 q[high] = n[low] << @intCast(Log2SingleInt, single_int_bits - sr);
169 }171 }
170 }172 }
171 }173 }
172 // Not a special case174 // Not a special case
173 // q and r are initialized with:175 // q and r are initialized with:
174 // q.all = a << (DoubleInt.bit_count - sr);176 // q.all = a << (double_int_bits - sr);
175 // r.all = a >> sr;177 // r.all = a >> sr;
176 // 1 <= sr <= DoubleInt.bit_count - 1178 // 1 <= sr <= double_int_bits - 1
177 var carry: u32 = 0;179 var carry: u32 = 0;
178 var r_all: DoubleInt = undefined;180 var r_all: DoubleInt = undefined;
179 while (sr > 0) : (sr -= 1) {181 while (sr > 0) : (sr -= 1) {
180 // r:q = ((r:q) << 1) | carry182 // r:q = ((r:q) << 1) | carry
181 r[high] = (r[high] << 1) | (r[low] >> (SingleInt.bit_count - 1));183 r[high] = (r[high] << 1) | (r[low] >> (single_int_bits - 1));
182 r[low] = (r[low] << 1) | (q[high] >> (SingleInt.bit_count - 1));184 r[low] = (r[low] << 1) | (q[high] >> (single_int_bits - 1));
183 q[high] = (q[high] << 1) | (q[low] >> (SingleInt.bit_count - 1));185 q[high] = (q[high] << 1) | (q[low] >> (single_int_bits - 1));
184 q[low] = (q[low] << 1) | carry;186 q[low] = (q[low] << 1) | carry;
185 // carry = 0;187 // carry = 0;
186 // if (r.all >= b)188 // if (r.all >= b)
...@@ -189,7 +191,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:...@@ -189,7 +191,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:
189 // carry = 1;191 // carry = 1;
190 // }192 // }
191 r_all = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421193 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);
193 carry = @intCast(u32, s & 1);195 carry = @intCast(u32, s & 1);
194 r_all -= b & @bitCast(DoubleInt, s);196 r_all -= b & @bitCast(DoubleInt, s);
195 r = @ptrCast(*[2]SingleInt, &r_all).*; // TODO issue #421197 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 {...@@ -40,7 +40,7 @@ pub fn main() anyerror!void {
40 test_node.activate();40 test_node.activate();
41 progress.refresh();41 progress.refresh();
42 if (progress.terminal == null) {42 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 });
44 }44 }
45 const result = if (test_fn.async_frame_size) |size| switch (io_mode) {45 const result = if (test_fn.async_frame_size) |size| switch (io_mode) {
46 .evented => blk: {46 .evented => blk: {
lib/std/start.zig+2-2
...@@ -67,7 +67,7 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv...@@ -67,7 +67,7 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv
67 uefi.handle = handle;67 uefi.handle = handle;
68 uefi.system_table = system_table;68 uefi.system_table = system_table;
6969
70 switch (@TypeOf(root.main).ReturnType) {70 switch (@typeInfo(@TypeOf(root.main)).Fn.return_type.?) {
71 noreturn => {71 noreturn => {
72 root.main();72 root.main();
73 },73 },
...@@ -239,7 +239,7 @@ fn callMainAsync(loop: *std.event.Loop) callconv(.Async) u8 {...@@ -239,7 +239,7 @@ fn callMainAsync(loop: *std.event.Loop) callconv(.Async) u8 {
239// This is not marked inline because it is called with @asyncCall when239// This is not marked inline because it is called with @asyncCall when
240// there is an event loop.240// there is an event loop.
241pub fn callMain() u8 {241pub fn callMain() u8 {
242 switch (@typeInfo(@TypeOf(root.main).ReturnType)) {242 switch (@typeInfo(@typeInfo(@TypeOf(root.main)).Fn.return_type.?)) {
243 .NoReturn => {243 .NoReturn => {
244 root.main();244 root.main();
245 },245 },
lib/std/std.zig+1
...@@ -50,6 +50,7 @@ pub const builtin = @import("builtin.zig");...@@ -50,6 +50,7 @@ pub const builtin = @import("builtin.zig");
50pub const c = @import("c.zig");50pub const c = @import("c.zig");
51pub const cache_hash = @import("cache_hash.zig");51pub const cache_hash = @import("cache_hash.zig");
52pub const coff = @import("coff.zig");52pub const coff = @import("coff.zig");
53pub const compress = @import("compress.zig");
53pub const crypto = @import("crypto.zig");54pub const crypto = @import("crypto.zig");
54pub const cstr = @import("cstr.zig");55pub const cstr = @import("cstr.zig");
55pub const debug = @import("debug.zig");56pub const debug = @import("debug.zig");
lib/std/target.zig+59-1
...@@ -101,7 +101,7 @@ pub const Target = struct {...@@ -101,7 +101,7 @@ pub const Target = struct {
101101
102 /// Latest Windows version that the Zig Standard Library is aware of102 /// Latest Windows version that the Zig Standard Library is aware of
103 pub const latest = WindowsVersion.win10_20h1;103 pub const latest = WindowsVersion.win10_20h1;
104 104
105 pub const Range = struct {105 pub const Range = struct {
106 min: WindowsVersion,106 min: WindowsVersion,
107 max: WindowsVersion,107 max: WindowsVersion,
...@@ -468,6 +468,7 @@ pub const Target = struct {...@@ -468,6 +468,7 @@ pub const Target = struct {
468 /// TODO Get rid of this one.468 /// TODO Get rid of this one.
469 unknown,469 unknown,
470 coff,470 coff,
471 pe,
471 elf,472 elf,
472 macho,473 macho,
473 wasm,474 wasm,
...@@ -771,6 +772,63 @@ pub const Target = struct {...@@ -771,6 +772,63 @@ pub const Target = struct {
771 };772 };
772 }773 }
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
774 pub fn endian(arch: Arch) builtin.Endian {832 pub fn endian(arch: Arch) builtin.Endian {
775 return switch (arch) {833 return switch (arch) {
776 .avr,834 .avr,
lib/std/thread.zig+3-3
...@@ -166,7 +166,7 @@ pub const Thread = struct {...@@ -166,7 +166,7 @@ pub const Thread = struct {
166 fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD {166 fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD {
167 const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;167 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.?)) {
170 .NoReturn => {170 .NoReturn => {
171 startFn(arg);171 startFn(arg);
172 },172 },
...@@ -227,7 +227,7 @@ pub const Thread = struct {...@@ -227,7 +227,7 @@ pub const Thread = struct {
227 fn linuxThreadMain(ctx_addr: usize) callconv(.C) u8 {227 fn linuxThreadMain(ctx_addr: usize) callconv(.C) u8 {
228 const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;228 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.?)) {
231 .NoReturn => {231 .NoReturn => {
232 startFn(arg);232 startFn(arg);
233 },233 },
...@@ -259,7 +259,7 @@ pub const Thread = struct {...@@ -259,7 +259,7 @@ pub const Thread = struct {
259 fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void {259 fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void {
260 const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), ctx)).*;260 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.?)) {
263 .NoReturn => {263 .NoReturn => {
264 startFn(arg);264 startFn(arg);
265 },265 },
lib/std/zig.zig+1-1
...@@ -22,7 +22,7 @@ pub const SrcHash = [16]u8;...@@ -22,7 +22,7 @@ pub const SrcHash = [16]u8;
22/// If it is long, blake3 hash is computed.22/// If it is long, blake3 hash is computed.
23pub fn hashSrc(src: []const u8) SrcHash {23pub fn hashSrc(src: []const u8) SrcHash {
24 var out: SrcHash = undefined;24 var out: SrcHash = undefined;
25 if (src.len <= SrcHash.len) {25 if (src.len <= @typeInfo(SrcHash).Array.len) {
26 std.mem.copy(u8, &out, src);26 std.mem.copy(u8, &out, src);
27 std.mem.set(u8, out[src.len..], 0);27 std.mem.set(u8, out[src.len..], 0);
28 } else {28 } else {
lib/std/zig/parser_test.zig+114-6
...@@ -615,6 +615,17 @@ test "zig fmt: infix operator and then multiline string literal" {...@@ -615,6 +615,17 @@ test "zig fmt: infix operator and then multiline string literal" {
615 );615 );
616}616}
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
618test "zig fmt: C pointers" {629test "zig fmt: C pointers" {
619 try testCanonical(630 try testCanonical(
620 \\const Ptr = [*c]i32;631 \\const Ptr = [*c]i32;
...@@ -885,6 +896,28 @@ test "zig fmt: 2nd arg multiline string" {...@@ -885,6 +896,28 @@ test "zig fmt: 2nd arg multiline string" {
885 );896 );
886}897}
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
888test "zig fmt: if condition wraps" {921test "zig fmt: if condition wraps" {
889 try testTransform(922 try testTransform(
890 \\comptime {923 \\comptime {
...@@ -915,6 +948,11 @@ test "zig fmt: if condition wraps" {...@@ -915,6 +948,11 @@ test "zig fmt: if condition wraps" {
915 \\ var a = if (a) |*f| x: {948 \\ var a = if (a) |*f| x: {
916 \\ break :x &a.b;949 \\ break :x &a.b;
917 \\ } else |err| err;950 \\ } else |err| err;
951 \\ var a = if (cond and
952 \\ cond) |*f|
953 \\ x: {
954 \\ break :x &a.b;
955 \\ } else |err| err;
918 \\}956 \\}
919 ,957 ,
920 \\comptime {958 \\comptime {
...@@ -951,6 +989,35 @@ test "zig fmt: if condition wraps" {...@@ -951,6 +989,35 @@ test "zig fmt: if condition wraps" {
951 \\ var a = if (a) |*f| x: {989 \\ var a = if (a) |*f| x: {
952 \\ break :x &a.b;990 \\ break :x &a.b;
953 \\ } else |err| err;991 \\ } 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 \\ }
954 \\}1021 \\}
955 \\1022 \\
956 );1023 );
...@@ -977,6 +1044,18 @@ test "zig fmt: if condition has line break but must not wrap" {...@@ -977,6 +1044,18 @@ test "zig fmt: if condition has line break but must not wrap" {
977 );1044 );
978}1045}
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
980test "zig fmt: same-line doc comment on variable declaration" {1059test "zig fmt: same-line doc comment on variable declaration" {
981 try testTransform(1060 try testTransform(
982 \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space1061 \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space
...@@ -1228,7 +1307,7 @@ test "zig fmt: array literal with hint" {...@@ -1228,7 +1307,7 @@ test "zig fmt: array literal with hint" {
1228 \\const a = []u8{1307 \\const a = []u8{
1229 \\ 1, 2,1308 \\ 1, 2,
1230 \\ 3, //1309 \\ 3, //
1231 \\ 4,1310 \\ 4,
1232 \\ 5, 6,1311 \\ 5, 6,
1233 \\ 7,1312 \\ 7,
1234 \\};1313 \\};
...@@ -1293,7 +1372,7 @@ test "zig fmt: multiline string parameter in fn call with trailing comma" {...@@ -1293,7 +1372,7 @@ test "zig fmt: multiline string parameter in fn call with trailing comma" {
1293 \\ \\ZIG_C_HEADER_FILES {}1372 \\ \\ZIG_C_HEADER_FILES {}
1294 \\ \\ZIG_DIA_GUIDS_LIB {}1373 \\ \\ZIG_DIA_GUIDS_LIB {}
1295 \\ \\1374 \\ \\
1296 \\ ,1375 \\ ,
1297 \\ std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),1376 \\ std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),
1298 \\ std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),1377 \\ std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),
1299 \\ std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),1378 \\ std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),
...@@ -2885,20 +2964,20 @@ test "zig fmt: multiline string in array" {...@@ -2885,20 +2964,20 @@ test "zig fmt: multiline string in array" {
2885 try testCanonical(2964 try testCanonical(
2886 \\const Foo = [][]const u8{2965 \\const Foo = [][]const u8{
2887 \\ \\aaa2966 \\ \\aaa
2888 \\,2967 \\ ,
2889 \\ \\bbb2968 \\ \\bbb
2890 \\};2969 \\};
2891 \\2970 \\
2892 \\fn bar() void {2971 \\fn bar() void {
2893 \\ const Foo = [][]const u8{2972 \\ const Foo = [][]const u8{
2894 \\ \\aaa2973 \\ \\aaa
2895 \\ ,2974 \\ ,
2896 \\ \\bbb2975 \\ \\bbb
2897 \\ };2976 \\ };
2898 \\ const Bar = [][]const u8{ // comment here2977 \\ const Bar = [][]const u8{ // comment here
2899 \\ \\aaa2978 \\ \\aaa
2900 \\ \\2979 \\ \\
2901 \\ , // and another comment can go here2980 \\ , // and another comment can go here
2902 \\ \\bbb2981 \\ \\bbb
2903 \\ };2982 \\ };
2904 \\}2983 \\}
...@@ -3214,6 +3293,34 @@ test "zig fmt: C var args" {...@@ -3214,6 +3293,34 @@ test "zig fmt: C var args" {
3214 );3293 );
3215}3294}
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
3217const std = @import("std");3324const std = @import("std");
3218const mem = std.mem;3325const mem = std.mem;
3219const warn = std.debug.warn;3326const warn = std.debug.warn;
...@@ -3256,7 +3363,8 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b...@@ -3256,7 +3363,8 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
3256 var buffer = std.ArrayList(u8).init(allocator);3363 var buffer = std.ArrayList(u8).init(allocator);
3257 errdefer buffer.deinit();3364 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);
3260 return buffer.toOwnedSlice();3368 return buffer.toOwnedSlice();
3261}3369}
3262fn testTransform(source: []const u8, expected_source: []const u8) !void {3370fn testTransform(source: []const u8, expected_source: []const u8) !void {
lib/std/zig/render.zig+763-912
...@@ -6,10 +6,12 @@...@@ -6,10 +6,12 @@
6const std = @import("../std.zig");6const std = @import("../std.zig");
7const assert = std.debug.assert;7const assert = std.debug.assert;
8const mem = std.mem;8const mem = std.mem;
9const meta = std.meta;
9const ast = std.zig.ast;10const ast = std.zig.ast;
10const Token = std.zig.Token;11const Token = std.zig.Token;
1112
12const indent_delta = 4;13const indent_delta = 4;
14const asm_indent_delta = 2;
1315
14pub const Error = error{16pub const Error = error{
15 /// Ran out of memory allocating call stack frames to complete rendering.17 /// 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...@@ -21,70 +23,32 @@ pub fn render(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree) (@Typ
21 // cannot render an invalid tree23 // cannot render an invalid tree
22 std.debug.assert(tree.errors.len == 0);24 std.debug.assert(tree.errors.len == 0);
2325
24 // make a passthrough stream that checks whether something changed26 var change_detection_stream = std.io.changeDetectionStream(tree.source, stream);
25 const MyStream = struct {27 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, change_detection_stream.writer());
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 };
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) {31 return change_detection_stream.changeDetected();
64 my_stream.anything_changed = true;
65 }
66
67 return my_stream.anything_changed;
68}32}
6933
70fn renderRoot(34fn renderRoot(
71 allocator: *mem.Allocator,35 allocator: *mem.Allocator,
72 stream: anytype,36 ais: anytype,
73 tree: *ast.Tree,37 tree: *ast.Tree,
74) (@TypeOf(stream).Error || Error)!void {38) (@TypeOf(ais.*).Error || Error)!void {
39
75 // render all the line comments at the beginning of the file40 // render all the line comments at the beginning of the file
76 for (tree.token_ids) |token_id, i| {41 for (tree.token_ids) |token_id, i| {
77 if (token_id != .LineComment) break;42 if (token_id != .LineComment) break;
78 const token_loc = tree.token_locs[i];43 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), " ")});
80 const next_token = tree.token_locs[i + 1];45 const next_token = tree.token_locs[i + 1];
81 const loc = tree.tokenLocationLoc(token_loc.end, next_token);46 const loc = tree.tokenLocationLoc(token_loc.end, next_token);
82 if (loc.line >= 2) {47 if (loc.line >= 2) {
83 try stream.writeByte('\n');48 try ais.insertNewline();
84 }49 }
85 }50 }
8651
87 var start_col: usize = 0;
88 var decl_i: ast.NodeIndex = 0;52 var decl_i: ast.NodeIndex = 0;
89 const root_decls = tree.root_node.decls();53 const root_decls = tree.root_node.decls();
9054
...@@ -145,7 +109,7 @@ fn renderRoot(...@@ -145,7 +109,7 @@ fn renderRoot(
145 // If there's no next reformatted `decl`, just copy the109 // If there's no next reformatted `decl`, just copy the
146 // remaining input tokens and bail out.110 // remaining input tokens and bail out.
147 const start = tree.token_locs[copy_start_token_index].start;111 const start = tree.token_locs[copy_start_token_index].start;
148 try copyFixingWhitespace(stream, tree.source[start..]);112 try copyFixingWhitespace(ais, tree.source[start..]);
149 return;113 return;
150 }114 }
151 decl = root_decls[decl_i];115 decl = root_decls[decl_i];
...@@ -186,26 +150,25 @@ fn renderRoot(...@@ -186,26 +150,25 @@ fn renderRoot(
186150
187 const start = tree.token_locs[copy_start_token_index].start;151 const start = tree.token_locs[copy_start_token_index].start;
188 const end = tree.token_locs[copy_end_token_index].start;152 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]);
190 }154 }
191155
192 try renderTopLevelDecl(allocator, stream, tree, 0, &start_col, decl);156 try renderTopLevelDecl(allocator, ais, tree, decl);
193 decl_i += 1;157 decl_i += 1;
194 if (decl_i >= root_decls.len) return;158 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]);
196 }160 }
197}161}
198162
199fn renderExtraNewline(tree: *ast.Tree, stream: anytype, start_col: *usize, node: *ast.Node) @TypeOf(stream).Error!void {163fn renderExtraNewline(tree: *ast.Tree, ais: anytype, node: *ast.Node) @TypeOf(ais.*).Error!void {
200 return renderExtraNewlineToken(tree, stream, start_col, node.firstToken());164 return renderExtraNewlineToken(tree, ais, node.firstToken());
201}165}
202166
203fn renderExtraNewlineToken(167fn renderExtraNewlineToken(
204 tree: *ast.Tree,168 tree: *ast.Tree,
205 stream: anytype,169 ais: anytype,
206 start_col: *usize,
207 first_token: ast.TokenIndex,170 first_token: ast.TokenIndex,
208) @TypeOf(stream).Error!void {171) @TypeOf(ais.*).Error!void {
209 var prev_token = first_token;172 var prev_token = first_token;
210 if (prev_token == 0) return;173 if (prev_token == 0) return;
211 var newline_threshold: usize = 2;174 var newline_threshold: usize = 2;
...@@ -218,28 +181,27 @@ fn renderExtraNewlineToken(...@@ -218,28 +181,27 @@ fn renderExtraNewlineToken(
218 const prev_token_end = tree.token_locs[prev_token - 1].end;181 const prev_token_end = tree.token_locs[prev_token - 1].end;
219 const loc = tree.tokenLocation(prev_token_end, first_token);182 const loc = tree.tokenLocation(prev_token_end, first_token);
220 if (loc.line >= newline_threshold) {183 if (loc.line >= newline_threshold) {
221 try stream.writeByte('\n');184 try ais.insertNewline();
222 start_col.* = 0;
223 }185 }
224}186}
225187
226fn renderTopLevelDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Error || Error)!void {188fn renderTopLevelDecl(allocator: *mem.Allocator, ais: anytype, tree: *ast.Tree, decl: *ast.Node) (@TypeOf(ais.*).Error || Error)!void {
227 try renderContainerDecl(allocator, stream, tree, indent, start_col, decl, .Newline);189 try renderContainerDecl(allocator, ais, tree, decl, .Newline);
228}190}
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 {
231 switch (decl.tag) {193 switch (decl.tag) {
232 .FnProto => {194 .FnProto => {
233 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);195 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
237 if (fn_proto.getBodyNode()) |body_node| {199 if (fn_proto.getBodyNode()) |body_node| {
238 try renderExpression(allocator, stream, tree, indent, start_col, decl, .Space);200 try renderExpression(allocator, ais, tree, decl, .Space);
239 try renderExpression(allocator, stream, tree, indent, start_col, body_node, space);201 try renderExpression(allocator, ais, tree, body_node, space);
240 } else {202 } else {
241 try renderExpression(allocator, stream, tree, indent, start_col, decl, .None);203 try renderExpression(allocator, ais, tree, decl, .None);
242 try renderToken(tree, stream, tree.nextToken(decl.lastToken()), indent, start_col, space);204 try renderToken(tree, ais, tree.nextToken(decl.lastToken()), space);
243 }205 }
244 },206 },
245207
...@@ -247,35 +209,35 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tr...@@ -247,35 +209,35 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tr
247 const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);209 const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);
248210
249 if (use_decl.visib_token) |visib_token| {211 if (use_decl.visib_token) |visib_token| {
250 try renderToken(tree, stream, visib_token, indent, start_col, .Space); // pub212 try renderToken(tree, ais, visib_token, .Space); // pub
251 }213 }
252 try renderToken(tree, stream, use_decl.use_token, indent, start_col, .Space); // usingnamespace214 try renderToken(tree, ais, use_decl.use_token, .Space); // usingnamespace
253 try renderExpression(allocator, stream, tree, indent, start_col, use_decl.expr, .None);215 try renderExpression(allocator, ais, tree, use_decl.expr, .None);
254 try renderToken(tree, stream, use_decl.semicolon_token, indent, start_col, space); // ;216 try renderToken(tree, ais, use_decl.semicolon_token, space); // ;
255 },217 },
256218
257 .VarDecl => {219 .VarDecl => {
258 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);220 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
259221
260 try renderDocComments(tree, stream, var_decl, var_decl.getDocComments(), indent, start_col);222 try renderDocComments(tree, ais, var_decl, var_decl.getDocComments());
261 try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl);223 try renderVarDecl(allocator, ais, tree, var_decl);
262 },224 },
263225
264 .TestDecl => {226 .TestDecl => {
265 const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);227 const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
266228
267 try renderDocComments(tree, stream, test_decl, test_decl.doc_comments, indent, start_col);229 try renderDocComments(tree, ais, test_decl, test_decl.doc_comments);
268 try renderToken(tree, stream, test_decl.test_token, indent, start_col, .Space);230 try renderToken(tree, ais, test_decl.test_token, .Space);
269 try renderExpression(allocator, stream, tree, indent, start_col, test_decl.name, .Space);231 try renderExpression(allocator, ais, tree, test_decl.name, .Space);
270 try renderExpression(allocator, stream, tree, indent, start_col, test_decl.body_node, space);232 try renderExpression(allocator, ais, tree, test_decl.body_node, space);
271 },233 },
272234
273 .ContainerField => {235 .ContainerField => {
274 const field = @fieldParentPtr(ast.Node.ContainerField, "base", decl);236 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);
277 if (field.comptime_token) |t| {239 if (field.comptime_token) |t| {
278 try renderToken(tree, stream, t, indent, start_col, .Space); // comptime240 try renderToken(tree, ais, t, .Space); // comptime
279 }241 }
280242
281 const src_has_trailing_comma = blk: {243 const src_has_trailing_comma = blk: {
...@@ -288,68 +250,67 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tr...@@ -288,68 +250,67 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tr
288 const last_token_space: Space = if (src_has_trailing_comma) .None else space;250 const last_token_space: Space = if (src_has_trailing_comma) .None else space;
289251
290 if (field.type_expr == null and field.value_expr == null) {252 if (field.type_expr == null and field.value_expr == null) {
291 try renderToken(tree, stream, field.name_token, indent, start_col, last_token_space); // name253 try renderToken(tree, ais, field.name_token, last_token_space); // name
292 } else if (field.type_expr != null and field.value_expr == null) {254 } else if (field.type_expr != null and field.value_expr == null) {
293 try renderToken(tree, stream, field.name_token, indent, start_col, .None); // name255 try renderToken(tree, ais, field.name_token, .None); // name
294 try renderToken(tree, stream, tree.nextToken(field.name_token), indent, start_col, .Space); // :256 try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // :
295257
296 if (field.align_expr) |align_value_expr| {258 if (field.align_expr) |align_value_expr| {
297 try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, .Space); // type259 try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type
298 const lparen_token = tree.prevToken(align_value_expr.firstToken());260 const lparen_token = tree.prevToken(align_value_expr.firstToken());
299 const align_kw = tree.prevToken(lparen_token);261 const align_kw = tree.prevToken(lparen_token);
300 const rparen_token = tree.nextToken(align_value_expr.lastToken());262 const rparen_token = tree.nextToken(align_value_expr.lastToken());
301 try renderToken(tree, stream, align_kw, indent, start_col, .None); // align263 try renderToken(tree, ais, align_kw, .None); // align
302 try renderToken(tree, stream, lparen_token, indent, start_col, .None); // (264 try renderToken(tree, ais, lparen_token, .None); // (
303 try renderExpression(allocator, stream, tree, indent, start_col, align_value_expr, .None); // alignment265 try renderExpression(allocator, ais, tree, align_value_expr, .None); // alignment
304 try renderToken(tree, stream, rparen_token, indent, start_col, last_token_space); // )266 try renderToken(tree, ais, rparen_token, last_token_space); // )
305 } else {267 } else {
306 try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, last_token_space); // type268 try renderExpression(allocator, ais, tree, field.type_expr.?, last_token_space); // type
307 }269 }
308 } else if (field.type_expr == null and field.value_expr != null) {270 } else if (field.type_expr == null and field.value_expr != null) {
309 try renderToken(tree, stream, field.name_token, indent, start_col, .Space); // name271 try renderToken(tree, ais, field.name_token, .Space); // name
310 try renderToken(tree, stream, tree.nextToken(field.name_token), indent, start_col, .Space); // =272 try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // =
311 try renderExpression(allocator, stream, tree, indent, start_col, field.value_expr.?, last_token_space); // value273 try renderExpression(allocator, ais, tree, field.value_expr.?, last_token_space); // value
312 } else {274 } else {
313 try renderToken(tree, stream, field.name_token, indent, start_col, .None); // name275 try renderToken(tree, ais, field.name_token, .None); // name
314 try renderToken(tree, stream, tree.nextToken(field.name_token), indent, start_col, .Space); // :276 try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // :
315277
316 if (field.align_expr) |align_value_expr| {278 if (field.align_expr) |align_value_expr| {
317 try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, .Space); // type279 try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type
318 const lparen_token = tree.prevToken(align_value_expr.firstToken());280 const lparen_token = tree.prevToken(align_value_expr.firstToken());
319 const align_kw = tree.prevToken(lparen_token);281 const align_kw = tree.prevToken(lparen_token);
320 const rparen_token = tree.nextToken(align_value_expr.lastToken());282 const rparen_token = tree.nextToken(align_value_expr.lastToken());
321 try renderToken(tree, stream, align_kw, indent, start_col, .None); // align283 try renderToken(tree, ais, align_kw, .None); // align
322 try renderToken(tree, stream, lparen_token, indent, start_col, .None); // (284 try renderToken(tree, ais, lparen_token, .None); // (
323 try renderExpression(allocator, stream, tree, indent, start_col, align_value_expr, .None); // alignment285 try renderExpression(allocator, ais, tree, align_value_expr, .None); // alignment
324 try renderToken(tree, stream, rparen_token, indent, start_col, .Space); // )286 try renderToken(tree, ais, rparen_token, .Space); // )
325 } else {287 } else {
326 try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, .Space); // type288 try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type
327 }289 }
328 try renderToken(tree, stream, tree.prevToken(field.value_expr.?.firstToken()), indent, start_col, .Space); // =290 try renderToken(tree, ais, tree.prevToken(field.value_expr.?.firstToken()), .Space); // =
329 try renderExpression(allocator, stream, tree, indent, start_col, field.value_expr.?, last_token_space); // value291 try renderExpression(allocator, ais, tree, field.value_expr.?, last_token_space); // value
330 }292 }
331293
332 if (src_has_trailing_comma) {294 if (src_has_trailing_comma) {
333 const comma = tree.nextToken(field.lastToken());295 const comma = tree.nextToken(field.lastToken());
334 try renderToken(tree, stream, comma, indent, start_col, space);296 try renderToken(tree, ais, comma, space);
335 }297 }
336 },298 },
337299
338 .Comptime => {300 .Comptime => {
339 assert(!decl.requireSemiColon());301 assert(!decl.requireSemiColon());
340 try renderExpression(allocator, stream, tree, indent, start_col, decl, space);302 try renderExpression(allocator, ais, tree, decl, space);
341 },303 },
342304
343 .DocComment => {305 .DocComment => {
344 const comment = @fieldParentPtr(ast.Node.DocComment, "base", decl);306 const comment = @fieldParentPtr(ast.Node.DocComment, "base", decl);
345 const kind = tree.token_ids[comment.first_line];307 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);
347 var tok_i = comment.first_line + 1;309 var tok_i = comment.first_line + 1;
348 while (true) : (tok_i += 1) {310 while (true) : (tok_i += 1) {
349 const tok_id = tree.token_ids[tok_i];311 const tok_id = tree.token_ids[tok_i];
350 if (tok_id == kind) {312 if (tok_id == kind) {
351 try stream.writeByteNTimes(' ', indent);313 try renderToken(tree, ais, tok_i, .Newline);
352 try renderToken(tree, stream, tok_i, indent, start_col, .Newline);
353 } else if (tok_id == .LineComment) {314 } else if (tok_id == .LineComment) {
354 continue;315 continue;
355 } else {316 } else {
...@@ -363,13 +324,11 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tr...@@ -363,13 +324,11 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tr
363324
364fn renderExpression(325fn renderExpression(
365 allocator: *mem.Allocator,326 allocator: *mem.Allocator,
366 stream: anytype,327 ais: anytype,
367 tree: *ast.Tree,328 tree: *ast.Tree,
368 indent: usize,
369 start_col: *usize,
370 base: *ast.Node,329 base: *ast.Node,
371 space: Space,330 space: Space,
372) (@TypeOf(stream).Error || Error)!void {331) (@TypeOf(ais.*).Error || Error)!void {
373 switch (base.tag) {332 switch (base.tag) {
374 .Identifier,333 .Identifier,
375 .IntegerLiteral,334 .IntegerLiteral,
...@@ -383,18 +342,18 @@ fn renderExpression(...@@ -383,18 +342,18 @@ fn renderExpression(
383 .UndefinedLiteral,342 .UndefinedLiteral,
384 => {343 => {
385 const casted_node = base.cast(ast.Node.OneToken).?;344 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);
387 },346 },
388347
389 .AnyType => {348 .AnyType => {
390 const any_type = base.castTag(.AnyType).?;349 const any_type = base.castTag(.AnyType).?;
391 if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) {350 if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) {
392 // TODO remove in next release cycle351 // TODO remove in next release cycle
393 try stream.writeAll("anytype");352 try ais.writer().writeAll("anytype");
394 if (space == .Comma) try stream.writeAll(",\n");353 if (space == .Comma) try ais.writer().writeAll(",\n");
395 return;354 return;
396 }355 }
397 return renderToken(tree, stream, any_type.token, indent, start_col, space);356 return renderToken(tree, ais, any_type.token, space);
398 },357 },
399358
400 .Block, .LabeledBlock => {359 .Block, .LabeledBlock => {
...@@ -424,65 +383,65 @@ fn renderExpression(...@@ -424,65 +383,65 @@ fn renderExpression(
424 };383 };
425384
426 if (block.label) |label| {385 if (block.label) |label| {
427 try renderToken(tree, stream, label, indent, start_col, Space.None);386 try renderToken(tree, ais, label, Space.None);
428 try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space);387 try renderToken(tree, ais, tree.nextToken(label), Space.Space);
429 }388 }
430389
431 if (block.statements.len == 0) {390 if (block.statements.len == 0) {
432 try renderToken(tree, stream, block.lbrace, indent + indent_delta, start_col, Space.None);391 ais.pushIndentNextLine();
433 return renderToken(tree, stream, block.rbrace, indent, start_col, space);392 defer ais.popIndent();
393 try renderToken(tree, ais, block.lbrace, Space.None);
434 } else {394 } else {
435 const block_indent = indent + indent_delta;395 ais.pushIndentNextLine();
436 try renderToken(tree, stream, block.lbrace, block_indent, start_col, Space.Newline);396 defer ais.popIndent();
397
398 try renderToken(tree, ais, block.lbrace, Space.Newline);
437399
438 for (block.statements) |statement, i| {400 for (block.statements) |statement, i| {
439 try stream.writeByteNTimes(' ', block_indent);401 try renderStatement(allocator, ais, tree, statement);
440 try renderStatement(allocator, stream, tree, block_indent, start_col, statement);
441402
442 if (i + 1 < block.statements.len) {403 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]);
444 }405 }
445 }406 }
446
447 try stream.writeByteNTimes(' ', indent);
448 return renderToken(tree, stream, block.rbrace, indent, start_col, space);
449 }407 }
408 return renderToken(tree, ais, block.rbrace, space);
450 },409 },
451410
452 .Defer => {411 .Defer => {
453 const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);412 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);
456 if (defer_node.payload) |payload| {415 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);
458 }417 }
459 return renderExpression(allocator, stream, tree, indent, start_col, defer_node.expr, space);418 return renderExpression(allocator, ais, tree, defer_node.expr, space);
460 },419 },
461 .Comptime => {420 .Comptime => {
462 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);421 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
463422
464 try renderToken(tree, stream, comptime_node.comptime_token, indent, start_col, Space.Space);423 try renderToken(tree, ais, comptime_node.comptime_token, Space.Space);
465 return renderExpression(allocator, stream, tree, indent, start_col, comptime_node.expr, space);424 return renderExpression(allocator, ais, tree, comptime_node.expr, space);
466 },425 },
467 .Nosuspend => {426 .Nosuspend => {
468 const nosuspend_node = @fieldParentPtr(ast.Node.Nosuspend, "base", base);427 const nosuspend_node = @fieldParentPtr(ast.Node.Nosuspend, "base", base);
469 if (mem.eql(u8, tree.tokenSlice(nosuspend_node.nosuspend_token), "noasync")) {428 if (mem.eql(u8, tree.tokenSlice(nosuspend_node.nosuspend_token), "noasync")) {
470 // TODO: remove this429 // TODO: remove this
471 try stream.writeAll("nosuspend ");430 try ais.writer().writeAll("nosuspend ");
472 } else {431 } 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);
474 }433 }
475 return renderExpression(allocator, stream, tree, indent, start_col, nosuspend_node.expr, space);434 return renderExpression(allocator, ais, tree, nosuspend_node.expr, space);
476 },435 },
477436
478 .Suspend => {437 .Suspend => {
479 const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);438 const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
480439
481 if (suspend_node.body) |body| {440 if (suspend_node.body) |body| {
482 try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space);441 try renderToken(tree, ais, suspend_node.suspend_token, Space.Space);
483 return renderExpression(allocator, stream, tree, indent, start_col, body, space);442 return renderExpression(allocator, ais, tree, body, space);
484 } else {443 } else {
485 return renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, space);444 return renderToken(tree, ais, suspend_node.suspend_token, space);
486 }445 }
487 },446 },
488447
...@@ -490,26 +449,21 @@ fn renderExpression(...@@ -490,26 +449,21 @@ fn renderExpression(
490 const infix_op_node = @fieldParentPtr(ast.Node.Catch, "base", base);449 const infix_op_node = @fieldParentPtr(ast.Node.Catch, "base", base);
491450
492 const op_space = Space.Space;451 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
495 const after_op_space = blk: {454 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));455 const same_line = tree.tokensOnSameLine(infix_op_node.op_token, tree.nextToken(infix_op_node.op_token));
497 break :blk if (loc.line == 0) op_space else Space.Newline;456 break :blk if (same_line) op_space else Space.Newline;
498 };457 };
499458
500 try renderToken(tree, stream, infix_op_node.op_token, indent, start_col, after_op_space);459 try renderToken(tree, ais, infix_op_node.op_token, 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 }
507460
508 if (infix_op_node.payload) |payload| {461 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);
510 }463 }
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);
513 },467 },
514468
515 .Add,469 .Add,
...@@ -561,22 +515,16 @@ fn renderExpression(...@@ -561,22 +515,16 @@ fn renderExpression(
561 .Period, .ErrorUnion, .Range => Space.None,515 .Period, .ErrorUnion, .Range => Space.None,
562 else => Space.Space,516 else => Space.Space,
563 };517 };
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
566 const after_op_space = blk: {520 const after_op_space = blk: {
567 const loc = tree.tokenLocation(tree.token_locs[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token));521 const loc = tree.tokenLocation(tree.token_locs[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token));
568 break :blk if (loc.line == 0) op_space else Space.Newline;522 break :blk if (loc.line == 0) op_space else Space.Newline;
569 };523 };
570524
571 try renderToken(tree, stream, infix_op_node.op_token, indent, start_col, after_op_space);525 try renderToken(tree, ais, infix_op_node.op_token, after_op_space);
572 if (after_op_space == Space.Newline and526 ais.pushIndentOneShot();
573 tree.token_ids[tree.nextToken(infix_op_node.op_token)] != .MultilineStringLiteralLine)527 return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);
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);
580 },528 },
581529
582 .BitNot,530 .BitNot,
...@@ -587,8 +535,8 @@ fn renderExpression(...@@ -587,8 +535,8 @@ fn renderExpression(
587 .AddressOf,535 .AddressOf,
588 => {536 => {
589 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);537 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
590 try renderToken(tree, stream, casted_node.op_token, indent, start_col, Space.None);538 try renderToken(tree, ais, casted_node.op_token, Space.None);
591 return renderExpression(allocator, stream, tree, indent, start_col, casted_node.rhs, space);539 return renderExpression(allocator, ais, tree, casted_node.rhs, space);
592 },540 },
593541
594 .Try,542 .Try,
...@@ -596,18 +544,16 @@ fn renderExpression(...@@ -596,18 +544,16 @@ fn renderExpression(
596 .Await,544 .Await,
597 => {545 => {
598 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);546 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
599 try renderToken(tree, stream, casted_node.op_token, indent, start_col, Space.Space);547 try renderToken(tree, ais, casted_node.op_token, Space.Space);
600 return renderExpression(allocator, stream, tree, indent, start_col, casted_node.rhs, space);548 return renderExpression(allocator, ais, tree, casted_node.rhs, space);
601 },549 },
602550
603 .ArrayType => {551 .ArrayType => {
604 const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base);552 const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base);
605 return renderArrayType(553 return renderArrayType(
606 allocator,554 allocator,
607 stream,555 ais,
608 tree,556 tree,
609 indent,
610 start_col,
611 array_type.op_token,557 array_type.op_token,
612 array_type.rhs,558 array_type.rhs,
613 array_type.len_expr,559 array_type.len_expr,
...@@ -619,10 +565,8 @@ fn renderExpression(...@@ -619,10 +565,8 @@ fn renderExpression(
619 const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base);565 const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base);
620 return renderArrayType(566 return renderArrayType(
621 allocator,567 allocator,
622 stream,568 ais,
623 tree,569 tree,
624 indent,
625 start_col,
626 array_type.op_token,570 array_type.op_token,
627 array_type.rhs,571 array_type.rhs,
628 array_type.len_expr,572 array_type.len_expr,
...@@ -635,111 +579,111 @@ fn renderExpression(...@@ -635,111 +579,111 @@ fn renderExpression(
635 const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base);579 const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base);
636 const op_tok_id = tree.token_ids[ptr_type.op_token];580 const op_tok_id = tree.token_ids[ptr_type.op_token];
637 switch (op_tok_id) {581 switch (op_tok_id) {
638 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),582 .Asterisk, .AsteriskAsterisk => try ais.writer().writeByte('*'),
639 .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier)583 .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier)
640 try stream.writeAll("[*c")584 try ais.writer().writeAll("[*c")
641 else585 else
642 try stream.writeAll("[*"),586 try ais.writer().writeAll("[*"),
643 else => unreachable,587 else => unreachable,
644 }588 }
645 if (ptr_type.ptr_info.sentinel) |sentinel| {589 if (ptr_type.ptr_info.sentinel) |sentinel| {
646 const colon_token = tree.prevToken(sentinel.firstToken());590 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); // :
648 const sentinel_space = switch (op_tok_id) {592 const sentinel_space = switch (op_tok_id) {
649 .LBracket => Space.None,593 .LBracket => Space.None,
650 else => Space.Space,594 else => Space.Space,
651 };595 };
652 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space);596 try renderExpression(allocator, ais, tree, sentinel, sentinel_space);
653 }597 }
654 switch (op_tok_id) {598 switch (op_tok_id) {
655 .Asterisk, .AsteriskAsterisk => {},599 .Asterisk, .AsteriskAsterisk => {},
656 .LBracket => try stream.writeByte(']'),600 .LBracket => try ais.writer().writeByte(']'),
657 else => unreachable,601 else => unreachable,
658 }602 }
659 if (ptr_type.ptr_info.allowzero_token) |allowzero_token| {603 if (ptr_type.ptr_info.allowzero_token) |allowzero_token| {
660 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero604 try renderToken(tree, ais, allowzero_token, Space.Space); // allowzero
661 }605 }
662 if (ptr_type.ptr_info.align_info) |align_info| {606 if (ptr_type.ptr_info.align_info) |align_info| {
663 const lparen_token = tree.prevToken(align_info.node.firstToken());607 const lparen_token = tree.prevToken(align_info.node.firstToken());
664 const align_token = tree.prevToken(lparen_token);608 const align_token = tree.prevToken(lparen_token);
665609
666 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align610 try renderToken(tree, ais, align_token, Space.None); // align
667 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (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
671 if (align_info.bit_range) |bit_range| {615 if (align_info.bit_range) |bit_range| {
672 const colon1 = tree.prevToken(bit_range.start.firstToken());616 const colon1 = tree.prevToken(bit_range.start.firstToken());
673 const colon2 = tree.prevToken(bit_range.end.firstToken());617 const colon2 = tree.prevToken(bit_range.end.firstToken());
674618
675 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :619 try renderToken(tree, ais, colon1, Space.None); // :
676 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);620 try renderExpression(allocator, ais, tree, bit_range.start, Space.None);
677 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :621 try renderToken(tree, ais, colon2, Space.None); // :
678 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);622 try renderExpression(allocator, ais, tree, bit_range.end, Space.None);
679623
680 const rparen_token = tree.nextToken(bit_range.end.lastToken());624 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); // )
682 } else {626 } else {
683 const rparen_token = tree.nextToken(align_info.node.lastToken());627 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); // )
685 }629 }
686 }630 }
687 if (ptr_type.ptr_info.const_token) |const_token| {631 if (ptr_type.ptr_info.const_token) |const_token| {
688 try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const632 try renderToken(tree, ais, const_token, Space.Space); // const
689 }633 }
690 if (ptr_type.ptr_info.volatile_token) |volatile_token| {634 if (ptr_type.ptr_info.volatile_token) |volatile_token| {
691 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile635 try renderToken(tree, ais, volatile_token, Space.Space); // volatile
692 }636 }
693 return renderExpression(allocator, stream, tree, indent, start_col, ptr_type.rhs, space);637 return renderExpression(allocator, ais, tree, ptr_type.rhs, space);
694 },638 },
695639
696 .SliceType => {640 .SliceType => {
697 const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base);641 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); // [
699 if (slice_type.ptr_info.sentinel) |sentinel| {643 if (slice_type.ptr_info.sentinel) |sentinel| {
700 const colon_token = tree.prevToken(sentinel.firstToken());644 const colon_token = tree.prevToken(sentinel.firstToken());
701 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :645 try renderToken(tree, ais, colon_token, Space.None); // :
702 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);646 try renderExpression(allocator, ais, tree, sentinel, Space.None);
703 try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ]647 try renderToken(tree, ais, tree.nextToken(sentinel.lastToken()), Space.None); // ]
704 } else {648 } 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); // ]
706 }650 }
707651
708 if (slice_type.ptr_info.allowzero_token) |allowzero_token| {652 if (slice_type.ptr_info.allowzero_token) |allowzero_token| {
709 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero653 try renderToken(tree, ais, allowzero_token, Space.Space); // allowzero
710 }654 }
711 if (slice_type.ptr_info.align_info) |align_info| {655 if (slice_type.ptr_info.align_info) |align_info| {
712 const lparen_token = tree.prevToken(align_info.node.firstToken());656 const lparen_token = tree.prevToken(align_info.node.firstToken());
713 const align_token = tree.prevToken(lparen_token);657 const align_token = tree.prevToken(lparen_token);
714658
715 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align659 try renderToken(tree, ais, align_token, Space.None); // align
716 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (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
720 if (align_info.bit_range) |bit_range| {664 if (align_info.bit_range) |bit_range| {
721 const colon1 = tree.prevToken(bit_range.start.firstToken());665 const colon1 = tree.prevToken(bit_range.start.firstToken());
722 const colon2 = tree.prevToken(bit_range.end.firstToken());666 const colon2 = tree.prevToken(bit_range.end.firstToken());
723667
724 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :668 try renderToken(tree, ais, colon1, Space.None); // :
725 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);669 try renderExpression(allocator, ais, tree, bit_range.start, Space.None);
726 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :670 try renderToken(tree, ais, colon2, Space.None); // :
727 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);671 try renderExpression(allocator, ais, tree, bit_range.end, Space.None);
728672
729 const rparen_token = tree.nextToken(bit_range.end.lastToken());673 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); // )
731 } else {675 } else {
732 const rparen_token = tree.nextToken(align_info.node.lastToken());676 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); // )
734 }678 }
735 }679 }
736 if (slice_type.ptr_info.const_token) |const_token| {680 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);
738 }682 }
739 if (slice_type.ptr_info.volatile_token) |volatile_token| {683 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);
741 }685 }
742 return renderExpression(allocator, stream, tree, indent, start_col, slice_type.rhs, space);686 return renderExpression(allocator, ais, tree, slice_type.rhs, space);
743 },687 },
744688
745 .ArrayInitializer, .ArrayInitializerDot => {689 .ArrayInitializer, .ArrayInitializerDot => {
...@@ -768,27 +712,33 @@ fn renderExpression(...@@ -768,27 +712,33 @@ fn renderExpression(
768712
769 if (exprs.len == 0) {713 if (exprs.len == 0) {
770 switch (lhs) {714 switch (lhs) {
771 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),715 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
772 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, 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);
773 }723 }
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) {
779 const expr = exprs[0];728 const expr = exprs[0];
729
780 switch (lhs) {730 switch (lhs) {
781 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),731 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
782 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),732 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
783 }733 }
784 try renderToken(tree, stream, lbrace, indent, start_col, Space.None);734 try renderToken(tree, ais, lbrace, Space.None);
785 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None);735 try renderExpression(allocator, ais, tree, expr, Space.None);
786 return renderToken(tree, stream, rtoken, indent, start_col, space);736 return renderToken(tree, ais, rtoken, space);
787 }737 }
788738
789 switch (lhs) {739 switch (lhs) {
790 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),740 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
791 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),741 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
792 }742 }
793743
794 // scan to find row size744 // scan to find row size
...@@ -830,79 +780,70 @@ fn renderExpression(...@@ -830,79 +780,70 @@ fn renderExpression(
830 var expr_widths = widths[0 .. widths.len - row_size];780 var expr_widths = widths[0 .. widths.len - row_size];
831 var column_widths = widths[widths.len - row_size ..];781 var column_widths = widths[widths.len - row_size ..];
832782
833 // Null stream for counting the printed length of each expression783 // Null ais for counting the printed length of each expression
834 var counting_stream = std.io.countingOutStream(std.io.null_out_stream);784 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
836 for (exprs) |expr, i| {787 for (exprs) |expr, i| {
837 counting_stream.bytes_written = 0;788 counting_stream.bytes_written = 0;
838 var dummy_col: usize = 0;789 try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
839 try renderExpression(allocator, counting_stream.outStream(), tree, indent, &dummy_col, expr, Space.None);
840 const width = @intCast(usize, counting_stream.bytes_written);790 const width = @intCast(usize, counting_stream.bytes_written);
841 const col = i % row_size;791 const col = i % row_size;
842 column_widths[col] = std.math.max(column_widths[col], width);792 column_widths[col] = std.math.max(column_widths[col], width);
843 expr_widths[i] = width;793 expr_widths[i] = width;
844 }794 }
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) {801 var col: usize = 1;
849 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline);802 for (exprs) |expr, i| {
850 try stream.writeByteNTimes(' ', new_indent);803 if (i + 1 < exprs.len) {
851 } else {804 const next_expr = exprs[i + 1];
852 new_indent -= indent_delta;805 try renderExpression(allocator, ais, tree, expr, Space.None);
853 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.None);
854 }
855806
856 var col: usize = 1;807 const comma = tree.nextToken(expr.*.lastToken());
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);
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) {812 const padding = column_widths[i % row_size] - expr_widths[i];
865 try renderToken(tree, stream, comma, new_indent, start_col, Space.Space); // ,813 try ais.writer().writeByteNTimes(' ', padding);
866814
867 const padding = column_widths[i % row_size] - expr_widths[i];815 col += 1;
868 try stream.writeByteNTimes(' ', padding);816 continue;
817 }
818 col = 1;
869819
870 col += 1;820 if (tree.token_ids[tree.nextToken(comma)] != .MultilineStringLiteralLine) {
871 continue;821 try renderToken(tree, ais, comma, Space.Newline); // ,
872 }822 } else {
873 col = 1;823 try renderToken(tree, ais, comma, Space.None); // ,
824 }
874825
875 if (tree.token_ids[tree.nextToken(comma)] != .MultilineStringLiteralLine) {826 try renderExtraNewline(tree, ais, next_expr);
876 try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline); // ,
877 } else {827 } else {
878 try renderToken(tree, stream, comma, new_indent, start_col, Space.None); // ,828 try renderExpression(allocator, ais, tree, expr, Space.Comma); // ,
879 }
880
881 try renderExtraNewline(tree, stream, start_col, next_expr);
882 if (next_expr.tag != .MultilineStringLiteral) {
883 try stream.writeByteNTimes(' ', new_indent);
884 }829 }
885 } else {
886 try renderExpression(allocator, stream, tree, new_indent, start_col, expr, Space.Comma); // ,
887 }830 }
888 }831 }
889 if (exprs[exprs.len - 1].tag != .MultilineStringLiteral) {832 return renderToken(tree, ais, rtoken, space);
890 try stream.writeByteNTimes(' ', indent);
891 }
892 return renderToken(tree, stream, rtoken, indent, start_col, space);
893 } else {833 } else {
894 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);834 try renderToken(tree, ais, lbrace, Space.Space);
895 for (exprs) |expr, i| {835 for (exprs) |expr, i| {
896 if (i + 1 < exprs.len) {836 if (i + 1 < exprs.len) {
897 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None);837 const next_expr = exprs[i + 1];
898 const comma = tree.nextToken(expr.lastToken());838 try renderExpression(allocator, ais, tree, expr, Space.None);
899 try renderToken(tree, stream, comma, indent, start_col, Space.Space); // ,839 const comma = tree.nextToken(expr.*.lastToken());
840 try renderToken(tree, ais, comma, Space.Space); // ,
900 } else {841 } else {
901 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.Space);842 try renderExpression(allocator, ais, tree, expr, Space.Space);
902 }843 }
903 }844 }
904845
905 return renderToken(tree, stream, rtoken, indent, start_col, space);846 return renderToken(tree, ais, rtoken, space);
906 }847 }
907 },848 },
908849
...@@ -932,11 +873,17 @@ fn renderExpression(...@@ -932,11 +873,17 @@ fn renderExpression(
932873
933 if (field_inits.len == 0) {874 if (field_inits.len == 0) {
934 switch (lhs) {875 switch (lhs) {
935 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),876 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
936 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),877 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
937 }878 }
938 try renderToken(tree, stream, lbrace, indent + indent_delta, start_col, Space.None);879
939 return renderToken(tree, stream, rtoken, indent, start_col, space);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);
940 }887 }
941888
942 const src_has_trailing_comma = blk: {889 const src_has_trailing_comma = blk: {
...@@ -952,9 +899,10 @@ fn renderExpression(...@@ -952,9 +899,10 @@ fn renderExpression(
952 const expr_outputs_one_line = blk: {899 const expr_outputs_one_line = blk: {
953 // render field expressions until a LF is found900 // render field expressions until a LF is found
954 for (field_inits) |field_init| {901 for (field_inits) |field_init| {
955 var find_stream = FindByteOutStream.init('\n');902 var find_stream = std.io.findByteOutStream('\n', std.io.null_out_stream);
956 var dummy_col: usize = 0;903 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, find_stream.writer());
957 try renderExpression(allocator, find_stream.outStream(), tree, 0, &dummy_col, field_init, Space.None);904
905 try renderExpression(allocator, &auto_indenting_stream, tree, field_init, Space.None);
958 if (find_stream.byte_found) break :blk false;906 if (find_stream.byte_found) break :blk false;
959 }907 }
960 break :blk true;908 break :blk true;
...@@ -967,7 +915,6 @@ fn renderExpression(...@@ -967,7 +915,6 @@ fn renderExpression(
967 .StructInitializer,915 .StructInitializer,
968 .StructInitializerDot,916 .StructInitializerDot,
969 => break :blk,917 => break :blk,
970
971 else => {},918 else => {},
972 }919 }
973920
...@@ -977,76 +924,78 @@ fn renderExpression(...@@ -977,76 +924,78 @@ fn renderExpression(
977 }924 }
978925
979 switch (lhs) {926 switch (lhs) {
980 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),927 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
981 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),928 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
982 }929 }
983 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);930 try renderToken(tree, ais, lbrace, Space.Space);
984 try renderExpression(allocator, stream, tree, indent, start_col, &field_init.base, Space.Space);931 try renderExpression(allocator, ais, tree, &field_init.base, Space.Space);
985 return renderToken(tree, stream, rtoken, indent, start_col, space);932 return renderToken(tree, ais, rtoken, space);
986 }933 }
987934
988 if (!src_has_trailing_comma and src_same_line and expr_outputs_one_line) {935 if (!src_has_trailing_comma and src_same_line and expr_outputs_one_line) {
989 // render all on one line, no trailing comma936 // render all on one line, no trailing comma
990 switch (lhs) {937 switch (lhs) {
991 .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),938 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
992 .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),939 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
993 }940 }
994 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);941 try renderToken(tree, ais, lbrace, Space.Space);
995942
996 for (field_inits) |field_init, i| {943 for (field_inits) |field_init, i| {
997 if (i + 1 < field_inits.len) {944 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
1000 const comma = tree.nextToken(field_init.lastToken());947 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);
1002 } else {949 } else {
1003 try renderExpression(allocator, stream, tree, indent, start_col, field_init, Space.Space);950 try renderExpression(allocator, ais, tree, field_init, Space.Space);
1004 }951 }
1005 }952 }
1006953
1007 return renderToken(tree, stream, rtoken, indent, start_col, space);954 return renderToken(tree, ais, rtoken, space);
1008 }955 }
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) {963 ais.pushIndentNextLine();
1013 .dot => |dot| try renderToken(tree, stream, dot, new_indent, start_col, Space.None),964 defer ais.popIndent();
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);
1017965
1018 for (field_inits) |field_init, i| {966 try renderToken(tree, ais, lbrace, Space.Newline);
1019 try stream.writeByteNTimes(' ', new_indent);
1020967
1021 if (i + 1 < field_inits.len) {968 for (field_inits) |field_init, i| {
1022 try renderExpression(allocator, stream, tree, new_indent, start_col, field_init, Space.None);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());973 const comma = tree.nextToken(field_init.lastToken());
1025 try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline);974 try renderToken(tree, ais, comma, Space.Newline);
1026975
1027 try renderExtraNewline(tree, stream, start_col, field_inits[i + 1]);976 try renderExtraNewline(tree, ais, next_field_init);
1028 } else {977 } else {
1029 try renderExpression(allocator, stream, tree, new_indent, start_col, field_init, Space.Comma);978 try renderExpression(allocator, ais, tree, field_init, Space.Comma);
979 }
1030 }980 }
1031 }981 }
1032982
1033 try stream.writeByteNTimes(' ', indent);983 return renderToken(tree, ais, rtoken, space);
1034 return renderToken(tree, stream, rtoken, indent, start_col, space);
1035 },984 },
1036985
1037 .Call => {986 .Call => {
1038 const call = @fieldParentPtr(ast.Node.Call, "base", base);987 const call = @fieldParentPtr(ast.Node.Call, "base", base);
1039 if (call.async_token) |async_token| {988 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);
1041 }990 }
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
1045 const lparen = tree.nextToken(call.lhs.lastToken());994 const lparen = tree.nextToken(call.lhs.lastToken());
1046995
1047 if (call.params_len == 0) {996 if (call.params_len == 0) {
1048 try renderToken(tree, stream, lparen, indent, start_col, Space.None);997 try renderToken(tree, ais, lparen, Space.None);
1049 return renderToken(tree, stream, call.rtoken, indent, start_col, space);998 return renderToken(tree, ais, call.rtoken, space);
1050 }999 }
10511000
1052 const src_has_trailing_comma = blk: {1001 const src_has_trailing_comma = blk: {
...@@ -1055,43 +1004,41 @@ fn renderExpression(...@@ -1055,43 +1004,41 @@ fn renderExpression(
1055 };1004 };
10561005
1057 if (src_has_trailing_comma) {1006 if (src_has_trailing_comma) {
1058 const new_indent = indent + indent_delta;1007 try renderToken(tree, ais, lparen, Space.Newline);
1059 try renderToken(tree, stream, lparen, new_indent, start_col, Space.Newline);
10601008
1061 const params = call.params();1009 const params = call.params();
1062 for (params) |param_node, i| {1010 for (params) |param_node, i| {
1063 const param_node_new_indent = if (param_node.tag == .MultilineStringLiteral) blk: {1011 ais.pushIndent();
1064 break :blk indent;1012 defer ais.popIndent();
1065 } else blk: {
1066 try stream.writeByteNTimes(' ', new_indent);
1067 break :blk new_indent;
1068 };
10691013
1070 if (i + 1 < params.len) {1014 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);
1072 const comma = tree.nextToken(param_node.lastToken());1017 const comma = tree.nextToken(param_node.lastToken());
1073 try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline); // ,1018 try renderToken(tree, ais, comma, Space.Newline); // ,
1074 try renderExtraNewline(tree, stream, start_col, params[i + 1]);1019 try renderExtraNewline(tree, ais, next_node);
1075 } else {1020 } else {
1076 try renderExpression(allocator, stream, tree, param_node_new_indent, start_col, param_node, Space.Comma);1021 try renderExpression(allocator, ais, tree, param_node, Space.Comma);
1077 try stream.writeByteNTimes(' ', indent);
1078 return renderToken(tree, stream, call.rtoken, indent, start_col, space);
1079 }1022 }
1080 }1023 }
1024 return renderToken(tree, ais, call.rtoken, space);
1081 }1025 }
10821026
1083 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (1027 try renderToken(tree, ais, lparen, Space.None); // (
10841028
1085 const params = call.params();1029 const params = call.params();
1086 for (params) |param_node, i| {1030 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
1089 if (i + 1 < params.len) {1035 if (i + 1 < params.len) {
1036 const next_param = params[i + 1];
1090 const comma = tree.nextToken(param_node.lastToken());1037 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);
1092 }1039 }
1093 }1040 }
1094 return renderToken(tree, stream, call.rtoken, indent, start_col, space);1041 return renderToken(tree, ais, call.rtoken, space);
1095 },1042 },
10961043
1097 .ArrayAccess => {1044 .ArrayAccess => {
...@@ -1100,26 +1047,25 @@ fn renderExpression(...@@ -1100,26 +1047,25 @@ fn renderExpression(
1100 const lbracket = tree.nextToken(suffix_op.lhs.lastToken());1047 const lbracket = tree.nextToken(suffix_op.lhs.lastToken());
1101 const rbracket = tree.nextToken(suffix_op.index_expr.lastToken());1048 const rbracket = tree.nextToken(suffix_op.index_expr.lastToken());
11021049
1103 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);1050 try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
1104 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [1051 try renderToken(tree, ais, lbracket, Space.None); // [
11051052
1106 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;1053 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
1107 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;1054 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
1108 const new_indent = if (ends_with_comment) indent + indent_delta else indent;1055 {
1109 const new_space = if (ends_with_comment) Space.Newline else Space.None;1056 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);1057
1111 if (starts_with_comment) {1058 ais.pushIndent();
1112 try stream.writeByte('\n');1059 defer ais.popIndent();
1113 }1060 try renderExpression(allocator, ais, tree, suffix_op.index_expr, new_space);
1114 if (ends_with_comment or starts_with_comment) {
1115 try stream.writeByteNTimes(' ', indent);
1116 }1061 }
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); // ]
1118 },1064 },
1065
1119 .Slice => {1066 .Slice => {
1120 const suffix_op = base.castTag(.Slice).?;1067 const suffix_op = base.castTag(.Slice).?;
11211068 try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
1122 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
11231069
1124 const lbracket = tree.prevToken(suffix_op.start.firstToken());1070 const lbracket = tree.prevToken(suffix_op.start.firstToken());
1125 const dotdot = tree.nextToken(suffix_op.start.lastToken());1071 const dotdot = tree.nextToken(suffix_op.start.lastToken());
...@@ -1129,32 +1075,33 @@ fn renderExpression(...@@ -1129,32 +1075,33 @@ fn renderExpression(
1129 const after_start_space = if (after_start_space_bool) Space.Space else Space.None;1075 const after_start_space = if (after_start_space_bool) Space.Space else Space.None;
1130 const after_op_space = if (suffix_op.end != null) after_start_space else Space.None;1076 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); // [1078 try renderToken(tree, ais, lbracket, Space.None); // [
1133 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.start, after_start_space);1079 try renderExpression(allocator, ais, tree, suffix_op.start, after_start_space);
1134 try renderToken(tree, stream, dotdot, indent, start_col, after_op_space); // ..1080 try renderToken(tree, ais, dotdot, after_op_space); // ..
1135 if (suffix_op.end) |end| {1081 if (suffix_op.end) |end| {
1136 const after_end_space = if (suffix_op.sentinel != null) Space.Space else Space.None;1082 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);
1138 }1084 }
1139 if (suffix_op.sentinel) |sentinel| {1085 if (suffix_op.sentinel) |sentinel| {
1140 const colon = tree.prevToken(sentinel.firstToken());1086 const colon = tree.prevToken(sentinel.firstToken());
1141 try renderToken(tree, stream, colon, indent, start_col, Space.None); // :1087 try renderToken(tree, ais, colon, Space.None); // :
1142 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);1088 try renderExpression(allocator, ais, tree, sentinel, Space.None);
1143 }1089 }
1144 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ]1090 return renderToken(tree, ais, suffix_op.rtoken, space); // ]
1145 },1091 },
1092
1146 .Deref => {1093 .Deref => {
1147 const suffix_op = base.castTag(.Deref).?;1094 const suffix_op = base.castTag(.Deref).?;
11481095
1149 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);1096 try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
1150 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // .*1097 return renderToken(tree, ais, suffix_op.rtoken, space); // .*
1151 },1098 },
1152 .UnwrapOptional => {1099 .UnwrapOptional => {
1153 const suffix_op = base.castTag(.UnwrapOptional).?;1100 const suffix_op = base.castTag(.UnwrapOptional).?;
11541101
1155 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);1102 try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
1156 try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // .1103 try renderToken(tree, ais, tree.prevToken(suffix_op.rtoken), Space.None); // .
1157 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ?1104 return renderToken(tree, ais, suffix_op.rtoken, space); // ?
1158 },1105 },
11591106
1160 .Break => {1107 .Break => {
...@@ -1163,145 +1110,152 @@ fn renderExpression(...@@ -1163,145 +1110,152 @@ fn renderExpression(
1163 const maybe_label = flow_expr.getLabel();1110 const maybe_label = flow_expr.getLabel();
11641111
1165 if (maybe_label == null and maybe_rhs == null) {1112 if (maybe_label == null and maybe_rhs == null) {
1166 return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // break1113 return renderToken(tree, ais, flow_expr.ltoken, space); // break
1167 }1114 }
11681115
1169 try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // break1116 try renderToken(tree, ais, flow_expr.ltoken, Space.Space); // break
1170 if (maybe_label) |label| {1117 if (maybe_label) |label| {
1171 const colon = tree.nextToken(flow_expr.ltoken);1118 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
1174 if (maybe_rhs == null) {1121 if (maybe_rhs == null) {
1175 return renderToken(tree, stream, label, indent, start_col, space); // label1122 return renderToken(tree, ais, label, space); // label
1176 }1123 }
1177 try renderToken(tree, stream, label, indent, start_col, Space.Space); // label1124 try renderToken(tree, ais, label, Space.Space); // label
1178 }1125 }
1179 return renderExpression(allocator, stream, tree, indent, start_col, maybe_rhs.?, space);1126 return renderExpression(allocator, ais, tree, maybe_rhs.?, space);
1180 },1127 },
11811128
1182 .Continue => {1129 .Continue => {
1183 const flow_expr = base.castTag(.Continue).?;1130 const flow_expr = base.castTag(.Continue).?;
1184 if (flow_expr.getLabel()) |label| {1131 if (flow_expr.getLabel()) |label| {
1185 try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // continue1132 try renderToken(tree, ais, flow_expr.ltoken, Space.Space); // continue
1186 const colon = tree.nextToken(flow_expr.ltoken);1133 const colon = tree.nextToken(flow_expr.ltoken);
1187 try renderToken(tree, stream, colon, indent, start_col, Space.None); // :1134 try renderToken(tree, ais, colon, Space.None); // :
1188 return renderToken(tree, stream, label, indent, start_col, space); // label1135 return renderToken(tree, ais, label, space); // label
1189 } else {1136 } else {
1190 return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // continue1137 return renderToken(tree, ais, flow_expr.ltoken, space); // continue
1191 }1138 }
1192 },1139 },
11931140
1194 .Return => {1141 .Return => {
1195 const flow_expr = base.castTag(.Return).?;1142 const flow_expr = base.castTag(.Return).?;
1196 if (flow_expr.getRHS()) |rhs| {1143 if (flow_expr.getRHS()) |rhs| {
1197 try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space);1144 try renderToken(tree, ais, flow_expr.ltoken, Space.Space);
1198 return renderExpression(allocator, stream, tree, indent, start_col, rhs, space);1145 return renderExpression(allocator, ais, tree, rhs, space);
1199 } else {1146 } else {
1200 return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space);1147 return renderToken(tree, ais, flow_expr.ltoken, space);
1201 }1148 }
1202 },1149 },
12031150
1204 .Payload => {1151 .Payload => {
1205 const payload = @fieldParentPtr(ast.Node.Payload, "base", base);1152 const payload = @fieldParentPtr(ast.Node.Payload, "base", base);
12061153
1207 try renderToken(tree, stream, payload.lpipe, indent, start_col, Space.None);1154 try renderToken(tree, ais, payload.lpipe, Space.None);
1208 try renderExpression(allocator, stream, tree, indent, start_col, payload.error_symbol, Space.None);1155 try renderExpression(allocator, ais, tree, payload.error_symbol, Space.None);
1209 return renderToken(tree, stream, payload.rpipe, indent, start_col, space);1156 return renderToken(tree, ais, payload.rpipe, space);
1210 },1157 },
12111158
1212 .PointerPayload => {1159 .PointerPayload => {
1213 const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base);1160 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);
1216 if (payload.ptr_token) |ptr_token| {1163 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);
1218 }1165 }
1219 try renderExpression(allocator, stream, tree, indent, start_col, payload.value_symbol, Space.None);1166 try renderExpression(allocator, ais, tree, payload.value_symbol, Space.None);
1220 return renderToken(tree, stream, payload.rpipe, indent, start_col, space);1167 return renderToken(tree, ais, payload.rpipe, space);
1221 },1168 },
12221169
1223 .PointerIndexPayload => {1170 .PointerIndexPayload => {
1224 const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base);1171 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);
1227 if (payload.ptr_token) |ptr_token| {1174 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);
1229 }1176 }
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
1232 if (payload.index_symbol) |index_symbol| {1179 if (payload.index_symbol) |index_symbol| {
1233 const comma = tree.nextToken(payload.value_symbol.lastToken());1180 const comma = tree.nextToken(payload.value_symbol.lastToken());
12341181
1235 try renderToken(tree, stream, comma, indent, start_col, Space.Space);1182 try renderToken(tree, ais, comma, Space.Space);
1236 try renderExpression(allocator, stream, tree, indent, start_col, index_symbol, Space.None);1183 try renderExpression(allocator, ais, tree, index_symbol, Space.None);
1237 }1184 }
12381185
1239 return renderToken(tree, stream, payload.rpipe, indent, start_col, space);1186 return renderToken(tree, ais, payload.rpipe, space);
1240 },1187 },
12411188
1242 .GroupedExpression => {1189 .GroupedExpression => {
1243 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);1190 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);
12441191
1245 try renderToken(tree, stream, grouped_expr.lparen, indent, start_col, Space.None);1192 try renderToken(tree, ais, grouped_expr.lparen, Space.None);
1246 try renderExpression(allocator, stream, tree, indent, start_col, grouped_expr.expr, Space.None);1193 {
1247 return renderToken(tree, stream, grouped_expr.rparen, indent, start_col, space);1194 ais.pushIndentOneShot();
1195 try renderExpression(allocator, ais, tree, grouped_expr.expr, Space.None);
1196 }
1197 return renderToken(tree, ais, grouped_expr.rparen, space);
1248 },1198 },
12491199
1250 .FieldInitializer => {1200 .FieldInitializer => {
1251 const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);1201 const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);
12521202
1253 try renderToken(tree, stream, field_init.period_token, indent, start_col, Space.None); // .1203 try renderToken(tree, ais, field_init.period_token, Space.None); // .
1254 try renderToken(tree, stream, field_init.name_token, indent, start_col, Space.Space); // name1204 try renderToken(tree, ais, field_init.name_token, Space.Space); // name
1255 try renderToken(tree, stream, tree.nextToken(field_init.name_token), indent, start_col, Space.Space); // =1205 try renderToken(tree, ais, tree.nextToken(field_init.name_token), Space.Space); // =
1256 return renderExpression(allocator, stream, tree, indent, start_col, field_init.expr, space);1206 return renderExpression(allocator, ais, tree, field_init.expr, space);
1257 },1207 },
12581208
1259 .ContainerDecl => {1209 .ContainerDecl => {
1260 const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);1210 const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
12611211
1262 if (container_decl.layout_token) |layout_token| {1212 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);
1264 }1214 }
12651215
1266 switch (container_decl.init_arg_expr) {1216 switch (container_decl.init_arg_expr) {
1267 .None => {1217 .None => {
1268 try renderToken(tree, stream, container_decl.kind_token, indent, start_col, Space.Space); // union1218 try renderToken(tree, ais, container_decl.kind_token, Space.Space); // union
1269 },1219 },
1270 .Enum => |enum_tag_type| {1220 .Enum => |enum_tag_type| {
1271 try renderToken(tree, stream, container_decl.kind_token, indent, start_col, Space.None); // union1221 try renderToken(tree, ais, container_decl.kind_token, Space.None); // union
12721222
1273 const lparen = tree.nextToken(container_decl.kind_token);1223 const lparen = tree.nextToken(container_decl.kind_token);
1274 const enum_token = tree.nextToken(lparen);1224 const enum_token = tree.nextToken(lparen);
12751225
1276 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (1226 try renderToken(tree, ais, lparen, Space.None); // (
1277 try renderToken(tree, stream, enum_token, indent, start_col, Space.None); // enum1227 try renderToken(tree, ais, enum_token, Space.None); // enum
12781228
1279 if (enum_tag_type) |expr| {1229 if (enum_tag_type) |expr| {
1280 try renderToken(tree, stream, tree.nextToken(enum_token), indent, start_col, Space.None); // (1230 try renderToken(tree, ais, tree.nextToken(enum_token), Space.None); // (
1281 try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None);1231 try renderExpression(allocator, ais, tree, expr, Space.None);
12821232
1283 const rparen = tree.nextToken(expr.lastToken());1233 const rparen = tree.nextToken(expr.lastToken());
1284 try renderToken(tree, stream, rparen, indent, start_col, Space.None); // )1234 try renderToken(tree, ais, rparen, Space.None); // )
1285 try renderToken(tree, stream, tree.nextToken(rparen), indent, start_col, Space.Space); // )1235 try renderToken(tree, ais, tree.nextToken(rparen), Space.Space); // )
1286 } else {1236 } 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); // )
1288 }1238 }
1289 },1239 },
1290 .Type => |type_expr| {1240 .Type => |type_expr| {
1291 try renderToken(tree, stream, container_decl.kind_token, indent, start_col, Space.None); // union1241 try renderToken(tree, ais, container_decl.kind_token, Space.None); // union
12921242
1293 const lparen = tree.nextToken(container_decl.kind_token);1243 const lparen = tree.nextToken(container_decl.kind_token);
1294 const rparen = tree.nextToken(type_expr.lastToken());1244 const rparen = tree.nextToken(type_expr.lastToken());
12951245
1296 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (1246 try renderToken(tree, ais, lparen, Space.None); // (
1297 try renderExpression(allocator, stream, tree, indent, start_col, type_expr, Space.None);1247 try renderExpression(allocator, ais, tree, type_expr, Space.None);
1298 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )1248 try renderToken(tree, ais, rparen, Space.Space); // )
1299 },1249 },
1300 }1250 }
13011251
1302 if (container_decl.fields_and_decls_len == 0) {1252 if (container_decl.fields_and_decls_len == 0) {
1303 try renderToken(tree, stream, container_decl.lbrace_token, indent + indent_delta, start_col, Space.None); // {1253 {
1304 return renderToken(tree, stream, container_decl.rbrace_token, indent, start_col, space); // }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); // }
1305 }1259 }
13061260
1307 const src_has_trailing_comma = blk: {1261 const src_has_trailing_comma = blk: {
...@@ -1332,43 +1286,39 @@ fn renderExpression(...@@ -1332,43 +1286,39 @@ fn renderExpression(
13321286
1333 if (src_has_trailing_comma or !src_has_only_fields) {1287 if (src_has_trailing_comma or !src_has_only_fields) {
1334 // One declaration per line1288 // One declaration per line
1335 const new_indent = indent + indent_delta;1289 ais.pushIndentNextLine();
1336 try renderToken(tree, stream, container_decl.lbrace_token, new_indent, start_col, .Newline); // {1290 defer ais.popIndent();
1291 try renderToken(tree, ais, container_decl.lbrace_token, .Newline); // {
13371292
1338 for (fields_and_decls) |decl, i| {1293 for (fields_and_decls) |decl, i| {
1339 try stream.writeByteNTimes(' ', new_indent);1294 try renderContainerDecl(allocator, ais, tree, decl, .Newline);
1340 try renderContainerDecl(allocator, stream, tree, new_indent, start_col, decl, .Newline);
13411295
1342 if (i + 1 < fields_and_decls.len) {1296 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]);
1344 }1298 }
1345 }1299 }
1346
1347 try stream.writeByteNTimes(' ', indent);
1348 } else if (src_has_newline) {1300 } else if (src_has_newline) {
1349 // All the declarations on the same line, but place the items on1301 // All the declarations on the same line, but place the items on
1350 // their own line1302 // 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;1305 ais.pushIndent();
1354 try stream.writeByteNTimes(' ', new_indent);1306 defer ais.popIndent();
13551307
1356 for (fields_and_decls) |decl, i| {1308 for (fields_and_decls) |decl, i| {
1357 const space_after_decl: Space = if (i + 1 >= fields_and_decls.len) .Newline else .Space;1309 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);
1359 }1311 }
1360
1361 try stream.writeByteNTimes(' ', indent);
1362 } else {1312 } else {
1363 // All the declarations on the same line1313 // 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
1366 for (fields_and_decls) |decl| {1316 for (fields_and_decls) |decl| {
1367 try renderContainerDecl(allocator, stream, tree, indent, start_col, decl, .Space);1317 try renderContainerDecl(allocator, ais, tree, decl, .Space);
1368 }1318 }
1369 }1319 }
13701320
1371 return renderToken(tree, stream, container_decl.rbrace_token, indent, start_col, space); // }1321 return renderToken(tree, ais, container_decl.rbrace_token, space); // }
1372 },1322 },
13731323
1374 .ErrorSetDecl => {1324 .ErrorSetDecl => {
...@@ -1377,9 +1327,9 @@ fn renderExpression(...@@ -1377,9 +1327,9 @@ fn renderExpression(
1377 const lbrace = tree.nextToken(err_set_decl.error_token);1327 const lbrace = tree.nextToken(err_set_decl.error_token);
13781328
1379 if (err_set_decl.decls_len == 0) {1329 if (err_set_decl.decls_len == 0) {
1380 try renderToken(tree, stream, err_set_decl.error_token, indent, start_col, Space.None);1330 try renderToken(tree, ais, err_set_decl.error_token, Space.None);
1381 try renderToken(tree, stream, lbrace, indent, start_col, Space.None);1331 try renderToken(tree, ais, lbrace, Space.None);
1382 return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space);1332 return renderToken(tree, ais, err_set_decl.rbrace_token, space);
1383 }1333 }
13841334
1385 if (err_set_decl.decls_len == 1) blk: {1335 if (err_set_decl.decls_len == 1) blk: {
...@@ -1393,13 +1343,13 @@ fn renderExpression(...@@ -1393,13 +1343,13 @@ fn renderExpression(
1393 break :blk;1343 break :blk;
1394 }1344 }
13951345
1396 try renderToken(tree, stream, err_set_decl.error_token, indent, start_col, Space.None); // error1346 try renderToken(tree, ais, err_set_decl.error_token, Space.None); // error
1397 try renderToken(tree, stream, lbrace, indent, start_col, Space.None); // {1347 try renderToken(tree, ais, lbrace, Space.None); // {
1398 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None);1348 try renderExpression(allocator, ais, tree, node, Space.None);
1399 return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // }1349 return renderToken(tree, ais, err_set_decl.rbrace_token, space); // }
1400 }1350 }
14011351
1402 try renderToken(tree, stream, err_set_decl.error_token, indent, start_col, Space.None); // error1352 try renderToken(tree, ais, err_set_decl.error_token, Space.None); // error
14031353
1404 const src_has_trailing_comma = blk: {1354 const src_has_trailing_comma = blk: {
1405 const maybe_comma = tree.prevToken(err_set_decl.rbrace_token);1355 const maybe_comma = tree.prevToken(err_set_decl.rbrace_token);
...@@ -1407,72 +1357,66 @@ fn renderExpression(...@@ -1407,72 +1357,66 @@ fn renderExpression(
1407 };1357 };
14081358
1409 if (src_has_trailing_comma) {1359 if (src_has_trailing_comma) {
1410 try renderToken(tree, stream, lbrace, indent, start_col, Space.Newline); // {1360 {
1411 const new_indent = indent + indent_delta;1361 ais.pushIndent();
14121362 defer ais.popIndent();
1413 const decls = err_set_decl.decls();1363
1414 for (decls) |node, i| {1364 try renderToken(tree, ais, lbrace, Space.Newline); // {
1415 try stream.writeByteNTimes(' ', new_indent);1365 const decls = err_set_decl.decls();
14161366 for (decls) |node, i| {
1417 if (i + 1 < decls.len) {1367 if (i + 1 < decls.len) {
1418 try renderExpression(allocator, stream, tree, new_indent, start_col, node, Space.None);1368 try renderExpression(allocator, ais, tree, node, Space.None);
1419 try renderToken(tree, stream, tree.nextToken(node.lastToken()), new_indent, start_col, Space.Newline); // ,1369 try renderToken(tree, ais, tree.nextToken(node.lastToken()), Space.Newline); // ,
14201370
1421 try renderExtraNewline(tree, stream, start_col, decls[i + 1]);1371 try renderExtraNewline(tree, ais, decls[i + 1]);
1422 } else {1372 } else {
1423 try renderExpression(allocator, stream, tree, new_indent, start_col, node, Space.Comma);1373 try renderExpression(allocator, ais, tree, node, Space.Comma);
1374 }
1424 }1375 }
1425 }1376 }
14261377
1427 try stream.writeByteNTimes(' ', indent);1378 return renderToken(tree, ais, err_set_decl.rbrace_token, space); // }
1428 return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // }
1429 } else {1379 } else {
1430 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space); // {1380 try renderToken(tree, ais, lbrace, Space.Space); // {
14311381
1432 const decls = err_set_decl.decls();1382 const decls = err_set_decl.decls();
1433 for (decls) |node, i| {1383 for (decls) |node, i| {
1434 if (i + 1 < decls.len) {1384 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
1437 const comma_token = tree.nextToken(node.lastToken());1387 const comma_token = tree.nextToken(node.lastToken());
1438 assert(tree.token_ids[comma_token] == .Comma);1388 assert(tree.token_ids[comma_token] == .Comma);
1439 try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // ,1389 try renderToken(tree, ais, comma_token, Space.Space); // ,
1440 try renderExtraNewline(tree, stream, start_col, decls[i + 1]);1390 try renderExtraNewline(tree, ais, decls[i + 1]);
1441 } else {1391 } else {
1442 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.Space);1392 try renderExpression(allocator, ais, tree, node, Space.Space);
1443 }1393 }
1444 }1394 }
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); // }
1447 }1397 }
1448 },1398 },
14491399
1450 .ErrorTag => {1400 .ErrorTag => {
1451 const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", base);1401 const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", base);
14521402
1453 try renderDocComments(tree, stream, tag, tag.doc_comments, indent, start_col);1403 try renderDocComments(tree, ais, tag, tag.doc_comments);
1454 return renderToken(tree, stream, tag.name_token, indent, start_col, space); // name1404 return renderToken(tree, ais, tag.name_token, space); // name
1455 },1405 },
14561406
1457 .MultilineStringLiteral => {1407 .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
1460 const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);1408 const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
14611409
1462 var skip_first_indent = true;1410 {
1463 if (tree.token_ids[multiline_str_literal.firstToken() - 1] != .LineComment) {1411 const locked_indents = ais.lockOneShotIndent();
1464 try stream.print("\n", .{});1412 defer {
1465 skip_first_indent = false;1413 var i: u8 = 0;
1466 }1414 while (i < locked_indents) : (i += 1) ais.popIndent();
1467
1468 for (multiline_str_literal.lines()) |t| {
1469 if (!skip_first_indent) {
1470 try stream.writeByteNTimes(' ', indent + indent_delta);
1471 }1415 }
1472 try renderToken(tree, stream, t, indent, start_col, Space.None);1416 try ais.maybeInsertNewline();
1473 skip_first_indent = false;1417
1418 for (multiline_str_literal.lines()) |t| try renderToken(tree, ais, t, Space.None);
1474 }1419 }
1475 try stream.writeByteNTimes(' ', indent);
1476 },1420 },
14771421
1478 .BuiltinCall => {1422 .BuiltinCall => {
...@@ -1480,9 +1424,9 @@ fn renderExpression(...@@ -1480,9 +1424,9 @@ fn renderExpression(
14801424
1481 // TODO remove after 0.7.0 release1425 // TODO remove after 0.7.0 release
1482 if (mem.eql(u8, tree.tokenSlice(builtin_call.builtin_token), "@OpaqueType"))1426 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); // @name1429 try renderToken(tree, ais, builtin_call.builtin_token, Space.None); // @name
14861430
1487 const src_params_trailing_comma = blk: {1431 const src_params_trailing_comma = blk: {
1488 if (builtin_call.params_len < 2) break :blk false;1432 if (builtin_call.params_len < 2) break :blk false;
...@@ -1494,31 +1438,30 @@ fn renderExpression(...@@ -1494,31 +1438,30 @@ fn renderExpression(
1494 const lparen = tree.nextToken(builtin_call.builtin_token);1438 const lparen = tree.nextToken(builtin_call.builtin_token);
14951439
1496 if (!src_params_trailing_comma) {1440 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
1499 // render all on one line, no trailing comma1443 // render all on one line, no trailing comma
1500 const params = builtin_call.params();1444 const params = builtin_call.params();
1501 for (params) |param_node, i| {1445 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
1504 if (i + 1 < params.len) {1448 if (i + 1 < params.len) {
1505 const comma_token = tree.nextToken(param_node.lastToken());1449 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); // ,
1507 }1451 }
1508 }1452 }
1509 } else {1453 } else {
1510 // one param per line1454 // one param per line
1511 const new_indent = indent + indent_delta;1455 ais.pushIndent();
1512 try renderToken(tree, stream, lparen, new_indent, start_col, Space.Newline); // (1456 defer ais.popIndent();
1457 try renderToken(tree, ais, lparen, Space.Newline); // (
15131458
1514 for (builtin_call.params()) |param_node| {1459 for (builtin_call.params()) |param_node| {
1515 try stream.writeByteNTimes(' ', new_indent);1460 try renderExpression(allocator, ais, tree, param_node, Space.Comma);
1516 try renderExpression(allocator, stream, tree, indent, start_col, param_node, Space.Comma);
1517 }1461 }
1518 try stream.writeByteNTimes(' ', indent);
1519 }1462 }
15201463
1521 return renderToken(tree, stream, builtin_call.rparen_token, indent, start_col, space); // )1464 return renderToken(tree, ais, builtin_call.rparen_token, space); // )
1522 },1465 },
15231466
1524 .FnProto => {1467 .FnProto => {
...@@ -1528,24 +1471,24 @@ fn renderExpression(...@@ -1528,24 +1471,24 @@ fn renderExpression(
1528 const visib_token = tree.token_ids[visib_token_index];1471 const visib_token = tree.token_ids[visib_token_index];
1529 assert(visib_token == .Keyword_pub or visib_token == .Keyword_export);1472 assert(visib_token == .Keyword_pub or visib_token == .Keyword_export);
15301473
1531 try renderToken(tree, stream, visib_token_index, indent, start_col, Space.Space); // pub1474 try renderToken(tree, ais, visib_token_index, Space.Space); // pub
1532 }1475 }
15331476
1534 if (fn_proto.getExternExportInlineToken()) |extern_export_inline_token| {1477 if (fn_proto.getExternExportInlineToken()) |extern_export_inline_token| {
1535 if (fn_proto.getIsExternPrototype() == null)1478 if (fn_proto.getIsExternPrototype() == null)
1536 try renderToken(tree, stream, extern_export_inline_token, indent, start_col, Space.Space); // extern/export/inline1479 try renderToken(tree, ais, extern_export_inline_token, Space.Space); // extern/export/inline
1537 }1480 }
15381481
1539 if (fn_proto.getLibName()) |lib_name| {1482 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);
1541 }1484 }
15421485
1543 const lparen = if (fn_proto.getNameToken()) |name_token| blk: {1486 const lparen = if (fn_proto.getNameToken()) |name_token| blk: {
1544 try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.Space); // fn1487 try renderToken(tree, ais, fn_proto.fn_token, Space.Space); // fn
1545 try renderToken(tree, stream, name_token, indent, start_col, Space.None); // name1488 try renderToken(tree, ais, name_token, Space.None); // name
1546 break :blk tree.nextToken(name_token);1489 break :blk tree.nextToken(name_token);
1547 } else blk: {1490 } else blk: {
1548 try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.Space); // fn1491 try renderToken(tree, ais, fn_proto.fn_token, Space.Space); // fn
1549 break :blk tree.nextToken(fn_proto.fn_token);1492 break :blk tree.nextToken(fn_proto.fn_token);
1550 };1493 };
1551 assert(tree.token_ids[lparen] == .LParen);1494 assert(tree.token_ids[lparen] == .LParen);
...@@ -1572,47 +1515,45 @@ fn renderExpression(...@@ -1572,47 +1515,45 @@ fn renderExpression(
1572 };1515 };
15731516
1574 if (!src_params_trailing_comma) {1517 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
1577 // render all on one line, no trailing comma1520 // render all on one line, no trailing comma
1578 for (fn_proto.params()) |param_decl, i| {1521 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
1581 if (i + 1 < fn_proto.params_len or fn_proto.getVarArgsToken() != null) {1524 if (i + 1 < fn_proto.params_len or fn_proto.getVarArgsToken() != null) {
1582 const comma = tree.nextToken(param_decl.lastToken());1525 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); // ,
1584 }1527 }
1585 }1528 }
1586 if (fn_proto.getVarArgsToken()) |var_args_token| {1529 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);
1588 }1531 }
1589 } else {1532 } else {
1590 // one param per line1533 // one param per line
1591 const new_indent = indent + indent_delta;1534 ais.pushIndent();
1592 try renderToken(tree, stream, lparen, new_indent, start_col, Space.Newline); // (1535 defer ais.popIndent();
1536 try renderToken(tree, ais, lparen, Space.Newline); // (
15931537
1594 for (fn_proto.params()) |param_decl| {1538 for (fn_proto.params()) |param_decl| {
1595 try stream.writeByteNTimes(' ', new_indent);1539 try renderParamDecl(allocator, ais, tree, param_decl, Space.Comma);
1596 try renderParamDecl(allocator, stream, tree, new_indent, start_col, param_decl, Space.Comma);
1597 }1540 }
1598 if (fn_proto.getVarArgsToken()) |var_args_token| {1541 if (fn_proto.getVarArgsToken()) |var_args_token| {
1599 try stream.writeByteNTimes(' ', new_indent);1542 try renderToken(tree, ais, var_args_token, Space.Comma);
1600 try renderToken(tree, stream, var_args_token, new_indent, start_col, Space.Comma);
1601 }1543 }
1602 try stream.writeByteNTimes(' ', indent);
1603 }1544 }
16041545
1605 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )1546 try renderToken(tree, ais, rparen, Space.Space); // )
16061547
1607 if (fn_proto.getAlignExpr()) |align_expr| {1548 if (fn_proto.getAlignExpr()) |align_expr| {
1608 const align_rparen = tree.nextToken(align_expr.lastToken());1549 const align_rparen = tree.nextToken(align_expr.lastToken());
1609 const align_lparen = tree.prevToken(align_expr.firstToken());1550 const align_lparen = tree.prevToken(align_expr.firstToken());
1610 const align_kw = tree.prevToken(align_lparen);1551 const align_kw = tree.prevToken(align_lparen);
16111552
1612 try renderToken(tree, stream, align_kw, indent, start_col, Space.None); // align1553 try renderToken(tree, ais, align_kw, Space.None); // align
1613 try renderToken(tree, stream, align_lparen, indent, start_col, Space.None); // (1554 try renderToken(tree, ais, align_lparen, Space.None); // (
1614 try renderExpression(allocator, stream, tree, indent, start_col, align_expr, Space.None);1555 try renderExpression(allocator, ais, tree, align_expr, Space.None);
1615 try renderToken(tree, stream, align_rparen, indent, start_col, Space.Space); // )1556 try renderToken(tree, ais, align_rparen, Space.Space); // )
1616 }1557 }
16171558
1618 if (fn_proto.getSectionExpr()) |section_expr| {1559 if (fn_proto.getSectionExpr()) |section_expr| {
...@@ -1620,10 +1561,10 @@ fn renderExpression(...@@ -1620,10 +1561,10 @@ fn renderExpression(
1620 const section_lparen = tree.prevToken(section_expr.firstToken());1561 const section_lparen = tree.prevToken(section_expr.firstToken());
1621 const section_kw = tree.prevToken(section_lparen);1562 const section_kw = tree.prevToken(section_lparen);
16221563
1623 try renderToken(tree, stream, section_kw, indent, start_col, Space.None); // section1564 try renderToken(tree, ais, section_kw, Space.None); // section
1624 try renderToken(tree, stream, section_lparen, indent, start_col, Space.None); // (1565 try renderToken(tree, ais, section_lparen, Space.None); // (
1625 try renderExpression(allocator, stream, tree, indent, start_col, section_expr, Space.None);1566 try renderExpression(allocator, ais, tree, section_expr, Space.None);
1626 try renderToken(tree, stream, section_rparen, indent, start_col, Space.Space); // )1567 try renderToken(tree, ais, section_rparen, Space.Space); // )
1627 }1568 }
16281569
1629 if (fn_proto.getCallconvExpr()) |callconv_expr| {1570 if (fn_proto.getCallconvExpr()) |callconv_expr| {
...@@ -1631,23 +1572,23 @@ fn renderExpression(...@@ -1631,23 +1572,23 @@ fn renderExpression(
1631 const callconv_lparen = tree.prevToken(callconv_expr.firstToken());1572 const callconv_lparen = tree.prevToken(callconv_expr.firstToken());
1632 const callconv_kw = tree.prevToken(callconv_lparen);1573 const callconv_kw = tree.prevToken(callconv_lparen);
16331574
1634 try renderToken(tree, stream, callconv_kw, indent, start_col, Space.None); // callconv1575 try renderToken(tree, ais, callconv_kw, Space.None); // callconv
1635 try renderToken(tree, stream, callconv_lparen, indent, start_col, Space.None); // (1576 try renderToken(tree, ais, callconv_lparen, Space.None); // (
1636 try renderExpression(allocator, stream, tree, indent, start_col, callconv_expr, Space.None);1577 try renderExpression(allocator, ais, tree, callconv_expr, Space.None);
1637 try renderToken(tree, stream, callconv_rparen, indent, start_col, Space.Space); // )1578 try renderToken(tree, ais, callconv_rparen, Space.Space); // )
1638 } else if (fn_proto.getIsExternPrototype() != null) {1579 } else if (fn_proto.getIsExternPrototype() != null) {
1639 try stream.writeAll("callconv(.C) ");1580 try ais.writer().writeAll("callconv(.C) ");
1640 } else if (fn_proto.getIsAsync() != null) {1581 } else if (fn_proto.getIsAsync() != null) {
1641 try stream.writeAll("callconv(.Async) ");1582 try ais.writer().writeAll("callconv(.Async) ");
1642 }1583 }
16431584
1644 switch (fn_proto.return_type) {1585 switch (fn_proto.return_type) {
1645 .Explicit => |node| {1586 .Explicit => |node| {
1646 return renderExpression(allocator, stream, tree, indent, start_col, node, space);1587 return renderExpression(allocator, ais, tree, node, space);
1647 },1588 },
1648 .InferErrorSet => |node| {1589 .InferErrorSet => |node| {
1649 try renderToken(tree, stream, tree.prevToken(node.firstToken()), indent, start_col, Space.None); // !1590 try renderToken(tree, ais, tree.prevToken(node.firstToken()), Space.None); // !
1650 return renderExpression(allocator, stream, tree, indent, start_col, node, space);1591 return renderExpression(allocator, ais, tree, node, space);
1651 },1592 },
1652 .Invalid => unreachable,1593 .Invalid => unreachable,
1653 }1594 }
...@@ -1657,11 +1598,11 @@ fn renderExpression(...@@ -1657,11 +1598,11 @@ fn renderExpression(
1657 const anyframe_type = @fieldParentPtr(ast.Node.AnyFrameType, "base", base);1598 const anyframe_type = @fieldParentPtr(ast.Node.AnyFrameType, "base", base);
16581599
1659 if (anyframe_type.result) |result| {1600 if (anyframe_type.result) |result| {
1660 try renderToken(tree, stream, anyframe_type.anyframe_token, indent, start_col, Space.None); // anyframe1601 try renderToken(tree, ais, anyframe_type.anyframe_token, Space.None); // anyframe
1661 try renderToken(tree, stream, result.arrow_token, indent, start_col, Space.None); // ->1602 try renderToken(tree, ais, result.arrow_token, Space.None); // ->
1662 return renderExpression(allocator, stream, tree, indent, start_col, result.return_type, space);1603 return renderExpression(allocator, ais, tree, result.return_type, space);
1663 } else {1604 } else {
1664 return renderToken(tree, stream, anyframe_type.anyframe_token, indent, start_col, space); // anyframe1605 return renderToken(tree, ais, anyframe_type.anyframe_token, space); // anyframe
1665 }1606 }
1666 },1607 },
16671608
...@@ -1670,38 +1611,38 @@ fn renderExpression(...@@ -1670,38 +1611,38 @@ fn renderExpression(
1670 .Switch => {1611 .Switch => {
1671 const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);1612 const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
16721613
1673 try renderToken(tree, stream, switch_node.switch_token, indent, start_col, Space.Space); // switch1614 try renderToken(tree, ais, switch_node.switch_token, Space.Space); // switch
1674 try renderToken(tree, stream, tree.nextToken(switch_node.switch_token), indent, start_col, Space.None); // (1615 try renderToken(tree, ais, tree.nextToken(switch_node.switch_token), Space.None); // (
16751616
1676 const rparen = tree.nextToken(switch_node.expr.lastToken());1617 const rparen = tree.nextToken(switch_node.expr.lastToken());
1677 const lbrace = tree.nextToken(rparen);1618 const lbrace = tree.nextToken(rparen);
16781619
1679 if (switch_node.cases_len == 0) {1620 if (switch_node.cases_len == 0) {
1680 try renderExpression(allocator, stream, tree, indent, start_col, switch_node.expr, Space.None);1621 try renderExpression(allocator, ais, tree, switch_node.expr, Space.None);
1681 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )1622 try renderToken(tree, ais, rparen, Space.Space); // )
1682 try renderToken(tree, stream, lbrace, indent, start_col, Space.None); // {1623 try renderToken(tree, ais, lbrace, Space.None); // {
1683 return renderToken(tree, stream, switch_node.rbrace, indent, start_col, space); // }1624 return renderToken(tree, ais, switch_node.rbrace, space); // }
1684 }1625 }
16851626
1686 try renderExpression(allocator, stream, tree, indent, start_col, switch_node.expr, Space.None);1627 try renderExpression(allocator, ais, tree, switch_node.expr, Space.None);
16871628 try renderToken(tree, ais, rparen, Space.Space); // )
1688 const new_indent = indent + indent_delta;
16891629
1690 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )1630 {
1691 try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline); // {1631 ais.pushIndentNextLine();
1632 defer ais.popIndent();
1633 try renderToken(tree, ais, lbrace, Space.Newline); // {
16921634
1693 const cases = switch_node.cases();1635 const cases = switch_node.cases();
1694 for (cases) |node, i| {1636 for (cases) |node, i| {
1695 try stream.writeByteNTimes(' ', new_indent);1637 try renderExpression(allocator, ais, tree, node, Space.Comma);
1696 try renderExpression(allocator, stream, tree, new_indent, start_col, node, Space.Comma);
16971638
1698 if (i + 1 < cases.len) {1639 if (i + 1 < cases.len) {
1699 try renderExtraNewline(tree, stream, start_col, cases[i + 1]);1640 try renderExtraNewline(tree, ais, cases[i + 1]);
1641 }
1700 }1642 }
1701 }1643 }
17021644
1703 try stream.writeByteNTimes(' ', indent);1645 return renderToken(tree, ais, switch_node.rbrace, space); // }
1704 return renderToken(tree, stream, switch_node.rbrace, indent, start_col, space); // }
1705 },1646 },
17061647
1707 .SwitchCase => {1648 .SwitchCase => {
...@@ -1718,43 +1659,41 @@ fn renderExpression(...@@ -1718,43 +1659,41 @@ fn renderExpression(
1718 const items = switch_case.items();1659 const items = switch_case.items();
1719 for (items) |node, i| {1660 for (items) |node, i| {
1720 if (i + 1 < items.len) {1661 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
1723 const comma_token = tree.nextToken(node.lastToken());1664 const comma_token = tree.nextToken(node.lastToken());
1724 try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // ,1665 try renderToken(tree, ais, comma_token, Space.Space); // ,
1725 try renderExtraNewline(tree, stream, start_col, items[i + 1]);1666 try renderExtraNewline(tree, ais, items[i + 1]);
1726 } else {1667 } else {
1727 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.Space);1668 try renderExpression(allocator, ais, tree, node, Space.Space);
1728 }1669 }
1729 }1670 }
1730 } else {1671 } else {
1731 const items = switch_case.items();1672 const items = switch_case.items();
1732 for (items) |node, i| {1673 for (items) |node, i| {
1733 if (i + 1 < items.len) {1674 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
1736 const comma_token = tree.nextToken(node.lastToken());1677 const comma_token = tree.nextToken(node.lastToken());
1737 try renderToken(tree, stream, comma_token, indent, start_col, Space.Newline); // ,1678 try renderToken(tree, ais, comma_token, Space.Newline); // ,
1738 try renderExtraNewline(tree, stream, start_col, items[i + 1]);1679 try renderExtraNewline(tree, ais, items[i + 1]);
1739 try stream.writeByteNTimes(' ', indent);
1740 } else {1680 } else {
1741 try renderExpression(allocator, stream, tree, indent, start_col, node, Space.Comma);1681 try renderExpression(allocator, ais, tree, node, Space.Comma);
1742 try stream.writeByteNTimes(' ', indent);
1743 }1682 }
1744 }1683 }
1745 }1684 }
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
1749 if (switch_case.payload) |payload| {1688 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);
1751 }1690 }
17521691
1753 return renderExpression(allocator, stream, tree, indent, start_col, switch_case.expr, space);1692 return renderExpression(allocator, ais, tree, switch_case.expr, space);
1754 },1693 },
1755 .SwitchElse => {1694 .SwitchElse => {
1756 const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base);1695 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);
1758 },1697 },
1759 .Else => {1698 .Else => {
1760 const else_node = @fieldParentPtr(ast.Node.Else, "base", base);1699 const else_node = @fieldParentPtr(ast.Node.Else, "base", base);
...@@ -1763,37 +1702,37 @@ fn renderExpression(...@@ -1763,37 +1702,37 @@ fn renderExpression(
1763 const same_line = body_is_block or tree.tokensOnSameLine(else_node.else_token, else_node.body.lastToken());1702 const same_line = body_is_block or tree.tokensOnSameLine(else_node.else_token, else_node.body.lastToken());
17641703
1765 const after_else_space = if (same_line or else_node.payload != null) Space.Space else Space.Newline;1704 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
1768 if (else_node.payload) |payload| {1707 if (else_node.payload) |payload| {
1769 const payload_space = if (same_line) Space.Space else Space.Newline;1708 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);
1771 }1710 }
17721711
1773 if (same_line) {1712 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);
1775 }1718 }
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);
1780 },1719 },
17811720
1782 .While => {1721 .While => {
1783 const while_node = @fieldParentPtr(ast.Node.While, "base", base);1722 const while_node = @fieldParentPtr(ast.Node.While, "base", base);
17841723
1785 if (while_node.label) |label| {1724 if (while_node.label) |label| {
1786 try renderToken(tree, stream, label, indent, start_col, Space.None); // label1725 try renderToken(tree, ais, label, Space.None); // label
1787 try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); // :1726 try renderToken(tree, ais, tree.nextToken(label), Space.Space); // :
1788 }1727 }
17891728
1790 if (while_node.inline_token) |inline_token| {1729 if (while_node.inline_token) |inline_token| {
1791 try renderToken(tree, stream, inline_token, indent, start_col, Space.Space); // inline1730 try renderToken(tree, ais, inline_token, Space.Space); // inline
1792 }1731 }
17931732
1794 try renderToken(tree, stream, while_node.while_token, indent, start_col, Space.Space); // while1733 try renderToken(tree, ais, while_node.while_token, Space.Space); // while
1795 try renderToken(tree, stream, tree.nextToken(while_node.while_token), indent, start_col, Space.None); // (1734 try renderToken(tree, ais, tree.nextToken(while_node.while_token), Space.None); // (
1796 try renderExpression(allocator, stream, tree, indent, start_col, while_node.condition, Space.None);1735 try renderExpression(allocator, ais, tree, while_node.condition, Space.None);
17971736
1798 const cond_rparen = tree.nextToken(while_node.condition.lastToken());1737 const cond_rparen = tree.nextToken(while_node.condition.lastToken());
17991738
...@@ -1815,12 +1754,12 @@ fn renderExpression(...@@ -1815,12 +1754,12 @@ fn renderExpression(
18151754
1816 {1755 {
1817 const rparen_space = if (while_node.payload != null or while_node.continue_expr != null) Space.Space else block_start_space;1756 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); // )
1819 }1758 }
18201759
1821 if (while_node.payload) |payload| {1760 if (while_node.payload) |payload| {
1822 const payload_space = if (while_node.continue_expr != null) Space.Space else block_start_space;1761 const payload_space = Space.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);1762 try renderExpression(allocator, ais, tree, payload, payload_space);
1824 }1763 }
18251764
1826 if (while_node.continue_expr) |continue_expr| {1765 if (while_node.continue_expr) |continue_expr| {
...@@ -1828,29 +1767,22 @@ fn renderExpression(...@@ -1828,29 +1767,22 @@ fn renderExpression(
1828 const lparen = tree.prevToken(continue_expr.firstToken());1767 const lparen = tree.prevToken(continue_expr.firstToken());
1829 const colon = tree.prevToken(lparen);1768 const colon = tree.prevToken(lparen);
18301769
1831 try renderToken(tree, stream, colon, indent, start_col, Space.Space); // :1770 try renderToken(tree, ais, colon, Space.Space); // :
1832 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (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); // )
1837 }1776 }
18381777
1839 var new_indent = indent;1778 {
1840 if (block_start_space == Space.Newline) {1779 if (!body_is_block) ais.pushIndent();
1841 new_indent += indent_delta;1780 defer if (!body_is_block) ais.popIndent();
1842 try stream.writeByteNTimes(' ', new_indent);1781 try renderExpression(allocator, ais, tree, while_node.body, after_body_space);
1843 start_col.* = new_indent;
1844 }1782 }
18451783
1846 try renderExpression(allocator, stream, tree, indent, start_col, while_node.body, after_body_space);
1847
1848 if (while_node.@"else") |@"else"| {1784 if (while_node.@"else") |@"else"| {
1849 if (after_body_space == Space.Newline) {1785 return renderExpression(allocator, ais, tree, &@"else".base, space);
1850 try stream.writeByteNTimes(' ', indent);
1851 start_col.* = indent;
1852 }
1853 return renderExpression(allocator, stream, tree, indent, start_col, &@"else".base, space);
1854 }1786 }
1855 },1787 },
18561788
...@@ -1858,17 +1790,17 @@ fn renderExpression(...@@ -1858,17 +1790,17 @@ fn renderExpression(
1858 const for_node = @fieldParentPtr(ast.Node.For, "base", base);1790 const for_node = @fieldParentPtr(ast.Node.For, "base", base);
18591791
1860 if (for_node.label) |label| {1792 if (for_node.label) |label| {
1861 try renderToken(tree, stream, label, indent, start_col, Space.None); // label1793 try renderToken(tree, ais, label, Space.None); // label
1862 try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); // :1794 try renderToken(tree, ais, tree.nextToken(label), Space.Space); // :
1863 }1795 }
18641796
1865 if (for_node.inline_token) |inline_token| {1797 if (for_node.inline_token) |inline_token| {
1866 try renderToken(tree, stream, inline_token, indent, start_col, Space.Space); // inline1798 try renderToken(tree, ais, inline_token, Space.Space); // inline
1867 }1799 }
18681800
1869 try renderToken(tree, stream, for_node.for_token, indent, start_col, Space.Space); // for1801 try renderToken(tree, ais, for_node.for_token, Space.Space); // for
1870 try renderToken(tree, stream, tree.nextToken(for_node.for_token), indent, start_col, Space.None); // (1802 try renderToken(tree, ais, tree.nextToken(for_node.for_token), Space.None); // (
1871 try renderExpression(allocator, stream, tree, indent, start_col, for_node.array_expr, Space.None);1803 try renderExpression(allocator, ais, tree, for_node.array_expr, Space.None);
18721804
1873 const rparen = tree.nextToken(for_node.array_expr.lastToken());1805 const rparen = tree.nextToken(for_node.array_expr.lastToken());
18741806
...@@ -1876,10 +1808,10 @@ fn renderExpression(...@@ -1876,10 +1808,10 @@ fn renderExpression(
1876 const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());1808 const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());
1877 const body_on_same_line = body_is_block or src_one_line_to_body;1809 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
1881 const space_after_payload = if (body_on_same_line) Space.Space else Space.Newline;1813 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
1884 const space_after_body = blk: {1816 const space_after_body = blk: {
1885 if (for_node.@"else") |@"else"| {1817 if (for_node.@"else") |@"else"| {
...@@ -1894,13 +1826,14 @@ fn renderExpression(...@@ -1894,13 +1826,14 @@ fn renderExpression(
1894 }1826 }
1895 };1827 };
18961828
1897 const body_indent = if (body_on_same_line) indent else indent + indent_delta;1829 {
1898 if (!body_on_same_line) try stream.writeByteNTimes(' ', body_indent);1830 if (!body_on_same_line) ais.pushIndent();
1899 try renderExpression(allocator, stream, tree, body_indent, start_col, for_node.body, space_after_body); // { body }1831 defer if (!body_on_same_line) ais.popIndent();
1832 try renderExpression(allocator, ais, tree, for_node.body, space_after_body); // { body }
1833 }
19001834
1901 if (for_node.@"else") |@"else"| {1835 if (for_node.@"else") |@"else"| {
1902 if (space_after_body == Space.Newline) try stream.writeByteNTimes(' ', indent);1836 return renderExpression(allocator, ais, tree, &@"else".base, space); // else
1903 return renderExpression(allocator, stream, tree, indent, start_col, &@"else".base, space); // else
1904 }1837 }
1905 },1838 },
19061839
...@@ -1910,29 +1843,29 @@ fn renderExpression(...@@ -1910,29 +1843,29 @@ fn renderExpression(
1910 const lparen = tree.nextToken(if_node.if_token);1843 const lparen = tree.nextToken(if_node.if_token);
1911 const rparen = tree.nextToken(if_node.condition.lastToken());1844 const rparen = tree.nextToken(if_node.condition.lastToken());
19121845
1913 try renderToken(tree, stream, if_node.if_token, indent, start_col, Space.Space); // if1846 try renderToken(tree, ais, if_node.if_token, Space.Space); // if
1914 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (1847 try renderToken(tree, ais, lparen, Space.None); // (
19151848
1916 try renderExpression(allocator, stream, tree, indent, start_col, if_node.condition, Space.None); // condition1849 try renderExpression(allocator, ais, tree, if_node.condition, Space.None); // condition
19171850
1918 const body_is_if_block = if_node.body.tag == .If;1851 const body_is_if_block = if_node.body.tag == .If;
1919 const body_is_block = nodeIsBlock(if_node.body);1852 const body_is_block = nodeIsBlock(if_node.body);
19201853
1921 if (body_is_if_block) {1854 if (body_is_if_block) {
1922 try renderExtraNewline(tree, stream, start_col, if_node.body);1855 try renderExtraNewline(tree, ais, if_node.body);
1923 } else if (body_is_block) {1856 } else if (body_is_block) {
1924 const after_rparen_space = if (if_node.payload == null) Space.BlockStart else Space.Space;1857 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
1927 if (if_node.payload) |payload| {1860 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|
1929 }1862 }
19301863
1931 if (if_node.@"else") |@"else"| {1864 if (if_node.@"else") |@"else"| {
1932 try renderExpression(allocator, stream, tree, indent, start_col, if_node.body, Space.SpaceOrOutdent);1865 try renderExpression(allocator, ais, tree, if_node.body, Space.SpaceOrOutdent);
1933 return renderExpression(allocator, stream, tree, indent, start_col, &@"else".base, space);1866 return renderExpression(allocator, ais, tree, &@"else".base, space);
1934 } else {1867 } else {
1935 return renderExpression(allocator, stream, tree, indent, start_col, if_node.body, space);1868 return renderExpression(allocator, ais, tree, if_node.body, space);
1936 }1869 }
1937 }1870 }
19381871
...@@ -1940,186 +1873,184 @@ fn renderExpression(...@@ -1940,186 +1873,184 @@ fn renderExpression(
19401873
1941 if (src_has_newline) {1874 if (src_has_newline) {
1942 const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space;1875 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
1945 if (if_node.payload) |payload| {1878 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);
1947 }1880 }
19481881
1949 const new_indent = indent + indent_delta;
1950 try stream.writeByteNTimes(' ', new_indent);
1951
1952 if (if_node.@"else") |@"else"| {1882 if (if_node.@"else") |@"else"| {
1953 const else_is_block = nodeIsBlock(@"else".body);1883 const else_is_block = nodeIsBlock(@"else".body);
1954 try renderExpression(allocator, stream, tree, new_indent, start_col, if_node.body, Space.Newline);1884
1955 try stream.writeByteNTimes(' ', indent);1885 {
1886 ais.pushIndent();
1887 defer ais.popIndent();
1888 try renderExpression(allocator, ais, tree, if_node.body, Space.Newline);
1889 }
19561890
1957 if (else_is_block) {1891 if (else_is_block) {
1958 try renderToken(tree, stream, @"else".else_token, indent, start_col, Space.Space); // else1892 try renderToken(tree, ais, @"else".else_token, Space.Space); // else
19591893
1960 if (@"else".payload) |payload| {1894 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);
1962 }1896 }
19631897
1964 return renderExpression(allocator, stream, tree, indent, start_col, @"else".body, space);1898 return renderExpression(allocator, ais, tree, @"else".body, space);
1965 } else {1899 } else {
1966 const after_else_space = if (@"else".payload == null) Space.Newline else Space.Space;1900 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); // else1901 try renderToken(tree, ais, @"else".else_token, after_else_space); // else
19681902
1969 if (@"else".payload) |payload| {1903 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);
1971 }1905 }
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);
1975 }1910 }
1976 } else {1911 } 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);
1978 }1915 }
1979 }1916 }
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
1983 if (if_node.payload) |payload| {1922 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);
1985 }1924 }
19861925
1987 if (if_node.@"else") |@"else"| {1926 if (if_node.@"else") |@"else"| {
1988 try renderExpression(allocator, stream, tree, indent, start_col, if_node.body, Space.Space);1927 try renderExpression(allocator, ais, tree, if_node.body, Space.Space);
1989 try renderToken(tree, stream, @"else".else_token, indent, start_col, Space.Space);1928 try renderToken(tree, ais, @"else".else_token, Space.Space);
19901929
1991 if (@"else".payload) |payload| {1930 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);
1993 }1932 }
19941933
1995 return renderExpression(allocator, stream, tree, indent, start_col, @"else".body, space);1934 return renderExpression(allocator, ais, tree, @"else".body, space);
1996 } else {1935 } else {
1997 return renderExpression(allocator, stream, tree, indent, start_col, if_node.body, space);1936 return renderExpression(allocator, ais, tree, if_node.body, space);
1998 }1937 }
1999 },1938 },
20001939
2001 .Asm => {1940 .Asm => {
2002 const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);1941 const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);
20031942
2004 try renderToken(tree, stream, asm_node.asm_token, indent, start_col, Space.Space); // asm1943 try renderToken(tree, ais, asm_node.asm_token, Space.Space); // asm
20051944
2006 if (asm_node.volatile_token) |volatile_token| {1945 if (asm_node.volatile_token) |volatile_token| {
2007 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile1946 try renderToken(tree, ais, volatile_token, Space.Space); // volatile
2008 try renderToken(tree, stream, tree.nextToken(volatile_token), indent, start_col, Space.None); // (1947 try renderToken(tree, ais, tree.nextToken(volatile_token), Space.None); // (
2009 } else {1948 } 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); // (
2011 }1950 }
20121951
2013 if (asm_node.outputs.len == 0 and asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {1952 asmblk: {
2014 try renderExpression(allocator, stream, tree, indent, start_col, asm_node.template, Space.None);1953 ais.pushIndent();
2015 return renderToken(tree, stream, asm_node.rparen, indent, start_col, space);1954 defer ais.popIndent();
2016 }
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) {1963 ais.setIndentDelta(asm_indent_delta);
2023 // After rendering a multiline string literal the cursor is1964 defer ais.setIndentDelta(indent_delta);
2024 // already offset by indent
2025 try stream.writeByteNTimes(' ', indent_delta);
2026 } else {
2027 try stream.writeByteNTimes(' ', indent_once);
2028 }
20291965
2030 const colon1 = tree.nextToken(asm_node.template.lastToken());1966 const colon1 = tree.nextToken(asm_node.template.lastToken());
2031 const indent_extra = indent_once + 2;
20321967
2033 const colon2 = if (asm_node.outputs.len == 0) blk: {1968 const colon2 = if (asm_node.outputs.len == 0) blk: {
2034 try renderToken(tree, stream, colon1, indent, start_col, Space.Newline); // :1969 try renderToken(tree, ais, colon1, Space.Newline); // :
2035 try stream.writeByteNTimes(' ', indent_once);
20361970
2037 break :blk tree.nextToken(colon1);1971 break :blk tree.nextToken(colon1);
2038 } else blk: {1972 } else blk: {
2039 try renderToken(tree, stream, colon1, indent, start_col, Space.Space); // :1973 try renderToken(tree, ais, colon1, 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 };
20671974
2068 const colon3 = if (asm_node.inputs.len == 0) blk: {1975 ais.pushIndent();
2069 try renderToken(tree, stream, colon2, indent, start_col, Space.Newline); // :1976 defer ais.popIndent();
2070 try stream.writeByteNTimes(' ', indent_once);
20711977
2072 break :blk tree.nextToken(colon2);1978 for (asm_node.outputs) |*asm_output, i| {
2073 } else blk: {1979 if (i + 1 < asm_node.outputs.len) {
2074 try renderToken(tree, stream, colon2, indent, start_col, Space.Space); // :1980 const next_asm_output = asm_node.outputs[i + 1];
20751981 try renderAsmOutput(allocator, ais, tree, asm_output, Space.None);
2076 for (asm_node.inputs) |*asm_input, i| {1982
2077 if (i + 1 < asm_node.inputs.len) {1983 const comma = tree.prevToken(next_asm_output.firstToken());
2078 const next_asm_input = &asm_node.inputs[i + 1];1984 try renderToken(tree, ais, comma, Space.Newline); // ,
2079 try renderAsmInput(allocator, stream, tree, indent_extra, start_col, asm_input, Space.None);1985 try renderExtraNewlineToken(tree, ais, next_asm_output.firstToken());
20801986 } else if (asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {
2081 const comma = tree.prevToken(next_asm_input.firstToken());1987 try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline);
2082 try renderToken(tree, stream, comma, indent_extra, start_col, Space.Newline); // ,1988 break :asmblk;
2083 try renderExtraNewlineToken(tree, stream, start_col, next_asm_input.firstToken());1989 } else {
20841990 try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline);
2085 try stream.writeByteNTimes(' ', indent_extra);1991 const comma_or_colon = tree.nextToken(asm_output.lastToken());
2086 } else if (asm_node.clobbers.len == 0) {1992 break :blk switch (tree.token_ids[comma_or_colon]) {
2087 try renderAsmInput(allocator, stream, tree, indent_extra, start_col, asm_input, Space.Newline);1993 .Comma => tree.nextToken(comma_or_colon),
2088 try stream.writeByteNTimes(' ', indent);1994 else => comma_or_colon,
2089 return renderToken(tree, stream, asm_node.rparen, indent, start_col, space); // )1995 };
2090 } else {1996 }
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 };
2098 }1997 }
2099 }1998 unreachable;
2100 unreachable;1999 };
2101 };
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| {2031 try renderToken(tree, ais, colon3, Space.Space); // :
2106 if (i + 1 >= asm_node.clobbers.len) {2032 ais.pushIndent();
2107 try renderExpression(allocator, stream, tree, indent_extra, start_col, clobber_node, Space.Newline);2033 defer ais.popIndent();
2108 try stream.writeByteNTimes(' ', indent);2034 for (asm_node.clobbers) |clobber_node, i| {
2109 return renderToken(tree, stream, asm_node.rparen, indent, start_col, space);2035 if (i + 1 >= asm_node.clobbers.len) {
2110 } else {2036 try renderExpression(allocator, ais, tree, clobber_node, Space.Newline);
2111 try renderExpression(allocator, stream, tree, indent_extra, start_col, clobber_node, Space.None);2037 break :asmblk;
2112 const comma = tree.nextToken(clobber_node.lastToken());2038 } else {
2113 try renderToken(tree, stream, comma, indent_once, start_col, Space.Space); // ,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 }
2114 }2043 }
2115 }2044 }
2045
2046 return renderToken(tree, ais, asm_node.rparen, space);
2116 },2047 },
21172048
2118 .EnumLiteral => {2049 .EnumLiteral => {
2119 const enum_literal = @fieldParentPtr(ast.Node.EnumLiteral, "base", base);2050 const enum_literal = @fieldParentPtr(ast.Node.EnumLiteral, "base", base);
21202051
2121 try renderToken(tree, stream, enum_literal.dot, indent, start_col, Space.None); // .2052 try renderToken(tree, ais, enum_literal.dot, Space.None); // .
2122 return renderToken(tree, stream, enum_literal.name, indent, start_col, space); // name2053 return renderToken(tree, ais, enum_literal.name, space); // name
2123 },2054 },
21242055
2125 .ContainerField,2056 .ContainerField,
...@@ -2133,118 +2064,115 @@ fn renderExpression(...@@ -2133,118 +2064,115 @@ fn renderExpression(
21332064
2134fn renderArrayType(2065fn renderArrayType(
2135 allocator: *mem.Allocator,2066 allocator: *mem.Allocator,
2136 stream: anytype,2067 ais: anytype,
2137 tree: *ast.Tree,2068 tree: *ast.Tree,
2138 indent: usize,
2139 start_col: *usize,
2140 lbracket: ast.TokenIndex,2069 lbracket: ast.TokenIndex,
2141 rhs: *ast.Node,2070 rhs: *ast.Node,
2142 len_expr: *ast.Node,2071 len_expr: *ast.Node,
2143 opt_sentinel: ?*ast.Node,2072 opt_sentinel: ?*ast.Node,
2144 space: Space,2073 space: Space,
2145) (@TypeOf(stream).Error || Error)!void {2074) (@TypeOf(ais.*).Error || Error)!void {
2146 const rbracket = tree.nextToken(if (opt_sentinel) |sentinel|2075 const rbracket = tree.nextToken(if (opt_sentinel) |sentinel|
2147 sentinel.lastToken()2076 sentinel.lastToken()
2148 else2077 else
2149 len_expr.lastToken());2078 len_expr.lastToken());
21502079
2151 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
2152
2153 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;2080 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
2154 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;2081 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
2155 const new_indent = if (ends_with_comment) indent + indent_delta else indent;
2156 const new_space = if (ends_with_comment) Space.Newline else Space.None;2082 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);2083 {
2158 if (starts_with_comment) {2084 const do_indent = (starts_with_comment or ends_with_comment);
2159 try stream.writeByte('\n');2085 if (do_indent) ais.pushIndent();
2160 }2086 defer if (do_indent) ais.popIndent();
2161 if (ends_with_comment or starts_with_comment) {2087
2162 try stream.writeByteNTimes(' ', indent);2088 try renderToken(tree, ais, lbracket, Space.None); // [
2163 }2089 try renderExpression(allocator, ais, tree, len_expr, new_space);
2164 if (opt_sentinel) |sentinel| {2090
2165 const colon_token = tree.prevToken(sentinel.firstToken());2091 if (starts_with_comment) {
2166 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :2092 try ais.maybeInsertNewline();
2167 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);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 }
2168 }2102 }
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);
2172}2106}
21732107
2174fn renderAsmOutput(2108fn renderAsmOutput(
2175 allocator: *mem.Allocator,2109 allocator: *mem.Allocator,
2176 stream: anytype,2110 ais: anytype,
2177 tree: *ast.Tree,2111 tree: *ast.Tree,
2178 indent: usize,
2179 start_col: *usize,
2180 asm_output: *const ast.Node.Asm.Output,2112 asm_output: *const ast.Node.Asm.Output,
2181 space: Space,2113 space: Space,
2182) (@TypeOf(stream).Error || Error)!void {2114) (@TypeOf(ais.*).Error || Error)!void {
2183 try stream.writeAll("[");2115 try ais.writer().writeAll("[");
2184 try renderExpression(allocator, stream, tree, indent, start_col, asm_output.symbolic_name, Space.None);2116 try renderExpression(allocator, ais, tree, asm_output.symbolic_name, Space.None);
2185 try stream.writeAll("] ");2117 try ais.writer().writeAll("] ");
2186 try renderExpression(allocator, stream, tree, indent, start_col, asm_output.constraint, Space.None);2118 try renderExpression(allocator, ais, tree, asm_output.constraint, Space.None);
2187 try stream.writeAll(" (");2119 try ais.writer().writeAll(" (");
21882120
2189 switch (asm_output.kind) {2121 switch (asm_output.kind) {
2190 ast.Node.Asm.Output.Kind.Variable => |variable_name| {2122 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);
2192 },2124 },
2193 ast.Node.Asm.Output.Kind.Return => |return_type| {2125 ast.Node.Asm.Output.Kind.Return => |return_type| {
2194 try stream.writeAll("-> ");2126 try ais.writer().writeAll("-> ");
2195 try renderExpression(allocator, stream, tree, indent, start_col, return_type, Space.None);2127 try renderExpression(allocator, ais, tree, return_type, Space.None);
2196 },2128 },
2197 }2129 }
21982130
2199 return renderToken(tree, stream, asm_output.lastToken(), indent, start_col, space); // )2131 return renderToken(tree, ais, asm_output.lastToken(), space); // )
2200}2132}
22012133
2202fn renderAsmInput(2134fn renderAsmInput(
2203 allocator: *mem.Allocator,2135 allocator: *mem.Allocator,
2204 stream: anytype,2136 ais: anytype,
2205 tree: *ast.Tree,2137 tree: *ast.Tree,
2206 indent: usize,
2207 start_col: *usize,
2208 asm_input: *const ast.Node.Asm.Input,2138 asm_input: *const ast.Node.Asm.Input,
2209 space: Space,2139 space: Space,
2210) (@TypeOf(stream).Error || Error)!void {2140) (@TypeOf(ais.*).Error || Error)!void {
2211 try stream.writeAll("[");2141 try ais.writer().writeAll("[");
2212 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.symbolic_name, Space.None);2142 try renderExpression(allocator, ais, tree, asm_input.symbolic_name, Space.None);
2213 try stream.writeAll("] ");2143 try ais.writer().writeAll("] ");
2214 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.constraint, Space.None);2144 try renderExpression(allocator, ais, tree, asm_input.constraint, Space.None);
2215 try stream.writeAll(" (");2145 try ais.writer().writeAll(" (");
2216 try renderExpression(allocator, stream, tree, indent, start_col, asm_input.expr, Space.None);2146 try renderExpression(allocator, ais, tree, asm_input.expr, Space.None);
2217 return renderToken(tree, stream, asm_input.lastToken(), indent, start_col, space); // )2147 return renderToken(tree, ais, asm_input.lastToken(), space); // )
2218}2148}
22192149
2220fn renderVarDecl(2150fn renderVarDecl(
2221 allocator: *mem.Allocator,2151 allocator: *mem.Allocator,
2222 stream: anytype,2152 ais: anytype,
2223 tree: *ast.Tree,2153 tree: *ast.Tree,
2224 indent: usize,
2225 start_col: *usize,
2226 var_decl: *ast.Node.VarDecl,2154 var_decl: *ast.Node.VarDecl,
2227) (@TypeOf(stream).Error || Error)!void {2155) (@TypeOf(ais.*).Error || Error)!void {
2228 if (var_decl.getVisibToken()) |visib_token| {2156 if (var_decl.getVisibToken()) |visib_token| {
2229 try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub2157 try renderToken(tree, ais, visib_token, Space.Space); // pub
2230 }2158 }
22312159
2232 if (var_decl.getExternExportToken()) |extern_export_token| {2160 if (var_decl.getExternExportToken()) |extern_export_token| {
2233 try renderToken(tree, stream, extern_export_token, indent, start_col, Space.Space); // extern2161 try renderToken(tree, ais, extern_export_token, Space.Space); // extern
22342162
2235 if (var_decl.getLibName()) |lib_name| {2163 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"
2237 }2165 }
2238 }2166 }
22392167
2240 if (var_decl.getComptimeToken()) |comptime_token| {2168 if (var_decl.getComptimeToken()) |comptime_token| {
2241 try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space); // comptime2169 try renderToken(tree, ais, comptime_token, Space.Space); // comptime
2242 }2170 }
22432171
2244 if (var_decl.getThreadLocalToken()) |thread_local_token| {2172 if (var_decl.getThreadLocalToken()) |thread_local_token| {
2245 try renderToken(tree, stream, thread_local_token, indent, start_col, Space.Space); // threadlocal2173 try renderToken(tree, ais, thread_local_token, Space.Space); // threadlocal
2246 }2174 }
2247 try renderToken(tree, stream, var_decl.mut_token, indent, start_col, Space.Space); // var2175 try renderToken(tree, ais, var_decl.mut_token, Space.Space); // var
22482176
2249 const name_space = if (var_decl.getTypeNode() == null and2177 const name_space = if (var_decl.getTypeNode() == null and
2250 (var_decl.getAlignNode() != null or2178 (var_decl.getAlignNode() != null or
...@@ -2253,95 +2181,92 @@ fn renderVarDecl(...@@ -2253,95 +2181,92 @@ fn renderVarDecl(
2253 Space.Space2181 Space.Space
2254 else2182 else
2255 Space.None;2183 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
2258 if (var_decl.getTypeNode()) |type_node| {2186 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);
2260 const s = if (var_decl.getAlignNode() != null or2188 const s = if (var_decl.getAlignNode() != null or
2261 var_decl.getSectionNode() != null or2189 var_decl.getSectionNode() != null or
2262 var_decl.getInitNode() != null) Space.Space else Space.None;2190 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);
2264 }2192 }
22652193
2266 if (var_decl.getAlignNode()) |align_node| {2194 if (var_decl.getAlignNode()) |align_node| {
2267 const lparen = tree.prevToken(align_node.firstToken());2195 const lparen = tree.prevToken(align_node.firstToken());
2268 const align_kw = tree.prevToken(lparen);2196 const align_kw = tree.prevToken(lparen);
2269 const rparen = tree.nextToken(align_node.lastToken());2197 const rparen = tree.nextToken(align_node.lastToken());
2270 try renderToken(tree, stream, align_kw, indent, start_col, Space.None); // align2198 try renderToken(tree, ais, align_kw, Space.None); // align
2271 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (2199 try renderToken(tree, ais, lparen, Space.None); // (
2272 try renderExpression(allocator, stream, tree, indent, start_col, align_node, Space.None);2200 try renderExpression(allocator, ais, tree, align_node, Space.None);
2273 const s = if (var_decl.getSectionNode() != null or var_decl.getInitNode() != null) Space.Space else Space.None;2201 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); // )
2275 }2203 }
22762204
2277 if (var_decl.getSectionNode()) |section_node| {2205 if (var_decl.getSectionNode()) |section_node| {
2278 const lparen = tree.prevToken(section_node.firstToken());2206 const lparen = tree.prevToken(section_node.firstToken());
2279 const section_kw = tree.prevToken(lparen);2207 const section_kw = tree.prevToken(lparen);
2280 const rparen = tree.nextToken(section_node.lastToken());2208 const rparen = tree.nextToken(section_node.lastToken());
2281 try renderToken(tree, stream, section_kw, indent, start_col, Space.None); // linksection2209 try renderToken(tree, ais, section_kw, Space.None); // linksection
2282 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (2210 try renderToken(tree, ais, lparen, Space.None); // (
2283 try renderExpression(allocator, stream, tree, indent, start_col, section_node, Space.None);2211 try renderExpression(allocator, ais, tree, section_node, Space.None);
2284 const s = if (var_decl.getInitNode() != null) Space.Space else Space.None;2212 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); // )
2286 }2214 }
22872215
2288 if (var_decl.getInitNode()) |init_node| {2216 if (var_decl.getInitNode()) |init_node| {
2289 const s = if (init_node.tag == .MultilineStringLiteral) Space.None else Space.Space;2217 const s = if (init_node.tag == .MultilineStringLiteral) Space.None else Space.Space;
2290 try renderToken(tree, stream, var_decl.getEqToken().?, indent, start_col, s); // =2218 try renderToken(tree, ais, var_decl.getEqToken().?, s); // =
2291 try renderExpression(allocator, stream, tree, indent, start_col, init_node, Space.None);2219 ais.pushIndentOneShot();
2220 try renderExpression(allocator, ais, tree, init_node, Space.None);
2292 }2221 }
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);
2295}2224}
22962225
2297fn renderParamDecl(2226fn renderParamDecl(
2298 allocator: *mem.Allocator,2227 allocator: *mem.Allocator,
2299 stream: anytype,2228 ais: anytype,
2300 tree: *ast.Tree,2229 tree: *ast.Tree,
2301 indent: usize,
2302 start_col: *usize,
2303 param_decl: ast.Node.FnProto.ParamDecl,2230 param_decl: ast.Node.FnProto.ParamDecl,
2304 space: Space,2231 space: Space,
2305) (@TypeOf(stream).Error || Error)!void {2232) (@TypeOf(ais.*).Error || Error)!void {
2306 try renderDocComments(tree, stream, param_decl, param_decl.doc_comments, indent, start_col);2233 try renderDocComments(tree, ais, param_decl, param_decl.doc_comments);
23072234
2308 if (param_decl.comptime_token) |comptime_token| {2235 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);
2310 }2237 }
2311 if (param_decl.noalias_token) |noalias_token| {2238 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);
2313 }2240 }
2314 if (param_decl.name_token) |name_token| {2241 if (param_decl.name_token) |name_token| {
2315 try renderToken(tree, stream, name_token, indent, start_col, Space.None);2242 try renderToken(tree, ais, name_token, Space.None);
2316 try renderToken(tree, stream, tree.nextToken(name_token), indent, start_col, Space.Space); // :2243 try renderToken(tree, ais, tree.nextToken(name_token), Space.Space); // :
2317 }2244 }
2318 switch (param_decl.param_type) {2245 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),
2320 }2247 }
2321}2248}
23222249
2323fn renderStatement(2250fn renderStatement(
2324 allocator: *mem.Allocator,2251 allocator: *mem.Allocator,
2325 stream: anytype,2252 ais: anytype,
2326 tree: *ast.Tree,2253 tree: *ast.Tree,
2327 indent: usize,
2328 start_col: *usize,
2329 base: *ast.Node,2254 base: *ast.Node,
2330) (@TypeOf(stream).Error || Error)!void {2255) (@TypeOf(ais.*).Error || Error)!void {
2331 switch (base.tag) {2256 switch (base.tag) {
2332 .VarDecl => {2257 .VarDecl => {
2333 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);2258 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);
2335 },2260 },
2336 else => {2261 else => {
2337 if (base.requireSemiColon()) {2262 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
2340 const semicolon_index = tree.nextToken(base.lastToken());2265 const semicolon_index = tree.nextToken(base.lastToken());
2341 assert(tree.token_ids[semicolon_index] == .Semicolon);2266 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);
2343 } else {2268 } else {
2344 try renderExpression(allocator, stream, tree, indent, start_col, base, Space.Newline);2269 try renderExpression(allocator, ais, tree, base, Space.Newline);
2345 }2270 }
2346 },2271 },
2347 }2272 }
...@@ -2360,24 +2285,19 @@ const Space = enum {...@@ -2360,24 +2285,19 @@ const Space = enum {
23602285
2361fn renderTokenOffset(2286fn renderTokenOffset(
2362 tree: *ast.Tree,2287 tree: *ast.Tree,
2363 stream: anytype,2288 ais: anytype,
2364 token_index: ast.TokenIndex,2289 token_index: ast.TokenIndex,
2365 indent: usize,
2366 start_col: *usize,
2367 space: Space,2290 space: Space,
2368 token_skip_bytes: usize,2291 token_skip_bytes: usize,
2369) (@TypeOf(stream).Error || Error)!void {2292) (@TypeOf(ais.*).Error || Error)!void {
2370 if (space == Space.BlockStart) {2293 if (space == Space.BlockStart) {
2371 if (start_col.* < indent + indent_delta)2294 // If placing the lbrace on the current line would cause an uggly gap then put the lbrace on the next line
2372 return renderToken(tree, stream, token_index, indent, start_col, Space.Space);2295 const new_space = if (ais.isLineOverIndented()) Space.Newline else Space.Space;
2373 try renderToken(tree, stream, token_index, indent, start_col, Space.Newline);2296 return renderToken(tree, ais, token_index, new_space);
2374 try stream.writeByteNTimes(' ', indent);
2375 start_col.* = indent;
2376 return;
2377 }2297 }
23782298
2379 var token_loc = tree.token_locs[token_index];2299 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
2382 if (space == Space.NoComment)2302 if (space == Space.NoComment)
2383 return;2303 return;
...@@ -2386,20 +2306,20 @@ fn renderTokenOffset(...@@ -2386,20 +2306,20 @@ fn renderTokenOffset(
2386 var next_token_loc = tree.token_locs[token_index + 1];2306 var next_token_loc = tree.token_locs[token_index + 1];
23872307
2388 if (space == Space.Comma) switch (next_token_id) {2308 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),
2390 .LineComment => {2310 .LineComment => {
2391 try stream.writeAll(", ");2311 try ais.writer().writeAll(", ");
2392 return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline);2312 return renderToken(tree, ais, token_index + 1, Space.Newline);
2393 },2313 },
2394 else => {2314 else => {
2395 if (token_index + 2 < tree.token_ids.len and2315 if (token_index + 2 < tree.token_ids.len and
2396 tree.token_ids[token_index + 2] == .MultilineStringLiteralLine)2316 tree.token_ids[token_index + 2] == .MultilineStringLiteralLine)
2397 {2317 {
2398 try stream.writeAll(",");2318 try ais.writer().writeAll(",");
2399 return;2319 return;
2400 } else {2320 } else {
2401 try stream.writeAll(",\n");2321 try ais.writer().writeAll(",");
2402 start_col.* = 0;2322 try ais.insertNewline();
2403 return;2323 return;
2404 }2324 }
2405 },2325 },
...@@ -2423,15 +2343,14 @@ fn renderTokenOffset(...@@ -2423,15 +2343,14 @@ fn renderTokenOffset(
2423 if (next_token_id == .MultilineStringLiteralLine) {2343 if (next_token_id == .MultilineStringLiteralLine) {
2424 return;2344 return;
2425 } else {2345 } else {
2426 try stream.writeAll("\n");2346 try ais.insertNewline();
2427 start_col.* = 0;
2428 return;2347 return;
2429 }2348 }
2430 },2349 },
2431 Space.Space, Space.SpaceOrOutdent => {2350 Space.Space, Space.SpaceOrOutdent => {
2432 if (next_token_id == .MultilineStringLiteralLine)2351 if (next_token_id == .MultilineStringLiteralLine)
2433 return;2352 return;
2434 try stream.writeByte(' ');2353 try ais.writer().writeByte(' ');
2435 return;2354 return;
2436 },2355 },
2437 Space.NoComment, Space.Comma, Space.BlockStart => unreachable,2356 Space.NoComment, Space.Comma, Space.BlockStart => unreachable,
...@@ -2448,8 +2367,7 @@ fn renderTokenOffset(...@@ -2448,8 +2367,7 @@ fn renderTokenOffset(
2448 next_token_id = tree.token_ids[token_index + offset];2367 next_token_id = tree.token_ids[token_index + offset];
2449 next_token_loc = tree.token_locs[token_index + offset];2368 next_token_loc = tree.token_locs[token_index + offset];
2450 if (next_token_id != .LineComment) {2369 if (next_token_id != .LineComment) {
2451 try stream.writeByte('\n');2370 try ais.insertNewline();
2452 start_col.* = 0;
2453 return;2371 return;
2454 }2372 }
2455 },2373 },
...@@ -2462,7 +2380,7 @@ fn renderTokenOffset(...@@ -2462,7 +2380,7 @@ fn renderTokenOffset(
24622380
2463 var loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);2381 var loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
2464 if (loc.line == 0) {2382 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), " ")});
2466 offset = 2;2384 offset = 2;
2467 token_loc = next_token_loc;2385 token_loc = next_token_loc;
2468 next_token_loc = tree.token_locs[token_index + offset];2386 next_token_loc = tree.token_locs[token_index + offset];
...@@ -2470,26 +2388,16 @@ fn renderTokenOffset(...@@ -2470,26 +2388,16 @@ fn renderTokenOffset(
2470 if (next_token_id != .LineComment) {2388 if (next_token_id != .LineComment) {
2471 switch (space) {2389 switch (space) {
2472 Space.None, Space.Space => {2390 Space.None, Space.Space => {
2473 try stream.writeByte('\n');2391 try ais.insertNewline();
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;
2481 },2392 },
2482 Space.SpaceOrOutdent => {2393 Space.SpaceOrOutdent => {
2483 try stream.writeByte('\n');2394 try ais.insertNewline();
2484 try stream.writeByteNTimes(' ', indent);
2485 start_col.* = indent;
2486 },2395 },
2487 Space.Newline => {2396 Space.Newline => {
2488 if (next_token_id == .MultilineStringLiteralLine) {2397 if (next_token_id == .MultilineStringLiteralLine) {
2489 return;2398 return;
2490 } else {2399 } else {
2491 try stream.writeAll("\n");2400 try ais.insertNewline();
2492 start_col.* = 0;
2493 return;2401 return;
2494 }2402 }
2495 },2403 },
...@@ -2505,10 +2413,9 @@ fn renderTokenOffset(...@@ -2505,10 +2413,9 @@ fn renderTokenOffset(
2505 // translate-c doesn't generate correct newlines2413 // translate-c doesn't generate correct newlines
2506 // in generated code (loc.line == 0) so treat that case2414 // in generated code (loc.line == 0) so treat that case
2507 // as though there was meant to be a newline between the tokens2415 // 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);2416 var newline_count = if (loc.line <= 1) @as(u8, 1) else @as(u8, 2);
2509 try stream.writeByteNTimes('\n', newline_count);2417 while (newline_count > 0) : (newline_count -= 1) try ais.insertNewline();
2510 try stream.writeByteNTimes(' ', indent);2418 try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " "));
2511 try stream.writeAll(mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " "));
25122419
2513 offset += 1;2420 offset += 1;
2514 token_loc = next_token_loc;2421 token_loc = next_token_loc;
...@@ -2520,32 +2427,15 @@ fn renderTokenOffset(...@@ -2520,32 +2427,15 @@ fn renderTokenOffset(
2520 if (next_token_id == .MultilineStringLiteralLine) {2427 if (next_token_id == .MultilineStringLiteralLine) {
2521 return;2428 return;
2522 } else {2429 } else {
2523 try stream.writeAll("\n");2430 try ais.insertNewline();
2524 start_col.* = 0;
2525 return;2431 return;
2526 }2432 }
2527 },2433 },
2528 Space.None, Space.Space => {2434 Space.None, Space.Space => {
2529 try stream.writeByte('\n');2435 try ais.insertNewline();
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;
2544 },2436 },
2545 Space.SpaceOrOutdent => {2437 Space.SpaceOrOutdent => {
2546 try stream.writeByte('\n');2438 try ais.insertNewline();
2547 try stream.writeByteNTimes(' ', indent);
2548 start_col.* = indent;
2549 },2439 },
2550 Space.NoNewline => {},2440 Space.NoNewline => {},
2551 Space.NoComment, Space.Comma, Space.BlockStart => unreachable,2441 Space.NoComment, Space.Comma, Space.BlockStart => unreachable,
...@@ -2558,46 +2448,38 @@ fn renderTokenOffset(...@@ -2558,46 +2448,38 @@ fn renderTokenOffset(
25582448
2559fn renderToken(2449fn renderToken(
2560 tree: *ast.Tree,2450 tree: *ast.Tree,
2561 stream: anytype,2451 ais: anytype,
2562 token_index: ast.TokenIndex,2452 token_index: ast.TokenIndex,
2563 indent: usize,
2564 start_col: *usize,
2565 space: Space,2453 space: Space,
2566) (@TypeOf(stream).Error || Error)!void {2454) (@TypeOf(ais.*).Error || Error)!void {
2567 return renderTokenOffset(tree, stream, token_index, indent, start_col, space, 0);2455 return renderTokenOffset(tree, ais, token_index, space, 0);
2568}2456}
25692457
2570fn renderDocComments(2458fn renderDocComments(
2571 tree: *ast.Tree,2459 tree: *ast.Tree,
2572 stream: anytype,2460 ais: anytype,
2573 node: anytype,2461 node: anytype,
2574 doc_comments: ?*ast.Node.DocComment,2462 doc_comments: ?*ast.Node.DocComment,
2575 indent: usize,2463) (@TypeOf(ais.*).Error || Error)!void {
2576 start_col: *usize,
2577) (@TypeOf(stream).Error || Error)!void {
2578 const comment = doc_comments orelse return;2464 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());
2580}2466}
25812467
2582fn renderDocCommentsToken(2468fn renderDocCommentsToken(
2583 tree: *ast.Tree,2469 tree: *ast.Tree,
2584 stream: anytype,2470 ais: anytype,
2585 comment: *ast.Node.DocComment,2471 comment: *ast.Node.DocComment,
2586 first_token: ast.TokenIndex,2472 first_token: ast.TokenIndex,
2587 indent: usize,2473) (@TypeOf(ais.*).Error || Error)!void {
2588 start_col: *usize,
2589) (@TypeOf(stream).Error || Error)!void {
2590 var tok_i = comment.first_line;2474 var tok_i = comment.first_line;
2591 while (true) : (tok_i += 1) {2475 while (true) : (tok_i += 1) {
2592 switch (tree.token_ids[tok_i]) {2476 switch (tree.token_ids[tok_i]) {
2593 .DocComment, .ContainerDocComment => {2477 .DocComment, .ContainerDocComment => {
2594 if (comment.first_line < first_token) {2478 if (comment.first_line < first_token) {
2595 try renderToken(tree, stream, tok_i, indent, start_col, Space.Newline);2479 try renderToken(tree, ais, tok_i, Space.Newline);
2596 try stream.writeByteNTimes(' ', indent);
2597 } else {2480 } else {
2598 try renderToken(tree, stream, tok_i, indent, start_col, Space.NoComment);2481 try renderToken(tree, ais, tok_i, Space.NoComment);
2599 try stream.writeAll("\n");2482 try ais.insertNewline();
2600 try stream.writeByteNTimes(' ', indent);
2601 }2483 }
2602 },2484 },
2603 .LineComment => continue,2485 .LineComment => continue,
...@@ -2669,41 +2551,10 @@ fn nodeCausesSliceOpSpace(base: *ast.Node) bool {...@@ -2669,41 +2551,10 @@ fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
2669 };2551 };
2670}2552}
26712553
2672/// A `std.io.OutStream` that returns whether the given character has been written to it.2554fn copyFixingWhitespace(ais: anytype, slice: []const u8) @TypeOf(ais.*).Error!void {
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 {
2704 for (slice) |byte| switch (byte) {2555 for (slice) |byte| switch (byte) {
2705 '\t' => try stream.writeAll(" "),2556 '\t' => try ais.writer().writeAll(" "),
2706 '\r' => {},2557 '\r' => {},
2707 else => try stream.writeByte(byte),2558 else => try ais.writer().writeByte(byte),
2708 };2559 };
2709}2560}
lib/std/zig/tokenizer.zig+2-1
...@@ -1175,6 +1175,7 @@ pub const Tokenizer = struct {...@@ -1175,6 +1175,7 @@ pub const Tokenizer = struct {
1175 },1175 },
1176 .num_dot_dec => switch (c) {1176 .num_dot_dec => switch (c) {
1177 '.' => {1177 '.' => {
1178 result.id = .IntegerLiteral;
1178 self.index -= 1;1179 self.index -= 1;
1179 state = .start;1180 state = .start;
1180 break;1181 break;
...@@ -1183,7 +1184,6 @@ pub const Tokenizer = struct {...@@ -1183,7 +1184,6 @@ pub const Tokenizer = struct {
1183 state = .float_exponent_unsigned;1184 state = .float_exponent_unsigned;
1184 },1185 },
1185 '0'...'9' => {1186 '0'...'9' => {
1186 result.id = .FloatLiteral;
1187 state = .float_fraction_dec;1187 state = .float_fraction_dec;
1188 },1188 },
1189 else => {1189 else => {
...@@ -1769,6 +1769,7 @@ test "tokenizer - number literals decimal" {...@@ -1769,6 +1769,7 @@ test "tokenizer - number literals decimal" {
1769 testTokenize("7", &[_]Token.Id{.IntegerLiteral});1769 testTokenize("7", &[_]Token.Id{.IntegerLiteral});
1770 testTokenize("8", &[_]Token.Id{.IntegerLiteral});1770 testTokenize("8", &[_]Token.Id{.IntegerLiteral});
1771 testTokenize("9", &[_]Token.Id{.IntegerLiteral});1771 testTokenize("9", &[_]Token.Id{.IntegerLiteral});
1772 testTokenize("1..", &[_]Token.Id{ .IntegerLiteral, .Ellipsis2 });
1772 testTokenize("0a", &[_]Token.Id{ .Invalid, .Identifier });1773 testTokenize("0a", &[_]Token.Id{ .Invalid, .Identifier });
1773 testTokenize("9b", &[_]Token.Id{ .Invalid, .Identifier });1774 testTokenize("9b", &[_]Token.Id{ .Invalid, .Identifier });
1774 testTokenize("1z", &[_]Token.Id{ .Invalid, .Identifier });1775 testTokenize("1z", &[_]Token.Id{ .Invalid, .Identifier });
src-self-hosted/Module.zig+165-54
...@@ -125,7 +125,7 @@ pub const Decl = struct {...@@ -125,7 +125,7 @@ pub const Decl = struct {
125 /// mapping them to an address in the output file.125 /// mapping them to an address in the output file.
126 /// Memory owned by this decl, using Module's allocator.126 /// Memory owned by this decl, using Module's allocator.
127 name: [*:0]const u8,127 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`.
129 /// Reference to externally owned memory.129 /// Reference to externally owned memory.
130 scope: *Scope,130 scope: *Scope,
131 /// The AST Node decl index or ZIR Inst index that contains this declaration.131 /// The AST Node decl index or ZIR Inst index that contains this declaration.
...@@ -217,9 +217,10 @@ pub const Decl = struct {...@@ -217,9 +217,10 @@ pub const Decl = struct {
217217
218 pub fn src(self: Decl) usize {218 pub fn src(self: Decl) usize {
219 switch (self.scope.tag) {219 switch (self.scope.tag) {
220 .file => {220 .container => {
221 const file = @fieldParentPtr(Scope.File, "base", self.scope);221 const container = @fieldParentPtr(Scope.Container, "base", self.scope);
222 const tree = file.contents.tree;222 const tree = container.file_scope.contents.tree;
223 // TODO Container should have it's own decls()
223 const decl_node = tree.root_node.decls()[self.src_index];224 const decl_node = tree.root_node.decls()[self.src_index];
224 return tree.token_locs[decl_node.firstToken()].start;225 return tree.token_locs[decl_node.firstToken()].start;
225 },226 },
...@@ -229,7 +230,7 @@ pub const Decl = struct {...@@ -229,7 +230,7 @@ pub const Decl = struct {
229 const src_decl = module.decls[self.src_index];230 const src_decl = module.decls[self.src_index];
230 return src_decl.inst.src;231 return src_decl.inst.src;
231 },232 },
232 .block => unreachable,233 .file, .block => unreachable,
233 .gen_zir => unreachable,234 .gen_zir => unreachable,
234 .local_val => unreachable,235 .local_val => unreachable,
235 .local_ptr => unreachable,236 .local_ptr => unreachable,
...@@ -359,6 +360,7 @@ pub const Scope = struct {...@@ -359,6 +360,7 @@ pub const Scope = struct {
359 .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,360 .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
360 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,361 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
361 .file => unreachable,362 .file => unreachable,
363 .container => unreachable,
362 }364 }
363 }365 }
364366
...@@ -368,15 +370,16 @@ pub const Scope = struct {...@@ -368,15 +370,16 @@ pub const Scope = struct {
368 return switch (self.tag) {370 return switch (self.tag) {
369 .block => self.cast(Block).?.decl,371 .block => self.cast(Block).?.decl,
370 .gen_zir => self.cast(GenZIR).?.decl,372 .gen_zir => self.cast(GenZIR).?.decl,
371 .local_val => return self.cast(LocalVal).?.gen_zir.decl,373 .local_val => self.cast(LocalVal).?.gen_zir.decl,
372 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl,374 .local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
373 .decl => self.cast(DeclAnalysis).?.decl,375 .decl => self.cast(DeclAnalysis).?.decl,
374 .zir_module => null,376 .zir_module => null,
375 .file => null,377 .file => null,
378 .container => null,
376 };379 };
377 }380 }
378381
379 /// Asserts the scope has a parent which is a ZIRModule or File and382 /// Asserts the scope has a parent which is a ZIRModule or Container and
380 /// returns it.383 /// returns it.
381 pub fn namespace(self: *Scope) *Scope {384 pub fn namespace(self: *Scope) *Scope {
382 switch (self.tag) {385 switch (self.tag) {
...@@ -385,7 +388,8 @@ pub const Scope = struct {...@@ -385,7 +388,8 @@ pub const Scope = struct {
385 .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,388 .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,
386 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,389 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,
387 .decl => return self.cast(DeclAnalysis).?.decl.scope,390 .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,
389 }393 }
390 }394 }
391395
...@@ -399,8 +403,9 @@ pub const Scope = struct {...@@ -399,8 +403,9 @@ pub const Scope = struct {
399 .local_val => unreachable,403 .local_val => unreachable,
400 .local_ptr => unreachable,404 .local_ptr => unreachable,
401 .decl => unreachable,405 .decl => unreachable,
406 .file => unreachable,
402 .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),407 .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
403 .file => return self.cast(File).?.fullyQualifiedNameHash(name),408 .container => return self.cast(Container).?.fullyQualifiedNameHash(name),
404 }409 }
405 }410 }
406411
...@@ -409,11 +414,12 @@ pub const Scope = struct {...@@ -409,11 +414,12 @@ pub const Scope = struct {
409 switch (self.tag) {414 switch (self.tag) {
410 .file => return self.cast(File).?.contents.tree,415 .file => return self.cast(File).?.contents.tree,
411 .zir_module => unreachable,416 .zir_module => unreachable,
412 .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(File).?.contents.tree,417 .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,
413 .block => return self.cast(Block).?.decl.scope.cast(File).?.contents.tree,418 .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,
414 .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(File).?.contents.tree,419 .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,
415 .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(File).?.contents.tree,420 .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
416 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(File).?.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,
417 }423 }
418 }424 }
419425
...@@ -427,13 +433,15 @@ pub const Scope = struct {...@@ -427,13 +433,15 @@ pub const Scope = struct {
427 .decl => unreachable,433 .decl => unreachable,
428 .zir_module => unreachable,434 .zir_module => unreachable,
429 .file => unreachable,435 .file => unreachable,
436 .container => unreachable,
430 };437 };
431 }438 }
432439
433 /// Asserts the scope has a parent which is a ZIRModule or File and440 /// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and
434 /// returns the sub_file_path field.441 /// returns the sub_file_path field.
435 pub fn subFilePath(base: *Scope) []const u8 {442 pub fn subFilePath(base: *Scope) []const u8 {
436 switch (base.tag) {443 switch (base.tag) {
444 .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,
437 .file => return @fieldParentPtr(File, "base", base).sub_file_path,445 .file => return @fieldParentPtr(File, "base", base).sub_file_path,
438 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,446 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
439 .block => unreachable,447 .block => unreachable,
...@@ -453,11 +461,13 @@ pub const Scope = struct {...@@ -453,11 +461,13 @@ pub const Scope = struct {
453 .local_val => unreachable,461 .local_val => unreachable,
454 .local_ptr => unreachable,462 .local_ptr => unreachable,
455 .decl => unreachable,463 .decl => unreachable,
464 .container => unreachable,
456 }465 }
457 }466 }
458467
459 pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {468 pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {
460 switch (base.tag) {469 switch (base.tag) {
470 .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),
461 .file => return @fieldParentPtr(File, "base", base).getSource(module),471 .file => return @fieldParentPtr(File, "base", base).getSource(module),
462 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),472 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
463 .gen_zir => unreachable,473 .gen_zir => unreachable,
...@@ -471,8 +481,9 @@ pub const Scope = struct {...@@ -471,8 +481,9 @@ pub const Scope = struct {
471 /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.481 /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
472 pub fn removeDecl(base: *Scope, child: *Decl) void {482 pub fn removeDecl(base: *Scope, child: *Decl) void {
473 switch (base.tag) {483 switch (base.tag) {
474 .file => return @fieldParentPtr(File, "base", base).removeDecl(child),484 .container => return @fieldParentPtr(Container, "base", base).removeDecl(child),
475 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),485 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
486 .file => unreachable,
476 .block => unreachable,487 .block => unreachable,
477 .gen_zir => unreachable,488 .gen_zir => unreachable,
478 .local_val => unreachable,489 .local_val => unreachable,
...@@ -499,6 +510,7 @@ pub const Scope = struct {...@@ -499,6 +510,7 @@ pub const Scope = struct {
499 .local_val => unreachable,510 .local_val => unreachable,
500 .local_ptr => unreachable,511 .local_ptr => unreachable,
501 .decl => unreachable,512 .decl => unreachable,
513 .container => unreachable,
502 }514 }
503 }515 }
504516
...@@ -515,6 +527,8 @@ pub const Scope = struct {...@@ -515,6 +527,8 @@ pub const Scope = struct {
515 zir_module,527 zir_module,
516 /// .zig source code.528 /// .zig source code.
517 file,529 file,
530 /// struct, enum or union, every .file contains one of these.
531 container,
518 block,532 block,
519 decl,533 decl,
520 gen_zir,534 gen_zir,
...@@ -522,6 +536,33 @@ pub const Scope = struct {...@@ -522,6 +536,33 @@ pub const Scope = struct {
522 local_ptr,536 local_ptr,
523 };537 };
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
525 pub const File = struct {566 pub const File = struct {
526 pub const base_tag: Tag = .file;567 pub const base_tag: Tag = .file;
527 base: Scope = Scope{ .tag = base_tag },568 base: Scope = Scope{ .tag = base_tag },
...@@ -544,8 +585,7 @@ pub const Scope = struct {...@@ -544,8 +585,7 @@ pub const Scope = struct {
544 loaded_success,585 loaded_success,
545 },586 },
546587
547 /// Direct children of the file.588 root_container: Container,
548 decls: ArrayListUnmanaged(*Decl),
549589
550 pub fn unload(self: *File, gpa: *Allocator) void {590 pub fn unload(self: *File, gpa: *Allocator) void {
551 switch (self.status) {591 switch (self.status) {
...@@ -569,20 +609,11 @@ pub const Scope = struct {...@@ -569,20 +609,11 @@ pub const Scope = struct {
569 }609 }
570610
571 pub fn deinit(self: *File, gpa: *Allocator) void {611 pub fn deinit(self: *File, gpa: *Allocator) void {
572 self.decls.deinit(gpa);612 self.root_container.deinit(gpa);
573 self.unload(gpa);613 self.unload(gpa);
574 self.* = undefined;614 self.* = undefined;
575 }615 }
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
586 pub fn dumpSrc(self: *File, src: usize) void {617 pub fn dumpSrc(self: *File, src: usize) void {
587 const loc = std.zig.findLineColumn(self.source.bytes, src);618 const loc = std.zig.findLineColumn(self.source.bytes, src);
588 std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });619 std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
...@@ -595,6 +626,7 @@ pub const Scope = struct {...@@ -595,6 +626,7 @@ pub const Scope = struct {
595 module.gpa,626 module.gpa,
596 self.sub_file_path,627 self.sub_file_path,
597 std.math.maxInt(u32),628 std.math.maxInt(u32),
629 null,
598 1,630 1,
599 0,631 0,
600 );632 );
...@@ -604,11 +636,6 @@ pub const Scope = struct {...@@ -604,11 +636,6 @@ pub const Scope = struct {
604 .bytes => |bytes| return bytes,636 .bytes => |bytes| return bytes,
605 }637 }
606 }638 }
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 }
612 };639 };
613640
614 pub const ZIRModule = struct {641 pub const ZIRModule = struct {
...@@ -697,6 +724,7 @@ pub const Scope = struct {...@@ -697,6 +724,7 @@ pub const Scope = struct {
697 module.gpa,724 module.gpa,
698 self.sub_file_path,725 self.sub_file_path,
699 std.math.maxInt(u32),726 std.math.maxInt(u32),
727 null,
700 1,728 1,
701 0,729 0,
702 );730 );
...@@ -861,7 +889,10 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -861,7 +889,10 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
861 .source = .{ .unloaded = {} },889 .source = .{ .unloaded = {} },
862 .contents = .{ .not_available = {} },890 .contents = .{ .not_available = {} },
863 .status = .never_loaded,891 .status = .never_loaded,
864 .decls = .{},892 .root_container = .{
893 .file_scope = root_scope,
894 .decls = .{},
895 },
865 };896 };
866 break :blk &root_scope.base;897 break :blk &root_scope.base;
867 } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) {898 } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) {
...@@ -969,7 +1000,7 @@ pub fn update(self: *Module) !void {...@@ -969,7 +1000,7 @@ pub fn update(self: *Module) !void {
969 // to force a refresh we unload now.1000 // to force a refresh we unload now.
970 if (self.root_scope.cast(Scope.File)) |zig_file| {1001 if (self.root_scope.cast(Scope.File)) |zig_file| {
971 zig_file.unload(self.gpa);1002 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) {
973 error.AnalysisFail => {1004 error.AnalysisFail => {
974 assert(self.totalErrorCount() != 0);1005 assert(self.totalErrorCount() != 0);
975 },1006 },
...@@ -1237,8 +1268,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1237,8 +1268,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1237 const tracy = trace(@src());1268 const tracy = trace(@src());
1238 defer tracy.end();1269 defer tracy.end();
12391270
1240 const file_scope = decl.scope.cast(Scope.File).?;1271 const container_scope = decl.scope.cast(Scope.Container).?;
1241 const tree = try self.getAstTree(file_scope);1272 const tree = try self.getAstTree(container_scope);
1242 const ast_node = tree.root_node.decls()[decl.src_index];1273 const ast_node = tree.root_node.decls()[decl.src_index];
1243 switch (ast_node.tag) {1274 switch (ast_node.tag) {
1244 .FnProto => {1275 .FnProto => {
...@@ -1698,10 +1729,12 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {...@@ -1698,10 +1729,12 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
1698 }1729 }
1699}1730}
17001731
1701fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {1732fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
1702 const tracy = trace(@src());1733 const tracy = trace(@src());
1703 defer tracy.end();1734 defer tracy.end();
17041735
1736 const root_scope = container_scope.file_scope;
1737
1705 switch (root_scope.status) {1738 switch (root_scope.status) {
1706 .never_loaded, .unloaded_success => {1739 .never_loaded, .unloaded_success => {
1707 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);1740 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 {...@@ -1743,25 +1776,25 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1743 }1776 }
1744}1777}
17451778
1746fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {1779fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
1747 const tracy = trace(@src());1780 const tracy = trace(@src());
1748 defer tracy.end();1781 defer tracy.end();
17491782
1750 // We may be analyzing it for the first time, or this may be1783 // We may be analyzing it for the first time, or this may be
1751 // an incremental update. This code handles both cases.1784 // an incremental update. This code handles both cases.
1752 const tree = try self.getAstTree(root_scope);1785 const tree = try self.getAstTree(container_scope);
1753 const decls = tree.root_node.decls();1786 const decls = tree.root_node.decls();
17541787
1755 try self.work_queue.ensureUnusedCapacity(decls.len);1788 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
1758 // Keep track of the decls that we expect to see in this file so that1791 // Keep track of the decls that we expect to see in this file so that
1759 // we know which ones have been deleted.1792 // we know which ones have been deleted.
1760 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);1793 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
1761 defer deleted_decls.deinit();1794 defer deleted_decls.deinit();
1762 try deleted_decls.ensureCapacity(root_scope.decls.items.len);1795 try deleted_decls.ensureCapacity(container_scope.decls.items().len);
1763 for (root_scope.decls.items) |file_decl| {1796 for (container_scope.decls.items()) |entry| {
1764 deleted_decls.putAssumeCapacityNoClobber(file_decl, {});1797 deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
1765 }1798 }
17661799
1767 for (decls) |src_decl, decl_i| {1800 for (decls) |src_decl, decl_i| {
...@@ -1773,7 +1806,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {...@@ -1773,7 +1806,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
17731806
1774 const name_loc = tree.token_locs[name_tok];1807 const name_loc = tree.token_locs[name_tok];
1775 const name = tree.tokenSliceLoc(name_loc);1808 const name = tree.tokenSliceLoc(name_loc);
1776 const name_hash = root_scope.fullyQualifiedNameHash(name);1809 const name_hash = container_scope.fullyQualifiedNameHash(name);
1777 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));1810 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
1778 if (self.decl_table.get(name_hash)) |decl| {1811 if (self.decl_table.get(name_hash)) |decl| {
1779 // Update the AST Node index of the decl, even if its contents are unchanged, it may1812 // 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 {...@@ -1789,6 +1822,9 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1789 try self.markOutdatedDecl(decl);1822 try self.markOutdatedDecl(decl);
1790 decl.contents_hash = contents_hash;1823 decl.contents_hash = contents_hash;
1791 } else switch (self.bin_file.tag) {1824 } else switch (self.bin_file.tag) {
1825 .coff => {
1826 // TODO Implement for COFF
1827 },
1792 .elf => if (decl.fn_link.elf.len != 0) {1828 .elf => if (decl.fn_link.elf.len != 0) {
1793 // TODO Look into detecting when this would be unnecessary by storing enough state1829 // TODO Look into detecting when this would be unnecessary by storing enough state
1794 // in `Decl` to notice that the line number did not change.1830 // in `Decl` to notice that the line number did not change.
...@@ -1801,8 +1837,8 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {...@@ -1801,8 +1837,8 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1801 }1837 }
1802 }1838 }
1803 } else {1839 } else {
1804 const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);1840 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1805 root_scope.decls.appendAssumeCapacity(new_decl);1841 container_scope.decls.putAssumeCapacity(new_decl, {});
1806 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {1842 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
1807 if (tree.token_ids[maybe_export_token] == .Keyword_export) {1843 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1808 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });1844 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
...@@ -1812,7 +1848,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {...@@ -1812,7 +1848,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1812 } else if (src_decl.castTag(.VarDecl)) |var_decl| {1848 } else if (src_decl.castTag(.VarDecl)) |var_decl| {
1813 const name_loc = tree.token_locs[var_decl.name_token];1849 const name_loc = tree.token_locs[var_decl.name_token];
1814 const name = tree.tokenSliceLoc(name_loc);1850 const name = tree.tokenSliceLoc(name_loc);
1815 const name_hash = root_scope.fullyQualifiedNameHash(name);1851 const name_hash = container_scope.fullyQualifiedNameHash(name);
1816 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));1852 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
1817 if (self.decl_table.get(name_hash)) |decl| {1853 if (self.decl_table.get(name_hash)) |decl| {
1818 // Update the AST Node index of the decl, even if its contents are unchanged, it may1854 // 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 {...@@ -1828,8 +1864,8 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1828 decl.contents_hash = contents_hash;1864 decl.contents_hash = contents_hash;
1829 }1865 }
1830 } else {1866 } else {
1831 const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);1867 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1832 root_scope.decls.appendAssumeCapacity(new_decl);1868 container_scope.decls.putAssumeCapacity(new_decl, {});
1833 if (var_decl.getExternExportToken()) |maybe_export_token| {1869 if (var_decl.getExternExportToken()) |maybe_export_token| {
1834 if (tree.token_ids[maybe_export_token] == .Keyword_export) {1870 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1835 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });1871 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
...@@ -1841,11 +1877,11 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {...@@ -1841,11 +1877,11 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1841 const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index});1877 const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index});
1842 defer self.gpa.free(name);1878 defer self.gpa.free(name);
18431879
1844 const name_hash = root_scope.fullyQualifiedNameHash(name);1880 const name_hash = container_scope.fullyQualifiedNameHash(name);
1845 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));1881 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);1883 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1848 root_scope.decls.appendAssumeCapacity(new_decl);1884 container_scope.decls.putAssumeCapacity(new_decl, {});
1849 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });1885 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1850 } else if (src_decl.castTag(.ContainerField)) |container_field| {1886 } else if (src_decl.castTag(.ContainerField)) |container_field| {
1851 log.err("TODO: analyze container field", .{});1887 log.err("TODO: analyze container field", .{});
...@@ -2047,12 +2083,14 @@ fn allocateNewDecl(...@@ -2047,12 +2083,14 @@ fn allocateNewDecl(
2047 .deletion_flag = false,2083 .deletion_flag = false,
2048 .contents_hash = contents_hash,2084 .contents_hash = contents_hash,
2049 .link = switch (self.bin_file.tag) {2085 .link = switch (self.bin_file.tag) {
2086 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
2050 .elf => .{ .elf = link.File.Elf.TextBlock.empty },2087 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
2051 .macho => .{ .macho = link.File.MachO.TextBlock.empty },2088 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
2052 .c => .{ .c = {} },2089 .c => .{ .c = {} },
2053 .wasm => .{ .wasm = {} },2090 .wasm => .{ .wasm = {} },
2054 },2091 },
2055 .fn_link = switch (self.bin_file.tag) {2092 .fn_link = switch (self.bin_file.tag) {
2093 .coff => .{ .coff = {} },
2056 .elf => .{ .elf = link.File.Elf.SrcFn.empty },2094 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
2057 .macho => .{ .macho = link.File.MachO.SrcFn.empty },2095 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
2058 .c => .{ .c = {} },2096 .c => .{ .c = {} },
...@@ -2591,6 +2629,72 @@ pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) In...@@ -2591,6 +2629,72 @@ pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) In
2591 return self.fail(scope, src, "TODO implement analysis of iserr", .{});2629 return self.fail(scope, src, "TODO implement analysis of iserr", .{});
2592}2630}
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
2594/// Asserts that lhs and rhs types are both numeric.2698/// Asserts that lhs and rhs types are both numeric.
2595pub fn cmpNumeric(2699pub fn cmpNumeric(
2596 self: *Module,2700 self: *Module,
...@@ -2801,6 +2905,12 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty...@@ -2801,6 +2905,12 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty
2801 prev_inst = next_inst;2905 prev_inst = next_inst;
2802 continue;2906 continue;
2803 }2907 }
2908 if (next_inst.ty.zigTypeTag() == .Undefined)
2909 continue;
2910 if (prev_inst.ty.zigTypeTag() == .Undefined) {
2911 prev_inst = next_inst;
2912 continue;
2913 }
2804 if (prev_inst.ty.isInt() and2914 if (prev_inst.ty.isInt() and
2805 next_inst.ty.isInt() and2915 next_inst.ty.isInt() and
2806 prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())2916 prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())
...@@ -3052,6 +3162,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err...@@ -3052,6 +3162,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err
3052 self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);3162 self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
3053 },3163 },
3054 .file => unreachable,3164 .file => unreachable,
3165 .container => unreachable,
3055 }3166 }
3056 return error.AnalysisFail;3167 return error.AnalysisFail;
3057}3168}
src-self-hosted/astgen.zig+74-26
...@@ -275,16 +275,16 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -275,16 +275,16 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
275 .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),275 .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
276 .For => return forExpr(mod, scope, rl, node.castTag(.For).?),276 .For => return forExpr(mod, scope, rl, node.castTag(.For).?),
277 .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),277 .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
278 .Slice => return rlWrap(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)),
278 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),279 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
279 .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),280 .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
281 .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
280282
281 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),283 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
282 .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),284 .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", .{}),
284 .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),285 .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
285 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),286 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
286 .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),287 .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", .{}),
288 .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),288 .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),
289 .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),289 .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
290 .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),290 .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!*...@@ -790,13 +790,31 @@ fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*
790}790}
791791
792fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst {792fn 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 {
793 const tree = scope.tree();811 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);814 const operand_ptr = try expr(mod, scope, .ref, lhs);
797 // TODO we could avoid an unnecessary copy if .iserr took a pointer815 // TODO we could avoid an unnecessary copy if .iserr, .isnull took a pointer
798 const err_union = try addZIRUnOp(mod, scope, src, .deref, err_union_ptr);816 const err_union = try addZIRUnOp(mod, scope, src, .deref, operand_ptr);
799 const cond = try addZIRUnOp(mod, scope, src, .iserr, err_union);817 const cond = try addZIRUnOp(mod, scope, src, cond_op, err_union);
800818
801 var block_scope: Scope.GenZIR = .{819 var block_scope: Scope.GenZIR = .{
802 .parent = scope,820 .parent = scope,
...@@ -825,55 +843,55 @@ fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch)...@@ -825,55 +843,55 @@ fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch)
825 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },843 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
826 };844 };
827845
828 var err_scope: Scope.GenZIR = .{846 var then_scope: Scope.GenZIR = .{
829 .parent = scope,847 .parent = scope,
830 .decl = block_scope.decl,848 .decl = block_scope.decl,
831 .arena = block_scope.arena,849 .arena = block_scope.arena,
832 .instructions = .{},850 .instructions = .{},
833 };851 };
834 defer err_scope.instructions.deinit(mod.gpa);852 defer then_scope.instructions.deinit(mod.gpa);
835853
836 var err_val_scope: Scope.LocalVal = undefined;854 var err_val_scope: Scope.LocalVal = undefined;
837 const err_sub_scope = blk: {855 const then_sub_scope = blk: {
838 const payload = node.payload orelse856 const payload = payload_node orelse
839 break :blk &err_scope.base;857 break :blk &then_scope.base;
840858
841 const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());859 const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());
842 if (mem.eql(u8, err_name, "_"))860 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);
846 err_val_scope = .{864 err_val_scope = .{
847 .parent = &err_scope.base,865 .parent = &then_scope.base,
848 .gen_zir = &err_scope,866 .gen_zir = &then_scope,
849 .name = err_name,867 .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),
851 };869 };
852 break :blk &err_val_scope.base;870 break :blk &err_val_scope.base;
853 };871 };
854872
855 _ = try addZIRInst(mod, &err_scope.base, src, zir.Inst.Break, .{873 _ = try addZIRInst(mod, &then_scope.base, src, zir.Inst.Break, .{
856 .block = block,874 .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),
858 }, .{});876 }, .{});
859877
860 var not_err_scope: Scope.GenZIR = .{878 var else_scope: Scope.GenZIR = .{
861 .parent = scope,879 .parent = scope,
862 .decl = block_scope.decl,880 .decl = block_scope.decl,
863 .arena = block_scope.arena,881 .arena = block_scope.arena,
864 .instructions = .{},882 .instructions = .{},
865 };883 };
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);886 const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand_ptr);
869 _ = try addZIRInst(mod, &not_err_scope.base, src, zir.Inst.Break, .{887 _ = try addZIRInst(mod, &else_scope.base, src, zir.Inst.Break, .{
870 .block = block,888 .block = block,
871 .operand = unwrapped_payload,889 .operand = unwrapped_payload,
872 }, .{});890 }, .{});
873891
874 condbr.positionals.then_body = .{ .instructions = try err_scope.arena.dupe(*zir.Inst, err_scope.instructions.items) };892 condbr.positionals.then_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items) };
875 condbr.positionals.else_body = .{ .instructions = try not_err_scope.arena.dupe(*zir.Inst, not_err_scope.instructions.items) };893 condbr.positionals.else_body = .{ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items) };
876 return rlWrap(mod, scope, rl, &block.base);894 return rlWrapPtr(mod, scope, rl, &block.base);
877}895}
878896
879/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.897/// 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...@@ -933,6 +951,36 @@ fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Array
933 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{}));951 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{}));
934}952}
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
936fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {984fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
937 const tree = scope.tree();985 const tree = scope.tree();
938 const src = tree.token_locs[node.rtoken].start;986 const src = tree.token_locs[node.rtoken].start;
src-self-hosted/codegen.zig+225-116
...@@ -59,14 +59,21 @@ pub const GenerateSymbolError = error{...@@ -59,14 +59,21 @@ pub const GenerateSymbolError = error{
59 AnalysisFail,59 AnalysisFail,
60};60};
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
62pub fn generateSymbol(71pub fn generateSymbol(
63 bin_file: *link.File,72 bin_file: *link.File,
64 src: usize,73 src: usize,
65 typed_value: TypedValue,74 typed_value: TypedValue,
66 code: *std.ArrayList(u8),75 code: *std.ArrayList(u8),
67 dbg_line: *std.ArrayList(u8),76 debug_output: DebugInfoOutput,
68 dbg_info: *std.ArrayList(u8),
69 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
70) GenerateSymbolError!Result {77) GenerateSymbolError!Result {
71 const tracy = trace(@src());78 const tracy = trace(@src());
72 defer tracy.end();79 defer tracy.end();
...@@ -76,70 +83,70 @@ pub fn generateSymbol(...@@ -76,70 +83,70 @@ pub fn generateSymbol(
76 switch (bin_file.options.target.cpu.arch) {83 switch (bin_file.options.target.cpu.arch) {
77 .wasm32 => unreachable, // has its own code path84 .wasm32 => unreachable, // has its own code path
78 .wasm64 => unreachable, // has its own code path85 .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),86 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, debug_output),
80 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),87 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
81 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),88 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, debug_output),
82 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),89 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, debug_output),
83 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),90 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, debug_output),
84 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),91 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, debug_output),
85 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),92 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, debug_output),
86 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),93 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, debug_output),
87 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),94 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
88 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),95 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, debug_output),
89 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),96 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, debug_output),
90 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),97 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, debug_output),
91 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),98 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, debug_output),
92 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),99 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, debug_output),
93 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),100 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, debug_output),
94 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),101 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, debug_output),
95 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),102 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, debug_output),
96 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),103 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, debug_output),
97 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),104 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, debug_output),
98 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),105 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, debug_output),
99 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),106 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, debug_output),
100 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),107 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, debug_output),
101 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),108 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, debug_output),
102 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),109 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, debug_output),
103 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),110 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, debug_output),
104 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),111 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, debug_output),
105 .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),112 .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, debug_output),
106 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),113 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, debug_output),
107 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),114 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, debug_output),
108 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),115 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, debug_output),
109 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),116 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
110 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),117 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, debug_output),
111 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),118 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, debug_output),
112 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),119 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, debug_output),
113 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),120 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, debug_output),
114 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),121 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, debug_output),
115 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),122 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, debug_output),
116 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),123 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, debug_output),
117 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),124 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, debug_output),
118 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),125 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, debug_output),
119 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),126 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, debug_output),
120 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),127 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, debug_output),
121 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),128 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, debug_output),
122 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),129 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, debug_output),
123 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),130 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, debug_output),
124 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),131 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, debug_output),
125 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),132 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, debug_output),
126 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),133 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, debug_output),
127 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),134 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, debug_output),
128 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),135 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, debug_output),
129 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."),136 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."),
130 }137 }
131 },138 },
132 .Array => {139 .Array => {
133 // TODO populate .debug_info for the array140 // TODO populate .debug_info for the array
134 if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {141 if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {
135 if (typed_value.ty.arraySentinel()) |sentinel| {142 if (typed_value.ty.sentinel()) |sentinel| {
136 try code.ensureCapacity(code.items.len + payload.data.len + 1);143 try code.ensureCapacity(code.items.len + payload.data.len + 1);
137 code.appendSliceAssumeCapacity(payload.data);144 code.appendSliceAssumeCapacity(payload.data);
138 const prev_len = code.items.len;145 const prev_len = code.items.len;
139 switch (try generateSymbol(bin_file, src, .{146 switch (try generateSymbol(bin_file, src, .{
140 .ty = typed_value.ty.elemType(),147 .ty = typed_value.ty.elemType(),
141 .val = sentinel,148 .val = sentinel,
142 }, code, dbg_line, dbg_info, dbg_info_type_relocs)) {149 }, code, debug_output)) {
143 .appended => return Result{ .appended = {} },150 .appended => return Result{ .appended = {} },
144 .externally_managed => |slice| {151 .externally_managed => |slice| {
145 code.appendSliceAssumeCapacity(slice);152 code.appendSliceAssumeCapacity(slice);
...@@ -239,9 +246,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -239,9 +246,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
239 target: *const std.Target,246 target: *const std.Target,
240 mod_fn: *const Module.Fn,247 mod_fn: *const Module.Fn,
241 code: *std.ArrayList(u8),248 code: *std.ArrayList(u8),
242 dbg_line: *std.ArrayList(u8),249 debug_output: DebugInfoOutput,
243 dbg_info: *std.ArrayList(u8),
244 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
245 err_msg: ?*ErrorMsg,250 err_msg: ?*ErrorMsg,
246 args: []MCValue,251 args: []MCValue,
247 ret_mcv: MCValue,252 ret_mcv: MCValue,
...@@ -419,9 +424,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -419,9 +424,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
419 src: usize,424 src: usize,
420 typed_value: TypedValue,425 typed_value: TypedValue,
421 code: *std.ArrayList(u8),426 code: *std.ArrayList(u8),
422 dbg_line: *std.ArrayList(u8),427 debug_output: DebugInfoOutput,
423 dbg_info: *std.ArrayList(u8),
424 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
425 ) GenerateSymbolError!Result {428 ) GenerateSymbolError!Result {
426 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;429 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 {...@@ -436,8 +439,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
436 try branch_stack.append(.{});439 try branch_stack.append(.{});
437440
438 const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: {441 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| {442 if (module_fn.owner_decl.scope.cast(Module.Scope.Container)) |container_scope| {
440 const tree = scope_file.contents.tree;443 const tree = container_scope.file_scope.contents.tree;
441 const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?;444 const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?;
442 const block = fn_proto.getBodyNode().?.castTag(.Block).?;445 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
443 const lbrace_src = tree.token_locs[block.lbrace].start;446 const lbrace_src = tree.token_locs[block.lbrace].start;
...@@ -457,9 +460,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -457,9 +460,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
457 .bin_file = bin_file,460 .bin_file = bin_file,
458 .mod_fn = module_fn,461 .mod_fn = module_fn,
459 .code = code,462 .code = code,
460 .dbg_line = dbg_line,463 .debug_output = debug_output,
461 .dbg_info = dbg_info,
462 .dbg_info_type_relocs = dbg_info_type_relocs,
463 .err_msg = null,464 .err_msg = null,
464 .args = undefined, // populated after `resolveCallingConventionValues`465 .args = undefined, // populated after `resolveCallingConventionValues`
465 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`466 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
...@@ -598,35 +599,50 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -598,35 +599,50 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
598 }599 }
599600
600 fn dbgSetPrologueEnd(self: *Self) InnerError!void {601 fn dbgSetPrologueEnd(self: *Self) InnerError!void {
601 try self.dbg_line.append(DW.LNS_set_prologue_end);602 switch (self.debug_output) {
602 try self.dbgAdvancePCAndLine(self.prev_di_src);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 }
603 }609 }
604610
605 fn dbgSetEpilogueBegin(self: *Self) InnerError!void {611 fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
606 try self.dbg_line.append(DW.LNS_set_epilogue_begin);612 switch (self.debug_output) {
607 try self.dbgAdvancePCAndLine(self.prev_di_src);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 }
608 }619 }
609620
610 fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void {621 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;
617 self.prev_di_src = src;622 self.prev_di_src = src;
618 self.prev_di_pc = self.code.items.len;623 self.prev_di_pc = self.code.items.len;
619 // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit624 switch (self.debug_output) {
620 // single-byte opcodes that add different numbers to both the PC and the line number625 .dwarf => |dbg_out| {
621 // at the same time.626 // TODO Look into improving the performance here by adding a token-index-to-line
622 try self.dbg_line.ensureCapacity(self.dbg_line.items.len + 11);627 // lookup table, and changing ir.Inst from storing byte offset to token. Currently
623 self.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc);628 // this involves scanning over the source code for newlines
624 leb128.writeULEB128(self.dbg_line.writer(), delta_pc) catch unreachable;629 // (but only from the previous byte offset to the new one).
625 if (delta_line != 0) {630 const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src);
626 self.dbg_line.appendAssumeCapacity(DW.LNS_advance_line);631 const delta_pc = self.code.items.len - self.prev_di_pc;
627 leb128.writeILEB128(self.dbg_line.writer(), delta_line) catch unreachable;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 => {},
628 }645 }
629 self.dbg_line.appendAssumeCapacity(DW.LNS_copy);
630 }646 }
631647
632 /// Asserts there is already capacity to insert into top branch inst_table.648 /// 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 {...@@ -654,18 +670,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
654 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,670 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
655 /// after codegen for this symbol is done.671 /// after codegen for this symbol is done.
656 fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {672 fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
657 assert(ty.hasCodeGenBits());673 switch (self.debug_output) {
658 const index = self.dbg_info.items.len;674 .dwarf => |dbg_out| {
659 try self.dbg_info.resize(index + 4); // DW.AT_type, DW.FORM_ref4675 assert(ty.hasCodeGenBits());
660676 const index = dbg_out.dbg_info.items.len;
661 const gop = try self.dbg_info_type_relocs.getOrPut(self.gpa, ty);677 try dbg_out.dbg_info.resize(index + 4); // DW.AT_type, DW.FORM_ref4
662 if (!gop.found_existing) {678
663 gop.entry.value = .{679 const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty);
664 .off = undefined,680 if (!gop.found_existing) {
665 .relocs = .{},681 gop.entry.value = .{
666 };682 .off = undefined,
683 .relocs = .{},
684 };
685 }
686 try gop.entry.value.relocs.append(self.gpa, @intCast(u32, index));
687 },
688 .none => {},
667 }689 }
668 try gop.entry.value.relocs.append(self.gpa, @intCast(u32, index));
669 }690 }
670691
671 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {692 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
...@@ -1258,14 +1279,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1258,14 +1279,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1258 self.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), &inst.base);1279 self.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), &inst.base);
1259 self.markRegUsed(reg);1280 self.markRegUsed(reg);
12601281
1261 try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len);1282 switch (self.debug_output) {
1262 self.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);1283 .dwarf => |dbg_out| {
1263 self.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT_location, DW.FORM_exprloc1284 try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 8 + name_with_null.len);
1264 1, // ULEB128 dwarf expression length1285 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
1265 reg.dwarfLocOp(),1286 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT_location, DW.FORM_exprloc
1266 });1287 1, // ULEB128 dwarf expression length
1267 try self.addDbgInfoTypeReloc(inst.base.ty); // DW.AT_type, DW.FORM_ref41288 reg.dwarfLocOp(),
1268 self.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string1289 });
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 }
1269 },1295 },
1270 else => {},1296 else => {},
1271 }1297 }
...@@ -1302,7 +1328,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1302,7 +1328,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13021328
1303 // Due to incremental compilation, how function calls are generated depends1329 // Due to incremental compilation, how function calls are generated depends
1304 // on linking.1330 // 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) {
1306 switch (arch) {1332 switch (arch) {
1307 .x86_64 => {1333 .x86_64 => {
1308 for (info.args) |mc_arg, arg_i| {1334 for (info.args) |mc_arg, arg_i| {
...@@ -1341,10 +1367,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1341,10 +1367,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1341 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {1367 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1342 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {1368 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1343 const func = func_val.func;1369 const func = func_val.func;
1344 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];1370
1345 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1371 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1346 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1372 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
1348 // ff 14 25 xx xx xx xx call [addr]1381 // ff 14 25 xx xx xx xx call [addr]
1349 try self.code.ensureCapacity(self.code.items.len + 7);1382 try self.code.ensureCapacity(self.code.items.len + 7);
1350 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });1383 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
...@@ -1362,10 +1395,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1362,10 +1395,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1362 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {1395 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1363 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {1396 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1364 const func = func_val.func;1397 const func = func_val.func;
1365 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];1398
1366 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1399 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1367 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1400 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
1370 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });1409 try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
1371 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());1410 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 {...@@ -1383,8 +1422,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1383 }1422 }
1384 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {1423 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1385 const func = func_val.func;1424 const func = func_val.func;
1386 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];1425 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1387 const got_addr = @intCast(u16, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * 2);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
1388 const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType();1433 const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType();
1389 // First, push the return address, then jump; if noreturn, don't bother with the first step1434 // First, push the return address, then jump; if noreturn, don't bother with the first step
1390 // TODO: implement packed struct -> u16 at comptime and move the bitcast here1435 // 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 {...@@ -1420,10 +1465,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1420 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {1465 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1421 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {1466 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1422 const func = func_val.func;1467 const func = func_val.func;
1423 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1424 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1468 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1425 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1469 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
1428 // TODO only works with leaf functions1478 // TODO only works with leaf functions
1429 // at the moment, which works fine for1479 // at the moment, which works fine for
...@@ -1443,7 +1493,57 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1443,7 +1493,57 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1443 }1493 }
1444 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {1494 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
1445 switch (arch) {1495 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 },
1447 .aarch64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for aarch64 arch", .{}),1547 .aarch64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for aarch64 arch", .{}),
1448 else => unreachable,1548 else => unreachable,
1449 }1549 }
...@@ -1933,7 +2033,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1933,7 +2033,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19332033
1934 if (mem.eql(u8, inst.asm_source, "syscall")) {2034 if (mem.eql(u8, inst.asm_source, "syscall")) {
1935 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });2035 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
1936 } else {2036 } else if (inst.asm_source.len != 0) {
1937 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});2037 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
1938 }2038 }
19392039
...@@ -2486,6 +2586,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2486,6 +2586,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2486 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];2586 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
2487 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;2587 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
2488 return MCValue{ .memory = got_addr };2588 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 };
2489 } else {2598 } else {
2490 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});2599 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
2491 }2600 }
src-self-hosted/codegen/c.zig+1-1
...@@ -85,7 +85,7 @@ fn genArray(file: *C, decl: *Decl) !void {...@@ -85,7 +85,7 @@ fn genArray(file: *C, decl: *Decl) !void {
85 const name = try map(file.base.allocator, mem.span(decl.name));85 const name = try map(file.base.allocator, mem.span(decl.name));
86 defer file.base.allocator.free(name);86 defer file.base.allocator.free(name);
87 if (tv.val.cast(Value.Payload.Bytes)) |payload|87 if (tv.val.cast(Value.Payload.Bytes)) |payload|
88 if (tv.ty.arraySentinel()) |sentinel|88 if (tv.ty.sentinel()) |sentinel|
89 if (sentinel.toUnsignedInt() == 0)89 if (sentinel.toUnsignedInt() == 0)
90 try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })90 try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
91 else91 else
src-self-hosted/link.zig+20-2
...@@ -34,6 +34,7 @@ pub const File = struct {...@@ -34,6 +34,7 @@ pub const File = struct {
3434
35 pub const LinkBlock = union {35 pub const LinkBlock = union {
36 elf: Elf.TextBlock,36 elf: Elf.TextBlock,
37 coff: Coff.TextBlock,
37 macho: MachO.TextBlock,38 macho: MachO.TextBlock,
38 c: void,39 c: void,
39 wasm: void,40 wasm: void,
...@@ -41,6 +42,7 @@ pub const File = struct {...@@ -41,6 +42,7 @@ pub const File = struct {
4142
42 pub const LinkFn = union {43 pub const LinkFn = union {
43 elf: Elf.SrcFn,44 elf: Elf.SrcFn,
45 coff: Coff.SrcFn,
44 macho: MachO.SrcFn,46 macho: MachO.SrcFn,
45 c: void,47 c: void,
46 wasm: ?Wasm.FnData,48 wasm: ?Wasm.FnData,
...@@ -66,7 +68,7 @@ pub const File = struct {...@@ -66,7 +68,7 @@ pub const File = struct {
66 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {68 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
67 switch (options.object_format) {69 switch (options.object_format) {
68 .unknown => unreachable,70 .unknown => unreachable,
69 .coff => return error.TODOImplementCoff,71 .coff, .pe => return Coff.openPath(allocator, dir, sub_path, options),
70 .elf => return Elf.openPath(allocator, dir, sub_path, options),72 .elf => return Elf.openPath(allocator, dir, sub_path, options),
71 .macho => return MachO.openPath(allocator, dir, sub_path, options),73 .macho => return MachO.openPath(allocator, dir, sub_path, options),
72 .wasm => return Wasm.openPath(allocator, dir, sub_path, options),74 .wasm => return Wasm.openPath(allocator, dir, sub_path, options),
...@@ -85,7 +87,7 @@ pub const File = struct {...@@ -85,7 +87,7 @@ pub const File = struct {
8587
86 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {88 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
87 switch (base.tag) {89 switch (base.tag) {
88 .elf, .macho => {90 .coff, .elf, .macho => {
89 if (base.file != null) return;91 if (base.file != null) return;
90 base.file = try dir.createFile(sub_path, .{92 base.file = try dir.createFile(sub_path, .{
91 .truncate = false,93 .truncate = false,
...@@ -112,6 +114,7 @@ pub const File = struct {...@@ -112,6 +114,7 @@ pub const File = struct {
112 /// after allocateDeclIndexes for any given Decl.114 /// after allocateDeclIndexes for any given Decl.
113 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {115 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
114 switch (base.tag) {116 switch (base.tag) {
117 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
115 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),118 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
116 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),119 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
117 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),120 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
...@@ -121,6 +124,7 @@ pub const File = struct {...@@ -121,6 +124,7 @@ pub const File = struct {
121124
122 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {125 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
123 switch (base.tag) {126 switch (base.tag) {
127 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
124 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),128 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
125 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),129 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
126 .c, .wasm => {},130 .c, .wasm => {},
...@@ -131,6 +135,7 @@ pub const File = struct {...@@ -131,6 +135,7 @@ pub const File = struct {
131 /// any given Decl.135 /// any given Decl.
132 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {136 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
133 switch (base.tag) {137 switch (base.tag) {
138 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
134 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),139 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
135 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),140 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
136 .c, .wasm => {},141 .c, .wasm => {},
...@@ -140,6 +145,7 @@ pub const File = struct {...@@ -140,6 +145,7 @@ pub const File = struct {
140 pub fn deinit(base: *File) void {145 pub fn deinit(base: *File) void {
141 if (base.file) |f| f.close();146 if (base.file) |f| f.close();
142 switch (base.tag) {147 switch (base.tag) {
148 .coff => @fieldParentPtr(Coff, "base", base).deinit(),
143 .elf => @fieldParentPtr(Elf, "base", base).deinit(),149 .elf => @fieldParentPtr(Elf, "base", base).deinit(),
144 .macho => @fieldParentPtr(MachO, "base", base).deinit(),150 .macho => @fieldParentPtr(MachO, "base", base).deinit(),
145 .c => @fieldParentPtr(C, "base", base).deinit(),151 .c => @fieldParentPtr(C, "base", base).deinit(),
...@@ -149,6 +155,11 @@ pub const File = struct {...@@ -149,6 +155,11 @@ pub const File = struct {
149155
150 pub fn destroy(base: *File) void {156 pub fn destroy(base: *File) void {
151 switch (base.tag) {157 switch (base.tag) {
158 .coff => {
159 const parent = @fieldParentPtr(Coff, "base", base);
160 parent.deinit();
161 base.allocator.destroy(parent);
162 },
152 .elf => {163 .elf => {
153 const parent = @fieldParentPtr(Elf, "base", base);164 const parent = @fieldParentPtr(Elf, "base", base);
154 parent.deinit();165 parent.deinit();
...@@ -177,6 +188,7 @@ pub const File = struct {...@@ -177,6 +188,7 @@ pub const File = struct {
177 defer tracy.end();188 defer tracy.end();
178189
179 try switch (base.tag) {190 try switch (base.tag) {
191 .coff => @fieldParentPtr(Coff, "base", base).flush(module),
180 .elf => @fieldParentPtr(Elf, "base", base).flush(module),192 .elf => @fieldParentPtr(Elf, "base", base).flush(module),
181 .macho => @fieldParentPtr(MachO, "base", base).flush(module),193 .macho => @fieldParentPtr(MachO, "base", base).flush(module),
182 .c => @fieldParentPtr(C, "base", base).flush(module),194 .c => @fieldParentPtr(C, "base", base).flush(module),
...@@ -186,6 +198,7 @@ pub const File = struct {...@@ -186,6 +198,7 @@ pub const File = struct {
186198
187 pub fn freeDecl(base: *File, decl: *Module.Decl) void {199 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
188 switch (base.tag) {200 switch (base.tag) {
201 .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
189 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),202 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
190 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),203 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
191 .c => unreachable,204 .c => unreachable,
...@@ -195,6 +208,7 @@ pub const File = struct {...@@ -195,6 +208,7 @@ pub const File = struct {
195208
196 pub fn errorFlags(base: *File) ErrorFlags {209 pub fn errorFlags(base: *File) ErrorFlags {
197 return switch (base.tag) {210 return switch (base.tag) {
211 .coff => @fieldParentPtr(Coff, "base", base).error_flags,
198 .elf => @fieldParentPtr(Elf, "base", base).error_flags,212 .elf => @fieldParentPtr(Elf, "base", base).error_flags,
199 .macho => @fieldParentPtr(MachO, "base", base).error_flags,213 .macho => @fieldParentPtr(MachO, "base", base).error_flags,
200 .c => return .{ .no_entry_point_found = false },214 .c => return .{ .no_entry_point_found = false },
...@@ -211,6 +225,7 @@ pub const File = struct {...@@ -211,6 +225,7 @@ pub const File = struct {
211 exports: []const *Module.Export,225 exports: []const *Module.Export,
212 ) !void {226 ) !void {
213 switch (base.tag) {227 switch (base.tag) {
228 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
214 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),229 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
215 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),230 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
216 .c => return {},231 .c => return {},
...@@ -220,6 +235,7 @@ pub const File = struct {...@@ -220,6 +235,7 @@ pub const File = struct {
220235
221 pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {236 pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
222 switch (base.tag) {237 switch (base.tag) {
238 .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
223 .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),239 .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
224 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),240 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
225 .c => unreachable,241 .c => unreachable,
...@@ -228,6 +244,7 @@ pub const File = struct {...@@ -228,6 +244,7 @@ pub const File = struct {
228 }244 }
229245
230 pub const Tag = enum {246 pub const Tag = enum {
247 coff,
231 elf,248 elf,
232 macho,249 macho,
233 c,250 c,
...@@ -239,6 +256,7 @@ pub const File = struct {...@@ -239,6 +256,7 @@ pub const File = struct {
239 };256 };
240257
241 pub const C = @import("link/C.zig");258 pub const C = @import("link/C.zig");
259 pub const Coff = @import("link/Coff.zig");
242 pub const Elf = @import("link/Elf.zig");260 pub const Elf = @import("link/Elf.zig");
243 pub const MachO = @import("link/MachO.zig");261 pub const MachO = @import("link/MachO.zig");
244 pub const Wasm = @import("link/Wasm.zig");262 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 {...@@ -1656,8 +1656,8 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
1656 try dbg_line_buffer.ensureCapacity(26);1656 try dbg_line_buffer.ensureCapacity(26);
16571657
1658 const line_off: u28 = blk: {1658 const line_off: u28 = blk: {
1659 if (decl.scope.cast(Module.Scope.File)) |scope_file| {1659 if (decl.scope.cast(Module.Scope.Container)) |container_scope| {
1660 const tree = scope_file.contents.tree;1660 const tree = container_scope.file_scope.contents.tree;
1661 const file_ast_decls = tree.root_node.decls();1661 const file_ast_decls = tree.root_node.decls();
1662 // TODO Look into improving the performance here by adding a token-index-to-line1662 // TODO Look into improving the performance here by adding a token-index-to-line
1663 // lookup table. Currently this involves scanning over the source code for newlines.1663 // 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 {...@@ -1735,7 +1735,13 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
1735 } else {1735 } else {
1736 // TODO implement .debug_info for global variables1736 // TODO implement .debug_info for global variables
1737 }1737 }
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 });
1739 const code = switch (res) {1745 const code = switch (res) {
1740 .externally_managed => |x| x,1746 .externally_managed => |x| x,
1741 .appended => code_buffer.items,1747 .appended => code_buffer.items,
...@@ -2157,8 +2163,8 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec...@@ -2157,8 +2163,8 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
2157 const tracy = trace(@src());2163 const tracy = trace(@src());
2158 defer tracy.end();2164 defer tracy.end();
21592165
2160 const scope_file = decl.scope.cast(Module.Scope.File).?;2166 const container_scope = decl.scope.cast(Module.Scope.Container).?;
2161 const tree = scope_file.contents.tree;2167 const tree = container_scope.file_scope.contents.tree;
2162 const file_ast_decls = tree.root_node.decls();2168 const file_ast_decls = tree.root_node.decls();
2163 // TODO Look into improving the performance here by adding a token-index-to-line2169 // TODO Look into improving the performance here by adding a token-index-to-line
2164 // lookup table. Currently this involves scanning over the source code for newlines.2170 // 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;...@@ -18,36 +18,66 @@ const File = link.File;
1818
19pub const base_tag: File.Tag = File.Tag.macho;19pub 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
21base: File,37base: File,
2238
23/// List of all load command headers that are in the file.39/// Table of all load commands
24/// We use it to track number and size of all commands needed by the header.40load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
25commands: std.ArrayListUnmanaged(macho.load_command) = std.ArrayListUnmanaged(macho.load_command){},41segment_cmd_index: ?u16 = null,
26command_file_offset: ?u64 = 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.46/// Table of all sections
29/// Same order as in the file.47sections: std.ArrayListUnmanaged(macho.section_64) = .{},
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){},
3348
34/// Offset (index) into __TEXT segment load command.49/// __TEXT segment sections
35text_segment_offset: ?u64 = null,50text_section_index: ?u16 = null,
36/// Offset (index) into __LINKEDIT segment load command.51cstring_section_index: ?u16 = null,
37linkedit_segment_offset: ?u664 = 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,
41entry_addr: ?u64 = null,60entry_addr: ?u64 = null,
4261
43/// The first 4GB of process' memory is reserved for the null (__PAGEZERO) segment.62/// Table of all symbols used.
44/// This is also the start address for our binary.63/// Internally references string table for names (which are optional).
45vm_start_address: u64 = 0x100000000,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
49error_flags: File.ErrorFlags = File.ErrorFlags{},74error_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
51/// `alloc_num / alloc_den` is the factor of padding when allocating.81/// `alloc_num / alloc_den` is the factor of padding when allocating.
52const alloc_num = 4;82const alloc_num = 4;
53const alloc_den = 3;83const alloc_den = 3;
...@@ -67,7 +97,23 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System";...@@ -67,7 +97,23 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
67const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";97const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
6898
69pub const TextBlock = struct {99pub 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 };
71};117};
72118
73pub const SrcFn = struct {119pub const SrcFn = struct {
...@@ -117,6 +163,12 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO...@@ -117,6 +163,12 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO
117/// Truncates the existing file contents and overwrites the contents.163/// Truncates the existing file contents and overwrites the contents.
118/// Returns an error if `file` is not already open with +read +write +seek abilities.164/// Returns an error if `file` is not already open with +read +write +seek abilities.
119fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO {165fn 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
120 var self: MachO = .{172 var self: MachO = .{
121 .base = .{173 .base = .{
122 .file = file,174 .file = file,
...@@ -127,104 +179,15 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach...@@ -127,104 +179,15 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach
127 };179 };
128 errdefer self.deinit();180 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
156 try self.populateMissingMetadata();182 try self.populateMissingMetadata();
157183
158 return self;184 return self;
159}185}
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
210pub fn flush(self: *MachO, module: *Module) !void {187pub 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
226 switch (self.base.options.output_mode) {188 switch (self.base.options.output_mode) {
227 .Exe => {189 .Exe => {
190 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
228 {191 {
229 // Specify path to dynamic linker dyld192 // Specify path to dynamic linker dyld
230 const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH));193 const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH));
...@@ -235,18 +198,14 @@ pub fn flush(self: *MachO, module: *Module) !void {...@@ -235,18 +198,14 @@ pub fn flush(self: *MachO, module: *Module) !void {
235 .name = @sizeOf(macho.dylinker_command),198 .name = @sizeOf(macho.dylinker_command),
236 },199 },
237 };200 };
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);
246 try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);205 try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);
247206
248 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);207 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);
249 self.command_file_offset.? += cmdsize;208 last_cmd_offset += cmdsize;
250 }209 }
251210
252 {211 {
...@@ -268,21 +227,44 @@ pub fn flush(self: *MachO, module: *Module) !void {...@@ -268,21 +227,44 @@ pub fn flush(self: *MachO, module: *Module) !void {
268 .dylib = dylib,227 .dylib = dylib,
269 },228 },
270 };229 };
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);
279 try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);234 try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);
280235
281 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);236 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();
283 }264 }
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);
284 },267 },
285 .Obj => return error.TODOImplementWritingObjFiles,
286 .Lib => return error.TODOImplementWritingLibFiles,268 .Lib => return error.TODOImplementWritingLibFiles,
287 }269 }
288270
...@@ -297,14 +279,87 @@ pub fn flush(self: *MachO, module: *Module) !void {...@@ -297,14 +279,87 @@ pub fn flush(self: *MachO, module: *Module) !void {
297}279}
298280
299pub fn deinit(self: *MachO) void {281pub fn deinit(self: *MachO) void {
300 self.commands.deinit(self.base.allocator);282 self.offset_table.deinit(self.base.allocator);
301 self.segments.deinit(self.base.allocator);283 self.string_table.deinit(self.base.allocator);
284 self.symbol_table.deinit(self.base.allocator);
302 self.sections.deinit(self.base.allocator);285 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;
303}310}
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
309pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}364pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
310365
...@@ -313,51 +368,191 @@ pub fn updateDeclExports(...@@ -313,51 +368,191 @@ pub fn updateDeclExports(
313 module: *Module,368 module: *Module,
314 decl: *const Module.Decl,369 decl: *const Module.Decl,
315 exports: []const *Module.Export,370 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
318pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}387pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
319388
320pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {389pub 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;
322}391}
323392
324pub fn populateMissingMetadata(self: *MachO) !void {393pub fn populateMissingMetadata(self: *MachO) !void {
325 if (self.text_segment_offset == null) {394 if (self.segment_cmd_index == null) {
326 self.text_segment_offset = @intCast(u64, self.segments.items.len);395 self.segment_cmd_index = @intCast(u16, self.load_commands.items.len);
327 const file_size = alignSize(u64, self.base.options.program_code_size_hint, 0x1000);396 try self.load_commands.append(self.base.allocator, .{
328 log.debug("vmsize/filesize = {}", .{file_size});397 .Segment = .{
329 const file_offset = 0;398 .cmd = macho.LC_SEGMENT_64,
330 const vm_address = self.vm_start_address; // the end of __PAGEZERO segment in VM399 .cmdsize = @sizeOf(macho.segment_command_64),
331 const protection = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;400 .segname = makeStaticString(""),
332 const cmdsize = commandSize(@sizeOf(macho.segment_command_64));401 .vmaddr = 0,
333 const text_segment = .{402 .vmsize = 0,
334 .cmd = macho.LC_SEGMENT_64,403 .fileoff = 0,
335 .cmdsize = cmdsize,404 .filesize = 0,
336 .segname = makeString("__TEXT"),405 .maxprot = 0,
337 .vmaddr = vm_address,406 .initprot = 0,
338 .vmsize = file_size,407 .nsects = 0,
339 .fileoff = 0, // __TEXT segment *always* starts at 0 file offset408 .flags = 0,
340 .filesize = 0, //file_size,409 },
341 .maxprot = protection,410 });
342 .initprot = protection,411 self.cmd_table_dirty = true;
343 .nsects = 0,412 }
344 .flags = 0,413 if (self.symtab_cmd_index == null) {
345 };414 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
346 try self.commands.append(self.base.allocator, .{415 try self.load_commands.append(self.base.allocator, .{
347 .cmd = macho.LC_SEGMENT_64,416 .Symtab = .{
348 .cmdsize = cmdsize,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,
349 });452 });
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;
351 }536 }
537
538 return addr;
352}539}
353540
354fn makeString(comptime bytes: []const u8) [16]u8 {541fn makeStaticString(comptime bytes: []const u8) [16]u8 {
355 var buf = [_]u8{0} ** 16;542 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");
357 mem.copy(u8, buf[0..], bytes);544 mem.copy(u8, buf[0..], bytes);
358 return buf;545 return buf;
359}546}
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
361fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int {556fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int {
362 const size = @intCast(Int, min_size);557 const size = @intCast(Int, min_size);
363 if (size % alignment == 0) return size;558 if (size % alignment == 0) return size;
...@@ -370,7 +565,7 @@ fn commandSize(min_size: anytype) u32 {...@@ -370,7 +565,7 @@ fn commandSize(min_size: anytype) u32 {
370 return alignSize(u32, min_size, @sizeOf(u64));565 return alignSize(u32, min_size, @sizeOf(u64));
371}566}
372567
373fn addPadding(self: *MachO, size: u32, file_offset: u64) !void {568fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
374 if (size == 0) return;569 if (size == 0) return;
375570
376 const buf = try self.base.allocator.alloc(u8, size);571 const buf = try self.base.allocator.alloc(u8, size);
...@@ -380,3 +575,151 @@ fn addPadding(self: *MachO, size: u32, file_offset: u64) !void {...@@ -380,3 +575,151 @@ fn addPadding(self: *MachO, size: u32, file_offset: u64) !void {
380575
381 try self.base.file.?.pwriteAll(buf, file_offset);576 try self.base.file.?.pwriteAll(buf, file_offset);
382}577}
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 =...@@ -153,8 +153,8 @@ const usage_build_generic =
153 \\ elf Executable and Linking Format153 \\ elf Executable and Linking Format
154 \\ c Compile to C source code154 \\ c Compile to C source code
155 \\ wasm WebAssembly155 \\ wasm WebAssembly
156 \\ pe Portable Executable (Windows)
156 \\ coff (planned) Common Object File Format (Windows)157 \\ coff (planned) Common Object File Format (Windows)
157 \\ pe (planned) Portable Executable (Windows)
158 \\ macho (planned) macOS relocatables158 \\ macho (planned) macOS relocatables
159 \\ hex (planned) Intel IHEX159 \\ hex (planned) Intel IHEX
160 \\ raw (planned) Dump machine code directly160 \\ raw (planned) Dump machine code directly
...@@ -451,7 +451,7 @@ fn buildOutputType(...@@ -451,7 +451,7 @@ fn buildOutputType(
451 } else if (mem.eql(u8, ofmt, "coff")) {451 } else if (mem.eql(u8, ofmt, "coff")) {
452 break :blk .coff;452 break :blk .coff;
453 } else if (mem.eql(u8, ofmt, "pe")) {453 } else if (mem.eql(u8, ofmt, "pe")) {
454 break :blk .coff;454 break :blk .pe;
455 } else if (mem.eql(u8, ofmt, "macho")) {455 } else if (mem.eql(u8, ofmt, "macho")) {
456 break :blk .macho;456 break :blk .macho;
457 } else if (mem.eql(u8, ofmt, "wasm")) {457 } else if (mem.eql(u8, ofmt, "wasm")) {
...@@ -524,17 +524,19 @@ fn buildOutputType(...@@ -524,17 +524,19 @@ fn buildOutputType(
524 try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});524 try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});
525 continue;525 continue;
526 }) |line| {526 }) |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")) {
528 if (output_mode == .Exe) {530 if (output_mode == .Exe) {
529 try module.makeBinFileWritable();531 try module.makeBinFileWritable();
530 }532 }
531 try updateModule(gpa, &module, zir_out_path);533 try updateModule(gpa, &module, zir_out_path);
532 } else if (mem.eql(u8, line, "exit")) {534 } else if (mem.eql(u8, actual_line, "exit")) {
533 break;535 break;
534 } else if (mem.eql(u8, line, "help")) {536 } else if (mem.eql(u8, actual_line, "help")) {
535 try stderr.writeAll(repl_help);537 try stderr.writeAll(repl_help);
536 } else {538 } else {
537 try stderr.print("unknown command: {}\n", .{line});539 try stderr.print("unknown command: {}\n", .{actual_line});
538 }540 }
539 } else {541 } else {
540 break;542 break;
...@@ -742,6 +744,7 @@ const FmtError = error{...@@ -742,6 +744,7 @@ const FmtError = error{
742 LinkQuotaExceeded,744 LinkQuotaExceeded,
743 FileBusy,745 FileBusy,
744 EndOfStream,746 EndOfStream,
747 Unseekable,
745 NotOpenForWriting,748 NotOpenForWriting,
746} || fs.File.OpenError;749} || fs.File.OpenError;
747750
...@@ -805,7 +808,13 @@ fn fmtPathFile(...@@ -805,7 +808,13 @@ fn fmtPathFile(
805 if (stat.kind == .Directory)808 if (stat.kind == .Directory)
806 return error.IsDir;809 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) {
809 error.ConnectionResetByPeer => unreachable,818 error.ConnectionResetByPeer => unreachable,
810 error.ConnectionTimedOut => unreachable,819 error.ConnectionTimedOut => unreachable,
811 error.NotOpenForReading => unreachable,820 error.NotOpenForReading => unreachable,
...@@ -839,7 +848,8 @@ fn fmtPathFile(...@@ -839,7 +848,8 @@ fn fmtPathFile(
839 // As a heuristic, we make enough capacity for the same as the input source.848 // As a heuristic, we make enough capacity for the same as the input source.
840 try fmt.out_buffer.ensureCapacity(source_code.len);849 try fmt.out_buffer.ensureCapacity(source_code.len);
841 fmt.out_buffer.items.len = 0;850 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);
843 if (!anything_changed)853 if (!anything_changed)
844 return; // Good thing we didn't waste any file system access on this.854 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: [...@@ -615,7 +615,6 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [
615 error.NotOpenForWriting => unreachable,615 error.NotOpenForWriting => unreachable,
616 error.NotOpenForReading => unreachable,616 error.NotOpenForReading => unreachable,
617 error.Unexpected => return .Unexpected,617 error.Unexpected => return .Unexpected,
618 error.EndOfStream => return .EndOfFile,
619 error.IsDir => return .IsDir,618 error.IsDir => return .IsDir,
620 error.ConnectionResetByPeer => unreachable,619 error.ConnectionResetByPeer => unreachable,
621 error.ConnectionTimedOut => unreachable,620 error.ConnectionTimedOut => unreachable,
src-self-hosted/type.zig+98-20
...@@ -163,7 +163,7 @@ pub const Type = extern union {...@@ -163,7 +163,7 @@ pub const Type = extern union {
163 // Hot path for common case:163 // Hot path for common case:
164 if (a.castPointer()) |a_payload| {164 if (a.castPointer()) |a_payload| {
165 if (b.castPointer()) |b_payload| {165 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);
167 }167 }
168 }168 }
169 const is_slice_a = isSlice(a);169 const is_slice_a = isSlice(a);
...@@ -189,10 +189,10 @@ pub const Type = extern union {...@@ -189,10 +189,10 @@ pub const Type = extern union {
189 .Array => {189 .Array => {
190 if (a.arrayLen() != b.arrayLen())190 if (a.arrayLen() != b.arrayLen())
191 return false;191 return false;
192 if (a.elemType().eql(b.elemType()))192 if (!a.elemType().eql(b.elemType()))
193 return false;193 return false;
194 const sentinel_a = a.arraySentinel();194 const sentinel_a = a.sentinel();
195 const sentinel_b = b.arraySentinel();195 const sentinel_b = b.sentinel();
196 if (sentinel_a) |sa| {196 if (sentinel_a) |sa| {
197 if (sentinel_b) |sb| {197 if (sentinel_b) |sb| {
198 return sa.eql(sb);198 return sa.eql(sb);
...@@ -501,9 +501,9 @@ pub const Type = extern union {...@@ -501,9 +501,9 @@ pub const Type = extern union {
501 .noreturn,501 .noreturn,
502 => return out_stream.writeAll(@tagName(t)),502 => return out_stream.writeAll(@tagName(t)),
503503
504 .enum_literal => return out_stream.writeAll("@TypeOf(.EnumLiteral)"),504 .enum_literal => return out_stream.writeAll("@Type(.EnumLiteral)"),
505 .@"null" => return out_stream.writeAll("@TypeOf(null)"),505 .@"null" => return out_stream.writeAll("@Type(.Null)"),
506 .@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"),506 .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"),
507507
508 .@"anyframe" => return out_stream.writeAll("anyframe"),508 .@"anyframe" => return out_stream.writeAll("anyframe"),
509 .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),509 .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
...@@ -630,8 +630,8 @@ pub const Type = extern union {...@@ -630,8 +630,8 @@ pub const Type = extern union {
630 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);630 const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
631 if (payload.sentinel) |some| switch (payload.size) {631 if (payload.sentinel) |some| switch (payload.size) {
632 .One, .C => unreachable,632 .One, .C => unreachable,
633 .Many => try out_stream.writeAll("[*:{}]"),633 .Many => try out_stream.print("[*:{}]", .{some}),
634 .Slice => try out_stream.writeAll("[:{}]"),634 .Slice => try out_stream.print("[:{}]", .{some}),
635 } else switch (payload.size) {635 } else switch (payload.size) {
636 .One => try out_stream.writeAll("*"),636 .One => try out_stream.writeAll("*"),
637 .Many => try out_stream.writeAll("[*]"),637 .Many => try out_stream.writeAll("[*]"),
...@@ -1341,6 +1341,81 @@ pub const Type = extern union {...@@ -1341,6 +1341,81 @@ pub const Type = extern union {
1341 };1341 };
1342 }1342 }
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
1344 /// Asserts that the type is an optional1419 /// Asserts that the type is an optional
1345 pub fn isPtrLikeOptional(self: Type) bool {1420 pub fn isPtrLikeOptional(self: Type) bool {
1346 switch (self.tag()) {1421 switch (self.tag()) {
...@@ -1585,8 +1660,8 @@ pub const Type = extern union {...@@ -1585,8 +1660,8 @@ pub const Type = extern union {
1585 };1660 };
1586 }1661 }
15871662
1588 /// Asserts the type is an array or vector.1663 /// Asserts the type is an array, pointer or vector.
1589 pub fn arraySentinel(self: Type) ?Value {1664 pub fn sentinel(self: Type) ?Value {
1590 return switch (self.tag()) {1665 return switch (self.tag()) {
1591 .u8,1666 .u8,
1592 .i8,1667 .i8,
...@@ -1626,16 +1701,8 @@ pub const Type = extern union {...@@ -1626,16 +1701,8 @@ pub const Type = extern union {
1626 .fn_naked_noreturn_no_args,1701 .fn_naked_noreturn_no_args,
1627 .fn_ccc_void_no_args,1702 .fn_ccc_void_no_args,
1628 .function,1703 .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,
1636 .const_slice,1704 .const_slice,
1637 .mut_slice,1705 .mut_slice,
1638 .single_const_pointer_to_comptime_int,
1639 .const_slice_u8,1706 .const_slice_u8,
1640 .int_unsigned,1707 .int_unsigned,
1641 .int_signed,1708 .int_signed,
...@@ -1651,7 +1718,18 @@ pub const Type = extern union {...@@ -1651,7 +1718,18 @@ pub const Type = extern union {
1651 .error_set_single,1718 .error_set_single,
1652 => unreachable,1719 => 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,
1655 .array_sentinel => return self.cast(Payload.ArraySentinel).?.sentinel,1733 .array_sentinel => return self.cast(Payload.ArraySentinel).?.sentinel,
1656 .array_u8_sentinel_0 => return Value.initTag(.zero),1734 .array_u8_sentinel_0 => return Value.initTag(.zero),
1657 };1735 };
src-self-hosted/value.zig+3-3
...@@ -301,15 +301,15 @@ pub const Value = extern union {...@@ -301,15 +301,15 @@ pub const Value = extern union {
301 .comptime_int_type => return out_stream.writeAll("comptime_int"),301 .comptime_int_type => return out_stream.writeAll("comptime_int"),
302 .comptime_float_type => return out_stream.writeAll("comptime_float"),302 .comptime_float_type => return out_stream.writeAll("comptime_float"),
303 .noreturn_type => return out_stream.writeAll("noreturn"),303 .noreturn_type => return out_stream.writeAll("noreturn"),
304 .null_type => return out_stream.writeAll("@TypeOf(null)"),304 .null_type => return out_stream.writeAll("@Type(.Null)"),
305 .undefined_type => return out_stream.writeAll("@TypeOf(undefined)"),305 .undefined_type => return out_stream.writeAll("@Type(.Undefined)"),
306 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),306 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
307 .fn_void_no_args_type => return out_stream.writeAll("fn() void"),307 .fn_void_no_args_type => return out_stream.writeAll("fn() void"),
308 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),308 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
309 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),309 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
310 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),310 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
311 .const_slice_u8_type => return out_stream.writeAll("[]const u8"),311 .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)"),
313 .anyframe_type => return out_stream.writeAll("anyframe"),313 .anyframe_type => return out_stream.writeAll("anyframe"),
314314
315 .null_value => return out_stream.writeAll("null"),315 .null_value => return out_stream.writeAll("null"),
src-self-hosted/zir.zig+23-1
...@@ -231,6 +231,10 @@ pub const Inst = struct {...@@ -231,6 +231,10 @@ pub const Inst = struct {
231 const_slice_type,231 const_slice_type,
232 /// Create a pointer type with attributes232 /// Create a pointer type with attributes
233 ptr_type,233 ptr_type,
234 /// Slice operation `array_ptr[start..end:sentinel]`
235 slice,
236 /// Slice operation with just start `lhs[rhs..]`
237 slice_start,
234 /// Write a value to a pointer. For loading, see `deref`.238 /// Write a value to a pointer. For loading, see `deref`.
235 store,239 store,
236 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.240 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
...@@ -343,6 +347,7 @@ pub const Inst = struct {...@@ -343,6 +347,7 @@ pub const Inst = struct {
343 .xor,347 .xor,
344 .error_union_type,348 .error_union_type,
345 .merge_error_sets,349 .merge_error_sets,
350 .slice_start,
346 => BinOp,351 => BinOp,
347352
348 .block,353 .block,
...@@ -380,6 +385,7 @@ pub const Inst = struct {...@@ -380,6 +385,7 @@ pub const Inst = struct {
380 .ptr_type => PtrType,385 .ptr_type => PtrType,
381 .enum_literal => EnumLiteral,386 .enum_literal => EnumLiteral,
382 .error_set => ErrorSet,387 .error_set => ErrorSet,
388 .slice => Slice,
383 };389 };
384 }390 }
385391
...@@ -481,6 +487,8 @@ pub const Inst = struct {...@@ -481,6 +487,8 @@ pub const Inst = struct {
481 .error_union_type,487 .error_union_type,
482 .bitnot,488 .bitnot,
483 .error_set,489 .error_set,
490 .slice,
491 .slice_start,
484 => false,492 => false,
485493
486 .@"break",494 .@"break",
...@@ -961,6 +969,20 @@ pub const Inst = struct {...@@ -961,6 +969,20 @@ pub const Inst = struct {
961 },969 },
962 kw_args: struct {},970 kw_args: struct {},
963 };971 };
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 };
964};986};
965987
966pub const ErrorMsg = struct {988pub const ErrorMsg = struct {
...@@ -2574,7 +2596,7 @@ const EmitZIR = struct {...@@ -2574,7 +2596,7 @@ const EmitZIR = struct {
2574 var len_pl = Value.Payload.Int_u64{ .int = ty.arrayLen() };2596 var len_pl = Value.Payload.Int_u64{ .int = ty.arrayLen() };
2575 const len = Value.initPayload(&len_pl.base);2597 const len = Value.initPayload(&len_pl.base);
25762598
2577 const inst = if (ty.arraySentinel()) |sentinel| blk: {2599 const inst = if (ty.sentinel()) |sentinel| blk: {
2578 const inst = try self.arena.allocator.create(Inst.ArrayTypeSentinel);2600 const inst = try self.arena.allocator.create(Inst.ArrayTypeSentinel);
2579 inst.* = .{2601 inst.* = .{
2580 .base = .{2602 .base = .{
src-self-hosted/zir_sema.zig+24
...@@ -132,6 +132,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -132,6 +132,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
132 .error_union_type => return analyzeInstErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?),132 .error_union_type => return analyzeInstErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?),
133 .anyframe_type => return analyzeInstAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?),133 .anyframe_type => return analyzeInstAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?),
134 .error_set => return analyzeInstErrorSet(mod, scope, old_inst.castTag(.error_set).?),134 .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).?),
135 }137 }
136}138}
137139
...@@ -1172,6 +1174,22 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne...@@ -1172,6 +1174,22 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
1172 return mod.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});1174 return mod.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
1173}1175}
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
1175fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {1193fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1176 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{});1194 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{});
1177}1195}
...@@ -1239,6 +1257,12 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn...@@ -1239,6 +1257,12 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn
12391257
1240 if (casted_lhs.value()) |lhs_val| {1258 if (casted_lhs.value()) |lhs_val| {
1241 if (casted_rhs.value()) |rhs_val| {1259 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 }
1242 return analyzeInstComptimeOp(mod, scope, scalar_type, inst, lhs_val, rhs_val);1266 return analyzeInstComptimeOp(mod, scope, scalar_type, inst, lhs_val, rhs_val);
1243 }1267 }
1244 }1268 }
src/analyze.cpp+1-1
...@@ -1810,7 +1810,7 @@ Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) {...@@ -1810,7 +1810,7 @@ Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) {
1810ZigType *get_auto_err_set_type(CodeGen *g, ZigFn *fn_entry) {1810ZigType *get_auto_err_set_type(CodeGen *g, ZigFn *fn_entry) {
1811 ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);1811 ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
1812 buf_resize(&err_set_type->name, 0);1812 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));
1814 err_set_type->data.error_set.err_count = 0;1814 err_set_type->data.error_set.err_count = 0;
1815 err_set_type->data.error_set.errors = nullptr;1815 err_set_type->data.error_set.errors = nullptr;
1816 err_set_type->data.error_set.infer_fn = fn_entry;1816 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,...@@ -15341,9 +15341,14 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
15341 ZigType *array_type = actual_type->data.pointer.child_type;15341 ZigType *array_type = actual_type->data.pointer.child_type;
15342 bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 015342 bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0
15343 || !actual_type->data.pointer.is_const);15343 || !actual_type->data.pointer.is_const);
15344
15344 if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,15345 if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
15345 array_type->data.array.child_type, source_node,15346 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))))
15347 {15352 {
15348 // If the pointers both have ABI align, it works.15353 // If the pointers both have ABI align, it works.
15349 // Or if the array length is 0, alignment doesn't matter.15354 // 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...@@ -22830,167 +22835,9 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
22830 bool ptr_is_volatile = false;22835 bool ptr_is_volatile = false;
22831 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, const_val,22836 return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, const_val,
22832 err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);22837 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 }
22991 } else {22838 } else {
22992 ir_add_error(ira, &field_ptr_instruction->base.base,22839 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)));
22994 return ira->codegen->invalid_inst_gen;22841 return ira->codegen->invalid_inst_gen;
22995 }22842 }
22996 } else if (field_ptr_instruction->initializing) {22843 } else if (field_ptr_instruction->initializing) {
...@@ -26747,7 +26594,7 @@ static IrInstGen *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstSrcCmpxch...@@ -26747,7 +26594,7 @@ static IrInstGen *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstSrcCmpxch
2674726594
26748 if (operand_type->id == ZigTypeIdFloat) {26595 if (operand_type->id == ZigTypeIdFloat) {
26749 ir_add_error(ira, &instruction->type_value->child->base,26596 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)));
26751 return ira->codegen->invalid_inst_gen;26598 return ira->codegen->invalid_inst_gen;
26752 }26599 }
2675326600
...@@ -30402,7 +30249,7 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstGen *op) {...@@ -30402,7 +30249,7 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstGen *op) {
30402 return ira->codegen->builtin_types.entry_invalid;30249 return ira->codegen->builtin_types.entry_invalid;
30403 if (operand_ptr_type == nullptr) {30250 if (operand_ptr_type == nullptr) {
30404 ir_add_error(ira, &op->base,30251 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'",
30406 buf_ptr(&operand_type->name)));30253 buf_ptr(&operand_type->name)));
30407 return ira->codegen->builtin_types.entry_invalid;30254 return ira->codegen->builtin_types.entry_invalid;
30408 }30255 }
test/compile_errors.zig+16-17
...@@ -2,6 +2,14 @@ const tests = @import("tests.zig");...@@ -2,6 +2,14 @@ const tests = @import("tests.zig");
2const std = @import("std");2const std = @import("std");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub 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
5 cases.add("@Type with undefined",13 cases.add("@Type with undefined",
6 \\comptime {14 \\comptime {
7 \\ _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } });15 \\ _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } });
...@@ -168,11 +176,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -168,11 +176,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
168 , &[_][]const u8{176 , &[_][]const u8{
169 "tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'",177 "tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'",
170 "tmp.zig:1:17: note: function cannot return an error",178 "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'",
172 "tmp.zig:7:17: note: function cannot return an error",180 "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'",
174 "tmp.zig:10:17: note: function cannot return an error",182 "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'",
176 "tmp.zig:14:5: note: cannot store an error in type 'u32'",184 "tmp.zig:14:5: note: cannot store an error in type 'u32'",
177 });185 });
178186
...@@ -891,7 +899,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -891,7 +899,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
891 \\ _ = @cmpxchgWeak(f32, &x, 1, 2, .SeqCst, .SeqCst);899 \\ _ = @cmpxchgWeak(f32, &x, 1, 2, .SeqCst, .SeqCst);
892 \\}900 \\}
893 , &[_][]const u8{901 , &[_][]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'",
895 });903 });
896904
897 cases.add("atomicrmw with float op not .Xchg, .Add or .Sub",905 cases.add("atomicrmw with float op not .Xchg, .Add or .Sub",
...@@ -1216,7 +1224,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1216,7 +1224,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1216 \\ };1224 \\ };
1217 \\}1225 \\}
1218 , &[_][]const u8{1226 , &[_][]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'",
1220 });1228 });
12211229
1222 cases.add("assigning to struct or union fields that are not optionals with a function that returns an optional",1230 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 {...@@ -1921,7 +1929,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1921 \\ const info = @TypeOf(slice).unknown;1929 \\ const info = @TypeOf(slice).unknown;
1922 \\}1930 \\}
1923 , &[_][]const u8{1931 , &[_][]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",
1925 });1933 });
19261934
1927 cases.add("peer cast then implicit cast const pointer to mutable C pointer",1935 cases.add("peer cast then implicit cast const pointer to mutable C pointer",
...@@ -3534,7 +3542,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -3534,7 +3542,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
3534 \\ }3542 \\ }
3535 \\}3543 \\}
3536 , &[_][]const u8{3544 , &[_][]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'",
3538 "tmp.zig:3:14: note: other value is here",3546 "tmp.zig:3:14: note: other value is here",
3539 });3547 });
35403548
...@@ -3666,7 +3674,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -3666,7 +3674,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
3666 \\ try foo();3674 \\ try foo();
3667 \\}3675 \\}
3668 , &[_][]const u8{3676 , &[_][]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",
3670 });3678 });
36713679
3672 cases.add("implicit cast of error set not a subset",3680 cases.add("implicit cast of error set not a subset",
...@@ -7198,15 +7206,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -7198,15 +7206,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
7198 "tmp.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set",7206 "tmp.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set",
7199 });7207 });
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
7210 cases.add("unsupported modifier at start of asm output constraint",7209 cases.add("unsupported modifier at start of asm output constraint",
7211 \\export fn foo() void {7210 \\export fn foo() void {
7212 \\ var bar: u32 = 3;7211 \\ var bar: u32 = 3;
test/stage1/behavior/align.zig+1-1
...@@ -5,7 +5,7 @@ const builtin = @import("builtin");...@@ -5,7 +5,7 @@ const builtin = @import("builtin");
5var foo: u8 align(4) = 100;5var foo: u8 align(4) = 100;
66
7test "global variable alignment" {7test "global variable alignment" {
8 comptime expect(@TypeOf(&foo).alignment == 4);8 comptime expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
9 comptime expect(@TypeOf(&foo) == *align(4) u8);9 comptime expect(@TypeOf(&foo) == *align(4) u8);
10 {10 {
11 const slice = @as(*[1]u8, &foo)[0..];11 const slice = @as(*[1]u8, &foo)[0..];
test/stage1/behavior/array.zig-10
...@@ -136,16 +136,6 @@ test "array literal with specified size" {...@@ -136,16 +136,6 @@ test "array literal with specified size" {
136 expect(array[1] == 2);136 expect(array[1] == 2);
137}137}
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
149test "array len field" {139test "array len field" {
150 var arr = [4]u8{ 0, 0, 0, 0 };140 var arr = [4]u8{ 0, 0, 0, 0 };
151 var ptr = &arr;141 var ptr = &arr;
test/stage1/behavior/async_fn.zig+3-3
...@@ -331,7 +331,7 @@ test "async fn with inferred error set" {...@@ -331,7 +331,7 @@ test "async fn with inferred error set" {
331 fn doTheTest() void {331 fn doTheTest() void {
332 var frame: [1]@Frame(middle) = undefined;332 var frame: [1]@Frame(middle) = undefined;
333 var fn_ptr = middle;333 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;
335 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, fn_ptr, .{});335 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, fn_ptr, .{});
336 resume global_frame;336 resume global_frame;
337 std.testing.expectError(error.Fail, result);337 std.testing.expectError(error.Fail, result);
...@@ -950,7 +950,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" {...@@ -950,7 +950,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" {
950950
951 fn doTheTest() void {951 fn doTheTest() void {
952 var frame: [1]@Frame(middle) = undefined;952 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;
954 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, middle, .{});954 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, middle, .{});
955 resume global_frame;955 resume global_frame;
956 std.testing.expectError(error.Fail, result);956 std.testing.expectError(error.Fail, result);
...@@ -1018,7 +1018,7 @@ test "@TypeOf an async function call of generic fn with error union type" {...@@ -1018,7 +1018,7 @@ test "@TypeOf an async function call of generic fn with error union type" {
1018 const S = struct {1018 const S = struct {
1019 fn func(comptime x: anytype) anyerror!i32 {1019 fn func(comptime x: anytype) anyerror!i32 {
1020 const T = @TypeOf(async func(x));1020 const T = @TypeOf(async func(x));
1021 comptime expect(T == @TypeOf(@frame()).Child);1021 comptime expect(T == @typeInfo(@TypeOf(@frame())).Pointer.child);
1022 return undefined;1022 return undefined;
1023 }1023 }
1024 };1024 };
test/stage1/behavior/bit_shifting.zig+7-5
...@@ -2,16 +2,18 @@ const std = @import("std");...@@ -2,16 +2,18 @@ const std = @import("std");
2const expect = std.testing.expect;2const expect = std.testing.expect;
33
4fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime V: type) type {4fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime V: type) type {
5 expect(Key == std.meta.Int(false, Key.bit_count));5 const key_bits = @typeInfo(Key).Int.bits;
6 expect(Key.bit_count >= mask_bit_count);6 expect(Key == std.meta.Int(false, key_bits));
7 expect(key_bits >= mask_bit_count);
8 const shard_key_bits = mask_bit_count;
7 const ShardKey = std.meta.Int(false, mask_bit_count);9 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;
9 return struct {11 return struct {
10 const Self = @This();12 const Self = @This();
11 shards: [1 << ShardKey.bit_count]?*Node,13 shards: [1 << shard_key_bits]?*Node,
1214
13 pub fn create() Self {15 pub fn create() Self {
14 return Self{ .shards = [_]?*Node{null} ** (1 << ShardKey.bit_count) };16 return Self{ .shards = [_]?*Node{null} ** (1 << shard_key_bits) };
15 }17 }
1618
17 fn getShardKey(key: Key) ShardKey {19 fn getShardKey(key: Key) ShardKey {
test/stage1/behavior/bugs/5487.zig+2-2
...@@ -3,8 +3,8 @@ const io = @import("std").io;...@@ -3,8 +3,8 @@ const io = @import("std").io;
3pub fn write(_: void, bytes: []const u8) !usize {3pub fn write(_: void, bytes: []const u8) !usize {
4 return 0;4 return 0;
5}5}
6pub fn outStream() io.OutStream(void, @TypeOf(write).ReturnType.ErrorSet, write) {6pub fn outStream() io.OutStream(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_type.?).ErrorUnion.error_set, write) {
7 return io.OutStream(void, @TypeOf(write).ReturnType.ErrorSet, write){ .context = {} };7 return io.OutStream(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_type.?).ErrorUnion.error_set, write){ .context = {} };
8}8}
99
10test "crash" {10test "crash" {
test/stage1/behavior/error.zig+2-2
...@@ -84,8 +84,8 @@ fn testErrorUnionType() void {...@@ -84,8 +84,8 @@ fn testErrorUnionType() void {
84 const x: anyerror!i32 = 1234;84 const x: anyerror!i32 = 1234;
85 if (x) |value| expect(value == 1234) else |_| unreachable;85 if (x) |value| expect(value == 1234) else |_| unreachable;
86 expect(@typeInfo(@TypeOf(x)) == .ErrorUnion);86 expect(@typeInfo(@TypeOf(x)) == .ErrorUnion);
87 expect(@typeInfo(@TypeOf(x).ErrorSet) == .ErrorSet);87 expect(@typeInfo(@typeInfo(@TypeOf(x)).ErrorUnion.error_set) == .ErrorSet);
88 expect(@TypeOf(x).ErrorSet == anyerror);88 expect(@typeInfo(@TypeOf(x)).ErrorUnion.error_set == anyerror);
89}89}
9090
91test "error set type" {91test "error set type" {
test/stage1/behavior/misc.zig-10
...@@ -24,12 +24,6 @@ test "call disabled extern fn" {...@@ -24,12 +24,6 @@ test "call disabled extern fn" {
24 disabledExternFn();24 disabledExternFn();
25}25}
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
33test "short circuit" {27test "short circuit" {
34 testShortCircuit(false, true);28 testShortCircuit(false, true);
35 comptime testShortCircuit(false, true);29 comptime testShortCircuit(false, true);
...@@ -577,10 +571,6 @@ test "slice string literal has correct type" {...@@ -577,10 +571,6 @@ test "slice string literal has correct type" {
577 comptime expect(@TypeOf(array[runtime_zero..]) == []const i32);571 comptime expect(@TypeOf(array[runtime_zero..]) == []const i32);
578}572}
579573
580test "pointer child field" {
581 expect((*u32).Child == u32);
582}
583
584test "struct inside function" {574test "struct inside function" {
585 testStructInFn();575 testStructInFn();
586 comptime testStructInFn();576 comptime testStructInFn();
test/stage1/behavior/reflection.zig+7-15
...@@ -2,23 +2,15 @@ const expect = @import("std").testing.expect;...@@ -2,23 +2,15 @@ const expect = @import("std").testing.expect;
2const mem = @import("std").mem;2const mem = @import("std").mem;
3const reflection = @This();3const 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
14test "reflection: function return type, var args, and param types" {5test "reflection: function return type, var args, and param types" {
15 comptime {6 comptime {
16 expect(@TypeOf(dummy).ReturnType == i32);7 const info = @typeInfo(@TypeOf(dummy)).Fn;
17 expect(!@TypeOf(dummy).is_var_args);8 expect(info.return_type.? == i32);
18 expect(@TypeOf(dummy).arg_count == 3);9 expect(!info.is_var_args);
19 expect(@typeInfo(@TypeOf(dummy)).Fn.args[0].arg_type.? == bool);10 expect(info.args.len == 3);
20 expect(@typeInfo(@TypeOf(dummy)).Fn.args[1].arg_type.? == i32);11 expect(info.args[0].arg_type.? == bool);
21 expect(@typeInfo(@TypeOf(dummy)).Fn.args[2].arg_type.? == f32);12 expect(info.args[1].arg_type.? == i32);
13 expect(info.args[2].arg_type.? == f32);
22 }14 }
23}15}
2416