diff --git a/doc/langref.html.in b/doc/langref.html.in index 50807377cd..dee95cf773 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1422,7 +1422,8 @@ fn foo() i32 { {#header_open|Thread Local Variables#}

A variable may be specified to be a thread-local variable using the - {#syntax#}threadlocal{#endsyntax#} keyword:

+ {#syntax#}threadlocal{#endsyntax#} keyword, + which makes each thread work with a separate instance of the variable:

{#code_begin|test|test_thread_local_variables#} const std = @import("std"); const assert = std.debug.assert; @@ -4278,7 +4279,7 @@ const expectError = std.testing.expectError; fn isFieldOptional(comptime T: type, field_index: usize) !bool { const fields = @typeInfo(T).Struct.fields; return switch (field_index) { - // This prong is analyzed `fields.len - 1` times with `idx` being an + // This prong is analyzed `fields.len - 1` times with `idx` being a // unique comptime-known value each time. inline 0...fields.len - 1 => |idx| @typeInfo(fields[idx].type) == .Optional, else => return error.IndexOutOfBounds, @@ -4667,6 +4668,29 @@ test "for basics" { sum2 += @intCast(i32, i); } try expect(sum2 == 10); + + // To iterate over consecutive integers, use the range syntax. + // Unbounded range is always a compile error. + var sum3 : usize = 0; + for (0..5) |i| { + sum3 += i; + } + try expect(sum3 == 10); +} + +test "multi object for" { + const items = [_]usize{ 1, 2, 3 }; + const items2 = [_]usize{ 4, 5, 6 }; + var count: usize = 0; + + // Iterate over multiple objects. + // All lengths must be equal at the start of the loop, otherwise detectable + // illegal behavior occurs. + for (items, items2) |i, j| { + count += i + j; + } + + try expect(count == 21); } test "for reference" { @@ -4710,8 +4734,8 @@ const expect = std.testing.expect; test "nested break" { var count: usize = 0; - outer: for ([_]i32{ 1, 2, 3, 4, 5 }) |_| { - for ([_]i32{ 1, 2, 3, 4, 5 }) |_| { + outer: for (1..6) |_| { + for (1..6) |_| { count += 1; break :outer; } @@ -4721,8 +4745,8 @@ test "nested break" { test "nested continue" { var count: usize = 0; - outer: for ([_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| { - for ([_]i32{ 1, 2, 3, 4, 5 }) |_| { + outer: for (1..9) |_| { + for (1..6) |_| { count += 1; continue :outer; } @@ -8017,7 +8041,7 @@ pub const CallModifier = enum {

{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type or an integer vector type.

{#syntax#}operand{#endsyntax#} may be an {#link|integer|Integers#} or {#link|vector|Vectors#}.

- This function counts the number of most-significant (leading in a big-Endian sense) zeroes in an integer. + Counts the number of most-significant (leading in a big-endian sense) zeroes in an integer - "count leading zeroes".

If {#syntax#}operand{#endsyntax#} is a {#link|comptime#}-known integer, @@ -8167,7 +8191,7 @@ test "main" {

{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type or an integer vector type.

{#syntax#}operand{#endsyntax#} may be an {#link|integer|Integers#} or {#link|vector|Vectors#}.

- This function counts the number of least-significant (trailing in a big-Endian sense) zeroes in an integer. + Counts the number of least-significant (trailing in a big-endian sense) zeroes in an integer - "count trailing zeroes".

If {#syntax#}operand{#endsyntax#} is a {#link|comptime#}-known integer, @@ -8553,16 +8577,27 @@ test "@hasDecl" {

{#see_also|Compile Variables|@embedFile#} {#header_close#} + {#header_open|@inComptime#} +
{#syntax#}@inComptime() bool{#endsyntax#}
+

+ Returns whether the builtin was run in a {#syntax#}comptime{#endsyntax#} context. The result is a compile-time constant. +

+

+ This can be used to provide alternative, comptime-friendly implementations of functions. It should not be used, for instance, to exclude certain functions from being evaluated at comptime. +

+ {#see_also|comptime#} + {#header_close#} + {#header_open|@intCast#}
{#syntax#}@intCast(comptime DestType: type, int: anytype) DestType{#endsyntax#}

@@ -8780,7 +8815,9 @@ test "@wasmMemoryGrow" {

{#syntax#}@popCount(operand: anytype) anytype{#endsyntax#}

{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type.

{#syntax#}operand{#endsyntax#} may be an {#link|integer|Integers#} or {#link|vector|Vectors#}.

-

Counts the number of bits set in an integer.

+

+ Counts the number of bits set in an integer - "population count". +

If {#syntax#}operand{#endsyntax#} is a {#link|comptime#}-known integer, the return type is {#syntax#}comptime_int{#endsyntax#}. @@ -8812,6 +8849,8 @@ test "@wasmMemoryGrow" { pub const PrefetchOptions = struct { /// Whether the prefetch should prepare for a read or a write. rw: Rw = .read, + /// The data's locality in an inclusive range from 0 to 3. + /// /// 0 means no temporal locality. That is, the data can be immediately /// dropped from the cache after it is accessed. /// @@ -8821,12 +8860,12 @@ pub const PrefetchOptions = struct { /// The cache that the prefetch should be preformed on. cache: Cache = .data, - pub const Rw = enum { + pub const Rw = enum(u1) { read, write, }; - pub const Cache = enum { + pub const Cache = enum(u1) { instruction, data, }; @@ -10948,7 +10987,7 @@ pub const MAKELOCAL = @compileError("unable to translate C expr: unexpected toke

{#syntax#}[*c]T{#endsyntax#} - C pointer.

+ + +
{#syntax#}noinline{#endsyntax#}
+ + + {#syntax#}noinline{#endsyntax#} disallows function to be inlined in all call sites. + + +
{#syntax#}nosuspend{#endsyntax#}
diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 3b67f4b24c..cae779a306 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -184,7 +184,7 @@ pub const File = struct { pub const HashHelper = struct { hasher: Hasher = hasher_init, - /// Record a slice of bytes as an dependency of the process being cached + /// Record a slice of bytes as a dependency of the process being cached. pub fn addBytes(hh: *HashHelper, bytes: []const u8) void { hh.hasher.update(mem.asBytes(&bytes.len)); hh.hasher.update(bytes); diff --git a/lib/std/RingBuffer.zig b/lib/std/RingBuffer.zig index 857775b5a0..080e6f54d3 100644 --- a/lib/std/RingBuffer.zig +++ b/lib/std/RingBuffer.zig @@ -1,7 +1,7 @@ //! This ring buffer stores read and write indices while being able to utilise //! the full backing slice by incrementing the indices modulo twice the slice's //! length and reducing indices modulo the slice's length on slice access. This -//! means that whether the ring buffer if full or empty can be distinguished by +//! means that whether the ring buffer is full or empty can be distinguished by //! looking at the difference between the read and write indices without adding //! an extra boolean flag or having to reserve a slot in the buffer. //! diff --git a/lib/std/Uri.zig b/lib/std/Uri.zig index b010ce8662..b0bb3047cb 100644 --- a/lib/std/Uri.zig +++ b/lib/std/Uri.zig @@ -1,4 +1,4 @@ -//! Implements URI parsing roughly adhering to . +//! Uniform Resource Identifier (URI) parsing roughly adhering to . //! Does not do perfect grammar and character class checking, but should be robust against URIs in the wild. const Uri = @This(); diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index 205649ae4d..1695e2bd87 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -221,6 +221,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Asserts the array has at least one item. /// Invalidates pointers to end of list. /// This operation is O(N). + /// This preserves item order. Use `swapRemove` if order preservation is not important. pub fn orderedRemove(self: *Self, i: usize) T { const newlen = self.items.len - 1; if (newlen == i) return self.pop(); @@ -235,6 +236,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Removes the element at the specified index and returns it. /// The empty slot is filled from the end of the list. /// This operation is O(1). + /// This may not preserve item order. Use `orderedRemove` if you need to preserve order. pub fn swapRemove(self: *Self, i: usize) T { if (self.items.len - 1 == i) return self.pop(); diff --git a/lib/std/bit_set.zig b/lib/std/bit_set.zig index d8f9b7f90e..292d099303 100644 --- a/lib/std/bit_set.zig +++ b/lib/std/bit_set.zig @@ -35,9 +35,10 @@ const assert = std.debug.assert; const Allocator = std.mem.Allocator; /// Returns the optimal static bit set type for the specified number -/// of elements. The returned type will perform no allocations, +/// of elements: either `IntegerBitSet` or `ArrayBitSet`, +/// both of which fulfill the same interface. +/// The returned type will perform no allocations, /// can be copied by value, and does not require deinitialization. -/// Both possible implementations fulfill the same interface. pub fn StaticBitSet(comptime size: usize) type { if (size <= @bitSizeOf(usize)) { return IntegerBitSet(size); diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 1d28e69d61..cfe22099cb 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -144,22 +144,47 @@ pub const Mode = OptimizeMode; /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. pub const CallingConvention = enum { + /// This is the default Zig calling convention used when not using `export` on `fn` + /// and no other calling convention is specified. Unspecified, + /// Matches the C ABI for the target. + /// This is the default calling convention when using `export` on `fn` + /// and no other calling convention is specified. C, + /// This makes a function not have any function prologue or epilogue, + /// making the function itself uncallable in regular Zig code. + /// This can be useful when integrating with assembly. Naked, + /// Functions with this calling convention are called asynchronously, + /// as if called as `async function()`. Async, + /// Functions with this calling convention are inlined at all call sites. Inline, + /// x86-only. Interrupt, Signal, + /// x86-only. Stdcall, + /// x86-only. Fastcall, + /// x86-only. Vectorcall, + /// x86-only. Thiscall, + /// ARM Procedure Call Standard (obsolete) + /// ARM-only. APCS, + /// ARM Architecture Procedure Call Standard (current standard) + /// ARM-only. AAPCS, + /// ARM Architecture Procedure Call Standard Vector Floating-Point + /// ARM-only. AAPCSVFP, + /// x86-64-only. SysV, + /// x86-64-only. Win64, + /// AMD GPU, NVPTX, or SPIR-V kernel Kernel, }; @@ -716,6 +741,8 @@ pub const VaList = switch (builtin.cpu.arch) { pub const PrefetchOptions = struct { /// Whether the prefetch should prepare for a read or a write. rw: Rw = .read, + /// The data's locality in an inclusive range from 0 to 3. + /// /// 0 means no temporal locality. That is, the data can be immediately /// dropped from the cache after it is accessed. /// diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index a46337b721..19baee92d3 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -3846,3 +3846,11 @@ pub extern "c" fn os_signpost_interval_begin(log: os_log_t, signpos: os_signpost pub extern "c" fn os_signpost_interval_end(log: os_log_t, signpos: os_signpost_id_t, func: [*]const u8, ...) void; pub extern "c" fn os_signpost_id_make_with_pointer(log: os_log_t, ptr: ?*anyopaque) os_signpost_id_t; pub extern "c" fn os_signpost_enabled(log: os_log_t) bool; + +pub extern "c" fn proc_listpids(tpe: u32, tinfo: u32, buffer: ?*anyopaque, buffersize: c_int) c_int; +pub extern "c" fn proc_listallpids(buffer: ?*anyopaque, buffersize: c_int) c_int; +pub extern "c" fn proc_listpgrppids(pgrpid: pid_t, buffer: ?*anyopaque, buffersize: c_int) c_int; +pub extern "c" fn proc_listchildpids(ppid: pid_t, buffer: ?*anyopaque, buffersize: c_int) c_int; +pub extern "c" fn proc_pidinfo(pid: c_int, flavor: c_int, arg: u64, buffer: ?*anyopaque, buffersize: c_int) c_int; +pub extern "c" fn proc_name(pid: c_int, buffer: ?*anyopaque, buffersize: u32) c_int; +pub extern "c" fn proc_pidpath(pid: c_int, buffer: ?*anyopaque, buffersize: u32) c_int; diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index 900e1165f0..c7561d558d 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -1143,3 +1143,20 @@ pub const POLL = struct { pub const HUP = 0x0010; pub const NVAL = 0x0020; }; + +pub const SIGEV = struct { + pub const NONE = 0; + pub const SIGNAL = 1; + pub const THREAD = 2; +}; + +pub const sigevent = extern struct { + sigev_notify: c_int, + __sigev_u: extern union { + __sigev_signo: c_int, + __sigev_notify_kqueue: c_int, + __sigev_notify_attributes: ?*pthread_attr_t, + }, + sigev_value: sigval, + sigev_notify_function: ?*const fn (sigval) callconv(.C) void, +}; diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 534b6bc592..d3fa8f51c7 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -29,6 +29,8 @@ pub const CPU_WHICH_TIDPID: cpuwhich_t = 8; extern "c" fn __error() *c_int; pub const _errno = __error; +pub extern "c" var malloc_options: [*:0]const u8; + pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; @@ -42,6 +44,7 @@ pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void; pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int; pub extern "c" fn malloc_usable_size(?*const anyopaque) usize; +pub extern "c" fn reallocf(?*anyopaque, usize) ?*anyopaque; pub extern "c" fn getpid() pid_t; @@ -50,6 +53,9 @@ pub extern "c" fn kinfo_getvmmap(pid: pid_t, cntp: *c_int) ?[*]kinfo_vmentry; pub extern "c" fn cpuset_getaffinity(level: cpulevel_t, which: cpuwhich_t, id: id_t, setsize: usize, mask: *cpuset_t) c_int; pub extern "c" fn cpuset_setaffinity(level: cpulevel_t, which: cpuwhich_t, id: id_t, setsize: usize, mask: *const cpuset_t) c_int; +pub extern "c" fn sched_getaffinity(pid: pid_t, cpusetsz: usize, cpuset: *cpuset_t) c_int; +pub extern "c" fn sched_setaffinity(pid: pid_t, cpusetsz: usize, cpuset: *const cpuset_t) c_int; +pub extern "c" fn sched_getcpu() c_int; pub const sf_hdtr = extern struct { headers: [*]const iovec_const, @@ -1102,6 +1108,11 @@ pub const DT = struct { pub const WHT = 14; }; +pub const accept_filter = extern struct { + af_name: [16]u8, + af_args: [240]u8, +}; + /// add event to kq (implies enable) pub const EV_ADD = 0x0001; @@ -1383,15 +1394,47 @@ pub const mcontext_t = switch (builtin.cpu.arch) { rflags: u64, rsp: u64, ss: u64, - len: u64, - fpformat: u64, - ownedfp: u64, - fpstate: [64]u64 align(16), + len: c_long, + fpformat: c_long, + ownedfp: c_long, + fpstate: [64]c_long align(16), fsbase: u64, gsbase: u64, xfpustate: u64, xfpustate_len: u64, - spare: [4]u64, + spare: [4]c_long, + }, + .x86 => extern struct { + onstack: u32, + gs: u32, + fs: u32, + es: u32, + ds: u32, + edi: u32, + esi: u32, + ebp: u32, + isp: u32, + ebx: u32, + edx: u32, + ecx: u32, + eax: u32, + trapno: u32, + err: u32, + eip: u32, + cs: u32, + eflags: u32, + esp: u32, + ss: u32, + len: c_int, + fpformat: c_int, + ownedfp: c_int, + flags: u32, + fpstate: [128]c_int align(16), + fsbase: u32, + gsbase: u32, + xpustate: u32, + xpustate_len: u32, + spare2: [4]c_int, }, .aarch64 => extern struct { gpregs: extern struct { @@ -2205,3 +2248,46 @@ pub const shm_largeconf = extern struct { pub extern "c" fn shm_create_largepage(path: [*:0]const u8, flags: c_int, psind: c_int, alloc_policy: c_int, mode: mode_t) c_int; pub extern "c" fn elf_aux_info(aux: c_int, buf: ?*anyopaque, buflen: c_int) c_int; + +pub const lwpid = i32; + +pub const SIGEV = struct { + pub const NONE = 0; + pub const SIGNAL = 1; + pub const THREAD = 2; + pub const KEVENT = 3; + pub const THREAD_ID = 4; +}; + +pub const sigevent = extern struct { + sigev_notify: c_int, + sigev_signo: c_int, + sigev_value: sigval, + _sigev_un: extern union { + _threadid: lwpid, + _sigev_thread: extern struct { + _function: ?*const fn (sigval) callconv(.C) void, + _attribute: ?**pthread_attr_t, + }, + _kevent_flags: c_ushort, + __spare__: [8]c_long, + }, +}; + +pub const MIN = struct { + pub const INCORE = 0x1; + pub const REFERENCED = 0x2; + pub const MODIFIED = 0x4; + pub const REFERENCED_OTHER = 0x8; + pub const MODIFIED_OTHER = 0x10; + pub const SUPER = 0x60; + pub fn PSIND(i: u32) u32 { + return (i << 5) & SUPER; + } +}; + +pub extern "c" fn mincore( + addr: *align(std.mem.page_size) const anyopaque, + length: usize, + vec: [*]u8, +) c_int; diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig index 9c4f8460de..9b693a59c2 100644 --- a/lib/std/c/haiku.zig +++ b/lib/std/c/haiku.zig @@ -5,11 +5,15 @@ const maxInt = std.math.maxInt; const iovec = std.os.iovec; const iovec_const = std.os.iovec_const; +const status_t = i32; + extern "c" fn _errnop() *c_int; pub const _errno = _errnop; -pub extern "c" fn find_directory(which: c_int, volume: i32, createIt: bool, path_ptr: [*]u8, length: i32) u64; +pub extern "c" fn find_directory(which: c_int, volume: i32, createIt: bool, path_ptr: [*]u8, length: i32) status_t; + +pub extern "c" fn find_path(codePointer: *const u8, baseDirectory: c_int, subPath: [*:0]const u8, pathBuffer: [*:0]u8, bufferSize: usize) status_t; pub extern "c" fn find_thread(thread_name: ?*anyopaque) i32; @@ -1038,3 +1042,22 @@ pub const termios = extern struct { }; pub const MSG_NOSIGNAL = 0x0800; + +pub const SIGEV = struct { + pub const NONE = 0; + pub const SIGNAL = 1; + pub const THREAD = 2; +}; + +pub const sigval = extern union { + int: c_int, + ptr: ?*anyopaque, +}; + +pub const sigevent = extern struct { + sigev_notify: c_int, + sigev_signo: c_int, + sigev_value: sigval, + sigev_notify_function: ?*const fn (sigval) callconv(.C) void, + sigev_notify_attributes: ?*pthread_attr_t, +}; diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index 41524885f7..f70549775c 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -1633,3 +1633,22 @@ pub const POLL = struct { pub const HUP = 0x0010; pub const NVAL = 0x0020; }; + +pub const SIGEV = struct { + pub const NONE = 0; + pub const SIGNAL = 1; + pub const THREAD = 2; +}; + +pub const sigval = extern union { + int: c_int, + ptr: ?*anyopaque, +}; + +pub const sigevent = extern struct { + sigev_notify: c_int, + sigev_signo: c_int, + sigev_value: sigval, + sigev_notify_function: ?*const fn (sigval) callconv(.C) void, + sigev_notify_attributes: ?*pthread_attr_t, +}; diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig index b1b789c77b..c2c38f46ce 100644 --- a/lib/std/c/solaris.zig +++ b/lib/std/c/solaris.zig @@ -1927,3 +1927,22 @@ pub fn IOW(io_type: u8, nr: u8, comptime IOT: type) i32 { pub fn IOWR(io_type: u8, nr: u8, comptime IOT: type) i32 { return ioImpl(.read_write, io_type, nr, IOT); } + +pub const SIGEV = struct { + pub const NONE = 0; + pub const SIGNAL = 1; + pub const THREAD = 2; +}; + +pub const sigval = extern union { + int: c_int, + ptr: ?*anyopaque, +}; + +pub const sigevent = extern struct { + sigev_notify: c_int, + sigev_signo: c_int, + sigev_value: sigval, + sigev_notify_function: ?*const fn (sigval) callconv(.C) void, + sigev_notify_attributes: ?*pthread_attr_t, +}; diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index f59de87e6b..1e8a0fc86f 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -10,7 +10,7 @@ pub const decompress = @import("zstandard/decompress.zig"); pub const DecompressStreamOptions = struct { verify_checksum: bool = true, - window_size_max: usize = 1 << 23, // 8MiB default maximum window size, + window_size_max: usize = 1 << 23, // 8MiB default maximum window size }; pub fn DecompressStream( diff --git a/lib/std/compress/zstandard/decode/fse.zig b/lib/std/compress/zstandard/decode/fse.zig index 41a34d0fc1..741fd81ccc 100644 --- a/lib/std/compress/zstandard/decode/fse.zig +++ b/lib/std/compress/zstandard/decode/fse.zig @@ -21,7 +21,7 @@ pub fn decodeFseTable( var accumulated_probability: u16 = 0; while (accumulated_probability < total_probability) { - // WARNING: The RFC in poorly worded, and would suggest std.math.log2_int_ceil is correct here, + // WARNING: The RFC is poorly worded, and would suggest std.math.log2_int_ceil is correct here, // but power of two (remaining probabilities + 1) need max bits set to 1 more. const max_bits = std.math.log2_int(u16, total_probability - accumulated_probability + 1) + 1; const small = try bit_reader.readBitsNoEof(u16, max_bits - 1); diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index 07bda89585..ad99079852 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -71,12 +71,6 @@ const Sha256Params = Sha2Params32{ const v4u32 = @Vector(4, u32); -// TODO: Remove once https://github.com/ziglang/zig/issues/868 is resolved. -fn isComptime() bool { - var a: u8 = 0; - return @typeInfo(@TypeOf(.{a})).Struct.fields[0].is_comptime; -} - /// SHA-224 pub const Sha224 = Sha2x32(Sha224Params); @@ -203,7 +197,7 @@ fn Sha2x32(comptime params: Sha2Params32) type { s[i] = mem.readIntBig(u32, mem.asBytes(elem)); } - if (!isComptime()) { + if (!@inComptime()) { switch (builtin.cpu.arch) { .aarch64 => if (builtin.zig_backend != .stage2_c and comptime std.Target.aarch64.featureSetHas(builtin.cpu.features, .sha2)) { var x: v4u32 = d.s[0..4].*; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index e093fa5dc8..c965dd6436 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -651,6 +651,8 @@ pub fn writeCurrentStackTraceWindows( } } +/// Provides simple functionality for manipulating the terminal in some way, +/// for debugging purposes, such as coloring text, etc. pub const TTY = struct { pub const Color = enum { Red, diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 2eb89e810a..e2cad5640e 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -221,75 +221,138 @@ pub const DT_IA_64_NUM = 1; pub const DT_NIOS2_GP = 0x70000002; +/// Program header table entry unused pub const PT_NULL = 0; +/// Loadable program segment pub const PT_LOAD = 1; +/// Dynamic linking information pub const PT_DYNAMIC = 2; +/// Program interpreter pub const PT_INTERP = 3; +/// Auxiliary information pub const PT_NOTE = 4; +/// Reserved pub const PT_SHLIB = 5; +/// Entry for header table itself pub const PT_PHDR = 6; +/// Thread-local storage segment pub const PT_TLS = 7; +/// Number of defined types pub const PT_NUM = 8; +/// Start of OS-specific pub const PT_LOOS = 0x60000000; +/// GCC .eh_frame_hdr segment pub const PT_GNU_EH_FRAME = 0x6474e550; +/// Indicates stack executability pub const PT_GNU_STACK = 0x6474e551; +/// Read-only after relocation pub const PT_GNU_RELRO = 0x6474e552; pub const PT_LOSUNW = 0x6ffffffa; +/// Sun specific segment pub const PT_SUNWBSS = 0x6ffffffa; +/// Stack segment pub const PT_SUNWSTACK = 0x6ffffffb; pub const PT_HISUNW = 0x6fffffff; +/// End of OS-specific pub const PT_HIOS = 0x6fffffff; +/// Start of processor-specific pub const PT_LOPROC = 0x70000000; +/// End of processor-specific pub const PT_HIPROC = 0x7fffffff; +/// Section header table entry unused pub const SHT_NULL = 0; +/// Program data pub const SHT_PROGBITS = 1; +/// Symbol table pub const SHT_SYMTAB = 2; +/// String table pub const SHT_STRTAB = 3; +/// Relocation entries with addends pub const SHT_RELA = 4; +/// Symbol hash table pub const SHT_HASH = 5; +/// Dynamic linking information pub const SHT_DYNAMIC = 6; +/// Notes pub const SHT_NOTE = 7; +/// Program space with no data (bss) pub const SHT_NOBITS = 8; +/// Relocation entries, no addends pub const SHT_REL = 9; +/// Reserved pub const SHT_SHLIB = 10; +/// Dynamic linker symbol table pub const SHT_DYNSYM = 11; +/// Array of constructors pub const SHT_INIT_ARRAY = 14; +/// Array of destructors pub const SHT_FINI_ARRAY = 15; +/// Array of pre-constructors pub const SHT_PREINIT_ARRAY = 16; +/// Section group pub const SHT_GROUP = 17; +/// Extended section indices pub const SHT_SYMTAB_SHNDX = 18; +/// Start of OS-specific pub const SHT_LOOS = 0x60000000; +/// End of OS-specific pub const SHT_HIOS = 0x6fffffff; +/// Start of processor-specific pub const SHT_LOPROC = 0x70000000; +/// End of processor-specific pub const SHT_HIPROC = 0x7fffffff; +/// Start of application-specific pub const SHT_LOUSER = 0x80000000; +/// End of application-specific pub const SHT_HIUSER = 0xffffffff; +/// Local symbol pub const STB_LOCAL = 0; +/// Global symbol pub const STB_GLOBAL = 1; +/// Weak symbol pub const STB_WEAK = 2; +/// Number of defined types pub const STB_NUM = 3; +/// Start of OS-specific pub const STB_LOOS = 10; +/// Unique symbol pub const STB_GNU_UNIQUE = 10; +/// End of OS-specific pub const STB_HIOS = 12; +/// Start of processor-specific pub const STB_LOPROC = 13; +/// End of processor-specific pub const STB_HIPROC = 15; pub const STB_MIPS_SPLIT_COMMON = 13; +/// Symbol type is unspecified pub const STT_NOTYPE = 0; +/// Symbol is a data object pub const STT_OBJECT = 1; +/// Symbol is a code object pub const STT_FUNC = 2; +/// Symbol associated with a section pub const STT_SECTION = 3; +/// Symbol's name is file name pub const STT_FILE = 4; +/// Symbol is a common data object pub const STT_COMMON = 5; +/// Symbol is thread-local data object pub const STT_TLS = 6; +/// Number of defined types pub const STT_NUM = 7; +/// Start of OS-specific pub const STT_LOOS = 10; +/// Symbol is indirect code object pub const STT_GNU_IFUNC = 10; +/// End of OS-specific pub const STT_HIOS = 12; +/// Start of processor-specific pub const STT_LOPROC = 13; +/// End of processor-specific pub const STT_HIPROC = 15; pub const STT_SPARC_REGISTER = 13; @@ -656,6 +719,13 @@ pub const Elf32_Sym = extern struct { st_info: u8, st_other: u8, st_shndx: Elf32_Section, + + pub inline fn st_type(self: @This()) u4 { + return @truncate(u4, self.st_info); + } + pub inline fn st_bind(self: @This()) u4 { + return @truncate(u4, self.st_info >> 4); + } }; pub const Elf64_Sym = extern struct { st_name: Elf64_Word, @@ -664,6 +734,13 @@ pub const Elf64_Sym = extern struct { st_shndx: Elf64_Section, st_value: Elf64_Addr, st_size: Elf64_Xword, + + pub inline fn st_type(self: @This()) u4 { + return @truncate(u4, self.st_info); + } + pub inline fn st_bind(self: @This()) u4 { + return @truncate(u4, self.st_info >> 4); + } }; pub const Elf32_Syminfo = extern struct { si_boundto: Elf32_Half, @@ -681,7 +758,7 @@ pub const Elf32_Rel = extern struct { return @truncate(u24, self.r_info >> 8); } pub inline fn r_type(self: @This()) u8 { - return @truncate(u8, self.r_info & 0xff); + return @truncate(u8, self.r_info); } }; pub const Elf64_Rel = extern struct { @@ -692,7 +769,7 @@ pub const Elf64_Rel = extern struct { return @truncate(u32, self.r_info >> 32); } pub inline fn r_type(self: @This()) u32 { - return @truncate(u32, self.r_info & 0xffffffff); + return @truncate(u32, self.r_info); } }; pub const Elf32_Rela = extern struct { @@ -704,7 +781,7 @@ pub const Elf32_Rela = extern struct { return @truncate(u24, self.r_info >> 8); } pub inline fn r_type(self: @This()) u8 { - return @truncate(u8, self.r_info & 0xff); + return @truncate(u8, self.r_info); } }; pub const Elf64_Rela = extern struct { @@ -716,7 +793,7 @@ pub const Elf64_Rela = extern struct { return @truncate(u32, self.r_info >> 32); } pub inline fn r_type(self: @This()) u32 { - return @truncate(u32, self.r_info & 0xffffffff); + return @truncate(u32, self.r_info); } }; pub const Elf32_Dyn = extern struct { @@ -1630,14 +1707,20 @@ pub const PF_MASKOS = 0x0ff00000; /// Bits for processor-specific semantics. pub const PF_MASKPROC = 0xf0000000; -// Special section indexes used in Elf{32,64}_Sym. +/// Undefined section pub const SHN_UNDEF = 0; +/// Start of reserved indices pub const SHN_LORESERVE = 0xff00; +/// Start of processor-specific pub const SHN_LOPROC = 0xff00; +/// End of processor-specific pub const SHN_HIPROC = 0xff1f; pub const SHN_LIVEPATCH = 0xff20; +/// Associated symbol is absolute pub const SHN_ABS = 0xfff1; +/// Associated symbol is common pub const SHN_COMMON = 0xfff2; +/// End of reserved indices pub const SHN_HIRESERVE = 0xffff; /// AMD x86-64 relocations. diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 3f697f8117..cf791df1a6 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -41,7 +41,7 @@ pub const FormatOptions = struct { /// brackets, e.g. {[score]...} as opposed to the numeric index form which can be written e.g. {2...} /// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) /// - *fill* is a single character which is used to pad the formatted text -/// - *alignment* is one of the three characters `<`, `^` or `>`. they define if the text is *left*, *center*, or *right* aligned +/// - *alignment* is one of the three characters `<`, `^`, or `>` to make the text left-, center-, or right-aligned, respectively /// - *width* is the total width of the field in characters /// - *precision* specifies how many decimals a formatted number should have /// @@ -1428,8 +1428,7 @@ pub fn formatInt( var a: MinInt = abs_value; var index: usize = buf.len; - // TODO isComptime here because of https://github.com/ziglang/zig/issues/13335. - if (base == 10 and !isComptime()) { + if (base == 10) { while (a >= 100) : (a = @divTrunc(a, 100)) { index -= 2; buf[index..][0..2].* = digits2(@intCast(usize, a % 100)); @@ -1469,12 +1468,6 @@ pub fn formatInt( return formatBuf(buf[index..], options, writer); } -// TODO: Remove once https://github.com/ziglang/zig/issues/868 is resolved. -fn isComptime() bool { - var a: u8 = 0; - return @typeInfo(@TypeOf(.{a})).Struct.fields[0].is_comptime; -} - pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, case: Case, options: FormatOptions) usize { var fbs = std.io.fixedBufferStream(out_buf); formatInt(value, base, case, options, fbs.writer()) catch unreachable; diff --git a/lib/std/fs.zig b/lib/std/fs.zig index b5e2d7ad63..290eb151f7 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1236,6 +1236,7 @@ pub const Dir = struct { .capable_io_mode = std.io.default_mode, .intended_io_mode = flags.intended_io_mode, }; + errdefer file.close(); var io: w.IO_STATUS_BLOCK = undefined; const range_off: w.LARGE_INTEGER = 0; const range_len: w.LARGE_INTEGER = 1; @@ -1396,6 +1397,7 @@ pub const Dir = struct { .capable_io_mode = std.io.default_mode, .intended_io_mode = flags.intended_io_mode, }; + errdefer file.close(); var io: w.IO_STATUS_BLOCK = undefined; const range_off: w.LARGE_INTEGER = 0; const range_len: w.LARGE_INTEGER = 1; @@ -2210,7 +2212,7 @@ pub const Dir = struct { var need_to_retry: bool = false; parent_dir.deleteDir(name) catch |err| switch (err) { error.FileNotFound => {}, - error.DirNotEmpty => need_to_retry = false, + error.DirNotEmpty => need_to_retry = true, else => |e| return e, }; @@ -2913,6 +2915,7 @@ pub const OpenSelfExeError = error{ /// On Windows, file paths cannot contain these characters: /// '/', '*', '?', '"', '<', '>', '|' BadPathName, + Overflow, Unexpected, } || os.OpenError || SelfExePathError || os.FlockError; @@ -2991,7 +2994,15 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { // TODO could this slice from 0 to out_len instead? return mem.sliceTo(out_buffer, 0); }, - .openbsd, .haiku => { + .haiku => { + // The only possible issue when looking for the self image path is + // when the buffer is too short. + // TODO replace with proper constants + if (os.find_path(null, 1000, null, out_buffer.ptr, out_buffer.len) != 0) + return error.Overflow; + return mem.sliceTo(out_buffer, 0); + }, + .openbsd => { // OpenBSD doesn't support getting the path of a running process, so try to guess it if (os.argv.len == 0) return error.FileNotFound; diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 4d780b2ead..beb386f92c 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -1214,10 +1214,9 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons try testing.expectEqualStrings(expected_output, result); } -/// Returns the extension of the file name (if any). -/// This function will search for the file extension (separated by a `.`) and will return the text after the `.`. -/// Files that end with `.`, or that start with `.` and have no other `.` in their name, -/// are considered to have no extension. +/// Searches for a file extension separated by a `.` and returns the string after that `.`. +/// Files that end or start with `.` and have no other `.` in their name +/// are considered to have no extension, in which case this returns "". /// Examples: /// - `"main.zig"` ⇒ `".zig"` /// - `"src/main.zig"` ⇒ `".zig"` diff --git a/lib/std/http.zig b/lib/std/http.zig index 364cc4eeda..2f0a20de8d 100644 --- a/lib/std/http.zig +++ b/lib/std/http.zig @@ -275,4 +275,5 @@ test { _ = Client; _ = Method; _ = Status; + _ = @import("http/test.zig"); } diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 4ff29a215a..cc01a181f0 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -645,7 +645,6 @@ pub const Request = struct { if (req.response.parser.state.isContent()) break; } - req.response.headers = http.Headers{ .allocator = req.client.allocator, .owned = false }; try req.response.parse(req.response.parser.header_bytes.items); if (req.response.status == .switching_protocols) { @@ -765,7 +764,7 @@ pub const Request = struct { } if (has_trail) { - req.response.headers = http.Headers{ .allocator = req.client.allocator, .owned = false }; + req.response.headers.clearRetainingCapacity(); // The response headers before the trailers are already guaranteed to be valid, so they will always be parsed again and cannot return an error. // This will *only* fail for a malformed trailer. @@ -797,18 +796,18 @@ pub const Request = struct { /// Write `bytes` to the server. The `transfer_encoding` request header determines how data will be sent. pub fn write(req: *Request, bytes: []const u8) WriteError!usize { - switch (req.headers.transfer_encoding) { + switch (req.transfer_encoding) { .chunked => { - try req.connection.data.conn.writer().print("{x}\r\n", .{bytes.len}); - try req.connection.data.conn.writeAll(bytes); - try req.connection.data.conn.writeAll("\r\n"); + try req.connection.data.buffered.writer().print("{x}\r\n", .{bytes.len}); + try req.connection.data.buffered.writeAll(bytes); + try req.connection.data.buffered.writeAll("\r\n"); return bytes.len; }, .content_length => |*len| { if (len.* < bytes.len) return error.MessageTooLong; - const amt = try req.connection.data.conn.write(bytes); + const amt = try req.connection.data.buffered.write(bytes); len.* -= amt; return amt; }, @@ -828,7 +827,7 @@ pub const Request = struct { /// Finish the body of a request. This notifies the server that you have no more data to send. pub fn finish(req: *Request) FinishError!void { switch (req.transfer_encoding) { - .chunked => try req.connection.data.conn.writeAll("0\r\n\r\n"), + .chunked => try req.connection.data.buffered.writeAll("0\r\n\r\n"), .content_length => |len| if (len != 0) return error.MessageNotCompleted, .none => {}, } @@ -1019,7 +1018,7 @@ pub fn request(client: *Client, method: http.Method, uri: Uri, headers: http.Hea .status = undefined, .reason = undefined, .version = undefined, - .headers = undefined, + .headers = http.Headers{ .allocator = client.allocator, .owned = false }, .parser = switch (options.header_strategy) { .dynamic => |max| proto.HeadersParser.initDynamic(max), .static => |buf| proto.HeadersParser.initStatic(buf), diff --git a/lib/std/http/Headers.zig b/lib/std/http/Headers.zig index e84a890862..0322c32481 100644 --- a/lib/std/http/Headers.zig +++ b/lib/std/http/Headers.zig @@ -68,17 +68,7 @@ pub const Headers = struct { } pub fn deinit(headers: *Headers) void { - var it = headers.index.iterator(); - while (it.next()) |entry| { - entry.value_ptr.deinit(headers.allocator); - - if (headers.owned) headers.allocator.free(entry.key_ptr.*); - } - - for (headers.list.items) |entry| { - if (headers.owned) headers.allocator.free(entry.value); - } - + headers.deallocateIndexListsAndFields(); headers.index.deinit(headers.allocator); headers.list.deinit(headers.allocator); @@ -255,6 +245,39 @@ pub const Headers = struct { try out_stream.writeAll("\r\n"); } + + /// Frees all `HeaderIndexList`s within `index` + /// Frees names and values of all fields if they are owned. + fn deallocateIndexListsAndFields(headers: *Headers) void { + var it = headers.index.iterator(); + while (it.next()) |entry| { + entry.value_ptr.deinit(headers.allocator); + + if (headers.owned) headers.allocator.free(entry.key_ptr.*); + } + + if (headers.owned) { + for (headers.list.items) |entry| { + headers.allocator.free(entry.value); + } + } + } + + /// Clears and frees the underlying data structures. + /// Frees names and values if they are owned. + pub fn clearAndFree(headers: *Headers) void { + headers.deallocateIndexListsAndFields(); + headers.index.clearAndFree(headers.allocator); + headers.list.clearAndFree(headers.allocator); + } + + /// Clears the underlying data structures while retaining their capacities. + /// Frees names and values if they are owned. + pub fn clearRetainingCapacity(headers: *Headers) void { + headers.deallocateIndexListsAndFields(); + headers.index.clearRetainingCapacity(); + headers.list.clearRetainingCapacity(); + } }; test "Headers.append" { @@ -384,3 +407,42 @@ test "Headers consistency" { try h.formatCommaSeparated("foo", writer); try testing.expectEqualStrings("foo: bar, baz\r\n", fbs.getWritten()); } + +test "Headers.clearRetainingCapacity and clearAndFree" { + var h = Headers.init(std.testing.allocator); + defer h.deinit(); + + h.clearRetainingCapacity(); + + try h.append("foo", "bar"); + try h.append("bar", "world"); + try h.append("foo", "baz"); + try h.append("baz", "hello"); + try testing.expectEqual(@as(usize, 4), h.list.items.len); + try testing.expectEqual(@as(usize, 3), h.index.count()); + const list_capacity = h.list.capacity; + const index_capacity = h.index.capacity(); + + h.clearRetainingCapacity(); + try testing.expectEqual(@as(usize, 0), h.list.items.len); + try testing.expectEqual(@as(usize, 0), h.index.count()); + try testing.expectEqual(list_capacity, h.list.capacity); + try testing.expectEqual(index_capacity, h.index.capacity()); + + try h.append("foo", "bar"); + try h.append("bar", "world"); + try h.append("foo", "baz"); + try h.append("baz", "hello"); + try testing.expectEqual(@as(usize, 4), h.list.items.len); + try testing.expectEqual(@as(usize, 3), h.index.count()); + // Capacity should still be the same since we shouldn't have needed to grow + // when adding back the same fields + try testing.expectEqual(list_capacity, h.list.capacity); + try testing.expectEqual(index_capacity, h.index.capacity()); + + h.clearAndFree(); + try testing.expectEqual(@as(usize, 0), h.list.items.len); + try testing.expectEqual(@as(usize, 0), h.index.count()); + try testing.expectEqual(@as(usize, 0), h.list.capacity); + try testing.expectEqual(@as(usize, 0), h.index.capacity()); +} diff --git a/lib/std/http/Server.zig b/lib/std/http/Server.zig index 445a10bf48..66adb1bb96 100644 --- a/lib/std/http/Server.zig +++ b/lib/std/http/Server.zig @@ -336,8 +336,15 @@ pub const Response = struct { headers: http.Headers, request: Request, + pub fn deinit(res: *Response) void { + res.server.allocator.destroy(res); + } + /// Reset this response to its initial state. This must be called before handling a second request on the same connection. pub fn reset(res: *Response) void { + res.request.headers.deinit(); + res.headers.deinit(); + switch (res.request.compression) { .none => {}, .deflate => |*deflate| deflate.deinit(), @@ -356,8 +363,6 @@ pub const Response = struct { if (res.request.parser.header_bytes_owned) { res.request.parser.header_bytes.deinit(res.server.allocator); } - - res.* = undefined; } else { res.request.parser.reset(); } @@ -656,3 +661,74 @@ pub fn accept(server: *Server, options: HeaderStrategy) AcceptError!*Response { return res; } + +test "HTTP server handles a chunked transfer coding request" { + const builtin = @import("builtin"); + + // This test requires spawning threads. + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + const native_endian = comptime builtin.cpu.arch.endian(); + if (builtin.zig_backend == .stage2_llvm and native_endian == .Big) { + // https://github.com/ziglang/zig/issues/13782 + return error.SkipZigTest; + } + + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const allocator = std.testing.allocator; + const expect = std.testing.expect; + + const max_header_size = 8192; + var server = std.http.Server.init(allocator, .{ .reuse_address = true }); + defer server.deinit(); + + const address = try std.net.Address.parseIp("127.0.0.1", 0); + try server.listen(address); + const server_port = server.socket.listen_address.in.getPort(); + + const server_thread = try std.Thread.spawn(.{}, (struct { + fn apply(s: *std.http.Server) !void { + const res = try s.accept(.{ .dynamic = max_header_size }); + defer res.deinit(); + defer res.reset(); + try res.wait(); + + try expect(res.request.transfer_encoding.? == .chunked); + + const server_body: []const u8 = "message from server!\n"; + res.transfer_encoding = .{ .content_length = server_body.len }; + try res.headers.append("content-type", "text/plain"); + try res.headers.append("connection", "close"); + try res.do(); + + var buf: [128]u8 = undefined; + const n = try res.readAll(&buf); + try expect(std.mem.eql(u8, buf[0..n], "ABCD")); + _ = try res.writer().writeAll(server_body); + try res.finish(); + } + }).apply, .{&server}); + + const request_bytes = + "POST / HTTP/1.1\r\n" ++ + "Content-Type: text/plain\r\n" ++ + "Transfer-Encoding: chunked\r\n" ++ + "\r\n" ++ + "1\r\n" ++ + "A\r\n" ++ + "1\r\n" ++ + "B\r\n" ++ + "2\r\n" ++ + "CD\r\n" ++ + "0\r\n" ++ + "\r\n"; + + const stream = try std.net.tcpConnectToHost(allocator, "127.0.0.1", server_port); + defer stream.close(); + _ = try stream.writeAll(request_bytes[0..]); + + server_thread.join(); +} diff --git a/lib/std/http/protocol.zig b/lib/std/http/protocol.zig index 0d661bb31f..962376ce82 100644 --- a/lib/std/http/protocol.zig +++ b/lib/std/http/protocol.zig @@ -556,8 +556,12 @@ pub const HeadersParser = struct { switch (r.state) { .invalid => return error.HttpChunkInvalid, .chunk_data => if (r.next_chunk_length == 0) { - // The trailer section is formatted identically to the header section. - r.state = .seen_rn; + if (std.mem.eql(u8, bconn.peek(), "\r\n")) { + r.state = .finished; + } else { + // The trailer section is formatted identically to the header section. + r.state = .seen_rn; + } r.done = true; return out_index; diff --git a/lib/std/http/test.zig b/lib/std/http/test.zig new file mode 100644 index 0000000000..ce55c21392 --- /dev/null +++ b/lib/std/http/test.zig @@ -0,0 +1,72 @@ +const std = @import("std"); +const expect = std.testing.expect; + +test "client requests server" { + const builtin = @import("builtin"); + + // This test requires spawning threads. + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + const native_endian = comptime builtin.cpu.arch.endian(); + if (builtin.zig_backend == .stage2_llvm and native_endian == .Big) { + // https://github.com/ziglang/zig/issues/13782 + return error.SkipZigTest; + } + + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const allocator = std.testing.allocator; + + const max_header_size = 8192; + var server = std.http.Server.init(allocator, .{ .reuse_address = true }); + defer server.deinit(); + + const address = try std.net.Address.parseIp("127.0.0.1", 0); + try server.listen(address); + const server_port = server.socket.listen_address.in.getPort(); + + const server_thread = try std.Thread.spawn(.{}, (struct { + fn apply(s: *std.http.Server) !void { + const res = try s.accept(.{ .dynamic = max_header_size }); + defer res.deinit(); + defer res.reset(); + try res.wait(); + + const server_body: []const u8 = "message from server!\n"; + res.transfer_encoding = .{ .content_length = server_body.len }; + try res.headers.append("content-type", "text/plain"); + try res.headers.append("connection", "close"); + try res.do(); + + var buf: [128]u8 = undefined; + const n = try res.readAll(&buf); + try expect(std.mem.eql(u8, buf[0..n], "Hello, World!\n")); + _ = try res.writer().writeAll(server_body); + try res.finish(); + } + }).apply, .{&server}); + + var uri_buf: [22]u8 = undefined; + const uri = try std.Uri.parse(try std.fmt.bufPrint(&uri_buf, "http://127.0.0.1:{d}", .{server_port})); + var client = std.http.Client{ .allocator = allocator }; + defer client.deinit(); + var client_headers = std.http.Headers{ .allocator = allocator }; + defer client_headers.deinit(); + var client_req = try client.request(.POST, uri, client_headers, .{}); + defer client_req.deinit(); + + client_req.transfer_encoding = .{ .content_length = 14 }; // this will be checked to ensure you sent exactly 14 bytes + try client_req.start(); // this sends the request + try client_req.writeAll("Hello, "); + try client_req.writeAll("World!\n"); + try client_req.finish(); + try client_req.do(); // this waits for a response + + const body = try client_req.reader().readAllAlloc(allocator, 8192 * 1024); + defer allocator.free(body); + try expect(std.mem.eql(u8, body, "message from server!\n")); + + server_thread.join(); +} diff --git a/lib/std/macho.zig b/lib/std/macho.zig index ff12e718f6..a25ffca4fa 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -540,13 +540,13 @@ pub const dylib_command = extern struct { dylib: dylib, }; -/// Dynamicaly linked shared libraries are identified by two things. The +/// Dynamically linked shared libraries are identified by two things. The /// pathname (the name of the library as found for execution), and the /// compatibility version number. The pathname must match and the compatibility /// number in the user of the library must be greater than or equal to the /// library being used. The time stamp is used to record the time a library was /// built and copied into user so it can be use to determined if the library used -/// at runtime is exactly the same as used to built the program. +/// at runtime is exactly the same as used to build the program. pub const dylib = extern struct { /// library's pathname (offset pointing at the end of dylib_command) name: u32, diff --git a/lib/std/math.zig b/lib/std/math.zig index 14c71a796f..02b737610c 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -782,7 +782,8 @@ fn testOverflow() !void { } /// Returns the absolute value of x, where x is a value of a signed integer type. -/// See also: `absCast` +/// Does not convert and returns a value of a signed integer type. +/// Use `absCast` if you want to convert the result and get an unsigned type. pub fn absInt(x: anytype) !@TypeOf(x) { const T = @TypeOf(x); return switch (@typeInfo(T)) { @@ -1015,8 +1016,8 @@ pub inline fn fabs(value: anytype) @TypeOf(value) { } /// Returns the absolute value of the integer parameter. -/// Result is an unsigned integer. -/// See also: `absInt` +/// Converts result type to unsigned if needed and returns a value of an unsigned integer type. +/// Use `absInt` if you want to keep your integer type signed. pub fn absCast(x: anytype) switch (@typeInfo(@TypeOf(x))) { .ComptimeInt => comptime_int, .Int => |int_info| std.meta.Int(.unsigned, int_info.bits), diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 940882e930..4b796c851a 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -227,7 +227,7 @@ pub fn set(comptime T: type, dest: []T, value: T) void { /// interfacing with a C API where this practice is more common and relied upon. If you are performing code review and see this /// function used, examine closely - it may be a code smell. /// Zero initializes the type. -/// This can be used to zero initialize a any type for which it makes sense. Structs will be initialized recursively. +/// This can be used to zero-initialize any type for which it makes sense. Structs will be initialized recursively. pub fn zeroes(comptime T: type) T { switch (@typeInfo(T)) { .ComptimeInt, .Int, .ComptimeFloat, .Float => { diff --git a/lib/std/net.zig b/lib/std/net.zig index 8800bbe6be..205fbf4842 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1867,6 +1867,7 @@ pub const StreamServer = struct { /// Copied from `Options` on `init`. kernel_backlog: u31, reuse_address: bool, + reuse_port: bool, /// `undefined` until `listen` returns successfully. listen_address: Address, @@ -1881,6 +1882,9 @@ pub const StreamServer = struct { /// Enable SO.REUSEADDR on the socket. reuse_address: bool = false, + + /// Enable SO.REUSEPORT on the socket. + reuse_port: bool = false, }; /// After this call succeeds, resources have been acquired and must @@ -1890,6 +1894,7 @@ pub const StreamServer = struct { .sockfd = null, .kernel_backlog = options.kernel_backlog, .reuse_address = options.reuse_address, + .reuse_port = options.reuse_port, .listen_address = undefined, }; } @@ -1920,6 +1925,14 @@ pub const StreamServer = struct { &mem.toBytes(@as(c_int, 1)), ); } + if (@hasDecl(os.SO, "REUSEPORT") and self.reuse_port) { + try os.setsockopt( + sockfd, + os.SOL.SOCKET, + os.SO.REUSEPORT, + &mem.toBytes(@as(c_int, 1)), + ); + } var socklen = address.getOsSockLen(); try os.bind(sockfd, &address.any, socklen); diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index c4afc0e4e3..f59d73d621 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -230,6 +230,27 @@ test "listen on ipv4 try connect on ipv6 then ipv4" { try await client_frame; } +test "listen on an in use port" { + if (builtin.os.tag != .linux and comptime !builtin.os.tag.isDarwin()) { + // TODO build abstractions for other operating systems + return error.SkipZigTest; + } + + const localhost = try net.Address.parseIp("127.0.0.1", 0); + + var server1 = net.StreamServer.init(net.StreamServer.Options{ + .reuse_port = true, + }); + defer server1.deinit(); + try server1.listen(localhost); + + var server2 = net.StreamServer.init(net.StreamServer.Options{ + .reuse_port = true, + }); + defer server2.deinit(); + try server2.listen(server1.listen_address); +} + fn testClientToHost(allocator: mem.Allocator, name: []const u8, port: u16) anyerror!void { if (builtin.os.tag == .wasi) return error.SkipZigTest; diff --git a/lib/std/os.zig b/lib/std/os.zig index 13b0c62455..5b5189ea60 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4722,11 +4722,8 @@ pub fn sysctl( newp: ?*anyopaque, newlen: usize, ) SysCtlError!void { - if (builtin.os.tag == .wasi) { - @panic("unsupported"); // TODO should be compile error, not panic - } - if (builtin.os.tag == .haiku) { - @panic("unsupported"); // TODO should be compile error, not panic + if (builtin.os.tag == .wasi or builtin.os.tag == .haiku) { + @compileError("unsupported OS"); } const name_len = math.cast(c_uint, name.len) orelse return error.NameTooLong; @@ -4747,11 +4744,8 @@ pub fn sysctlbynameZ( newp: ?*anyopaque, newlen: usize, ) SysCtlError!void { - if (builtin.os.tag == .wasi) { - @panic("unsupported"); // TODO should be compile error, not panic - } - if (builtin.os.tag == .haiku) { - @panic("unsupported"); // TODO should be compile error, not panic + if (builtin.os.tag == .wasi or builtin.os.tag == .haiku) { + @compileError("unsupported OS"); } switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) { diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index b6ec05997f..6646cb1d32 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -3464,7 +3464,10 @@ pub const CAP = struct { pub const WAKE_ALARM = 35; pub const BLOCK_SUSPEND = 36; pub const AUDIT_READ = 37; - pub const LAST_CAP = AUDIT_READ; + pub const PERFMON = 38; + pub const BPF = 39; + pub const CHECKPOINT_RESTORE = 40; + pub const LAST_CAP = CHECKPOINT_RESTORE; pub fn valid(x: u8) bool { return x >= 0 and x <= LAST_CAP; diff --git a/lib/std/os/linux/seccomp.zig b/lib/std/os/linux/seccomp.zig index b659c3d0e8..23dbb6ee38 100644 --- a/lib/std/os/linux/seccomp.zig +++ b/lib/std/os/linux/seccomp.zig @@ -20,7 +20,7 @@ //! //! 1. Each CPU architecture supported by Linux has its own unique ABI and //! syscall API. It is not guaranteed that the syscall numbers and arguments -//! are the same across architectures, or that they're even implemted. Thus, +//! are the same across architectures, or that they're even implemented. Thus, //! filters cannot be assumed to be portable without consulting documentation //! like syscalls(2) and testing on target hardware. This also requires //! checking the value of `data.arch` to make sure that a filter was compiled diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 5174090712..682bd4f6f8 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -1101,6 +1101,8 @@ test "isatty" { defer tmp.cleanup(); var file = try tmp.dir.createFile("foo", .{}); + defer file.close(); + try expectEqual(os.isatty(file.handle), false); } diff --git a/lib/std/process.zig b/lib/std/process.zig index 578efa62c4..56e3708f3b 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -818,7 +818,8 @@ pub const ArgIterator = struct { } }; -/// Use argsWithAllocator() for cross-platform code +/// Holds the command-line arguments, with the program name as the first entry. +/// Use argsWithAllocator() for cross-platform code. pub fn args() ArgIterator { return ArgIterator.init(); } @@ -1162,12 +1163,17 @@ pub fn totalSystemMemory() TotalSystemMemoryError!usize { .linux => { return totalSystemMemoryLinux() catch return error.UnknownTotalSystemMemory; }, - .freebsd => { + .freebsd, .netbsd, .openbsd, .dragonfly, .macos => { var physmem: c_ulong = undefined; var len: usize = @sizeOf(c_ulong); - os.sysctlbynameZ("hw.physmem", &physmem, &len, null, 0) catch |err| switch (err) { + const name = switch (builtin.os.tag) { + .macos => "hw.memsize", + .netbsd => "hw.physmem64", + else => "hw.physmem", + }; + os.sysctlbynameZ(name, &physmem, &len, null, 0) catch |err| switch (err) { error.NameTooLong, error.UnknownName => unreachable, - else => |e| return e, + else => return error.UnknownTotalSystemMemory, }; return @intCast(usize, physmem); }, diff --git a/lib/std/rand.zig b/lib/std/rand.zig index 4f9cb0db56..204409c10e 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -389,6 +389,8 @@ pub const Random = struct { /// Randomly selects an index into `proportions`, where the likelihood of each /// index is weighted by that proportion. + /// It is more likely for the index of the last proportion to be returned + /// than the index of the first proportion in the slice, and vice versa. /// /// This is useful for selecting an item from a slice where weights are not equal. /// `T` must be a numeric type capable of holding the sum of `proportions`. diff --git a/lib/std/tar.zig b/lib/std/tar.zig index 91772d7319..ec668d5f93 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -35,7 +35,7 @@ pub const Header = struct { pub fn fileSize(header: Header) !u64 { const raw = header.bytes[124..][0..12]; const ltrimmed = std.mem.trimLeft(u8, raw, "0"); - const rtrimmed = std.mem.trimRight(u8, ltrimmed, "\x00"); + const rtrimmed = std.mem.trimRight(u8, ltrimmed, " \x00"); if (rtrimmed.len == 0) return 0; return std.fmt.parseInt(u64, rtrimmed, 8); } @@ -122,13 +122,16 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi .directory => { const file_name = try stripComponents(unstripped_file_name, options.strip_components); if (file_name.len != 0) { - try dir.makeDir(file_name); + try dir.makePath(file_name); } }, .normal => { if (file_size == 0 and unstripped_file_name.len == 0) return; const file_name = try stripComponents(unstripped_file_name, options.strip_components); + if (std.fs.path.dirname(file_name)) |dir_name| { + try dir.makePath(dir_name); + } var file = try dir.createFile(file_name, .{}); defer file.close(); diff --git a/src/Air.zig b/src/Air.zig index 19ba576a5f..33a8d0515b 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -681,7 +681,7 @@ pub const Inst = struct { /// Uses the `un_op` field. tag_name, - /// Given an error value, return the error name. Result type is always `[:0] const u8`. + /// Given an error value, return the error name. Result type is always `[:0]const u8`. /// Uses the `un_op` field. error_name, diff --git a/src/AstGen.zig b/src/AstGen.zig index 4419f5e803..d802e9dcc8 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -839,12 +839,9 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .slice_open => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; - + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs); - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{ .lhs = lhs, .start = start, @@ -854,14 +851,11 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .slice => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; - + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end); - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{ .lhs = lhs, .start = start, @@ -872,15 +866,12 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .slice_sentinel => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; - + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel); - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{ .lhs = lhs, .start = start, @@ -914,20 +905,16 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .ref => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; - try emitDbgStmt(gz, line, column); + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + try emitDbgStmt(gz, cursor); return gz.addUnNode(.optional_payload_safe_ptr, lhs, node); }, else => { const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; - try emitDbgStmt(gz, line, column); + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + try emitDbgStmt(gz, cursor); return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node); }, @@ -3330,23 +3317,17 @@ fn assignOp( const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); - var line: u32 = undefined; - var column: u32 = undefined; - switch (op_inst_tag) { - .add, .sub, .mul, .div, .mod_rem => { - maybeAdvanceSourceCursorToMainToken(gz, infix_node); - line = gz.astgen.source_line - gz.decl_line; - column = gz.astgen.source_column; - }, - else => {}, - } + const cursor = switch (op_inst_tag) { + .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, infix_node), + else => undefined, + }; const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node); const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = lhs_type } }, node_datas[infix_node].rhs); switch (op_inst_tag) { .add, .sub, .mul, .div, .mod_rem => { - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); }, else => {}, } @@ -5360,8 +5341,7 @@ fn tryExpr( if (!parent_gz.is_comptime) { try emitDbgNode(parent_gz, node); } - const try_line = astgen.source_line - parent_gz.decl_line; - const try_column = astgen.source_column; + const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; const operand_ri: ResultInfo = switch (ri.rl) { .ref => .{ .rl = .ref, .ctx = .error_handling_expr }, @@ -5382,7 +5362,7 @@ fn tryExpr( }; const err_code = try else_scope.addUnNode(err_tag, operand, node); try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); - try emitDbgStmt(&else_scope, try_line, try_column); + try emitDbgStmt(&else_scope, try_lc); _ = try else_scope.addUnNode(.ret_node, err_code, node); try else_scope.setTryBody(try_inst, operand); @@ -5607,10 +5587,8 @@ fn addFieldAccess( const str_index = try astgen.identAsString(field_ident); const lhs = try expr(gz, scope, lhs_ri, object_node); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; - try emitDbgStmt(gz, line, column); + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + try emitDbgStmt(gz, cursor); return gz.addPlNode(tag, node, Zir.Inst.Field{ .lhs = lhs, @@ -5630,24 +5608,20 @@ fn arrayAccess( .ref => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const rhs = try expr(gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_datas[node].rhs); - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); }, else => { const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const rhs = try expr(gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_datas[node].rhs); - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node); }, @@ -5674,21 +5648,15 @@ fn simpleBinOp( } const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node); - var line: u32 = undefined; - var column: u32 = undefined; - switch (op_inst_tag) { - .add, .sub, .mul, .div, .mod_rem => { - maybeAdvanceSourceCursorToMainToken(gz, node); - line = gz.astgen.source_line - gz.decl_line; - column = gz.astgen.source_column; - }, - else => {}, - } + const cursor = switch (op_inst_tag) { + .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, node), + else => undefined, + }; const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].rhs, node); switch (op_inst_tag) { .add, .sub, .mul, .div, .mod_rem => { - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); }, else => {}, } @@ -6787,14 +6755,15 @@ fn switchExpr( } const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none }; + astgen.advanceSourceCursorToNode(operand_node); - const operand_line = astgen.source_line - parent_gz.decl_line; - const operand_column = astgen.source_column; + const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; + const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node); const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond; const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node); // Sema expects a dbg_stmt immediately after switch_cond(_ref) - try emitDbgStmt(parent_gz, operand_line, operand_column); + try emitDbgStmt(parent_gz, operand_lc); // We need the type of the operand to use as the result location for all the prong items. const cond_ty_inst = try parent_gz.addUnNode(.typeof, cond, operand_node); const item_ri: ResultInfo = .{ .rl = .{ .ty = cond_ty_inst } }; @@ -7154,8 +7123,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref if (!gz.is_comptime) { try emitDbgNode(gz, node); } - const ret_line = astgen.source_line - gz.decl_line; - const ret_column = astgen.source_column; + const ret_lc = LineColumn{ astgen.source_line - gz.decl_line, astgen.source_column }; const defer_outer = &astgen.fn_block.?.base; @@ -7179,13 +7147,13 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref const defer_counts = countDefers(defer_outer, scope); if (!defer_counts.need_err_code) { try genDefers(gz, defer_outer, scope, .both_sans_err); - try emitDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_lc); _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token); return Zir.Inst.Ref.unreachable_value; } const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token); try genDefers(gz, defer_outer, scope, .{ .both = err_code }); - try emitDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_lc); _ = try gz.addUnNode(.ret_node, err_code, node); return Zir.Inst.Ref.unreachable_value; } @@ -7210,7 +7178,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref // As our last action before the return, "pop" the error trace if needed _ = try gz.addRestoreErrRetIndex(.ret, .always); - try emitDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_lc); try gz.addRet(ri, operand, node); return Zir.Inst.Ref.unreachable_value; }, @@ -7218,7 +7186,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref // Value is always an error. Emit both error defers and regular defers. const err_code = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; try genDefers(gz, defer_outer, scope, .{ .both = err_code }); - try emitDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_lc); try gz.addRet(ri, operand, node); return Zir.Inst.Ref.unreachable_value; }, @@ -7227,7 +7195,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref if (!defer_counts.have_err) { // Only regular defers; no branch needed. try genDefers(gz, defer_outer, scope, .normal_only); - try emitDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_lc); // As our last action before the return, "pop" the error trace if needed const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; @@ -7250,7 +7218,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref // As our last action before the return, "pop" the error trace if needed _ = try then_scope.addRestoreErrRetIndex(.ret, .always); - try emitDbgStmt(&then_scope, ret_line, ret_column); + try emitDbgStmt(&then_scope, ret_lc); try then_scope.addRet(ri, operand, node); var else_scope = gz.makeSubBlock(scope); @@ -7260,7 +7228,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref .both = try else_scope.addUnNode(.err_union_code, result, node), }; try genDefers(&else_scope, defer_outer, scope, which_ones); - try emitDbgStmt(&else_scope, ret_line, ret_column); + try emitDbgStmt(&else_scope, ret_lc); try else_scope.addRet(ri, operand, node); try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0); @@ -8174,6 +8142,7 @@ fn builtinCall( .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node), .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node), .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node), + .in_comptime => return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime, node), node), .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info), .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of), @@ -8649,11 +8618,14 @@ fn typeCast( rhs_node: Ast.Node.Index, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { - try emitDbgNode(gz, node); + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + const result_type = try typeExpr(gz, scope, lhs_node); + const operand = try expr(gz, scope, .{ .rl = .none }, rhs_node); + try emitDbgStmt(gz, cursor); const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ - .lhs = try typeExpr(gz, scope, lhs_node), - .rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node), + .lhs = result_type, + .rhs = operand, }); return rvalue(gz, ri, result, node); } @@ -8680,14 +8652,15 @@ fn simpleUnOp( operand_node: Ast.Node.Index, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { - switch (tag) { - .tag_name, .error_name, .ptr_to_int => try emitDbgNode(gz, node), - else => {}, - } + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const operand = if (tag == .compile_error) try comptimeExpr(gz, scope, operand_ri, operand_node) else try expr(gz, scope, operand_ri, operand_node); + switch (tag) { + .tag_name, .error_name, .ptr_to_int => try emitDbgStmt(gz, cursor), + else => {}, + } const result = try gz.addUnNode(tag, operand, node); return rvalue(gz, ri, result, node); } @@ -8759,12 +8732,12 @@ fn divBuiltin( rhs_node: Ast.Node.Index, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { - try emitDbgNode(gz, node); + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node); + const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node); - const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ - .lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node), - .rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node), - }); + try emitDbgStmt(gz, cursor); + const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); return rvalue(gz, ri, result, node); } @@ -8813,23 +8786,21 @@ fn shiftOp( rhs_node: Ast.Node.Index, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { - var line = gz.astgen.source_line - gz.decl_line; - var column = gz.astgen.source_column; const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node); - switch (gz.astgen.tree.nodes.items(.tag)[node]) { - .shl, .shr => { - maybeAdvanceSourceCursorToMainToken(gz, node); - line = gz.astgen.source_line - gz.decl_line; - column = gz.astgen.source_column; - }, - else => {}, - } + const cursor = switch (gz.astgen.tree.nodes.items(.tag)[node]) { + .shl, .shr => maybeAdvanceSourceCursorToMainToken(gz, node), + else => undefined, + }; const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node); const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node); - try emitDbgStmt(gz, line, column); + switch (gz.astgen.tree.nodes.items(.tag)[node]) { + .shl, .shr => try emitDbgStmt(gz, cursor), + else => undefined, + } + const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs, @@ -12593,16 +12564,20 @@ fn detectLocalShadowing( }; } +const LineColumn = struct { u32, u32 }; + /// Advances the source cursor to the main token of `node` if not in comptime scope. /// Usually paired with `emitDbgStmt`. -fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) void { - if (gz.is_comptime) return; +fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) LineColumn { + if (gz.is_comptime) return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column }; const tree = gz.astgen.tree; const token_starts = tree.tokens.items(.start); const main_tokens = tree.nodes.items(.main_token); const node_start = token_starts[main_tokens[node]]; gz.astgen.advanceSourceCursor(node_start); + + return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column }; } /// Advances the source cursor to the beginning of `node`. @@ -12806,13 +12781,13 @@ fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 { return @intCast(u32, count); } -fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void { +fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void { if (gz.is_comptime) return; _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ .dbg_stmt = .{ - .line = line, - .column = column, + .line = lc[0], + .column = lc[1], }, } }); } diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 67cb6b7325..3c1beda74a 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -4076,7 +4076,7 @@ fn analyzeFancyFunction( else => null, }; - // if we're analyzing a funcion signature (ie without body), we + // if we're analyzing a function signature (ie without body), we // actually don't have an ast_node reserved for us, but since // we don't have a name, we don't need it. const src = if (fn_info.body.len == 0) 0 else self_ast_node_index; @@ -4229,7 +4229,7 @@ fn analyzeFunction( } else break :blk ret_type_ref; }; - // if we're analyzing a funcion signature (ie without body), we + // if we're analyzing a function signature (ie without body), we // actually don't have an ast_node reserved for us, but since // we don't have a name, we don't need it. const src = if (fn_info.body.len == 0) 0 else self_ast_node_index; diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig index 4a98a5a615..ee11aecbf4 100644 --- a/src/BuiltinFn.zig +++ b/src/BuiltinFn.zig @@ -58,6 +58,7 @@ pub const Tag = enum { has_decl, has_field, import, + in_comptime, int_cast, int_to_enum, int_to_error, @@ -560,6 +561,13 @@ pub const list = list: { .param_count = 1, }, }, + .{ + "@inComptime", + .{ + .tag = .in_comptime, + .param_count = 0, + }, + }, .{ "@intCast", .{ diff --git a/src/Module.zig b/src/Module.zig index fa91e8c1ed..27f7b24e7a 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -6626,7 +6626,7 @@ pub fn backendSupportsFeature(mod: Module, feature: Feature) bool { .safety_check_formatted => mod.comp.bin_file.options.use_llvm, .error_return_trace => mod.comp.bin_file.options.use_llvm, .is_named_enum_value => mod.comp.bin_file.options.use_llvm, - .error_set_has_value => mod.comp.bin_file.options.use_llvm, + .error_set_has_value => mod.comp.bin_file.options.use_llvm or mod.comp.bin_file.options.target.isWasm(), .field_reordering => mod.comp.bin_file.options.use_llvm, }; } diff --git a/src/Sema.zig b/src/Sema.zig index 5c19d37431..ed36417876 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1166,6 +1166,7 @@ fn analyzeBodyInner( .work_item_id => try sema.zirWorkItem( block, extended, extended.opcode), .work_group_size => try sema.zirWorkItem( block, extended, extended.opcode), .work_group_id => try sema.zirWorkItem( block, extended, extended.opcode), + .in_comptime => try sema.zirInComptime( block), // zig fmt: on .fence => { @@ -4155,7 +4156,7 @@ fn validateUnionInit( const msg = try sema.errMsg( block, init_src, - "cannot initialize multiple union fields at once, unions can only have one active field", + "cannot initialize multiple union fields at once; unions can only have one active field", .{}, ); errdefer msg.destroy(sema.gpa); @@ -9646,7 +9647,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .Union => "union", else => unreachable, }; - return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', {s} does not have a guaranteed in-memory layout", .{ + return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}'; {s} does not have a guaranteed in-memory layout", .{ dest_ty.fmt(sema.mod), container, }); }, @@ -9709,7 +9710,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .Union => "union", else => unreachable, }; - return sema.fail(block, operand_src, "cannot @bitCast from '{}', {s} does not have a guaranteed in-memory layout", .{ + return sema.fail(block, operand_src, "cannot @bitCast from '{}'; {s} does not have a guaranteed in-memory layout", .{ operand_ty.fmt(sema.mod), container, }); }, @@ -19626,7 +19627,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } try sema.requireRuntimeBlock(block, src, operand_src); - if (block.wantSafety() and try sema.typeHasRuntimeBits(elem_ty)) { + if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag() == .Fn)) { if (!ptr_ty.isAllowzeroPtr()) { const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize); try sema.addSafetyCheck(block, is_non_zero, .cast_to_null); @@ -19852,7 +19853,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air try sema.requireRuntimeBlock(block, src, null); if (block.wantSafety() and operand_ty.ptrAllowsZero() and !dest_ty.ptrAllowsZero() and - try sema.typeHasRuntimeBits(dest_ty.elemType2())) + (try sema.typeHasRuntimeBits(dest_ty.elemType2()) or dest_ty.elemType2().zigTypeTag() == .Fn)) { const ptr_int = try block.addUnOp(.ptrtoint, ptr); const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); @@ -22466,6 +22467,18 @@ fn zirWorkItem( }); } +fn zirInComptime( + sema: *Sema, + block: *Block, +) CompileError!Air.Inst.Ref { + _ = sema; + if (block.is_comptime) { + return Air.Inst.Ref.bool_true; + } else { + return Air.Inst.Ref.bool_false; + } +} + fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { if (block.is_comptime) { const msg = msg: { @@ -23738,7 +23751,6 @@ fn fieldCallBind( { const first_param_type = decl_type.fnParamType(0); const first_param_tag = first_param_type.tag(); - var opt_buf: Type.Payload.ElemType = undefined; // zig fmt: off if (first_param_tag == .var_args_param or first_param_tag == .generic_poison or ( @@ -23764,17 +23776,29 @@ fn fieldCallBind( .arg0_inst = deref, }); return sema.addConstant(ty, value); - } else if (first_param_tag != .generic_poison and first_param_type.zigTypeTag() == .Optional and - first_param_type.optionalChild(&opt_buf).eql(concrete_ty, sema.mod)) - { - const deref = try sema.analyzeLoad(block, src, object_ptr, src); - const ty = Type.Tag.bound_fn.init(); - const value = try Value.Tag.bound_fn.create(arena, .{ - .func_inst = decl_val, - .arg0_inst = deref, - }); - return sema.addConstant(ty, value); - } else if (first_param_tag != .generic_poison and first_param_type.zigTypeTag() == .ErrorUnion and + } else if (first_param_type.zigTypeTag() == .Optional) { + var opt_buf: Type.Payload.ElemType = undefined; + const child = first_param_type.optionalChild(&opt_buf); + if (child.eql(concrete_ty, sema.mod)) { + const deref = try sema.analyzeLoad(block, src, object_ptr, src); + const ty = Type.Tag.bound_fn.init(); + const value = try Value.Tag.bound_fn.create(arena, .{ + .func_inst = decl_val, + .arg0_inst = deref, + }); + return sema.addConstant(ty, value); + } else if (child.zigTypeTag() == .Pointer and + child.ptrSize() == .One and + child.childType().eql(concrete_ty, sema.mod)) + { + const ty = Type.Tag.bound_fn.init(); + const value = try Value.Tag.bound_fn.create(arena, .{ + .func_inst = decl_val, + .arg0_inst = object_ptr, + }); + return sema.addConstant(ty, value); + } + } else if (first_param_type.zigTypeTag() == .ErrorUnion and first_param_type.errorUnionPayload().eql(concrete_ty, sema.mod)) { const deref = try sema.analyzeLoad(block, src, object_ptr, src); @@ -26434,7 +26458,7 @@ fn coerceVarArgParam( .ComptimeInt, .ComptimeFloat => return sema.fail( block, inst_src, - "integer and float literals passed variadic function must be casted to a fixed-size number type", + "integer and float literals passed to variadic function must be casted to a fixed-size number type", .{}, ), .Fn => blk: { @@ -27718,7 +27742,7 @@ fn coerceCompatiblePtrs( try sema.requireRuntimeBlock(block, inst_src, null); const inst_allows_zero = inst_ty.zigTypeTag() != .Pointer or inst_ty.ptrAllowsZero(); if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero() and - try sema.typeHasRuntimeBits(dest_ty.elemType2())) + (try sema.typeHasRuntimeBits(dest_ty.elemType2()) or dest_ty.elemType2().zigTypeTag() == .Fn)) { const actual_ptr = if (inst_ty.isSlice()) try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty) @@ -27891,7 +27915,7 @@ fn coerceAnonStructToUnion( const msg = if (field_count > 1) try sema.errMsg( block, inst_src, - "cannot initialize multiple union fields at once, unions can only have one active field", + "cannot initialize multiple union fields at once; unions can only have one active field", .{}, ) else try sema.errMsg( block, diff --git a/src/Zir.zig b/src/Zir.zig index 904e02c755..a58b4b4070 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1994,10 +1994,10 @@ pub const Inst = struct { /// Implement builtin `@cVaArg`. /// `operand` is payload index to `BinNode`. c_va_arg, - /// Implement builtin `@cVaStart`. + /// Implement builtin `@cVaCopy`. /// `operand` is payload index to `UnNode`. c_va_copy, - /// Implement builtin `@cVaStart`. + /// Implement builtin `@cVaEnd`. /// `operand` is payload index to `UnNode`. c_va_end, /// Implement builtin `@cVaStart`. @@ -2018,6 +2018,9 @@ pub const Inst = struct { /// Implements the `@workGroupId` builtin. /// `operand` is payload index to `UnNode`. work_group_id, + /// Implements the `@inComptime` builtin. + /// `operand` is `src_node: i32`. + in_comptime, pub const InstData = struct { opcode: Extended, diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b94d3993f9..9bf39b73f1 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1946,6 +1946,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .ret_addr => func.airRetAddr(inst), .tag_name => func.airTagName(inst), + .error_set_has_value => func.airErrorSetHasValue(inst), + .mul_sat, .mod, .assembly, @@ -1967,7 +1969,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .set_err_return_trace, .save_err_return_trace_index, .is_named_enum_value, - .error_set_has_value, .addrspace_cast, .vector_store_elem, .c_va_arg, @@ -3338,9 +3339,14 @@ fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const un_op = func.air.instructions.items(.data)[inst].un_op; const operand = try func.resolveInst(un_op); + const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null); + const errors_len = WValue{ .memory = sym_index }; - _ = operand; - return func.fail("TODO implement airCmpLtErrorsLen for wasm", .{}); + try func.emitWValue(operand); + const errors_len_val = try func.load(errors_len, Type.err_int, 0); + const result = try func.cmp(.stack, errors_len_val, Type.err_int, .lt); + + return func.finishAir(inst, try result.toLocal(func, Type.bool), &.{un_op}); } fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { @@ -6510,3 +6516,84 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty}, slice_ty, func.target); return func.bin_file.createFunction(func_name, func_type, &body_list, &relocs); } + +fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { + const ty_op = func.air.instructions.items(.data)[inst].ty_op; + + const operand = try func.resolveInst(ty_op.operand); + const error_set_ty = func.air.getRefType(ty_op.ty); + const result = try func.allocLocal(Type.bool); + + const names = error_set_ty.errorSetNames(); + var values = try std.ArrayList(u32).initCapacity(func.gpa, names.len); + defer values.deinit(); + + const module = func.bin_file.base.options.module.?; + var lowest: ?u32 = null; + var highest: ?u32 = null; + for (names) |name| { + const err_int = module.global_error_set.get(name).?; + if (lowest) |*l| { + if (err_int < l.*) { + l.* = err_int; + } + } else { + lowest = err_int; + } + if (highest) |*h| { + if (err_int > h.*) { + highest = err_int; + } + } else { + highest = err_int; + } + + values.appendAssumeCapacity(err_int); + } + + // start block for 'true' branch + try func.startBlock(.block, wasm.block_empty); + // start block for 'false' branch + try func.startBlock(.block, wasm.block_empty); + // block for the jump table itself + try func.startBlock(.block, wasm.block_empty); + + // lower operand to determine jump table target + try func.emitWValue(operand); + try func.addImm32(@intCast(i32, lowest.?)); + try func.addTag(.i32_sub); + + // Account for default branch so always add '1' + const depth = @intCast(u32, highest.? - lowest.? + 1); + const jump_table: Mir.JumpTable = .{ .length = depth }; + const table_extra_index = try func.addExtra(jump_table); + try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } }); + try func.mir_extra.ensureUnusedCapacity(func.gpa, depth); + + var value: u32 = lowest.?; + while (value <= highest.?) : (value += 1) { + const idx: u32 = blk: { + for (values.items) |val| { + if (val == value) break :blk 1; + } + break :blk 0; + }; + func.mir_extra.appendAssumeCapacity(idx); + } + try func.endBlock(); + + // 'false' branch (i.e. error set does not have value + // ensure we set local to 0 in case the local was re-used. + try func.addImm32(0); + try func.addLabel(.local_set, result.local.value); + try func.addLabel(.br, 1); + try func.endBlock(); + + // 'true' branch + try func.addImm32(1); + try func.addLabel(.local_set, result.local.value); + try func.addLabel(.br, 0); + try func.endBlock(); + + return func.finishAir(inst, result, &.{ty_op.operand}); +} diff --git a/src/clang.zig b/src/clang.zig index 04261f06f0..f368bb0c3c 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -460,6 +460,9 @@ pub const Expr = opaque { pub const evaluateAsConstantExpr = ZigClangExpr_EvaluateAsConstantExpr; extern fn ZigClangExpr_EvaluateAsConstantExpr(*const Expr, *ExprEvalResult, Expr_ConstantExprKind, *const ASTContext) bool; + + pub const castToStringLiteral = ZigClangExpr_castToStringLiteral; + extern fn ZigClangExpr_castToStringLiteral(*const Expr) ?*const StringLiteral; }; pub const FieldDecl = opaque { @@ -1053,6 +1056,12 @@ pub const InitListExpr = opaque { pub const getArrayFiller = ZigClangInitListExpr_getArrayFiller; extern fn ZigClangInitListExpr_getArrayFiller(*const InitListExpr) *const Expr; + pub const hasArrayFiller = ZigClangInitListExpr_hasArrayFiller; + extern fn ZigClangInitListExpr_hasArrayFiller(*const InitListExpr) bool; + + pub const isStringLiteralInit = ZigClangInitListExpr_isStringLiteralInit; + extern fn ZigClangInitListExpr_isStringLiteralInit(*const InitListExpr) bool; + pub const getNumInits = ZigClangInitListExpr_getNumInits; extern fn ZigClangInitListExpr_getNumInits(*const InitListExpr) c_uint; diff --git a/src/link/NvPtx.zig b/src/link/NvPtx.zig index c542241cd9..69cd73a602 100644 --- a/src/link/NvPtx.zig +++ b/src/link/NvPtx.zig @@ -1,4 +1,4 @@ -//! NVidia PTX (Paralle Thread Execution) +//! NVidia PTX (Parallel Thread Execution) //! https://docs.nvidia.com/cuda/parallel-thread-execution/index.html //! For this we rely on the nvptx backend of LLVM //! Kernel functions need to be marked both as "export" and "callconv(.Kernel)" diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 2125a8faaa..0fe9ec5e3b 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1209,6 +1209,11 @@ fn resolveLazySymbols(wasm: *Wasm) !void { try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); } } + if (wasm.undefs.fetchSwapRemove("__zig_errors_len")) |kv| { + const loc = try wasm.createSyntheticSymbol("__zig_errors_len", .data); + try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); + _ = wasm.resolved_symbols.swapRemove(kv.value); + } } // Tries to find a global symbol by its name. Returns null when not found, @@ -2185,6 +2190,43 @@ fn setupInitFunctions(wasm: *Wasm) !void { std.sort.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan); } +/// Generates an atom containing the global error set' size. +/// This will only be generated if the symbol exists. +fn setupErrorsLen(wasm: *Wasm) !void { + const loc = wasm.findGlobalSymbol("__zig_errors_len") orelse return; + + const errors_len = wasm.base.options.module.?.global_error_set.count(); + // overwrite existing atom if it already exists (maybe the error set has increased) + // if not, allcoate a new atom. + const atom_index = if (wasm.symbol_atom.get(loc)) |index| blk: { + const atom = wasm.getAtomPtr(index); + if (atom.next) |next_atom_index| { + const next_atom = wasm.getAtomPtr(next_atom_index); + next_atom.prev = atom.prev; + atom.next = null; + } + if (atom.prev) |prev_index| { + const prev_atom = wasm.getAtomPtr(prev_index); + prev_atom.next = atom.next; + atom.prev = null; + } + atom.deinit(wasm); + break :blk index; + } else new_atom: { + const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); + try wasm.symbol_atom.put(wasm.base.allocator, loc, atom_index); + try wasm.managed_atoms.append(wasm.base.allocator, undefined); + break :new_atom atom_index; + }; + const atom = wasm.getAtomPtr(atom_index); + atom.* = Atom.empty; + atom.sym_index = loc.index; + atom.size = 2; + try atom.code.writer(wasm.base.allocator).writeIntLittle(u16, @intCast(u16, errors_len)); + + try wasm.parseAtom(atom_index, .{ .data = .read_only }); +} + /// Creates a function body for the `__wasm_call_ctors` symbol. /// Loops over all constructors found in `init_funcs` and calls them /// respectively based on their priority which was sorted by `setupInitFunctions`. @@ -3317,6 +3359,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // So we can rebuild the binary file on each incremental update defer wasm.resetState(); try wasm.setupInitFunctions(); + try wasm.setupErrorsLen(); try wasm.setupStart(); try wasm.setupImports(); if (wasm.base.options.module) |mod| { diff --git a/src/main.zig b/src/main.zig index 6a83791ca3..66b207aa43 100644 --- a/src/main.zig +++ b/src/main.zig @@ -402,8 +402,8 @@ const usage_build_generic = \\ --name [name] Override root name (not a file path) \\ -O [mode] Choose what to optimize for \\ Debug (default) Optimizations off, safety on - \\ ReleaseFast Optimizations on, safety off - \\ ReleaseSafe Optimizations on, safety on + \\ ReleaseFast Optimize for performance, safety off + \\ ReleaseSafe Optimize for performance, safety on \\ ReleaseSmall Optimize for small binary, safety off \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name \\ deps: [dep],[dep],... diff --git a/src/print_zir.zig b/src/print_zir.zig index 79c802f936..927d8a8b33 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -466,6 +466,7 @@ const Writer = struct { .frame_address, .breakpoint, .c_va_start, + .in_comptime, => try self.writeExtNode(stream, extended), .builtin_src => { diff --git a/src/translate_c.zig b/src/translate_c.zig index 45b9f38b75..9d8dcd80b9 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -2697,6 +2697,13 @@ fn transInitListExprArray( return Tag.empty_array.create(c.arena, child_type); } + if (expr.isStringLiteralInit()) { + assert(init_count == 1); + const init_expr = expr.getInit(0); + const string_literal = init_expr.castToStringLiteral().?; + return try transStringLiteral(c, scope, string_literal, .used); + } + const init_node = if (init_count != 0) blk: { const init_list = try c.arena.alloc(Node, init_count); @@ -2714,6 +2721,7 @@ fn transInitListExprArray( break :blk init_node; } else null; + assert(expr.hasArrayFiller()); const filler_val_expr = expr.getArrayFiller(); const filler_node = try Tag.array_filler.create(c.arena, .{ .type = child_type, @@ -4176,6 +4184,17 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { try c.global_scope.nodes.append(decl_node); } +fn transQualTypeInitializedStringLiteral(c: *Context, elem_ty: Node, string_lit: *const clang.StringLiteral) TypeError!Node { + const string_lit_size = string_lit.getLength(); + const array_size = @intCast(usize, string_lit_size); + + // incomplete array initialized with empty string, will be translated as [1]T{0} + // see https://github.com/ziglang/zig/issues/8256 + if (array_size == 0) return Tag.array_type.create(c.arena, .{ .len = 1, .elem_type = elem_ty }); + + return Tag.null_sentinel_array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty }); +} + /// Translate a qualtype for a variable with an initializer. This only matters /// for incomplete arrays, since the initializer determines the size of the array. fn transQualTypeInitialized( @@ -4193,18 +4212,18 @@ fn transQualTypeInitialized( switch (decl_init.getStmtClass()) { .StringLiteralClass => { const string_lit = @ptrCast(*const clang.StringLiteral, decl_init); - const string_lit_size = string_lit.getLength(); - const array_size = @intCast(usize, string_lit_size); - - // incomplete array initialized with empty string, will be translated as [1]T{0} - // see https://github.com/ziglang/zig/issues/8256 - if (array_size == 0) return Tag.array_type.create(c.arena, .{ .len = 1, .elem_type = elem_ty }); - - return Tag.null_sentinel_array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty }); + return transQualTypeInitializedStringLiteral(c, elem_ty, string_lit); }, .InitListExprClass => { const init_expr = @ptrCast(*const clang.InitListExpr, decl_init); const size = init_expr.getNumInits(); + + if (init_expr.isStringLiteralInit()) { + assert(size == 1); + const string_lit = init_expr.getInit(0).castToStringLiteral().?; + return transQualTypeInitializedStringLiteral(c, elem_ty, string_lit); + } + return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_ty }); }, else => {}, diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index ccc4453cdf..699431009f 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -2382,6 +2382,12 @@ bool ZigClangExpr_EvaluateAsConstantExpr(const ZigClangExpr *self, ZigClangExprE return true; } +const ZigClangStringLiteral *ZigClangExpr_castToStringLiteral(const struct ZigClangExpr *self) { + auto casted_self = reinterpret_cast(self); + auto cast = clang::dyn_cast(casted_self); + return reinterpret_cast(cast); +} + const ZigClangExpr *ZigClangInitListExpr_getInit(const ZigClangInitListExpr *self, unsigned i) { auto casted = reinterpret_cast(self); const clang::Expr *result = casted->getInit(i); @@ -2394,6 +2400,16 @@ const ZigClangExpr *ZigClangInitListExpr_getArrayFiller(const ZigClangInitListEx return reinterpret_cast(result); } +bool ZigClangInitListExpr_hasArrayFiller(const ZigClangInitListExpr *self) { + auto casted = reinterpret_cast(self); + return casted->hasArrayFiller(); +} + +bool ZigClangInitListExpr_isStringLiteralInit(const ZigClangInitListExpr *self) { + auto casted = reinterpret_cast(self); + return casted->isStringLiteralInit(); +} + const ZigClangFieldDecl *ZigClangInitListExpr_getInitializedFieldInUnion(const ZigClangInitListExpr *self) { auto casted = reinterpret_cast(self); const clang::FieldDecl *result = casted->getInitializedFieldInUnion(); diff --git a/src/zig_clang.h b/src/zig_clang.h index 2fd91163f7..6efde35932 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -1220,9 +1220,12 @@ ZIG_EXTERN_C bool ZigClangExpr_EvaluateAsFloat(const struct ZigClangExpr *self, ZigClangAPFloat **result, const struct ZigClangASTContext *ctx); ZIG_EXTERN_C bool ZigClangExpr_EvaluateAsConstantExpr(const struct ZigClangExpr *, struct ZigClangExprEvalResult *, ZigClangExpr_ConstantExprKind, const struct ZigClangASTContext *); +ZIG_EXTERN_C const struct ZigClangStringLiteral *ZigClangExpr_castToStringLiteral(const struct ZigClangExpr *self); ZIG_EXTERN_C const ZigClangExpr *ZigClangInitListExpr_getInit(const ZigClangInitListExpr *, unsigned); ZIG_EXTERN_C const ZigClangExpr *ZigClangInitListExpr_getArrayFiller(const ZigClangInitListExpr *); +ZIG_EXTERN_C bool ZigClangInitListExpr_hasArrayFiller(const ZigClangInitListExpr *); +ZIG_EXTERN_C bool ZigClangInitListExpr_isStringLiteralInit(const ZigClangInitListExpr *); ZIG_EXTERN_C unsigned ZigClangInitListExpr_getNumInits(const ZigClangInitListExpr *); ZIG_EXTERN_C const ZigClangFieldDecl *ZigClangInitListExpr_getInitializedFieldInUnion(const ZigClangInitListExpr *self); diff --git a/stage1/zig.h b/stage1/zig.h index 36f3318650..f73dfb72ef 100644 --- a/stage1/zig.h +++ b/stage1/zig.h @@ -253,97 +253,6 @@ typedef char bool; #define zig_concat(lhs, rhs) lhs##rhs #define zig_expand_concat(lhs, rhs) zig_concat(lhs, rhs) -#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) -#include -#define zig_atomic(type) _Atomic(type) -#define zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) atomic_compare_exchange_strong_explicit(obj, &(expected), desired, succ, fail) -#define zig_cmpxchg_weak(obj, expected, desired, succ, fail, type) atomic_compare_exchange_weak_explicit (obj, &(expected), desired, succ, fail) -#define zig_atomicrmw_xchg(obj, arg, order, type) atomic_exchange_explicit (obj, arg, order) -#define zig_atomicrmw_add(obj, arg, order, type) atomic_fetch_add_explicit (obj, arg, order) -#define zig_atomicrmw_sub(obj, arg, order, type) atomic_fetch_sub_explicit (obj, arg, order) -#define zig_atomicrmw_or(obj, arg, order, type) atomic_fetch_or_explicit (obj, arg, order) -#define zig_atomicrmw_xor(obj, arg, order, type) atomic_fetch_xor_explicit (obj, arg, order) -#define zig_atomicrmw_and(obj, arg, order, type) atomic_fetch_and_explicit (obj, arg, order) -#define zig_atomicrmw_nand(obj, arg, order, type) __atomic_fetch_nand (obj, arg, order) -#define zig_atomicrmw_min(obj, arg, order, type) __atomic_fetch_min (obj, arg, order) -#define zig_atomicrmw_max(obj, arg, order, type) __atomic_fetch_max (obj, arg, order) -#define zig_atomic_store(obj, arg, order, type) atomic_store_explicit (obj, arg, order) -#define zig_atomic_load(obj, order, type) atomic_load_explicit (obj, order) -#define zig_fence(order) atomic_thread_fence(order) -#elif defined(__GNUC__) -#define memory_order_relaxed __ATOMIC_RELAXED -#define memory_order_consume __ATOMIC_CONSUME -#define memory_order_acquire __ATOMIC_ACQUIRE -#define memory_order_release __ATOMIC_RELEASE -#define memory_order_acq_rel __ATOMIC_ACQ_REL -#define memory_order_seq_cst __ATOMIC_SEQ_CST -#define zig_atomic(type) type -#define zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) __atomic_compare_exchange_n(obj, &(expected), desired, false, succ, fail) -#define zig_cmpxchg_weak(obj, expected, desired, succ, fail, type) __atomic_compare_exchange_n(obj, &(expected), desired, true , succ, fail) -#define zig_atomicrmw_xchg(obj, arg, order, type) __atomic_exchange_n(obj, arg, order) -#define zig_atomicrmw_add(obj, arg, order, type) __atomic_fetch_add (obj, arg, order) -#define zig_atomicrmw_sub(obj, arg, order, type) __atomic_fetch_sub (obj, arg, order) -#define zig_atomicrmw_or(obj, arg, order, type) __atomic_fetch_or (obj, arg, order) -#define zig_atomicrmw_xor(obj, arg, order, type) __atomic_fetch_xor (obj, arg, order) -#define zig_atomicrmw_and(obj, arg, order, type) __atomic_fetch_and (obj, arg, order) -#define zig_atomicrmw_nand(obj, arg, order, type) __atomic_fetch_nand(obj, arg, order) -#define zig_atomicrmw_min(obj, arg, order, type) __atomic_fetch_min (obj, arg, order) -#define zig_atomicrmw_max(obj, arg, order, type) __atomic_fetch_max (obj, arg, order) -#define zig_atomic_store(obj, arg, order, type) __atomic_store_n (obj, arg, order) -#define zig_atomic_load(obj, order, type) __atomic_load_n (obj, order) -#define zig_fence(order) __atomic_thread_fence(order) -#elif _MSC_VER && (_M_IX86 || _M_X64) -#define memory_order_relaxed 0 -#define memory_order_consume 1 -#define memory_order_acquire 2 -#define memory_order_release 3 -#define memory_order_acq_rel 4 -#define memory_order_seq_cst 5 -#define zig_atomic(type) type -#define zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) zig_expand_concat(zig_msvc_cmpxchg_, type)(obj, &(expected), desired) -#define zig_cmpxchg_weak(obj, expected, desired, succ, fail, type) zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) -#define zig_atomicrmw_xchg(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_xchg_, type)(obj, arg) -#define zig_atomicrmw_add(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_add_, type)(obj, arg) -#define zig_atomicrmw_sub(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_sub_, type)(obj, arg) -#define zig_atomicrmw_or(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_or_, type)(obj, arg) -#define zig_atomicrmw_xor(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_xor_, type)(obj, arg) -#define zig_atomicrmw_and(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_and_, type)(obj, arg) -#define zig_atomicrmw_nand(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_nand_, type)(obj, arg) -#define zig_atomicrmw_min(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_min_, type)(obj, arg) -#define zig_atomicrmw_max(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_max_, type)(obj, arg) -#define zig_atomic_store(obj, arg, order, type) zig_expand_concat(zig_msvc_atomic_store_, type)(obj, arg) -#define zig_atomic_load(obj, order, type) zig_expand_concat(zig_msvc_atomic_load_, type)(obj) -#if _M_X64 -#define zig_fence(order) __faststorefence() -#else -#define zig_fence(order) zig_msvc_atomic_barrier() -#endif - -// TODO: _MSC_VER && (_M_ARM || _M_ARM64) -#else -#define memory_order_relaxed 0 -#define memory_order_consume 1 -#define memory_order_acquire 2 -#define memory_order_release 3 -#define memory_order_acq_rel 4 -#define memory_order_seq_cst 5 -#define zig_atomic(type) type -#define zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) zig_unimplemented() -#define zig_cmpxchg_weak(obj, expected, desired, succ, fail, type) zig_unimplemented() -#define zig_atomicrmw_xchg(obj, arg, order, type) zig_unimplemented() -#define zig_atomicrmw_add(obj, arg, order, type) zig_unimplemented() -#define zig_atomicrmw_sub(obj, arg, order, type) zig_unimplemented() -#define zig_atomicrmw_or(obj, arg, order, type) zig_unimplemented() -#define zig_atomicrmw_xor(obj, arg, order, type) zig_unimplemented() -#define zig_atomicrmw_and(obj, arg, order, type) zig_unimplemented() -#define zig_atomicrmw_nand(obj, arg, order, type) zig_unimplemented() -#define zig_atomicrmw_min(obj, arg, order, type) zig_unimplemented() -#define zig_atomicrmw_max(obj, arg, order, type) zig_unimplemented() -#define zig_atomic_store(obj, arg, order, type) zig_unimplemented() -#define zig_atomic_load(obj, order, type) zig_unimplemented() -#define zig_fence(order) zig_unimplemented() -#endif - #if __STDC_VERSION__ >= 201112L #define zig_noreturn _Noreturn #elif zig_has_attribute(noreturn) || defined(zig_gnuc) @@ -502,15 +411,6 @@ typedef ptrdiff_t intptr_t; #endif -#define zig_make_small_i8(val) INT8_C(val) -#define zig_make_small_u8(val) UINT8_C(val) -#define zig_make_small_i16(val) INT16_C(val) -#define zig_make_small_u16(val) UINT16_C(val) -#define zig_make_small_i32(val) INT32_C(val) -#define zig_make_small_u32(val) UINT32_C(val) -#define zig_make_small_i64(val) INT64_C(val) -#define zig_make_small_u64(val) UINT64_C(val) - #define zig_minInt_i8 INT8_MIN #define zig_maxInt_i8 INT8_MAX #define zig_minInt_u8 UINT8_C(0) @@ -534,24 +434,24 @@ typedef ptrdiff_t intptr_t; #define zig_minInt_u(w, bits) zig_intLimit(u, w, min, bits) #define zig_maxInt_u(w, bits) zig_intLimit(u, w, max, bits) -#define zig_int_operator(Type, RhsType, operation, operator) \ +#define zig_operator(Type, RhsType, operation, operator) \ static inline Type zig_##operation(Type lhs, RhsType rhs) { \ return lhs operator rhs; \ } -#define zig_int_basic_operator(Type, operation, operator) \ - zig_int_operator(Type, Type, operation, operator) -#define zig_int_shift_operator(Type, operation, operator) \ - zig_int_operator(Type, uint8_t, operation, operator) +#define zig_basic_operator(Type, operation, operator) \ + zig_operator(Type, Type, operation, operator) +#define zig_shift_operator(Type, operation, operator) \ + zig_operator(Type, uint8_t, operation, operator) #define zig_int_helpers(w) \ - zig_int_basic_operator(uint##w##_t, and_u##w, &) \ - zig_int_basic_operator( int##w##_t, and_i##w, &) \ - zig_int_basic_operator(uint##w##_t, or_u##w, |) \ - zig_int_basic_operator( int##w##_t, or_i##w, |) \ - zig_int_basic_operator(uint##w##_t, xor_u##w, ^) \ - zig_int_basic_operator( int##w##_t, xor_i##w, ^) \ - zig_int_shift_operator(uint##w##_t, shl_u##w, <<) \ - zig_int_shift_operator( int##w##_t, shl_i##w, <<) \ - zig_int_shift_operator(uint##w##_t, shr_u##w, >>) \ + zig_basic_operator(uint##w##_t, and_u##w, &) \ + zig_basic_operator( int##w##_t, and_i##w, &) \ + zig_basic_operator(uint##w##_t, or_u##w, |) \ + zig_basic_operator( int##w##_t, or_i##w, |) \ + zig_basic_operator(uint##w##_t, xor_u##w, ^) \ + zig_basic_operator( int##w##_t, xor_i##w, ^) \ + zig_shift_operator(uint##w##_t, shl_u##w, <<) \ + zig_shift_operator( int##w##_t, shl_i##w, <<) \ + zig_shift_operator(uint##w##_t, shr_u##w, >>) \ \ static inline int##w##_t zig_shr_i##w(int##w##_t lhs, uint8_t rhs) { \ int##w##_t sign_mask = lhs < INT##w##_C(0) ? -INT##w##_C(1) : INT##w##_C(0); \ @@ -576,13 +476,13 @@ typedef ptrdiff_t intptr_t; ? val | zig_minInt_i(w, bits) : val & zig_maxInt_i(w, bits); \ } \ \ - zig_int_basic_operator(uint##w##_t, div_floor_u##w, /) \ + zig_basic_operator(uint##w##_t, div_floor_u##w, /) \ \ static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \ return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < INT##w##_C(0)); \ } \ \ - zig_int_basic_operator(uint##w##_t, mod_u##w, %) \ + zig_basic_operator(uint##w##_t, mod_u##w, %) \ \ static inline int##w##_t zig_mod_i##w(int##w##_t lhs, int##w##_t rhs) { \ int##w##_t rem = lhs % rhs; \ @@ -1253,8 +1153,8 @@ typedef signed __int128 zig_i128; #define zig_lo_u128(val) ((uint64_t)((val) >> 0)) #define zig_hi_i128(val) (( int64_t)((val) >> 64)) #define zig_lo_i128(val) ((uint64_t)((val) >> 0)) -#define zig_bitcast_u128(val) ((zig_u128)(val)) -#define zig_bitcast_i128(val) ((zig_i128)(val)) +#define zig_bitCast_u128(val) ((zig_u128)(val)) +#define zig_bitCast_i128(val) ((zig_i128)(val)) #define zig_cmp_int128(Type) \ static inline int32_t zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ return (lhs > rhs) - (lhs < rhs); \ @@ -1288,8 +1188,8 @@ typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128; #define zig_lo_u128(val) ((val).lo) #define zig_hi_i128(val) ((val).hi) #define zig_lo_i128(val) ((val).lo) -#define zig_bitcast_u128(val) zig_make_u128((uint64_t)(val).hi, (val).lo) -#define zig_bitcast_i128(val) zig_make_i128(( int64_t)(val).hi, (val).lo) +#define zig_bitCast_u128(val) zig_make_u128((uint64_t)(val).hi, (val).lo) +#define zig_bitCast_i128(val) zig_make_i128(( int64_t)(val).hi, (val).lo) #define zig_cmp_int128(Type) \ static inline int32_t zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ return (lhs.hi == rhs.hi) \ @@ -1303,9 +1203,6 @@ typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128; #endif /* zig_has_int128 */ -#define zig_make_small_u128(val) zig_make_u128(0, val) -#define zig_make_small_i128(val) zig_make_i128((val) < 0 ? -INT64_C(1) : INT64_C(0), val) - #define zig_minInt_u128 zig_make_u128(zig_minInt_u64, zig_minInt_u64) #define zig_maxInt_u128 zig_make_u128(zig_maxInt_u64, zig_maxInt_u64) #define zig_minInt_i128 zig_make_i128(zig_minInt_i64, zig_minInt_u64) @@ -1466,18 +1363,18 @@ static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { } static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { - return zig_bitcast_u128(zig_mul_i128(zig_bitcast_i128(lhs), zig_bitcast_i128(rhs))); + return zig_bitCast_u128(zig_mul_i128(zig_bitCast_i128(lhs), zig_bitCast_i128(rhs))); } zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs); static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { return __udivti3(lhs, rhs); -}; +} zig_extern zig_i128 __divti3(zig_i128 lhs, zig_i128 rhs); static zig_i128 zig_div_trunc_i128(zig_i128 lhs, zig_i128 rhs) { return __divti3(lhs, rhs); -}; +} zig_extern zig_u128 __umodti3(zig_u128 lhs, zig_u128 rhs); static zig_u128 zig_rem_u128(zig_u128 lhs, zig_u128 rhs) { @@ -1503,10 +1400,6 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { #define zig_div_floor_u128 zig_div_trunc_u128 #define zig_mod_u128 zig_rem_u128 -static inline zig_u128 zig_nand_u128(zig_u128 lhs, zig_u128 rhs) { - return zig_not_u128(zig_and_u128(lhs, rhs), 128); -} - static inline zig_u128 zig_min_u128(zig_u128 lhs, zig_u128 rhs) { return zig_cmp_u128(lhs, rhs) < INT32_C(0) ? lhs : rhs; } @@ -1538,7 +1431,7 @@ static inline zig_u128 zig_shlw_u128(zig_u128 lhs, uint8_t rhs, uint8_t bits) { } static inline zig_i128 zig_shlw_i128(zig_i128 lhs, uint8_t rhs, uint8_t bits) { - return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), rhs)), bits); + return zig_wrap_i128(zig_bitCast_i128(zig_shl_u128(zig_bitCast_u128(lhs), rhs)), bits); } static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { @@ -1546,7 +1439,7 @@ static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { } static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { - return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); + return zig_wrap_i128(zig_bitCast_i128(zig_add_u128(zig_bitCast_u128(lhs), zig_bitCast_u128(rhs))), bits); } static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { @@ -1554,7 +1447,7 @@ static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { } static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { - return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); + return zig_wrap_i128(zig_bitCast_i128(zig_sub_u128(zig_bitCast_u128(lhs), zig_bitCast_u128(rhs))), bits); } static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { @@ -1562,7 +1455,7 @@ static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { } static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { - return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); + return zig_wrap_i128(zig_bitCast_i128(zig_mul_u128(zig_bitCast_u128(lhs), zig_bitCast_u128(rhs))), bits); } #if zig_has_int128 @@ -1697,7 +1590,7 @@ static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, uint8_t rhs, uint8 static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, uint8_t rhs, uint8_t bits) { *res = zig_shlw_i128(lhs, rhs, bits); - zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - UINT8_C(1))); + zig_i128 mask = zig_bitCast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - UINT8_C(1))); return zig_cmp_i128(zig_and_i128(lhs, mask), zig_make_i128(0, 0)) != INT32_C(0) && zig_cmp_i128(zig_and_i128(lhs, mask), mask) != INT32_C(0); } @@ -1711,7 +1604,7 @@ static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { zig_i128 res; - if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_make_u128(0, bits)) < INT32_C(0) && !zig_shlo_i128(&res, lhs, (uint8_t)zig_lo_i128(rhs), bits)) return res; + if (zig_cmp_u128(zig_bitCast_u128(rhs), zig_make_u128(0, bits)) < INT32_C(0) && !zig_shlo_i128(&res, lhs, (uint8_t)zig_lo_i128(rhs), bits)) return res; return zig_cmp_i128(lhs, zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); } @@ -1755,7 +1648,7 @@ static inline uint8_t zig_clz_u128(zig_u128 val, uint8_t bits) { } static inline uint8_t zig_clz_i128(zig_i128 val, uint8_t bits) { - return zig_clz_u128(zig_bitcast_u128(val), bits); + return zig_clz_u128(zig_bitCast_u128(val), bits); } static inline uint8_t zig_ctz_u128(zig_u128 val, uint8_t bits) { @@ -1764,7 +1657,7 @@ static inline uint8_t zig_ctz_u128(zig_u128 val, uint8_t bits) { } static inline uint8_t zig_ctz_i128(zig_i128 val, uint8_t bits) { - return zig_ctz_u128(zig_bitcast_u128(val), bits); + return zig_ctz_u128(zig_bitCast_u128(val), bits); } static inline uint8_t zig_popcount_u128(zig_u128 val, uint8_t bits) { @@ -1773,7 +1666,7 @@ static inline uint8_t zig_popcount_u128(zig_u128 val, uint8_t bits) { } static inline uint8_t zig_popcount_i128(zig_i128 val, uint8_t bits) { - return zig_popcount_u128(zig_bitcast_u128(val), bits); + return zig_popcount_u128(zig_bitCast_u128(val), bits); } static inline zig_u128 zig_byte_swap_u128(zig_u128 val, uint8_t bits) { @@ -1788,7 +1681,7 @@ static inline zig_u128 zig_byte_swap_u128(zig_u128 val, uint8_t bits) { } static inline zig_i128 zig_byte_swap_i128(zig_i128 val, uint8_t bits) { - return zig_bitcast_i128(zig_byte_swap_u128(zig_bitcast_u128(val), bits)); + return zig_bitCast_i128(zig_byte_swap_u128(zig_bitCast_u128(val), bits)); } static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, uint8_t bits) { @@ -1798,7 +1691,7 @@ static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, uint8_t bits) { } static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { - return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits)); + return zig_bitCast_i128(zig_bit_reverse_u128(zig_bitCast_u128(val), bits)); } /* ========================== Big Integer Support =========================== */ @@ -1972,6 +1865,243 @@ static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_sign return 0; } +static inline void zig_and_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + uint8_t *res_bytes = res; + const uint8_t *lhs_bytes = lhs; + const uint8_t *rhs_bytes = rhs; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + (void)is_signed; + + while (remaining_bytes >= 128 / CHAR_BIT) { + zig_u128 res_limb; + zig_u128 lhs_limb; + zig_u128 rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_and_u128(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 128 / CHAR_BIT; + byte_offset += 128 / CHAR_BIT; + } + + while (remaining_bytes >= 64 / CHAR_BIT) { + uint64_t res_limb; + uint64_t lhs_limb; + uint64_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_and_u64(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 64 / CHAR_BIT; + byte_offset += 64 / CHAR_BIT; + } + + while (remaining_bytes >= 32 / CHAR_BIT) { + uint32_t res_limb; + uint32_t lhs_limb; + uint32_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_and_u32(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 32 / CHAR_BIT; + byte_offset += 32 / CHAR_BIT; + } + + while (remaining_bytes >= 16 / CHAR_BIT) { + uint16_t res_limb; + uint16_t lhs_limb; + uint16_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_and_u16(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 16 / CHAR_BIT; + byte_offset += 16 / CHAR_BIT; + } + + while (remaining_bytes >= 8 / CHAR_BIT) { + uint8_t res_limb; + uint8_t lhs_limb; + uint8_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_and_u8(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 8 / CHAR_BIT; + byte_offset += 8 / CHAR_BIT; + } +} + +static inline void zig_or_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + uint8_t *res_bytes = res; + const uint8_t *lhs_bytes = lhs; + const uint8_t *rhs_bytes = rhs; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + (void)is_signed; + + while (remaining_bytes >= 128 / CHAR_BIT) { + zig_u128 res_limb; + zig_u128 lhs_limb; + zig_u128 rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_or_u128(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 128 / CHAR_BIT; + byte_offset += 128 / CHAR_BIT; + } + + while (remaining_bytes >= 64 / CHAR_BIT) { + uint64_t res_limb; + uint64_t lhs_limb; + uint64_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_or_u64(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 64 / CHAR_BIT; + byte_offset += 64 / CHAR_BIT; + } + + while (remaining_bytes >= 32 / CHAR_BIT) { + uint32_t res_limb; + uint32_t lhs_limb; + uint32_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_or_u32(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 32 / CHAR_BIT; + byte_offset += 32 / CHAR_BIT; + } + + while (remaining_bytes >= 16 / CHAR_BIT) { + uint16_t res_limb; + uint16_t lhs_limb; + uint16_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_or_u16(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 16 / CHAR_BIT; + byte_offset += 16 / CHAR_BIT; + } + + while (remaining_bytes >= 8 / CHAR_BIT) { + uint8_t res_limb; + uint8_t lhs_limb; + uint8_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_or_u8(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 8 / CHAR_BIT; + byte_offset += 8 / CHAR_BIT; + } +} + +static inline void zig_xor_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { + uint8_t *res_bytes = res; + const uint8_t *lhs_bytes = lhs; + const uint8_t *rhs_bytes = rhs; + uint16_t byte_offset = 0; + uint16_t remaining_bytes = zig_int_bytes(bits); + (void)is_signed; + + while (remaining_bytes >= 128 / CHAR_BIT) { + zig_u128 res_limb; + zig_u128 lhs_limb; + zig_u128 rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_xor_u128(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 128 / CHAR_BIT; + byte_offset += 128 / CHAR_BIT; + } + + while (remaining_bytes >= 64 / CHAR_BIT) { + uint64_t res_limb; + uint64_t lhs_limb; + uint64_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_xor_u64(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 64 / CHAR_BIT; + byte_offset += 64 / CHAR_BIT; + } + + while (remaining_bytes >= 32 / CHAR_BIT) { + uint32_t res_limb; + uint32_t lhs_limb; + uint32_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_xor_u32(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 32 / CHAR_BIT; + byte_offset += 32 / CHAR_BIT; + } + + while (remaining_bytes >= 16 / CHAR_BIT) { + uint16_t res_limb; + uint16_t lhs_limb; + uint16_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_xor_u16(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 16 / CHAR_BIT; + byte_offset += 16 / CHAR_BIT; + } + + while (remaining_bytes >= 8 / CHAR_BIT) { + uint8_t res_limb; + uint8_t lhs_limb; + uint8_t rhs_limb; + + memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb)); + memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb)); + res_limb = zig_xor_u8(lhs_limb, rhs_limb); + memcpy(&res_bytes[byte_offset], &res_limb, sizeof(res_limb)); + + remaining_bytes -= 8 / CHAR_BIT; + byte_offset += 8 / CHAR_BIT; + } +} + static inline bool zig_addo_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) { uint8_t *res_bytes = res; const uint8_t *lhs_bytes = lhs; @@ -2827,24 +2957,20 @@ long double __cdecl nanl(char const* input); #endif #if (zig_has_builtin(nan) && zig_has_builtin(nans) && zig_has_builtin(inf)) || defined(zig_gnuc) -#define zig_has_float_builtins 1 -#define zig_make_special_f16(sign, name, arg, repr) sign zig_make_f16(__builtin_##name, )(arg) -#define zig_make_special_f32(sign, name, arg, repr) sign zig_make_f32(__builtin_##name, )(arg) -#define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg) -#define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg) +#define zig_make_special_f16(sign, name, arg, repr) sign zig_make_f16 (__builtin_##name, )(arg) +#define zig_make_special_f32(sign, name, arg, repr) sign zig_make_f32 (__builtin_##name, )(arg) +#define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64 (__builtin_##name, )(arg) +#define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80 (__builtin_##name, )(arg) #define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg) #else -#define zig_has_float_builtins 0 -#define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) -#define zig_make_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr) -#define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) -#define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) -#define zig_make_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) +#define zig_make_special_f16(sign, name, arg, repr) zig_bitCast_f16 (repr) +#define zig_make_special_f32(sign, name, arg, repr) zig_bitCast_f32 (repr) +#define zig_make_special_f64(sign, name, arg, repr) zig_bitCast_f64 (repr) +#define zig_make_special_f80(sign, name, arg, repr) zig_bitCast_f80 (repr) +#define zig_make_special_f128(sign, name, arg, repr) zig_bitCast_f128(repr) #endif #define zig_has_f16 1 -#define zig_bitSizeOf_f16 16 -typedef uint16_t zig_repr_f16; #define zig_libc_name_f16(name) __##name##h #define zig_init_special_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr) #if FLT_MANT_DIG == 11 @@ -2854,10 +2980,6 @@ typedef float zig_f16; typedef double zig_f16; #define zig_make_f16(fp, repr) fp #elif LDBL_MANT_DIG == 11 -#define zig_bitSizeOf_c_longdouble 16 -#ifndef ZIG_TARGET_ABI_MSVC -typedef zig_repr_f16 zig_repr_c_longdouble; -#endif typedef long double zig_f16; #define zig_make_f16(fp, repr) fp##l #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) @@ -2869,8 +2991,8 @@ typedef __fp16 zig_f16; #else #undef zig_has_f16 #define zig_has_f16 0 -#define zig_bitSizeOf_repr_f16 16 -typedef zig_repr_f16 zig_f16; +#define zig_repr_f16 u16 +typedef uint16_t zig_f16; #define zig_make_f16(fp, repr) repr #undef zig_make_special_f16 #define zig_make_special_f16(sign, name, arg, repr) repr @@ -2878,15 +3000,12 @@ typedef zig_repr_f16 zig_f16; #define zig_init_special_f16(sign, name, arg, repr) repr #endif #if __APPLE__ && (defined(__i386__) || defined(__x86_64__)) -typedef zig_repr_f16 zig_compiler_rt_f16; +typedef uint16_t zig_compiler_rt_f16; #else typedef zig_f16 zig_compiler_rt_f16; #endif -#define zig_compiler_rt_abbrev_zig_compiler_rt_f16 zig_compiler_rt_abbrev_zig_f16 #define zig_has_f32 1 -#define zig_bitSizeOf_f32 32 -typedef uint32_t zig_repr_f32; #define zig_libc_name_f32(name) name##f #if _MSC_VER #define zig_init_special_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, ) @@ -2900,10 +3019,6 @@ typedef float zig_f32; typedef double zig_f32; #define zig_make_f32(fp, repr) fp #elif LDBL_MANT_DIG == 24 -#define zig_bitSizeOf_c_longdouble 32 -#ifndef ZIG_TARGET_ABI_MSVC -typedef zig_repr_f32 zig_repr_c_longdouble; -#endif typedef long double zig_f32; #define zig_make_f32(fp, repr) fp##l #elif FLT32_MANT_DIG == 24 @@ -2912,8 +3027,8 @@ typedef _Float32 zig_f32; #else #undef zig_has_f32 #define zig_has_f32 0 -#define zig_bitSizeOf_repr_f32 32 -typedef zig_repr_f32 zig_f32; +#define zig_repr_f32 u32 +ypedef uint32_t zig_f32; #define zig_make_f32(fp, repr) repr #undef zig_make_special_f32 #define zig_make_special_f32(sign, name, arg, repr) repr @@ -2922,20 +3037,12 @@ typedef zig_repr_f32 zig_f32; #endif #define zig_has_f64 1 -#define zig_bitSizeOf_f64 64 -typedef uint64_t zig_repr_f64; #define zig_libc_name_f64(name) name #if _MSC_VER -#ifdef ZIG_TARGET_ABI_MSVC -#define zig_bitSizeOf_c_longdouble 64 -#ifndef ZIG_TARGET_ABI_MSVC -typedef zig_repr_f64 zig_repr_c_longdouble; -#endif -#endif #define zig_init_special_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, ) -#else /* _MSC_VER */ +#else #define zig_init_special_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr) -#endif /* _MSC_VER */ +#endif #if FLT_MANT_DIG == 53 typedef float zig_f64; #define zig_make_f64(fp, repr) fp##f @@ -2943,10 +3050,6 @@ typedef float zig_f64; typedef double zig_f64; #define zig_make_f64(fp, repr) fp #elif LDBL_MANT_DIG == 53 -#define zig_bitSizeOf_c_longdouble 64 -#ifndef ZIG_TARGET_ABI_MSVC -typedef zig_repr_f64 zig_repr_c_longdouble; -#endif typedef long double zig_f64; #define zig_make_f64(fp, repr) fp##l #elif FLT64_MANT_DIG == 53 @@ -2958,8 +3061,8 @@ typedef _Float32x zig_f64; #else #undef zig_has_f64 #define zig_has_f64 0 -#define zig_bitSizeOf_repr_f64 64 -typedef zig_repr_f64 zig_f64; +#define zig_repr_f64 u64 +typedef uint64_t zig_f64; #define zig_make_f64(fp, repr) repr #undef zig_make_special_f64 #define zig_make_special_f64(sign, name, arg, repr) repr @@ -2968,8 +3071,6 @@ typedef zig_repr_f64 zig_f64; #endif #define zig_has_f80 1 -#define zig_bitSizeOf_f80 80 -typedef zig_u128 zig_repr_f80; #define zig_libc_name_f80(name) __##name##x #define zig_init_special_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr) #if FLT_MANT_DIG == 64 @@ -2979,10 +3080,6 @@ typedef float zig_f80; typedef double zig_f80; #define zig_make_f80(fp, repr) fp #elif LDBL_MANT_DIG == 64 -#define zig_bitSizeOf_c_longdouble 80 -#ifndef ZIG_TARGET_ABI_MSVC -typedef zig_repr_f80 zig_repr_c_longdouble; -#endif typedef long double zig_f80; #define zig_make_f80(fp, repr) fp##l #elif FLT80_MANT_DIG == 64 @@ -2997,8 +3094,8 @@ typedef __float80 zig_f80; #else #undef zig_has_f80 #define zig_has_f80 0 -#define zig_bitSizeOf_repr_f80 128 -typedef zig_repr_f80 zig_f80; +#define zig_repr_f80 u128 +typedef zig_u128 zig_f80; #define zig_make_f80(fp, repr) repr #undef zig_make_special_f80 #define zig_make_special_f80(sign, name, arg, repr) repr @@ -3007,8 +3104,6 @@ typedef zig_repr_f80 zig_f80; #endif #define zig_has_f128 1 -#define zig_bitSizeOf_f128 128 -typedef zig_u128 zig_repr_f128; #define zig_libc_name_f128(name) name##q #define zig_init_special_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr) #if FLT_MANT_DIG == 113 @@ -3018,10 +3113,6 @@ typedef float zig_f128; typedef double zig_f128; #define zig_make_f128(fp, repr) fp #elif LDBL_MANT_DIG == 113 -#define zig_bitSizeOf_c_longdouble 128 -#ifndef ZIG_TARGET_ABI_MSVC -typedef zig_repr_f128 zig_repr_c_longdouble; -#endif typedef long double zig_f128; #define zig_make_f128(fp, repr) fp##l #elif FLT128_MANT_DIG == 113 @@ -3038,50 +3129,49 @@ typedef __float128 zig_f128; #else #undef zig_has_f128 #define zig_has_f128 0 -#define zig_bitSizeOf_repr_f128 128 -typedef zig_repr_f128 zig_f128; -#define zig_make_f128(fp, repr) repr #undef zig_make_special_f128 -#define zig_make_special_f128(sign, name, arg, repr) repr #undef zig_init_special_f128 +#if __APPLE__ || defined(__aarch64__) +typedef __attribute__((__vector_size__(2 * sizeof(uint64_t)))) uint64_t zig_v2u64; +zig_basic_operator(zig_v2u64, xor_v2u64, ^) +#define zig_repr_f128 v2u64 +typedef zig_v2u64 zig_f128; +#define zig_make_f128_zig_make_u128(hi, lo) (zig_f128){ lo, hi } +#define zig_make_f128_zig_init_u128 zig_make_f128_zig_make_u128 +#define zig_make_f128(fp, repr) zig_make_f128_##repr +#define zig_make_special_f128(sign, name, arg, repr) zig_make_f128_##repr +#define zig_init_special_f128(sign, name, arg, repr) zig_make_f128_##repr +#else +#define zig_repr_f128 u128 +typedef zig_u128 zig_f128; +#define zig_make_f128(fp, repr) repr +#define zig_make_special_f128(sign, name, arg, repr) repr #define zig_init_special_f128(sign, name, arg, repr) repr #endif +#endif -#ifdef zig_bitSizeOf_c_longdouble - -#define zig_has_c_longdouble 1 -#ifdef ZIG_TARGET_ABI_MSVC -#undef zig_bitSizeOf_c_longdouble -#define zig_bitSizeOf_c_longdouble 64 +#if !_MSC_VER && defined(ZIG_TARGET_ABI_MSVC) +/* Emulate msvc abi on a gnu compiler */ typedef zig_f64 zig_c_longdouble; -typedef zig_repr_f64 zig_repr_c_longdouble; +#elif _MSC_VER && !defined(ZIG_TARGET_ABI_MSVC) +/* Emulate gnu abi on an msvc compiler */ +typedef zig_f128 zig_c_longdouble; #else +/* Target and compiler abi match */ typedef long double zig_c_longdouble; #endif -#else /* zig_bitSizeOf_c_longdouble */ - -#define zig_has_c_longdouble 0 -#define zig_bitSizeOf_repr_c_longdouble 128 -typedef zig_f128 zig_c_longdouble; -typedef zig_repr_f128 zig_repr_c_longdouble; - -#endif /* zig_bitSizeOf_c_longdouble */ - -#if !zig_has_float_builtins -#define zig_float_from_repr(Type) \ - static inline zig_##Type zig_float_from_repr_##Type(zig_repr_##Type repr) { \ +#define zig_bitCast_float(Type, ReprType) \ + static inline zig_##Type zig_bitCast_##Type(ReprType repr) { \ zig_##Type result; \ memcpy(&result, &repr, sizeof(result)); \ return result; \ } - -zig_float_from_repr(f16) -zig_float_from_repr(f32) -zig_float_from_repr(f64) -zig_float_from_repr(f80) -zig_float_from_repr(f128) -#endif +zig_bitCast_float(f16, uint16_t) +zig_bitCast_float(f32, uint32_t) +zig_bitCast_float(f64, uint64_t) +zig_bitCast_float(f80, zig_u128) +zig_bitCast_float(f128, zig_u128) #define zig_cast_f16 (zig_f16) #define zig_cast_f32 (zig_f32) @@ -3095,44 +3185,53 @@ zig_float_from_repr(f128) #define zig_cast_f128 (zig_f128) #endif -#define zig_convert_builtin(ResType, operation, ArgType, version) \ - zig_extern ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ - zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(ArgType); -zig_convert_builtin(zig_compiler_rt_f16, trunc, zig_f32, 2) -zig_convert_builtin(zig_compiler_rt_f16, trunc, zig_f64, 2) -zig_convert_builtin(zig_f16, trunc, zig_f80, 2) -zig_convert_builtin(zig_f16, trunc, zig_f128, 2) -zig_convert_builtin(zig_f32, extend, zig_compiler_rt_f16, 2) -zig_convert_builtin(zig_f32, trunc, zig_f64, 2) -zig_convert_builtin(zig_f32, trunc, zig_f80, 2) -zig_convert_builtin(zig_f32, trunc, zig_f128, 2) -zig_convert_builtin(zig_f64, extend, zig_compiler_rt_f16, 2) -zig_convert_builtin(zig_f64, extend, zig_f32, 2) -zig_convert_builtin(zig_f64, trunc, zig_f80, 2) -zig_convert_builtin(zig_f64, trunc, zig_f128, 2) -zig_convert_builtin(zig_f80, extend, zig_f16, 2) -zig_convert_builtin(zig_f80, extend, zig_f32, 2) -zig_convert_builtin(zig_f80, extend, zig_f64, 2) -zig_convert_builtin(zig_f80, trunc, zig_f128, 2) -zig_convert_builtin(zig_f128, extend, zig_f16, 2) -zig_convert_builtin(zig_f128, extend, zig_f32, 2) -zig_convert_builtin(zig_f128, extend, zig_f64, 2) -zig_convert_builtin(zig_f128, extend, zig_f80, 2) +#define zig_convert_builtin(ExternResType, ResType, operation, ExternArgType, ArgType, version) \ + zig_extern ExternResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ + zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(ExternArgType); \ + static inline ResType zig_expand_concat(zig_expand_concat(zig_##operation, \ + zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType)(ArgType arg) { \ + ResType res; \ + ExternResType extern_res; \ + ExternArgType extern_arg; \ + memcpy(&extern_arg, &arg, sizeof(extern_arg)); \ + extern_res = zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ + zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(extern_arg); \ + memcpy(&res, &extern_res, sizeof(res)); \ + return extern_res; \ + } +zig_convert_builtin(zig_compiler_rt_f16, zig_f16, trunc, zig_f32, zig_f32, 2) +zig_convert_builtin(zig_compiler_rt_f16, zig_f16, trunc, zig_f64, zig_f64, 2) +zig_convert_builtin(zig_f16, zig_f16, trunc, zig_f80, zig_f80, 2) +zig_convert_builtin(zig_f16, zig_f16, trunc, zig_f128, zig_f128, 2) +zig_convert_builtin(zig_f32, zig_f32, extend, zig_compiler_rt_f16, zig_f16, 2) +zig_convert_builtin(zig_f32, zig_f32, trunc, zig_f64, zig_f64, 2) +zig_convert_builtin(zig_f32, zig_f32, trunc, zig_f80, zig_f80, 2) +zig_convert_builtin(zig_f32, zig_f32, trunc, zig_f128, zig_f128, 2) +zig_convert_builtin(zig_f64, zig_f64, extend, zig_compiler_rt_f16, zig_f16, 2) +zig_convert_builtin(zig_f64, zig_f64, extend, zig_f32, zig_f32, 2) +zig_convert_builtin(zig_f64, zig_f64, trunc, zig_f80, zig_f80, 2) +zig_convert_builtin(zig_f64, zig_f64, trunc, zig_f128, zig_f128, 2) +zig_convert_builtin(zig_f80, zig_f80, extend, zig_f16, zig_f16, 2) +zig_convert_builtin(zig_f80, zig_f80, extend, zig_f32, zig_f32, 2) +zig_convert_builtin(zig_f80, zig_f80, extend, zig_f64, zig_f64, 2) +zig_convert_builtin(zig_f80, zig_f80, trunc, zig_f128, zig_f128, 2) +zig_convert_builtin(zig_f128, zig_f128, extend, zig_f16, zig_f16, 2) +zig_convert_builtin(zig_f128, zig_f128, extend, zig_f32, zig_f32, 2) +zig_convert_builtin(zig_f128, zig_f128, extend, zig_f64, zig_f64, 2) +zig_convert_builtin(zig_f128, zig_f128, extend, zig_f80, zig_f80, 2) -#define zig_float_negate_builtin_0(w) \ +#define zig_float_negate_builtin_0(w, c, sb) \ + zig_expand_concat(zig_xor_, zig_repr_f##w)(arg, zig_make_f##w(-0x0.0p0, c sb)) +#define zig_float_negate_builtin_1(w, c, sb) -arg +#define zig_float_negate_builtin(w, c, sb) \ static inline zig_f##w zig_neg_f##w(zig_f##w arg) { \ - return zig_expand_concat(zig_xor_u, zig_bitSizeOf_repr_f##w)( \ - arg, \ - zig_expand_concat(zig_shl_u, zig_bitSizeOf_repr_f##w)( \ - zig_expand_concat(zig_make_small_u, zig_bitSizeOf_repr_f##w)(1), \ - UINT8_C(w - 1) \ - ) \ - ); \ - } -#define zig_float_negate_builtin_1(w) \ - static inline zig_f##w zig_neg_f##w(zig_f##w arg) { \ - return -arg; \ + return zig_expand_concat(zig_float_negate_builtin_, zig_has_f##w)(w, c, sb); \ } +zig_float_negate_builtin(16, , UINT16_C(1) << 15 ) +zig_float_negate_builtin(32, , UINT32_C(1) << 31 ) +zig_float_negate_builtin(64, , UINT64_C(1) << 63 ) +zig_float_negate_builtin(80, zig_make_u128, (UINT64_C(1) << 15, UINT64_C(0))) +zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0))) #define zig_float_less_builtin_0(Type, operation) \ zig_extern int32_t zig_expand_concat(zig_expand_concat(__##operation, \ @@ -3164,19 +3263,18 @@ zig_convert_builtin(zig_f128, extend, zig_f80, 2) } #define zig_float_builtins(w) \ - zig_convert_builtin( int32_t, fix, zig_f##w, ) \ - zig_convert_builtin(uint32_t, fixuns, zig_f##w, ) \ - zig_convert_builtin( int64_t, fix, zig_f##w, ) \ - zig_convert_builtin(uint64_t, fixuns, zig_f##w, ) \ - zig_convert_builtin(zig_i128, fix, zig_f##w, ) \ - zig_convert_builtin(zig_u128, fixuns, zig_f##w, ) \ - zig_convert_builtin(zig_f##w, float, int32_t, ) \ - zig_convert_builtin(zig_f##w, floatun, uint32_t, ) \ - zig_convert_builtin(zig_f##w, float, int64_t, ) \ - zig_convert_builtin(zig_f##w, floatun, uint64_t, ) \ - zig_convert_builtin(zig_f##w, float, zig_i128, ) \ - zig_convert_builtin(zig_f##w, floatun, zig_u128, ) \ - zig_expand_concat(zig_float_negate_builtin_, zig_has_f##w)(w) \ + zig_convert_builtin( int32_t, int32_t, fix, zig_f##w, zig_f##w, ) \ + zig_convert_builtin(uint32_t, uint32_t, fixuns, zig_f##w, zig_f##w, ) \ + zig_convert_builtin( int64_t, int64_t, fix, zig_f##w, zig_f##w, ) \ + zig_convert_builtin(uint64_t, uint64_t, fixuns, zig_f##w, zig_f##w, ) \ + zig_convert_builtin(zig_i128, zig_i128, fix, zig_f##w, zig_f##w, ) \ + zig_convert_builtin(zig_u128, zig_u128, fixuns, zig_f##w, zig_f##w, ) \ + zig_convert_builtin(zig_f##w, zig_f##w, float, int32_t, int32_t, ) \ + zig_convert_builtin(zig_f##w, zig_f##w, floatun, uint32_t, uint32_t, ) \ + zig_convert_builtin(zig_f##w, zig_f##w, float, int64_t, int64_t, ) \ + zig_convert_builtin(zig_f##w, zig_f##w, floatun, uint64_t, uint64_t, ) \ + zig_convert_builtin(zig_f##w, zig_f##w, float, zig_i128, zig_i128, ) \ + zig_convert_builtin(zig_f##w, zig_f##w, floatun, zig_u128, zig_u128, ) \ zig_expand_concat(zig_float_less_builtin_, zig_has_f##w)(f##w, cmp) \ zig_expand_concat(zig_float_less_builtin_, zig_has_f##w)(f##w, ne) \ zig_expand_concat(zig_float_less_builtin_, zig_has_f##w)(f##w, eq) \ @@ -3224,9 +3322,238 @@ zig_float_builtins(64) zig_float_builtins(80) zig_float_builtins(128) +/* ============================ Atomics Support ============================= */ + +/* Note that atomics should be implemented as macros because most + compilers silently discard runtime atomic order information. */ + +/* Define fallback implementations first that can later be undef'd on compilers with builtin support. */ +/* Note that zig_atomicrmw_expected is needed to handle aliasing between res and arg. */ +#define zig_atomicrmw_xchg_float(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, arg, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) +#define zig_atomicrmw_add_float(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_##Type zig_atomicrmw_desired; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + do { \ + zig_atomicrmw_desired = zig_add_##Type(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) +#define zig_atomicrmw_sub_float(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_##Type zig_atomicrmw_desired; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + do { \ + zig_atomicrmw_desired = zig_sub_##Type(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) +#define zig_atomicrmw_min_float(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_##Type zig_atomicrmw_desired; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + do { \ + zig_atomicrmw_desired = zig_libc_name_##Type(fmin)(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) +#define zig_atomicrmw_max_float(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_##Type zig_atomicrmw_desired; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + do { \ + zig_atomicrmw_desired = zig_libc_name_##Type(fmax)(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) + +#define zig_atomicrmw_xchg_int128(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, arg, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) +#define zig_atomicrmw_add_int128(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_##Type zig_atomicrmw_desired; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + do { \ + zig_atomicrmw_desired = zig_add_##Type(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) +#define zig_atomicrmw_sub_int128(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_##Type zig_atomicrmw_desired; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + do { \ + zig_atomicrmw_desired = zig_sub_##Type(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) +#define zig_atomicrmw_and_int128(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_##Type zig_atomicrmw_desired; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + do { \ + zig_atomicrmw_desired = zig_and_##Type(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) +#define zig_atomicrmw_nand_int128(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_##Type zig_atomicrmw_desired; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + do { \ + zig_atomicrmw_desired = zig_not_##Type(zig_and_##Type(zig_atomicrmw_expected, arg), 128); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) +#define zig_atomicrmw_or_int128(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_##Type zig_atomicrmw_desired; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + do { \ + zig_atomicrmw_desired = zig_or_##Type(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) +#define zig_atomicrmw_xor_int128(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_##Type zig_atomicrmw_desired; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + do { \ + zig_atomicrmw_desired = zig_xor_##Type(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) +#define zig_atomicrmw_min_int128(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_##Type zig_atomicrmw_desired; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + do { \ + zig_atomicrmw_desired = zig_min_##Type(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) +#define zig_atomicrmw_max_int128(res, obj, arg, order, Type, ReprType) do { \ + zig_##Type zig_atomicrmw_expected; \ + zig_##Type zig_atomicrmw_desired; \ + zig_atomic_load(zig_atomicrmw_expected, obj, memory_order_relaxed, Type, ReprType); \ + do { \ + zig_atomicrmw_desired = zig_max_##Type(zig_atomicrmw_expected, arg); \ + } while (!zig_cmpxchg_weak(obj, zig_atomicrmw_expected, zig_atomicrmw_desired, order, memory_order_relaxed, Type, ReprType)); \ + res = zig_atomicrmw_expected; \ +} while (0) + +#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) +#include +typedef enum memory_order zig_memory_order; +#define zig_atomic(Type) _Atomic(Type) +#define zig_cmpxchg_strong( obj, expected, desired, succ, fail, Type, ReprType) atomic_compare_exchange_strong_explicit(obj, &(expected), desired, succ, fail) +#define zig_cmpxchg_weak( obj, expected, desired, succ, fail, Type, ReprType) atomic_compare_exchange_weak_explicit (obj, &(expected), desired, succ, fail) +#define zig_atomicrmw_xchg(res, obj, arg, order, Type, ReprType) res = atomic_exchange_explicit (obj, arg, order) +#define zig_atomicrmw_add(res, obj, arg, order, Type, ReprType) res = atomic_fetch_add_explicit (obj, arg, order) +#define zig_atomicrmw_sub(res, obj, arg, order, Type, ReprType) res = atomic_fetch_sub_explicit (obj, arg, order) +#define zig_atomicrmw_or(res, obj, arg, order, Type, ReprType) res = atomic_fetch_or_explicit (obj, arg, order) +#define zig_atomicrmw_xor(res, obj, arg, order, Type, ReprType) res = atomic_fetch_xor_explicit (obj, arg, order) +#define zig_atomicrmw_and(res, obj, arg, order, Type, ReprType) res = atomic_fetch_and_explicit (obj, arg, order) +#define zig_atomicrmw_nand(res, obj, arg, order, Type, ReprType) res = __atomic_fetch_nand(obj, arg, order) +#define zig_atomicrmw_min(res, obj, arg, order, Type, ReprType) res = __atomic_fetch_min (obj, arg, order) +#define zig_atomicrmw_max(res, obj, arg, order, Type, ReprType) res = __atomic_fetch_max (obj, arg, order) +#define zig_atomic_store( obj, arg, order, Type, ReprType) atomic_store_explicit (obj, arg, order) +#define zig_atomic_load(res, obj, order, Type, ReprType) res = atomic_load_explicit (obj, order) +#undef zig_atomicrmw_xchg_float +#define zig_atomicrmw_xchg_float zig_atomicrmw_xchg +#undef zig_atomicrmw_add_float +#define zig_atomicrmw_add_float zig_atomicrmw_add +#undef zig_atomicrmw_sub_float +#define zig_atomicrmw_sub_float zig_atomicrmw_sub +#define zig_fence(order) atomic_thread_fence(order) +#elif defined(__GNUC__) +typedef int zig_memory_order; +#define memory_order_relaxed __ATOMIC_RELAXED +#define memory_order_consume __ATOMIC_CONSUME +#define memory_order_acquire __ATOMIC_ACQUIRE +#define memory_order_release __ATOMIC_RELEASE +#define memory_order_acq_rel __ATOMIC_ACQ_REL +#define memory_order_seq_cst __ATOMIC_SEQ_CST +#define zig_atomic(Type) Type +#define zig_cmpxchg_strong( obj, expected, desired, succ, fail, Type, ReprType) __atomic_compare_exchange(obj, &(expected), &(desired), false, succ, fail) +#define zig_cmpxchg_weak( obj, expected, desired, succ, fail, Type, ReprType) __atomic_compare_exchange(obj, &(expected), &(desired), true, succ, fail) +#define zig_atomicrmw_xchg(res, obj, arg, order, Type, ReprType) __atomic_exchange(obj, &(arg), &(res), order) +#define zig_atomicrmw_add(res, obj, arg, order, Type, ReprType) res = __atomic_fetch_add (obj, arg, order) +#define zig_atomicrmw_sub(res, obj, arg, order, Type, ReprType) res = __atomic_fetch_sub (obj, arg, order) +#define zig_atomicrmw_or(res, obj, arg, order, Type, ReprType) res = __atomic_fetch_or (obj, arg, order) +#define zig_atomicrmw_xor(res, obj, arg, order, Type, ReprType) res = __atomic_fetch_xor (obj, arg, order) +#define zig_atomicrmw_and(res, obj, arg, order, Type, ReprType) res = __atomic_fetch_and (obj, arg, order) +#define zig_atomicrmw_nand(res, obj, arg, order, Type, ReprType) res = __atomic_fetch_nand(obj, arg, order) +#define zig_atomicrmw_min(res, obj, arg, order, Type, ReprType) res = __atomic_fetch_min (obj, arg, order) +#define zig_atomicrmw_max(res, obj, arg, order, Type, ReprType) res = __atomic_fetch_max (obj, arg, order) +#define zig_atomic_store( obj, arg, order, Type, ReprType) __atomic_store (obj, &(arg), order) +#define zig_atomic_load(res, obj, order, Type, ReprType) __atomic_load (obj, &(res), order) +#undef zig_atomicrmw_xchg_float +#define zig_atomicrmw_xchg_float zig_atomicrmw_xchg +#define zig_fence(order) __atomic_thread_fence(order) +#elif _MSC_VER && (_M_IX86 || _M_X64) +#define memory_order_relaxed 0 +#define memory_order_consume 1 +#define memory_order_acquire 2 +#define memory_order_release 3 +#define memory_order_acq_rel 4 +#define memory_order_seq_cst 5 +#define zig_atomic(Type) Type +#define zig_cmpxchg_strong( obj, expected, desired, succ, fail, Type, ReprType) zig_msvc_cmpxchg_##Type(obj, &(expected), desired) +#define zig_cmpxchg_weak( obj, expected, desired, succ, fail, Type, ReprType) zig_cmpxchg_strong(obj, expected, desired, succ, fail, Type, ReprType) +#define zig_atomicrmw_xchg(res, obj, arg, order, Type, ReprType) res = zig_msvc_atomicrmw_xchg_##Type(obj, arg) +#define zig_atomicrmw_add(res, obj, arg, order, Type, ReprType) res = zig_msvc_atomicrmw_add_ ##Type(obj, arg) +#define zig_atomicrmw_sub(res, obj, arg, order, Type, ReprType) res = zig_msvc_atomicrmw_sub_ ##Type(obj, arg) +#define zig_atomicrmw_or(res, obj, arg, order, Type, ReprType) res = zig_msvc_atomicrmw_or_ ##Type(obj, arg) +#define zig_atomicrmw_xor(res, obj, arg, order, Type, ReprType) res = zig_msvc_atomicrmw_xor_ ##Type(obj, arg) +#define zig_atomicrmw_and(res, obj, arg, order, Type, ReprType) res = zig_msvc_atomicrmw_and_ ##Type(obj, arg) +#define zig_atomicrmw_nand(res, obj, arg, order, Type, ReprType) res = zig_msvc_atomicrmw_nand_##Type(obj, arg) +#define zig_atomicrmw_min(res, obj, arg, order, Type, ReprType) res = zig_msvc_atomicrmw_min_ ##Type(obj, arg) +#define zig_atomicrmw_max(res, obj, arg, order, Type, ReprType) res = zig_msvc_atomicrmw_max_ ##Type(obj, arg) +#define zig_atomic_store( obj, arg, order, Type, ReprType) zig_msvc_atomic_store_ ##Type(obj, arg) +#define zig_atomic_load(res, obj, order, Type, ReprType) res = zig_msvc_atomic_load_ ##Type(obj) +#if _M_X64 +#define zig_fence(order) __faststorefence() +#else +#define zig_fence(order) zig_msvc_atomic_barrier() +#endif +/* TODO: _MSC_VER && (_M_ARM || _M_ARM64) */ +#else +#define memory_order_relaxed 0 +#define memory_order_consume 1 +#define memory_order_acquire 2 +#define memory_order_release 3 +#define memory_order_acq_rel 4 +#define memory_order_seq_cst 5 +#define zig_atomic(Type) Type +#define zig_cmpxchg_strong( obj, expected, desired, succ, fail, Type, ReprType) zig_atomics_unavailable +#define zig_cmpxchg_weak( obj, expected, desired, succ, fail, Type, ReprType) zig_atomics_unavailable +#define zig_atomicrmw_xchg(res, obj, arg, order, Type, ReprType) zig_atomics_unavailable +#define zig_atomicrmw_add(res, obj, arg, order, Type, ReprType) zig_atomics_unavailable +#define zig_atomicrmw_sub(res, obj, arg, order, Type, ReprType) zig_atomics_unavailable +#define zig_atomicrmw_or(res, obj, arg, order, Type, ReprType) zig_atomics_unavailable +#define zig_atomicrmw_xor(res, obj, arg, order, Type, ReprType) zig_atomics_unavailable +#define zig_atomicrmw_and(res, obj, arg, order, Type, ReprType) zig_atomics_unavailable +#define zig_atomicrmw_nand(res, obj, arg, order, Type, ReprType) zig_atomics_unavailable +#define zig_atomicrmw_min(res, obj, arg, order, Type, ReprType) zig_atomics_unavailable +#define zig_atomicrmw_max(res, obj, arg, order, Type, ReprType) zig_atomics_unavailable +#define zig_atomic_store( obj, arg, order, Type, ReprType) zig_atomics_unavailable +#define zig_atomic_load(res, obj, order, Type, ReprType) zig_atomics_unavailable +#define zig_fence(order) zig_fence_unavailable +#endif + #if _MSC_VER && (_M_IX86 || _M_X64) -// TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64 +/* TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64 */ #define zig_msvc_atomics(ZigType, Type, SigType, suffix) \ static inline bool zig_msvc_cmpxchg_##ZigType(Type volatile* obj, Type* expected, Type desired) { \ @@ -3316,51 +3643,30 @@ zig_msvc_atomics(u64, uint64_t, __int64, 64) zig_msvc_atomics(i64, int64_t, __int64, 64) #endif -#define zig_msvc_flt_atomics(Type, ReprType, suffix) \ +#define zig_msvc_flt_atomics(Type, SigType, suffix) \ static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ - ReprType exchange; \ - ReprType comparand; \ - ReprType initial; \ + SigType exchange; \ + SigType comparand; \ + SigType initial; \ bool success; \ memcpy(&comparand, expected, sizeof(comparand)); \ memcpy(&exchange, &desired, sizeof(exchange)); \ - initial = _InterlockedCompareExchange##suffix((ReprType volatile*)obj, exchange, comparand); \ + initial = _InterlockedCompareExchange##suffix((SigType volatile*)obj, exchange, comparand); \ success = initial == comparand; \ if (!success) memcpy(expected, &initial, sizeof(*expected)); \ return success; \ } \ - static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - ReprType repr; \ - ReprType initial; \ + static inline void zig_msvc_atomic_store_##Type(zig_##Type volatile* obj, zig_##Type arg) { \ + SigType value; \ + memcpy(&value, &arg, sizeof(value)); \ + (void)_InterlockedExchange##suffix((SigType volatile*)obj, value); \ + } \ + static inline zig_##Type zig_msvc_atomic_load_##Type(zig_##Type volatile* obj) { \ zig_##Type result; \ - memcpy(&repr, &value, sizeof(repr)); \ - initial = _InterlockedExchange##suffix((ReprType volatile*)obj, repr); \ + SigType initial = _InterlockedExchangeAdd##suffix((SigType volatile*)obj, (SigType)0); \ memcpy(&result, &initial, sizeof(result)); \ return result; \ - } \ - static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - ReprType repr; \ - zig_##Type expected; \ - zig_##Type desired; \ - repr = *(ReprType volatile*)obj; \ - memcpy(&expected, &repr, sizeof(expected)); \ - do { \ - desired = expected + value; \ - } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \ - return expected; \ - } \ - static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - ReprType repr; \ - zig_##Type expected; \ - zig_##Type desired; \ - repr = *(ReprType volatile*)obj; \ - memcpy(&expected, &repr, sizeof(expected)); \ - do { \ - desired = expected - value; \ - } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \ - return expected; \ } - zig_msvc_flt_atomics(f32, long, ) #if _M_X64 zig_msvc_flt_atomics(f64, int64_t, 64) @@ -3421,42 +3727,6 @@ static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expec static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) { return _InterlockedCompareExchange128((__int64 volatile*)obj, (__int64)zig_hi_i128(desired), (__int64)zig_lo_i128(desired), (__int64*)expected); } - -#define zig_msvc_atomics_128xchg(Type) \ - static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - bool success = false; \ - zig_##Type prev; \ - while (!success) { \ - prev = *obj; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, value); \ - } \ - return prev; \ - } - -zig_msvc_atomics_128xchg(u128) -zig_msvc_atomics_128xchg(i128) - -#define zig_msvc_atomics_128op(Type, operation) \ - static inline zig_##Type zig_msvc_atomicrmw_##operation##_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - bool success = false; \ - zig_##Type new; \ - zig_##Type prev; \ - while (!success) { \ - prev = *obj; \ - new = zig_##operation##_##Type(prev, value); \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ - } \ - return prev; \ - } - -zig_msvc_atomics_128op(u128, add) -zig_msvc_atomics_128op(u128, sub) -zig_msvc_atomics_128op(u128, or) -zig_msvc_atomics_128op(u128, xor) -zig_msvc_atomics_128op(u128, and) -zig_msvc_atomics_128op(u128, nand) -zig_msvc_atomics_128op(u128, min) -zig_msvc_atomics_128op(u128, max) #endif /* _M_IX86 */ #endif /* _MSC_VER && (_M_IX86 || _M_X64) */ diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm index 7855cfa8d5..1f6753f6cc 100644 Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index bdff7c4de4..fc364da0b8 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -401,7 +401,6 @@ test "expected [*c]const u8, found [*:0]const u8" { } test "explicit cast from integer to error type" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 302de93591..0b3cf0771a 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1649,3 +1649,15 @@ test "early exit in container level const" { }; try expect(S.value == 1); } + +test "@inComptime" { + const S = struct { + fn inComptime() bool { + return @inComptime(); + } + }; + try expectEqual(false, @inComptime()); + try expectEqual(true, comptime @inComptime()); + try expectEqual(false, S.inComptime()); + try expectEqual(true, comptime S.inComptime()); +} diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index e854764649..0dba59f92f 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -455,6 +455,23 @@ test "method call with optional and error union first param" { try s.errUnion(); } +test "method call with optional pointer first param" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + + const S = struct { + x: i32 = 1234, + + fn method(s: ?*@This()) !void { + try expect(s.?.x == 1234); + } + }; + var s: S = .{}; + try s.method(); + const s_ptr = &s; + try s_ptr.method(); +} + test "using @ptrCast on function pointers" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/type.zig b/test/behavior/type.zig index a12949fffd..0d309b9a6e 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -486,10 +486,9 @@ test "Type.Union from regular enum" { } test "Type.Fn" { - if (true) { - // https://github.com/ziglang/zig/issues/12360 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const some_opaque = opaque {}; const some_ptr = *some_opaque; diff --git a/test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig b/test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig index a700f0d0f2..f808ec8227 100644 --- a/test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig +++ b/test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig @@ -29,10 +29,10 @@ export fn u2m() void { // // :9:1: error: union initializer must initialize one field // :1:12: note: union declared here -// :14:20: error: cannot initialize multiple union fields at once, unions can only have one active field +// :14:20: error: cannot initialize multiple union fields at once; unions can only have one active field // :14:31: note: additional initializer here // :1:12: note: union declared here // :18:21: error: union initializer must initialize one field -// :22:20: error: cannot initialize multiple union fields at once, unions can only have one active field +// :22:20: error: cannot initialize multiple union fields at once; unions can only have one active field // :22:31: note: additional initializer here // :5:12: note: union declared here diff --git a/test/cases/compile_errors/variadic_arg_validation.zig b/test/cases/compile_errors/variadic_arg_validation.zig index 830d3a0877..bddcef92f6 100644 --- a/test/cases/compile_errors/variadic_arg_validation.zig +++ b/test/cases/compile_errors/variadic_arg_validation.zig @@ -21,7 +21,7 @@ pub export fn entry3() void { // backend=stage2 // target=native // -// :4:33: error: integer and float literals passed variadic function must be casted to a fixed-size number type +// :4:33: error: integer and float literals passed to variadic function must be casted to a fixed-size number type // :9:24: error: arrays must be passed by reference to variadic function // :13:24: error: cannot pass 'u48' to variadic function // :13:24: note: only integers with power of two bits are extern compatible diff --git a/test/cases/safety/pointer casting to null function pointer.zig b/test/cases/safety/pointer casting to null function pointer.zig new file mode 100644 index 0000000000..bedbcdda85 --- /dev/null +++ b/test/cases/safety/pointer casting to null function pointer.zig @@ -0,0 +1,23 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "cast causes pointer to be null")) { + std.process.exit(0); + } + std.process.exit(1); +} + +fn getNullPtr() ?*const anyopaque { + return null; +} +pub fn main() !void { + const null_ptr: ?*const anyopaque = getNullPtr(); + const required_ptr: *align(1) const fn() void = @ptrCast(*align(1) const fn() void, null_ptr); + _ = required_ptr; + return error.TestFailed; +} + +// run +// backend=llvm +// target=native diff --git a/test/tests.zig b/test/tests.zig index 3202d19b3e..7ec1aaaa65 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1040,6 +1040,12 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { }); compile_c.addIncludePath("lib"); // for zig.h if (test_target.target.getOsTag() == .windows) { + if (true) { + // Unfortunately this requires about 8G of RAM for clang to compile + // and our Windows CI runners do not have this much. + step.dependOn(&these_tests.step); + continue; + } if (test_target.link_libc == false) { compile_c.subsystem = .Console; compile_c.linkSystemLibrary("kernel32"); diff --git a/test/translate_c.zig b/test/translate_c.zig index f4cd374a06..7534226881 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3956,4 +3956,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ .name = "foo", \\}); }); + + cases.add("string array initializer", + \\static const char foo[] = {"bar"}; + , &[_][]const u8{ + \\pub const foo: [3:0]u8 = "bar"; + }); }