Merge branch 'master' into autodoc-pkg-mod

This commit is contained in:
Loris Cro 2023-04-25 20:03:53 +02:00 committed by GitHub
commit 015ea6fd6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 1712 additions and 599 deletions

View File

@ -1422,7 +1422,8 @@ fn foo() i32 {
{#header_open|Thread Local Variables#}
<p>A variable may be specified to be a thread-local variable using the
{#syntax#}threadlocal{#endsyntax#} keyword:</p>
{#syntax#}threadlocal{#endsyntax#} keyword,
which makes each thread work with a separate instance of the variable:</p>
{#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 {
<p>{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type or an integer vector type.</p>
<p>{#syntax#}operand{#endsyntax#} may be an {#link|integer|Integers#} or {#link|vector|Vectors#}.</p>
<p>
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".
</p>
<p>
If {#syntax#}operand{#endsyntax#} is a {#link|comptime#}-known integer,
@ -8167,7 +8191,7 @@ test "main" {
<p>{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type or an integer vector type.</p>
<p>{#syntax#}operand{#endsyntax#} may be an {#link|integer|Integers#} or {#link|vector|Vectors#}.</p>
<p>
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".
</p>
<p>
If {#syntax#}operand{#endsyntax#} is a {#link|comptime#}-known integer,
@ -8553,16 +8577,27 @@ test "@hasDecl" {
</p>
<ul>
<li>{#syntax#}@import("std"){#endsyntax#} - Zig Standard Library</li>
<li>{#syntax#}@import("builtin"){#endsyntax#} - Target-specific information.
<li>{#syntax#}@import("builtin"){#endsyntax#} - Target-specific information
The command <code>zig build-exe --show-builtin</code> outputs the source to stdout for reference.
</li>
<li>{#syntax#}@import("root"){#endsyntax#} - Points to the root source file.
This is usually <code>src/main.zig</code> but it depends on what file is chosen to be built.
<li>{#syntax#}@import("root"){#endsyntax#} - Root source file
This is usually <code>src/main.zig</code> but depends on what file is built.
</li>
</ul>
{#see_also|Compile Variables|@embedFile#}
{#header_close#}
{#header_open|@inComptime#}
<pre>{#syntax#}@inComptime() bool{#endsyntax#}</pre>
<p>
Returns whether the builtin was run in a {#syntax#}comptime{#endsyntax#} context. The result is a compile-time constant.
</p>
<p>
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.
</p>
{#see_also|comptime#}
{#header_close#}
{#header_open|@intCast#}
<pre>{#syntax#}@intCast(comptime DestType: type, int: anytype) DestType{#endsyntax#}</pre>
<p>
@ -8780,7 +8815,9 @@ test "@wasmMemoryGrow" {
<pre>{#syntax#}@popCount(operand: anytype) anytype{#endsyntax#}</pre>
<p>{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type.</p>
<p>{#syntax#}operand{#endsyntax#} may be an {#link|integer|Integers#} or {#link|vector|Vectors#}.</p>
<p>Counts the number of bits set in an integer.</p>
<p>
Counts the number of bits set in an integer - "population count".
</p>
<p>
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
</p>
<p>{#syntax#}[*c]T{#endsyntax#} - C pointer.</p>
<ul>
<li>Supports all the syntax of the other two pointer types.</li>
<li>Supports all the syntax of the other two pointer types ({#syntax#}*T{#endsyntax#}) and ({#syntax#}[*]T{#endsyntax#}).</li>
<li>Coerces to other pointer types, as well as {#link|Optional Pointers#}.
When a C pointer is coerced to a non-optional pointer, safety-checked
{#link|Undefined Behavior#} occurs if the address is 0.
@ -11966,6 +12005,17 @@ fn readU32Be() u32 {}
</ul>
</td>
</tr>
<tr>
<th scope="row">
<pre>{#syntax#}noinline{#endsyntax#}</pre>
</th>
<td>
{#syntax#}noinline{#endsyntax#} disallows function to be inlined in all call sites.
<ul>
<li>See also {#link|Functions#}</li>
</ul>
</td>
</tr>
<tr>
<th scope="row">
<pre>{#syntax#}nosuspend{#endsyntax#}</pre>

View File

@ -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);

View File

@ -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.
//!

View File

@ -1,4 +1,4 @@
//! Implements URI parsing roughly adhering to <https://tools.ietf.org/html/rfc3986>.
//! Uniform Resource Identifier (URI) parsing roughly adhering to <https://tools.ietf.org/html/rfc3986>.
//! Does not do perfect grammar and character class checking, but should be robust against URIs in the wild.
const Uri = @This();

View File

@ -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();

View File

@ -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);

View File

@ -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.
///

View File

@ -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;

View File

@ -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,
};

View File

@ -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;

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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(

View File

@ -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);

View File

@ -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].*;

View File

@ -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,

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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"`

View File

@ -275,4 +275,5 @@ test {
_ = Client;
_ = Method;
_ = Status;
_ = @import("http/test.zig");
}

View File

@ -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),

View File

@ -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());
}

View File

@ -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();
}

View File

@ -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;

72
lib/std/http/test.zig Normal file
View File

@ -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();
}

View File

@ -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,

View File

@ -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),

View File

@ -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 => {

View File

@ -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);

View File

@ -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;

View File

@ -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))) {

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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);
},

View File

@ -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`.

View File

@ -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();

View File

@ -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,

View File

@ -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],
},
} });
}

View File

@ -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;

View File

@ -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",
.{

View File

@ -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,
};
}

View File

@ -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,

View File

@ -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,

View File

@ -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});
}

View File

@ -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;

View File

@ -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)"

View File

@ -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| {

View File

@ -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],...

View File

@ -466,6 +466,7 @@ const Writer = struct {
.frame_address,
.breakpoint,
.c_va_start,
.in_comptime,
=> try self.writeExtNode(stream, extended),
.builtin_src => {

View File

@ -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 => {},

View File

@ -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<const clang::Expr *>(self);
auto cast = clang::dyn_cast<const clang::StringLiteral>(casted_self);
return reinterpret_cast<const ZigClangStringLiteral *>(cast);
}
const ZigClangExpr *ZigClangInitListExpr_getInit(const ZigClangInitListExpr *self, unsigned i) {
auto casted = reinterpret_cast<const clang::InitListExpr *>(self);
const clang::Expr *result = casted->getInit(i);
@ -2394,6 +2400,16 @@ const ZigClangExpr *ZigClangInitListExpr_getArrayFiller(const ZigClangInitListEx
return reinterpret_cast<const ZigClangExpr *>(result);
}
bool ZigClangInitListExpr_hasArrayFiller(const ZigClangInitListExpr *self) {
auto casted = reinterpret_cast<const clang::InitListExpr *>(self);
return casted->hasArrayFiller();
}
bool ZigClangInitListExpr_isStringLiteralInit(const ZigClangInitListExpr *self) {
auto casted = reinterpret_cast<const clang::InitListExpr *>(self);
return casted->isStringLiteralInit();
}
const ZigClangFieldDecl *ZigClangInitListExpr_getInitializedFieldInUnion(const ZigClangInitListExpr *self) {
auto casted = reinterpret_cast<const clang::InitListExpr *>(self);
const clang::FieldDecl *result = casted->getInitializedFieldInUnion();

View File

@ -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);

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -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

View File

@ -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());
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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";
});
}