mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
Merge branch 'master' into autodoc-pkg-mod
This commit is contained in:
commit
015ea6fd6c
@ -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>
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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.
|
||||
//!
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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.
|
||||
///
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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].*;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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"`
|
||||
|
||||
@ -275,4 +275,5 @@ test {
|
||||
_ = Client;
|
||||
_ = Method;
|
||||
_ = Status;
|
||||
_ = @import("http/test.zig");
|
||||
}
|
||||
|
||||
@ -645,7 +645,6 @@ pub const Request = struct {
|
||||
if (req.response.parser.state.isContent()) break;
|
||||
}
|
||||
|
||||
req.response.headers = http.Headers{ .allocator = req.client.allocator, .owned = false };
|
||||
try req.response.parse(req.response.parser.header_bytes.items);
|
||||
|
||||
if (req.response.status == .switching_protocols) {
|
||||
@ -765,7 +764,7 @@ pub const Request = struct {
|
||||
}
|
||||
|
||||
if (has_trail) {
|
||||
req.response.headers = http.Headers{ .allocator = req.client.allocator, .owned = false };
|
||||
req.response.headers.clearRetainingCapacity();
|
||||
|
||||
// The response headers before the trailers are already guaranteed to be valid, so they will always be parsed again and cannot return an error.
|
||||
// This will *only* fail for a malformed trailer.
|
||||
@ -797,18 +796,18 @@ pub const Request = struct {
|
||||
|
||||
/// Write `bytes` to the server. The `transfer_encoding` request header determines how data will be sent.
|
||||
pub fn write(req: *Request, bytes: []const u8) WriteError!usize {
|
||||
switch (req.headers.transfer_encoding) {
|
||||
switch (req.transfer_encoding) {
|
||||
.chunked => {
|
||||
try req.connection.data.conn.writer().print("{x}\r\n", .{bytes.len});
|
||||
try req.connection.data.conn.writeAll(bytes);
|
||||
try req.connection.data.conn.writeAll("\r\n");
|
||||
try req.connection.data.buffered.writer().print("{x}\r\n", .{bytes.len});
|
||||
try req.connection.data.buffered.writeAll(bytes);
|
||||
try req.connection.data.buffered.writeAll("\r\n");
|
||||
|
||||
return bytes.len;
|
||||
},
|
||||
.content_length => |*len| {
|
||||
if (len.* < bytes.len) return error.MessageTooLong;
|
||||
|
||||
const amt = try req.connection.data.conn.write(bytes);
|
||||
const amt = try req.connection.data.buffered.write(bytes);
|
||||
len.* -= amt;
|
||||
return amt;
|
||||
},
|
||||
@ -828,7 +827,7 @@ pub const Request = struct {
|
||||
/// Finish the body of a request. This notifies the server that you have no more data to send.
|
||||
pub fn finish(req: *Request) FinishError!void {
|
||||
switch (req.transfer_encoding) {
|
||||
.chunked => try req.connection.data.conn.writeAll("0\r\n\r\n"),
|
||||
.chunked => try req.connection.data.buffered.writeAll("0\r\n\r\n"),
|
||||
.content_length => |len| if (len != 0) return error.MessageNotCompleted,
|
||||
.none => {},
|
||||
}
|
||||
@ -1019,7 +1018,7 @@ pub fn request(client: *Client, method: http.Method, uri: Uri, headers: http.Hea
|
||||
.status = undefined,
|
||||
.reason = undefined,
|
||||
.version = undefined,
|
||||
.headers = undefined,
|
||||
.headers = http.Headers{ .allocator = client.allocator, .owned = false },
|
||||
.parser = switch (options.header_strategy) {
|
||||
.dynamic => |max| proto.HeadersParser.initDynamic(max),
|
||||
.static => |buf| proto.HeadersParser.initStatic(buf),
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
72
lib/std/http/test.zig
Normal 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();
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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))) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
},
|
||||
|
||||
@ -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`.
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
|
||||
175
src/AstGen.zig
175
src/AstGen.zig
@ -839,12 +839,9 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
|
||||
.slice_open => {
|
||||
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
|
||||
|
||||
maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const line = gz.astgen.source_line - gz.decl_line;
|
||||
const column = gz.astgen.source_column;
|
||||
|
||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
|
||||
try emitDbgStmt(gz, line, column);
|
||||
try emitDbgStmt(gz, cursor);
|
||||
const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{
|
||||
.lhs = lhs,
|
||||
.start = start,
|
||||
@ -854,14 +851,11 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
|
||||
.slice => {
|
||||
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
|
||||
|
||||
maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const line = gz.astgen.source_line - gz.decl_line;
|
||||
const column = gz.astgen.source_column;
|
||||
|
||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
|
||||
const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
|
||||
const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end);
|
||||
try emitDbgStmt(gz, line, column);
|
||||
try emitDbgStmt(gz, cursor);
|
||||
const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{
|
||||
.lhs = lhs,
|
||||
.start = start,
|
||||
@ -872,15 +866,12 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
|
||||
.slice_sentinel => {
|
||||
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
|
||||
|
||||
maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const line = gz.astgen.source_line - gz.decl_line;
|
||||
const column = gz.astgen.source_column;
|
||||
|
||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
|
||||
const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
|
||||
const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
|
||||
const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
|
||||
try emitDbgStmt(gz, line, column);
|
||||
try emitDbgStmt(gz, cursor);
|
||||
const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
|
||||
.lhs = lhs,
|
||||
.start = start,
|
||||
@ -914,20 +905,16 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
|
||||
.ref => {
|
||||
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
|
||||
|
||||
maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const line = gz.astgen.source_line - gz.decl_line;
|
||||
const column = gz.astgen.source_column;
|
||||
try emitDbgStmt(gz, line, column);
|
||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
try emitDbgStmt(gz, cursor);
|
||||
|
||||
return gz.addUnNode(.optional_payload_safe_ptr, lhs, node);
|
||||
},
|
||||
else => {
|
||||
const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
|
||||
|
||||
maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const line = gz.astgen.source_line - gz.decl_line;
|
||||
const column = gz.astgen.source_column;
|
||||
try emitDbgStmt(gz, line, column);
|
||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
try emitDbgStmt(gz, cursor);
|
||||
|
||||
return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node);
|
||||
},
|
||||
@ -3330,23 +3317,17 @@ fn assignOp(
|
||||
|
||||
const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
|
||||
|
||||
var line: u32 = undefined;
|
||||
var column: u32 = undefined;
|
||||
switch (op_inst_tag) {
|
||||
.add, .sub, .mul, .div, .mod_rem => {
|
||||
maybeAdvanceSourceCursorToMainToken(gz, infix_node);
|
||||
line = gz.astgen.source_line - gz.decl_line;
|
||||
column = gz.astgen.source_column;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
const cursor = switch (op_inst_tag) {
|
||||
.add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, infix_node),
|
||||
else => undefined,
|
||||
};
|
||||
const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
|
||||
const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
|
||||
const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = lhs_type } }, node_datas[infix_node].rhs);
|
||||
|
||||
switch (op_inst_tag) {
|
||||
.add, .sub, .mul, .div, .mod_rem => {
|
||||
try emitDbgStmt(gz, line, column);
|
||||
try emitDbgStmt(gz, cursor);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@ -5360,8 +5341,7 @@ fn tryExpr(
|
||||
if (!parent_gz.is_comptime) {
|
||||
try emitDbgNode(parent_gz, node);
|
||||
}
|
||||
const try_line = astgen.source_line - parent_gz.decl_line;
|
||||
const try_column = astgen.source_column;
|
||||
const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
|
||||
|
||||
const operand_ri: ResultInfo = switch (ri.rl) {
|
||||
.ref => .{ .rl = .ref, .ctx = .error_handling_expr },
|
||||
@ -5382,7 +5362,7 @@ fn tryExpr(
|
||||
};
|
||||
const err_code = try else_scope.addUnNode(err_tag, operand, node);
|
||||
try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
|
||||
try emitDbgStmt(&else_scope, try_line, try_column);
|
||||
try emitDbgStmt(&else_scope, try_lc);
|
||||
_ = try else_scope.addUnNode(.ret_node, err_code, node);
|
||||
|
||||
try else_scope.setTryBody(try_inst, operand);
|
||||
@ -5607,10 +5587,8 @@ fn addFieldAccess(
|
||||
const str_index = try astgen.identAsString(field_ident);
|
||||
const lhs = try expr(gz, scope, lhs_ri, object_node);
|
||||
|
||||
maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const line = gz.astgen.source_line - gz.decl_line;
|
||||
const column = gz.astgen.source_column;
|
||||
try emitDbgStmt(gz, line, column);
|
||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
try emitDbgStmt(gz, cursor);
|
||||
|
||||
return gz.addPlNode(tag, node, Zir.Inst.Field{
|
||||
.lhs = lhs,
|
||||
@ -5630,24 +5608,20 @@ fn arrayAccess(
|
||||
.ref => {
|
||||
const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
|
||||
|
||||
maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const line = gz.astgen.source_line - gz.decl_line;
|
||||
const column = gz.astgen.source_column;
|
||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
|
||||
const rhs = try expr(gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_datas[node].rhs);
|
||||
try emitDbgStmt(gz, line, column);
|
||||
try emitDbgStmt(gz, cursor);
|
||||
|
||||
return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
|
||||
},
|
||||
else => {
|
||||
const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
|
||||
|
||||
maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const line = gz.astgen.source_line - gz.decl_line;
|
||||
const column = gz.astgen.source_column;
|
||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
|
||||
const rhs = try expr(gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_datas[node].rhs);
|
||||
try emitDbgStmt(gz, line, column);
|
||||
try emitDbgStmt(gz, cursor);
|
||||
|
||||
return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node);
|
||||
},
|
||||
@ -5674,21 +5648,15 @@ fn simpleBinOp(
|
||||
}
|
||||
|
||||
const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node);
|
||||
var line: u32 = undefined;
|
||||
var column: u32 = undefined;
|
||||
switch (op_inst_tag) {
|
||||
.add, .sub, .mul, .div, .mod_rem => {
|
||||
maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
line = gz.astgen.source_line - gz.decl_line;
|
||||
column = gz.astgen.source_column;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
const cursor = switch (op_inst_tag) {
|
||||
.add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, node),
|
||||
else => undefined,
|
||||
};
|
||||
const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].rhs, node);
|
||||
|
||||
switch (op_inst_tag) {
|
||||
.add, .sub, .mul, .div, .mod_rem => {
|
||||
try emitDbgStmt(gz, line, column);
|
||||
try emitDbgStmt(gz, cursor);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@ -6787,14 +6755,15 @@ fn switchExpr(
|
||||
}
|
||||
|
||||
const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };
|
||||
|
||||
astgen.advanceSourceCursorToNode(operand_node);
|
||||
const operand_line = astgen.source_line - parent_gz.decl_line;
|
||||
const operand_column = astgen.source_column;
|
||||
const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
|
||||
|
||||
const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
|
||||
const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond;
|
||||
const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node);
|
||||
// Sema expects a dbg_stmt immediately after switch_cond(_ref)
|
||||
try emitDbgStmt(parent_gz, operand_line, operand_column);
|
||||
try emitDbgStmt(parent_gz, operand_lc);
|
||||
// We need the type of the operand to use as the result location for all the prong items.
|
||||
const cond_ty_inst = try parent_gz.addUnNode(.typeof, cond, operand_node);
|
||||
const item_ri: ResultInfo = .{ .rl = .{ .ty = cond_ty_inst } };
|
||||
@ -7154,8 +7123,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
if (!gz.is_comptime) {
|
||||
try emitDbgNode(gz, node);
|
||||
}
|
||||
const ret_line = astgen.source_line - gz.decl_line;
|
||||
const ret_column = astgen.source_column;
|
||||
const ret_lc = LineColumn{ astgen.source_line - gz.decl_line, astgen.source_column };
|
||||
|
||||
const defer_outer = &astgen.fn_block.?.base;
|
||||
|
||||
@ -7179,13 +7147,13 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
const defer_counts = countDefers(defer_outer, scope);
|
||||
if (!defer_counts.need_err_code) {
|
||||
try genDefers(gz, defer_outer, scope, .both_sans_err);
|
||||
try emitDbgStmt(gz, ret_line, ret_column);
|
||||
try emitDbgStmt(gz, ret_lc);
|
||||
_ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
}
|
||||
const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
|
||||
try genDefers(gz, defer_outer, scope, .{ .both = err_code });
|
||||
try emitDbgStmt(gz, ret_line, ret_column);
|
||||
try emitDbgStmt(gz, ret_lc);
|
||||
_ = try gz.addUnNode(.ret_node, err_code, node);
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
}
|
||||
@ -7210,7 +7178,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
// As our last action before the return, "pop" the error trace if needed
|
||||
_ = try gz.addRestoreErrRetIndex(.ret, .always);
|
||||
|
||||
try emitDbgStmt(gz, ret_line, ret_column);
|
||||
try emitDbgStmt(gz, ret_lc);
|
||||
try gz.addRet(ri, operand, node);
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
},
|
||||
@ -7218,7 +7186,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
// Value is always an error. Emit both error defers and regular defers.
|
||||
const err_code = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
|
||||
try genDefers(gz, defer_outer, scope, .{ .both = err_code });
|
||||
try emitDbgStmt(gz, ret_line, ret_column);
|
||||
try emitDbgStmt(gz, ret_lc);
|
||||
try gz.addRet(ri, operand, node);
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
},
|
||||
@ -7227,7 +7195,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
if (!defer_counts.have_err) {
|
||||
// Only regular defers; no branch needed.
|
||||
try genDefers(gz, defer_outer, scope, .normal_only);
|
||||
try emitDbgStmt(gz, ret_line, ret_column);
|
||||
try emitDbgStmt(gz, ret_lc);
|
||||
|
||||
// As our last action before the return, "pop" the error trace if needed
|
||||
const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
|
||||
@ -7250,7 +7218,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
// As our last action before the return, "pop" the error trace if needed
|
||||
_ = try then_scope.addRestoreErrRetIndex(.ret, .always);
|
||||
|
||||
try emitDbgStmt(&then_scope, ret_line, ret_column);
|
||||
try emitDbgStmt(&then_scope, ret_lc);
|
||||
try then_scope.addRet(ri, operand, node);
|
||||
|
||||
var else_scope = gz.makeSubBlock(scope);
|
||||
@ -7260,7 +7228,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
.both = try else_scope.addUnNode(.err_union_code, result, node),
|
||||
};
|
||||
try genDefers(&else_scope, defer_outer, scope, which_ones);
|
||||
try emitDbgStmt(&else_scope, ret_line, ret_column);
|
||||
try emitDbgStmt(&else_scope, ret_lc);
|
||||
try else_scope.addRet(ri, operand, node);
|
||||
|
||||
try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0);
|
||||
@ -8174,6 +8142,7 @@ fn builtinCall(
|
||||
.frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node),
|
||||
.frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node),
|
||||
.breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),
|
||||
.in_comptime => return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime, node), node),
|
||||
|
||||
.type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
|
||||
.size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
|
||||
@ -8649,11 +8618,14 @@ fn typeCast(
|
||||
rhs_node: Ast.Node.Index,
|
||||
tag: Zir.Inst.Tag,
|
||||
) InnerError!Zir.Inst.Ref {
|
||||
try emitDbgNode(gz, node);
|
||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const result_type = try typeExpr(gz, scope, lhs_node);
|
||||
const operand = try expr(gz, scope, .{ .rl = .none }, rhs_node);
|
||||
|
||||
try emitDbgStmt(gz, cursor);
|
||||
const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
|
||||
.lhs = try typeExpr(gz, scope, lhs_node),
|
||||
.rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node),
|
||||
.lhs = result_type,
|
||||
.rhs = operand,
|
||||
});
|
||||
return rvalue(gz, ri, result, node);
|
||||
}
|
||||
@ -8680,14 +8652,15 @@ fn simpleUnOp(
|
||||
operand_node: Ast.Node.Index,
|
||||
tag: Zir.Inst.Tag,
|
||||
) InnerError!Zir.Inst.Ref {
|
||||
switch (tag) {
|
||||
.tag_name, .error_name, .ptr_to_int => try emitDbgNode(gz, node),
|
||||
else => {},
|
||||
}
|
||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const operand = if (tag == .compile_error)
|
||||
try comptimeExpr(gz, scope, operand_ri, operand_node)
|
||||
else
|
||||
try expr(gz, scope, operand_ri, operand_node);
|
||||
switch (tag) {
|
||||
.tag_name, .error_name, .ptr_to_int => try emitDbgStmt(gz, cursor),
|
||||
else => {},
|
||||
}
|
||||
const result = try gz.addUnNode(tag, operand, node);
|
||||
return rvalue(gz, ri, result, node);
|
||||
}
|
||||
@ -8759,12 +8732,12 @@ fn divBuiltin(
|
||||
rhs_node: Ast.Node.Index,
|
||||
tag: Zir.Inst.Tag,
|
||||
) InnerError!Zir.Inst.Ref {
|
||||
try emitDbgNode(gz, node);
|
||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
|
||||
const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node);
|
||||
|
||||
const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
|
||||
.lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node),
|
||||
.rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node),
|
||||
});
|
||||
try emitDbgStmt(gz, cursor);
|
||||
const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
|
||||
return rvalue(gz, ri, result, node);
|
||||
}
|
||||
|
||||
@ -8813,23 +8786,21 @@ fn shiftOp(
|
||||
rhs_node: Ast.Node.Index,
|
||||
tag: Zir.Inst.Tag,
|
||||
) InnerError!Zir.Inst.Ref {
|
||||
var line = gz.astgen.source_line - gz.decl_line;
|
||||
var column = gz.astgen.source_column;
|
||||
const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
|
||||
|
||||
switch (gz.astgen.tree.nodes.items(.tag)[node]) {
|
||||
.shl, .shr => {
|
||||
maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||
line = gz.astgen.source_line - gz.decl_line;
|
||||
column = gz.astgen.source_column;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
const cursor = switch (gz.astgen.tree.nodes.items(.tag)[node]) {
|
||||
.shl, .shr => maybeAdvanceSourceCursorToMainToken(gz, node),
|
||||
else => undefined,
|
||||
};
|
||||
|
||||
const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node);
|
||||
const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node);
|
||||
|
||||
try emitDbgStmt(gz, line, column);
|
||||
switch (gz.astgen.tree.nodes.items(.tag)[node]) {
|
||||
.shl, .shr => try emitDbgStmt(gz, cursor),
|
||||
else => undefined,
|
||||
}
|
||||
|
||||
const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
|
||||
.lhs = lhs,
|
||||
.rhs = rhs,
|
||||
@ -12593,16 +12564,20 @@ fn detectLocalShadowing(
|
||||
};
|
||||
}
|
||||
|
||||
const LineColumn = struct { u32, u32 };
|
||||
|
||||
/// Advances the source cursor to the main token of `node` if not in comptime scope.
|
||||
/// Usually paired with `emitDbgStmt`.
|
||||
fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) void {
|
||||
if (gz.is_comptime) return;
|
||||
fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) LineColumn {
|
||||
if (gz.is_comptime) return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column };
|
||||
|
||||
const tree = gz.astgen.tree;
|
||||
const token_starts = tree.tokens.items(.start);
|
||||
const main_tokens = tree.nodes.items(.main_token);
|
||||
const node_start = token_starts[main_tokens[node]];
|
||||
gz.astgen.advanceSourceCursor(node_start);
|
||||
|
||||
return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column };
|
||||
}
|
||||
|
||||
/// Advances the source cursor to the beginning of `node`.
|
||||
@ -12806,13 +12781,13 @@ fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
|
||||
return @intCast(u32, count);
|
||||
}
|
||||
|
||||
fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void {
|
||||
fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void {
|
||||
if (gz.is_comptime) return;
|
||||
|
||||
_ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
|
||||
.dbg_stmt = .{
|
||||
.line = line,
|
||||
.column = column,
|
||||
.line = lc[0],
|
||||
.column = lc[1],
|
||||
},
|
||||
} });
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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",
|
||||
.{
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
64
src/Sema.zig
64
src/Sema.zig
@ -1166,6 +1166,7 @@ fn analyzeBodyInner(
|
||||
.work_item_id => try sema.zirWorkItem( block, extended, extended.opcode),
|
||||
.work_group_size => try sema.zirWorkItem( block, extended, extended.opcode),
|
||||
.work_group_id => try sema.zirWorkItem( block, extended, extended.opcode),
|
||||
.in_comptime => try sema.zirInComptime( block),
|
||||
// zig fmt: on
|
||||
|
||||
.fence => {
|
||||
@ -4155,7 +4156,7 @@ fn validateUnionInit(
|
||||
const msg = try sema.errMsg(
|
||||
block,
|
||||
init_src,
|
||||
"cannot initialize multiple union fields at once, unions can only have one active field",
|
||||
"cannot initialize multiple union fields at once; unions can only have one active field",
|
||||
.{},
|
||||
);
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
@ -9646,7 +9647,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
|
||||
.Union => "union",
|
||||
else => unreachable,
|
||||
};
|
||||
return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', {s} does not have a guaranteed in-memory layout", .{
|
||||
return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}'; {s} does not have a guaranteed in-memory layout", .{
|
||||
dest_ty.fmt(sema.mod), container,
|
||||
});
|
||||
},
|
||||
@ -9709,7 +9710,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
|
||||
.Union => "union",
|
||||
else => unreachable,
|
||||
};
|
||||
return sema.fail(block, operand_src, "cannot @bitCast from '{}', {s} does not have a guaranteed in-memory layout", .{
|
||||
return sema.fail(block, operand_src, "cannot @bitCast from '{}'; {s} does not have a guaranteed in-memory layout", .{
|
||||
operand_ty.fmt(sema.mod), container,
|
||||
});
|
||||
},
|
||||
@ -19626,7 +19627,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src, operand_src);
|
||||
if (block.wantSafety() and try sema.typeHasRuntimeBits(elem_ty)) {
|
||||
if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag() == .Fn)) {
|
||||
if (!ptr_ty.isAllowzeroPtr()) {
|
||||
const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);
|
||||
try sema.addSafetyCheck(block, is_non_zero, .cast_to_null);
|
||||
@ -19852,7 +19853,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
|
||||
|
||||
try sema.requireRuntimeBlock(block, src, null);
|
||||
if (block.wantSafety() and operand_ty.ptrAllowsZero() and !dest_ty.ptrAllowsZero() and
|
||||
try sema.typeHasRuntimeBits(dest_ty.elemType2()))
|
||||
(try sema.typeHasRuntimeBits(dest_ty.elemType2()) or dest_ty.elemType2().zigTypeTag() == .Fn))
|
||||
{
|
||||
const ptr_int = try block.addUnOp(.ptrtoint, ptr);
|
||||
const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize);
|
||||
@ -22466,6 +22467,18 @@ fn zirWorkItem(
|
||||
});
|
||||
}
|
||||
|
||||
fn zirInComptime(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
_ = sema;
|
||||
if (block.is_comptime) {
|
||||
return Air.Inst.Ref.bool_true;
|
||||
} else {
|
||||
return Air.Inst.Ref.bool_false;
|
||||
}
|
||||
}
|
||||
|
||||
fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
|
||||
if (block.is_comptime) {
|
||||
const msg = msg: {
|
||||
@ -23738,7 +23751,6 @@ fn fieldCallBind(
|
||||
{
|
||||
const first_param_type = decl_type.fnParamType(0);
|
||||
const first_param_tag = first_param_type.tag();
|
||||
var opt_buf: Type.Payload.ElemType = undefined;
|
||||
// zig fmt: off
|
||||
if (first_param_tag == .var_args_param or
|
||||
first_param_tag == .generic_poison or (
|
||||
@ -23764,17 +23776,29 @@ fn fieldCallBind(
|
||||
.arg0_inst = deref,
|
||||
});
|
||||
return sema.addConstant(ty, value);
|
||||
} else if (first_param_tag != .generic_poison and first_param_type.zigTypeTag() == .Optional and
|
||||
first_param_type.optionalChild(&opt_buf).eql(concrete_ty, sema.mod))
|
||||
{
|
||||
const deref = try sema.analyzeLoad(block, src, object_ptr, src);
|
||||
const ty = Type.Tag.bound_fn.init();
|
||||
const value = try Value.Tag.bound_fn.create(arena, .{
|
||||
.func_inst = decl_val,
|
||||
.arg0_inst = deref,
|
||||
});
|
||||
return sema.addConstant(ty, value);
|
||||
} else if (first_param_tag != .generic_poison and first_param_type.zigTypeTag() == .ErrorUnion and
|
||||
} else if (first_param_type.zigTypeTag() == .Optional) {
|
||||
var opt_buf: Type.Payload.ElemType = undefined;
|
||||
const child = first_param_type.optionalChild(&opt_buf);
|
||||
if (child.eql(concrete_ty, sema.mod)) {
|
||||
const deref = try sema.analyzeLoad(block, src, object_ptr, src);
|
||||
const ty = Type.Tag.bound_fn.init();
|
||||
const value = try Value.Tag.bound_fn.create(arena, .{
|
||||
.func_inst = decl_val,
|
||||
.arg0_inst = deref,
|
||||
});
|
||||
return sema.addConstant(ty, value);
|
||||
} else if (child.zigTypeTag() == .Pointer and
|
||||
child.ptrSize() == .One and
|
||||
child.childType().eql(concrete_ty, sema.mod))
|
||||
{
|
||||
const ty = Type.Tag.bound_fn.init();
|
||||
const value = try Value.Tag.bound_fn.create(arena, .{
|
||||
.func_inst = decl_val,
|
||||
.arg0_inst = object_ptr,
|
||||
});
|
||||
return sema.addConstant(ty, value);
|
||||
}
|
||||
} else if (first_param_type.zigTypeTag() == .ErrorUnion and
|
||||
first_param_type.errorUnionPayload().eql(concrete_ty, sema.mod))
|
||||
{
|
||||
const deref = try sema.analyzeLoad(block, src, object_ptr, src);
|
||||
@ -26434,7 +26458,7 @@ fn coerceVarArgParam(
|
||||
.ComptimeInt, .ComptimeFloat => return sema.fail(
|
||||
block,
|
||||
inst_src,
|
||||
"integer and float literals passed variadic function must be casted to a fixed-size number type",
|
||||
"integer and float literals passed to variadic function must be casted to a fixed-size number type",
|
||||
.{},
|
||||
),
|
||||
.Fn => blk: {
|
||||
@ -27718,7 +27742,7 @@ fn coerceCompatiblePtrs(
|
||||
try sema.requireRuntimeBlock(block, inst_src, null);
|
||||
const inst_allows_zero = inst_ty.zigTypeTag() != .Pointer or inst_ty.ptrAllowsZero();
|
||||
if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero() and
|
||||
try sema.typeHasRuntimeBits(dest_ty.elemType2()))
|
||||
(try sema.typeHasRuntimeBits(dest_ty.elemType2()) or dest_ty.elemType2().zigTypeTag() == .Fn))
|
||||
{
|
||||
const actual_ptr = if (inst_ty.isSlice())
|
||||
try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty)
|
||||
@ -27891,7 +27915,7 @@ fn coerceAnonStructToUnion(
|
||||
const msg = if (field_count > 1) try sema.errMsg(
|
||||
block,
|
||||
inst_src,
|
||||
"cannot initialize multiple union fields at once, unions can only have one active field",
|
||||
"cannot initialize multiple union fields at once; unions can only have one active field",
|
||||
.{},
|
||||
) else try sema.errMsg(
|
||||
block,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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});
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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)"
|
||||
|
||||
@ -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| {
|
||||
|
||||
@ -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],...
|
||||
|
||||
@ -466,6 +466,7 @@ const Writer = struct {
|
||||
.frame_address,
|
||||
.breakpoint,
|
||||
.c_va_start,
|
||||
.in_comptime,
|
||||
=> try self.writeExtNode(stream, extended),
|
||||
|
||||
.builtin_src => {
|
||||
|
||||
@ -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 => {},
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
966
stage1/zig.h
966
stage1/zig.h
File diff suppressed because it is too large
Load Diff
BIN
stage1/zig1.wasm
BIN
stage1/zig1.wasm
Binary file not shown.
@ -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
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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");
|
||||
|
||||
@ -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";
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user