From b7af9edb8a8802c35f1a460f8dafff8643b34639 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 14 Apr 2018 02:12:19 -0400 Subject: add std.os.createThread this adds kernel thread support to the standard library for linux. See #174 --- src/ir.cpp | 2 +- std/fmt/index.zig | 26 +++++----- std/os/index.zig | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ std/os/linux/index.zig | 30 +++++++++++ std/os/linux/x86_64.zig | 3 ++ std/os/test.zig | 39 +++++++++++++++ std/special/builtin.zig | 35 +++++++++++++ 7 files changed, 250 insertions(+), 14 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 3ba58a09bd..0fac1bd219 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18407,6 +18407,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAddImplicitReturnType: case IrInstructionIdMergeErrRetTraces: case IrInstructionIdMarkErrRetTracePtr: + case IrInstructionIdAtomicRmw: return true; case IrInstructionIdPhi: @@ -18487,7 +18488,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCoroSize: case IrInstructionIdCoroSuspend: case IrInstructionIdCoroFree: - case IrInstructionIdAtomicRmw: case IrInstructionIdCoroPromise: case IrInstructionIdPromiseResultType: return false; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index cfdd70e95b..4be5c6f827 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -8,25 +8,25 @@ const errol3 = @import("errol/index.zig").errol3; const max_int_digits = 65; -const State = enum { // TODO put inside format function and make sure the name and debug info is correct - Start, - OpenBrace, - CloseBrace, - Integer, - IntegerWidth, - Float, - FloatWidth, - Character, - Buf, - BufWidth, -}; - /// Renders fmt string with args, calling output with slices of bytes. /// If `output` returns an error, the error is returned from `format` and /// `output` is not called again. pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void { + const State = enum { + Start, + OpenBrace, + CloseBrace, + Integer, + IntegerWidth, + Float, + FloatWidth, + Character, + Buf, + BufWidth, + }; + comptime var start_index = 0; comptime var state = State.Start; comptime var next_arg = 0; diff --git a/std/os/index.zig b/std/os/index.zig index b6caed6f53..15b54f2e98 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2384,3 +2384,132 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. } } + +pub const Thread = struct { + pid: i32, + allocator: ?&mem.Allocator, + stack: []u8, + + pub fn wait(self: &const Thread) void { + while (true) { + const pid_value = self.pid; // TODO atomic load + if (pid_value == 0) break; + const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null); + switch (linux.getErrno(rc)) { + 0 => continue, + posix.EINTR => continue, + posix.EAGAIN => continue, + else => unreachable, + } + } + if (self.allocator) |a| { + a.free(self.stack); + } + } +}; + +pub const SpawnThreadError = error { + /// A system-imposed limit on the number of threads was encountered. + /// There are a number of limits that may trigger this error: + /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), + /// which limits the number of processes and threads for a real + /// user ID, was reached; + /// * the kernel's system-wide limit on the number of processes and + /// threads, /proc/sys/kernel/threads-max, was reached (see + /// proc(5)); + /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was + /// reached (see proc(5)); or + /// * the PID limit (pids.max) imposed by the cgroup "process num‐ + /// ber" (PIDs) controller was reached. + ThreadQuotaExceeded, + + /// The kernel cannot allocate sufficient memory to allocate a task structure + /// for the child, or to copy those parts of the caller's context that need to + /// be copied. + SystemResources, + + Unexpected, +}; + +pub const SpawnThreadAllocatorError = SpawnThreadError || error{OutOfMemory}; + +/// caller must call wait on the returned thread +/// fn startFn(@typeOf(context)) T +/// where T is u8, noreturn, void, or !void +pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime startFn: var) SpawnThreadAllocatorError!&Thread { + // TODO compile-time call graph analysis to determine stack upper bound + // https://github.com/zig-lang/zig/issues/157 + const default_stack_size = 8 * 1024 * 1024; + const stack_bytes = try allocator.alloc(u8, default_stack_size); + const thread = try spawnThread(stack_bytes, context, startFn); + thread.allocator = allocator; + return thread; +} + +/// stack must be big enough to store one Thread and one @typeOf(context), each with default alignment, at the end +/// fn startFn(@typeOf(context)) T +/// where T is u8, noreturn, void, or !void +/// caller must call wait on the returned thread +pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThreadError!&Thread { + const Context = @typeOf(context); + comptime assert(@ArgType(@typeOf(startFn), 0) == Context); + + var stack_end: usize = @ptrToInt(stack.ptr) + stack.len; + var arg: usize = undefined; + if (@sizeOf(Context) != 0) { + stack_end -= @sizeOf(Context); + stack_end -= stack_end % @alignOf(Context); + assert(stack_end >= @ptrToInt(stack.ptr)); + const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end)); + *context_ptr = context; + arg = stack_end; + } + + stack_end -= @sizeOf(Thread); + stack_end -= stack_end % @alignOf(Thread); + assert(stack_end >= @ptrToInt(stack.ptr)); + const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end)); + thread_ptr.stack = stack; + thread_ptr.allocator = null; + + const threadMain = struct { + extern fn threadMain(ctx_addr: usize) u8 { + if (@sizeOf(Context) == 0) { + return startFn({}); + } else { + return startFn(*@intToPtr(&const Context, ctx_addr)); + } + } + }.threadMain; + + const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND + | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS + | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; + const newtls: usize = 0; + const rc = posix.clone(threadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid); + const err = posix.getErrno(rc); + switch (err) { + 0 => return thread_ptr, + posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, + posix.EINVAL => unreachable, + posix.ENOMEM => return SpawnThreadError.SystemResources, + posix.ENOSPC => unreachable, + posix.EPERM => unreachable, + posix.EUSERS => unreachable, + else => return unexpectedErrorPosix(err), + } +} + +pub fn posixWait(pid: i32) i32 { + var status: i32 = undefined; + while (true) { + const err = posix.getErrno(posix.waitpid(pid, &status, 0)); + switch (err) { + 0 => return status, + posix.EINTR => continue, + posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. + posix.EINVAL => unreachable, // The options argument was invalid + else => unreachable, + } + } +} diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index e100af7733..6eb2d74bb7 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -14,6 +14,22 @@ pub const STDIN_FILENO = 0; pub const STDOUT_FILENO = 1; pub const STDERR_FILENO = 2; +pub const FUTEX_WAIT = 0; +pub const FUTEX_WAKE = 1; +pub const FUTEX_FD = 2; +pub const FUTEX_REQUEUE = 3; +pub const FUTEX_CMP_REQUEUE = 4; +pub const FUTEX_WAKE_OP = 5; +pub const FUTEX_LOCK_PI = 6; +pub const FUTEX_UNLOCK_PI = 7; +pub const FUTEX_TRYLOCK_PI = 8; +pub const FUTEX_WAIT_BITSET = 9; + +pub const FUTEX_PRIVATE_FLAG = 128; + +pub const FUTEX_CLOCK_REALTIME = 256; + + pub const PROT_NONE = 0; pub const PROT_READ = 1; pub const PROT_WRITE = 2; @@ -652,6 +668,10 @@ pub fn fork() usize { return syscall0(SYS_fork); } +pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?×pec) usize { + return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout)); +} + pub fn getcwd(buf: &u8, size: usize) usize { return syscall2(SYS_getcwd, @ptrToInt(buf), size); } @@ -746,6 +766,16 @@ pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize { return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode); } +/// See also `clone` (from the arch-specific include) +pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: &i32, child_tid: &i32, newtls: usize) usize { + return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls); +} + +/// See also `clone` (from the arch-specific include) +pub fn clone2(flags: usize, child_stack_ptr: usize) usize { + return syscall2(SYS_clone, flags, child_stack_ptr); +} + pub fn close(fd: i32) usize { return syscall1(SYS_close, usize(fd)); } diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index cfb2231df9..d3d2c702fc 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -443,6 +443,9 @@ pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz : "rcx", "r11"); } +/// This matches the libc clone function. +pub extern fn clone(func: extern fn(arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: &i32, tls: usize, ctid: &i32) usize; + pub nakedcc fn restore_rt() void { return asm volatile ("syscall" : diff --git a/std/os/test.zig b/std/os/test.zig index 718d1ce2c8..41afee004a 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -6,6 +6,8 @@ const io = std.io; const a = std.debug.global_allocator; const builtin = @import("builtin"); +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; test "makePath, put some files in it, deleteTree" { if (builtin.os == builtin.Os.windows) { @@ -40,3 +42,40 @@ test "access file" { assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true); try os.deleteTree(a, "os_test_tmp"); } + +test "spawn threads" { + if (builtin.os != builtin.Os.linux) { + // TODO implement threads on macos and windows + return; + } + + var direct_allocator = std.heap.DirectAllocator.init(); + defer direct_allocator.deinit(); + + var shared_ctx: i32 = 1; + + const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1); + const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2); + + var stack1: [1024]u8 = undefined; + var stack2: [1024]u8 = undefined; + + const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2); + const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2); + + thread1.wait(); + thread2.wait(); + thread3.wait(); + thread4.wait(); + + assert(shared_ctx == 4); +} + +fn start1(ctx: void) u8 { + return 0; +} + +fn start2(ctx: &i32) u8 { + _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + return 0; +} diff --git a/std/special/builtin.zig b/std/special/builtin.zig index 9de0aa7679..ac6eefe3d9 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -57,11 +57,46 @@ comptime { if (builtin.mode != builtin.Mode.ReleaseFast and builtin.os != builtin.Os.windows) { @export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong); } + if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) { + @export("clone", clone, builtin.GlobalLinkage.Strong); + } } extern fn __stack_chk_fail() noreturn { @panic("stack smashing detected"); } +// TODO we should be able to put this directly in std/linux/x86_64.zig but +// it causes a segfault in release mode. this is a workaround of calling it +// across .o file boundaries. fix comptime @ptrCast of nakedcc functions. +nakedcc fn clone() void { + asm volatile ( + \\ xor %%eax,%%eax + \\ mov $56,%%al + \\ mov %%rdi,%%r11 + \\ mov %%rdx,%%rdi + \\ mov %%r8,%%rdx + \\ mov %%r9,%%r8 + \\ mov 8(%%rsp),%%r10 + \\ mov %%r11,%%r9 + \\ and $-16,%%rsi + \\ sub $8,%%rsi + \\ mov %%rcx,(%%rsi) + \\ syscall + \\ test %%eax,%%eax + \\ jnz 1f + \\ xor %%ebp,%%ebp + \\ pop %%rdi + \\ call *%%r9 + \\ mov %%eax,%%edi + \\ xor %%eax,%%eax + \\ mov $60,%%al + \\ syscall + \\ hlt + \\1: ret + \\ + ); +} + const math = @import("../math/index.zig"); export fn fmodf(x: f32, y: f32) f32 { return generic_fmod(f32, x, y); } -- cgit v1.2.3 From 4a2bfec150ac8b78185d98324782da7841eddb9b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Apr 2018 12:57:45 -0400 Subject: fix linux implementation of self exe path closes #894 --- src/os.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/os.cpp b/src/os.cpp index e0491b21de..97462bd658 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1007,6 +1007,7 @@ int os_self_exe_path(Buf *out_path) { buf_resize(out_path, buf_len(out_path) * 2); continue; } + buf_resize(out_path, amt); return 0; } #endif -- cgit v1.2.3 From b5459eb987d89c4759c31123a7baa0a0d962c024 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Apr 2018 13:21:52 -0400 Subject: add @sqrt built-in function See #767 --- CMakeLists.txt | 1 - doc/langref.html.in | 12 +- src/all_types.hpp | 12 +- src/analyze.cpp | 9 +- src/bigfloat.cpp | 4 + src/bigfloat.hpp | 1 + src/codegen.cpp | 24 +++- src/ir.cpp | 94 +++++++++++++++ src/ir_print.cpp | 15 +++ std/math/sqrt.zig | 295 ++++++----------------------------------------- std/math/x86_64/sqrt.zig | 15 --- std/special/builtin.zig | 209 +++++++++++++++++++++++++++++++++ test/cases/math.zig | 16 +++ 13 files changed, 419 insertions(+), 288 deletions(-) delete mode 100644 std/math/x86_64/sqrt.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 021fd43cf0..bf90a7ef46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -498,7 +498,6 @@ set(ZIG_STD_FILES "math/tan.zig" "math/tanh.zig" "math/trunc.zig" - "math/x86_64/sqrt.zig" "mem.zig" "net.zig" "os/child_process.zig" diff --git a/doc/langref.html.in b/doc/langref.html.in index 856d62f142..d9436e55b7 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4669,6 +4669,16 @@ pub const FloatMode = enum { The result is a target-specific compile time constant.

{#header_close#} + {#header_open|@sqrt#} +
@sqrt(comptime T: type, value: T) -> T
+

+ Performs the square root of a floating point number. Uses a dedicated hardware instruction + when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO. +

+

+ This is a low-level intrinsic. Most code can use std.math.sqrt instead. +

+ {#header_close#} {#header_open|@subWithOverflow#}
@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool

@@ -5991,7 +6001,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw", + built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index d27a5c7a1c..b43214a60e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1317,6 +1317,7 @@ enum BuiltinFnId { BuiltinFnIdDivFloor, BuiltinFnIdRem, BuiltinFnIdMod, + BuiltinFnIdSqrt, BuiltinFnIdTruncate, BuiltinFnIdIntType, BuiltinFnIdSetCold, @@ -1413,6 +1414,7 @@ enum ZigLLVMFnId { ZigLLVMFnIdOverflowArithmetic, ZigLLVMFnIdFloor, ZigLLVMFnIdCeil, + ZigLLVMFnIdSqrt, }; enum AddSubMul { @@ -1433,7 +1435,7 @@ struct ZigLLVMFnKey { } clz; struct { uint32_t bit_count; - } floor_ceil; + } floating; struct { AddSubMul add_sub_mul; uint32_t bit_count; @@ -2047,6 +2049,7 @@ enum IrInstructionId { IrInstructionIdAddImplicitReturnType, IrInstructionIdMergeErrRetTraces, IrInstructionIdMarkErrRetTracePtr, + IrInstructionIdSqrt, }; struct IrInstruction { @@ -3036,6 +3039,13 @@ struct IrInstructionMarkErrRetTracePtr { IrInstruction *err_ret_trace_ptr; }; +struct IrInstructionSqrt { + IrInstruction base; + + IrInstruction *type; + IrInstruction *op; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index c73e6b39e3..9092da6e3b 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5801,9 +5801,11 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) { case ZigLLVMFnIdClz: return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817; case ZigLLVMFnIdFloor: - return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1899859168; + return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168; case ZigLLVMFnIdCeil: - return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1953839089; + return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089; + case ZigLLVMFnIdSqrt: + return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385; case ZigLLVMFnIdOverflowArithmetic: return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) + ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) + @@ -5822,7 +5824,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { return a.data.clz.bit_count == b.data.clz.bit_count; case ZigLLVMFnIdFloor: case ZigLLVMFnIdCeil: - return a.data.floor_ceil.bit_count == b.data.floor_ceil.bit_count; + case ZigLLVMFnIdSqrt: + return a.data.floating.bit_count == b.data.floating.bit_count; case ZigLLVMFnIdOverflowArithmetic: return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) && (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) && diff --git a/src/bigfloat.cpp b/src/bigfloat.cpp index 2cab9658e8..dcb6db61db 100644 --- a/src/bigfloat.cpp +++ b/src/bigfloat.cpp @@ -181,3 +181,7 @@ bool bigfloat_has_fraction(const BigFloat *bigfloat) { f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored); return !f128M_eq(&floored, &bigfloat->value); } + +void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) { + f128M_sqrt(&op->value, &dest->value); +} diff --git a/src/bigfloat.hpp b/src/bigfloat.hpp index 894b252c3a..e212c30c87 100644 --- a/src/bigfloat.hpp +++ b/src/bigfloat.hpp @@ -42,6 +42,7 @@ void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2 void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); +void bigfloat_sqrt(BigFloat *dest, const BigFloat *op); void bigfloat_append_buf(Buf *buf, const BigFloat *op); Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2); diff --git a/src/codegen.cpp b/src/codegen.cpp index a58832f983..b45214a5e0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -717,12 +717,12 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry, return fn_val; } -static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) { +static LLVMValueRef get_float_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) { assert(type_entry->id == TypeTableEntryIdFloat); ZigLLVMFnKey key = {}; key.id = fn_id; - key.data.floor_ceil.bit_count = (uint32_t)type_entry->data.floating.bit_count; + key.data.floating.bit_count = (uint32_t)type_entry->data.floating.bit_count; auto existing_entry = g->llvm_fn_table.maybe_get(key); if (existing_entry) @@ -733,6 +733,8 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi name = "floor"; } else if (fn_id == ZigLLVMFnIdCeil) { name = "ceil"; + } else if (fn_id == ZigLLVMFnIdSqrt) { + name = "sqrt"; } else { zig_unreachable(); } @@ -1900,7 +1902,7 @@ static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type if (type_entry->id == TypeTableEntryIdInt) return val; - LLVMValueRef floor_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdFloor); + LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor); return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); } @@ -1908,7 +1910,7 @@ static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_ if (type_entry->id == TypeTableEntryIdInt) return val; - LLVMValueRef ceil_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdCeil); + LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil); return LLVMBuildCall(g->builder, ceil_fn, &val, 1, ""); } @@ -3247,10 +3249,12 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui fn_name = "cttz"; key.id = ZigLLVMFnIdCtz; key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count; - } else { + } else if (fn_id == BuiltinFnIdClz) { fn_name = "ctlz"; key.id = ZigLLVMFnIdClz; key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count; + } else { + zig_unreachable(); } auto existing_entry = g->llvm_fn_table.maybe_get(key); @@ -4402,6 +4406,13 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e return nullptr; } +static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) { + LLVMValueRef op = ir_llvm_value(g, instruction->op); + assert(instruction->base.value.type->id == TypeTableEntryIdFloat); + LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdSqrt); + return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -4623,6 +4634,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction); case IrInstructionIdMarkErrRetTracePtr: return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction); + case IrInstructionIdSqrt: + return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); } zig_unreachable(); } @@ -6109,6 +6122,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2); create_builtin_fn(g, BuiltinFnIdRem, "rem", 2); create_builtin_fn(g, BuiltinFnIdMod, "mod", 2); + create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2); create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1); diff --git a/src/ir.cpp b/src/ir.cpp index 0fac1bd219..08229b8bb3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -733,6 +733,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP return IrInstructionIdMarkErrRetTracePtr; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) { + return IrInstructionIdSqrt; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2731,6 +2735,17 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco return &instruction->base; } +static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) { + IrInstructionSqrt *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type = type; + instruction->op = op; + + if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block); + ir_ref_instruction(op, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -3845,6 +3860,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true); } + case BuiltinFnIdSqrt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + return ir_build_sqrt(irb, scope, node, arg0_value, arg1_value); + } case BuiltinFnIdTruncate: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -18031,6 +18060,68 @@ static TypeTableEntry *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze * return result->value.type; } +static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) { + TypeTableEntry *float_type = ir_resolve_type(ira, instruction->type->other); + if (type_is_invalid(float_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *op = instruction->op->other; + if (type_is_invalid(op->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + bool ok_type = float_type->id == TypeTableEntryIdNumLitFloat || float_type->id == TypeTableEntryIdFloat; + if (!ok_type) { + ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type); + if (type_is_invalid(casted_op->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (instr_is_comptime(casted_op)) { + ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad); + if (!val) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + + if (float_type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat); + } else if (float_type->id == TypeTableEntryIdFloat) { + switch (float_type->data.floating.bit_count) { + case 32: + out_val->data.x_f32 = sqrtf(val->data.x_f32); + break; + case 64: + out_val->data.x_f64 = sqrt(val->data.x_f64); + break; + case 128: + f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128); + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } + + return float_type; + } + + assert(float_type->id == TypeTableEntryIdFloat); + if (float_type->data.floating.bit_count != 32 && float_type->data.floating.bit_count != 64) { + ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, nullptr, casted_op); + ir_link_new_instruction(result, &instruction->base); + result->value.type = float_type; + return result->value.type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -18278,6 +18369,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction); case IrInstructionIdMarkErrRetTracePtr: return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction); + case IrInstructionIdSqrt: + return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction); } zig_unreachable(); } @@ -18490,6 +18583,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCoroFree: case IrInstructionIdCoroPromise: case IrInstructionIdPromiseResultType: + case IrInstructionIdSqrt: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 99f79ff75e..5f8dd60187 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1204,6 +1204,18 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe fprintf(irp->f, ")"); } +static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) { + fprintf(irp->f, "@sqrt("); + if (instruction->type != nullptr) { + ir_print_other_instruction(irp, instruction->type); + } else { + fprintf(irp->f, "null"); + } + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->op); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1590,6 +1602,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdMarkErrRetTracePtr: ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction); break; + case IrInstructionIdSqrt: + ir_print_sqrt(irp, (IrInstructionSqrt *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig index 690f8b6901..982bd28b72 100644 --- a/std/math/sqrt.zig +++ b/std/math/sqrt.zig @@ -14,26 +14,8 @@ const TypeId = builtin.TypeId; pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) { const T = @typeOf(x); switch (@typeId(T)) { - TypeId.FloatLiteral => { - return T(sqrt64(x)); - }, - TypeId.Float => { - switch (T) { - f32 => { - switch (builtin.arch) { - builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt32(x), - else => return sqrt32(x), - } - }, - f64 => { - switch (builtin.arch) { - builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt64(x), - else => return sqrt64(x), - } - }, - else => @compileError("sqrt not implemented for " ++ @typeName(T)), - } - }, + TypeId.FloatLiteral => return T(@sqrt(f64, x)), // TODO upgrade to f128 + TypeId.Float => return @sqrt(T, x), TypeId.IntLiteral => comptime { if (x > @maxValue(u128)) { @compileError("sqrt not implemented for comptime_int greater than 128 bits"); @@ -43,269 +25,58 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ } return T(sqrt_int(u128, x)); }, - TypeId.Int => { - return sqrt_int(T, x); - }, + TypeId.Int => return sqrt_int(T, x), else => @compileError("sqrt not implemented for " ++ @typeName(T)), } } -fn sqrt32(x: f32) f32 { - const tiny: f32 = 1.0e-30; - const sign: i32 = @bitCast(i32, u32(0x80000000)); - var ix: i32 = @bitCast(i32, x); - - if ((ix & 0x7F800000) == 0x7F800000) { - return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan - } - - // zero - if (ix <= 0) { - if (ix & ~sign == 0) { - return x; // sqrt (+-0) = +-0 - } - if (ix < 0) { - return math.snan(f32); - } - } - - // normalize - var m = ix >> 23; - if (m == 0) { - // subnormal - var i: i32 = 0; - while (ix & 0x00800000 == 0) : (i += 1) { - ix <<= 1; - } - m -= i - 1; - } - - m -= 127; // unbias exponent - ix = (ix & 0x007FFFFF) | 0x00800000; - - if (m & 1 != 0) { // odd m, double x to even - ix += ix; - } - - m >>= 1; // m = [m / 2] - - // sqrt(x) bit by bit - ix += ix; - var q: i32 = 0; // q = sqrt(x) - var s: i32 = 0; - var r: i32 = 0x01000000; // r = moving bit right -> left - - while (r != 0) { - const t = s + r; - if (t <= ix) { - s = t + r; - ix -= t; - q += r; - } - ix += ix; - r >>= 1; - } - - // floating add to find rounding direction - if (ix != 0) { - var z = 1.0 - tiny; // inexact - if (z >= 1.0) { - z = 1.0 + tiny; - if (z > 1.0) { - q += 2; - } else { - if (q & 1 != 0) { - q += 1; - } - } - } - } - - ix = (q >> 1) + 0x3f000000; - ix += m << 23; - return @bitCast(f32, ix); -} - -// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound -// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are -// potentially some edge cases remaining that are not handled in the same way. -fn sqrt64(x: f64) f64 { - const tiny: f64 = 1.0e-300; - const sign: u32 = 0x80000000; - const u = @bitCast(u64, x); - - var ix0 = u32(u >> 32); - var ix1 = u32(u & 0xFFFFFFFF); - - // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan - if (ix0 & 0x7FF00000 == 0x7FF00000) { - return x * x + x; - } - - // sqrt(+-0) = +-0 - if (x == 0.0) { - return x; - } - // sqrt(-ve) = snan - if (ix0 & sign != 0) { - return math.snan(f64); - } - - // normalize x - var m = i32(ix0 >> 20); - if (m == 0) { - // subnormal - while (ix0 == 0) { - m -= 21; - ix0 |= ix1 >> 11; - ix1 <<= 21; - } - - // subnormal - var i: u32 = 0; - while (ix0 & 0x00100000 == 0) : (i += 1) { - ix0 <<= 1; - } - m -= i32(i) - 1; - ix0 |= ix1 >> u5(32 - i); - ix1 <<= u5(i); - } - - // unbias exponent - m -= 1023; - ix0 = (ix0 & 0x000FFFFF) | 0x00100000; - if (m & 1 != 0) { - ix0 += ix0 + (ix1 >> 31); - ix1 = ix1 +% ix1; - } - m >>= 1; - - // sqrt(x) bit by bit - ix0 += ix0 + (ix1 >> 31); - ix1 = ix1 +% ix1; - - var q: u32 = 0; - var q1: u32 = 0; - var s0: u32 = 0; - var s1: u32 = 0; - var r: u32 = 0x00200000; - var t: u32 = undefined; - var t1: u32 = undefined; - - while (r != 0) { - t = s0 +% r; - if (t <= ix0) { - s0 = t + r; - ix0 -= t; - q += r; - } - ix0 = ix0 +% ix0 +% (ix1 >> 31); - ix1 = ix1 +% ix1; - r >>= 1; - } - - r = sign; - while (r != 0) { - t = s1 +% r; - t = s0; - if (t < ix0 or (t == ix0 and t1 <= ix1)) { - s1 = t1 +% r; - if (t1 & sign == sign and s1 & sign == 0) { - s0 += 1; - } - ix0 -= t; - if (ix1 < t1) { - ix0 -= 1; - } - ix1 = ix1 -% t1; - q1 += r; - } - ix0 = ix0 +% ix0 +% (ix1 >> 31); - ix1 = ix1 +% ix1; - r >>= 1; - } - - // rounding direction - if (ix0 | ix1 != 0) { - var z = 1.0 - tiny; // raise inexact - if (z >= 1.0) { - z = 1.0 + tiny; - if (q1 == 0xFFFFFFFF) { - q1 = 0; - q += 1; - } else if (z > 1.0) { - if (q1 == 0xFFFFFFFE) { - q += 1; - } - q1 += 2; - } else { - q1 += q1 & 1; - } - } - } - - ix0 = (q >> 1) + 0x3FE00000; - ix1 = q1 >> 1; - if (q & 1 != 0) { - ix1 |= 0x80000000; - } - - // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same - // behaviour at least. - var iix0 = i32(ix0); - iix0 = iix0 +% (m << 20); - - const uz = (u64(iix0) << 32) | ix1; - return @bitCast(f64, uz); -} - test "math.sqrt" { - assert(sqrt(f32(0.0)) == sqrt32(0.0)); - assert(sqrt(f64(0.0)) == sqrt64(0.0)); + assert(sqrt(f32(0.0)) == @sqrt(f32, 0.0)); + assert(sqrt(f64(0.0)) == @sqrt(f64, 0.0)); } test "math.sqrt32" { const epsilon = 0.000001; - assert(sqrt32(0.0) == 0.0); - assert(math.approxEq(f32, sqrt32(2.0), 1.414214, epsilon)); - assert(math.approxEq(f32, sqrt32(3.6), 1.897367, epsilon)); - assert(sqrt32(4.0) == 2.0); - assert(math.approxEq(f32, sqrt32(7.539840), 2.745877, epsilon)); - assert(math.approxEq(f32, sqrt32(19.230934), 4.385309, epsilon)); - assert(sqrt32(64.0) == 8.0); - assert(math.approxEq(f32, sqrt32(64.1), 8.006248, epsilon)); - assert(math.approxEq(f32, sqrt32(8942.230469), 94.563370, epsilon)); + assert(@sqrt(f32, 0.0) == 0.0); + assert(math.approxEq(f32, @sqrt(f32, 2.0), 1.414214, epsilon)); + assert(math.approxEq(f32, @sqrt(f32, 3.6), 1.897367, epsilon)); + assert(@sqrt(f32, 4.0) == 2.0); + assert(math.approxEq(f32, @sqrt(f32, 7.539840), 2.745877, epsilon)); + assert(math.approxEq(f32, @sqrt(f32, 19.230934), 4.385309, epsilon)); + assert(@sqrt(f32, 64.0) == 8.0); + assert(math.approxEq(f32, @sqrt(f32, 64.1), 8.006248, epsilon)); + assert(math.approxEq(f32, @sqrt(f32, 8942.230469), 94.563370, epsilon)); } test "math.sqrt64" { const epsilon = 0.000001; - assert(sqrt64(0.0) == 0.0); - assert(math.approxEq(f64, sqrt64(2.0), 1.414214, epsilon)); - assert(math.approxEq(f64, sqrt64(3.6), 1.897367, epsilon)); - assert(sqrt64(4.0) == 2.0); - assert(math.approxEq(f64, sqrt64(7.539840), 2.745877, epsilon)); - assert(math.approxEq(f64, sqrt64(19.230934), 4.385309, epsilon)); - assert(sqrt64(64.0) == 8.0); - assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon)); - assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon)); + assert(@sqrt(f64, 0.0) == 0.0); + assert(math.approxEq(f64, @sqrt(f64, 2.0), 1.414214, epsilon)); + assert(math.approxEq(f64, @sqrt(f64, 3.6), 1.897367, epsilon)); + assert(@sqrt(f64, 4.0) == 2.0); + assert(math.approxEq(f64, @sqrt(f64, 7.539840), 2.745877, epsilon)); + assert(math.approxEq(f64, @sqrt(f64, 19.230934), 4.385309, epsilon)); + assert(@sqrt(f64, 64.0) == 8.0); + assert(math.approxEq(f64, @sqrt(f64, 64.1), 8.006248, epsilon)); + assert(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon)); } test "math.sqrt32.special" { - assert(math.isPositiveInf(sqrt32(math.inf(f32)))); - assert(sqrt32(0.0) == 0.0); - assert(sqrt32(-0.0) == -0.0); - assert(math.isNan(sqrt32(-1.0))); - assert(math.isNan(sqrt32(math.nan(f32)))); + assert(math.isPositiveInf(@sqrt(f32, math.inf(f32)))); + assert(@sqrt(f32, 0.0) == 0.0); + assert(@sqrt(f32, -0.0) == -0.0); + assert(math.isNan(@sqrt(f32, -1.0))); + assert(math.isNan(@sqrt(f32, math.nan(f32)))); } test "math.sqrt64.special" { - assert(math.isPositiveInf(sqrt64(math.inf(f64)))); - assert(sqrt64(0.0) == 0.0); - assert(sqrt64(-0.0) == -0.0); - assert(math.isNan(sqrt64(-1.0))); - assert(math.isNan(sqrt64(math.nan(f64)))); + assert(math.isPositiveInf(@sqrt(f64, math.inf(f64)))); + assert(@sqrt(f64, 0.0) == 0.0); + assert(@sqrt(f64, -0.0) == -0.0); + assert(math.isNan(@sqrt(f64, -1.0))); + assert(math.isNan(@sqrt(f64, math.nan(f64)))); } fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) { diff --git a/std/math/x86_64/sqrt.zig b/std/math/x86_64/sqrt.zig deleted file mode 100644 index ad9ce0c96c..0000000000 --- a/std/math/x86_64/sqrt.zig +++ /dev/null @@ -1,15 +0,0 @@ -pub fn sqrt32(x: f32) f32 { - return asm ( - \\sqrtss %%xmm0, %%xmm0 - : [ret] "={xmm0}" (-> f32) - : [x] "{xmm0}" (x) - ); -} - -pub fn sqrt64(x: f64) f64 { - return asm ( - \\sqrtsd %%xmm0, %%xmm0 - : [ret] "={xmm0}" (-> f64) - : [x] "{xmm0}" (x) - ); -} diff --git a/std/special/builtin.zig b/std/special/builtin.zig index ac6eefe3d9..56aa2ebaf8 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -194,3 +194,212 @@ fn isNan(comptime T: type, bits: T) bool { unreachable; } } + +// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound +// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are +// potentially some edge cases remaining that are not handled in the same way. +export fn sqrt(x: f64) f64 { + const tiny: f64 = 1.0e-300; + const sign: u32 = 0x80000000; + const u = @bitCast(u64, x); + + var ix0 = u32(u >> 32); + var ix1 = u32(u & 0xFFFFFFFF); + + // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan + if (ix0 & 0x7FF00000 == 0x7FF00000) { + return x * x + x; + } + + // sqrt(+-0) = +-0 + if (x == 0.0) { + return x; + } + // sqrt(-ve) = snan + if (ix0 & sign != 0) { + return math.snan(f64); + } + + // normalize x + var m = i32(ix0 >> 20); + if (m == 0) { + // subnormal + while (ix0 == 0) { + m -= 21; + ix0 |= ix1 >> 11; + ix1 <<= 21; + } + + // subnormal + var i: u32 = 0; + while (ix0 & 0x00100000 == 0) : (i += 1) { + ix0 <<= 1; + } + m -= i32(i) - 1; + ix0 |= ix1 >> u5(32 - i); + ix1 <<= u5(i); + } + + // unbias exponent + m -= 1023; + ix0 = (ix0 & 0x000FFFFF) | 0x00100000; + if (m & 1 != 0) { + ix0 += ix0 + (ix1 >> 31); + ix1 = ix1 +% ix1; + } + m >>= 1; + + // sqrt(x) bit by bit + ix0 += ix0 + (ix1 >> 31); + ix1 = ix1 +% ix1; + + var q: u32 = 0; + var q1: u32 = 0; + var s0: u32 = 0; + var s1: u32 = 0; + var r: u32 = 0x00200000; + var t: u32 = undefined; + var t1: u32 = undefined; + + while (r != 0) { + t = s0 +% r; + if (t <= ix0) { + s0 = t + r; + ix0 -= t; + q += r; + } + ix0 = ix0 +% ix0 +% (ix1 >> 31); + ix1 = ix1 +% ix1; + r >>= 1; + } + + r = sign; + while (r != 0) { + t = s1 +% r; + t = s0; + if (t < ix0 or (t == ix0 and t1 <= ix1)) { + s1 = t1 +% r; + if (t1 & sign == sign and s1 & sign == 0) { + s0 += 1; + } + ix0 -= t; + if (ix1 < t1) { + ix0 -= 1; + } + ix1 = ix1 -% t1; + q1 += r; + } + ix0 = ix0 +% ix0 +% (ix1 >> 31); + ix1 = ix1 +% ix1; + r >>= 1; + } + + // rounding direction + if (ix0 | ix1 != 0) { + var z = 1.0 - tiny; // raise inexact + if (z >= 1.0) { + z = 1.0 + tiny; + if (q1 == 0xFFFFFFFF) { + q1 = 0; + q += 1; + } else if (z > 1.0) { + if (q1 == 0xFFFFFFFE) { + q += 1; + } + q1 += 2; + } else { + q1 += q1 & 1; + } + } + } + + ix0 = (q >> 1) + 0x3FE00000; + ix1 = q1 >> 1; + if (q & 1 != 0) { + ix1 |= 0x80000000; + } + + // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same + // behaviour at least. + var iix0 = i32(ix0); + iix0 = iix0 +% (m << 20); + + const uz = (u64(iix0) << 32) | ix1; + return @bitCast(f64, uz); +} + +export fn sqrtf(x: f32) f32 { + const tiny: f32 = 1.0e-30; + const sign: i32 = @bitCast(i32, u32(0x80000000)); + var ix: i32 = @bitCast(i32, x); + + if ((ix & 0x7F800000) == 0x7F800000) { + return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan + } + + // zero + if (ix <= 0) { + if (ix & ~sign == 0) { + return x; // sqrt (+-0) = +-0 + } + if (ix < 0) { + return math.snan(f32); + } + } + + // normalize + var m = ix >> 23; + if (m == 0) { + // subnormal + var i: i32 = 0; + while (ix & 0x00800000 == 0) : (i += 1) { + ix <<= 1; + } + m -= i - 1; + } + + m -= 127; // unbias exponent + ix = (ix & 0x007FFFFF) | 0x00800000; + + if (m & 1 != 0) { // odd m, double x to even + ix += ix; + } + + m >>= 1; // m = [m / 2] + + // sqrt(x) bit by bit + ix += ix; + var q: i32 = 0; // q = sqrt(x) + var s: i32 = 0; + var r: i32 = 0x01000000; // r = moving bit right -> left + + while (r != 0) { + const t = s + r; + if (t <= ix) { + s = t + r; + ix -= t; + q += r; + } + ix += ix; + r >>= 1; + } + + // floating add to find rounding direction + if (ix != 0) { + var z = 1.0 - tiny; // inexact + if (z >= 1.0) { + z = 1.0 + tiny; + if (z > 1.0) { + q += 2; + } else { + if (q & 1 != 0) { + q += 1; + } + } + } + } + + ix = (q >> 1) + 0x3f000000; + ix += m << 23; + return @bitCast(f32, ix); +} diff --git a/test/cases/math.zig b/test/cases/math.zig index 574aa39bb1..47d001a590 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -402,3 +402,19 @@ test "comptime float rem int" { assert(x == 1.0); } } + +test "@sqrt" { + testSqrt(f64, 12.0); + comptime testSqrt(f64, 12.0); + testSqrt(f32, 13.0); + comptime testSqrt(f32, 13.0); + + const x = 14.0; + const y = x * x; + const z = @sqrt(@typeOf(y), y); + comptime assert(z == x); +} + +fn testSqrt(comptime T: type, x: T) void { + assert(@sqrt(T, x * x) == x); +} -- cgit v1.2.3 From a8d794215e5031d60c5bd761a2478f382972bde6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Apr 2018 15:22:07 -0400 Subject: exit with error code instead of panic for file not found --- src/codegen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index b45214a5e0..7a0117421a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6337,7 +6337,8 @@ static void define_builtin_compile_vars(CodeGen *g) { int err; Buf *abs_full_path = buf_alloc(); if ((err = os_path_real(builtin_zig_path, abs_full_path))) { - zig_panic("unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err)); + exit(1); } assert(g->root_package); -- cgit v1.2.3 From 859b10d8bfcca3c4a30798b4522fd88ec6c66de6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Apr 2018 15:20:54 -0400 Subject: std.math.ln and std.math.exp use float strict mode closes #920 --- std/math/exp.zig | 5 +++++ std/math/ln.zig | 2 ++ test/behavior.zig | 1 + test/cases/bugs/920.zig | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 test/cases/bugs/920.zig diff --git a/std/math/exp.zig b/std/math/exp.zig index 4032930a43..21aa558c57 100644 --- a/std/math/exp.zig +++ b/std/math/exp.zig @@ -6,6 +6,7 @@ const std = @import("../index.zig"); const math = std.math; const assert = std.debug.assert; +const builtin = @import("builtin"); pub fn exp(x: var) @typeOf(x) { const T = @typeOf(x); @@ -17,6 +18,8 @@ pub fn exp(x: var) @typeOf(x) { } fn exp32(x_: f32) f32 { + @setFloatMode(this, builtin.FloatMode.Strict); + const half = []f32 { 0.5, -0.5 }; const ln2hi = 6.9314575195e-1; const ln2lo = 1.4286067653e-6; @@ -94,6 +97,8 @@ fn exp32(x_: f32) f32 { } fn exp64(x_: f64) f64 { + @setFloatMode(this, builtin.FloatMode.Strict); + const half = []const f64 { 0.5, -0.5 }; const ln2hi: f64 = 6.93147180369123816490e-01; const ln2lo: f64 = 1.90821492927058770002e-10; diff --git a/std/math/ln.zig b/std/math/ln.zig index c349ed7c6f..d09494b998 100644 --- a/std/math/ln.zig +++ b/std/math/ln.zig @@ -89,6 +89,8 @@ pub fn ln_32(x_: f32) f32 { } pub fn ln_64(x_: f64) f64 { + @setFloatMode(this, @import("builtin").FloatMode.Strict); + const ln2_hi: f64 = 6.93147180369123816490e-01; const ln2_lo: f64 = 1.90821492927058770002e-10; const Lg1: f64 = 6.666666666666735130e-01; diff --git a/test/behavior.zig b/test/behavior.zig index de39b20dad..2c10c6d71b 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -12,6 +12,7 @@ comptime { _ = @import("cases/bugs/655.zig"); _ = @import("cases/bugs/656.zig"); _ = @import("cases/bugs/828.zig"); + _ = @import("cases/bugs/920.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); _ = @import("cases/coroutines.zig"); diff --git a/test/cases/bugs/920.zig b/test/cases/bugs/920.zig new file mode 100644 index 0000000000..13c03a304f --- /dev/null +++ b/test/cases/bugs/920.zig @@ -0,0 +1,60 @@ +const std = @import("std"); +const math = std.math; +const Random = std.rand.Random; + +const ZigTable = struct { + r: f64, + x: [257]f64, + f: [257]f64, + + pdf: fn(f64) f64, + is_symmetric: bool, + zero_case: fn(&Random, f64) f64, +}; + +fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64, + comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable { + var tables: ZigTable = undefined; + + tables.is_symmetric = is_symmetric; + tables.r = r; + tables.pdf = f; + tables.zero_case = zero_case; + + tables.x[0] = v / f(r); + tables.x[1] = r; + + for (tables.x[2..256]) |*entry, i| { + const last = tables.x[2 + i - 1]; + *entry = f_inv(v / last + f(last)); + } + tables.x[256] = 0; + + for (tables.f[0..]) |*entry, i| { + *entry = f(tables.x[i]); + } + + return tables; +} + +const norm_r = 3.6541528853610088; +const norm_v = 0.00492867323399; + +fn norm_f(x: f64) f64 { return math.exp(-x * x / 2.0); } +fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); } +fn norm_zero_case(random: &Random, u: f64) f64 { return 0.0; } + +const NormalDist = blk: { + @setEvalBranchQuota(30000); + break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); +}; + +test "bug 920 fixed" { + const NormalDist1 = blk: { + break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); + }; + + for (NormalDist1.f) |_, i| { + std.debug.assert(NormalDist1.f[i] == NormalDist.f[i]); + } +} -- cgit v1.2.3 From b9360640cefd1aa30dedf71a0c6b7bddc51a6ae3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Apr 2018 18:12:00 -0400 Subject: add @atomicLoad builtin See #174 --- doc/langref.html.in | 21 +++++++++- src/all_types.hpp | 11 +++++ src/codegen.cpp | 13 ++++++ src/ir.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++++---- src/ir_print.cpp | 21 ++++++++++ std/os/index.zig | 2 +- test/cases/atomics.zig | 14 ++++++- 7 files changed, 183 insertions(+), 11 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index d9436e55b7..5cbec218a9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -3880,6 +3880,25 @@ pub fn main() void { {#header_open|@ArgType#}

TODO

{#header_close#} + {#header_open|@atomicLoad#} +
@atomicLoad(comptime T: type, ptr: &const T, comptime ordering: builtin.AtomicOrder) -> T
+

+ This builtin function atomically dereferences a pointer and returns the value. +

+

+ T must be a pointer type, a bool, + or an integer whose bit count meets these requirements: +

+
    +
  • At least 8
  • +
  • At most the same as usize
  • +
  • Power of 2
  • +
+

+ TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe + we can remove this restriction +

+ {#header_close#} {#header_open|@atomicRmw#}
@atomicRmw(comptime T: type, ptr: &T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) -> T

@@ -6001,7 +6020,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index b43214a60e..708ad81791 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1347,6 +1347,7 @@ enum BuiltinFnId { BuiltinFnIdExport, BuiltinFnIdErrorReturnTrace, BuiltinFnIdAtomicRmw, + BuiltinFnIdAtomicLoad, }; struct BuiltinFnEntry { @@ -2043,6 +2044,7 @@ enum IrInstructionId { IrInstructionIdCoroPromise, IrInstructionIdCoroAllocHelper, IrInstructionIdAtomicRmw, + IrInstructionIdAtomicLoad, IrInstructionIdPromiseResultType, IrInstructionIdAwaitBookkeeping, IrInstructionIdSaveErrRetAddr, @@ -3003,6 +3005,15 @@ struct IrInstructionAtomicRmw { AtomicOrder resolved_ordering; }; +struct IrInstructionAtomicLoad { + IrInstruction base; + + IrInstruction *operand_type; + IrInstruction *ptr; + IrInstruction *ordering; + AtomicOrder resolved_ordering; +}; + struct IrInstructionPromiseResultType { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 7a0117421a..9f319d9122 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4385,6 +4385,16 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable, return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, ""); } +static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutable *executable, + IrInstructionAtomicLoad *instruction) +{ + LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering); + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value.type, ""); + LLVMSetOrdering(load_inst, ordering); + return load_inst; +} + static LLVMValueRef ir_render_merge_err_ret_traces(CodeGen *g, IrExecutable *executable, IrInstructionMergeErrRetTraces *instruction) { @@ -4628,6 +4638,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction); case IrInstructionIdAtomicRmw: return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction); + case IrInstructionIdAtomicLoad: + return ir_render_atomic_load(g, executable, (IrInstructionAtomicLoad *)instruction); case IrInstructionIdSaveErrRetAddr: return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction); case IrInstructionIdMergeErrRetTraces: @@ -6136,6 +6148,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdExport, "export", 3); create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); + create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index 08229b8bb3..d43efe0190 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -709,6 +709,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) { return IrInstructionIdAtomicRmw; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) { + return IrInstructionIdAtomicLoad; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultType *) { return IrInstructionIdPromiseResultType; } @@ -2673,6 +2677,23 @@ static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } +static IrInstruction *ir_build_atomic_load(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *operand_type, IrInstruction *ptr, + IrInstruction *ordering, AtomicOrder resolved_ordering) +{ + IrInstructionAtomicLoad *instruction = ir_build_instruction(irb, scope, source_node); + instruction->operand_type = operand_type; + instruction->ptr = ptr; + instruction->ordering = ordering; + instruction->resolved_ordering = resolved_ordering; + + if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block); + ir_ref_instruction(ptr, irb->current_basic_block); + if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_promise_result_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *promise_type) { @@ -4303,6 +4324,27 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo // these 2 values don't mean anything since we passed non-null values for other args AtomicRmwOp_xchg, AtomicOrderMonotonic); } + case BuiltinFnIdAtomicLoad: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + AstNode *arg2_node = node->data.fn_call_expr.params.at(2); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + if (arg2_value == irb->codegen->invalid_instruction) + return arg2_value; + + return ir_build_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value, + // this value does not mean anything since we passed non-null values for other arg + AtomicOrderMonotonic); + } } zig_unreachable(); } @@ -17898,35 +17940,43 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, return result->value.type; } -static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) { - TypeTableEntry *operand_type = ir_resolve_type(ira, instruction->operand_type->other); - if (type_is_invalid(operand_type)) { +static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) { + TypeTableEntry *operand_type = ir_resolve_type(ira, op); + if (type_is_invalid(operand_type)) return ira->codegen->builtin_types.entry_invalid; - } + if (operand_type->id == TypeTableEntryIdInt) { if (operand_type->data.integral.bit_count < 8) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, op, buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type", operand_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, op, buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type", operand_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } if (!is_power_of_2(operand_type->data.integral.bit_count)) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, op, buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } } else if (get_codegen_ptr_type(operand_type) == nullptr) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, op, buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name))); return ira->codegen->builtin_types.entry_invalid; } + return operand_type; +} + +static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) { + TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other); + if (type_is_invalid(operand_type)) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *ptr_inst = instruction->ptr->other; if (type_is_invalid(ptr_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -17974,6 +18024,49 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr return result->value.type; } +static TypeTableEntry *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstructionAtomicLoad *instruction) { + TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other); + if (type_is_invalid(operand_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *ptr_inst = instruction->ptr->other; + if (type_is_invalid(ptr_inst->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true); + IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); + if (type_is_invalid(casted_ptr->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + AtomicOrder ordering; + if (instruction->ordering == nullptr) { + ordering = instruction->resolved_ordering; + } else { + if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering)) + return ira->codegen->builtin_types.entry_invalid; + } + + if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) { + assert(instruction->ordering != nullptr); + ir_add_error(ira, instruction->ordering, + buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel")); + return ira->codegen->builtin_types.entry_invalid; + } + + if (instr_is_comptime(casted_ptr)) { + IrInstruction *result = ir_get_deref(ira, &instruction->base, casted_ptr); + ir_link_new_instruction(result, &instruction->base); + assert(result->value.type != nullptr); + return result->value.type; + } + + IrInstruction *result = ir_build_atomic_load(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, nullptr, casted_ptr, nullptr, ordering); + ir_link_new_instruction(result, &instruction->base); + result->value.type = operand_type; + return result->value.type; +} + static TypeTableEntry *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) { TypeTableEntry *promise_type = ir_resolve_type(ira, instruction->promise_type->other); if (type_is_invalid(promise_type)) @@ -18357,6 +18450,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction); case IrInstructionIdAtomicRmw: return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction); + case IrInstructionIdAtomicLoad: + return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction); case IrInstructionIdPromiseResultType: return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction); case IrInstructionIdAwaitBookkeeping: @@ -18584,6 +18679,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCoroPromise: case IrInstructionIdPromiseResultType: case IrInstructionIdSqrt: + case IrInstructionIdAtomicLoad: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 5f8dd60187..45b666ae73 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1172,6 +1172,24 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio fprintf(irp->f, ")"); } +static void ir_print_atomic_load(IrPrint *irp, IrInstructionAtomicLoad *instruction) { + fprintf(irp->f, "@atomicLoad("); + if (instruction->operand_type != nullptr) { + ir_print_other_instruction(irp, instruction->operand_type); + } else { + fprintf(irp->f, "[TODO print]"); + } + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ","); + if (instruction->ordering != nullptr) { + ir_print_other_instruction(irp, instruction->ordering); + } else { + fprintf(irp->f, "[TODO print]"); + } + fprintf(irp->f, ")"); +} + static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeeping *instruction) { fprintf(irp->f, "@awaitBookkeeping("); ir_print_other_instruction(irp, instruction->promise_result_type); @@ -1605,6 +1623,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdSqrt: ir_print_sqrt(irp, (IrInstructionSqrt *)instruction); break; + case IrInstructionIdAtomicLoad: + ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/std/os/index.zig b/std/os/index.zig index 15b54f2e98..dbdb8c90cd 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2392,7 +2392,7 @@ pub const Thread = struct { pub fn wait(self: &const Thread) void { while (true) { - const pid_value = self.pid; // TODO atomic load + const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst); if (pid_value == 0) break; const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null); switch (linux.getErrno(rc)) { diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig index e8e81b76e6..323906e4a4 100644 --- a/test/cases/atomics.zig +++ b/test/cases/atomics.zig @@ -15,13 +15,25 @@ test "fence" { x = 5678; } -test "atomicrmw" { +test "atomicrmw and atomicload" { var data: u8 = 200; testAtomicRmw(&data); assert(data == 42); + testAtomicLoad(&data); } fn testAtomicRmw(ptr: &u8) void { const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst); assert(prev_value == 200); + comptime { + var x: i32 = 1234; + const y: i32 = 12345; + assert(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234); + assert(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345); + } +} + +fn testAtomicLoad(ptr: &u8) void { + const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); + assert(x == 42); } -- cgit v1.2.3 From 253ecd5c11747f49575b8425a506c2fecfe26ee2 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Mon, 16 Apr 2018 03:26:10 +0300 Subject: Added ReleaseSmall mode --- src/all_types.hpp | 1 + src/codegen.cpp | 18 ++++++++++++------ src/main.cpp | 3 +++ src/zig_llvm.cpp | 4 ++-- src/zig_llvm.h | 2 +- std/build.zig | 15 ++++++++++----- std/special/builtin.zig | 4 +++- 7 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 708ad81791..7ef7c10393 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1457,6 +1457,7 @@ enum BuildMode { BuildModeDebug, BuildModeFastRelease, BuildModeSafeRelease, + BuildModeSmallRelease, }; enum EmitFileType { diff --git a/src/codegen.cpp b/src/codegen.cpp index 9f319d9122..0279771be7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -512,7 +512,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { } if (fn_table_entry->body_node != nullptr) { - bool want_fn_safety = g->build_mode != BuildModeFastRelease && !fn_table_entry->def_scope->safety_off; + bool want_fn_safety = g->build_mode != BuildModeFastRelease && + g->build_mode != BuildModeSmallRelease && + !fn_table_entry->def_scope->safety_off; if (want_fn_safety) { if (g->libc_link_lib != nullptr) { addLLVMFnAttr(fn_table_entry->llvm_value, "sspstrong"); @@ -817,7 +819,7 @@ static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) { } static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) { - if (g->build_mode == BuildModeFastRelease) + if (g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease) return false; // TODO memoize @@ -5747,10 +5749,12 @@ static void do_code_gen(CodeGen *g) { os_path_join(g->cache_dir, o_basename, output_path); ensure_cache_dir(g); + bool is_small = g->build_mode == BuildModeSmallRelease; + switch (g->emit_file_type) { case EmitFileTypeBinary: if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), - ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug)) + ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug, is_small)) { zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg); } @@ -5760,7 +5764,7 @@ static void do_code_gen(CodeGen *g) { case EmitFileTypeAssembly: if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), - ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug)) + ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug, is_small)) { zig_panic("unable to write assembly file %s: %s", buf_ptr(output_path), err_msg); } @@ -5769,7 +5773,7 @@ static void do_code_gen(CodeGen *g) { case EmitFileTypeLLVMIr: if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), - ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug)) + ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug, is_small)) { zig_panic("unable to write llvm-ir file %s: %s", buf_ptr(output_path), err_msg); } @@ -6160,6 +6164,7 @@ static const char *build_mode_to_str(BuildMode build_mode) { case BuildModeDebug: return "Mode.Debug"; case BuildModeSafeRelease: return "Mode.ReleaseSafe"; case BuildModeFastRelease: return "Mode.ReleaseFast"; + case BuildModeSmallRelease: return "Mode.ReleaseSmall"; } zig_unreachable(); } @@ -6300,6 +6305,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " Debug,\n" " ReleaseSafe,\n" " ReleaseFast,\n" + " ReleaseSmall,\n" "};\n\n"); } { @@ -6471,7 +6477,7 @@ static void init(CodeGen *g) { } } - g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease; + g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease && g->build_mode != BuildModeSmallRelease; define_builtin_fns(g); define_builtin_compile_vars(g); diff --git a/src/main.cpp b/src/main.cpp index 35c7462f4b..3398fd1dea 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,6 +43,7 @@ static int usage(const char *arg0) { " --pkg-end pop current pkg\n" " --release-fast build with optimizations on and safety off\n" " --release-safe build with optimizations on and safety on\n" + " --release-small build with size optimizations on and safety off\n" " --static output will be statically linked\n" " --strip exclude debug symbols\n" " --target-arch [name] specify target architecture\n" @@ -482,6 +483,8 @@ int main(int argc, char **argv) { build_mode = BuildModeFastRelease; } else if (strcmp(arg, "--release-safe") == 0) { build_mode = BuildModeSafeRelease; + } else if (strcmp(arg, "--release-small") == 0) { + build_mode = BuildModeSmallRelease; } else if (strcmp(arg, "--strip") == 0) { strip = true; } else if (strcmp(arg, "--static") == 0) { diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index b4eef13cc1..ef0e9f24a8 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -81,7 +81,7 @@ static const bool assertions_on = false; #endif bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, - const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug) + const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small) { std::error_code EC; raw_fd_ostream dest(filename, EC, sys::fs::F_None); @@ -100,7 +100,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM return true; } PMBuilder->OptLevel = target_machine->getOptLevel(); - PMBuilder->SizeLevel = 0; + PMBuilder->SizeLevel = is_small ? 1 : 0; PMBuilder->DisableTailCalls = is_debug; PMBuilder->DisableUnitAtATime = is_debug; diff --git a/src/zig_llvm.h b/src/zig_llvm.h index d6809000ce..0d267b1014 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -52,7 +52,7 @@ enum ZigLLVM_EmitOutputType { }; ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, - const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug); + const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small); ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref); diff --git a/std/build.zig b/std/build.zig index a4d745e450..a312b28a6f 100644 --- a/std/build.zig +++ b/std/build.zig @@ -426,15 +426,18 @@ pub const Builder = struct { const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false; const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false; + const release_small = self.option(bool, "release-small", "size optimizations on and safety off") ?? false; - const mode = if (release_safe and !release_fast) + const mode = if (release_safe and !release_fast and !release_small) builtin.Mode.ReleaseSafe - else if (release_fast and !release_safe) + else if (release_fast and !release_safe and !release_small) builtin.Mode.ReleaseFast - else if (!release_fast and !release_safe) + else if (release_small and !release_fast and !release_safe) + builtin.Mode.ReleaseSmall + else if (!release_fast and !release_safe and !release_small) builtin.Mode.Debug else x: { - warn("Both -Drelease-safe and -Drelease-fast specified"); + warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)"); self.markInvalidUserInput(); break :x builtin.Mode.Debug; }; @@ -1229,6 +1232,7 @@ pub const LibExeObjStep = struct { builtin.Mode.Debug => {}, builtin.Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable, builtin.Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable, + builtin.Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable, } zig_args.append("--cache-dir") catch unreachable; @@ -1369,7 +1373,7 @@ pub const LibExeObjStep = struct { args.append("ssp-buffer-size=4") catch unreachable; } }, - builtin.Mode.ReleaseFast => { + builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => { args.append("-O2") catch unreachable; args.append("-fno-stack-protector") catch unreachable; }, @@ -1706,6 +1710,7 @@ pub const TestStep = struct { builtin.Mode.Debug => {}, builtin.Mode.ReleaseSafe => try zig_args.append("--release-safe"), builtin.Mode.ReleaseFast => try zig_args.append("--release-fast"), + builtin.Mode.ReleaseSmall => try zig_args.append("--release-small"), } switch (self.target) { diff --git a/std/special/builtin.zig b/std/special/builtin.zig index 56aa2ebaf8..a5126bc4f3 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -54,7 +54,9 @@ export fn memmove(dest: ?&u8, src: ?&const u8, n: usize) ?&u8 { } comptime { - if (builtin.mode != builtin.Mode.ReleaseFast and builtin.os != builtin.Os.windows) { + if (builtin.mode != builtin.Mode.ReleaseFast and + builtin.mode != builtin.Mode.ReleaseSmall and + builtin.os != builtin.Os.windows) { @export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong); } if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) { -- cgit v1.2.3 From 1c85050dad6a7e1d486606205ca2eb2cd7028ef5 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Mon, 16 Apr 2018 03:54:40 +0300 Subject: Set SizeLevel to 2 in ReleaseSmall mode --- src/zig_llvm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index ef0e9f24a8..a56378ab3e 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -100,7 +100,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM return true; } PMBuilder->OptLevel = target_machine->getOptLevel(); - PMBuilder->SizeLevel = is_small ? 1 : 0; + PMBuilder->SizeLevel = is_small ? 2 : 0; PMBuilder->DisableTailCalls = is_debug; PMBuilder->DisableUnitAtATime = is_debug; -- cgit v1.2.3 From 6492763befb40e65be3fca2bbb2c093997935746 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Mon, 16 Apr 2018 04:06:00 +0300 Subject: Fixed test build code --- test/tests.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/test/tests.zig b/test/tests.zig index 19a4f82b74..c3c7bf9d4b 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -583,6 +583,7 @@ pub const CompileErrorContext = struct { Mode.Debug => {}, Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable, Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable, + Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable, } warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name); -- cgit v1.2.3 From 1bc140964fb394819908d267561103b6c7e5036c Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Mon, 16 Apr 2018 04:18:52 +0300 Subject: Added ReleaseSmall mode to docgen --- doc/docgen.zig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/docgen.zig b/doc/docgen.zig index 56d9a04412..bd9dc6c147 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -749,6 +749,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var try build_args.append("--release-fast"); try out.print(" --release-fast"); }, + builtin.Mode.ReleaseSmall => { + try build_args.append("--release-small"); + try out.print(" --release-small"); + }, } for (code.link_objects) |link_object| { const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext); @@ -810,6 +814,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var try test_args.append("--release-fast"); try out.print(" --release-fast"); }, + builtin.Mode.ReleaseSmall => { + try test_args.append("--release-small"); + try out.print(" --release-small"); + }, } if (code.target_windows) { try test_args.appendSlice([][]const u8{ @@ -840,6 +848,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var try test_args.append("--release-fast"); try out.print(" --release-fast"); }, + builtin.Mode.ReleaseSmall => { + try test_args.append("--release-small"); + try out.print(" --release-small"); + }, } const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size); switch (result.term) { @@ -874,6 +886,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var builtin.Mode.Debug => {}, builtin.Mode.ReleaseSafe => try test_args.append("--release-safe"), builtin.Mode.ReleaseFast => try test_args.append("--release-fast"), + builtin.Mode.ReleaseSmall => try test_args.append("--release-small"), } const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size); @@ -927,6 +940,12 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var try out.print(" --release-fast"); } }, + builtin.Mode.ReleaseSmall => { + try build_args.append("--release-small"); + if (!code.is_inline) { + try out.print(" --release-small"); + } + }, } if (maybe_error_match) |error_match| { -- cgit v1.2.3 From caefaf781e22a7b053426621719a6f1d0f69d7cb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Apr 2018 20:15:19 -0400 Subject: std.debug: dumpStackTrace & friends use DirectAllocator this has the downside of failing to print a stack trace when the system is out of memory (maybe we could add a FallbackAllocator which tries DirectAllocator and falls back on the 200KB preallocated buffer). but for the more common use case when the system is not out of memory, but the debug info cannot fit in std.debug.global_allocator, now stack traces will work. this is the case for the self hosted compiler. --- std/debug/index.zig | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/std/debug/index.zig b/std/debug/index.zig index a573dc5549..9057f157de 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -38,7 +38,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace { if (self_debug_info) |info| { return info; } else { - const info = try openSelfDebugInfo(global_allocator); + const info = try openSelfDebugInfo(getDebugInfoAllocator()); self_debug_info = info; return info; } @@ -51,7 +51,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; return; }; - writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), start_addr) catch |err| { + writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty(), start_addr) catch |err| { stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; return; }; @@ -64,7 +64,7 @@ pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; return; }; - writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| { + writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty()) catch |err| { stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; return; }; @@ -592,8 +592,8 @@ fn getString(st: &ElfStackTrace, offset: u64) ![]u8 { } fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 { - const buf = try global_allocator.alloc(u8, size); - errdefer global_allocator.free(buf); + const buf = try allocator.alloc(u8, size); + errdefer allocator.free(buf); if ((try in_stream.read(buf)) < size) return error.EndOfFile; return buf; } @@ -1126,6 +1126,21 @@ fn readILeb128(in_stream: var) !i64 { } } +/// This should only be used in temporary test programs. pub const global_allocator = &global_fixed_allocator.allocator; var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]); var global_allocator_mem: [100 * 1024]u8 = undefined; + + +// TODO make thread safe +var debug_info_allocator: ?&mem.Allocator = null; +var debug_info_direct_allocator: std.heap.DirectAllocator = undefined; +var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined; +fn getDebugInfoAllocator() &mem.Allocator { + if (debug_info_allocator) |a| return a; + + debug_info_direct_allocator = std.heap.DirectAllocator.init(); + debug_info_arena_allocator = std.heap.ArenaAllocator.init(&debug_info_direct_allocator.allocator); + debug_info_allocator = &debug_info_arena_allocator.allocator; + return &debug_info_arena_allocator.allocator; +} -- cgit v1.2.3 From c7cb5c31e5b0cb9a88365c1264bfddf3c50ed107 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 14 Apr 2018 21:08:49 +1200 Subject: Add exp/norm distributed random float generation --- CMakeLists.txt | 1 + std/rand/index.zig | 24 +++++++-- std/rand/ziggurat.zig | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 std/rand/ziggurat.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index bf90a7ef46..ea21f6fc75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -515,6 +515,7 @@ set(ZIG_STD_FILES "os/windows/util.zig" "os/zen.zig" "rand/index.zig" + "rand/ziggurat.zig" "sort.zig" "special/bootstrap.zig" "special/bootstrap_lib.zig" diff --git a/std/rand/index.zig b/std/rand/index.zig index 6a746fce92..bd6209009e 100644 --- a/std/rand/index.zig +++ b/std/rand/index.zig @@ -19,6 +19,7 @@ const builtin = @import("builtin"); const assert = std.debug.assert; const mem = std.mem; const math = std.math; +const ziggurat = @import("ziggurat.zig"); // When you need fast unbiased random numbers pub const DefaultPrng = Xoroshiro128; @@ -109,15 +110,28 @@ pub const Random = struct { } } - /// Return a floating point value normally distributed in the range [0, 1]. + /// Return a floating point value normally distributed with mean = 0, stddev = 1. + /// + /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean. pub fn floatNorm(r: &Random, comptime T: type) T { - // TODO(tiehuis): See https://www.doornik.com/research/ziggurat.pdf - @compileError("floatNorm is unimplemented"); + const value = ziggurat.next_f64(r, ziggurat.NormDist); + switch (T) { + f32 => return f32(value), + f64 => return value, + else => @compileError("unknown floating point type"), + } } - /// Return a exponentially distributed float between (0, @maxValue(f64)) + /// Return an exponentially distributed float with a rate parameter of 1. + /// + /// To use a different rate parameter, use: floatExp(...) / desiredRate. pub fn floatExp(r: &Random, comptime T: type) T { - @compileError("floatExp is unimplemented"); + const value = ziggurat.next_f64(r, ziggurat.ExpDist); + switch (T) { + f32 => return f32(value), + f64 => return value, + else => @compileError("unknown floating point type"), + } } /// Shuffle a slice into a random order. diff --git a/std/rand/ziggurat.zig b/std/rand/ziggurat.zig new file mode 100644 index 0000000000..7790b71d26 --- /dev/null +++ b/std/rand/ziggurat.zig @@ -0,0 +1,146 @@ +// Implements ZIGNOR [1]. +// +// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to Generate Normal Random Samples*] +// (https://www.doornik.com/research/ziggurat.pdf). Nuffield College, Oxford. +// +// rust/rand used as a reference; +// +// NOTE: This seems interesting but reference code is a bit hard to grok: +// https://sbarral.github.io/etf. + +const std = @import("../index.zig"); +const math = std.math; +const Random = std.rand.Random; + +pub fn next_f64(random: &Random, comptime tables: &const ZigTable) f64 { + while (true) { + // We manually construct a float from parts as we can avoid an extra random lookup here by + // using the unused exponent for the lookup table entry. + const bits = random.scalar(u64); + const i = usize(bits & 0xff); + + const u = blk: { + if (tables.is_symmetric) { + // Generate a value in the range [2, 4) and scale into [-1, 1) + const repr = ((0x3ff + 1) << 52) | (bits >> 12); + break :blk @bitCast(f64, repr) - 3.0; + } else { + // Generate a value in the range [1, 2) and scale into (0, 1) + const repr = (0x3ff << 52) | (bits >> 12); + break :blk @bitCast(f64, repr) - (1.0 - math.f64_epsilon / 2.0); + } + }; + + const x = u * tables.x[i]; + const test_x = if (tables.is_symmetric) math.fabs(x) else x; + + // equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i]) + if (test_x < tables.x[i + 1]) { + return x; + } + + if (i == 0) { + return tables.zero_case(random, u); + } + + // equivalent to f1 + DRanU() * (f0 - f1) < 1 + if (tables.f[i + 1] + (tables.f[i] - tables.f[i + 1]) * random.float(f64) < tables.pdf(x)) { + return x; + } + } +} + +pub const ZigTable = struct { + r: f64, + x: [257]f64, + f: [257]f64, + + // probability density function used as a fallback + pdf: fn(f64) f64, + // whether the distribution is symmetric + is_symmetric: bool, + // fallback calculation in the case we are in the 0 block + zero_case: fn(&Random, f64) f64, +}; + +// zigNorInit +fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64, + comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable { + var tables: ZigTable = undefined; + + tables.is_symmetric = is_symmetric; + tables.r = r; + tables.pdf = f; + tables.zero_case = zero_case; + + tables.x[0] = v / f(r); + tables.x[1] = r; + + for (tables.x[2..256]) |*entry, i| { + const last = tables.x[2 + i - 1]; + *entry = f_inv(v / last + f(last)); + } + tables.x[256] = 0; + + for (tables.f[0..]) |*entry, i| { + *entry = f(tables.x[i]); + } + + return tables; +} + +// N(0, 1) +pub const NormDist = blk: { + @setEvalBranchQuota(30000); + break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); +}; + +const norm_r = 3.6541528853610088; +const norm_v = 0.00492867323399; + +fn norm_f(x: f64) f64 { return math.exp(-x * x / 2.0); } +fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); } +fn norm_zero_case(random: &Random, u: f64) f64 { + var x: f64 = 1; + var y: f64 = 0; + + while (-2.0 * y < x * x) { + x = math.ln(random.float(f64)) / norm_r; + y = math.ln(random.float(f64)); + } + + if (u < 0) { + return x - norm_r; + } else { + return norm_r - x; + } +} + +test "ziggurant normal dist sanity" { + var prng = std.rand.DefaultPrng.init(0); + var i: usize = 0; + while (i < 1000) : (i += 1) { + _ = prng.random.floatNorm(f64); + } +} + +// Exp(1) +pub const ExpDist = blk: { + @setEvalBranchQuota(30000); + break :blk ZigTableGen(false, exp_r, exp_v, exp_f, exp_f_inv, exp_zero_case); +}; + +const exp_r = 7.69711747013104972; +const exp_v = 0.0039496598225815571993; + +fn exp_f(x: f64) f64 { return math.exp(-x); } +fn exp_f_inv(y: f64) f64 { return -math.ln(y); } +fn exp_zero_case(random: &Random, _: f64) f64 { return exp_r - math.ln(random.float(f64)); } + +test "ziggurant exp dist sanity" { + var prng = std.rand.DefaultPrng.init(0); + var i: usize = 0; + while (i < 1000) : (i += 1) { + _ = prng.random.floatExp(f64); + } +} -- cgit v1.2.3 From 96ebd8b23b39e2d4019a8019a6774d7c3d20149d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Apr 2018 22:33:34 -0400 Subject: fix windows not respecting --msvc-lib-dir, --kernel32-lib-dir I believe this was a regression caused by 51a6ff18d454f4cb0faa0f1837df9f0c55a80b43 closes #927 --- src/analyze.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 9092da6e3b..ca18208ba9 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4421,24 +4421,30 @@ void find_libc_lib_path(CodeGen *g) { if (g->zig_target.os == OsWindows) { ZigWindowsSDK *sdk = get_windows_sdk(g); - Buf* vc_lib_dir = buf_alloc(); - if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { - zig_panic("Unable to determine vcruntime path."); + if (g->msvc_lib_dir == nullptr) { + Buf* vc_lib_dir = buf_alloc(); + if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { + zig_panic("Unable to determine vcruntime path."); + } + g->msvc_lib_dir = vc_lib_dir; } - Buf* ucrt_lib_path = buf_alloc(); - if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) { - zig_panic("Unable to determine ucrt path."); + if (g->libc_lib_dir == nullptr) { + Buf* ucrt_lib_path = buf_alloc(); + if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) { + zig_panic("Unable to determine ucrt path."); + } + g->libc_lib_dir = ucrt_lib_path; } - Buf* kern_lib_path = buf_alloc(); - if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) { - zig_panic("Unable to determine kernel32 path."); + if (g->kernel32_lib_dir == nullptr) { + Buf* kern_lib_path = buf_alloc(); + if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) { + zig_panic("Unable to determine kernel32 path."); + } + g->kernel32_lib_dir = kern_lib_path; } - g->msvc_lib_dir = vc_lib_dir; - g->libc_lib_dir = ucrt_lib_path; - g->kernel32_lib_dir = kern_lib_path; } else if (g->zig_target.os == OsLinux) { g->libc_lib_dir = get_linux_libc_lib_path("crt1.o"); } else { -- cgit v1.2.3 From f1f998e07124f141312289ff82e0ad8d99af1cf7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Apr 2018 12:16:42 -0400 Subject: improve cmpxchg * remove @cmpxchg, add @cmpxchgWeak and @cmpxchgStrong - See explanations in the langref. * add operand type as first parameter * return type is ?T where T is the operand type closes #461 --- doc/langref.html.in | 54 ++++++++++++++++++++++++++++--- src/all_types.hpp | 9 +++++- src/codegen.cpp | 21 ++++++++++-- src/ir.cpp | 85 ++++++++++++++++++++++++++----------------------- src/zig_llvm.cpp | 8 +++-- src/zig_llvm.h | 2 +- test/cases/atomics.zig | 16 ++++++++-- test/compile_errors.zig | 16 +++++----- 8 files changed, 148 insertions(+), 63 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 5cbec218a9..a5d31aada4 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4065,16 +4065,60 @@ comptime {

{#header_close#} - {#header_open|@cmpxchg#} -
@cmpxchg(ptr: &T, cmp: T, new: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> bool
+ {#header_open|@cmpxchgStrong#} +
@cmpxchgStrong(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> ?T

- This function performs an atomic compare exchange operation. + This function performs a strong atomic compare exchange operation. It's the equivalent of this code, + except atomic: +

+ {#code_begin|syntax#} +fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T { + const old_value = *ptr; + if (old_value == expected_value) { + *ptr = new_value; + return null; + } else { + return old_value; + } +} + {#code_end#} +

+ If you are using cmpxchg in a loop, {#link|@cmpxchgWeak#} is the better choice, because it can be implemented + more efficiently in machine instructions.

AtomicOrder can be found with @import("builtin").AtomicOrder.

@typeOf(ptr).alignment must be >= @sizeOf(T).

- {#see_also|Compile Variables#} + {#see_also|Compile Variables|cmpxchgWeak#} + {#header_close#} + {#header_open|@cmpxchgWeak#} +
@cmpxchgWeak(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> ?T
+

+ This function performs a weak atomic compare exchange operation. It's the equivalent of this code, + except atomic: +

+ {#code_begin|syntax#} +fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T { + const old_value = *ptr; + if (old_value == expected_value and usuallyTrueButSometimesFalse()) { + *ptr = new_value; + return null; + } else { + return old_value; + } +} + {#code_end#} +

+ If you are using cmpxchg in a loop, the sporadic failure will be no problem, and cmpxchgWeak + is the better choice, because it can be implemented more efficiently in machine instructions. + However if you need a stronger guarantee, use {#link|@cmpxchgStrong#}. +

+

+ AtomicOrder can be found with @import("builtin").AtomicOrder. +

+

@typeOf(ptr).alignment must be >= @sizeOf(T).

+ {#see_also|Compile Variables|cmpxchgStrong#} {#header_close#} {#header_open|@compileError#}
@compileError(comptime msg: []u8)
@@ -6020,7 +6064,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index 7ef7c10393..5a3590dd4d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1310,7 +1310,8 @@ enum BuiltinFnId { BuiltinFnIdReturnAddress, BuiltinFnIdFrameAddress, BuiltinFnIdEmbedFile, - BuiltinFnIdCmpExchange, + BuiltinFnIdCmpxchgWeak, + BuiltinFnIdCmpxchgStrong, BuiltinFnIdFence, BuiltinFnIdDivExact, BuiltinFnIdDivTrunc, @@ -2528,6 +2529,7 @@ struct IrInstructionEmbedFile { struct IrInstructionCmpxchg { IrInstruction base; + IrInstruction *type_value; IrInstruction *ptr; IrInstruction *cmp_value; IrInstruction *new_value; @@ -2535,8 +2537,13 @@ struct IrInstructionCmpxchg { IrInstruction *failure_order_value; // if this instruction gets to runtime then we know these values: + TypeTableEntry *type; AtomicOrder success_order; AtomicOrder failure_order; + + bool is_weak; + + LLVMValueRef tmp_ptr; }; struct IrInstructionFence { diff --git a/src/codegen.cpp b/src/codegen.cpp index 0279771be7..a7d373e9d0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3558,9 +3558,20 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order); LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, - success_order, failure_order); + success_order, failure_order, instruction->is_weak); - return LLVMBuildExtractValue(g->builder, result_val, 1, ""); + assert(instruction->tmp_ptr != nullptr); + assert(type_has_bits(instruction->type)); + + LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); + gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val); + + LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); + LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); + LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); + gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false); + return instruction->tmp_ptr; } static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) { @@ -5588,6 +5599,9 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdErrWrapCode) { IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction; slot = &err_wrap_code_instruction->tmp_ptr; + } else if (instruction->id == IrInstructionIdCmpxchg) { + IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction; + slot = &cmpxchg_instruction->tmp_ptr; } else { zig_unreachable(); } @@ -6115,7 +6129,8 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1); create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2); create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1); - create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5); + create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6); + create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6); create_builtin_fn(g, BuiltinFnIdFence, "fence", 1); create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); diff --git a/src/ir.cpp b/src/ir.cpp index d43efe0190..89193a4c27 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -110,6 +110,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type); static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr); +static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == TypeTableEntryIdPointer); @@ -1832,38 +1833,34 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, - IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, - AtomicOrder success_order, AtomicOrder failure_order) +static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, + IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, + IrInstruction *success_order_value, IrInstruction *failure_order_value, + bool is_weak, + TypeTableEntry *type, AtomicOrder success_order, AtomicOrder failure_order) { IrInstructionCmpxchg *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; instruction->ptr = ptr; instruction->cmp_value = cmp_value; instruction->new_value = new_value; instruction->success_order_value = success_order_value; instruction->failure_order_value = failure_order_value; + instruction->is_weak = is_weak; + instruction->type = type; instruction->success_order = success_order; instruction->failure_order = failure_order; + if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(cmp_value, irb->current_basic_block); ir_ref_instruction(new_value, irb->current_basic_block); - ir_ref_instruction(success_order_value, irb->current_basic_block); - ir_ref_instruction(failure_order_value, irb->current_basic_block); + if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block); + if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block); return &instruction->base; } -static IrInstruction *ir_build_cmpxchg_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr, - IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, - AtomicOrder success_order, AtomicOrder failure_order) -{ - IrInstruction *new_instruction = ir_build_cmpxchg(irb, old_instruction->scope, old_instruction->source_node, - ptr, cmp_value, new_value, success_order_value, failure_order_value, success_order, failure_order); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) { IrInstructionFence *instruction = ir_build_instruction(irb, scope, source_node); instruction->order_value = order_value; @@ -3771,7 +3768,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_embed_file(irb, scope, node, arg0_value); } - case BuiltinFnIdCmpExchange: + case BuiltinFnIdCmpxchgWeak: + case BuiltinFnIdCmpxchgStrong: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); @@ -3798,9 +3796,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg4_value == irb->codegen->invalid_instruction) return arg4_value; + AstNode *arg5_node = node->data.fn_call_expr.params.at(5); + IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope); + if (arg5_value == irb->codegen->invalid_instruction) + return arg5_value; + return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, - arg2_value, arg3_value, arg4_value, - AtomicOrderUnordered, AtomicOrderUnordered); + arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), + nullptr, AtomicOrderUnordered, AtomicOrderUnordered); } case BuiltinFnIdFence: { @@ -15730,10 +15733,20 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr } static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) { + TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->other); + if (type_is_invalid(operand_type)) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *ptr = instruction->ptr->other; if (type_is_invalid(ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; + // TODO let this be volatile + TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); + IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr, ptr_type); + if (type_is_invalid(casted_ptr->value.type)) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *cmp_value = instruction->cmp_value->other; if (type_is_invalid(cmp_value->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -15758,28 +15771,11 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order)) return ira->codegen->builtin_types.entry_invalid; - if (ptr->value.type->id != TypeTableEntryIdPointer) { - ir_add_error(ira, instruction->ptr, - buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&ptr->value.type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - - TypeTableEntry *child_type = ptr->value.type->data.pointer.child_type; - - uint32_t align_bytes = ptr->value.type->data.pointer.alignment; - uint64_t size_bytes = type_size(ira->codegen, child_type); - if (align_bytes < size_bytes) { - ir_add_error(ira, instruction->ptr, - buf_sprintf("expected pointer alignment of at least %" ZIG_PRI_u64 ", found %" PRIu32, - size_bytes, align_bytes)); - return ira->codegen->builtin_types.entry_invalid; - } - - IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, child_type); + IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, operand_type); if (type_is_invalid(casted_cmp_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, child_type); + IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, operand_type); if (type_is_invalid(casted_new_value->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -15804,9 +15800,17 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct return ira->codegen->builtin_types.entry_invalid; } - ir_build_cmpxchg_from(&ira->new_irb, &instruction->base, ptr, casted_cmp_value, casted_new_value, - success_order_value, failure_order_value, success_order, failure_order); - return ira->codegen->builtin_types.entry_bool; + if (instr_is_comptime(casted_ptr) && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) { + zig_panic("TODO compile-time execution of cmpxchg"); + } + + IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node, + nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, + operand_type, success_order, failure_order); + result->value.type = get_maybe_type(ira->codegen, operand_type); + ir_link_new_instruction(result, &instruction->base); + ir_add_alloca(ira, result, result->value.type); + return result->value.type; } static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) { @@ -17981,6 +17985,7 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr if (type_is_invalid(ptr_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; + // TODO let this be volatile TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); if (type_is_invalid(casted_ptr->value.type)) diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index a56378ab3e..5905fa8167 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -765,10 +765,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) { LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp, LLVMValueRef new_val, LLVMAtomicOrdering success_ordering, - LLVMAtomicOrdering failure_ordering) + LLVMAtomicOrdering failure_ordering, bool is_weak) { - return wrap(unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), unwrap(new_val), - mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering))); + AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), + unwrap(new_val), mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering)); + inst->setWeak(is_weak); + return wrap(inst); } LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 0d267b1014..d34300b8ae 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -66,7 +66,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LL ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp, LLVMValueRef new_val, LLVMAtomicOrdering success_ordering, - LLVMAtomicOrdering failure_ordering); + LLVMAtomicOrdering failure_ordering, bool is_weak); ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char *name); diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig index 323906e4a4..4cadabb728 100644 --- a/test/cases/atomics.zig +++ b/test/cases/atomics.zig @@ -1,12 +1,24 @@ -const assert = @import("std").debug.assert; +const std = @import("std"); +const assert = std.debug.assert; const builtin = @import("builtin"); const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; test "cmpxchg" { var x: i32 = 1234; - while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} + if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assert(x1 == 1234); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assert(x1 == 1234); + } assert(x == 5678); + + assert(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assert(x == 42); } test "fence" { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index b22816a9a8..926e997c6e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1394,17 +1394,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn f() void { \\ var x: i32 = 1234; - \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {} \\} - , ".tmp_source.zig:4:72: error: failure atomic ordering must be no stricter than success"); + , ".tmp_source.zig:4:81: error: failure atomic ordering must be no stricter than success"); cases.add("atomic orderings of cmpxchg - success Monotonic or stricter", \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn f() void { \\ var x: i32 = 1234; - \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {} \\} - , ".tmp_source.zig:4:49: error: success atomic ordering must be Monotonic or stricter"); + , ".tmp_source.zig:4:58: error: success atomic ordering must be Monotonic or stricter"); cases.add("negation overflow in function evaluation", \\const y = neg(-128); @@ -2460,11 +2460,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn entry() bool { \\ var x: i32 align(1) = 1234; - \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} \\ return x == 5678; \\} , - ".tmp_source.zig:4:23: error: expected pointer alignment of at least 4, found 1"); + ".tmp_source.zig:4:32: error: expected type '&i32', found '&align(1) i32'"); cases.add("wrong size to an array literal", \\comptime { @@ -2534,10 +2534,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add("wrong types given to atomic order args in cmpxchg", \\export fn entry() void { \\ var x: i32 = 1234; - \\ while (!@cmpxchg(&x, 1234, 5678, u32(1234), u32(1234))) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, u32(1234), u32(1234))) {} \\} , - ".tmp_source.zig:3:41: error: expected type 'AtomicOrder', found 'u32'"); + ".tmp_source.zig:3:50: error: expected type 'AtomicOrder', found 'u32'"); cases.add("wrong types given to @export", \\extern fn entry() void { } -- cgit v1.2.3 From c90f936eef81fce3355231c4d79ecfe40df84f7e Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 18 Apr 2018 13:52:25 -0500 Subject: Added timestamp, high-perf. timer functions. --- std/os/epoch.zig | 26 ++++++ std/os/time.zig | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+) create mode 100644 std/os/epoch.zig create mode 100644 std/os/time.zig diff --git a/std/os/epoch.zig b/std/os/epoch.zig new file mode 100644 index 0000000000..8f64fe8572 --- /dev/null +++ b/std/os/epoch.zig @@ -0,0 +1,26 @@ +/// Epoch reference times in terms of their difference from +/// posix epoch in seconds. +pub const posix = 0; //Jan 01, 1970 AD +pub const dos = 315532800; //Jan 01, 1980 AD +pub const ios = 978307200; //Jan 01, 2001 AD +pub const openvms = -3506716800; //Nov 17, 1858 AD +pub const zos = -2208988800; //Jan 01, 1900 AD +pub const windows = -11644473600; //Jan 01, 1601 AD +pub const amiga = 252460800; //Jan 01, 1978 AD +pub const pickos = -63244800; //Dec 31, 1967 AD +pub const gps = 315964800; //Jan 06, 1980 AD +pub const clr = 62135769600; //Jan 01, 0001 AD + +pub const unix = posix; +pub const android = posix; +pub const os2 = dos; +pub const bios = dos; +pub const vfat = dos; +pub const ntfs = windows; +pub const ntp = zos; +pub const jbase = pickos; +pub const aros = amiga; +pub const morphos = amiga; +pub const brew = gps; +pub const atsc = gps; +pub const go = clr; \ No newline at end of file diff --git a/std/os/time.zig b/std/os/time.zig new file mode 100644 index 0000000000..f2e1307057 --- /dev/null +++ b/std/os/time.zig @@ -0,0 +1,262 @@ +const std = @import("../index.zig"); +const builtin = @import("builtin"); +const Os = builtin.Os; +const debug = std.debug; + +const windows = std.os.windows; +const darwin = std.os.darwin; +const posix = std.os.posix; + +pub const epoch = @import("epoch.zig"); + +/// Sleep for the specified duration +pub fn sleep(seconds: usize, nanoseconds: usize) void { + switch(builtin.os) { + Os.linux, Os.macosx, Os.ios => { + posixSleep(u63(seconds), u63(nanoseconds)); + }, + Os.windows => { + const milliseconds = seconds * ms_per_s + nanoseconds / (ns_per_s / ms_per_s); + windows.Sleep(windows.DWORD(milliseconds)); + }, + else => @compileError("Unsupported OS"), + } +} + +const u63 = @IntType(false, 63); +pub fn posixSleep(seconds: u63, nanoseconds: u63) void { + var req = posix.timespec { + .tv_sec = seconds, + .tv_nsec = nanoseconds, + }; + var rem: posix.timespec = undefined; + while (true) { + const ret_val = posix.nanosleep(&req, &rem); + const err = posix.getErrno(ret_val); + if (err == 0) return; + switch (err) { + posix.EFAULT => unreachable, + posix.EINVAL => { + // Sometimes Darwin returns EINVAL for no reason. + // We treat it as a spurious wakeup. + return; + }, + posix.EINTR => { + req = rem; + continue; + }, + else => return, + } + } +} + +/// Get the posix timestamp, UTC, in seconds +pub fn timestamp() u64 { + return @divFloor(miliTimestamp(), ms_per_s); +} + +/// Get the posix timestamp, UTC, in nanoseconds +pub const miliTimestamp = switch(builtin.os) { + Os.windows => miliTimestampWindows, + Os.linux => miliTimestampPosix, + Os.macosx, Os.ios => miliTimestampDarwin, + else => @compileError("Unsupported OS"), +}; + +fn miliTimestampWindows() u64 { + //FileTime has a granularity of 100 nanoseconds + // and uses the NTFS/Windows epoch + var ft: i64 = undefined; + windows.GetSystemTimeAsFileTime(&ft); + const hns_per_ms = (ns_per_s / 100) / ms_per_s; + const epoch_adj = epoch.windows * ms_per_s; + return u64(@divFloor(ft, hns_per_ms) + epoch_adj); +} + +fn miliTimestampDarwin() u64 { + //Sources suggest MacOS 10.12 has support for + // posix clock_gettime. + var tv: darwin.timeval = undefined; + var err = darwin.gettimeofday(&tv, null); + debug.assert(err == 0); + return tv.tv_sec * ms_per_s + ts.tv_usec + * (us_per_s / ms_per_s); +} + +fn miliTimestampPosix() u64 { + //From what I can tell there's no reason clock_gettime + // should ever fail for us with CLOCK_REALTIME + var ts: posix.timespec = undefined; + const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts); + debug.assert(err == 0); + const sec_ms = ts.tv_sec * ms_per_s; + const nsec_ms = @divFloor(ts.tv_nsec, ns_per_s / ms_per_s); + return u64(sec_ms) + u64(nsec_ms); +} + +/// Divisions of a second +pub const ns_per_s = 1000000000; +pub const us_per_s = 1000000; +pub const ms_per_s = 1000; +pub const cs_per_s = 100; + +/// Common time divisions +pub const s_per_min = 60; +pub const s_per_hour = s_per_min * 60; +pub const s_per_day = s_per_hour * 24; +pub const s_per_week = s_per_day * 7; + + +/// A monotonic high-performance timer. +/// Timer.start() must be called to initialize the struct, which captures +/// the counter frequency on windows and darwin, records the resolution, +/// and gives the user an oportunity to check for the existnece of +/// monotonic clocks without forcing them to check for error on each read. +/// .resolution is in nanoseconds on all platforms but .start_time's meaning +/// depends on the OS. On Windows and Darwin it is a hardware counter +/// value that requires calculation to convert to a meaninful unit. +pub const Timer = struct { + + //if we used resolution's value when performing the + // performance counter calc on windows, it would be + // less precise + frequency: switch(builtin.os) { + Os.windows => u64, + Os.macosx, Os.ios => darwin.mach_timebase_info_data, + else => void, + }, + resolution: u64, + start_time: u64, + + //Initialize the timer structure. + //This gives us an oportunity to grab the counter frequency in windows. + //On Windows: QueryPerformanceCounter will succeed on anything > XP. + //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not + // supported, or if the timespec pointer is out of bounds, which should be + // impossible here barring cosmic rays or other such occurances of + // incredibly bad luck. + //On Darwin: This cannot fail, as far as I am able to tell. + pub fn start() !Timer { + var self: Timer = undefined; + + switch(builtin.os) { + Os.windows => { + var freq: i64 = undefined; + var err = windows.QueryPerformanceFrequency(&freq); + if(err == 0) return error.TimerUnsupported; + self.frequency = u64(freq); + self.resolution = @divFloor(ns_per_s, self.frequency); + var start_time: i64 = undefined; + _ = windows.QueryPerformanceCounter(&start_time); + self.start_time = u64(start_time); + }, + Os.linux => { + var ts: posix.timespec = undefined; + var result = posix.clock_getres(posix.CLOCK_MONOTONIC, &ts); + switch(posix.getErrno(result)) { + 0 => {}, + posix.EINVAL => return error.TimerUnsupported, + else => unreachable, + } + self.resolution = u64(ts.tv_sec * ns_per_s + ts.tv_nsec); + _ = posix.clock_gettime(posix.CLOCK_MONOTONIC, &ts); + self.start_time = u64(ts.tv_sec * ns_per_s + ts.tv_nsec); + }, + Os.macosx, Os.ios => { + darwin.c.mach_timebase_info(&self.frequency); + self.resolution = @divFloor(self.frequency.numer, self.denom); + self.start_time = darwin.c.mach_absolute_time(); + }, + else => @compileError("Unsupported OS"), + } + return self; + } + + /// Reads the timer value since start or the last reset in nanoseconds + pub fn read(self: &Timer) u64 { + var clock = clockNative() - self.start_time; + return switch(builtin.os) { + Os.windows => @divFloor(clock * ns_per_s, self.frequency), + Os.linux => clock, + Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom), + else => @compileError("Unsupported OS"), + }; + } + + /// Resets the timer value to 0/now. + pub fn reset(self: &Timer) void + { + self.start_time = clockNative(); + } + + /// Returns the current value of the timer in nanoseconds, then resets it + pub fn lap(self: &Timer) u64 { + var now = clockNative(); + var lap_time = self.read(); + self.start_time = now; + return lap_time; + } + + + const clockNative = switch(builtin.os) { + Os.windows => clockWindows, + Os.linux => clockLinux, + Os.macosx, Os.ios => clockDarwin, + else => @compileError("Unsupported OS"), + }; + + fn clockWindows() u64 { + var result: i64 = undefined; + var err = windows.QueryPerformanceCounter(&result); + debug.assert(err != 0); + return u64(result); + } + + fn clockDarwin() u64 { + var result: u64 = undefined; + darwin.c.mach_absolute_time(&result); + return result; + } + + fn clockLinux() u64 { + var ts: posix.timespec = undefined; + var result = posix.clock_gettime(posix.CLOCK_MONOTONIC, &ts); + debug.assert(posix.getErrno(result) == 0); + return u64(ts.tv_sec * ns_per_s + ts.tv_nsec); + } +}; + + + + + +test "os.time.sleep" { + sleep(0, 1); +} + +test "os.time.timestamp" { + const ns_per_ms = (ns_per_s / ms_per_s); + const margin = 50; + + const time_0 = miliTimestamp(); + sleep(0, ns_per_ms); + const time_1 = miliTimestamp(); + const interval = time_1 - time_0; + debug.assert(interval > 0 and interval < margin); +} + +test "os.time.Timer" { + const ns_per_ms = (ns_per_s / ms_per_s); + const margin = ns_per_ms * 50; + + var timer = try Timer.start(); + sleep(0, 10 * ns_per_ms); + const time_0 = timer.read(); + debug.assert(time_0 > 0 and time_0 < margin); + + const time_1 = timer.lap(); + debug.assert(time_1 > time_0); + + timer.reset(); + debug.assert(timer.read() < time_1); +} \ No newline at end of file -- cgit v1.2.3 From 8b66dd8c7d2809c3ec86f1eec8acc0a1c184c8c2 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 18 Apr 2018 13:55:42 -0500 Subject: Added unstaged changes. --- CMakeLists.txt | 2 ++ std/c/darwin.zig | 18 ++++++++++++++++++ std/c/index.zig | 1 + std/fmt/index.zig | 3 ++- std/os/darwin.zig | 4 ++++ std/os/index.zig | 45 +-------------------------------------------- std/os/linux/index.zig | 20 ++++++++++++++++++++ std/os/linux/x86_64.zig | 10 ++++++++++ std/os/windows/index.zig | 7 +++++++ 9 files changed, 65 insertions(+), 45 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bb9bf517c..42bc71fd97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -507,6 +507,8 @@ set(ZIG_STD_FILES "os/linux/index.zig" "os/linux/x86_64.zig" "os/path.zig" + "os/time.zig" + "os/epoch.zig" "os/windows/error.zig" "os/windows/index.zig" "os/windows/util.zig" diff --git a/std/c/darwin.zig b/std/c/darwin.zig index aa49dfa3df..14b0ea0086 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -3,10 +3,28 @@ pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int; pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize; +pub extern "c" fn mach_absolute_time() u64; +pub extern "c" fn mach_timebase_info(&mach_timebase_info_data) void; + pub use @import("../os/darwin_errno.zig"); pub const _errno = __error; +pub const timeval = extern struct { + tv_sec: isize, + tv_usec: isize, +}; + +pub const timezone = extern struct { + tz_minuteswest: i32, + tz_dsttime: i32, +}; + +pub const mach_timebase_info_data = struct { + numer: u32, + denom: u32, +}; + /// Renamed to Stat to not conflict with the stat function. pub const Stat = extern struct { dev: i32, diff --git a/std/c/index.zig b/std/c/index.zig index 369ea2b358..223d6026ce 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -40,6 +40,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize; pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8; pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int; +pub extern "c" fn gettimeofday(&timeval, ?&timezone) c_int; pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int; pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?×pec) c_int; pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index bd5b5710e0..56395a0bd4 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -86,7 +86,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), }, 's' => { state = State.Buf; - },'.' => { + }, + '.' => { state = State.Float; }, else => @compileError("Unknown format character: " ++ []u8{c}), diff --git a/std/os/darwin.zig b/std/os/darwin.zig index f8b1fbed3b..3cf08199b2 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -251,6 +251,10 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u return errnoWrap(c.readlink(path, buf_ptr, buf_len)); } +pub fn gettimeofday(&timeval, ?&timezone) usize { + return errnoWrap(c.gettimeofday(timeval, timezone)); +} + pub fn nanosleep(req: &const timespec, rem: ?×pec) usize { return errnoWrap(c.nanosleep(req, rem)); } diff --git a/std/os/index.zig b/std/os/index.zig index 4b74af035e..5f76c4732a 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -18,6 +18,7 @@ pub const posix = switch(builtin.os) { pub const ChildProcess = @import("child_process.zig").ChildProcess; pub const path = @import("path.zig"); pub const File = @import("file.zig").File; +pub const time = @import("time.zig"); pub const FileMode = switch (builtin.os) { Os.windows => void, @@ -1356,50 +1357,6 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 { } } -pub fn sleep(seconds: usize, nanoseconds: usize) void { - switch(builtin.os) { - Os.linux, Os.macosx, Os.ios => { - posixSleep(u63(seconds), u63(nanoseconds)); - }, - Os.windows => { - const milliseconds = seconds * 1000 + nanoseconds / 1000000; - windows.Sleep(windows.DWORD(milliseconds)); - }, - else => @compileError("Unsupported OS"), - } -} - -const u63 = @IntType(false, 63); -pub fn posixSleep(seconds: u63, nanoseconds: u63) void { - var req = posix.timespec { - .tv_sec = seconds, - .tv_nsec = nanoseconds, - }; - var rem: posix.timespec = undefined; - while (true) { - const ret_val = posix.nanosleep(&req, &rem); - const err = posix.getErrno(ret_val); - if (err == 0) return; - switch (err) { - posix.EFAULT => unreachable, - posix.EINVAL => { - // Sometimes Darwin returns EINVAL for no reason. - // We treat it as a spurious wakeup. - return; - }, - posix.EINTR => { - req = rem; - continue; - }, - else => return, - } - } -} - -test "os.sleep" { - sleep(0, 1); -} - pub fn posix_setuid(uid: u32) !void { const err = posix.getErrno(posix.setuid(uid)); if (err == 0) return; diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 8fd8bcbe78..dff91a500d 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -495,6 +495,26 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize { return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); } +pub fn clock_gettime(clk_id: i32, tp: ×pec) usize { + return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); +} + +pub fn clock_getres(clk_id: i32, tp: ×pec) usize { + return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); +} + +pub fn clock_settime(clk_id: i32, tp: &const timespec) usize { + return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); +} + +pub fn gettimeofday(tv: &timeval, tz: &timezone) usize { + return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); +} + +pub fn settimeofdat(tv: &const timeval, tz: &const timezone) usize { + return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); +} + pub fn nanosleep(req: &const timespec, rem: ?×pec) usize { return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); } diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index cfb2231df9..0a50c67d88 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -489,6 +489,16 @@ pub const timespec = extern struct { tv_nsec: isize, }; +pub const timeval = extern struct { + tv_sec: isize, + tv_usec: isize, +}; + +pub const timezone = extern struct { + tz_minuteswest: i32, + tz_dsttime: i32, +}; + pub const dirent = extern struct { d_ino: usize, d_off: usize, diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 2709cf2a78..d944af9575 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -61,6 +61,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; +pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?&FILETIME) void; + pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void; @@ -77,6 +79,10 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR, dwFlags: DWORD) BOOL; + +pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: &LARGE_INTEGER) BOOL; + +pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: &LARGE_INTEGER) BOOL; pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void, in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD, @@ -137,6 +143,7 @@ pub const UNICODE = false; pub const WCHAR = u16; pub const WORD = u16; pub const LARGE_INTEGER = i64; +pub const FILETIME = i64; pub const TRUE = 1; pub const FALSE = 0; -- cgit v1.2.3 From bf9cf28322bf19aef72cbc5876e2ca083f762d7c Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 18 Apr 2018 15:46:50 -0500 Subject: Fixed compiler errors around darwin code. --- std/c/darwin.zig | 2 +- std/c/index.zig | 2 +- std/os/darwin.zig | 12 ++++++++++-- std/os/time.zig | 18 +++++++++--------- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 14b0ea0086..05b45edb2f 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -4,7 +4,7 @@ pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int; pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize; pub extern "c" fn mach_absolute_time() u64; -pub extern "c" fn mach_timebase_info(&mach_timebase_info_data) void; +pub extern "c" fn mach_timebase_info(tinfo: ?&mach_timebase_info_data) void; pub use @import("../os/darwin_errno.zig"); diff --git a/std/c/index.zig b/std/c/index.zig index 223d6026ce..35bd97f117 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -40,7 +40,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize; pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8; pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int; -pub extern "c" fn gettimeofday(&timeval, ?&timezone) c_int; +pub extern "c" fn gettimeofday(tv: ?&timeval, tz: ?&timezone) c_int; pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int; pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?×pec) c_int; pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 3cf08199b2..0f9c0be28b 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -251,8 +251,8 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u return errnoWrap(c.readlink(path, buf_ptr, buf_len)); } -pub fn gettimeofday(&timeval, ?&timezone) usize { - return errnoWrap(c.gettimeofday(timeval, timezone)); +pub fn gettimeofday(tv: ?&timeval, tz: ?&timezone) usize { + return errnoWrap(c.gettimeofday(tv, tz)); } pub fn nanosleep(req: &const timespec, rem: ?×pec) usize { @@ -322,3 +322,11 @@ pub fn sigaddset(set: &sigset_t, signo: u5) void { fn errnoWrap(value: isize) usize { return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value); } + + +pub const timezone = c.timezone; +pub const timeval = c.timeval; +pub const mach_timebase_info_data = c.mach_timebase_info_data; + +pub const mach_absolute_time = c.mach_absolute_time; +pub const mach_timebase_info = c.mach_timebase_info; \ No newline at end of file diff --git a/std/os/time.zig b/std/os/time.zig index f2e1307057..e6b614d433 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -79,8 +79,9 @@ fn miliTimestampDarwin() u64 { var tv: darwin.timeval = undefined; var err = darwin.gettimeofday(&tv, null); debug.assert(err == 0); - return tv.tv_sec * ms_per_s + ts.tv_usec - * (us_per_s / ms_per_s); + const sec_ms = tv.tv_sec * ms_per_s; + const usec_ms = @divFloor(tv.tv_usec, (us_per_s / ms_per_s)); + return u64(sec_ms) + u64(usec_ms); } fn miliTimestampPosix() u64 { @@ -136,7 +137,8 @@ pub const Timer = struct { // impossible here barring cosmic rays or other such occurances of // incredibly bad luck. //On Darwin: This cannot fail, as far as I am able to tell. - pub fn start() !Timer { + const TimerError = error{TimerUnsupported}; + pub fn start() TimerError!Timer { var self: Timer = undefined; switch(builtin.os) { @@ -163,9 +165,9 @@ pub const Timer = struct { self.start_time = u64(ts.tv_sec * ns_per_s + ts.tv_nsec); }, Os.macosx, Os.ios => { - darwin.c.mach_timebase_info(&self.frequency); - self.resolution = @divFloor(self.frequency.numer, self.denom); - self.start_time = darwin.c.mach_absolute_time(); + darwin.mach_timebase_info(&self.frequency); + self.resolution = @divFloor(self.frequency.numer, self.frequency.denom); + self.start_time = darwin.mach_absolute_time(); }, else => @compileError("Unsupported OS"), } @@ -213,9 +215,7 @@ pub const Timer = struct { } fn clockDarwin() u64 { - var result: u64 = undefined; - darwin.c.mach_absolute_time(&result); - return result; + return darwin.mach_absolute_time(); } fn clockLinux() u64 { -- cgit v1.2.3 From ca4341f7ba845e7af3c6f2be52cd60c51ec6d68f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Apr 2018 17:14:09 -0400 Subject: add --no-rosegment cli option this provides a workaround for #896 until valgrind adds support for clang/LLD (equivalent to gcc/gold -rosegment) --- src/all_types.hpp | 2 ++ src/link.cpp | 3 +++ src/main.cpp | 5 +++++ 3 files changed, 10 insertions(+) diff --git a/src/all_types.hpp b/src/all_types.hpp index 5a3590dd4d..88e0ba27a8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1705,6 +1705,8 @@ struct CodeGen { ZigList error_di_types; ZigList forbidden_libs; + + bool no_rosegment_workaround; }; enum VarLinkage { diff --git a/src/link.cpp b/src/link.cpp index 3c6e27e331..d454d77aae 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -217,6 +217,9 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append(g->linker_script); } + if (g->no_rosegment_workaround) { + lj->args.append("--no-rosegment"); + } lj->args.append("--gc-sections"); lj->args.append("-m"); diff --git a/src/main.cpp b/src/main.cpp index 3398fd1dea..9c36f9b091 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -74,6 +74,7 @@ static int usage(const char *arg0) { " -L[dir] alias for --library-path\n" " -rdynamic add all symbols to the dynamic symbol table\n" " -rpath [path] add directory to the runtime library search path\n" + " --no-rosegment compromise security to workaround valgrind bug\n" " -mconsole (windows) --subsystem console to the linker\n" " -mwindows (windows) --subsystem windows to the linker\n" " -framework [name] (darwin) link against framework\n" @@ -324,6 +325,7 @@ int main(int argc, char **argv) { ZigList test_exec_args = {0}; int comptime_args_end = 0; int runtime_args_start = argc; + bool no_rosegment_workaround = false; if (argc >= 2 && strcmp(argv[1], "build") == 0) { const char *zig_exe_path = arg0; @@ -507,6 +509,8 @@ int main(int argc, char **argv) { mconsole = true; } else if (strcmp(arg, "-rdynamic") == 0) { rdynamic = true; + } else if (strcmp(arg, "--no-rosegment") == 0) { + no_rosegment_workaround = true; } else if (strcmp(arg, "--each-lib-rpath") == 0) { each_lib_rpath = true; } else if (strcmp(arg, "--enable-timing-info") == 0) { @@ -844,6 +848,7 @@ int main(int argc, char **argv) { codegen_set_windows_subsystem(g, mwindows, mconsole); codegen_set_rdynamic(g, rdynamic); + g->no_rosegment_workaround = no_rosegment_workaround; if (mmacosx_version_min && mios_version_min) { fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n"); return EXIT_FAILURE; -- cgit v1.2.3 From 7cfe328a16ef0d1436d8eb37980d6ebf6908a0d8 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 18 Apr 2018 17:43:35 -0500 Subject: fixed typos. --- std/os/linux/index.zig | 2 +- std/os/time.zig | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index dff91a500d..d37cfdcf91 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -511,7 +511,7 @@ pub fn gettimeofday(tv: &timeval, tz: &timezone) usize { return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); } -pub fn settimeofdat(tv: &const timeval, tz: &const timezone) usize { +pub fn settimeofday(tv: &const timeval, tz: &const timezone) usize { return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); } diff --git a/std/os/time.zig b/std/os/time.zig index e6b614d433..d8a432f1a3 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -52,18 +52,18 @@ pub fn posixSleep(seconds: u63, nanoseconds: u63) void { /// Get the posix timestamp, UTC, in seconds pub fn timestamp() u64 { - return @divFloor(miliTimestamp(), ms_per_s); + return @divFloor(milliTimestamp(), ms_per_s); } /// Get the posix timestamp, UTC, in nanoseconds -pub const miliTimestamp = switch(builtin.os) { - Os.windows => miliTimestampWindows, - Os.linux => miliTimestampPosix, - Os.macosx, Os.ios => miliTimestampDarwin, +pub const milliTimestamp = switch(builtin.os) { + Os.windows => milliTimestampWindows, + Os.linux => milliTimestampPosix, + Os.macosx, Os.ios => milliTimestampDarwin, else => @compileError("Unsupported OS"), }; -fn miliTimestampWindows() u64 { +fn milliTimestampWindows() u64 { //FileTime has a granularity of 100 nanoseconds // and uses the NTFS/Windows epoch var ft: i64 = undefined; @@ -73,7 +73,7 @@ fn miliTimestampWindows() u64 { return u64(@divFloor(ft, hns_per_ms) + epoch_adj); } -fn miliTimestampDarwin() u64 { +fn milliTimestampDarwin() u64 { //Sources suggest MacOS 10.12 has support for // posix clock_gettime. var tv: darwin.timeval = undefined; @@ -84,7 +84,7 @@ fn miliTimestampDarwin() u64 { return u64(sec_ms) + u64(usec_ms); } -fn miliTimestampPosix() u64 { +fn milliTimestampPosix() u64 { //From what I can tell there's no reason clock_gettime // should ever fail for us with CLOCK_REALTIME var ts: posix.timespec = undefined; @@ -238,9 +238,9 @@ test "os.time.timestamp" { const ns_per_ms = (ns_per_s / ms_per_s); const margin = 50; - const time_0 = miliTimestamp(); + const time_0 = milliTimestamp(); sleep(0, ns_per_ms); - const time_1 = miliTimestamp(); + const time_1 = milliTimestamp(); const interval = time_1 - time_0; debug.assert(interval > 0 and interval < margin); } -- cgit v1.2.3 From 5c83d271a31508b33276310c93d4552f78ce459e Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 18 Apr 2018 18:50:28 -0500 Subject: Fixed incorrect sign on epoch.clr --- std/os/epoch.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/os/epoch.zig b/std/os/epoch.zig index 8f64fe8572..e1256c1374 100644 --- a/std/os/epoch.zig +++ b/std/os/epoch.zig @@ -9,7 +9,7 @@ pub const windows = -11644473600; //Jan 01, 1601 AD pub const amiga = 252460800; //Jan 01, 1978 AD pub const pickos = -63244800; //Dec 31, 1967 AD pub const gps = 315964800; //Jan 06, 1980 AD -pub const clr = 62135769600; //Jan 01, 0001 AD +pub const clr = -62135769600; //Jan 01, 0001 AD pub const unix = posix; pub const android = posix; -- cgit v1.2.3 From fdebe38fa3763fe60aab191ed3fb44bfdb9a3b6f Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 18 Apr 2018 19:48:19 -0500 Subject: Added notes regarding CLOCK_MONOTONIC_RAW and made it easy to change our mind in the future. Updated std.os imported tests' block with lazy declaration workaround and added time.zig. Corrected some incorrect comments. --- std/os/index.zig | 27 +++++++++++++++------------ std/os/time.zig | 24 ++++++++++++++++++------ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index 5f76c4732a..37e6115ba5 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1710,18 +1710,21 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const assert(it.next(debug.global_allocator) == null); } -test "std.os" { - _ = @import("child_process.zig"); - _ = @import("darwin_errno.zig"); - _ = @import("darwin.zig"); - _ = @import("get_user_id.zig"); - _ = @import("linux/errno.zig"); - //_ = @import("linux_i386.zig"); - _ = @import("linux/x86_64.zig"); - _ = @import("linux/index.zig"); - _ = @import("path.zig"); - _ = @import("windows/index.zig"); - _ = @import("test.zig"); +comptime { + if(builtin.is_test) { + _ = @import("child_process.zig"); + _ = @import("darwin_errno.zig"); + _ = @import("darwin.zig"); + _ = @import("get_user_id.zig"); + _ = @import("linux/errno.zig"); + //_ = @import("linux_i386.zig"); + _ = @import("linux/x86_64.zig"); + _ = @import("linux/index.zig"); + _ = @import("path.zig"); + _ = @import("time.zig"); + _ = @import("windows/index.zig"); + _ = @import("test.zig"); + } } diff --git a/std/os/time.zig b/std/os/time.zig index d8a432f1a3..a872f32a68 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -4,6 +4,7 @@ const Os = builtin.Os; const debug = std.debug; const windows = std.os.windows; +const linux = std.os.linux; const darwin = std.os.darwin; const posix = std.os.posix; @@ -119,8 +120,8 @@ pub const s_per_week = s_per_day * 7; pub const Timer = struct { //if we used resolution's value when performing the - // performance counter calc on windows, it would be - // less precise + // performance counter calc on windows/darwin, it would + // be less precise frequency: switch(builtin.os) { Os.windows => u64, Os.macosx, Os.ios => darwin.mach_timebase_info_data, @@ -129,9 +130,20 @@ pub const Timer = struct { resolution: u64, start_time: u64, + + //At some point we may change our minds on RAW, but for now we're + // sticking with posix standard MONOTONIC. For more information, see: + // https://github.com/zig-lang/zig/pull/933 + // + //const monotonic_clock_id = switch(builtin.os) { + // Os.linux => linux.CLOCK_MONOTONIC_RAW, + // else => posix.CLOCK_MONOTONIC, + //}; + const monotonic_clock_id = posix.CLOCK_MONOTONIC; + //Initialize the timer structure. //This gives us an oportunity to grab the counter frequency in windows. - //On Windows: QueryPerformanceCounter will succeed on anything > XP. + //On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000. //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not // supported, or if the timespec pointer is out of bounds, which should be // impossible here barring cosmic rays or other such occurances of @@ -154,14 +166,14 @@ pub const Timer = struct { }, Os.linux => { var ts: posix.timespec = undefined; - var result = posix.clock_getres(posix.CLOCK_MONOTONIC, &ts); + var result = posix.clock_getres(monotonic_clock_id, &ts); switch(posix.getErrno(result)) { 0 => {}, posix.EINVAL => return error.TimerUnsupported, else => unreachable, } self.resolution = u64(ts.tv_sec * ns_per_s + ts.tv_nsec); - _ = posix.clock_gettime(posix.CLOCK_MONOTONIC, &ts); + _ = posix.clock_gettime(monotonic_clock_id, &ts); self.start_time = u64(ts.tv_sec * ns_per_s + ts.tv_nsec); }, Os.macosx, Os.ios => { @@ -220,7 +232,7 @@ pub const Timer = struct { fn clockLinux() u64 { var ts: posix.timespec = undefined; - var result = posix.clock_gettime(posix.CLOCK_MONOTONIC, &ts); + var result = posix.clock_gettime(monotonic_clock_id, &ts); debug.assert(posix.getErrno(result) == 0); return u64(ts.tv_sec * ns_per_s + ts.tv_nsec); } -- cgit v1.2.3 From 3c9b6f8cd54b9ffa1fd4c32fd2811a7c20c04b62 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 18 Apr 2018 19:57:47 -0500 Subject: Fixed another incorrect comment --- std/os/time.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/os/time.zig b/std/os/time.zig index a872f32a68..90a8b15c03 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -56,7 +56,7 @@ pub fn timestamp() u64 { return @divFloor(milliTimestamp(), ms_per_s); } -/// Get the posix timestamp, UTC, in nanoseconds +/// Get the posix timestamp, UTC, in milliseconds pub const milliTimestamp = switch(builtin.os) { Os.windows => milliTimestampWindows, Os.linux => milliTimestampPosix, -- cgit v1.2.3 From 06909ceaab8ecb33d1f41049870797a3ae721610 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Apr 2018 22:21:54 -0400 Subject: support break in suspend blocks * you can label suspend blocks * labeled break supports suspend blocks See #803 --- doc/langref.html.in | 2 +- src/all_types.hpp | 13 ++++++++++ src/analyze.cpp | 9 +++++++ src/analyze.hpp | 1 + src/codegen.cpp | 1 + src/ir.cpp | 60 +++++++++++++++++++++++++++++++++++++++-------- src/parser.cpp | 25 ++++++++++++++++++-- test/cases/coroutines.zig | 18 ++++++++++++++ test/compile_errors.zig | 20 ++++++++++++++++ 9 files changed, 136 insertions(+), 13 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index a5d31aada4..034b8c1629 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5918,7 +5918,7 @@ Defer(body) = ("defer" | "deferror") body IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) -SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) +SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body) diff --git a/src/all_types.hpp b/src/all_types.hpp index 88e0ba27a8..42ce01355c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -867,6 +867,7 @@ struct AstNodeAwaitExpr { }; struct AstNodeSuspend { + Buf *name; AstNode *block; AstNode *promise_symbol; }; @@ -1757,6 +1758,7 @@ enum ScopeId { ScopeIdVarDecl, ScopeIdCImport, ScopeIdLoop, + ScopeIdSuspend, ScopeIdFnDef, ScopeIdCompTime, ScopeIdCoroPrelude, @@ -1852,6 +1854,17 @@ struct ScopeLoop { ZigList *incoming_blocks; }; +// This scope is created for a suspend block in order to have labeled +// suspend for breaking out of a suspend and for detecting if a suspend +// block is inside a suspend block. +struct ScopeSuspend { + Scope base; + + Buf *name; + IrBasicBlock *resume_block; + bool reported_err; +}; + // This scope is created for a comptime expression. // NodeTypeCompTime, NodeTypeSwitchExpr struct ScopeCompTime { diff --git a/src/analyze.cpp b/src/analyze.cpp index ca18208ba9..d142b86326 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -156,6 +156,14 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) { return scope; } +ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) { + assert(node->type == NodeTypeSuspend); + ScopeSuspend *scope = allocate(1); + init_scope(&scope->base, ScopeIdSuspend, node, parent); + scope->name = node->data.suspend.name; + return scope; +} + ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) { ScopeFnDef *scope = allocate(1); init_scope(&scope->base, ScopeIdFnDef, node, parent); @@ -3616,6 +3624,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) { case ScopeIdVarDecl: case ScopeIdCImport: case ScopeIdLoop: + case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdCoroPrelude: scope = scope->parent; diff --git a/src/analyze.hpp b/src/analyze.hpp index aa4557666b..aca78f4e25 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -104,6 +104,7 @@ ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent); Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var); ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent); ScopeLoop *create_loop_scope(AstNode *node, Scope *parent); +ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent); ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry); ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import); Scope *create_comptime_scope(AstNode *node, Scope *parent); diff --git a/src/codegen.cpp b/src/codegen.cpp index a7d373e9d0..5b51d9e755 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -654,6 +654,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { } case ScopeIdDeferExpr: case ScopeIdLoop: + case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdCoroPrelude: return get_di_scope(g, scope->parent); diff --git a/src/ir.cpp b/src/ir.cpp index 89193a4c27..dcfe3afb48 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2829,6 +2829,18 @@ static void ir_set_cursor_at_end_and_append_block(IrBuilder *irb, IrBasicBlock * ir_set_cursor_at_end(irb, basic_block); } +static ScopeSuspend *get_scope_suspend(Scope *scope) { + while (scope) { + if (scope->id == ScopeIdSuspend) + return (ScopeSuspend *)scope; + if (scope->id == ScopeIdFnDef) + return nullptr; + + scope = scope->parent; + } + return nullptr; +} + static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) { while (scope) { if (scope->id == ScopeIdDeferExpr) @@ -5665,6 +5677,15 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop return ir_build_br(irb, break_scope, node, dest_block, is_comptime); } +static IrInstruction *ir_gen_break_from_suspend(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeSuspend *suspend_scope) { + IrInstruction *is_comptime = ir_build_const_bool(irb, break_scope, node, false); + + IrBasicBlock *dest_block = suspend_scope->resume_block; + ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false); + + return ir_build_br(irb, break_scope, node, dest_block, is_comptime); +} + static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) { assert(node->type == NodeTypeBreak); @@ -5704,6 +5725,13 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode * assert(this_block_scope->end_block != nullptr); return ir_gen_return_from_block(irb, break_scope, node, this_block_scope); } + } else if (search_scope->id == ScopeIdSuspend) { + ScopeSuspend *this_suspend_scope = (ScopeSuspend *)search_scope; + if (node->data.break_expr.name != nullptr && + (this_suspend_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_suspend_scope->name))) + { + return ir_gen_break_from_suspend(irb, break_scope, node, this_suspend_scope); + } } search_scope = search_scope->parent; } @@ -6290,14 +6318,26 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope); if (scope_defer_expr) { if (!scope_defer_expr->reported_err) { - add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression")); + ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression")); + add_error_note(irb->codegen, msg, scope_defer_expr->base.source_node, buf_sprintf("defer here")); scope_defer_expr->reported_err = true; } return irb->codegen->invalid_instruction; } + ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope); + if (existing_suspend_scope) { + if (!existing_suspend_scope->reported_err) { + ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block")); + add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here")); + existing_suspend_scope->reported_err = true; + } + return irb->codegen->invalid_instruction; + } Scope *outer_scope = irb->exec->begin_scope; + IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); + IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); IrInstruction *suspend_code; IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); @@ -6316,28 +6356,28 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod } else { child_scope = parent_scope; } + ScopeSuspend *suspend_scope = create_suspend_scope(node, child_scope); + suspend_scope->resume_block = resume_block; + child_scope = &suspend_scope->base; IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle); ir_gen_node(irb, node->data.suspend.block, child_scope); - suspend_code = ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false); + suspend_code = ir_mark_gen(ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false)); } - IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); - IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); - IrInstructionSwitchBrCase *cases = allocate(2); - cases[0].value = ir_build_const_u8(irb, parent_scope, node, 0); + cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0)); cases[0].block = resume_block; - cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1); + cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1)); cases[1].block = cleanup_block; - ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, - 2, cases, const_bool_false); + ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, + 2, cases, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, resume_block); - return ir_build_const_void(irb, parent_scope, node); + return ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); } static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, diff --git a/src/parser.cpp b/src/parser.cpp index 2bd94033cc..4b70e904b8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -648,12 +648,30 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m } /* -SuspendExpression(body) = "suspend" "|" Symbol "|" body +SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) */ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { size_t orig_token_index = *token_index; - Token *suspend_token = &pc->tokens->at(*token_index); + Token *name_token = nullptr; + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdSymbol) { + *token_index += 1; + Token *colon_token = &pc->tokens->at(*token_index); + if (colon_token->id == TokenIdColon) { + *token_index += 1; + name_token = token; + token = &pc->tokens->at(*token_index); + } else if (mandatory) { + ast_expect_token(pc, colon_token, TokenIdColon); + zig_unreachable(); + } else { + *token_index = orig_token_index; + return nullptr; + } + } + + Token *suspend_token = token; if (suspend_token->id == TokenIdKeywordSuspend) { *token_index += 1; } else if (mandatory) { @@ -675,6 +693,9 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b } AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); + if (name_token != nullptr) { + node->data.suspend.name = token_buf(name_token); + } node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index); ast_eat_token(pc, token_index, TokenIdBinOr); node->data.suspend.block = ast_parse_block(pc, token_index, true); diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 6d28b98c9d..46055d7469 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -224,3 +224,21 @@ async fn printTrace(p: promise->error!void) void { } }; } + +test "break from suspend" { + var buf: [500]u8 = undefined; + var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; + var my_result: i32 = 1; + const p = try async testBreakFromSuspend(&my_result); + cancel p; + std.debug.assert(my_result == 2); +} + +async fn testBreakFromSuspend(my_result: &i32) void { + s: suspend |p| { + break :s; + } + *my_result += 1; + suspend; + *my_result += 1; +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 926e997c6e..6ac73d18a2 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,26 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("suspend inside suspend block", + \\const std = @import("std"); + \\ + \\export fn entry() void { + \\ var buf: [500]u8 = undefined; + \\ var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; + \\ const p = (async foo()) catch unreachable; + \\ cancel p; + \\} + \\ + \\async fn foo() void { + \\ suspend |p| { + \\ suspend |p1| { + \\ } + \\ } + \\} + , + ".tmp_source.zig:12:9: error: cannot suspend inside suspend block", + ".tmp_source.zig:11:5: note: other suspend block here"); + cases.add("assign inline fn to non-comptime var", \\export fn entry() void { \\ var a = b; -- cgit v1.2.3 From 89eade0548c3afe8531fcbccc753c5c1e22f8e89 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Thu, 19 Apr 2018 10:01:41 -0500 Subject: Style cleanups, u64 casts, Timer.start returns error instead of unreachable on unexpected errno. --- std/os/index.zig | 2 +- std/os/time.zig | 57 +++++++++++++++++++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index 37e6115ba5..8a0326f291 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1711,7 +1711,7 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const } comptime { - if(builtin.is_test) { + if (builtin.is_test) { _ = @import("child_process.zig"); _ = @import("darwin_errno.zig"); _ = @import("darwin.zig"); diff --git a/std/os/time.zig b/std/os/time.zig index 90a8b15c03..4f002de79e 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -12,12 +12,13 @@ pub const epoch = @import("epoch.zig"); /// Sleep for the specified duration pub fn sleep(seconds: usize, nanoseconds: usize) void { - switch(builtin.os) { + switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { posixSleep(u63(seconds), u63(nanoseconds)); }, Os.windows => { - const milliseconds = seconds * ms_per_s + nanoseconds / (ns_per_s / ms_per_s); + const ns_per_ms = ns_per_s / ms_per_s; + const milliseconds = seconds * ms_per_s + nanoseconds / ns_per_ms; windows.Sleep(windows.DWORD(milliseconds)); }, else => @compileError("Unsupported OS"), @@ -57,7 +58,7 @@ pub fn timestamp() u64 { } /// Get the posix timestamp, UTC, in milliseconds -pub const milliTimestamp = switch(builtin.os) { +pub const milliTimestamp = switch (builtin.os) { Os.windows => milliTimestampWindows, Os.linux => milliTimestampPosix, Os.macosx, Os.ios => milliTimestampDarwin, @@ -80,20 +81,21 @@ fn milliTimestampDarwin() u64 { var tv: darwin.timeval = undefined; var err = darwin.gettimeofday(&tv, null); debug.assert(err == 0); - const sec_ms = tv.tv_sec * ms_per_s; - const usec_ms = @divFloor(tv.tv_usec, (us_per_s / ms_per_s)); + const sec_ms = u64(tv.tv_sec) * ms_per_s; + const usec_ms = @divFloor(u64(tv.tv_usec), us_per_s / ms_per_s); return u64(sec_ms) + u64(usec_ms); } fn milliTimestampPosix() u64 { //From what I can tell there's no reason clock_gettime - // should ever fail for us with CLOCK_REALTIME + // should ever fail for us with CLOCK_REALTIME, + // seccomp aside. var ts: posix.timespec = undefined; const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts); debug.assert(err == 0); - const sec_ms = ts.tv_sec * ms_per_s; - const nsec_ms = @divFloor(ts.tv_nsec, ns_per_s / ms_per_s); - return u64(sec_ms) + u64(nsec_ms); + const sec_ms = u64(ts.tv_sec) * ms_per_s; + const nsec_ms = @divFloor(u64(ts.tv_nsec), ns_per_s / ms_per_s); + return sec_ms + nsec_ms; } /// Divisions of a second @@ -122,7 +124,7 @@ pub const Timer = struct { //if we used resolution's value when performing the // performance counter calc on windows/darwin, it would // be less precise - frequency: switch(builtin.os) { + frequency: switch (builtin.os) { Os.windows => u64, Os.macosx, Os.ios => darwin.mach_timebase_info_data, else => void, @@ -141,7 +143,8 @@ pub const Timer = struct { //}; const monotonic_clock_id = posix.CLOCK_MONOTONIC; - //Initialize the timer structure. + + /// Initialize the timer structure. //This gives us an oportunity to grab the counter frequency in windows. //On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000. //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not @@ -149,32 +152,40 @@ pub const Timer = struct { // impossible here barring cosmic rays or other such occurances of // incredibly bad luck. //On Darwin: This cannot fail, as far as I am able to tell. - const TimerError = error{TimerUnsupported}; + const TimerError = error{TimerUnsupported, UnexpectedErrnoValue}; pub fn start() TimerError!Timer { var self: Timer = undefined; - switch(builtin.os) { + switch (builtin.os) { Os.windows => { var freq: i64 = undefined; var err = windows.QueryPerformanceFrequency(&freq); - if(err == 0) return error.TimerUnsupported; + if (err == 0) return error.TimerUnsupported; self.frequency = u64(freq); self.resolution = @divFloor(ns_per_s, self.frequency); + var start_time: i64 = undefined; - _ = windows.QueryPerformanceCounter(&start_time); + err = windows.QueryPerformanceCounter(&start_time); + debug.assert(err != 0); self.start_time = u64(start_time); }, Os.linux => { + //On Linux, seccomp can do arbitrary things to our ability to call + // syscalls, including return any errno value it wants and + // inconsistently throwing errors. Since we can't account for + // abuses of seccomp in a reasonable way, we'll assume that if + // seccomp is going to block us it will at least do so consistently var ts: posix.timespec = undefined; var result = posix.clock_getres(monotonic_clock_id, &ts); - switch(posix.getErrno(result)) { + switch (posix.getErrno(result)) { 0 => {}, posix.EINVAL => return error.TimerUnsupported, - else => unreachable, + else => return error.UnexpectedErrnoValue, } - self.resolution = u64(ts.tv_sec * ns_per_s + ts.tv_nsec); - _ = posix.clock_gettime(monotonic_clock_id, &ts); - self.start_time = u64(ts.tv_sec * ns_per_s + ts.tv_nsec); + self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); + result = posix.clock_gettime(monotonic_clock_id, &ts); + if (posix.getErrno(result) != 0) return error.UnexpectedErrnoValue; + self.start_time = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); }, Os.macosx, Os.ios => { darwin.mach_timebase_info(&self.frequency); @@ -189,7 +200,7 @@ pub const Timer = struct { /// Reads the timer value since start or the last reset in nanoseconds pub fn read(self: &Timer) u64 { var clock = clockNative() - self.start_time; - return switch(builtin.os) { + return switch (builtin.os) { Os.windows => @divFloor(clock * ns_per_s, self.frequency), Os.linux => clock, Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom), @@ -212,7 +223,7 @@ pub const Timer = struct { } - const clockNative = switch(builtin.os) { + const clockNative = switch (builtin.os) { Os.windows => clockWindows, Os.linux => clockLinux, Os.macosx, Os.ios => clockDarwin, @@ -234,7 +245,7 @@ pub const Timer = struct { var ts: posix.timespec = undefined; var result = posix.clock_gettime(monotonic_clock_id, &ts); debug.assert(posix.getErrno(result) == 0); - return u64(ts.tv_sec * ns_per_s + ts.tv_nsec); + return u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); } }; -- cgit v1.2.3 From 6b4f6ebd89c4788fde09dd5cde17f3cb54c6c656 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Thu, 19 Apr 2018 20:11:16 +0200 Subject: Added field builtin function --- src/all_types.hpp | 3 ++- src/codegen.cpp | 2 +- src/ir.cpp | 38 ++++++++++++++++++++++++++++++++++---- src/ir_print.cpp | 3 ++- test/cases/reflection.zig | 24 ++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 42ce01355c..33fd41ba03 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1292,6 +1292,7 @@ enum BuiltinFnId { BuiltinFnIdMemberCount, BuiltinFnIdMemberType, BuiltinFnIdMemberName, + BuiltinFnIdField, BuiltinFnIdTypeof, BuiltinFnIdAddWithOverflow, BuiltinFnIdSubWithOverflow, @@ -2225,7 +2226,7 @@ struct IrInstructionFieldPtr { IrInstruction base; IrInstruction *container_ptr; - Buf *field_name; + IrInstruction *field_name_expr; bool is_const; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 5b51d9e755..b5c8fdecac 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6114,6 +6114,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1); create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2); create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2); + create_builtin_fn(g, BuiltinFnIdField, "field", 2); create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); @@ -7185,4 +7186,3 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir, } return pkg; } - diff --git a/src/ir.cpp b/src/ir.cpp index dcfe3afb48..1fb9f86a61 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1033,18 +1033,26 @@ static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_ return new_instruction; } -static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *container_ptr, Buf *field_name) +static IrInstruction *ir_build_field_ptr_inner(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *container_ptr, IrInstruction *field_name_expr) { IrInstructionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->container_ptr = container_ptr; - instruction->field_name = field_name; + instruction->field_name_expr = field_name_expr; ir_ref_instruction(container_ptr, irb->current_basic_block); + ir_ref_instruction(field_name_expr, irb->current_basic_block); return &instruction->base; } +static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *container_ptr, Buf *field_name) +{ + IrInstruction *field_name_expr = ir_build_const_str_lit(irb, scope, source_node, field_name); + return ir_build_field_ptr_inner(irb, scope, source_node, container_ptr, field_name_expr); +} + static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *struct_ptr, TypeStructField *field) { @@ -4015,6 +4023,24 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_member_name(irb, scope, node, arg0_value, arg1_value); } + case BuiltinFnIdField: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LVAL_PTR); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *ptr_instruction = ir_build_field_ptr_inner(irb, scope, node, arg0_value, arg1_value); + //if (lval.is_ptr) + // return ptr_instruction; + + return ir_build_load_ptr(irb, scope, node, ptr_instruction); + } case BuiltinFnIdBreakpoint: return ir_build_breakpoint(irb, scope, node); case BuiltinFnIdReturnAddress: @@ -13458,7 +13484,11 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru zig_unreachable(); } - Buf *field_name = field_ptr_instruction->field_name; + IrInstruction *field_name_expr = field_ptr_instruction->field_name_expr->other; + Buf *field_name = ir_resolve_str(ira, field_name_expr); + if (!field_name) + return ira->codegen->builtin_types.entry_invalid; + AstNode *source_node = field_ptr_instruction->base.source_node; if (type_is_invalid(container_type)) { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 45b666ae73..bb22c258e2 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -360,7 +360,8 @@ static void ir_print_ptr_type_child(IrPrint *irp, IrInstructionPtrTypeChild *ins static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) { fprintf(irp->f, "fieldptr "); ir_print_other_instruction(irp, instruction->container_ptr); - fprintf(irp->f, ".%s", buf_ptr(instruction->field_name)); + fprintf(irp->f, "."); + ir_print_other_instruction(irp, instruction->field_name_expr); } static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) { diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index 18a766d9fc..df723f9b0b 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -1,5 +1,6 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; +const reflection = this; test "reflection: array, pointer, nullable, error union type child" { comptime { @@ -56,7 +57,30 @@ test "reflection: enum member types and names" { } +test "reflection: @field" { + const f = Foo { + .one = 42, + .two = true, + .three = void{}, + }; + + assert(f.one == f.one); + assert(@field(f, "o" ++ "ne") == f.one); + assert(@field(f, "t" ++ "wo") == f.two); + assert(@field(f, "th" ++ "ree") == f.three); + assert(@field(Foo, "const" ++ "ant") == Foo.constant); + assert(@field(Bar, "O" ++ "ne") == Bar.One); + assert(@field(Bar, "O" ++ "ne") == Bar.One); + assert(@field(Bar, "O" ++ "ne") == Bar.One); + assert(@field(Bar, "T" ++ "wo") == Bar.Two); + assert(@field(Bar, "Th" ++ "ree") == Bar.Three); + assert(@field(Bar, "F" ++ "our") == Bar.Four); + assert(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); +} + const Foo = struct { + const constant = 52; + one: i32, two: bool, three: void, -- cgit v1.2.3 From 1b91478bffa021721cff2c19a6c8389c7dcb3e1d Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Thu, 19 Apr 2018 21:34:18 +0200 Subject: Optimized field ptr ir for hot path and fix assignment bug --- src/all_types.hpp | 1 + src/ir.cpp | 245 ++++++++++++++++++++++++++++++---------------- src/ir_print.cpp | 16 ++- test/cases/reflection.zig | 6 +- 4 files changed, 176 insertions(+), 92 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 33fd41ba03..d1b2ad61d2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2226,6 +2226,7 @@ struct IrInstructionFieldPtr { IrInstruction base; IrInstruction *container_ptr; + Buf *field_name_buffer; IrInstruction *field_name_expr; bool is_const; }; diff --git a/src/ir.cpp b/src/ir.cpp index 1fb9f86a61..a34c990afe 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -111,6 +111,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr); static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); +static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == TypeTableEntryIdPointer); @@ -1033,11 +1034,12 @@ static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_ return new_instruction; } -static IrInstruction *ir_build_field_ptr_inner(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_field_ptr_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_ptr, IrInstruction *field_name_expr) { IrInstructionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->container_ptr = container_ptr; + instruction->field_name_buffer = nullptr; instruction->field_name_expr = field_name_expr; ir_ref_instruction(container_ptr, irb->current_basic_block); @@ -1049,8 +1051,14 @@ static IrInstruction *ir_build_field_ptr_inner(IrBuilder *irb, Scope *scope, Ast static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_ptr, Buf *field_name) { - IrInstruction *field_name_expr = ir_build_const_str_lit(irb, scope, source_node, field_name); - return ir_build_field_ptr_inner(irb, scope, source_node, container_ptr, field_name_expr); + IrInstructionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->container_ptr = container_ptr; + instruction->field_name_buffer = field_name; + instruction->field_name_expr = nullptr; + + ir_ref_instruction(container_ptr, irb->current_basic_block); + + return &instruction->base; } static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, @@ -3532,7 +3540,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode return ir_build_load_ptr(irb, scope, node, ptr_instruction); } -static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeFieldAccessExpr); AstNode *container_ref_node = node->data.field_access_expr.struct_expr; @@ -3542,11 +3550,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode if (container_ref_instruction == irb->codegen->invalid_instruction) return container_ref_instruction; - IrInstruction *ptr_instruction = ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name); - if (lval.is_ptr) - return ptr_instruction; - - return ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name); } static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) { @@ -3577,7 +3581,7 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode * return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr); } -static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeFnCallExpr); AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; @@ -3609,7 +3613,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *arg = ir_gen_node(irb, arg_node, scope); if (arg == irb->codegen->invalid_instruction) return arg; - return ir_build_typeof(irb, scope, node, arg); + + IrInstruction *type_of = ir_build_typeof(irb, scope, node, arg); + return ir_lval_wrap(irb, scope, type_of, lval); } case BuiltinFnIdSetCold: { @@ -3618,7 +3624,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_set_cold(irb, scope, node, arg0_value); + IrInstruction *set_cold = ir_build_set_cold(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_cold, lval); } case BuiltinFnIdSetRuntimeSafety: { @@ -3627,7 +3634,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_set_runtime_safety(irb, scope, node, arg0_value); + IrInstruction *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_safety, lval); } case BuiltinFnIdSetFloatMode: { @@ -3641,7 +3649,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_set_float_mode(irb, scope, node, arg0_value, arg1_value); + IrInstruction *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, set_float_mode, lval); } case BuiltinFnIdSizeof: { @@ -3650,7 +3659,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_size_of(irb, scope, node, arg0_value); + IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, size_of, lval); } case BuiltinFnIdCtz: { @@ -3659,7 +3669,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_ctz(irb, scope, node, arg0_value); + IrInstruction *ctz = ir_build_ctz(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, ctz, lval); } case BuiltinFnIdClz: { @@ -3668,7 +3679,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_clz(irb, scope, node, arg0_value); + IrInstruction *clz = ir_build_clz(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, clz, lval); } case BuiltinFnIdImport: { @@ -3677,11 +3689,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_import(irb, scope, node, arg0_value); + IrInstruction *import = ir_build_import(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, import, lval); } case BuiltinFnIdCImport: { - return ir_build_c_import(irb, scope, node); + IrInstruction *c_import = ir_build_c_import(irb, scope, node); + return ir_lval_wrap(irb, scope, c_import, lval); } case BuiltinFnIdCInclude: { @@ -3695,7 +3709,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return irb->codegen->invalid_instruction; } - return ir_build_c_include(irb, scope, node, arg0_value); + IrInstruction *c_include = ir_build_c_include(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, c_include, lval); } case BuiltinFnIdCDefine: { @@ -3714,7 +3729,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return irb->codegen->invalid_instruction; } - return ir_build_c_define(irb, scope, node, arg0_value, arg1_value); + IrInstruction *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, c_define, lval); } case BuiltinFnIdCUndef: { @@ -3728,7 +3744,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return irb->codegen->invalid_instruction; } - return ir_build_c_undef(irb, scope, node, arg0_value); + IrInstruction *c_undef = ir_build_c_undef(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, c_undef, lval); } case BuiltinFnIdMaxValue: { @@ -3737,7 +3754,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_max_value(irb, scope, node, arg0_value); + IrInstruction *max_value = ir_build_max_value(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, max_value, lval); } case BuiltinFnIdMinValue: { @@ -3746,7 +3764,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_min_value(irb, scope, node, arg0_value); + IrInstruction *min_value = ir_build_min_value(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, min_value, lval); } case BuiltinFnIdCompileErr: { @@ -3755,7 +3774,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_compile_err(irb, scope, node, arg0_value); + IrInstruction *compile_err = ir_build_compile_err(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, compile_err, lval); } case BuiltinFnIdCompileLog: { @@ -3768,7 +3788,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return irb->codegen->invalid_instruction; } - return ir_build_compile_log(irb, scope, node, actual_param_count, args); + IrInstruction *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args); + return ir_lval_wrap(irb, scope, compile_log, lval); } case BuiltinFnIdErrName: { @@ -3777,7 +3798,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_err_name(irb, scope, node, arg0_value); + IrInstruction *err_name = ir_build_err_name(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, err_name, lval); } case BuiltinFnIdEmbedFile: { @@ -3786,7 +3808,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_embed_file(irb, scope, node, arg0_value); + IrInstruction *embed_file = ir_build_embed_file(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, embed_file, lval); } case BuiltinFnIdCmpxchgWeak: case BuiltinFnIdCmpxchgStrong: @@ -3821,9 +3844,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg5_value == irb->codegen->invalid_instruction) return arg5_value; - return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, + IrInstruction *cmpxchg = ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), nullptr, AtomicOrderUnordered, AtomicOrderUnordered); + return ir_lval_wrap(irb, scope, cmpxchg, lval); } case BuiltinFnIdFence: { @@ -3832,7 +3856,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered); + IrInstruction *fence = ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered); + return ir_lval_wrap(irb, scope, fence, lval); } case BuiltinFnIdDivExact: { @@ -3846,7 +3871,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdDivTrunc: { @@ -3860,7 +3886,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdDivFloor: { @@ -3874,7 +3901,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdRem: { @@ -3888,7 +3916,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdMod: { @@ -3902,7 +3931,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdSqrt: { @@ -3916,7 +3946,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_sqrt(irb, scope, node, arg0_value, arg1_value); + IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, ir_sqrt, lval); } case BuiltinFnIdTruncate: { @@ -3930,7 +3961,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_truncate(irb, scope, node, arg0_value, arg1_value); + IrInstruction *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, truncate, lval); } case BuiltinFnIdIntType: { @@ -3944,7 +3976,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_int_type(irb, scope, node, arg0_value, arg1_value); + IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, int_type, lval); } case BuiltinFnIdMemcpy: { @@ -3963,7 +3996,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; - return ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value); + IrInstruction *ir_memcpy = ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value); + return ir_lval_wrap(irb, scope, ir_memcpy, lval); } case BuiltinFnIdMemset: { @@ -3982,7 +4016,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; - return ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value); + IrInstruction *ir_memset = ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value); + return ir_lval_wrap(irb, scope, ir_memset, lval); } case BuiltinFnIdMemberCount: { @@ -3991,7 +4026,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_member_count(irb, scope, node, arg0_value); + IrInstruction *member_count = ir_build_member_count(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, member_count, lval); } case BuiltinFnIdMemberType: { @@ -4006,7 +4042,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; - return ir_build_member_type(irb, scope, node, arg0_value, arg1_value); + IrInstruction *member_type = ir_build_member_type(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, member_type, lval); } case BuiltinFnIdMemberName: { @@ -4021,7 +4058,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; - return ir_build_member_name(irb, scope, node, arg0_value, arg1_value); + IrInstruction *member_name = ir_build_member_name(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, member_name, lval); } case BuiltinFnIdField: { @@ -4035,18 +4073,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *ptr_instruction = ir_build_field_ptr_inner(irb, scope, node, arg0_value, arg1_value); - //if (lval.is_ptr) - // return ptr_instruction; + IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value); + + if (lval.is_ptr) + return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); } case BuiltinFnIdBreakpoint: - return ir_build_breakpoint(irb, scope, node); + return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval); case BuiltinFnIdReturnAddress: - return ir_build_return_address(irb, scope, node); + return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval); case BuiltinFnIdFrameAddress: - return ir_build_frame_address(irb, scope, node); + return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -4054,16 +4093,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_align_of(irb, scope, node, arg0_value); + IrInstruction *align_of = ir_build_align_of(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, align_of, lval); } case BuiltinFnIdAddWithOverflow: - return ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd); + return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval); case BuiltinFnIdSubWithOverflow: - return ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub); + return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval); case BuiltinFnIdMulWithOverflow: - return ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul); + return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval); case BuiltinFnIdShlWithOverflow: - return ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl); + return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval); case BuiltinFnIdTypeName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -4071,7 +4111,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_type_name(irb, scope, node, arg0_value); + IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, type_name, lval); } case BuiltinFnIdCanImplicitCast: { @@ -4085,7 +4126,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *can_implicit_cast = ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, can_implicit_cast, lval); } case BuiltinFnIdPanic: { @@ -4094,7 +4136,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_panic(irb, scope, node, arg0_value); + IrInstruction *panic = ir_build_panic(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, panic, lval); } case BuiltinFnIdPtrCast: { @@ -4108,7 +4151,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *ptr_cast = ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, ptr_cast, lval); } case BuiltinFnIdBitCast: { @@ -4122,7 +4166,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *bit_cast = ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, bit_cast, lval); } case BuiltinFnIdIntToPtr: { @@ -4136,7 +4181,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value); + IrInstruction *int_to_ptr = ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, int_to_ptr, lval); } case BuiltinFnIdPtrToInt: { @@ -4145,7 +4191,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_ptr_to_int(irb, scope, node, arg0_value); + IrInstruction *ptr_to_int = ir_build_ptr_to_int(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, ptr_to_int, lval); } case BuiltinFnIdTagName: { @@ -4155,7 +4202,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value); - return ir_build_tag_name(irb, scope, node, actual_tag); + IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, actual_tag); + return ir_lval_wrap(irb, scope, tag_name, lval); } case BuiltinFnIdTagType: { @@ -4164,7 +4212,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_tag_type(irb, scope, node, arg0_value); + IrInstruction *tag_type = ir_build_tag_type(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, tag_type, lval); } case BuiltinFnIdFieldParentPtr: { @@ -4183,7 +4232,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; - return ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr); + IrInstruction *field_parent_ptr = ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr); + return ir_lval_wrap(irb, scope, field_parent_ptr, lval); } case BuiltinFnIdOffsetOf: { @@ -4197,7 +4247,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_offset_of(irb, scope, node, arg0_value, arg1_value); + IrInstruction *offset_of = ir_build_offset_of(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, offset_of, lval); } case BuiltinFnIdInlineCall: case BuiltinFnIdNoInlineCall: @@ -4223,7 +4274,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; - return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr); + IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr); + return ir_lval_wrap(irb, scope, call, lval); } case BuiltinFnIdTypeId: { @@ -4232,7 +4284,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_type_id(irb, scope, node, arg0_value); + IrInstruction *type_id = ir_build_type_id(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, type_id, lval); } case BuiltinFnIdShlExact: { @@ -4246,7 +4299,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdShrExact: { @@ -4260,7 +4314,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdSetEvalBranchQuota: { @@ -4269,7 +4324,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_set_eval_branch_quota(irb, scope, node, arg0_value); + IrInstruction *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval); } case BuiltinFnIdAlignCast: { @@ -4283,10 +4339,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_align_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *align_cast = ir_build_align_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, align_cast, lval); } case BuiltinFnIdOpaqueType: - return ir_build_opaque_type(irb, scope, node); + { + IrInstruction *opaque_type = ir_build_opaque_type(irb, scope, node); + return ir_lval_wrap(irb, scope, opaque_type, lval); + } case BuiltinFnIdSetAlignStack: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -4294,7 +4354,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_set_align_stack(irb, scope, node, arg0_value); + IrInstruction *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_align_stack, lval); } case BuiltinFnIdArgType: { @@ -4308,7 +4369,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_arg_type(irb, scope, node, arg0_value, arg1_value); + IrInstruction *arg_type = ir_build_arg_type(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, arg_type, lval); } case BuiltinFnIdExport: { @@ -4327,11 +4389,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; - return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value); + IrInstruction *ir_export = ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value); + return ir_lval_wrap(irb, scope, ir_export, lval); } case BuiltinFnIdErrorReturnTrace: { - return ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null); + IrInstruction *error_return_trace = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null); + return ir_lval_wrap(irb, scope, error_return_trace, lval); } case BuiltinFnIdAtomicRmw: { @@ -4390,11 +4454,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo zig_unreachable(); } -static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeFnCallExpr); if (node->data.fn_call_expr.is_builtin) - return ir_gen_builtin_fn_call(irb, scope, node); + return ir_gen_builtin_fn_call(irb, scope, node, lval); AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); @@ -4420,7 +4484,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } } - return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator); + IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator); + return ir_lval_wrap(irb, scope, fn_call, lval); } static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -6436,7 +6501,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeSymbol: return ir_gen_symbol(irb, scope, node, lval); case NodeTypeFnCallExpr: - return ir_lval_wrap(irb, scope, ir_gen_fn_call(irb, scope, node), lval); + return ir_gen_fn_call(irb, scope, node, lval); case NodeTypeIfBoolExpr: return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval); case NodeTypePrefixOpExpr: @@ -6456,7 +6521,13 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeReturnExpr: return ir_gen_return(irb, scope, node, lval); case NodeTypeFieldAccessExpr: - return ir_gen_field_access(irb, scope, node, lval); + { + IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node); + if (lval.is_ptr) + return ptr_instruction; + + return ir_build_load_ptr(irb, scope, node, ptr_instruction); + } case NodeTypeThisLiteral: return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval); case NodeTypeBoolLiteral: @@ -13484,10 +13555,14 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru zig_unreachable(); } - IrInstruction *field_name_expr = field_ptr_instruction->field_name_expr->other; - Buf *field_name = ir_resolve_str(ira, field_name_expr); - if (!field_name) - return ira->codegen->builtin_types.entry_invalid; + Buf *field_name = field_ptr_instruction->field_name_buffer; + if (!field_name) { + IrInstruction *field_name_expr = field_ptr_instruction->field_name_expr->other; + field_name = ir_resolve_str(ira, field_name_expr); + if (!field_name) + return ira->codegen->builtin_types.entry_invalid; + } + AstNode *source_node = field_ptr_instruction->base.source_node; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index bb22c258e2..a77ae244d4 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -358,10 +358,18 @@ static void ir_print_ptr_type_child(IrPrint *irp, IrInstructionPtrTypeChild *ins } static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) { - fprintf(irp->f, "fieldptr "); - ir_print_other_instruction(irp, instruction->container_ptr); - fprintf(irp->f, "."); - ir_print_other_instruction(irp, instruction->field_name_expr); + if (instruction->field_name_buffer) { + fprintf(irp->f, "fieldptr "); + ir_print_other_instruction(irp, instruction->container_ptr); + fprintf(irp->f, ".%s", buf_ptr(instruction->field_name_buffer)); + } else { + assert(instruction->field_name_expr); + fprintf(irp->f, "@field("); + ir_print_other_instruction(irp, instruction->container_ptr); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->field_name_expr); + fprintf(irp->f, ")"); + } } static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) { diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index df723f9b0b..0abc46c9de 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -58,7 +58,7 @@ test "reflection: enum member types and names" { } test "reflection: @field" { - const f = Foo { + var f = Foo { .one = 42, .two = true, .three = void{}, @@ -70,12 +70,12 @@ test "reflection: @field" { assert(@field(f, "th" ++ "ree") == f.three); assert(@field(Foo, "const" ++ "ant") == Foo.constant); assert(@field(Bar, "O" ++ "ne") == Bar.One); - assert(@field(Bar, "O" ++ "ne") == Bar.One); - assert(@field(Bar, "O" ++ "ne") == Bar.One); assert(@field(Bar, "T" ++ "wo") == Bar.Two); assert(@field(Bar, "Th" ++ "ree") == Bar.Three); assert(@field(Bar, "F" ++ "our") == Bar.Four); assert(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); + @field(f, "o" ++ "ne") = 4; + assert(f.one == 4); } const Foo = struct { -- cgit v1.2.3 From 72bf9d90cc5fa4384ca7db1e5bbd4b9445e63bd3 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Thu, 19 Apr 2018 21:48:09 +0200 Subject: Added builtin field to docs --- doc/langref.html.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 034b8c1629..16fafdaad9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4412,6 +4412,10 @@ fn add(a: i32, b: i32) i32 { return a + b; } It does not include functions, variables, or constants.

{#header_close#} + {#header_open|@field#} +
@field(lhs: var, comptime field_name: []const u8) -> (field)
+

Preforms field access equivalent to lhs.->field_name-<.

+ {#header_close#} {#header_open|@memberType#}
@memberType(comptime T: type, comptime index: usize) -> type

Returns the field type of a struct or union.

@@ -6064,7 +6068,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; -- cgit v1.2.3 From ca4053ba49d8bd171e592783c6024795c4794fda Mon Sep 17 00:00:00 2001 From: tgschultz Date: Thu, 19 Apr 2018 14:53:58 -0500 Subject: Use std.os.errorUnexpectedPosix if timer initialization encounters unexpected error --- std/os/time.zig | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/std/os/time.zig b/std/os/time.zig index 4f002de79e..8a906d7954 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -152,7 +152,7 @@ pub const Timer = struct { // impossible here barring cosmic rays or other such occurances of // incredibly bad luck. //On Darwin: This cannot fail, as far as I am able to tell. - const TimerError = error{TimerUnsupported, UnexpectedErrnoValue}; + const TimerError = error{TimerUnsupported, Unexpected}; pub fn start() TimerError!Timer { var self: Timer = undefined; @@ -177,14 +177,17 @@ pub const Timer = struct { // seccomp is going to block us it will at least do so consistently var ts: posix.timespec = undefined; var result = posix.clock_getres(monotonic_clock_id, &ts); - switch (posix.getErrno(result)) { + var errno = posix.getErrno(result); + switch (errno) { 0 => {}, posix.EINVAL => return error.TimerUnsupported, - else => return error.UnexpectedErrnoValue, + else => return std.os.unexpectedErrorPosix(errno), } self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); + result = posix.clock_gettime(monotonic_clock_id, &ts); - if (posix.getErrno(result) != 0) return error.UnexpectedErrnoValue; + errno = posix.getErrno(result); + if (errno != 0) return std.os.unexpectedErrorPosix(errno); self.start_time = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); }, Os.macosx, Os.ios => { -- cgit v1.2.3 From 6e57243a79b08f37b80122f0eb24d073d6e2e95c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Apr 2018 02:15:09 -0400 Subject: zig fmt: preserve comments in front of test blocks * refactor std.zig.parser * fix compiler crashing for some compile errors * take advantage of @field in std.zig.ast * move ast.NodeFoo to ast.Node.Foo * comment preservation is more explicit See #911 --- src/ir.cpp | 4 + std/zig/ast.zig | 2693 +++++++++++++++++++++++++--------------------------- std/zig/parser.zig | 975 ++++++++++--------- 3 files changed, 1820 insertions(+), 1852 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index a34c990afe..865a6823be 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6523,6 +6523,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeFieldAccessExpr: { IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node); + if (ptr_instruction == irb->codegen->invalid_instruction) + return ptr_instruction; if (lval.is_ptr) return ptr_instruction; @@ -15556,6 +15558,8 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, } ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; TypeStructField *field = find_struct_type_field(container_type, field_name); if (field == nullptr) { diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 17a19e6213..76977a979a 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -6,7 +6,6 @@ const mem = std.mem; pub const Node = struct { id: Id, - comment: ?&NodeLineComment, pub const Id = enum { // Top level @@ -74,1750 +73,1686 @@ pub const Node = struct { FieldInitializer, }; - const IdTypePair = struct { - id: Id, - Type: type, - }; - - // TODO: When @field exists, we could generate this by iterating over all members of `Id`, - // and making an array of `IdTypePair { .id = @field(Id, @memberName(Id, i)), .Type = @field(ast, "Node" ++ @memberName(Id, i)) }` - const idTypeTable = []IdTypePair { - IdTypePair { .id = Id.Root, .Type = NodeRoot }, - IdTypePair { .id = Id.Use, .Type = NodeUse }, - IdTypePair { .id = Id.TestDecl, .Type = NodeTestDecl }, - - IdTypePair { .id = Id.VarDecl, .Type = NodeVarDecl }, - IdTypePair { .id = Id.Defer, .Type = NodeDefer }, - - IdTypePair { .id = Id.InfixOp, .Type = NodeInfixOp }, - IdTypePair { .id = Id.PrefixOp, .Type = NodePrefixOp }, - IdTypePair { .id = Id.SuffixOp, .Type = NodeSuffixOp }, - - IdTypePair { .id = Id.Switch, .Type = NodeSwitch }, - IdTypePair { .id = Id.While, .Type = NodeWhile }, - IdTypePair { .id = Id.For, .Type = NodeFor }, - IdTypePair { .id = Id.If, .Type = NodeIf }, - IdTypePair { .id = Id.ControlFlowExpression, .Type = NodeControlFlowExpression }, - IdTypePair { .id = Id.Suspend, .Type = NodeSuspend }, - - IdTypePair { .id = Id.VarType, .Type = NodeVarType }, - IdTypePair { .id = Id.ErrorType, .Type = NodeErrorType }, - IdTypePair { .id = Id.FnProto, .Type = NodeFnProto }, - - IdTypePair { .id = Id.IntegerLiteral, .Type = NodeIntegerLiteral }, - IdTypePair { .id = Id.FloatLiteral, .Type = NodeFloatLiteral }, - IdTypePair { .id = Id.StringLiteral, .Type = NodeStringLiteral }, - IdTypePair { .id = Id.MultilineStringLiteral, .Type = NodeMultilineStringLiteral }, - IdTypePair { .id = Id.CharLiteral, .Type = NodeCharLiteral }, - IdTypePair { .id = Id.BoolLiteral, .Type = NodeBoolLiteral }, - IdTypePair { .id = Id.NullLiteral, .Type = NodeNullLiteral }, - IdTypePair { .id = Id.UndefinedLiteral, .Type = NodeUndefinedLiteral }, - IdTypePair { .id = Id.ThisLiteral, .Type = NodeThisLiteral }, - IdTypePair { .id = Id.Unreachable, .Type = NodeUnreachable }, - IdTypePair { .id = Id.Identifier, .Type = NodeIdentifier }, - IdTypePair { .id = Id.GroupedExpression, .Type = NodeGroupedExpression }, - IdTypePair { .id = Id.BuiltinCall, .Type = NodeBuiltinCall }, - IdTypePair { .id = Id.ErrorSetDecl, .Type = NodeErrorSetDecl }, - IdTypePair { .id = Id.ContainerDecl, .Type = NodeContainerDecl }, - IdTypePair { .id = Id.Asm, .Type = NodeAsm }, - IdTypePair { .id = Id.Comptime, .Type = NodeComptime }, - IdTypePair { .id = Id.Block, .Type = NodeBlock }, - - IdTypePair { .id = Id.LineComment, .Type = NodeLineComment }, - IdTypePair { .id = Id.SwitchCase, .Type = NodeSwitchCase }, - IdTypePair { .id = Id.SwitchElse, .Type = NodeSwitchElse }, - IdTypePair { .id = Id.Else, .Type = NodeElse }, - IdTypePair { .id = Id.Payload, .Type = NodePayload }, - IdTypePair { .id = Id.PointerPayload, .Type = NodePointerPayload }, - IdTypePair { .id = Id.PointerIndexPayload, .Type = NodePointerIndexPayload }, - IdTypePair { .id = Id.StructField, .Type = NodeStructField }, - IdTypePair { .id = Id.UnionTag, .Type = NodeUnionTag }, - IdTypePair { .id = Id.EnumTag, .Type = NodeEnumTag }, - IdTypePair { .id = Id.AsmInput, .Type = NodeAsmInput }, - IdTypePair { .id = Id.AsmOutput, .Type = NodeAsmOutput }, - IdTypePair { .id = Id.AsyncAttribute, .Type = NodeAsyncAttribute }, - IdTypePair { .id = Id.ParamDecl, .Type = NodeParamDecl }, - IdTypePair { .id = Id.FieldInitializer, .Type = NodeFieldInitializer }, - }; - - pub fn IdToType(comptime id: Id) type { - inline for (idTypeTable) |id_type_pair| { - if (id == id_type_pair.id) - return id_type_pair.Type; - } - - unreachable; - } - - pub fn typeToId(comptime T: type) Id { - inline for (idTypeTable) |id_type_pair| { - if (T == id_type_pair.Type) - return id_type_pair.id; - } - - unreachable; - } - pub fn iterate(base: &Node, index: usize) ?&Node { - inline for (idTypeTable) |id_type_pair| { - if (base.id == id_type_pair.id) - return @fieldParentPtr(id_type_pair.Type, "base", base).iterate(index); + comptime var i = 0; + inline while (i < @memberCount(Id)) : (i += 1) { + if (base.id == @field(Id, @memberName(Id, i))) { + const T = @field(Node, @memberName(Id, i)); + return @fieldParentPtr(T, "base", base).iterate(index); + } } - unreachable; } pub fn firstToken(base: &Node) Token { - inline for (idTypeTable) |id_type_pair| { - if (base.id == id_type_pair.id) - return @fieldParentPtr(id_type_pair.Type, "base", base).firstToken(); + comptime var i = 0; + inline while (i < @memberCount(Id)) : (i += 1) { + if (base.id == @field(Id, @memberName(Id, i))) { + const T = @field(Node, @memberName(Id, i)); + return @fieldParentPtr(T, "base", base).firstToken(); + } } - unreachable; } pub fn lastToken(base: &Node) Token { - inline for (idTypeTable) |id_type_pair| { - if (base.id == id_type_pair.id) - return @fieldParentPtr(id_type_pair.Type, "base", base).lastToken(); + comptime var i = 0; + inline while (i < @memberCount(Id)) : (i += 1) { + if (base.id == @field(Id, @memberName(Id, i))) { + const T = @field(Node, @memberName(Id, i)); + return @fieldParentPtr(T, "base", base).lastToken(); + } } - unreachable; } -}; -pub const NodeRoot = struct { - base: Node, - decls: ArrayList(&Node), - eof_token: Token, - - pub fn iterate(self: &NodeRoot, index: usize) ?&Node { - if (index < self.decls.len) { - return self.decls.items[self.decls.len - index - 1]; + pub fn typeToId(comptime T: type) Id { + comptime var i = 0; + inline while (i < @memberCount(Id)) : (i += 1) { + if (T == @field(Node, @memberName(Id, i))) { + return @field(Id, @memberName(Id, i)); + } } - return null; - } - - pub fn firstToken(self: &NodeRoot) Token { - return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken(); + unreachable; } - pub fn lastToken(self: &NodeRoot) Token { - return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken(); - } -}; + pub const Root = struct { + base: Node, + decls: ArrayList(&Node), + eof_token: Token, -pub const NodeVarDecl = struct { - base: Node, - visib_token: ?Token, - name_token: Token, - eq_token: Token, - mut_token: Token, - comptime_token: ?Token, - extern_export_token: ?Token, - lib_name: ?&Node, - type_node: ?&Node, - align_node: ?&Node, - init_node: ?&Node, - semicolon_token: Token, - - pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node { - var i = index; - - if (self.type_node) |type_node| { - if (i < 1) return type_node; - i -= 1; + pub fn iterate(self: &Root, index: usize) ?&Node { + if (index < self.decls.len) { + return self.decls.items[self.decls.len - index - 1]; + } + return null; } - if (self.align_node) |align_node| { - if (i < 1) return align_node; - i -= 1; + pub fn firstToken(self: &Root) Token { + return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken(); } - if (self.init_node) |init_node| { - if (i < 1) return init_node; - i -= 1; + pub fn lastToken(self: &Root) Token { + return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken(); } + }; - return null; - } - - pub fn firstToken(self: &NodeVarDecl) Token { - if (self.visib_token) |visib_token| return visib_token; - if (self.comptime_token) |comptime_token| return comptime_token; - if (self.extern_export_token) |extern_export_token| return extern_export_token; - assert(self.lib_name == null); - return self.mut_token; - } - - pub fn lastToken(self: &NodeVarDecl) Token { - return self.semicolon_token; - } -}; - -pub const NodeUse = struct { - base: Node, - visib_token: ?Token, - expr: &Node, - semicolon_token: Token, - - pub fn iterate(self: &NodeUse, index: usize) ?&Node { - var i = index; + pub const VarDecl = struct { + base: Node, + comments: ?&LineComment, + visib_token: ?Token, + name_token: Token, + eq_token: Token, + mut_token: Token, + comptime_token: ?Token, + extern_export_token: ?Token, + lib_name: ?&Node, + type_node: ?&Node, + align_node: ?&Node, + init_node: ?&Node, + semicolon_token: Token, + + pub fn iterate(self: &VarDecl, index: usize) ?&Node { + var i = index; + + if (self.type_node) |type_node| { + if (i < 1) return type_node; + i -= 1; + } - if (i < 1) return self.expr; - i -= 1; + if (self.align_node) |align_node| { + if (i < 1) return align_node; + i -= 1; + } - return null; - } + if (self.init_node) |init_node| { + if (i < 1) return init_node; + i -= 1; + } - pub fn firstToken(self: &NodeUse) Token { - if (self.visib_token) |visib_token| return visib_token; - return self.expr.firstToken(); - } + return null; + } - pub fn lastToken(self: &NodeUse) Token { - return self.semicolon_token; - } -}; + pub fn firstToken(self: &VarDecl) Token { + if (self.visib_token) |visib_token| return visib_token; + if (self.comptime_token) |comptime_token| return comptime_token; + if (self.extern_export_token) |extern_export_token| return extern_export_token; + assert(self.lib_name == null); + return self.mut_token; + } -pub const NodeErrorSetDecl = struct { - base: Node, - error_token: Token, - decls: ArrayList(&Node), - rbrace_token: Token, + pub fn lastToken(self: &VarDecl) Token { + return self.semicolon_token; + } + }; - pub fn iterate(self: &NodeErrorSetDecl, index: usize) ?&Node { - var i = index; + pub const Use = struct { + base: Node, + visib_token: ?Token, + expr: &Node, + semicolon_token: Token, - if (i < self.decls.len) return self.decls.at(i); - i -= self.decls.len; + pub fn iterate(self: &Use, index: usize) ?&Node { + var i = index; - return null; - } + if (i < 1) return self.expr; + i -= 1; - pub fn firstToken(self: &NodeErrorSetDecl) Token { - return self.error_token; - } + return null; + } - pub fn lastToken(self: &NodeErrorSetDecl) Token { - return self.rbrace_token; - } -}; + pub fn firstToken(self: &Use) Token { + if (self.visib_token) |visib_token| return visib_token; + return self.expr.firstToken(); + } -pub const NodeContainerDecl = struct { - base: Node, - ltoken: Token, - layout: Layout, - kind: Kind, - init_arg_expr: InitArg, - fields_and_decls: ArrayList(&Node), - rbrace_token: Token, - - const Layout = enum { - Auto, - Extern, - Packed, + pub fn lastToken(self: &Use) Token { + return self.semicolon_token; + } }; - const Kind = enum { - Struct, - Enum, - Union, - }; + pub const ErrorSetDecl = struct { + base: Node, + error_token: Token, + decls: ArrayList(&Node), + rbrace_token: Token, - const InitArg = union(enum) { - None, - Enum, - Type: &Node, - }; + pub fn iterate(self: &ErrorSetDecl, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeContainerDecl, index: usize) ?&Node { - var i = index; + if (i < self.decls.len) return self.decls.at(i); + i -= self.decls.len; - switch (self.init_arg_expr) { - InitArg.Type => |t| { - if (i < 1) return t; - i -= 1; - }, - InitArg.None, - InitArg.Enum => { } + return null; } - if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i); - i -= self.fields_and_decls.len; + pub fn firstToken(self: &ErrorSetDecl) Token { + return self.error_token; + } - return null; - } + pub fn lastToken(self: &ErrorSetDecl) Token { + return self.rbrace_token; + } + }; - pub fn firstToken(self: &NodeContainerDecl) Token { - return self.ltoken; - } + pub const ContainerDecl = struct { + base: Node, + ltoken: Token, + layout: Layout, + kind: Kind, + init_arg_expr: InitArg, + fields_and_decls: ArrayList(&Node), + rbrace_token: Token, + + const Layout = enum { + Auto, + Extern, + Packed, + }; - pub fn lastToken(self: &NodeContainerDecl) Token { - return self.rbrace_token; - } -}; + const Kind = enum { + Struct, + Enum, + Union, + }; + + const InitArg = union(enum) { + None, + Enum, + Type: &Node, + }; -pub const NodeStructField = struct { - base: Node, - visib_token: ?Token, - name_token: Token, - type_expr: &Node, + pub fn iterate(self: &ContainerDecl, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeStructField, index: usize) ?&Node { - var i = index; + switch (self.init_arg_expr) { + InitArg.Type => |t| { + if (i < 1) return t; + i -= 1; + }, + InitArg.None, + InitArg.Enum => { } + } - if (i < 1) return self.type_expr; - i -= 1; + if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i); + i -= self.fields_and_decls.len; - return null; - } + return null; + } - pub fn firstToken(self: &NodeStructField) Token { - if (self.visib_token) |visib_token| return visib_token; - return self.name_token; - } + pub fn firstToken(self: &ContainerDecl) Token { + return self.ltoken; + } - pub fn lastToken(self: &NodeStructField) Token { - return self.type_expr.lastToken(); - } -}; + pub fn lastToken(self: &ContainerDecl) Token { + return self.rbrace_token; + } + }; -pub const NodeUnionTag = struct { - base: Node, - name_token: Token, - type_expr: ?&Node, + pub const StructField = struct { + base: Node, + visib_token: ?Token, + name_token: Token, + type_expr: &Node, - pub fn iterate(self: &NodeUnionTag, index: usize) ?&Node { - var i = index; + pub fn iterate(self: &StructField, index: usize) ?&Node { + var i = index; - if (self.type_expr) |type_expr| { - if (i < 1) return type_expr; + if (i < 1) return self.type_expr; i -= 1; - } - return null; - } + return null; + } - pub fn firstToken(self: &NodeUnionTag) Token { - return self.name_token; - } + pub fn firstToken(self: &StructField) Token { + if (self.visib_token) |visib_token| return visib_token; + return self.name_token; + } - pub fn lastToken(self: &NodeUnionTag) Token { - if (self.type_expr) |type_expr| { - return type_expr.lastToken(); + pub fn lastToken(self: &StructField) Token { + return self.type_expr.lastToken(); } + }; - return self.name_token; - } -}; + pub const UnionTag = struct { + base: Node, + name_token: Token, + type_expr: ?&Node, -pub const NodeEnumTag = struct { - base: Node, - name_token: Token, - value: ?&Node, + pub fn iterate(self: &UnionTag, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeEnumTag, index: usize) ?&Node { - var i = index; + if (self.type_expr) |type_expr| { + if (i < 1) return type_expr; + i -= 1; + } - if (self.value) |value| { - if (i < 1) return value; - i -= 1; + return null; } - return null; - } + pub fn firstToken(self: &UnionTag) Token { + return self.name_token; + } - pub fn firstToken(self: &NodeEnumTag) Token { - return self.name_token; - } + pub fn lastToken(self: &UnionTag) Token { + if (self.type_expr) |type_expr| { + return type_expr.lastToken(); + } - pub fn lastToken(self: &NodeEnumTag) Token { - if (self.value) |value| { - return value.lastToken(); + return self.name_token; } + }; - return self.name_token; - } -}; - -pub const NodeIdentifier = struct { - base: Node, - token: Token, + pub const EnumTag = struct { + base: Node, + name_token: Token, + value: ?&Node, - pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node { - return null; - } + pub fn iterate(self: &EnumTag, index: usize) ?&Node { + var i = index; - pub fn firstToken(self: &NodeIdentifier) Token { - return self.token; - } + if (self.value) |value| { + if (i < 1) return value; + i -= 1; + } - pub fn lastToken(self: &NodeIdentifier) Token { - return self.token; - } -}; + return null; + } -pub const NodeAsyncAttribute = struct { - base: Node, - async_token: Token, - allocator_type: ?&Node, - rangle_bracket: ?Token, + pub fn firstToken(self: &EnumTag) Token { + return self.name_token; + } - pub fn iterate(self: &NodeAsyncAttribute, index: usize) ?&Node { - var i = index; + pub fn lastToken(self: &EnumTag) Token { + if (self.value) |value| { + return value.lastToken(); + } - if (self.allocator_type) |allocator_type| { - if (i < 1) return allocator_type; - i -= 1; + return self.name_token; } + }; - return null; - } - - pub fn firstToken(self: &NodeAsyncAttribute) Token { - return self.async_token; - } + pub const Identifier = struct { + base: Node, + token: Token, - pub fn lastToken(self: &NodeAsyncAttribute) Token { - if (self.rangle_bracket) |rangle_bracket| { - return rangle_bracket; + pub fn iterate(self: &Identifier, index: usize) ?&Node { + return null; } - return self.async_token; - } -}; + pub fn firstToken(self: &Identifier) Token { + return self.token; + } -pub const NodeFnProto = struct { - base: Node, - visib_token: ?Token, - fn_token: Token, - name_token: ?Token, - params: ArrayList(&Node), - return_type: ReturnType, - var_args_token: ?Token, - extern_export_inline_token: ?Token, - cc_token: ?Token, - async_attr: ?&NodeAsyncAttribute, - body_node: ?&Node, - lib_name: ?&Node, // populated if this is an extern declaration - align_expr: ?&Node, // populated if align(A) is present - - pub const ReturnType = union(enum) { - Explicit: &Node, - InferErrorSet: &Node, + pub fn lastToken(self: &Identifier) Token { + return self.token; + } }; - pub fn iterate(self: &NodeFnProto, index: usize) ?&Node { - var i = index; + pub const AsyncAttribute = struct { + base: Node, + async_token: Token, + allocator_type: ?&Node, + rangle_bracket: ?Token, - if (self.body_node) |body_node| { - if (i < 1) return body_node; - i -= 1; - } + pub fn iterate(self: &AsyncAttribute, index: usize) ?&Node { + var i = index; - switch (self.return_type) { - // TODO allow this and next prong to share bodies since the types are the same - ReturnType.Explicit => |node| { - if (i < 1) return node; + if (self.allocator_type) |allocator_type| { + if (i < 1) return allocator_type; i -= 1; - }, - ReturnType.InferErrorSet => |node| { - if (i < 1) return node; - i -= 1; - }, + } + + return null; } - if (self.align_expr) |align_expr| { - if (i < 1) return align_expr; - i -= 1; + pub fn firstToken(self: &AsyncAttribute) Token { + return self.async_token; } - if (i < self.params.len) return self.params.items[self.params.len - i - 1]; - i -= self.params.len; + pub fn lastToken(self: &AsyncAttribute) Token { + if (self.rangle_bracket) |rangle_bracket| { + return rangle_bracket; + } - if (self.lib_name) |lib_name| { - if (i < 1) return lib_name; - i -= 1; + return self.async_token; } + }; - return null; - } + pub const FnProto = struct { + base: Node, + comments: ?&LineComment, + visib_token: ?Token, + fn_token: Token, + name_token: ?Token, + params: ArrayList(&Node), + return_type: ReturnType, + var_args_token: ?Token, + extern_export_inline_token: ?Token, + cc_token: ?Token, + async_attr: ?&AsyncAttribute, + body_node: ?&Node, + lib_name: ?&Node, // populated if this is an extern declaration + align_expr: ?&Node, // populated if align(A) is present + + pub const ReturnType = union(enum) { + Explicit: &Node, + InferErrorSet: &Node, + }; - pub fn firstToken(self: &NodeFnProto) Token { - if (self.visib_token) |visib_token| return visib_token; - if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token; - assert(self.lib_name == null); - if (self.cc_token) |cc_token| return cc_token; - return self.fn_token; - } + pub fn iterate(self: &FnProto, index: usize) ?&Node { + var i = index; - pub fn lastToken(self: &NodeFnProto) Token { - if (self.body_node) |body_node| return body_node.lastToken(); - switch (self.return_type) { - // TODO allow this and next prong to share bodies since the types are the same - ReturnType.Explicit => |node| return node.lastToken(), - ReturnType.InferErrorSet => |node| return node.lastToken(), - } - } -}; + if (self.body_node) |body_node| { + if (i < 1) return body_node; + i -= 1; + } -pub const NodeParamDecl = struct { - base: Node, - comptime_token: ?Token, - noalias_token: ?Token, - name_token: ?Token, - type_node: &Node, - var_args_token: ?Token, + switch (self.return_type) { + // TODO allow this and next prong to share bodies since the types are the same + ReturnType.Explicit => |node| { + if (i < 1) return node; + i -= 1; + }, + ReturnType.InferErrorSet => |node| { + if (i < 1) return node; + i -= 1; + }, + } - pub fn iterate(self: &NodeParamDecl, index: usize) ?&Node { - var i = index; + if (self.align_expr) |align_expr| { + if (i < 1) return align_expr; + i -= 1; + } - if (i < 1) return self.type_node; - i -= 1; + if (i < self.params.len) return self.params.items[self.params.len - i - 1]; + i -= self.params.len; - return null; - } + if (self.lib_name) |lib_name| { + if (i < 1) return lib_name; + i -= 1; + } - pub fn firstToken(self: &NodeParamDecl) Token { - if (self.comptime_token) |comptime_token| return comptime_token; - if (self.noalias_token) |noalias_token| return noalias_token; - if (self.name_token) |name_token| return name_token; - return self.type_node.firstToken(); - } + return null; + } - pub fn lastToken(self: &NodeParamDecl) Token { - if (self.var_args_token) |var_args_token| return var_args_token; - return self.type_node.lastToken(); - } -}; + pub fn firstToken(self: &FnProto) Token { + if (self.visib_token) |visib_token| return visib_token; + if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token; + assert(self.lib_name == null); + if (self.cc_token) |cc_token| return cc_token; + return self.fn_token; + } -pub const NodeBlock = struct { - base: Node, - label: ?Token, - lbrace: Token, - statements: ArrayList(&Node), - rbrace: Token, + pub fn lastToken(self: &FnProto) Token { + if (self.body_node) |body_node| return body_node.lastToken(); + switch (self.return_type) { + // TODO allow this and next prong to share bodies since the types are the same + ReturnType.Explicit => |node| return node.lastToken(), + ReturnType.InferErrorSet => |node| return node.lastToken(), + } + } + }; - pub fn iterate(self: &NodeBlock, index: usize) ?&Node { - var i = index; + pub const ParamDecl = struct { + base: Node, + comptime_token: ?Token, + noalias_token: ?Token, + name_token: ?Token, + type_node: &Node, + var_args_token: ?Token, - if (i < self.statements.len) return self.statements.items[i]; - i -= self.statements.len; + pub fn iterate(self: &ParamDecl, index: usize) ?&Node { + var i = index; - return null; - } + if (i < 1) return self.type_node; + i -= 1; - pub fn firstToken(self: &NodeBlock) Token { - if (self.label) |label| { - return label; + return null; } - return self.lbrace; - } + pub fn firstToken(self: &ParamDecl) Token { + if (self.comptime_token) |comptime_token| return comptime_token; + if (self.noalias_token) |noalias_token| return noalias_token; + if (self.name_token) |name_token| return name_token; + return self.type_node.firstToken(); + } - pub fn lastToken(self: &NodeBlock) Token { - return self.rbrace; - } -}; + pub fn lastToken(self: &ParamDecl) Token { + if (self.var_args_token) |var_args_token| return var_args_token; + return self.type_node.lastToken(); + } + }; -pub const NodeDefer = struct { - base: Node, - defer_token: Token, - kind: Kind, - expr: &Node, + pub const Block = struct { + base: Node, + label: ?Token, + lbrace: Token, + statements: ArrayList(&Node), + rbrace: Token, - const Kind = enum { - Error, - Unconditional, - }; + pub fn iterate(self: &Block, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeDefer, index: usize) ?&Node { - var i = index; + if (i < self.statements.len) return self.statements.items[i]; + i -= self.statements.len; - if (i < 1) return self.expr; - i -= 1; + return null; + } - return null; - } + pub fn firstToken(self: &Block) Token { + if (self.label) |label| { + return label; + } - pub fn firstToken(self: &NodeDefer) Token { - return self.defer_token; - } + return self.lbrace; + } - pub fn lastToken(self: &NodeDefer) Token { - return self.expr.lastToken(); - } -}; + pub fn lastToken(self: &Block) Token { + return self.rbrace; + } + }; -pub const NodeComptime = struct { - base: Node, - comptime_token: Token, - expr: &Node, + pub const Defer = struct { + base: Node, + defer_token: Token, + kind: Kind, + expr: &Node, - pub fn iterate(self: &NodeComptime, index: usize) ?&Node { - var i = index; + const Kind = enum { + Error, + Unconditional, + }; - if (i < 1) return self.expr; - i -= 1; + pub fn iterate(self: &Defer, index: usize) ?&Node { + var i = index; - return null; - } + if (i < 1) return self.expr; + i -= 1; - pub fn firstToken(self: &NodeComptime) Token { - return self.comptime_token; - } + return null; + } - pub fn lastToken(self: &NodeComptime) Token { - return self.expr.lastToken(); - } -}; + pub fn firstToken(self: &Defer) Token { + return self.defer_token; + } -pub const NodePayload = struct { - base: Node, - lpipe: Token, - error_symbol: &Node, - rpipe: Token, + pub fn lastToken(self: &Defer) Token { + return self.expr.lastToken(); + } + }; - pub fn iterate(self: &NodePayload, index: usize) ?&Node { - var i = index; + pub const Comptime = struct { + base: Node, + comptime_token: Token, + expr: &Node, - if (i < 1) return self.error_symbol; - i -= 1; + pub fn iterate(self: &Comptime, index: usize) ?&Node { + var i = index; - return null; - } + if (i < 1) return self.expr; + i -= 1; - pub fn firstToken(self: &NodePayload) Token { - return self.lpipe; - } + return null; + } - pub fn lastToken(self: &NodePayload) Token { - return self.rpipe; - } -}; + pub fn firstToken(self: &Comptime) Token { + return self.comptime_token; + } -pub const NodePointerPayload = struct { - base: Node, - lpipe: Token, - ptr_token: ?Token, - value_symbol: &Node, - rpipe: Token, + pub fn lastToken(self: &Comptime) Token { + return self.expr.lastToken(); + } + }; - pub fn iterate(self: &NodePointerPayload, index: usize) ?&Node { - var i = index; + pub const Payload = struct { + base: Node, + lpipe: Token, + error_symbol: &Node, + rpipe: Token, - if (i < 1) return self.value_symbol; - i -= 1; + pub fn iterate(self: &Payload, index: usize) ?&Node { + var i = index; - return null; - } + if (i < 1) return self.error_symbol; + i -= 1; - pub fn firstToken(self: &NodePointerPayload) Token { - return self.lpipe; - } + return null; + } - pub fn lastToken(self: &NodePointerPayload) Token { - return self.rpipe; - } -}; + pub fn firstToken(self: &Payload) Token { + return self.lpipe; + } -pub const NodePointerIndexPayload = struct { - base: Node, - lpipe: Token, - ptr_token: ?Token, - value_symbol: &Node, - index_symbol: ?&Node, - rpipe: Token, + pub fn lastToken(self: &Payload) Token { + return self.rpipe; + } + }; - pub fn iterate(self: &NodePointerIndexPayload, index: usize) ?&Node { - var i = index; + pub const PointerPayload = struct { + base: Node, + lpipe: Token, + ptr_token: ?Token, + value_symbol: &Node, + rpipe: Token, - if (i < 1) return self.value_symbol; - i -= 1; + pub fn iterate(self: &PointerPayload, index: usize) ?&Node { + var i = index; - if (self.index_symbol) |index_symbol| { - if (i < 1) return index_symbol; + if (i < 1) return self.value_symbol; i -= 1; - } - return null; - } + return null; + } - pub fn firstToken(self: &NodePointerIndexPayload) Token { - return self.lpipe; - } + pub fn firstToken(self: &PointerPayload) Token { + return self.lpipe; + } - pub fn lastToken(self: &NodePointerIndexPayload) Token { - return self.rpipe; - } -}; + pub fn lastToken(self: &PointerPayload) Token { + return self.rpipe; + } + }; -pub const NodeElse = struct { - base: Node, - else_token: Token, - payload: ?&Node, - body: &Node, + pub const PointerIndexPayload = struct { + base: Node, + lpipe: Token, + ptr_token: ?Token, + value_symbol: &Node, + index_symbol: ?&Node, + rpipe: Token, - pub fn iterate(self: &NodeElse, index: usize) ?&Node { - var i = index; + pub fn iterate(self: &PointerIndexPayload, index: usize) ?&Node { + var i = index; - if (self.payload) |payload| { - if (i < 1) return payload; + if (i < 1) return self.value_symbol; i -= 1; - } - if (i < 1) return self.body; - i -= 1; - - return null; - } + if (self.index_symbol) |index_symbol| { + if (i < 1) return index_symbol; + i -= 1; + } - pub fn firstToken(self: &NodeElse) Token { - return self.else_token; - } + return null; + } - pub fn lastToken(self: &NodeElse) Token { - return self.body.lastToken(); - } -}; + pub fn firstToken(self: &PointerIndexPayload) Token { + return self.lpipe; + } -pub const NodeSwitch = struct { - base: Node, - switch_token: Token, - expr: &Node, - cases: ArrayList(&NodeSwitchCase), - rbrace: Token, + pub fn lastToken(self: &PointerIndexPayload) Token { + return self.rpipe; + } + }; - pub fn iterate(self: &NodeSwitch, index: usize) ?&Node { - var i = index; + pub const Else = struct { + base: Node, + else_token: Token, + payload: ?&Node, + body: &Node, - if (i < 1) return self.expr; - i -= 1; + pub fn iterate(self: &Else, index: usize) ?&Node { + var i = index; - if (i < self.cases.len) return &self.cases.at(i).base; - i -= self.cases.len; + if (self.payload) |payload| { + if (i < 1) return payload; + i -= 1; + } - return null; - } + if (i < 1) return self.body; + i -= 1; - pub fn firstToken(self: &NodeSwitch) Token { - return self.switch_token; - } + return null; + } - pub fn lastToken(self: &NodeSwitch) Token { - return self.rbrace; - } -}; + pub fn firstToken(self: &Else) Token { + return self.else_token; + } -pub const NodeSwitchCase = struct { - base: Node, - items: ArrayList(&Node), - payload: ?&Node, - expr: &Node, + pub fn lastToken(self: &Else) Token { + return self.body.lastToken(); + } + }; - pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node { - var i = index; + pub const Switch = struct { + base: Node, + switch_token: Token, + expr: &Node, + cases: ArrayList(&SwitchCase), + rbrace: Token, - if (i < self.items.len) return self.items.at(i); - i -= self.items.len; + pub fn iterate(self: &Switch, index: usize) ?&Node { + var i = index; - if (self.payload) |payload| { - if (i < 1) return payload; + if (i < 1) return self.expr; i -= 1; - } - if (i < 1) return self.expr; - i -= 1; + if (i < self.cases.len) return &self.cases.at(i).base; + i -= self.cases.len; - return null; - } + return null; + } - pub fn firstToken(self: &NodeSwitchCase) Token { - return self.items.at(0).firstToken(); - } + pub fn firstToken(self: &Switch) Token { + return self.switch_token; + } - pub fn lastToken(self: &NodeSwitchCase) Token { - return self.expr.lastToken(); - } -}; + pub fn lastToken(self: &Switch) Token { + return self.rbrace; + } + }; -pub const NodeSwitchElse = struct { - base: Node, - token: Token, + pub const SwitchCase = struct { + base: Node, + items: ArrayList(&Node), + payload: ?&Node, + expr: &Node, - pub fn iterate(self: &NodeSwitchElse, index: usize) ?&Node { - return null; - } + pub fn iterate(self: &SwitchCase, index: usize) ?&Node { + var i = index; - pub fn firstToken(self: &NodeSwitchElse) Token { - return self.token; - } + if (i < self.items.len) return self.items.at(i); + i -= self.items.len; - pub fn lastToken(self: &NodeSwitchElse) Token { - return self.token; - } -}; + if (self.payload) |payload| { + if (i < 1) return payload; + i -= 1; + } -pub const NodeWhile = struct { - base: Node, - label: ?Token, - inline_token: ?Token, - while_token: Token, - condition: &Node, - payload: ?&Node, - continue_expr: ?&Node, - body: &Node, - @"else": ?&NodeElse, - - pub fn iterate(self: &NodeWhile, index: usize) ?&Node { - var i = index; - - if (i < 1) return self.condition; - i -= 1; - - if (self.payload) |payload| { - if (i < 1) return payload; + if (i < 1) return self.expr; i -= 1; - } - if (self.continue_expr) |continue_expr| { - if (i < 1) return continue_expr; - i -= 1; + return null; } - if (i < 1) return self.body; - i -= 1; + pub fn firstToken(self: &SwitchCase) Token { + return self.items.at(0).firstToken(); + } - if (self.@"else") |@"else"| { - if (i < 1) return &@"else".base; - i -= 1; + pub fn lastToken(self: &SwitchCase) Token { + return self.expr.lastToken(); } + }; - return null; - } + pub const SwitchElse = struct { + base: Node, + token: Token, - pub fn firstToken(self: &NodeWhile) Token { - if (self.label) |label| { - return label; + pub fn iterate(self: &SwitchElse, index: usize) ?&Node { + return null; } - if (self.inline_token) |inline_token| { - return inline_token; + pub fn firstToken(self: &SwitchElse) Token { + return self.token; } - return self.while_token; - } - - pub fn lastToken(self: &NodeWhile) Token { - if (self.@"else") |@"else"| { - return @"else".body.lastToken(); + pub fn lastToken(self: &SwitchElse) Token { + return self.token; } + }; - return self.body.lastToken(); - } -}; - -pub const NodeFor = struct { - base: Node, - label: ?Token, - inline_token: ?Token, - for_token: Token, - array_expr: &Node, - payload: ?&Node, - body: &Node, - @"else": ?&NodeElse, + pub const While = struct { + base: Node, + label: ?Token, + inline_token: ?Token, + while_token: Token, + condition: &Node, + payload: ?&Node, + continue_expr: ?&Node, + body: &Node, + @"else": ?&Else, + + pub fn iterate(self: &While, index: usize) ?&Node { + var i = index; + + if (i < 1) return self.condition; + i -= 1; - pub fn iterate(self: &NodeFor, index: usize) ?&Node { - var i = index; + if (self.payload) |payload| { + if (i < 1) return payload; + i -= 1; + } - if (i < 1) return self.array_expr; - i -= 1; + if (self.continue_expr) |continue_expr| { + if (i < 1) return continue_expr; + i -= 1; + } - if (self.payload) |payload| { - if (i < 1) return payload; + if (i < 1) return self.body; i -= 1; - } - if (i < 1) return self.body; - i -= 1; + if (self.@"else") |@"else"| { + if (i < 1) return &@"else".base; + i -= 1; + } - if (self.@"else") |@"else"| { - if (i < 1) return &@"else".base; - i -= 1; + return null; } - return null; - } + pub fn firstToken(self: &While) Token { + if (self.label) |label| { + return label; + } - pub fn firstToken(self: &NodeFor) Token { - if (self.label) |label| { - return label; - } + if (self.inline_token) |inline_token| { + return inline_token; + } - if (self.inline_token) |inline_token| { - return inline_token; + return self.while_token; } - return self.for_token; - } + pub fn lastToken(self: &While) Token { + if (self.@"else") |@"else"| { + return @"else".body.lastToken(); + } - pub fn lastToken(self: &NodeFor) Token { - if (self.@"else") |@"else"| { - return @"else".body.lastToken(); + return self.body.lastToken(); } + }; - return self.body.lastToken(); - } -}; + pub const For = struct { + base: Node, + label: ?Token, + inline_token: ?Token, + for_token: Token, + array_expr: &Node, + payload: ?&Node, + body: &Node, + @"else": ?&Else, -pub const NodeIf = struct { - base: Node, - if_token: Token, - condition: &Node, - payload: ?&Node, - body: &Node, - @"else": ?&NodeElse, + pub fn iterate(self: &For, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeIf, index: usize) ?&Node { - var i = index; + if (i < 1) return self.array_expr; + i -= 1; - if (i < 1) return self.condition; - i -= 1; + if (self.payload) |payload| { + if (i < 1) return payload; + i -= 1; + } - if (self.payload) |payload| { - if (i < 1) return payload; + if (i < 1) return self.body; i -= 1; - } - if (i < 1) return self.body; - i -= 1; + if (self.@"else") |@"else"| { + if (i < 1) return &@"else".base; + i -= 1; + } - if (self.@"else") |@"else"| { - if (i < 1) return &@"else".base; - i -= 1; + return null; } - return null; - } + pub fn firstToken(self: &For) Token { + if (self.label) |label| { + return label; + } - pub fn firstToken(self: &NodeIf) Token { - return self.if_token; - } + if (self.inline_token) |inline_token| { + return inline_token; + } - pub fn lastToken(self: &NodeIf) Token { - if (self.@"else") |@"else"| { - return @"else".body.lastToken(); + return self.for_token; } - return self.body.lastToken(); - } -}; + pub fn lastToken(self: &For) Token { + if (self.@"else") |@"else"| { + return @"else".body.lastToken(); + } -pub const NodeInfixOp = struct { - base: Node, - op_token: Token, - lhs: &Node, - op: InfixOp, - rhs: &Node, - - const InfixOp = union(enum) { - Add, - AddWrap, - ArrayCat, - ArrayMult, - Assign, - AssignBitAnd, - AssignBitOr, - AssignBitShiftLeft, - AssignBitShiftRight, - AssignBitXor, - AssignDiv, - AssignMinus, - AssignMinusWrap, - AssignMod, - AssignPlus, - AssignPlusWrap, - AssignTimes, - AssignTimesWarp, - BangEqual, - BitAnd, - BitOr, - BitShiftLeft, - BitShiftRight, - BitXor, - BoolAnd, - BoolOr, - Catch: ?&Node, - Div, - EqualEqual, - ErrorUnion, - GreaterOrEqual, - GreaterThan, - LessOrEqual, - LessThan, - MergeErrorSets, - Mod, - Mult, - MultWrap, - Period, - Range, - Sub, - SubWrap, - UnwrapMaybe, + return self.body.lastToken(); + } }; - pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node { - var i = index; + pub const If = struct { + base: Node, + if_token: Token, + condition: &Node, + payload: ?&Node, + body: &Node, + @"else": ?&Else, - if (i < 1) return self.lhs; - i -= 1; + pub fn iterate(self: &If, index: usize) ?&Node { + var i = index; - switch (self.op) { - InfixOp.Catch => |maybe_payload| { - if (maybe_payload) |payload| { - if (i < 1) return payload; - i -= 1; - } - }, - - InfixOp.Add, - InfixOp.AddWrap, - InfixOp.ArrayCat, - InfixOp.ArrayMult, - InfixOp.Assign, - InfixOp.AssignBitAnd, - InfixOp.AssignBitOr, - InfixOp.AssignBitShiftLeft, - InfixOp.AssignBitShiftRight, - InfixOp.AssignBitXor, - InfixOp.AssignDiv, - InfixOp.AssignMinus, - InfixOp.AssignMinusWrap, - InfixOp.AssignMod, - InfixOp.AssignPlus, - InfixOp.AssignPlusWrap, - InfixOp.AssignTimes, - InfixOp.AssignTimesWarp, - InfixOp.BangEqual, - InfixOp.BitAnd, - InfixOp.BitOr, - InfixOp.BitShiftLeft, - InfixOp.BitShiftRight, - InfixOp.BitXor, - InfixOp.BoolAnd, - InfixOp.BoolOr, - InfixOp.Div, - InfixOp.EqualEqual, - InfixOp.ErrorUnion, - InfixOp.GreaterOrEqual, - InfixOp.GreaterThan, - InfixOp.LessOrEqual, - InfixOp.LessThan, - InfixOp.MergeErrorSets, - InfixOp.Mod, - InfixOp.Mult, - InfixOp.MultWrap, - InfixOp.Period, - InfixOp.Range, - InfixOp.Sub, - InfixOp.SubWrap, - InfixOp.UnwrapMaybe => {}, - } - - if (i < 1) return self.rhs; - i -= 1; - - return null; - } + if (i < 1) return self.condition; + i -= 1; - pub fn firstToken(self: &NodeInfixOp) Token { - return self.lhs.firstToken(); - } + if (self.payload) |payload| { + if (i < 1) return payload; + i -= 1; + } - pub fn lastToken(self: &NodeInfixOp) Token { - return self.rhs.lastToken(); - } -}; + if (i < 1) return self.body; + i -= 1; -pub const NodePrefixOp = struct { - base: Node, - op_token: Token, - op: PrefixOp, - rhs: &Node, - - const PrefixOp = union(enum) { - AddrOf: AddrOfInfo, - ArrayType: &Node, - Await, - BitNot, - BoolNot, - Cancel, - Deref, - MaybeType, - Negation, - NegationWrap, - Resume, - SliceType: AddrOfInfo, - Try, - UnwrapMaybe, - }; + if (self.@"else") |@"else"| { + if (i < 1) return &@"else".base; + i -= 1; + } - const AddrOfInfo = struct { - align_expr: ?&Node, - bit_offset_start_token: ?Token, - bit_offset_end_token: ?Token, - const_token: ?Token, - volatile_token: ?Token, - }; + return null; + } - pub fn iterate(self: &NodePrefixOp, index: usize) ?&Node { - var i = index; + pub fn firstToken(self: &If) Token { + return self.if_token; + } - switch (self.op) { - PrefixOp.SliceType => |addr_of_info| { - if (addr_of_info.align_expr) |align_expr| { - if (i < 1) return align_expr; - i -= 1; - } - }, - PrefixOp.AddrOf => |addr_of_info| { - if (addr_of_info.align_expr) |align_expr| { - if (i < 1) return align_expr; - i -= 1; - } - }, - PrefixOp.ArrayType => |size_expr| { - if (i < 1) return size_expr; - i -= 1; - }, - PrefixOp.Await, - PrefixOp.BitNot, - PrefixOp.BoolNot, - PrefixOp.Cancel, - PrefixOp.Deref, - PrefixOp.MaybeType, - PrefixOp.Negation, - PrefixOp.NegationWrap, - PrefixOp.Try, - PrefixOp.Resume, - PrefixOp.UnwrapMaybe => {}, - } - - if (i < 1) return self.rhs; - i -= 1; - - return null; - } + pub fn lastToken(self: &If) Token { + if (self.@"else") |@"else"| { + return @"else".body.lastToken(); + } - pub fn firstToken(self: &NodePrefixOp) Token { - return self.op_token; - } + return self.body.lastToken(); + } + }; - pub fn lastToken(self: &NodePrefixOp) Token { - return self.rhs.lastToken(); - } -}; + pub const InfixOp = struct { + base: Node, + op_token: Token, + lhs: &Node, + op: Op, + rhs: &Node, + + pub const Op = union(enum) { + Add, + AddWrap, + ArrayCat, + ArrayMult, + Assign, + AssignBitAnd, + AssignBitOr, + AssignBitShiftLeft, + AssignBitShiftRight, + AssignBitXor, + AssignDiv, + AssignMinus, + AssignMinusWrap, + AssignMod, + AssignPlus, + AssignPlusWrap, + AssignTimes, + AssignTimesWarp, + BangEqual, + BitAnd, + BitOr, + BitShiftLeft, + BitShiftRight, + BitXor, + BoolAnd, + BoolOr, + Catch: ?&Node, + Div, + EqualEqual, + ErrorUnion, + GreaterOrEqual, + GreaterThan, + LessOrEqual, + LessThan, + MergeErrorSets, + Mod, + Mult, + MultWrap, + Period, + Range, + Sub, + SubWrap, + UnwrapMaybe, + }; -pub const NodeFieldInitializer = struct { - base: Node, - period_token: Token, - name_token: Token, - expr: &Node, + pub fn iterate(self: &InfixOp, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeFieldInitializer, index: usize) ?&Node { - var i = index; + if (i < 1) return self.lhs; + i -= 1; - if (i < 1) return self.expr; - i -= 1; + switch (self.op) { + Op.Catch => |maybe_payload| { + if (maybe_payload) |payload| { + if (i < 1) return payload; + i -= 1; + } + }, + + Op.Add, + Op.AddWrap, + Op.ArrayCat, + Op.ArrayMult, + Op.Assign, + Op.AssignBitAnd, + Op.AssignBitOr, + Op.AssignBitShiftLeft, + Op.AssignBitShiftRight, + Op.AssignBitXor, + Op.AssignDiv, + Op.AssignMinus, + Op.AssignMinusWrap, + Op.AssignMod, + Op.AssignPlus, + Op.AssignPlusWrap, + Op.AssignTimes, + Op.AssignTimesWarp, + Op.BangEqual, + Op.BitAnd, + Op.BitOr, + Op.BitShiftLeft, + Op.BitShiftRight, + Op.BitXor, + Op.BoolAnd, + Op.BoolOr, + Op.Div, + Op.EqualEqual, + Op.ErrorUnion, + Op.GreaterOrEqual, + Op.GreaterThan, + Op.LessOrEqual, + Op.LessThan, + Op.MergeErrorSets, + Op.Mod, + Op.Mult, + Op.MultWrap, + Op.Period, + Op.Range, + Op.Sub, + Op.SubWrap, + Op.UnwrapMaybe => {}, + } - return null; - } + if (i < 1) return self.rhs; + i -= 1; - pub fn firstToken(self: &NodeFieldInitializer) Token { - return self.period_token; - } + return null; + } - pub fn lastToken(self: &NodeFieldInitializer) Token { - return self.expr.lastToken(); - } -}; + pub fn firstToken(self: &InfixOp) Token { + return self.lhs.firstToken(); + } -pub const NodeSuffixOp = struct { - base: Node, - lhs: &Node, - op: SuffixOp, - rtoken: Token, - - const SuffixOp = union(enum) { - Call: CallInfo, - ArrayAccess: &Node, - Slice: SliceRange, - ArrayInitializer: ArrayList(&Node), - StructInitializer: ArrayList(&NodeFieldInitializer), + pub fn lastToken(self: &InfixOp) Token { + return self.rhs.lastToken(); + } }; - const CallInfo = struct { - params: ArrayList(&Node), - async_attr: ?&NodeAsyncAttribute, - }; + pub const PrefixOp = struct { + base: Node, + op_token: Token, + op: Op, + rhs: &Node, + + const Op = union(enum) { + AddrOf: AddrOfInfo, + ArrayType: &Node, + Await, + BitNot, + BoolNot, + Cancel, + Deref, + MaybeType, + Negation, + NegationWrap, + Resume, + SliceType: AddrOfInfo, + Try, + UnwrapMaybe, + }; - const SliceRange = struct { - start: &Node, - end: ?&Node, - }; + const AddrOfInfo = struct { + align_expr: ?&Node, + bit_offset_start_token: ?Token, + bit_offset_end_token: ?Token, + const_token: ?Token, + volatile_token: ?Token, + }; - pub fn iterate(self: &NodeSuffixOp, index: usize) ?&Node { - var i = index; + pub fn iterate(self: &PrefixOp, index: usize) ?&Node { + var i = index; + + switch (self.op) { + Op.SliceType => |addr_of_info| { + if (addr_of_info.align_expr) |align_expr| { + if (i < 1) return align_expr; + i -= 1; + } + }, + Op.AddrOf => |addr_of_info| { + if (addr_of_info.align_expr) |align_expr| { + if (i < 1) return align_expr; + i -= 1; + } + }, + Op.ArrayType => |size_expr| { + if (i < 1) return size_expr; + i -= 1; + }, + Op.Await, + Op.BitNot, + Op.BoolNot, + Op.Cancel, + Op.Deref, + Op.MaybeType, + Op.Negation, + Op.NegationWrap, + Op.Try, + Op.Resume, + Op.UnwrapMaybe => {}, + } - if (i < 1) return self.lhs; - i -= 1; + if (i < 1) return self.rhs; + i -= 1; - switch (self.op) { - SuffixOp.Call => |call_info| { - if (i < call_info.params.len) return call_info.params.at(i); - i -= call_info.params.len; - }, - SuffixOp.ArrayAccess => |index_expr| { - if (i < 1) return index_expr; - i -= 1; - }, - SuffixOp.Slice => |range| { - if (i < 1) return range.start; - i -= 1; + return null; + } - if (range.end) |end| { - if (i < 1) return end; - i -= 1; - } - }, - SuffixOp.ArrayInitializer => |exprs| { - if (i < exprs.len) return exprs.at(i); - i -= exprs.len; - }, - SuffixOp.StructInitializer => |fields| { - if (i < fields.len) return &fields.at(i).base; - i -= fields.len; - }, - } - - return null; - } + pub fn firstToken(self: &PrefixOp) Token { + return self.op_token; + } - pub fn firstToken(self: &NodeSuffixOp) Token { - return self.lhs.firstToken(); - } + pub fn lastToken(self: &PrefixOp) Token { + return self.rhs.lastToken(); + } + }; - pub fn lastToken(self: &NodeSuffixOp) Token { - return self.rtoken; - } -}; + pub const FieldInitializer = struct { + base: Node, + period_token: Token, + name_token: Token, + expr: &Node, -pub const NodeGroupedExpression = struct { - base: Node, - lparen: Token, - expr: &Node, - rparen: Token, + pub fn iterate(self: &FieldInitializer, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeGroupedExpression, index: usize) ?&Node { - var i = index; + if (i < 1) return self.expr; + i -= 1; - if (i < 1) return self.expr; - i -= 1; + return null; + } - return null; - } + pub fn firstToken(self: &FieldInitializer) Token { + return self.period_token; + } - pub fn firstToken(self: &NodeGroupedExpression) Token { - return self.lparen; - } + pub fn lastToken(self: &FieldInitializer) Token { + return self.expr.lastToken(); + } + }; - pub fn lastToken(self: &NodeGroupedExpression) Token { - return self.rparen; - } -}; + pub const SuffixOp = struct { + base: Node, + lhs: &Node, + op: Op, + rtoken: Token, + + const Op = union(enum) { + Call: CallInfo, + ArrayAccess: &Node, + Slice: SliceRange, + ArrayInitializer: ArrayList(&Node), + StructInitializer: ArrayList(&FieldInitializer), + }; + + const CallInfo = struct { + params: ArrayList(&Node), + async_attr: ?&AsyncAttribute, + }; -pub const NodeControlFlowExpression = struct { - base: Node, - ltoken: Token, - kind: Kind, - rhs: ?&Node, + const SliceRange = struct { + start: &Node, + end: ?&Node, + }; - const Kind = union(enum) { - Break: ?&Node, - Continue: ?&Node, - Return, - }; + pub fn iterate(self: &SuffixOp, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeControlFlowExpression, index: usize) ?&Node { - var i = index; + if (i < 1) return self.lhs; + i -= 1; - switch (self.kind) { - Kind.Break => |maybe_label| { - if (maybe_label) |label| { - if (i < 1) return label; + switch (self.op) { + Op.Call => |call_info| { + if (i < call_info.params.len) return call_info.params.at(i); + i -= call_info.params.len; + }, + Op.ArrayAccess => |index_expr| { + if (i < 1) return index_expr; i -= 1; - } - }, - Kind.Continue => |maybe_label| { - if (maybe_label) |label| { - if (i < 1) return label; + }, + Op.Slice => |range| { + if (i < 1) return range.start; i -= 1; - } - }, - Kind.Return => {}, + + if (range.end) |end| { + if (i < 1) return end; + i -= 1; + } + }, + Op.ArrayInitializer => |exprs| { + if (i < exprs.len) return exprs.at(i); + i -= exprs.len; + }, + Op.StructInitializer => |fields| { + if (i < fields.len) return &fields.at(i).base; + i -= fields.len; + }, + } + + return null; } - if (self.rhs) |rhs| { - if (i < 1) return rhs; - i -= 1; + pub fn firstToken(self: &SuffixOp) Token { + return self.lhs.firstToken(); } - return null; - } + pub fn lastToken(self: &SuffixOp) Token { + return self.rtoken; + } + }; - pub fn firstToken(self: &NodeControlFlowExpression) Token { - return self.ltoken; - } + pub const GroupedExpression = struct { + base: Node, + lparen: Token, + expr: &Node, + rparen: Token, + + pub fn iterate(self: &GroupedExpression, index: usize) ?&Node { + var i = index; - pub fn lastToken(self: &NodeControlFlowExpression) Token { - if (self.rhs) |rhs| { - return rhs.lastToken(); + if (i < 1) return self.expr; + i -= 1; + + return null; } - switch (self.kind) { - Kind.Break => |maybe_label| { - if (maybe_label) |label| { - return label.lastToken(); - } - }, - Kind.Continue => |maybe_label| { - if (maybe_label) |label| { - return label.lastToken(); - } - }, - Kind.Return => return self.ltoken, + pub fn firstToken(self: &GroupedExpression) Token { + return self.lparen; } - return self.ltoken; - } -}; + pub fn lastToken(self: &GroupedExpression) Token { + return self.rparen; + } + }; -pub const NodeSuspend = struct { - base: Node, - suspend_token: Token, - payload: ?&Node, - body: ?&Node, + pub const ControlFlowExpression = struct { + base: Node, + ltoken: Token, + kind: Kind, + rhs: ?&Node, - pub fn iterate(self: &NodeSuspend, index: usize) ?&Node { - var i = index; + const Kind = union(enum) { + Break: ?&Node, + Continue: ?&Node, + Return, + }; - if (self.payload) |payload| { - if (i < 1) return payload; - i -= 1; + pub fn iterate(self: &ControlFlowExpression, index: usize) ?&Node { + var i = index; + + switch (self.kind) { + Kind.Break => |maybe_label| { + if (maybe_label) |label| { + if (i < 1) return label; + i -= 1; + } + }, + Kind.Continue => |maybe_label| { + if (maybe_label) |label| { + if (i < 1) return label; + i -= 1; + } + }, + Kind.Return => {}, + } + + if (self.rhs) |rhs| { + if (i < 1) return rhs; + i -= 1; + } + + return null; } - if (self.body) |body| { - if (i < 1) return body; - i -= 1; + pub fn firstToken(self: &ControlFlowExpression) Token { + return self.ltoken; } - return null; - } + pub fn lastToken(self: &ControlFlowExpression) Token { + if (self.rhs) |rhs| { + return rhs.lastToken(); + } - pub fn firstToken(self: &NodeSuspend) Token { - return self.suspend_token; - } + switch (self.kind) { + Kind.Break => |maybe_label| { + if (maybe_label) |label| { + return label.lastToken(); + } + }, + Kind.Continue => |maybe_label| { + if (maybe_label) |label| { + return label.lastToken(); + } + }, + Kind.Return => return self.ltoken, + } - pub fn lastToken(self: &NodeSuspend) Token { - if (self.body) |body| { - return body.lastToken(); + return self.ltoken; } + }; + + pub const Suspend = struct { + base: Node, + suspend_token: Token, + payload: ?&Node, + body: ?&Node, + + pub fn iterate(self: &Suspend, index: usize) ?&Node { + var i = index; + + if (self.payload) |payload| { + if (i < 1) return payload; + i -= 1; + } - if (self.payload) |payload| { - return payload.lastToken(); + if (self.body) |body| { + if (i < 1) return body; + i -= 1; + } + + return null; } - return self.suspend_token; - } -}; + pub fn firstToken(self: &Suspend) Token { + return self.suspend_token; + } -pub const NodeIntegerLiteral = struct { - base: Node, - token: Token, + pub fn lastToken(self: &Suspend) Token { + if (self.body) |body| { + return body.lastToken(); + } - pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node { - return null; - } + if (self.payload) |payload| { + return payload.lastToken(); + } - pub fn firstToken(self: &NodeIntegerLiteral) Token { - return self.token; - } + return self.suspend_token; + } + }; - pub fn lastToken(self: &NodeIntegerLiteral) Token { - return self.token; - } -}; + pub const IntegerLiteral = struct { + base: Node, + token: Token, -pub const NodeFloatLiteral = struct { - base: Node, - token: Token, + pub fn iterate(self: &IntegerLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &IntegerLiteral) Token { + return self.token; + } - pub fn firstToken(self: &NodeFloatLiteral) Token { - return self.token; - } + pub fn lastToken(self: &IntegerLiteral) Token { + return self.token; + } + }; - pub fn lastToken(self: &NodeFloatLiteral) Token { - return self.token; - } -}; + pub const FloatLiteral = struct { + base: Node, + token: Token, -pub const NodeBuiltinCall = struct { - base: Node, - builtin_token: Token, - params: ArrayList(&Node), - rparen_token: Token, + pub fn iterate(self: &FloatLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeBuiltinCall, index: usize) ?&Node { - var i = index; + pub fn firstToken(self: &FloatLiteral) Token { + return self.token; + } - if (i < self.params.len) return self.params.at(i); - i -= self.params.len; + pub fn lastToken(self: &FloatLiteral) Token { + return self.token; + } + }; - return null; - } + pub const BuiltinCall = struct { + base: Node, + builtin_token: Token, + params: ArrayList(&Node), + rparen_token: Token, - pub fn firstToken(self: &NodeBuiltinCall) Token { - return self.builtin_token; - } + pub fn iterate(self: &BuiltinCall, index: usize) ?&Node { + var i = index; - pub fn lastToken(self: &NodeBuiltinCall) Token { - return self.rparen_token; - } -}; + if (i < self.params.len) return self.params.at(i); + i -= self.params.len; -pub const NodeStringLiteral = struct { - base: Node, - token: Token, + return null; + } - pub fn iterate(self: &NodeStringLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &BuiltinCall) Token { + return self.builtin_token; + } - pub fn firstToken(self: &NodeStringLiteral) Token { - return self.token; - } + pub fn lastToken(self: &BuiltinCall) Token { + return self.rparen_token; + } + }; - pub fn lastToken(self: &NodeStringLiteral) Token { - return self.token; - } -}; + pub const StringLiteral = struct { + base: Node, + token: Token, -pub const NodeMultilineStringLiteral = struct { - base: Node, - tokens: ArrayList(Token), + pub fn iterate(self: &StringLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeMultilineStringLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &StringLiteral) Token { + return self.token; + } - pub fn firstToken(self: &NodeMultilineStringLiteral) Token { - return self.tokens.at(0); - } + pub fn lastToken(self: &StringLiteral) Token { + return self.token; + } + }; - pub fn lastToken(self: &NodeMultilineStringLiteral) Token { - return self.tokens.at(self.tokens.len - 1); - } -}; + pub const MultilineStringLiteral = struct { + base: Node, + tokens: ArrayList(Token), -pub const NodeCharLiteral = struct { - base: Node, - token: Token, + pub fn iterate(self: &MultilineStringLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeCharLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &MultilineStringLiteral) Token { + return self.tokens.at(0); + } - pub fn firstToken(self: &NodeCharLiteral) Token { - return self.token; - } + pub fn lastToken(self: &MultilineStringLiteral) Token { + return self.tokens.at(self.tokens.len - 1); + } + }; - pub fn lastToken(self: &NodeCharLiteral) Token { - return self.token; - } -}; + pub const CharLiteral = struct { + base: Node, + token: Token, -pub const NodeBoolLiteral = struct { - base: Node, - token: Token, + pub fn iterate(self: &CharLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeBoolLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &CharLiteral) Token { + return self.token; + } - pub fn firstToken(self: &NodeBoolLiteral) Token { - return self.token; - } + pub fn lastToken(self: &CharLiteral) Token { + return self.token; + } + }; - pub fn lastToken(self: &NodeBoolLiteral) Token { - return self.token; - } -}; + pub const BoolLiteral = struct { + base: Node, + token: Token, -pub const NodeNullLiteral = struct { - base: Node, - token: Token, + pub fn iterate(self: &BoolLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeNullLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &BoolLiteral) Token { + return self.token; + } - pub fn firstToken(self: &NodeNullLiteral) Token { - return self.token; - } + pub fn lastToken(self: &BoolLiteral) Token { + return self.token; + } + }; - pub fn lastToken(self: &NodeNullLiteral) Token { - return self.token; - } -}; + pub const NullLiteral = struct { + base: Node, + token: Token, -pub const NodeUndefinedLiteral = struct { - base: Node, - token: Token, + pub fn iterate(self: &NullLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeUndefinedLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &NullLiteral) Token { + return self.token; + } - pub fn firstToken(self: &NodeUndefinedLiteral) Token { - return self.token; - } + pub fn lastToken(self: &NullLiteral) Token { + return self.token; + } + }; - pub fn lastToken(self: &NodeUndefinedLiteral) Token { - return self.token; - } -}; + pub const UndefinedLiteral = struct { + base: Node, + token: Token, -pub const NodeThisLiteral = struct { - base: Node, - token: Token, + pub fn iterate(self: &UndefinedLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeThisLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &UndefinedLiteral) Token { + return self.token; + } - pub fn firstToken(self: &NodeThisLiteral) Token { - return self.token; - } + pub fn lastToken(self: &UndefinedLiteral) Token { + return self.token; + } + }; - pub fn lastToken(self: &NodeThisLiteral) Token { - return self.token; - } -}; + pub const ThisLiteral = struct { + base: Node, + token: Token, -pub const NodeAsmOutput = struct { - base: Node, - symbolic_name: &Node, - constraint: &Node, - kind: Kind, + pub fn iterate(self: &ThisLiteral, index: usize) ?&Node { + return null; + } - const Kind = union(enum) { - Variable: &NodeIdentifier, - Return: &Node + pub fn firstToken(self: &ThisLiteral) Token { + return self.token; + } + + pub fn lastToken(self: &ThisLiteral) Token { + return self.token; + } }; - pub fn iterate(self: &NodeAsmOutput, index: usize) ?&Node { - var i = index; + pub const AsmOutput = struct { + base: Node, + symbolic_name: &Node, + constraint: &Node, + kind: Kind, - if (i < 1) return self.symbolic_name; - i -= 1; + const Kind = union(enum) { + Variable: &Identifier, + Return: &Node + }; - if (i < 1) return self.constraint; - i -= 1; + pub fn iterate(self: &AsmOutput, index: usize) ?&Node { + var i = index; - switch (self.kind) { - Kind.Variable => |variable_name| { - if (i < 1) return &variable_name.base; - i -= 1; - }, - Kind.Return => |return_type| { - if (i < 1) return return_type; - i -= 1; + if (i < 1) return self.symbolic_name; + i -= 1; + + if (i < 1) return self.constraint; + i -= 1; + + switch (self.kind) { + Kind.Variable => |variable_name| { + if (i < 1) return &variable_name.base; + i -= 1; + }, + Kind.Return => |return_type| { + if (i < 1) return return_type; + i -= 1; + } } - } - return null; - } + return null; + } - pub fn firstToken(self: &NodeAsmOutput) Token { - return self.symbolic_name.firstToken(); - } + pub fn firstToken(self: &AsmOutput) Token { + return self.symbolic_name.firstToken(); + } - pub fn lastToken(self: &NodeAsmOutput) Token { - return switch (self.kind) { - Kind.Variable => |variable_name| variable_name.lastToken(), - Kind.Return => |return_type| return_type.lastToken(), - }; - } -}; + pub fn lastToken(self: &AsmOutput) Token { + return switch (self.kind) { + Kind.Variable => |variable_name| variable_name.lastToken(), + Kind.Return => |return_type| return_type.lastToken(), + }; + } + }; -pub const NodeAsmInput = struct { - base: Node, - symbolic_name: &Node, - constraint: &Node, - expr: &Node, + pub const AsmInput = struct { + base: Node, + symbolic_name: &Node, + constraint: &Node, + expr: &Node, - pub fn iterate(self: &NodeAsmInput, index: usize) ?&Node { - var i = index; + pub fn iterate(self: &AsmInput, index: usize) ?&Node { + var i = index; - if (i < 1) return self.symbolic_name; - i -= 1; + if (i < 1) return self.symbolic_name; + i -= 1; - if (i < 1) return self.constraint; - i -= 1; + if (i < 1) return self.constraint; + i -= 1; - if (i < 1) return self.expr; - i -= 1; + if (i < 1) return self.expr; + i -= 1; - return null; - } + return null; + } - pub fn firstToken(self: &NodeAsmInput) Token { - return self.symbolic_name.firstToken(); - } + pub fn firstToken(self: &AsmInput) Token { + return self.symbolic_name.firstToken(); + } - pub fn lastToken(self: &NodeAsmInput) Token { - return self.expr.lastToken(); - } -}; + pub fn lastToken(self: &AsmInput) Token { + return self.expr.lastToken(); + } + }; -pub const NodeAsm = struct { - base: Node, - asm_token: Token, - volatile_token: ?Token, - template: &Node, - //tokens: ArrayList(AsmToken), - outputs: ArrayList(&NodeAsmOutput), - inputs: ArrayList(&NodeAsmInput), - cloppers: ArrayList(&Node), - rparen: Token, + pub const Asm = struct { + base: Node, + asm_token: Token, + volatile_token: ?Token, + template: &Node, + //tokens: ArrayList(AsmToken), + outputs: ArrayList(&AsmOutput), + inputs: ArrayList(&AsmInput), + cloppers: ArrayList(&Node), + rparen: Token, - pub fn iterate(self: &NodeAsm, index: usize) ?&Node { - var i = index; + pub fn iterate(self: &Asm, index: usize) ?&Node { + var i = index; - if (i < self.outputs.len) return &self.outputs.at(index).base; - i -= self.outputs.len; + if (i < self.outputs.len) return &self.outputs.at(index).base; + i -= self.outputs.len; - if (i < self.inputs.len) return &self.inputs.at(index).base; - i -= self.inputs.len; + if (i < self.inputs.len) return &self.inputs.at(index).base; + i -= self.inputs.len; - if (i < self.cloppers.len) return self.cloppers.at(index); - i -= self.cloppers.len; + if (i < self.cloppers.len) return self.cloppers.at(index); + i -= self.cloppers.len; - return null; - } + return null; + } - pub fn firstToken(self: &NodeAsm) Token { - return self.asm_token; - } + pub fn firstToken(self: &Asm) Token { + return self.asm_token; + } - pub fn lastToken(self: &NodeAsm) Token { - return self.rparen; - } -}; + pub fn lastToken(self: &Asm) Token { + return self.rparen; + } + }; -pub const NodeUnreachable = struct { - base: Node, - token: Token, + pub const Unreachable = struct { + base: Node, + token: Token, - pub fn iterate(self: &NodeUnreachable, index: usize) ?&Node { - return null; - } + pub fn iterate(self: &Unreachable, index: usize) ?&Node { + return null; + } - pub fn firstToken(self: &NodeUnreachable) Token { - return self.token; - } + pub fn firstToken(self: &Unreachable) Token { + return self.token; + } - pub fn lastToken(self: &NodeUnreachable) Token { - return self.token; - } -}; + pub fn lastToken(self: &Unreachable) Token { + return self.token; + } + }; -pub const NodeErrorType = struct { - base: Node, - token: Token, + pub const ErrorType = struct { + base: Node, + token: Token, - pub fn iterate(self: &NodeErrorType, index: usize) ?&Node { - return null; - } + pub fn iterate(self: &ErrorType, index: usize) ?&Node { + return null; + } - pub fn firstToken(self: &NodeErrorType) Token { - return self.token; - } + pub fn firstToken(self: &ErrorType) Token { + return self.token; + } - pub fn lastToken(self: &NodeErrorType) Token { - return self.token; - } -}; + pub fn lastToken(self: &ErrorType) Token { + return self.token; + } + }; -pub const NodeVarType = struct { - base: Node, - token: Token, + pub const VarType = struct { + base: Node, + token: Token, - pub fn iterate(self: &NodeVarType, index: usize) ?&Node { - return null; - } + pub fn iterate(self: &VarType, index: usize) ?&Node { + return null; + } - pub fn firstToken(self: &NodeVarType) Token { - return self.token; - } + pub fn firstToken(self: &VarType) Token { + return self.token; + } - pub fn lastToken(self: &NodeVarType) Token { - return self.token; - } -}; + pub fn lastToken(self: &VarType) Token { + return self.token; + } + }; -pub const NodeLineComment = struct { - base: Node, - lines: ArrayList(Token), + pub const LineComment = struct { + base: Node, + lines: ArrayList(Token), - pub fn iterate(self: &NodeLineComment, index: usize) ?&Node { - return null; - } + pub fn iterate(self: &LineComment, index: usize) ?&Node { + return null; + } - pub fn firstToken(self: &NodeLineComment) Token { - return self.lines.at(0); - } + pub fn firstToken(self: &LineComment) Token { + return self.lines.at(0); + } - pub fn lastToken(self: &NodeLineComment) Token { - return self.lines.at(self.lines.len - 1); - } -}; + pub fn lastToken(self: &LineComment) Token { + return self.lines.at(self.lines.len - 1); + } + }; -pub const NodeTestDecl = struct { - base: Node, - test_token: Token, - name: &Node, - body_node: &Node, + pub const TestDecl = struct { + base: Node, + comments: ?&LineComment, + test_token: Token, + name: &Node, + body_node: &Node, - pub fn iterate(self: &NodeTestDecl, index: usize) ?&Node { - var i = index; + pub fn iterate(self: &TestDecl, index: usize) ?&Node { + var i = index; - if (i < 1) return self.body_node; - i -= 1; + if (i < 1) return self.body_node; + i -= 1; - return null; - } + return null; + } - pub fn firstToken(self: &NodeTestDecl) Token { - return self.test_token; - } + pub fn firstToken(self: &TestDecl) Token { + return self.test_token; + } - pub fn lastToken(self: &NodeTestDecl) Token { - return self.body_node.lastToken(); - } + pub fn lastToken(self: &TestDecl) Token { + return self.body_node.lastToken(); + } + }; }; + diff --git a/std/zig/parser.zig b/std/zig/parser.zig index c0708581ea..017d293491 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -18,10 +18,10 @@ pub const Parser = struct { put_back_tokens: [2]Token, put_back_count: usize, source_file_name: []const u8, - pending_line_comment_node: ?&ast.NodeLineComment, + pending_line_comment_node: ?&ast.Node.LineComment, pub const Tree = struct { - root_node: &ast.NodeRoot, + root_node: &ast.Node.Root, arena_allocator: std.heap.ArenaAllocator, pub fn deinit(self: &Tree) void { @@ -66,22 +66,24 @@ pub const Parser = struct { extern_export_token: ?Token, lib_name: ?&ast.Node, list: &ArrayList(&ast.Node), + comments: ?&ast.Node.LineComment, }; const TopLevelExternOrFieldCtx = struct { visib_token: Token, - container_decl: &ast.NodeContainerDecl, + container_decl: &ast.Node.ContainerDecl, }; const ExternTypeCtx = struct { opt_ctx: OptionalCtx, extern_token: Token, + comments: ?&ast.Node.LineComment, }; const ContainerKindCtx = struct { opt_ctx: OptionalCtx, ltoken: Token, - layout: ast.NodeContainerDecl.Layout, + layout: ast.Node.ContainerDecl.Layout, }; const ExpectTokenSave = struct { @@ -132,7 +134,7 @@ pub const Parser = struct { const AsyncEndCtx = struct { ctx: OptionalCtx, - attribute: &ast.NodeAsyncAttribute, + attribute: &ast.Node.AsyncAttribute, }; const ErrorTypeOrSetDeclCtx = struct { @@ -141,13 +143,13 @@ pub const Parser = struct { }; const ParamDeclEndCtx = struct { - fn_proto: &ast.NodeFnProto, - param_decl: &ast.NodeParamDecl, + fn_proto: &ast.Node.FnProto, + param_decl: &ast.Node.ParamDecl, }; const ComptimeStatementCtx = struct { comptime_token: Token, - block: &ast.NodeBlock, + block: &ast.Node.Block, }; const OptionalCtx = union(enum) { @@ -190,24 +192,24 @@ pub const Parser = struct { TopLevelExternOrField: TopLevelExternOrFieldCtx, ContainerKind: ContainerKindCtx, - ContainerInitArgStart: &ast.NodeContainerDecl, - ContainerInitArg: &ast.NodeContainerDecl, - ContainerDecl: &ast.NodeContainerDecl, + ContainerInitArgStart: &ast.Node.ContainerDecl, + ContainerInitArg: &ast.Node.ContainerDecl, + ContainerDecl: &ast.Node.ContainerDecl, VarDecl: VarDeclCtx, - VarDeclAlign: &ast.NodeVarDecl, - VarDeclEq: &ast.NodeVarDecl, + VarDeclAlign: &ast.Node.VarDecl, + VarDeclEq: &ast.Node.VarDecl, - FnDef: &ast.NodeFnProto, - FnProto: &ast.NodeFnProto, - FnProtoAlign: &ast.NodeFnProto, - FnProtoReturnType: &ast.NodeFnProto, + FnDef: &ast.Node.FnProto, + FnProto: &ast.Node.FnProto, + FnProtoAlign: &ast.Node.FnProto, + FnProtoReturnType: &ast.Node.FnProto, - ParamDecl: &ast.NodeFnProto, - ParamDeclAliasOrComptime: &ast.NodeParamDecl, - ParamDeclName: &ast.NodeParamDecl, + ParamDecl: &ast.Node.FnProto, + ParamDeclAliasOrComptime: &ast.Node.ParamDecl, + ParamDeclName: &ast.Node.ParamDecl, ParamDeclEnd: ParamDeclEndCtx, - ParamDeclComma: &ast.NodeFnProto, + ParamDeclComma: &ast.Node.FnProto, MaybeLabeledExpression: MaybeLabeledExpressionCtx, LabeledExpression: LabelCtx, @@ -215,39 +217,39 @@ pub const Parser = struct { While: LoopCtx, WhileContinueExpr: &?&ast.Node, For: LoopCtx, - Else: &?&ast.NodeElse, + Else: &?&ast.Node.Else, - Block: &ast.NodeBlock, - Statement: &ast.NodeBlock, + Block: &ast.Node.Block, + Statement: &ast.Node.Block, ComptimeStatement: ComptimeStatementCtx, Semicolon: &&ast.Node, - AsmOutputItems: &ArrayList(&ast.NodeAsmOutput), - AsmOutputReturnOrType: &ast.NodeAsmOutput, - AsmInputItems: &ArrayList(&ast.NodeAsmInput), + AsmOutputItems: &ArrayList(&ast.Node.AsmOutput), + AsmOutputReturnOrType: &ast.Node.AsmOutput, + AsmInputItems: &ArrayList(&ast.Node.AsmInput), AsmClopperItems: &ArrayList(&ast.Node), ExprListItemOrEnd: ExprListCtx, ExprListCommaOrEnd: ExprListCtx, - FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer), - FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer), - FieldListCommaOrEnd: &ast.NodeContainerDecl, + FieldInitListItemOrEnd: ListSave(&ast.Node.FieldInitializer), + FieldInitListCommaOrEnd: ListSave(&ast.Node.FieldInitializer), + FieldListCommaOrEnd: &ast.Node.ContainerDecl, IdentifierListItemOrEnd: ListSave(&ast.Node), IdentifierListCommaOrEnd: ListSave(&ast.Node), - SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase), - SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase), + SwitchCaseOrEnd: ListSave(&ast.Node.SwitchCase), + SwitchCaseCommaOrEnd: ListSave(&ast.Node.SwitchCase), SwitchCaseFirstItem: &ArrayList(&ast.Node), SwitchCaseItem: &ArrayList(&ast.Node), SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node), - SuspendBody: &ast.NodeSuspend, - AsyncAllocator: &ast.NodeAsyncAttribute, + SuspendBody: &ast.Node.Suspend, + AsyncAllocator: &ast.Node.AsyncAttribute, AsyncEnd: AsyncEndCtx, ExternType: ExternTypeCtx, - SliceOrArrayAccess: &ast.NodeSuffixOp, - SliceOrArrayType: &ast.NodePrefixOp, - AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo, + SliceOrArrayAccess: &ast.Node.SuffixOp, + SliceOrArrayType: &ast.Node.PrefixOp, + AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo, Payload: OptionalCtx, PointerPayload: OptionalCtx, @@ -310,8 +312,8 @@ pub const Parser = struct { errdefer arena_allocator.deinit(); const arena = &arena_allocator.allocator; - const root_node = try self.createNode(arena, ast.NodeRoot, - ast.NodeRoot { + const root_node = try self.createNode(arena, ast.Node.Root, + ast.Node.Root { .base = undefined, .decls = ArrayList(&ast.Node).init(arena), // initialized when we get the eof token @@ -334,43 +336,19 @@ pub const Parser = struct { // warn("\n"); //} - // look for line comments - while (true) { - if (self.eatToken(Token.Id.LineComment)) |line_comment| { - const node = blk: { - if (self.pending_line_comment_node) |comment_node| { - break :blk comment_node; - } else { - const comment_node = try arena.create(ast.NodeLineComment); - *comment_node = ast.NodeLineComment { - .base = ast.Node { - .id = ast.Node.Id.LineComment, - .comment = null, - }, - .lines = ArrayList(Token).init(arena), - }; - self.pending_line_comment_node = comment_node; - break :blk comment_node; - } - }; - try node.lines.append(line_comment); - continue; - } - break; - } - // This gives us 1 free append that can't fail const state = stack.pop(); switch (state) { State.TopLevel => { + const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_test => { stack.append(State.TopLevel) catch unreachable; - const block = try self.createNode(arena, ast.NodeBlock, - ast.NodeBlock { + const block = try self.createNode(arena, ast.Node.Block, + ast.Node.Block { .base = undefined, .label = null, .lbrace = undefined, @@ -378,9 +356,10 @@ pub const Parser = struct { .rbrace = undefined, } ); - const test_node = try self.createAttachNode(arena, &root_node.decls, ast.NodeTestDecl, - ast.NodeTestDecl { + const test_node = try self.createAttachNode(arena, &root_node.decls, ast.Node.TestDecl, + ast.Node.TestDecl { .base = undefined, + .comments = comments, .test_token = token, .name = undefined, .body_node = &block.base, @@ -413,8 +392,8 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_comptime => { - const block = try self.createNode(arena, ast.NodeBlock, - ast.NodeBlock { + const block = try self.createNode(arena, ast.Node.Block, + ast.Node.Block { .base = undefined, .label = null, .lbrace = undefined, @@ -422,8 +401,8 @@ pub const Parser = struct { .rbrace = undefined, } ); - const node = try self.createAttachNode(arena, &root_node.decls, ast.NodeComptime, - ast.NodeComptime { + const node = try self.createAttachNode(arena, &root_node.decls, ast.Node.Comptime, + ast.Node.Comptime { .base = undefined, .comptime_token = token, .expr = &block.base, @@ -506,6 +485,7 @@ pub const Parser = struct { continue; }, State.TopLevelDecl => |ctx| { + const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_use => { @@ -513,8 +493,8 @@ pub const Parser = struct { return self.parseError(token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id)); } - const node = try self.createAttachNode(arena, ctx.decls, ast.NodeUse, - ast.NodeUse { + const node = try self.createAttachNode(arena, ctx.decls, ast.Node.Use, + ast.Node.Use { .base = undefined, .visib_token = ctx.visib_token, .expr = undefined, @@ -539,6 +519,7 @@ pub const Parser = struct { stack.append(State { .VarDecl = VarDeclCtx { + .comments = comments, .visib_token = ctx.visib_token, .lib_name = ctx.lib_name, .comptime_token = null, @@ -551,9 +532,10 @@ pub const Parser = struct { }, Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { - const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.NodeFnProto, - ast.NodeFnProto { + const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.Node.FnProto, + ast.Node.FnProto { .base = undefined, + .comments = comments, .visib_token = ctx.visib_token, .name_token = null, .fn_token = undefined, @@ -583,8 +565,8 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_async => { - const async_node = try self.createNode(arena, ast.NodeAsyncAttribute, - ast.NodeAsyncAttribute { + const async_node = try self.createNode(arena, ast.Node.AsyncAttribute, + ast.Node.AsyncAttribute { .base = undefined, .async_token = token, .allocator_type = null, @@ -616,9 +598,9 @@ pub const Parser = struct { }, State.TopLevelExternOrField => |ctx| { if (self.eatToken(Token.Id.Identifier)) |identifier| { - std.debug.assert(ctx.container_decl.kind == ast.NodeContainerDecl.Kind.Struct); - const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.NodeStructField, - ast.NodeStructField { + std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct); + const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.Node.StructField, + ast.Node.StructField { .base = undefined, .visib_token = ctx.visib_token, .name_token = identifier, @@ -647,15 +629,15 @@ pub const Parser = struct { State.ContainerKind => |ctx| { const token = self.getNextToken(); - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeContainerDecl, - ast.NodeContainerDecl { + const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl, + ast.Node.ContainerDecl { .base = undefined, .ltoken = ctx.ltoken, .layout = ctx.layout, .kind = switch (token.id) { - Token.Id.Keyword_struct => ast.NodeContainerDecl.Kind.Struct, - Token.Id.Keyword_union => ast.NodeContainerDecl.Kind.Union, - Token.Id.Keyword_enum => ast.NodeContainerDecl.Kind.Enum, + Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct, + Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union, + Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum, else => { return self.parseError(token, "expected {}, {} or {}, found {}", @tagName(Token.Id.Keyword_struct), @@ -664,7 +646,7 @@ pub const Parser = struct { @tagName(token.id)); }, }, - .init_arg_expr = ast.NodeContainerDecl.InitArg.None, + .init_arg_expr = ast.Node.ContainerDecl.InitArg.None, .fields_and_decls = ArrayList(&ast.Node).init(arena), .rbrace_token = undefined, } @@ -690,11 +672,11 @@ pub const Parser = struct { const init_arg_token = self.getNextToken(); switch (init_arg_token.id) { Token.Id.Keyword_enum => { - container_decl.init_arg_expr = ast.NodeContainerDecl.InitArg.Enum; + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg.Enum; }, else => { self.putBackToken(init_arg_token); - container_decl.init_arg_expr = ast.NodeContainerDecl.InitArg { .Type = undefined }; + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined }; stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; }, } @@ -705,9 +687,9 @@ pub const Parser = struct { switch (token.id) { Token.Id.Identifier => { switch (container_decl.kind) { - ast.NodeContainerDecl.Kind.Struct => { - const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeStructField, - ast.NodeStructField { + ast.Node.ContainerDecl.Kind.Struct => { + const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.StructField, + ast.Node.StructField { .base = undefined, .visib_token = null, .name_token = token, @@ -720,9 +702,9 @@ pub const Parser = struct { try stack.append(State { .ExpectToken = Token.Id.Colon }); continue; }, - ast.NodeContainerDecl.Kind.Union => { - const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeUnionTag, - ast.NodeUnionTag { + ast.Node.ContainerDecl.Kind.Union => { + const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.UnionTag, + ast.Node.UnionTag { .base = undefined, .name_token = token, .type_expr = null, @@ -734,9 +716,9 @@ pub const Parser = struct { try stack.append(State { .IfToken = Token.Id.Colon }); continue; }, - ast.NodeContainerDecl.Kind.Enum => { - const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeEnumTag, - ast.NodeEnumTag { + ast.Node.ContainerDecl.Kind.Enum => { + const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.EnumTag, + ast.Node.EnumTag { .base = undefined, .name_token = token, .value = null, @@ -752,7 +734,7 @@ pub const Parser = struct { }, Token.Id.Keyword_pub => { switch (container_decl.kind) { - ast.NodeContainerDecl.Kind.Struct => { + ast.Node.ContainerDecl.Kind.Struct => { try stack.append(State { .TopLevelExternOrField = TopLevelExternOrFieldCtx { .visib_token = token, @@ -809,9 +791,10 @@ pub const Parser = struct { State.VarDecl => |ctx| { - const var_decl = try self.createAttachNode(arena, ctx.list, ast.NodeVarDecl, - ast.NodeVarDecl { + const var_decl = try self.createAttachNode(arena, ctx.list, ast.Node.VarDecl, + ast.Node.VarDecl { .base = undefined, + .comments = ctx.comments, .visib_token = ctx.visib_token, .mut_token = ctx.mut_token, .comptime_token = ctx.comptime_token, @@ -881,8 +864,8 @@ pub const Parser = struct { const token = self.getNextToken(); switch(token.id) { Token.Id.LBrace => { - const block = try self.createNode(arena, ast.NodeBlock, - ast.NodeBlock { + const block = try self.createNode(arena, ast.Node.Block, + ast.Node.Block { .base = undefined, .label = null, .lbrace = token, @@ -924,7 +907,7 @@ pub const Parser = struct { const token = self.getNextToken(); switch (token.id) { Token.Id.Bang => { - fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined }; + fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined }; stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet }, }) catch unreachable; @@ -934,15 +917,15 @@ pub const Parser = struct { // TODO: this is a special case. Remove this when #760 is fixed if (token.id == Token.Id.Keyword_error) { if (self.isPeekToken(Token.Id.LBrace)) { - fn_proto.return_type = ast.NodeFnProto.ReturnType { - .Explicit = &(try self.createLiteral(arena, ast.NodeErrorType, token)).base + fn_proto.return_type = ast.Node.FnProto.ReturnType { + .Explicit = &(try self.createLiteral(arena, ast.Node.ErrorType, token)).base }; continue; } } self.putBackToken(token); - fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined }; + fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined }; stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; continue; }, @@ -954,8 +937,8 @@ pub const Parser = struct { if (self.eatToken(Token.Id.RParen)) |_| { continue; } - const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.NodeParamDecl, - ast.NodeParamDecl { + const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.Node.ParamDecl, + ast.Node.ParamDecl { .base = undefined, .comptime_token = null, .noalias_token = null, @@ -1026,15 +1009,15 @@ pub const Parser = struct { continue; } - _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.NodeIdentifier, ctx.label); + _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label); continue; }, State.LabeledExpression => |ctx| { const token = self.getNextToken(); switch (token.id) { Token.Id.LBrace => { - const block = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeBlock, - ast.NodeBlock { + const block = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block, + ast.Node.Block { .base = undefined, .label = ctx.label, .lbrace = token, @@ -1123,8 +1106,8 @@ pub const Parser = struct { } }, State.While => |ctx| { - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeWhile, - ast.NodeWhile { + const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.While, + ast.Node.While { .base = undefined, .label = ctx.label, .inline_token = ctx.inline_token, @@ -1153,8 +1136,8 @@ pub const Parser = struct { continue; }, State.For => |ctx| { - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeFor, - ast.NodeFor { + const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.For, + ast.Node.For { .base = undefined, .label = ctx.label, .inline_token = ctx.inline_token, @@ -1175,8 +1158,8 @@ pub const Parser = struct { }, State.Else => |dest| { if (self.eatToken(Token.Id.Keyword_else)) |else_token| { - const node = try self.createNode(arena, ast.NodeElse, - ast.NodeElse { + const node = try self.createNode(arena, ast.Node.Else, + ast.Node.Else { .base = undefined, .else_token = else_token, .payload = null, @@ -1210,6 +1193,7 @@ pub const Parser = struct { } }, State.Statement => |block| { + const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_comptime => { @@ -1224,6 +1208,7 @@ pub const Parser = struct { Token.Id.Keyword_var, Token.Id.Keyword_const => { stack.append(State { .VarDecl = VarDeclCtx { + .comments = comments, .visib_token = null, .comptime_token = null, .extern_export_token = null, @@ -1235,13 +1220,13 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { - const node = try self.createAttachNode(arena, &block.statements, ast.NodeDefer, - ast.NodeDefer { + const node = try self.createAttachNode(arena, &block.statements, ast.Node.Defer, + ast.Node.Defer { .base = undefined, .defer_token = token, .kind = switch (token.id) { - Token.Id.Keyword_defer => ast.NodeDefer.Kind.Unconditional, - Token.Id.Keyword_errdefer => ast.NodeDefer.Kind.Error, + Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, + Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, else => unreachable, }, .expr = undefined, @@ -1252,8 +1237,8 @@ pub const Parser = struct { continue; }, Token.Id.LBrace => { - const inner_block = try self.createAttachNode(arena, &block.statements, ast.NodeBlock, - ast.NodeBlock { + const inner_block = try self.createAttachNode(arena, &block.statements, ast.Node.Block, + ast.Node.Block { .base = undefined, .label = null, .lbrace = token, @@ -1274,11 +1259,13 @@ pub const Parser = struct { } }, State.ComptimeStatement => |ctx| { + const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_var, Token.Id.Keyword_const => { stack.append(State { .VarDecl = VarDeclCtx { + .comments = comments, .visib_token = null, .comptime_token = ctx.comptime_token, .extern_export_token = null, @@ -1316,8 +1303,8 @@ pub const Parser = struct { continue; } - const node = try self.createNode(arena, ast.NodeAsmOutput, - ast.NodeAsmOutput { + const node = try self.createNode(arena, ast.Node.AsmOutput, + ast.Node.AsmOutput { .base = undefined, .symbolic_name = undefined, .constraint = undefined, @@ -1340,11 +1327,11 @@ pub const Parser = struct { const token = self.getNextToken(); switch (token.id) { Token.Id.Identifier => { - node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.NodeIdentifier, token) }; + node.kind = ast.Node.AsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.Node.Identifier, token) }; continue; }, Token.Id.Arrow => { - node.kind = ast.NodeAsmOutput.Kind { .Return = undefined }; + node.kind = ast.Node.AsmOutput.Kind { .Return = undefined }; try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } }); continue; }, @@ -1362,8 +1349,8 @@ pub const Parser = struct { continue; } - const node = try self.createNode(arena, ast.NodeAsmInput, - ast.NodeAsmInput { + const node = try self.createNode(arena, ast.Node.AsmInput, + ast.Node.AsmInput { .base = undefined, .symbolic_name = undefined, .constraint = undefined, @@ -1415,8 +1402,8 @@ pub const Parser = struct { continue; } - const node = try self.createNode(arena, ast.NodeFieldInitializer, - ast.NodeFieldInitializer { + const node = try self.createNode(arena, ast.Node.FieldInitializer, + ast.Node.FieldInitializer { .base = undefined, .period_token = undefined, .name_token = undefined, @@ -1485,8 +1472,8 @@ pub const Parser = struct { continue; } - const node = try self.createNode(arena, ast.NodeSwitchCase, - ast.NodeSwitchCase { + const node = try self.createNode(arena, ast.Node.SwitchCase, + ast.Node.SwitchCase { .base = undefined, .items = ArrayList(&ast.Node).init(arena), .payload = null, @@ -1512,8 +1499,8 @@ pub const Parser = struct { State.SwitchCaseFirstItem => |case_items| { const token = self.getNextToken(); if (token.id == Token.Id.Keyword_else) { - const else_node = try self.createAttachNode(arena, case_items, ast.NodeSwitchElse, - ast.NodeSwitchElse { + const else_node = try self.createAttachNode(arena, case_items, ast.Node.SwitchElse, + ast.Node.SwitchElse { .base = undefined, .token = token, } @@ -1564,24 +1551,24 @@ pub const Parser = struct { switch (node.id) { ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", node); + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node); fn_proto.async_attr = ctx.attribute; continue; }, ast.Node.Id.SuffixOp => { - const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node); - if (suffix_op.op == ast.NodeSuffixOp.SuffixOp.Call) { + const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node); + if (suffix_op.op == ast.Node.SuffixOp.Op.Call) { suffix_op.op.Call.async_attr = ctx.attribute; continue; } return self.parseError(node.firstToken(), "expected {}, found {}.", - @tagName(ast.NodeSuffixOp.SuffixOp.Call), + @tagName(ast.Node.SuffixOp.Op.Call), @tagName(suffix_op.op)); }, else => { return self.parseError(node.firstToken(), "expected {} or {}, found {}.", - @tagName(ast.NodeSuffixOp.SuffixOp.Call), + @tagName(ast.Node.SuffixOp.Op.Call), @tagName(ast.Node.Id.FnProto), @tagName(node.id)); } @@ -1591,9 +1578,10 @@ pub const Parser = struct { State.ExternType => |ctx| { if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| { - const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeFnProto, - ast.NodeFnProto { + const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.FnProto, + ast.Node.FnProto { .base = undefined, + .comments = ctx.comments, .visib_token = null, .name_token = null, .fn_token = fn_token, @@ -1616,7 +1604,7 @@ pub const Parser = struct { .ContainerKind = ContainerKindCtx { .opt_ctx = ctx.opt_ctx, .ltoken = ctx.extern_token, - .layout = ast.NodeContainerDecl.Layout.Extern, + .layout = ast.Node.ContainerDecl.Layout.Extern, }, }) catch unreachable; continue; @@ -1626,8 +1614,8 @@ pub const Parser = struct { switch (token.id) { Token.Id.Ellipsis2 => { const start = node.op.ArrayAccess; - node.op = ast.NodeSuffixOp.SuffixOp { - .Slice = ast.NodeSuffixOp.SliceRange { + node.op = ast.Node.SuffixOp.Op { + .Slice = ast.Node.SuffixOp.SliceRange { .start = start, .end = null, } @@ -1653,8 +1641,8 @@ pub const Parser = struct { }, State.SliceOrArrayType => |node| { if (self.eatToken(Token.Id.RBracket)) |_| { - node.op = ast.NodePrefixOp.PrefixOp { - .SliceType = ast.NodePrefixOp.AddrOfInfo { + node.op = ast.Node.PrefixOp.Op { + .SliceType = ast.Node.PrefixOp.AddrOfInfo { .align_expr = null, .bit_offset_start_token = null, .bit_offset_end_token = null, @@ -1667,7 +1655,7 @@ pub const Parser = struct { continue; } - node.op = ast.NodePrefixOp.PrefixOp { .ArrayType = undefined }; + node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined }; stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; try stack.append(State { .ExpectToken = Token.Id.RBracket }); try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } }); @@ -1723,8 +1711,8 @@ pub const Parser = struct { continue; } - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePayload, - ast.NodePayload { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Payload, + ast.Node.Payload { .base = undefined, .lpipe = token, .error_symbol = undefined, @@ -1754,8 +1742,8 @@ pub const Parser = struct { continue; } - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePointerPayload, - ast.NodePointerPayload { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload, + ast.Node.PointerPayload { .base = undefined, .lpipe = token, .ptr_token = null, @@ -1792,8 +1780,8 @@ pub const Parser = struct { continue; } - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePointerIndexPayload, - ast.NodePointerIndexPayload { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload, + ast.Node.PointerIndexPayload { .base = undefined, .lpipe = token, .ptr_token = null, @@ -1826,8 +1814,8 @@ pub const Parser = struct { const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeControlFlowExpression, - ast.NodeControlFlowExpression { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression, + ast.Node.ControlFlowExpression { .base = undefined, .ltoken = token, .kind = undefined, @@ -1839,31 +1827,31 @@ pub const Parser = struct { switch (token.id) { Token.Id.Keyword_break => { - node.kind = ast.NodeControlFlowExpression.Kind { .Break = null }; + node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null }; try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } }); try stack.append(State { .IfToken = Token.Id.Colon }); }, Token.Id.Keyword_continue => { - node.kind = ast.NodeControlFlowExpression.Kind { .Continue = null }; + node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null }; try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } }); try stack.append(State { .IfToken = Token.Id.Colon }); }, Token.Id.Keyword_return => { - node.kind = ast.NodeControlFlowExpression.Kind.Return; + node.kind = ast.Node.ControlFlowExpression.Kind.Return; }, else => unreachable, } continue; }, Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp, - ast.NodePrefixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { .base = undefined, .op_token = token, .op = switch (token.id) { - Token.Id.Keyword_try => ast.NodePrefixOp.PrefixOp { .Try = void{} }, - Token.Id.Keyword_cancel => ast.NodePrefixOp.PrefixOp { .Cancel = void{} }, - Token.Id.Keyword_resume => ast.NodePrefixOp.PrefixOp { .Resume = void{} }, + Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} }, + Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} }, + Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} }, else => unreachable, }, .rhs = undefined, @@ -1891,12 +1879,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = ellipsis3, - .op = ast.NodeInfixOp.InfixOp.Range, + .op = ast.Node.InfixOp.Op.Range, .rhs = undefined, } ); @@ -1915,8 +1903,8 @@ pub const Parser = struct { const token = self.getNextToken(); if (tokenIdToAssignment(token.id)) |ass_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, @@ -1944,8 +1932,8 @@ pub const Parser = struct { const token = self.getNextToken(); if (tokenIdToUnwrapExpr(token.id)) |unwrap_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, @@ -1957,7 +1945,7 @@ pub const Parser = struct { stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); - if (node.op == ast.NodeInfixOp.InfixOp.Catch) { + if (node.op == ast.Node.InfixOp.Op.Catch) { try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } }); } continue; @@ -1977,12 +1965,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Keyword_or)) |or_token| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = or_token, - .op = ast.NodeInfixOp.InfixOp.BoolOr, + .op = ast.Node.InfixOp.Op.BoolOr, .rhs = undefined, } ); @@ -2002,12 +1990,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Keyword_and)) |and_token| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = and_token, - .op = ast.NodeInfixOp.InfixOp.BoolAnd, + .op = ast.Node.InfixOp.Op.BoolAnd, .rhs = undefined, } ); @@ -2028,8 +2016,8 @@ pub const Parser = struct { const token = self.getNextToken(); if (tokenIdToComparison(token.id)) |comp_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, @@ -2056,12 +2044,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Pipe)) |pipe| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = pipe, - .op = ast.NodeInfixOp.InfixOp.BitOr, + .op = ast.Node.InfixOp.Op.BitOr, .rhs = undefined, } ); @@ -2081,12 +2069,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Caret)) |caret| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = caret, - .op = ast.NodeInfixOp.InfixOp.BitXor, + .op = ast.Node.InfixOp.Op.BitXor, .rhs = undefined, } ); @@ -2106,12 +2094,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Ampersand)) |ampersand| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = ampersand, - .op = ast.NodeInfixOp.InfixOp.BitAnd, + .op = ast.Node.InfixOp.Op.BitAnd, .rhs = undefined, } ); @@ -2132,8 +2120,8 @@ pub const Parser = struct { const token = self.getNextToken(); if (tokenIdToBitShift(token.id)) |bitshift_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, @@ -2161,8 +2149,8 @@ pub const Parser = struct { const token = self.getNextToken(); if (tokenIdToAddition(token.id)) |add_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, @@ -2190,8 +2178,8 @@ pub const Parser = struct { const token = self.getNextToken(); if (tokenIdToMultiply(token.id)) |mult_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, @@ -2219,12 +2207,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.isPeekToken(Token.Id.Period)) { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp, - ast.NodeSuffixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { .base = undefined, .lhs = lhs, - .op = ast.NodeSuffixOp.SuffixOp { - .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena), + .op = ast.Node.SuffixOp.Op { + .StructInitializer = ArrayList(&ast.Node.FieldInitializer).init(arena), }, .rtoken = undefined, } @@ -2232,7 +2220,7 @@ pub const Parser = struct { stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .IfToken = Token.Id.LBrace }); try stack.append(State { - .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) { + .FieldInitListItemOrEnd = ListSave(&ast.Node.FieldInitializer) { .list = &node.op.StructInitializer, .ptr = &node.rtoken, } @@ -2240,11 +2228,11 @@ pub const Parser = struct { continue; } - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp, - ast.NodeSuffixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { .base = undefined, .lhs = lhs, - .op = ast.NodeSuffixOp.SuffixOp { + .op = ast.Node.SuffixOp.Op { .ArrayInitializer = ArrayList(&ast.Node).init(arena), }, .rtoken = undefined, @@ -2272,12 +2260,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Bang)) |bang| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = bang, - .op = ast.NodeInfixOp.InfixOp.ErrorUnion, + .op = ast.Node.InfixOp.Op.ErrorUnion, .rhs = undefined, } ); @@ -2290,8 +2278,8 @@ pub const Parser = struct { State.PrefixOpExpression => |opt_ctx| { const token = self.getNextToken(); if (tokenIdToPrefixOp(token.id)) |prefix_id| { - var node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp, - ast.NodePrefixOp { + var node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { .base = undefined, .op_token = token, .op = prefix_id, @@ -2301,8 +2289,8 @@ pub const Parser = struct { // Treat '**' token as two derefs if (token.id == Token.Id.AsteriskAsterisk) { - const child = try self.createNode(arena, ast.NodePrefixOp, - ast.NodePrefixOp { + const child = try self.createNode(arena, ast.Node.PrefixOp, + ast.Node.PrefixOp { .base = undefined, .op_token = token, .op = prefix_id, @@ -2314,7 +2302,7 @@ pub const Parser = struct { } stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - if (node.op == ast.NodePrefixOp.PrefixOp.AddrOf) { + if (node.op == ast.Node.PrefixOp.Op.AddrOf) { try stack.append(State { .AddrOfModifiers = &node.op.AddrOf }); } continue; @@ -2327,8 +2315,8 @@ pub const Parser = struct { State.SuffixOpExpressionBegin => |opt_ctx| { if (self.eatToken(Token.Id.Keyword_async)) |async_token| { - const async_node = try self.createNode(arena, ast.NodeAsyncAttribute, - ast.NodeAsyncAttribute { + const async_node = try self.createNode(arena, ast.Node.AsyncAttribute, + ast.Node.AsyncAttribute { .base = undefined, .async_token = async_token, .allocator_type = null, @@ -2358,12 +2346,12 @@ pub const Parser = struct { const token = self.getNextToken(); switch (token.id) { Token.Id.LParen => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp, - ast.NodeSuffixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { .base = undefined, .lhs = lhs, - .op = ast.NodeSuffixOp.SuffixOp { - .Call = ast.NodeSuffixOp.CallInfo { + .op = ast.Node.SuffixOp.Op { + .Call = ast.Node.SuffixOp.CallInfo { .params = ArrayList(&ast.Node).init(arena), .async_attr = null, } @@ -2382,11 +2370,11 @@ pub const Parser = struct { continue; }, Token.Id.LBracket => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp, - ast.NodeSuffixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { .base = undefined, .lhs = lhs, - .op = ast.NodeSuffixOp.SuffixOp { + .op = ast.Node.SuffixOp.Op { .ArrayAccess = undefined, }, .rtoken = undefined @@ -2398,12 +2386,12 @@ pub const Parser = struct { continue; }, Token.Id.Period => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, - .op = ast.NodeInfixOp.InfixOp.Period, + .op = ast.Node.InfixOp.Op.Period, .rhs = undefined, } ); @@ -2422,39 +2410,39 @@ pub const Parser = struct { const token = self.getNextToken(); switch (token.id) { Token.Id.IntegerLiteral => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeStringLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token); continue; }, Token.Id.FloatLiteral => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeFloatLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token); continue; }, Token.Id.CharLiteral => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeCharLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token); continue; }, Token.Id.Keyword_undefined => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeUndefinedLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token); continue; }, Token.Id.Keyword_true, Token.Id.Keyword_false => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeBoolLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token); continue; }, Token.Id.Keyword_null => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeNullLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token); continue; }, Token.Id.Keyword_this => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeThisLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token); continue; }, Token.Id.Keyword_var => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeVarType, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token); continue; }, Token.Id.Keyword_unreachable => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeUnreachable, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token); continue; }, Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { @@ -2462,8 +2450,8 @@ pub const Parser = struct { continue; }, Token.Id.LParen => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeGroupedExpression, - ast.NodeGroupedExpression { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression, + ast.Node.GroupedExpression { .base = undefined, .lparen = token, .expr = undefined, @@ -2480,8 +2468,8 @@ pub const Parser = struct { continue; }, Token.Id.Builtin => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeBuiltinCall, - ast.NodeBuiltinCall { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall, + ast.Node.BuiltinCall { .base = undefined, .builtin_token = token, .params = ArrayList(&ast.Node).init(arena), @@ -2499,8 +2487,8 @@ pub const Parser = struct { continue; }, Token.Id.LBracket => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp, - ast.NodePrefixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { .base = undefined, .op_token = token, .op = undefined, @@ -2524,7 +2512,7 @@ pub const Parser = struct { .ContainerKind = ContainerKindCtx { .opt_ctx = opt_ctx, .ltoken = token, - .layout = ast.NodeContainerDecl.Layout.Packed, + .layout = ast.Node.ContainerDecl.Layout.Packed, }, }) catch unreachable; continue; @@ -2534,6 +2522,7 @@ pub const Parser = struct { .ExternType = ExternTypeCtx { .opt_ctx = opt_ctx, .extern_token = token, + .comments = null, }, }) catch unreachable; continue; @@ -2544,7 +2533,7 @@ pub const Parser = struct { .ContainerKind = ContainerKindCtx { .opt_ctx = opt_ctx, .ltoken = token, - .layout = ast.NodeContainerDecl.Layout.Auto, + .layout = ast.Node.ContainerDecl.Layout.Auto, }, }) catch unreachable; continue; @@ -2559,9 +2548,10 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_fn => { - const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto, - ast.NodeFnProto { + const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto, + ast.Node.FnProto { .base = undefined, + .comments = null, .visib_token = null, .name_token = null, .fn_token = token, @@ -2580,9 +2570,10 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto, - ast.NodeFnProto { + const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto, + ast.Node.FnProto { .base = undefined, + .comments = null, .visib_token = null, .name_token = null, .fn_token = undefined, @@ -2607,15 +2598,15 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_asm => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeAsm, - ast.NodeAsm { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Asm, + ast.Node.Asm { .base = undefined, .asm_token = token, .volatile_token = null, .template = undefined, - //.tokens = ArrayList(ast.NodeAsm.AsmToken).init(arena), - .outputs = ArrayList(&ast.NodeAsmOutput).init(arena), - .inputs = ArrayList(&ast.NodeAsmInput).init(arena), + //.tokens = ArrayList(ast.Node.Asm.AsmToken).init(arena), + .outputs = ArrayList(&ast.Node.AsmOutput).init(arena), + .inputs = ArrayList(&ast.Node.AsmInput).init(arena), .cloppers = ArrayList(&ast.Node).init(arena), .rparen = undefined, } @@ -2666,12 +2657,12 @@ pub const Parser = struct { State.ErrorTypeOrSetDecl => |ctx| { if (self.eatToken(Token.Id.LBrace) == null) { - _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.NodeErrorType, ctx.error_token); + _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token); continue; } - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeErrorSetDecl, - ast.NodeErrorSetDecl { + const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ErrorSetDecl, + ast.Node.ErrorSetDecl { .base = undefined, .error_token = ctx.error_token, .decls = ArrayList(&ast.Node).init(arena), @@ -2702,7 +2693,7 @@ pub const Parser = struct { }, State.Identifier => |opt_ctx| { if (self.eatToken(Token.Id.Identifier)) |ident_token| { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeIdentifier, ident_token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token); continue; } @@ -2750,6 +2741,33 @@ pub const Parser = struct { } } + fn eatComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment { + var result: ?&ast.Node.LineComment = null; + while (true) { + if (self.eatToken(Token.Id.LineComment)) |line_comment| { + const node = blk: { + if (result) |comment_node| { + break :blk comment_node; + } else { + const comment_node = try arena.create(ast.Node.LineComment); + *comment_node = ast.Node.LineComment { + .base = ast.Node { + .id = ast.Node.Id.LineComment, + }, + .lines = ArrayList(Token).init(arena), + }; + result = comment_node; + break :blk comment_node; + } + }; + try node.lines.append(line_comment); + continue; + } + break; + } + return result; + } + fn requireSemiColon(node: &const ast.Node) bool { var n = node; while (true) { @@ -2770,7 +2788,7 @@ pub const Parser = struct { ast.Node.Id.LineComment, ast.Node.Id.TestDecl => return false, ast.Node.Id.While => { - const while_node = @fieldParentPtr(ast.NodeWhile, "base", n); + const while_node = @fieldParentPtr(ast.Node.While, "base", n); if (while_node.@"else") |@"else"| { n = @"else".base; continue; @@ -2779,7 +2797,7 @@ pub const Parser = struct { return while_node.body.id != ast.Node.Id.Block; }, ast.Node.Id.For => { - const for_node = @fieldParentPtr(ast.NodeFor, "base", n); + const for_node = @fieldParentPtr(ast.Node.For, "base", n); if (for_node.@"else") |@"else"| { n = @"else".base; continue; @@ -2788,7 +2806,7 @@ pub const Parser = struct { return for_node.body.id != ast.Node.Id.Block; }, ast.Node.Id.If => { - const if_node = @fieldParentPtr(ast.NodeIf, "base", n); + const if_node = @fieldParentPtr(ast.Node.If, "base", n); if (if_node.@"else") |@"else"| { n = @"else".base; continue; @@ -2797,20 +2815,20 @@ pub const Parser = struct { return if_node.body.id != ast.Node.Id.Block; }, ast.Node.Id.Else => { - const else_node = @fieldParentPtr(ast.NodeElse, "base", n); + const else_node = @fieldParentPtr(ast.Node.Else, "base", n); n = else_node.body; continue; }, ast.Node.Id.Defer => { - const defer_node = @fieldParentPtr(ast.NodeDefer, "base", n); + const defer_node = @fieldParentPtr(ast.Node.Defer, "base", n); return defer_node.expr.id != ast.Node.Id.Block; }, ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", n); + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", n); return comptime_node.expr.id != ast.Node.Id.Block; }, ast.Node.Id.Suspend => { - const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", n); + const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", n); if (suspend_node.body) |body| { return body.id != ast.Node.Id.Block; } @@ -2825,11 +2843,11 @@ pub const Parser = struct { fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node { switch (token.id) { Token.Id.StringLiteral => { - return &(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base; + return &(try self.createLiteral(arena, ast.Node.StringLiteral, token)).base; }, Token.Id.MultilineStringLiteralLine => { - const node = try self.createNode(arena, ast.NodeMultilineStringLiteral, - ast.NodeMultilineStringLiteral { + const node = try self.createNode(arena, ast.Node.MultilineStringLiteral, + ast.Node.MultilineStringLiteral { .base = undefined, .tokens = ArrayList(Token).init(arena), } @@ -2856,8 +2874,8 @@ pub const Parser = struct { fn parseBlockExpr(self: &Parser, stack: &ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx, token: &const Token) !bool { switch (token.id) { Token.Id.Keyword_suspend => { - const node = try self.createToCtxNode(arena, ctx, ast.NodeSuspend, - ast.NodeSuspend { + const node = try self.createToCtxNode(arena, ctx, ast.Node.Suspend, + ast.Node.Suspend { .base = undefined, .suspend_token = *token, .payload = null, @@ -2870,8 +2888,8 @@ pub const Parser = struct { return true; }, Token.Id.Keyword_if => { - const node = try self.createToCtxNode(arena, ctx, ast.NodeIf, - ast.NodeIf { + const node = try self.createToCtxNode(arena, ctx, ast.Node.If, + ast.Node.If { .base = undefined, .if_token = *token, .condition = undefined, @@ -2912,18 +2930,18 @@ pub const Parser = struct { return true; }, Token.Id.Keyword_switch => { - const node = try self.createToCtxNode(arena, ctx, ast.NodeSwitch, - ast.NodeSwitch { + const node = try self.createToCtxNode(arena, ctx, ast.Node.Switch, + ast.Node.Switch { .base = undefined, .switch_token = *token, .expr = undefined, - .cases = ArrayList(&ast.NodeSwitchCase).init(arena), + .cases = ArrayList(&ast.Node.SwitchCase).init(arena), .rbrace = undefined, } ); stack.append(State { - .SwitchCaseOrEnd = ListSave(&ast.NodeSwitchCase) { + .SwitchCaseOrEnd = ListSave(&ast.Node.SwitchCase) { .list = &node.cases, .ptr = &node.rbrace, }, @@ -2935,8 +2953,8 @@ pub const Parser = struct { return true; }, Token.Id.Keyword_comptime => { - const node = try self.createToCtxNode(arena, ctx, ast.NodeComptime, - ast.NodeComptime { + const node = try self.createToCtxNode(arena, ctx, ast.Node.Comptime, + ast.Node.Comptime { .base = undefined, .comptime_token = *token, .expr = undefined, @@ -2946,8 +2964,8 @@ pub const Parser = struct { return true; }, Token.Id.LBrace => { - const block = try self.createToCtxNode(arena, ctx, ast.NodeBlock, - ast.NodeBlock { + const block = try self.createToCtxNode(arena, ctx, ast.Node.Block, + ast.Node.Block { .base = undefined, .label = null, .lbrace = *token, @@ -2978,88 +2996,88 @@ pub const Parser = struct { } } - fn tokenIdToAssignment(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp { + fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op { // TODO: We have to cast all cases because of this: // error: expected type '?InfixOp', found '?@TagType(InfixOp)' return switch (*id) { - Token.Id.AmpersandEqual => ast.NodeInfixOp.InfixOp { .AssignBitAnd = void{} }, - Token.Id.AngleBracketAngleBracketLeftEqual => ast.NodeInfixOp.InfixOp { .AssignBitShiftLeft = void{} }, - Token.Id.AngleBracketAngleBracketRightEqual => ast.NodeInfixOp.InfixOp { .AssignBitShiftRight = void{} }, - Token.Id.AsteriskEqual => ast.NodeInfixOp.InfixOp { .AssignTimes = void{} }, - Token.Id.AsteriskPercentEqual => ast.NodeInfixOp.InfixOp { .AssignTimesWarp = void{} }, - Token.Id.CaretEqual => ast.NodeInfixOp.InfixOp { .AssignBitXor = void{} }, - Token.Id.Equal => ast.NodeInfixOp.InfixOp { .Assign = void{} }, - Token.Id.MinusEqual => ast.NodeInfixOp.InfixOp { .AssignMinus = void{} }, - Token.Id.MinusPercentEqual => ast.NodeInfixOp.InfixOp { .AssignMinusWrap = void{} }, - Token.Id.PercentEqual => ast.NodeInfixOp.InfixOp { .AssignMod = void{} }, - Token.Id.PipeEqual => ast.NodeInfixOp.InfixOp { .AssignBitOr = void{} }, - Token.Id.PlusEqual => ast.NodeInfixOp.InfixOp { .AssignPlus = void{} }, - Token.Id.PlusPercentEqual => ast.NodeInfixOp.InfixOp { .AssignPlusWrap = void{} }, - Token.Id.SlashEqual => ast.NodeInfixOp.InfixOp { .AssignDiv = void{} }, + Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = void{} }, + Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = void{} }, + Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = void{} }, + Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = void{} }, + Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = void{} }, + Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = void{} }, + Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = void{} }, + Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = void{} }, + Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = void{} }, + Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = void{} }, + Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = void{} }, + Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = void{} }, + Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = void{} }, + Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = void{} }, else => null, }; } - fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.Keyword_catch => ast.NodeInfixOp.InfixOp { .Catch = null }, - Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp { .UnwrapMaybe = void{} }, + Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null }, + Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} }, else => null, }; } - fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.BangEqual => ast.NodeInfixOp.InfixOp { .BangEqual = void{} }, - Token.Id.EqualEqual => ast.NodeInfixOp.InfixOp { .EqualEqual = void{} }, - Token.Id.AngleBracketLeft => ast.NodeInfixOp.InfixOp { .LessThan = void{} }, - Token.Id.AngleBracketLeftEqual => ast.NodeInfixOp.InfixOp { .LessOrEqual = void{} }, - Token.Id.AngleBracketRight => ast.NodeInfixOp.InfixOp { .GreaterThan = void{} }, - Token.Id.AngleBracketRightEqual => ast.NodeInfixOp.InfixOp { .GreaterOrEqual = void{} }, + Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} }, + Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} }, + Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} }, + Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} }, + Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} }, + Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} }, else => null, }; } - fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.AngleBracketAngleBracketLeft => ast.NodeInfixOp.InfixOp { .BitShiftLeft = void{} }, - Token.Id.AngleBracketAngleBracketRight => ast.NodeInfixOp.InfixOp { .BitShiftRight = void{} }, + Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} }, + Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} }, else => null, }; } - fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.Minus => ast.NodeInfixOp.InfixOp { .Sub = void{} }, - Token.Id.MinusPercent => ast.NodeInfixOp.InfixOp { .SubWrap = void{} }, - Token.Id.Plus => ast.NodeInfixOp.InfixOp { .Add = void{} }, - Token.Id.PlusPercent => ast.NodeInfixOp.InfixOp { .AddWrap = void{} }, - Token.Id.PlusPlus => ast.NodeInfixOp.InfixOp { .ArrayCat = void{} }, + Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} }, + Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} }, + Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} }, + Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} }, + Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} }, else => null, }; } - fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.Slash => ast.NodeInfixOp.InfixOp { .Div = void{} }, - Token.Id.Asterisk => ast.NodeInfixOp.InfixOp { .Mult = void{} }, - Token.Id.AsteriskAsterisk => ast.NodeInfixOp.InfixOp { .ArrayMult = void{} }, - Token.Id.AsteriskPercent => ast.NodeInfixOp.InfixOp { .MultWrap = void{} }, - Token.Id.Percent => ast.NodeInfixOp.InfixOp { .Mod = void{} }, - Token.Id.PipePipe => ast.NodeInfixOp.InfixOp { .MergeErrorSets = void{} }, + Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} }, + Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} }, + Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} }, + Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} }, + Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} }, + Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} }, else => null, }; } - fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.NodePrefixOp.PrefixOp { + fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { return switch (id) { - Token.Id.Bang => ast.NodePrefixOp.PrefixOp { .BoolNot = void{} }, - Token.Id.Tilde => ast.NodePrefixOp.PrefixOp { .BitNot = void{} }, - Token.Id.Minus => ast.NodePrefixOp.PrefixOp { .Negation = void{} }, - Token.Id.MinusPercent => ast.NodePrefixOp.PrefixOp { .NegationWrap = void{} }, - Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.NodePrefixOp.PrefixOp { .Deref = void{} }, - Token.Id.Ampersand => ast.NodePrefixOp.PrefixOp { - .AddrOf = ast.NodePrefixOp.AddrOfInfo { + Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} }, + Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} }, + Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} }, + Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} }, + Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} }, + Token.Id.Ampersand => ast.Node.PrefixOp.Op { + .AddrOf = ast.Node.PrefixOp.AddrOfInfo { .align_expr = null, .bit_offset_start_token = null, .bit_offset_end_token = null, @@ -3067,10 +3085,10 @@ pub const Parser = struct { .volatile_token = null, }, }, - Token.Id.QuestionMark => ast.NodePrefixOp.PrefixOp { .MaybeType = void{} }, - Token.Id.QuestionMarkQuestionMark => ast.NodePrefixOp.PrefixOp { .UnwrapMaybe = void{} }, - Token.Id.Keyword_await => ast.NodePrefixOp.PrefixOp { .Await = void{} }, - Token.Id.Keyword_try => ast.NodePrefixOp.PrefixOp { .Try = void{ } }, + Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} }, + Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} }, + Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} }, + Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } }, else => null, }; } @@ -3080,11 +3098,7 @@ pub const Parser = struct { *node = *init_to; node.base = blk: { const id = ast.Node.typeToId(T); - if (self.pending_line_comment_node) |comment_node| { - self.pending_line_comment_node = null; - break :blk ast.Node {.id = id, .comment = comment_node}; - } - break :blk ast.Node {.id = id, .comment = null }; + break :blk ast.Node {.id = id}; }; return node; @@ -3183,7 +3197,7 @@ pub const Parser = struct { indent: usize, }; - pub fn renderAst(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void { + pub fn renderAst(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { var stack = self.initUtilityArrayList(RenderAstFrame); defer self.deinitUtilityArrayList(stack); @@ -3215,14 +3229,14 @@ pub const Parser = struct { ParamDecl: &ast.Node, Text: []const u8, Expression: &ast.Node, - VarDecl: &ast.NodeVarDecl, + VarDecl: &ast.Node.VarDecl, Statement: &ast.Node, - FieldInitializer: &ast.NodeFieldInitializer, + FieldInitializer: &ast.Node.FieldInitializer, PrintIndent, Indent: usize, }; - pub fn renderSource(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void { + pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { var stack = self.initUtilityArrayList(RenderState); defer self.deinitUtilityArrayList(stack); @@ -3256,7 +3270,8 @@ pub const Parser = struct { RenderState.TopLevelDecl => |decl| { switch (decl.id) { ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl); + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); + try self.renderComments(stream, fn_proto, indent); if (fn_proto.body_node) |body_node| { stack.append(RenderState { .Expression = body_node}) catch unreachable; @@ -3268,7 +3283,7 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = decl }); }, ast.Node.Id.Use => { - const use_decl = @fieldParentPtr(ast.NodeUse, "base", decl); + const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); if (use_decl.visib_token) |visib_token| { try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token)); } @@ -3277,18 +3292,19 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = use_decl.expr }); }, ast.Node.Id.VarDecl => { - const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl); + const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); try stack.append(RenderState { .VarDecl = var_decl}); }, ast.Node.Id.TestDecl => { - const test_decl = @fieldParentPtr(ast.NodeTestDecl, "base", decl); + const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); + try self.renderComments(stream, test_decl, indent); try stream.print("test "); try stack.append(RenderState { .Expression = test_decl.body_node }); try stack.append(RenderState { .Text = " " }); try stack.append(RenderState { .Expression = test_decl.name }); }, ast.Node.Id.StructField => { - const field = @fieldParentPtr(ast.NodeStructField, "base", decl); + const field = @fieldParentPtr(ast.Node.StructField, "base", decl); if (field.visib_token) |visib_token| { try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token)); } @@ -3296,7 +3312,7 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = field.type_expr}); }, ast.Node.Id.UnionTag => { - const tag = @fieldParentPtr(ast.NodeUnionTag, "base", decl); + const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); if (tag.type_expr) |type_expr| { @@ -3305,7 +3321,7 @@ pub const Parser = struct { } }, ast.Node.Id.EnumTag => { - const tag = @fieldParentPtr(ast.NodeEnumTag, "base", decl); + const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); if (tag.value) |value| { @@ -3324,6 +3340,7 @@ pub const Parser = struct { }, RenderState.FieldInitializer => |field_init| { + //TODO try self.renderComments(stream, field_init, indent); try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token)); try stream.print(" = "); try stack.append(RenderState { .Expression = field_init.expr }); @@ -3369,7 +3386,8 @@ pub const Parser = struct { }, RenderState.ParamDecl => |base| { - const param_decl = @fieldParentPtr(ast.NodeParamDecl, "base", base); + const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); + // TODO try self.renderComments(stream, param_decl, indent); if (param_decl.comptime_token) |comptime_token| { try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token)); } @@ -3390,11 +3408,11 @@ pub const Parser = struct { }, RenderState.Expression => |base| switch (base.id) { ast.Node.Id.Identifier => { - const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base); + const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(identifier.token)); }, ast.Node.Id.Block => { - const block = @fieldParentPtr(ast.NodeBlock, "base", base); + const block = @fieldParentPtr(ast.Node.Block, "base", base); if (block.label) |label| { try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); } @@ -3430,17 +3448,17 @@ pub const Parser = struct { } }, ast.Node.Id.Defer => { - const defer_node = @fieldParentPtr(ast.NodeDefer, "base", base); + const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); try stream.print("{} ", self.tokenizer.getTokenSlice(defer_node.defer_token)); try stack.append(RenderState { .Expression = defer_node.expr }); }, ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", base); + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_node.comptime_token)); try stack.append(RenderState { .Expression = comptime_node.expr }); }, ast.Node.Id.AsyncAttribute => { - const async_attr = @fieldParentPtr(ast.NodeAsyncAttribute, "base", base); + const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(async_attr.async_token)); if (async_attr.allocator_type) |allocator_type| { @@ -3450,7 +3468,7 @@ pub const Parser = struct { } }, ast.Node.Id.Suspend => { - const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", base); + const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token)); if (suspend_node.body) |body| { @@ -3464,10 +3482,10 @@ pub const Parser = struct { } }, ast.Node.Id.InfixOp => { - const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base); + const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); try stack.append(RenderState { .Expression = prefix_op_node.rhs }); - if (prefix_op_node.op == ast.NodeInfixOp.InfixOp.Catch) { + if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) { if (prefix_op_node.op.Catch) |payload| { try stack.append(RenderState { .Text = " " }); try stack.append(RenderState { .Expression = payload }); @@ -3475,49 +3493,49 @@ pub const Parser = struct { try stack.append(RenderState { .Text = " catch " }); } else { const text = switch (prefix_op_node.op) { - ast.NodeInfixOp.InfixOp.Add => " + ", - ast.NodeInfixOp.InfixOp.AddWrap => " +% ", - ast.NodeInfixOp.InfixOp.ArrayCat => " ++ ", - ast.NodeInfixOp.InfixOp.ArrayMult => " ** ", - ast.NodeInfixOp.InfixOp.Assign => " = ", - ast.NodeInfixOp.InfixOp.AssignBitAnd => " &= ", - ast.NodeInfixOp.InfixOp.AssignBitOr => " |= ", - ast.NodeInfixOp.InfixOp.AssignBitShiftLeft => " <<= ", - ast.NodeInfixOp.InfixOp.AssignBitShiftRight => " >>= ", - ast.NodeInfixOp.InfixOp.AssignBitXor => " ^= ", - ast.NodeInfixOp.InfixOp.AssignDiv => " /= ", - ast.NodeInfixOp.InfixOp.AssignMinus => " -= ", - ast.NodeInfixOp.InfixOp.AssignMinusWrap => " -%= ", - ast.NodeInfixOp.InfixOp.AssignMod => " %= ", - ast.NodeInfixOp.InfixOp.AssignPlus => " += ", - ast.NodeInfixOp.InfixOp.AssignPlusWrap => " +%= ", - ast.NodeInfixOp.InfixOp.AssignTimes => " *= ", - ast.NodeInfixOp.InfixOp.AssignTimesWarp => " *%= ", - ast.NodeInfixOp.InfixOp.BangEqual => " != ", - ast.NodeInfixOp.InfixOp.BitAnd => " & ", - ast.NodeInfixOp.InfixOp.BitOr => " | ", - ast.NodeInfixOp.InfixOp.BitShiftLeft => " << ", - ast.NodeInfixOp.InfixOp.BitShiftRight => " >> ", - ast.NodeInfixOp.InfixOp.BitXor => " ^ ", - ast.NodeInfixOp.InfixOp.BoolAnd => " and ", - ast.NodeInfixOp.InfixOp.BoolOr => " or ", - ast.NodeInfixOp.InfixOp.Div => " / ", - ast.NodeInfixOp.InfixOp.EqualEqual => " == ", - ast.NodeInfixOp.InfixOp.ErrorUnion => "!", - ast.NodeInfixOp.InfixOp.GreaterOrEqual => " >= ", - ast.NodeInfixOp.InfixOp.GreaterThan => " > ", - ast.NodeInfixOp.InfixOp.LessOrEqual => " <= ", - ast.NodeInfixOp.InfixOp.LessThan => " < ", - ast.NodeInfixOp.InfixOp.MergeErrorSets => " || ", - ast.NodeInfixOp.InfixOp.Mod => " % ", - ast.NodeInfixOp.InfixOp.Mult => " * ", - ast.NodeInfixOp.InfixOp.MultWrap => " *% ", - ast.NodeInfixOp.InfixOp.Period => ".", - ast.NodeInfixOp.InfixOp.Sub => " - ", - ast.NodeInfixOp.InfixOp.SubWrap => " -% ", - ast.NodeInfixOp.InfixOp.UnwrapMaybe => " ?? ", - ast.NodeInfixOp.InfixOp.Range => " ... ", - ast.NodeInfixOp.InfixOp.Catch => unreachable, + ast.Node.InfixOp.Op.Add => " + ", + ast.Node.InfixOp.Op.AddWrap => " +% ", + ast.Node.InfixOp.Op.ArrayCat => " ++ ", + ast.Node.InfixOp.Op.ArrayMult => " ** ", + ast.Node.InfixOp.Op.Assign => " = ", + ast.Node.InfixOp.Op.AssignBitAnd => " &= ", + ast.Node.InfixOp.Op.AssignBitOr => " |= ", + ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ", + ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ", + ast.Node.InfixOp.Op.AssignBitXor => " ^= ", + ast.Node.InfixOp.Op.AssignDiv => " /= ", + ast.Node.InfixOp.Op.AssignMinus => " -= ", + ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ", + ast.Node.InfixOp.Op.AssignMod => " %= ", + ast.Node.InfixOp.Op.AssignPlus => " += ", + ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ", + ast.Node.InfixOp.Op.AssignTimes => " *= ", + ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ", + ast.Node.InfixOp.Op.BangEqual => " != ", + ast.Node.InfixOp.Op.BitAnd => " & ", + ast.Node.InfixOp.Op.BitOr => " | ", + ast.Node.InfixOp.Op.BitShiftLeft => " << ", + ast.Node.InfixOp.Op.BitShiftRight => " >> ", + ast.Node.InfixOp.Op.BitXor => " ^ ", + ast.Node.InfixOp.Op.BoolAnd => " and ", + ast.Node.InfixOp.Op.BoolOr => " or ", + ast.Node.InfixOp.Op.Div => " / ", + ast.Node.InfixOp.Op.EqualEqual => " == ", + ast.Node.InfixOp.Op.ErrorUnion => "!", + ast.Node.InfixOp.Op.GreaterOrEqual => " >= ", + ast.Node.InfixOp.Op.GreaterThan => " > ", + ast.Node.InfixOp.Op.LessOrEqual => " <= ", + ast.Node.InfixOp.Op.LessThan => " < ", + ast.Node.InfixOp.Op.MergeErrorSets => " || ", + ast.Node.InfixOp.Op.Mod => " % ", + ast.Node.InfixOp.Op.Mult => " * ", + ast.Node.InfixOp.Op.MultWrap => " *% ", + ast.Node.InfixOp.Op.Period => ".", + ast.Node.InfixOp.Op.Sub => " - ", + ast.Node.InfixOp.Op.SubWrap => " -% ", + ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ", + ast.Node.InfixOp.Op.Range => " ... ", + ast.Node.InfixOp.Op.Catch => unreachable, }; try stack.append(RenderState { .Text = text }); @@ -3525,10 +3543,10 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = prefix_op_node.lhs }); }, ast.Node.Id.PrefixOp => { - const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base); + const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); try stack.append(RenderState { .Expression = prefix_op_node.rhs }); switch (prefix_op_node.op) { - ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| { + ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { try stream.write("&"); if (addr_of_info.volatile_token != null) { try stack.append(RenderState { .Text = "volatile "}); @@ -3542,7 +3560,7 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = align_expr}); } }, - ast.NodePrefixOp.PrefixOp.SliceType => |addr_of_info| { + ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { try stream.write("[]"); if (addr_of_info.volatile_token != null) { try stack.append(RenderState { .Text = "volatile "}); @@ -3556,29 +3574,29 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = align_expr}); } }, - ast.NodePrefixOp.PrefixOp.ArrayType => |array_index| { + ast.Node.PrefixOp.Op.ArrayType => |array_index| { try stack.append(RenderState { .Text = "]"}); try stack.append(RenderState { .Expression = array_index}); try stack.append(RenderState { .Text = "["}); }, - ast.NodePrefixOp.PrefixOp.BitNot => try stream.write("~"), - ast.NodePrefixOp.PrefixOp.BoolNot => try stream.write("!"), - ast.NodePrefixOp.PrefixOp.Deref => try stream.write("*"), - ast.NodePrefixOp.PrefixOp.Negation => try stream.write("-"), - ast.NodePrefixOp.PrefixOp.NegationWrap => try stream.write("-%"), - ast.NodePrefixOp.PrefixOp.Try => try stream.write("try "), - ast.NodePrefixOp.PrefixOp.UnwrapMaybe => try stream.write("??"), - ast.NodePrefixOp.PrefixOp.MaybeType => try stream.write("?"), - ast.NodePrefixOp.PrefixOp.Await => try stream.write("await "), - ast.NodePrefixOp.PrefixOp.Cancel => try stream.write("cancel "), - ast.NodePrefixOp.PrefixOp.Resume => try stream.write("resume "), + ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), + ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), + ast.Node.PrefixOp.Op.Deref => try stream.write("*"), + ast.Node.PrefixOp.Op.Negation => try stream.write("-"), + ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"), + ast.Node.PrefixOp.Op.Try => try stream.write("try "), + ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"), + ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"), + ast.Node.PrefixOp.Op.Await => try stream.write("await "), + ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "), + ast.Node.PrefixOp.Op.Resume => try stream.write("resume "), } }, ast.Node.Id.SuffixOp => { - const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", base); + const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base); switch (suffix_op.op) { - ast.NodeSuffixOp.SuffixOp.Call => |call_info| { + ast.Node.SuffixOp.Op.Call => |call_info| { try stack.append(RenderState { .Text = ")"}); var i = call_info.params.len; while (i != 0) { @@ -3597,13 +3615,13 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = &async_attr.base }); } }, - ast.NodeSuffixOp.SuffixOp.ArrayAccess => |index_expr| { + ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { try stack.append(RenderState { .Text = "]"}); try stack.append(RenderState { .Expression = index_expr}); try stack.append(RenderState { .Text = "["}); try stack.append(RenderState { .Expression = suffix_op.lhs }); }, - ast.NodeSuffixOp.SuffixOp.Slice => |range| { + ast.Node.SuffixOp.Op.Slice => |range| { try stack.append(RenderState { .Text = "]"}); if (range.end) |end| { try stack.append(RenderState { .Expression = end}); @@ -3613,7 +3631,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "["}); try stack.append(RenderState { .Expression = suffix_op.lhs }); }, - ast.NodeSuffixOp.SuffixOp.StructInitializer => |field_inits| { + ast.Node.SuffixOp.Op.StructInitializer => |field_inits| { if (field_inits.len == 0) { try stack.append(RenderState { .Text = "{}" }); try stack.append(RenderState { .Expression = suffix_op.lhs }); @@ -3634,7 +3652,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = " {\n"}); try stack.append(RenderState { .Expression = suffix_op.lhs }); }, - ast.NodeSuffixOp.SuffixOp.ArrayInitializer => |exprs| { + ast.Node.SuffixOp.Op.ArrayInitializer => |exprs| { if (exprs.len == 0) { try stack.append(RenderState { .Text = "{}" }); try stack.append(RenderState { .Expression = suffix_op.lhs }); @@ -3658,7 +3676,7 @@ pub const Parser = struct { } }, ast.Node.Id.ControlFlowExpression => { - const flow_expr = @fieldParentPtr(ast.NodeControlFlowExpression, "base", base); + const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); if (flow_expr.rhs) |rhs| { try stack.append(RenderState { .Expression = rhs }); @@ -3666,34 +3684,34 @@ pub const Parser = struct { } switch (flow_expr.kind) { - ast.NodeControlFlowExpression.Kind.Break => |maybe_label| { + ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { try stream.print("break"); if (maybe_label) |label| { try stream.print(" :"); try stack.append(RenderState { .Expression = label }); } }, - ast.NodeControlFlowExpression.Kind.Continue => |maybe_label| { + ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { try stream.print("continue"); if (maybe_label) |label| { try stream.print(" :"); try stack.append(RenderState { .Expression = label }); } }, - ast.NodeControlFlowExpression.Kind.Return => { + ast.Node.ControlFlowExpression.Kind.Return => { try stream.print("return"); }, } }, ast.Node.Id.Payload => { - const payload = @fieldParentPtr(ast.NodePayload, "base", base); + const payload = @fieldParentPtr(ast.Node.Payload, "base", base); try stack.append(RenderState { .Text = "|"}); try stack.append(RenderState { .Expression = payload.error_symbol }); try stack.append(RenderState { .Text = "|"}); }, ast.Node.Id.PointerPayload => { - const payload = @fieldParentPtr(ast.NodePointerPayload, "base", base); + const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); try stack.append(RenderState { .Text = "|"}); try stack.append(RenderState { .Expression = payload.value_symbol }); @@ -3704,7 +3722,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "|"}); }, ast.Node.Id.PointerIndexPayload => { - const payload = @fieldParentPtr(ast.NodePointerIndexPayload, "base", base); + const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); try stack.append(RenderState { .Text = "|"}); if (payload.index_symbol) |index_symbol| { @@ -3721,69 +3739,69 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "|"}); }, ast.Node.Id.GroupedExpression => { - const grouped_expr = @fieldParentPtr(ast.NodeGroupedExpression, "base", base); + const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); try stack.append(RenderState { .Text = ")"}); try stack.append(RenderState { .Expression = grouped_expr.expr }); try stack.append(RenderState { .Text = "("}); }, ast.Node.Id.FieldInitializer => { - const field_init = @fieldParentPtr(ast.NodeFieldInitializer, "base", base); + const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token)); try stack.append(RenderState { .Expression = field_init.expr }); }, ast.Node.Id.IntegerLiteral => { - const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base); + const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token)); }, ast.Node.Id.FloatLiteral => { - const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base); + const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token)); }, ast.Node.Id.StringLiteral => { - const string_literal = @fieldParentPtr(ast.NodeStringLiteral, "base", base); + const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token)); }, ast.Node.Id.CharLiteral => { - const char_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base); + const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(char_literal.token)); }, ast.Node.Id.BoolLiteral => { - const bool_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base); + const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(bool_literal.token)); }, ast.Node.Id.NullLiteral => { - const null_literal = @fieldParentPtr(ast.NodeNullLiteral, "base", base); + const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(null_literal.token)); }, ast.Node.Id.ThisLiteral => { - const this_literal = @fieldParentPtr(ast.NodeThisLiteral, "base", base); + const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(this_literal.token)); }, ast.Node.Id.Unreachable => { - const unreachable_node = @fieldParentPtr(ast.NodeUnreachable, "base", base); + const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(unreachable_node.token)); }, ast.Node.Id.ErrorType => { - const error_type = @fieldParentPtr(ast.NodeErrorType, "base", base); + const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(error_type.token)); }, ast.Node.Id.VarType => { - const var_type = @fieldParentPtr(ast.NodeVarType, "base", base); + const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(var_type.token)); }, ast.Node.Id.ContainerDecl => { - const container_decl = @fieldParentPtr(ast.NodeContainerDecl, "base", base); + const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); switch (container_decl.layout) { - ast.NodeContainerDecl.Layout.Packed => try stream.print("packed "), - ast.NodeContainerDecl.Layout.Extern => try stream.print("extern "), - ast.NodeContainerDecl.Layout.Auto => { }, + ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "), + ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "), + ast.Node.ContainerDecl.Layout.Auto => { }, } switch (container_decl.kind) { - ast.NodeContainerDecl.Kind.Struct => try stream.print("struct"), - ast.NodeContainerDecl.Kind.Enum => try stream.print("enum"), - ast.NodeContainerDecl.Kind.Union => try stream.print("union"), + ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"), + ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"), + ast.Node.ContainerDecl.Kind.Union => try stream.print("union"), } try stack.append(RenderState { .Text = "}"}); @@ -3823,9 +3841,9 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "{"}); switch (container_decl.init_arg_expr) { - ast.NodeContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}), - ast.NodeContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}), - ast.NodeContainerDecl.InitArg.Type => |type_expr| { + ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}), + ast.Node.ContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}), + ast.Node.ContainerDecl.InitArg.Type => |type_expr| { try stack.append(RenderState { .Text = ") "}); try stack.append(RenderState { .Expression = type_expr}); try stack.append(RenderState { .Text = "("}); @@ -3833,7 +3851,7 @@ pub const Parser = struct { } }, ast.Node.Id.ErrorSetDecl => { - const err_set_decl = @fieldParentPtr(ast.NodeErrorSetDecl, "base", base); + const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); try stream.print("error "); try stack.append(RenderState { .Text = "}"}); @@ -3866,7 +3884,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "{"}); }, ast.Node.Id.MultilineStringLiteral => { - const multiline_str_literal = @fieldParentPtr(ast.NodeMultilineStringLiteral, "base", base); + const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); try stream.print("\n"); var i : usize = 0; @@ -3878,11 +3896,11 @@ pub const Parser = struct { try stream.writeByteNTimes(' ', indent + indent_delta); }, ast.Node.Id.UndefinedLiteral => { - const undefined_literal = @fieldParentPtr(ast.NodeUndefinedLiteral, "base", base); + const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(undefined_literal.token)); }, ast.Node.Id.BuiltinCall => { - const builtin_call = @fieldParentPtr(ast.NodeBuiltinCall, "base", base); + const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token)); try stack.append(RenderState { .Text = ")"}); var i = builtin_call.params.len; @@ -3896,13 +3914,13 @@ pub const Parser = struct { } }, ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", base); + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); switch (fn_proto.return_type) { - ast.NodeFnProto.ReturnType.Explicit => |node| { + ast.Node.FnProto.ReturnType.Explicit => |node| { try stack.append(RenderState { .Expression = node}); }, - ast.NodeFnProto.ReturnType.InferErrorSet => |node| { + ast.Node.FnProto.ReturnType.InferErrorSet => |node| { try stack.append(RenderState { .Expression = node}); try stack.append(RenderState { .Text = "!"}); }, @@ -3960,7 +3978,7 @@ pub const Parser = struct { }, ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"), ast.Node.Id.Switch => { - const switch_node = @fieldParentPtr(ast.NodeSwitch, "base", base); + const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token)); try stack.append(RenderState { .Text = "}"}); @@ -3994,7 +4012,7 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = switch_node.expr }); }, ast.Node.Id.SwitchCase => { - const switch_case = @fieldParentPtr(ast.NodeSwitchCase, "base", base); + const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); try stack.append(RenderState { .Expression = switch_case.expr }); if (switch_case.payload) |payload| { @@ -4016,11 +4034,11 @@ pub const Parser = struct { } }, ast.Node.Id.SwitchElse => { - const switch_else = @fieldParentPtr(ast.NodeSwitchElse, "base", base); + const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token)); }, ast.Node.Id.Else => { - const else_node = @fieldParentPtr(ast.NodeElse, "base", base); + const else_node = @fieldParentPtr(ast.Node.Else, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(else_node.else_token)); switch (else_node.body.id) { @@ -4045,7 +4063,7 @@ pub const Parser = struct { } }, ast.Node.Id.While => { - const while_node = @fieldParentPtr(ast.NodeWhile, "base", base); + const while_node = @fieldParentPtr(ast.Node.While, "base", base); if (while_node.label) |label| { try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); } @@ -4095,7 +4113,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "(" }); }, ast.Node.Id.For => { - const for_node = @fieldParentPtr(ast.NodeFor, "base", base); + const for_node = @fieldParentPtr(ast.Node.For, "base", base); if (for_node.label) |label| { try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); } @@ -4138,7 +4156,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "(" }); }, ast.Node.Id.If => { - const if_node = @fieldParentPtr(ast.NodeIf, "base", base); + const if_node = @fieldParentPtr(ast.Node.If, "base", base); try stream.print("{} ", self.tokenizer.getTokenSlice(if_node.if_token)); switch (if_node.body.id) { @@ -4185,7 +4203,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "(" }); }, ast.Node.Id.Asm => { - const asm_node = @fieldParentPtr(ast.NodeAsm, "base", base); + const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); try stream.print("{} ", self.tokenizer.getTokenSlice(asm_node.asm_token)); if (asm_node.volatile_token) |volatile_token| { @@ -4272,7 +4290,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "(" }); }, ast.Node.Id.AsmInput => { - const asm_input = @fieldParentPtr(ast.NodeAsmInput, "base", base); + const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); try stack.append(RenderState { .Text = ")"}); try stack.append(RenderState { .Expression = asm_input.expr}); @@ -4283,14 +4301,14 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "["}); }, ast.Node.Id.AsmOutput => { - const asm_output = @fieldParentPtr(ast.NodeAsmOutput, "base", base); + const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); try stack.append(RenderState { .Text = ")"}); switch (asm_output.kind) { - ast.NodeAsmOutput.Kind.Variable => |variable_name| { + ast.Node.AsmOutput.Kind.Variable => |variable_name| { try stack.append(RenderState { .Expression = &variable_name.base}); }, - ast.NodeAsmOutput.Kind.Return => |return_type| { + ast.Node.AsmOutput.Kind.Return => |return_type| { try stack.append(RenderState { .Expression = return_type}); try stack.append(RenderState { .Text = "-> "}); }, @@ -4312,15 +4330,10 @@ pub const Parser = struct { ast.Node.Id.ParamDecl => unreachable, }, RenderState.Statement => |base| { - if (base.comment) |comment| { - for (comment.lines.toSliceConst()) |line_token| { - try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token)); - try stream.writeByteNTimes(' ', indent); - } - } switch (base.id) { ast.Node.Id.VarDecl => { - const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base); + const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); + try self.renderComments(stream, var_decl, indent); try stack.append(RenderState { .VarDecl = var_decl}); }, else => { @@ -4337,6 +4350,14 @@ pub const Parser = struct { } } + fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void { + const comment = node.comments ?? return; + for (comment.lines.toSliceConst()) |line_token| { + try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token)); + try stream.writeByteNTimes(' ', indent); + } + } + fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) { const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T); self.utility_bytes = self.util_allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count); @@ -4411,6 +4432,14 @@ fn testCanonical(source: []const u8) !void { } } +test "zig fmt: preserve top level comments" { + try testCanonical( + \\// top level comment + \\test "hi" {} + \\ + ); +} + test "zig fmt: get stdout or fail" { try testCanonical( \\const std = @import("std"); -- cgit v1.2.3 From b229aff34aeef059823c1ab7dfd3a1f5c4445d9e Mon Sep 17 00:00:00 2001 From: Harry Eakins Date: Thu, 19 Apr 2018 23:42:52 -0700 Subject: Readability improvements and bug-fix to std/crypto/throughput_test.zig --- std/crypto/throughput_test.zig | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig index 60610411b5..c76f19e120 100644 --- a/std/crypto/throughput_test.zig +++ b/std/crypto/throughput_test.zig @@ -7,16 +7,15 @@ // zig build-exe --release-fast --library c throughput_test.zig // ./throughput_test // ``` -const HashFunction = @import("md5.zig").Md5; -const BytesToHash = 1024 * Mb; const std = @import("std"); - const c = @cImport({ @cInclude("time.h"); }); +const HashFunction = @import("md5.zig").Md5; -const Mb = 1024 * 1024; +const MB = 1024 * 1024; +const BytesToHash = 1024 * MB; pub fn main() !void { var stdout_file = try std.io.getStdOut(); @@ -35,9 +34,9 @@ pub fn main() !void { } const end = c.clock(); - const elapsed_s = f64((end - start) * c.CLOCKS_PER_SEC) / 1000000; + const elapsed_s = f64(end - start) / f64(c.CLOCKS_PER_SEC); const throughput = u64(BytesToHash / elapsed_s); try stdout.print("{}: ", @typeName(HashFunction)); - try stdout.print("{} Mb/s\n", throughput); + try stdout.print("{} MB/s\n", throughput / (1 * MB)); } -- cgit v1.2.3 From eef4bbb65f40f5101aeb8e89bb6e6f06343cc2d0 Mon Sep 17 00:00:00 2001 From: Harry Eakins Date: Thu, 19 Apr 2018 23:54:18 -0700 Subject: Changed all MB to MiB --- std/crypto/throughput_test.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig index c76f19e120..d086b555e9 100644 --- a/std/crypto/throughput_test.zig +++ b/std/crypto/throughput_test.zig @@ -14,8 +14,8 @@ const c = @cImport({ }); const HashFunction = @import("md5.zig").Md5; -const MB = 1024 * 1024; -const BytesToHash = 1024 * MB; +const MiB = 1024 * 1024; +const BytesToHash = 1024 * MiB; pub fn main() !void { var stdout_file = try std.io.getStdOut(); @@ -37,6 +37,5 @@ pub fn main() !void { const elapsed_s = f64(end - start) / f64(c.CLOCKS_PER_SEC); const throughput = u64(BytesToHash / elapsed_s); - try stdout.print("{}: ", @typeName(HashFunction)); - try stdout.print("{} MB/s\n", throughput / (1 * MB)); + try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB)); } -- cgit v1.2.3 From 1098545e475d0147d3f8b3031ed116be25c8e2bf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Apr 2018 19:21:31 -0400 Subject: std.zig.parser: remove unused field --- std/zig/parser.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 017d293491..7f45cce28b 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -18,7 +18,6 @@ pub const Parser = struct { put_back_tokens: [2]Token, put_back_count: usize, source_file_name: []const u8, - pending_line_comment_node: ?&ast.Node.LineComment, pub const Tree = struct { root_node: &ast.Node.Root, @@ -44,7 +43,6 @@ pub const Parser = struct { .put_back_count = 0, .source_file_name = source_file_name, .utility_bytes = []align(utility_bytes_align) u8{}, - .pending_line_comment_node = null, }; } -- cgit v1.2.3 From c4840d78fb0a1a5018e5d66cb7c5e76a080783c2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 21 Apr 2018 02:00:14 -0400 Subject: add test case for #936 --- test/compile_errors.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 6ac73d18a2..2c4e35c562 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,11 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("invalid field access in comptime", + \\comptime { var x = doesnt_exist.whatever; } + , + ".tmp_source.zig:1:20: error: use of undeclared identifier 'doesnt_exist'"); + cases.add("suspend inside suspend block", \\const std = @import("std"); \\ -- cgit v1.2.3 From a1083b019ccc2a22cbfcbe75477895b4a0de4f72 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Sat, 21 Apr 2018 20:41:49 -0500 Subject: Added DirectAllocator support for alignments > os.page_size on posix systems --- std/heap.zig | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/std/heap.zig b/std/heap.zig index ca6736af1e..b3a1e6bf27 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -79,19 +79,38 @@ pub const DirectAllocator = struct { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { - assert(alignment <= os.page_size); const p = os.posix; - const addr = p.mmap(null, n, p.PROT_READ|p.PROT_WRITE, - p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0); - if (addr == p.MAP_FAILED) { - return error.OutOfMemory; - } - return @intToPtr(&u8, addr)[0..n]; + const alloc_size = if(alignment <= os.page_size) n else n + alignment; + const addr = p.mmap(null, alloc_size, p.PROT_READ|p.PROT_WRITE, + p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0); + if(addr == p.MAP_FAILED) return error.OutOfMemory; + + if(alloc_size == n) return @intToPtr(&u8, addr)[0..n]; + + var aligned_addr = addr & ~usize(alignment - 1); + aligned_addr += alignment; + + //We can unmap the unused portions of our mmap, but we must only + // pass munmap bytes that exist outside our allocated pages or it + // will happily eat us too + + //Since alignment > page_size, we are by definition on a page boundry + const unused_start = addr; + const unused_len = aligned_addr - 1 - unused_start; + + var err = p.munmap(@intToPtr(&u8, unused_start), unused_len); + debug.assert(p.getErrno(err) == 0); + + //It is impossible that there is an unoccupied page at the top of our + // mmap. + + return @intToPtr(&u8, aligned_addr)[0..n]; }, Os.windows => { const amt = n + alignment + @sizeOf(usize); const heap_handle = self.heap_handle ?? blk: { - const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) ?? return error.OutOfMemory; + const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) + ?? return error.OutOfMemory; self.heap_handle = hh; break :blk hh; }; @@ -322,6 +341,7 @@ test "DirectAllocator" { const allocator = &direct_allocator.allocator; try testAllocator(allocator); + try testAllocatorLargeAlignment(allocator); } test "ArenaAllocator" { @@ -332,6 +352,7 @@ test "ArenaAllocator" { defer arena_allocator.deinit(); try testAllocator(&arena_allocator.allocator); + try testAllocatorLargeAlignment(&arena_allocator.allocator); } var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined; @@ -339,6 +360,7 @@ test "FixedBufferAllocator" { var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); try testAllocator(&fixed_buffer_allocator.allocator); + try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); } fn testAllocator(allocator: &mem.Allocator) !void { @@ -360,3 +382,32 @@ fn testAllocator(allocator: &mem.Allocator) !void { allocator.free(slice); } + +fn testAllocatorLargeAlignment(allocator: &mem.Allocator) mem.Allocator.Error!void { + //Maybe a platform's page_size is actually the same as or + // very near usize? + if(os.page_size << 2 > @maxValue(usize)) return; + + const USizeShift = @IntType(false, std.math.log2(usize.bit_count)); + const large_align = u29(os.page_size << 2); + + var align_mask: usize = undefined; + _ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(large_align)), &align_mask); + + var slice = try allocator.allocFn(allocator, 500, large_align); + debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); + + slice = try allocator.reallocFn(allocator, slice, 100, large_align); + debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); + + slice = try allocator.reallocFn(allocator, slice, 5000, large_align); + debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); + + slice = try allocator.reallocFn(allocator, slice, 10, large_align); + debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); + + slice = try allocator.reallocFn(allocator, slice, 20000, large_align); + debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); + + allocator.free(slice); +} -- cgit v1.2.3 From a3e9ae8f74050d8a28553defc92e1a61b09f34c8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 12:33:02 -0400 Subject: travis: use encrypted env vars for s3 credentials --- .travis.yml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index c5299e914e..731202f5f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,22 @@ sudo: required services: - - docker +- docker os: - - linux - - osx +- linux +- osx dist: trusty osx_image: xcode8.3 language: cpp before_install: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi +- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi install: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi +- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi script: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi +- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi +env: + global: + - secure: QmJ+eLOxj3Irl5SHxt6lQvrj7++1AIz8bYri6RScAQGHQPIztkmbpBjAkpFgYaWPkZ04ROtamFXdS7oHtJHSECesgPoqM/CHIychQkgpDq30+TsFyYbBpDGHY+N6r2WnQTvg+9EuAp6P365us6qFS0D5zQ3P40c56uMbazFu3J4W1HZP+pLWlLjEXaN88ePhHWqNZyvwGMkLpYl3ghcrE9H4vGZQ7jenRW4UmskLEkuhUPJbQiow3Td8arJiRmLVISzWqneqNraLUpGyUVr4F3Rbjzacfoo3r9ZZynhY0mFsEye82x6TMGgH2xsNGkd91zpQuckWUT+pQv/G6FXpnEnjIJSO2Z5WAxXrx6xB1k2HZ17/4NWLF3fJVhdQJm3mS6odeGzUjgGrl1A42evxU+7VbcofEJq1aMiLgU1jUT2pt+pefCwmKJYLpEsSzuyrVxgvskQz0QpC053TAYSNf2Jj6Qhg9YDWyOeemYmDgffTqErF7AYhc6NKH0s0XKkIiNFSxorkEsfG/Ck1o+15slHNmWZXlmXToxDqFkLDoPvfGKg7koU5YTGvci/F9ZKb1juhGLxZbwap/18zN40BqA+Ip2yDBJAKxsIiwSjSIguy6g/Z1I50s0xNGOr36urfRRQX5H+rqr/xCZ63B6WSe6qBcZboWAQMDn8HLS9Xiwc= + - secure: dnb7r5guUeMOX9e7XlPUSZzmga8VW3G9Q1aa7LxEKiTjSnWhu5KpPDe8o1X3Rj6nc5iXDqmBH/C/7eNXPDyXJJWPvpE2YRpGymyUkRaakul0QBKJEaMvwy2SuAfS69CWC+TSzfGRvtSYkdpBhhLvs0h5S819S5jYbCNSCmOKfFucaP5NsHNIZ/I19oIeTPTa0/UnVm7DLFZXZjvbS+czkdyH1DhbT85sLj+XqNTzLePImE68efrjaHnlSy/CzBVJzj55UgD5i9fxNCQWzGWim/SD5xZ0zKtLycSOf6wQN2lCo0lkjw9rDlYz69mM5L9ikfYL9oHDPZnh84oXKglQ5miOHCgqs/qs4439I05lIu8i/EfbFA55YG4NyO3rL9YVOOt5gwiwvJYhDcnkVVzSl0o5bsoZgQfYvPWaIQKNkl3C53zfDQjgqS54CeDzlZpFrQTDQ1RrH8oeVC1gfYAeMabMDadox5rfZmLIN5JTf/F8iD/QdxGcoUvkEENcQgfP9PnubExtexgHGsEmqbm6ORSZ1MkEh2m3fo0f8KE6TbN1UigmcQ8nTkWBHsSmfHnB8HwJQp8mwQmDamXA+Hl3e3w4LOdYkJVlNW1/TTyJJOOvjMQCjF8SJmPHuh+QpqKbSaT9XM/vBhxbIZEufH8kawJKCBBcCNspGMNjhXfNjM0= -- cgit v1.2.3 From 98b88bb52f6385cb5dbbd6bc2238939d8f45e47e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 12:52:28 -0400 Subject: add alignment docs --- std/mem.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/std/mem.zig b/std/mem.zig index 8a59d6251b..eb67ce83ef 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -11,6 +11,8 @@ pub const Allocator = struct { /// Allocate byte_count bytes and return them in a slice, with the /// slice's pointer aligned at least to alignment bytes. /// The returned newly allocated memory is undefined. + /// `alignment` is guaranteed to be >= 1 + /// `alignment` is guaranteed to be a power of 2 allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8, /// If `new_byte_count > old_mem.len`: @@ -22,6 +24,8 @@ pub const Allocator = struct { /// * alignment <= alignment of old_mem.ptr /// /// The returned newly allocated memory is undefined. + /// `alignment` is guaranteed to be >= 1 + /// `alignment` is guaranteed to be a power of 2 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` -- cgit v1.2.3 From da2af9c613841552e9e47b6c9f0e9e4ee74894fb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 13:36:26 -0400 Subject: fixups --- std/os/index.zig | 3 +-- std/os/linux/index.zig | 7 ++++--- std/os/linux/test.zig | 2 -- std/os/windows/index.zig | 11 ++++------- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index 8da5c05f06..1916e23db0 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -4,8 +4,7 @@ const Os = builtin.Os; const is_windows = builtin.os == Os.windows; const os = this; -comptime { - if (!builtin.is_test) return; +test "std.os" { _ = @import("child_process.zig"); _ = @import("darwin.zig"); _ = @import("darwin_errno.zig"); diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 42c19e91e6..d7924f7159 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -1309,7 +1309,8 @@ pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize { return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); } -comptime { - if (!builtin.is_test) return; - _ = @import("test.zig"); +test "import" { + if (builtin.os == builtin.Os.linux) { + _ = @import("test.zig"); + } } diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig index f6dc6c2a52..18a6e5f19f 100644 --- a/std/os/linux/test.zig +++ b/std/os/linux/test.zig @@ -4,8 +4,6 @@ const linux = std.os.linux; const assert = std.debug.assert; test "timer" { - if (builtin.os != builtin.Os.linux) return; - const epoll_fd = linux.epoll_create(); var err = linux.getErrno(epoll_fd); assert(err == 0); diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 4c127aba04..d6ef7205e8 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -1,10 +1,3 @@ -const builtin = @import("builtin"); -comptime { - if (!builtin.is_test) return; - _ = @import("util.zig"); -} - - pub const ERROR = @import("error.zig"); pub extern "advapi32" stdcallcc fn CryptAcquireContextA(phProv: &HCRYPTPROV, pszContainer: ?LPCSTR, @@ -324,3 +317,7 @@ pub const FILE_END = 2; pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; pub const HEAP_NO_SERIALIZE = 0x00000001; + +test "import" { + _ = @import("util.zig"); +} -- cgit v1.2.3 From 21767144fc1a8627a109e81a164c55171c279d82 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 18:11:50 -0400 Subject: linux: support VDSO for clock_gettime also fix a compiler crash when using cmpxchg with nullable pointer --- CMakeLists.txt | 1 + src/codegen.cpp | 10 + std/elf.zig | 611 ++++++++++++++++++++++++++++++++++++++++++++++ std/os/index.zig | 1 + std/os/linux/index.zig | 20 ++ std/os/linux/vdso.zig | 89 +++++++ std/os/linux/x86_64.zig | 8 + std/os/time.zig | 8 +- std/special/bootstrap.zig | 27 +- test/cases/atomics.zig | 20 ++ 10 files changed, 783 insertions(+), 12 deletions(-) create mode 100644 std/os/linux/vdso.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 07e722f7b8..e692974719 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -508,6 +508,7 @@ set(ZIG_STD_FILES "os/index.zig" "os/linux/errno.zig" "os/linux/index.zig" + "os/linux/vdso.zig" "os/linux/x86_64.zig" "os/path.zig" "os/time.zig" diff --git a/src/codegen.cpp b/src/codegen.cpp index b5c8fdecac..4581c3e2b3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3561,6 +3561,16 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, success_order, failure_order, instruction->is_weak); + TypeTableEntry *maybe_type = instruction->base.value.type; + assert(maybe_type->id == TypeTableEntryIdMaybe); + TypeTableEntry *child_type = maybe_type->data.maybe.child_type; + + if (type_is_codegen_pointer(child_type)) { + LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); + LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); + return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, ""); + } + assert(instruction->tmp_ptr != nullptr); assert(type_has_bits(instruction->type)); diff --git a/std/elf.zig b/std/elf.zig index 7e20fa000f..1764829bc8 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -7,6 +7,246 @@ const mem = std.mem; const debug = std.debug; const InStream = std.stream.InStream; +pub const AT_NULL = 0; +pub const AT_IGNORE = 1; +pub const AT_EXECFD = 2; +pub const AT_PHDR = 3; +pub const AT_PHENT = 4; +pub const AT_PHNUM = 5; +pub const AT_PAGESZ = 6; +pub const AT_BASE = 7; +pub const AT_FLAGS = 8; +pub const AT_ENTRY = 9; +pub const AT_NOTELF = 10; +pub const AT_UID = 11; +pub const AT_EUID = 12; +pub const AT_GID = 13; +pub const AT_EGID = 14; +pub const AT_CLKTCK = 17; +pub const AT_PLATFORM = 15; +pub const AT_HWCAP = 16; +pub const AT_FPUCW = 18; +pub const AT_DCACHEBSIZE = 19; +pub const AT_ICACHEBSIZE = 20; +pub const AT_UCACHEBSIZE = 21; +pub const AT_IGNOREPPC = 22; +pub const AT_SECURE = 23; +pub const AT_BASE_PLATFORM = 24; +pub const AT_RANDOM = 25; +pub const AT_HWCAP2 = 26; +pub const AT_EXECFN = 31; +pub const AT_SYSINFO = 32; +pub const AT_SYSINFO_EHDR = 33; +pub const AT_L1I_CACHESHAPE = 34; +pub const AT_L1D_CACHESHAPE = 35; +pub const AT_L2_CACHESHAPE = 36; +pub const AT_L3_CACHESHAPE = 37; +pub const AT_L1I_CACHESIZE = 40; +pub const AT_L1I_CACHEGEOMETRY = 41; +pub const AT_L1D_CACHESIZE = 42; +pub const AT_L1D_CACHEGEOMETRY = 43; +pub const AT_L2_CACHESIZE = 44; +pub const AT_L2_CACHEGEOMETRY = 45; +pub const AT_L3_CACHESIZE = 46; +pub const AT_L3_CACHEGEOMETRY = 47; + +pub const DT_NULL = 0; +pub const DT_NEEDED = 1; +pub const DT_PLTRELSZ = 2; +pub const DT_PLTGOT = 3; +pub const DT_HASH = 4; +pub const DT_STRTAB = 5; +pub const DT_SYMTAB = 6; +pub const DT_RELA = 7; +pub const DT_RELASZ = 8; +pub const DT_RELAENT = 9; +pub const DT_STRSZ = 10; +pub const DT_SYMENT = 11; +pub const DT_INIT = 12; +pub const DT_FINI = 13; +pub const DT_SONAME = 14; +pub const DT_RPATH = 15; +pub const DT_SYMBOLIC = 16; +pub const DT_REL = 17; +pub const DT_RELSZ = 18; +pub const DT_RELENT = 19; +pub const DT_PLTREL = 20; +pub const DT_DEBUG = 21; +pub const DT_TEXTREL = 22; +pub const DT_JMPREL = 23; +pub const DT_BIND_NOW = 24; +pub const DT_INIT_ARRAY = 25; +pub const DT_FINI_ARRAY = 26; +pub const DT_INIT_ARRAYSZ = 27; +pub const DT_FINI_ARRAYSZ = 28; +pub const DT_RUNPATH = 29; +pub const DT_FLAGS = 30; +pub const DT_ENCODING = 32; +pub const DT_PREINIT_ARRAY = 32; +pub const DT_PREINIT_ARRAYSZ = 33; +pub const DT_SYMTAB_SHNDX = 34; +pub const DT_NUM = 35; +pub const DT_LOOS = 0x6000000d; +pub const DT_HIOS = 0x6ffff000; +pub const DT_LOPROC = 0x70000000; +pub const DT_HIPROC = 0x7fffffff; +pub const DT_PROCNUM = DT_MIPS_NUM; + +pub const DT_VALRNGLO = 0x6ffffd00; +pub const DT_GNU_PRELINKED = 0x6ffffdf5; +pub const DT_GNU_CONFLICTSZ = 0x6ffffdf6; +pub const DT_GNU_LIBLISTSZ = 0x6ffffdf7; +pub const DT_CHECKSUM = 0x6ffffdf8; +pub const DT_PLTPADSZ = 0x6ffffdf9; +pub const DT_MOVEENT = 0x6ffffdfa; +pub const DT_MOVESZ = 0x6ffffdfb; +pub const DT_FEATURE_1 = 0x6ffffdfc; +pub const DT_POSFLAG_1 = 0x6ffffdfd; + +pub const DT_SYMINSZ = 0x6ffffdfe; +pub const DT_SYMINENT = 0x6ffffdff; +pub const DT_VALRNGHI = 0x6ffffdff; +pub const DT_VALNUM = 12; + +pub const DT_ADDRRNGLO = 0x6ffffe00; +pub const DT_GNU_HASH = 0x6ffffef5; +pub const DT_TLSDESC_PLT = 0x6ffffef6; +pub const DT_TLSDESC_GOT = 0x6ffffef7; +pub const DT_GNU_CONFLICT = 0x6ffffef8; +pub const DT_GNU_LIBLIST = 0x6ffffef9; +pub const DT_CONFIG = 0x6ffffefa; +pub const DT_DEPAUDIT = 0x6ffffefb; +pub const DT_AUDIT = 0x6ffffefc; +pub const DT_PLTPAD = 0x6ffffefd; +pub const DT_MOVETAB = 0x6ffffefe; +pub const DT_SYMINFO = 0x6ffffeff; +pub const DT_ADDRRNGHI = 0x6ffffeff; +pub const DT_ADDRNUM = 11; + + +pub const DT_VERSYM = 0x6ffffff0; + +pub const DT_RELACOUNT = 0x6ffffff9; +pub const DT_RELCOUNT = 0x6ffffffa; + + +pub const DT_FLAGS_1 = 0x6ffffffb; +pub const DT_VERDEF = 0x6ffffffc; + +pub const DT_VERDEFNUM = 0x6ffffffd; +pub const DT_VERNEED = 0x6ffffffe; + +pub const DT_VERNEEDNUM = 0x6fffffff; +pub const DT_VERSIONTAGNUM = 16; + + + +pub const DT_AUXILIARY = 0x7ffffffd; +pub const DT_FILTER = 0x7fffffff; +pub const DT_EXTRANUM = 3; + + +pub const DT_SPARC_REGISTER = 0x70000001; +pub const DT_SPARC_NUM = 2; + +pub const DT_MIPS_RLD_VERSION = 0x70000001; +pub const DT_MIPS_TIME_STAMP = 0x70000002; +pub const DT_MIPS_ICHECKSUM = 0x70000003; +pub const DT_MIPS_IVERSION = 0x70000004; +pub const DT_MIPS_FLAGS = 0x70000005; +pub const DT_MIPS_BASE_ADDRESS = 0x70000006; +pub const DT_MIPS_MSYM = 0x70000007; +pub const DT_MIPS_CONFLICT = 0x70000008; +pub const DT_MIPS_LIBLIST = 0x70000009; +pub const DT_MIPS_LOCAL_GOTNO = 0x7000000a; +pub const DT_MIPS_CONFLICTNO = 0x7000000b; +pub const DT_MIPS_LIBLISTNO = 0x70000010; +pub const DT_MIPS_SYMTABNO = 0x70000011; +pub const DT_MIPS_UNREFEXTNO = 0x70000012; +pub const DT_MIPS_GOTSYM = 0x70000013; +pub const DT_MIPS_HIPAGENO = 0x70000014; +pub const DT_MIPS_RLD_MAP = 0x70000016; +pub const DT_MIPS_DELTA_CLASS = 0x70000017; +pub const DT_MIPS_DELTA_CLASS_NO = 0x70000018; + +pub const DT_MIPS_DELTA_INSTANCE = 0x70000019; +pub const DT_MIPS_DELTA_INSTANCE_NO = 0x7000001a; + +pub const DT_MIPS_DELTA_RELOC = 0x7000001b; +pub const DT_MIPS_DELTA_RELOC_NO = 0x7000001c; + +pub const DT_MIPS_DELTA_SYM = 0x7000001d; + +pub const DT_MIPS_DELTA_SYM_NO = 0x7000001e; + +pub const DT_MIPS_DELTA_CLASSSYM = 0x70000020; + +pub const DT_MIPS_DELTA_CLASSSYM_NO = 0x70000021; + +pub const DT_MIPS_CXX_FLAGS = 0x70000022; +pub const DT_MIPS_PIXIE_INIT = 0x70000023; +pub const DT_MIPS_SYMBOL_LIB = 0x70000024; +pub const DT_MIPS_LOCALPAGE_GOTIDX = 0x70000025; +pub const DT_MIPS_LOCAL_GOTIDX = 0x70000026; +pub const DT_MIPS_HIDDEN_GOTIDX = 0x70000027; +pub const DT_MIPS_PROTECTED_GOTIDX = 0x70000028; +pub const DT_MIPS_OPTIONS = 0x70000029; +pub const DT_MIPS_INTERFACE = 0x7000002a; +pub const DT_MIPS_DYNSTR_ALIGN = 0x7000002b; +pub const DT_MIPS_INTERFACE_SIZE = 0x7000002c; +pub const DT_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002d; + +pub const DT_MIPS_PERF_SUFFIX = 0x7000002e; + +pub const DT_MIPS_COMPACT_SIZE = 0x7000002f; +pub const DT_MIPS_GP_VALUE = 0x70000030; +pub const DT_MIPS_AUX_DYNAMIC = 0x70000031; + +pub const DT_MIPS_PLTGOT = 0x70000032; + +pub const DT_MIPS_RWPLT = 0x70000034; +pub const DT_MIPS_RLD_MAP_REL = 0x70000035; +pub const DT_MIPS_NUM = 0x36; + +pub const DT_ALPHA_PLTRO = (DT_LOPROC + 0); +pub const DT_ALPHA_NUM = 1; + +pub const DT_PPC_GOT = (DT_LOPROC + 0); +pub const DT_PPC_OPT = (DT_LOPROC + 1); +pub const DT_PPC_NUM = 2; + +pub const DT_PPC64_GLINK = (DT_LOPROC + 0); +pub const DT_PPC64_OPD = (DT_LOPROC + 1); +pub const DT_PPC64_OPDSZ = (DT_LOPROC + 2); +pub const DT_PPC64_OPT = (DT_LOPROC + 3); +pub const DT_PPC64_NUM = 4; + +pub const DT_IA_64_PLT_RESERVE = (DT_LOPROC + 0); +pub const DT_IA_64_NUM = 1; + +pub const DT_NIOS2_GP = 0x70000002; + +pub const PT_NULL = 0; +pub const PT_LOAD = 1; +pub const PT_DYNAMIC = 2; +pub const PT_INTERP = 3; +pub const PT_NOTE = 4; +pub const PT_SHLIB = 5; +pub const PT_PHDR = 6; +pub const PT_TLS = 7; +pub const PT_NUM = 8; +pub const PT_LOOS = 0x60000000; +pub const PT_GNU_EH_FRAME = 0x6474e550; +pub const PT_GNU_STACK = 0x6474e551; +pub const PT_GNU_RELRO = 0x6474e552; +pub const PT_LOSUNW = 0x6ffffffa; +pub const PT_SUNWBSS = 0x6ffffffa; +pub const PT_SUNWSTACK = 0x6ffffffb; +pub const PT_HISUNW = 0x6fffffff; +pub const PT_HIOS = 0x6fffffff; +pub const PT_LOPROC = 0x70000000; +pub const PT_HIPROC = 0x7fffffff; + pub const SHT_NULL = 0; pub const SHT_PROGBITS = 1; pub const SHT_SYMTAB = 2; @@ -31,6 +271,45 @@ pub const SHT_HIPROC = 0x7fffffff; pub const SHT_LOUSER = 0x80000000; pub const SHT_HIUSER = 0xffffffff; +pub const STB_LOCAL = 0; +pub const STB_GLOBAL = 1; +pub const STB_WEAK = 2; +pub const STB_NUM = 3; +pub const STB_LOOS = 10; +pub const STB_GNU_UNIQUE = 10; +pub const STB_HIOS = 12; +pub const STB_LOPROC = 13; +pub const STB_HIPROC = 15; + +pub const STB_MIPS_SPLIT_COMMON = 13; + +pub const STT_NOTYPE = 0; +pub const STT_OBJECT = 1; +pub const STT_FUNC = 2; +pub const STT_SECTION = 3; +pub const STT_FILE = 4; +pub const STT_COMMON = 5; +pub const STT_TLS = 6; +pub const STT_NUM = 7; +pub const STT_LOOS = 10; +pub const STT_GNU_IFUNC = 10; +pub const STT_HIOS = 12; +pub const STT_LOPROC = 13; +pub const STT_HIPROC = 15; + +pub const STT_SPARC_REGISTER = 13; + +pub const STT_PARISC_MILLICODE = 13; + +pub const STT_HP_OPAQUE = (STT_LOOS + 0x1); +pub const STT_HP_STUB = (STT_LOOS + 0x2); + +pub const STT_ARM_TFUNC = STT_LOPROC; +pub const STT_ARM_16BIT = STT_HIPROC; + +pub const VER_FLG_BASE = 0x1; +pub const VER_FLG_WEAK = 0x2; + pub const FileType = enum { Relocatable, Executable, @@ -266,3 +545,335 @@ pub const Elf = struct { try elf.in_file.seekTo(elf_section.offset); } }; + +pub const EI_NIDENT = 16; +pub const Elf32_Half = u16; +pub const Elf64_Half = u16; +pub const Elf32_Word = u32; +pub const Elf32_Sword = i32; +pub const Elf64_Word = u32; +pub const Elf64_Sword = i32; +pub const Elf32_Xword = u64; +pub const Elf32_Sxword = i64; +pub const Elf64_Xword = u64; +pub const Elf64_Sxword = i64; +pub const Elf32_Addr = u32; +pub const Elf64_Addr = u64; +pub const Elf32_Off = u32; +pub const Elf64_Off = u64; +pub const Elf32_Section = u16; +pub const Elf64_Section = u16; +pub const Elf32_Versym = Elf32_Half; +pub const Elf64_Versym = Elf64_Half; +pub const Elf32_Ehdr = extern struct { + e_ident: [EI_NIDENT]u8, + e_type: Elf32_Half, + e_machine: Elf32_Half, + e_version: Elf32_Word, + e_entry: Elf32_Addr, + e_phoff: Elf32_Off, + e_shoff: Elf32_Off, + e_flags: Elf32_Word, + e_ehsize: Elf32_Half, + e_phentsize: Elf32_Half, + e_phnum: Elf32_Half, + e_shentsize: Elf32_Half, + e_shnum: Elf32_Half, + e_shstrndx: Elf32_Half, +}; +pub const Elf64_Ehdr = extern struct { + e_ident: [EI_NIDENT]u8, + e_type: Elf64_Half, + e_machine: Elf64_Half, + e_version: Elf64_Word, + e_entry: Elf64_Addr, + e_phoff: Elf64_Off, + e_shoff: Elf64_Off, + e_flags: Elf64_Word, + e_ehsize: Elf64_Half, + e_phentsize: Elf64_Half, + e_phnum: Elf64_Half, + e_shentsize: Elf64_Half, + e_shnum: Elf64_Half, + e_shstrndx: Elf64_Half, +}; +pub const Elf32_Shdr = extern struct { + sh_name: Elf32_Word, + sh_type: Elf32_Word, + sh_flags: Elf32_Word, + sh_addr: Elf32_Addr, + sh_offset: Elf32_Off, + sh_size: Elf32_Word, + sh_link: Elf32_Word, + sh_info: Elf32_Word, + sh_addralign: Elf32_Word, + sh_entsize: Elf32_Word, +}; +pub const Elf64_Shdr = extern struct { + sh_name: Elf64_Word, + sh_type: Elf64_Word, + sh_flags: Elf64_Xword, + sh_addr: Elf64_Addr, + sh_offset: Elf64_Off, + sh_size: Elf64_Xword, + sh_link: Elf64_Word, + sh_info: Elf64_Word, + sh_addralign: Elf64_Xword, + sh_entsize: Elf64_Xword, +}; +pub const Elf32_Chdr = extern struct { + ch_type: Elf32_Word, + ch_size: Elf32_Word, + ch_addralign: Elf32_Word, +}; +pub const Elf64_Chdr = extern struct { + ch_type: Elf64_Word, + ch_reserved: Elf64_Word, + ch_size: Elf64_Xword, + ch_addralign: Elf64_Xword, +}; +pub const Elf32_Sym = extern struct { + st_name: Elf32_Word, + st_value: Elf32_Addr, + st_size: Elf32_Word, + st_info: u8, + st_other: u8, + st_shndx: Elf32_Section, +}; +pub const Elf64_Sym = extern struct { + st_name: Elf64_Word, + st_info: u8, + st_other: u8, + st_shndx: Elf64_Section, + st_value: Elf64_Addr, + st_size: Elf64_Xword, +}; +pub const Elf32_Syminfo = extern struct { + si_boundto: Elf32_Half, + si_flags: Elf32_Half, +}; +pub const Elf64_Syminfo = extern struct { + si_boundto: Elf64_Half, + si_flags: Elf64_Half, +}; +pub const Elf32_Rel = extern struct { + r_offset: Elf32_Addr, + r_info: Elf32_Word, +}; +pub const Elf64_Rel = extern struct { + r_offset: Elf64_Addr, + r_info: Elf64_Xword, +}; +pub const Elf32_Rela = extern struct { + r_offset: Elf32_Addr, + r_info: Elf32_Word, + r_addend: Elf32_Sword, +}; +pub const Elf64_Rela = extern struct { + r_offset: Elf64_Addr, + r_info: Elf64_Xword, + r_addend: Elf64_Sxword, +}; +pub const Elf32_Phdr = extern struct { + p_type: Elf32_Word, + p_offset: Elf32_Off, + p_vaddr: Elf32_Addr, + p_paddr: Elf32_Addr, + p_filesz: Elf32_Word, + p_memsz: Elf32_Word, + p_flags: Elf32_Word, + p_align: Elf32_Word, +}; +pub const Elf64_Phdr = extern struct { + p_type: Elf64_Word, + p_flags: Elf64_Word, + p_offset: Elf64_Off, + p_vaddr: Elf64_Addr, + p_paddr: Elf64_Addr, + p_filesz: Elf64_Xword, + p_memsz: Elf64_Xword, + p_align: Elf64_Xword, +}; +pub const Elf32_Dyn = extern struct { + d_tag: Elf32_Sword, + d_un: extern union { + d_val: Elf32_Word, + d_ptr: Elf32_Addr, + }, +}; +pub const Elf64_Dyn = extern struct { + d_tag: Elf64_Sxword, + d_un: extern union { + d_val: Elf64_Xword, + d_ptr: Elf64_Addr, + }, +}; +pub const Elf32_Verdef = extern struct { + vd_version: Elf32_Half, + vd_flags: Elf32_Half, + vd_ndx: Elf32_Half, + vd_cnt: Elf32_Half, + vd_hash: Elf32_Word, + vd_aux: Elf32_Word, + vd_next: Elf32_Word, +}; +pub const Elf64_Verdef = extern struct { + vd_version: Elf64_Half, + vd_flags: Elf64_Half, + vd_ndx: Elf64_Half, + vd_cnt: Elf64_Half, + vd_hash: Elf64_Word, + vd_aux: Elf64_Word, + vd_next: Elf64_Word, +}; +pub const Elf32_Verdaux = extern struct { + vda_name: Elf32_Word, + vda_next: Elf32_Word, +}; +pub const Elf64_Verdaux = extern struct { + vda_name: Elf64_Word, + vda_next: Elf64_Word, +}; +pub const Elf32_Verneed = extern struct { + vn_version: Elf32_Half, + vn_cnt: Elf32_Half, + vn_file: Elf32_Word, + vn_aux: Elf32_Word, + vn_next: Elf32_Word, +}; +pub const Elf64_Verneed = extern struct { + vn_version: Elf64_Half, + vn_cnt: Elf64_Half, + vn_file: Elf64_Word, + vn_aux: Elf64_Word, + vn_next: Elf64_Word, +}; +pub const Elf32_Vernaux = extern struct { + vna_hash: Elf32_Word, + vna_flags: Elf32_Half, + vna_other: Elf32_Half, + vna_name: Elf32_Word, + vna_next: Elf32_Word, +}; +pub const Elf64_Vernaux = extern struct { + vna_hash: Elf64_Word, + vna_flags: Elf64_Half, + vna_other: Elf64_Half, + vna_name: Elf64_Word, + vna_next: Elf64_Word, +}; +pub const Elf32_auxv_t = extern struct { + a_type: u32, + a_un: extern union { + a_val: u32, + }, +}; +pub const Elf64_auxv_t = extern struct { + a_type: u64, + a_un: extern union { + a_val: u64, + }, +}; +pub const Elf32_Nhdr = extern struct { + n_namesz: Elf32_Word, + n_descsz: Elf32_Word, + n_type: Elf32_Word, +}; +pub const Elf64_Nhdr = extern struct { + n_namesz: Elf64_Word, + n_descsz: Elf64_Word, + n_type: Elf64_Word, +}; +pub const Elf32_Move = extern struct { + m_value: Elf32_Xword, + m_info: Elf32_Word, + m_poffset: Elf32_Word, + m_repeat: Elf32_Half, + m_stride: Elf32_Half, +}; +pub const Elf64_Move = extern struct { + m_value: Elf64_Xword, + m_info: Elf64_Xword, + m_poffset: Elf64_Xword, + m_repeat: Elf64_Half, + m_stride: Elf64_Half, +}; +pub const Elf32_gptab = extern union { + gt_header: extern struct { + gt_current_g_value: Elf32_Word, + gt_unused: Elf32_Word, + }, + gt_entry: extern struct { + gt_g_value: Elf32_Word, + gt_bytes: Elf32_Word, + }, +}; +pub const Elf32_RegInfo = extern struct { + ri_gprmask: Elf32_Word, + ri_cprmask: [4]Elf32_Word, + ri_gp_value: Elf32_Sword, +}; +pub const Elf_Options = extern struct { + kind: u8, + size: u8, + @"section": Elf32_Section, + info: Elf32_Word, +}; +pub const Elf_Options_Hw = extern struct { + hwp_flags1: Elf32_Word, + hwp_flags2: Elf32_Word, +}; +pub const Elf32_Lib = extern struct { + l_name: Elf32_Word, + l_time_stamp: Elf32_Word, + l_checksum: Elf32_Word, + l_version: Elf32_Word, + l_flags: Elf32_Word, +}; +pub const Elf64_Lib = extern struct { + l_name: Elf64_Word, + l_time_stamp: Elf64_Word, + l_checksum: Elf64_Word, + l_version: Elf64_Word, + l_flags: Elf64_Word, +}; +pub const Elf32_Conflict = Elf32_Addr; +pub const Elf_MIPS_ABIFlags_v0 = extern struct { + version: Elf32_Half, + isa_level: u8, + isa_rev: u8, + gpr_size: u8, + cpr1_size: u8, + cpr2_size: u8, + fp_abi: u8, + isa_ext: Elf32_Word, + ases: Elf32_Word, + flags1: Elf32_Word, + flags2: Elf32_Word, +}; + +pub const Ehdr = switch(@sizeOf(usize)) { + 4 => Elf32_Ehdr, + 8 => Elf64_Ehdr, + else => @compileError("expected pointer size of 32 or 64"), +}; +pub const Phdr = switch(@sizeOf(usize)) { + 4 => Elf32_Phdr, + 8 => Elf64_Phdr, + else => @compileError("expected pointer size of 32 or 64"), +}; +pub const Sym = switch(@sizeOf(usize)) { + 4 => Elf32_Sym, + 8 => Elf64_Sym, + else => @compileError("expected pointer size of 32 or 64"), +}; +pub const Verdef = switch(@sizeOf(usize)) { + 4 => Elf32_Verdef, + 8 => Elf64_Verdef, + else => @compileError("expected pointer size of 32 or 64"), +}; +pub const Verdaux = switch(@sizeOf(usize)) { + 4 => Elf32_Verdaux, + 8 => Elf64_Verdaux, + else => @compileError("expected pointer size of 32 or 64"), +}; diff --git a/std/os/index.zig b/std/os/index.zig index 1916e23db0..0639490725 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -478,6 +478,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { }; } +pub var linux_aux_raw = []usize{0} ** 38; pub var posix_environ_raw: []&u8 = undefined; /// Caller must free result when done. diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index d7924f7159..dcd9532d1d 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -1,6 +1,7 @@ const std = @import("../../index.zig"); const assert = std.debug.assert; const builtin = @import("builtin"); +const vdso = @import("vdso.zig"); pub use switch (builtin.arch) { builtin.Arch.x86_64 => @import("x86_64.zig"), builtin.Arch.i386 => @import("i386.zig"), @@ -806,8 +807,27 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize { } pub fn clock_gettime(clk_id: i32, tp: ×pec) usize { + if (VDSO_CGT_SYM.len != 0) { + const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered); + if (@ptrToInt(f) != 0) { + const rc = f(clk_id, tp); + switch (rc) { + 0, @bitCast(usize, isize(-EINVAL)) => return rc, + else => {}, + } + } + } return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); } +var vdso_clock_gettime = init_vdso_clock_gettime; +extern fn init_vdso_clock_gettime(clk: i32, ts: ×pec) usize { + const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM); + var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr); + _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f, + builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic); + if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS)); + return f(clk, ts); +} pub fn clock_getres(clk_id: i32, tp: ×pec) usize { return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig new file mode 100644 index 0000000000..f4fb513af9 --- /dev/null +++ b/std/os/linux/vdso.zig @@ -0,0 +1,89 @@ +const std = @import("../../index.zig"); +const elf = std.elf; +const linux = std.os.linux; +const cstr = std.cstr; +const mem = std.mem; + +pub fn lookup(vername: []const u8, name: []const u8) usize { + const vdso_addr = std.os.linux_aux_raw[std.elf.AT_SYSINFO_EHDR]; + if (vdso_addr == 0) return 0; + + const eh = @intToPtr(&elf.Ehdr, vdso_addr); + var ph_addr: usize = vdso_addr + eh.e_phoff; + const ph = @intToPtr(&elf.Phdr, ph_addr); + + var maybe_dynv: ?&usize = null; + var base: usize = @maxValue(usize); + { + var i: usize = 0; + while (i < eh.e_phnum) : ({i += 1; ph_addr += eh.e_phentsize;}) { + const this_ph = @intToPtr(&elf.Phdr, ph_addr); + switch (this_ph.p_type) { + elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr, + elf.PT_DYNAMIC => maybe_dynv = @intToPtr(&usize, vdso_addr + this_ph.p_offset), + else => {}, + } + } + } + const dynv = maybe_dynv ?? return 0; + if (base == @maxValue(usize)) return 0; + + var maybe_strings: ?&u8 = null; + var maybe_syms: ?&elf.Sym = null; + var maybe_hashtab: ?&linux.Elf_Symndx = null; + var maybe_versym: ?&u16 = null; + var maybe_verdef: ?&elf.Verdef = null; + + { + var i: usize = 0; + while (dynv[i] != 0) : (i += 2) { + const p = base + dynv[i + 1]; + switch (dynv[i]) { + elf.DT_STRTAB => maybe_strings = @intToPtr(&u8, p), + elf.DT_SYMTAB => maybe_syms = @intToPtr(&elf.Sym, p), + elf.DT_HASH => maybe_hashtab = @intToPtr(&linux.Elf_Symndx, p), + elf.DT_VERSYM => maybe_versym = @intToPtr(&u16, p), + elf.DT_VERDEF => maybe_verdef = @intToPtr(&elf.Verdef, p), + else => {}, + } + } + } + + const strings = maybe_strings ?? return 0; + const syms = maybe_syms ?? return 0; + const hashtab = maybe_hashtab ?? return 0; + if (maybe_verdef == null) maybe_versym = null; + + + const OK_TYPES = (1<>4) & OK_BINDS)) continue; + if (0==syms[i].st_shndx) continue; + if (!mem.eql(u8, name, cstr.toSliceConst(&strings[syms[i].st_name]))) continue; + if (maybe_versym) |versym| { + if (!checkver(??maybe_verdef, versym[i], vername, strings)) + continue; + } + return base + syms[i].st_value; + } + + return 0; +} + +fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: &u8) bool { + var def = def_arg; + const vsym = @bitCast(u32, vsym_arg) & 0x7fff; + while (true) { + if (0==(def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym) + break; + if (def.vd_next == 0) + return false; + def = @intToPtr(&elf.Verdef, @ptrToInt(def) + def.vd_next); + } + const aux = @intToPtr(&elf.Verdaux, @ptrToInt(def ) + def.vd_aux); + return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name])); +} diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index bfc27b0f38..544b2365ce 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -371,6 +371,13 @@ pub const F_GETOWN_EX = 16; pub const F_GETOWNER_UIDS = 17; + +pub const VDSO_USEFUL = true; +pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; +pub const VDSO_CGT_VER = "LINUX_2.6"; +pub const VDSO_GETCPU_SYM = "__vdso_getcpu"; +pub const VDSO_GETCPU_VER = "LINUX_2.6"; + pub fn syscall0(number: usize) usize { return asm volatile ("syscall" : [ret] "={rax}" (-> usize) @@ -509,3 +516,4 @@ pub const dirent = extern struct { d_name: u8, // field address is the address of first byte of name }; +pub const Elf_Symndx = u32; diff --git a/std/os/time.zig b/std/os/time.zig index 8a906d7954..e9fbf9798c 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -160,13 +160,13 @@ pub const Timer = struct { Os.windows => { var freq: i64 = undefined; var err = windows.QueryPerformanceFrequency(&freq); - if (err == 0) return error.TimerUnsupported; + if (err == windows.FALSE) return error.TimerUnsupported; self.frequency = u64(freq); self.resolution = @divFloor(ns_per_s, self.frequency); var start_time: i64 = undefined; err = windows.QueryPerformanceCounter(&start_time); - debug.assert(err != 0); + debug.assert(err != windows.FALSE); self.start_time = u64(start_time); }, Os.linux => { @@ -236,7 +236,7 @@ pub const Timer = struct { fn clockWindows() u64 { var result: i64 = undefined; var err = windows.QueryPerformanceCounter(&result); - debug.assert(err != 0); + debug.assert(err != windows.FALSE); return u64(result); } @@ -285,4 +285,4 @@ test "os.time.Timer" { timer.reset(); debug.assert(timer.read() < time_1); -} \ No newline at end of file +} diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index d2c22c13e1..1dc7e24869 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -48,22 +48,33 @@ extern fn WinMainCRTStartup() noreturn { fn posixCallMainAndExit() noreturn { const argc = *argc_ptr; const argv = @ptrCast(&&u8, &argc_ptr[1]); - const envp = @ptrCast(&?&u8, &argv[argc + 1]); + const envp_nullable = @ptrCast(&?&u8, &argv[argc + 1]); + var envp_count: usize = 0; + while (envp_nullable[envp_count]) |_| : (envp_count += 1) {} + const envp = @ptrCast(&&u8, envp_nullable)[0..envp_count]; + if (builtin.os == builtin.Os.linux) { + const auxv = &@ptrCast(&usize, envp.ptr)[envp_count + 1]; + var i: usize = 0; + while (auxv[i] != 0) : (i += 2) { + if (auxv[i] < std.os.linux_aux_raw.len) std.os.linux_aux_raw[auxv[i]] = auxv[i+1]; + } + std.debug.assert(std.os.linux_aux_raw[std.elf.AT_PAGESZ] == std.os.page_size); + } + std.os.posix.exit(callMainWithArgs(argc, argv, envp)); } -fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) u8 { +fn callMainWithArgs(argc: usize, argv: &&u8, envp: []&u8) u8 { std.os.ArgIteratorPosix.raw = argv[0..argc]; - - var env_count: usize = 0; - while (envp[env_count] != null) : (env_count += 1) {} - std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count]; - + std.os.posix_environ_raw = envp; return callMain(); } extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) i32 { - return callMainWithArgs(usize(c_argc), c_argv, c_envp); + var env_count: usize = 0; + while (c_envp[env_count] != null) : (env_count += 1) {} + const envp = @ptrCast(&&u8, c_envp)[0..env_count]; + return callMainWithArgs(usize(c_argc), c_argv, envp); } fn callMain() u8 { diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig index 4cadabb728..d406285d29 100644 --- a/test/cases/atomics.zig +++ b/test/cases/atomics.zig @@ -49,3 +49,23 @@ fn testAtomicLoad(ptr: &u8) void { const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); assert(x == 42); } + +test "cmpxchg with ptr" { + var data1: i32 = 1234; + var data2: i32 = 5678; + var data3: i32 = 9101; + var x: &i32 = &data1; + if (@cmpxchgWeak(&i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assert(x1 == &data1); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(&i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assert(x1 == &data1); + } + assert(x == &data3); + + assert(@cmpxchgStrong(&i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assert(x == &data2); +} -- cgit v1.2.3 From 7af6ed3f20bbf0459ce6aed833c7e170ee6c927b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 12:52:28 -0400 Subject: add alignment docs --- std/mem.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/std/mem.zig b/std/mem.zig index 8a59d6251b..eb67ce83ef 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -11,6 +11,8 @@ pub const Allocator = struct { /// Allocate byte_count bytes and return them in a slice, with the /// slice's pointer aligned at least to alignment bytes. /// The returned newly allocated memory is undefined. + /// `alignment` is guaranteed to be >= 1 + /// `alignment` is guaranteed to be a power of 2 allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8, /// If `new_byte_count > old_mem.len`: @@ -22,6 +24,8 @@ pub const Allocator = struct { /// * alignment <= alignment of old_mem.ptr /// /// The returned newly allocated memory is undefined. + /// `alignment` is guaranteed to be >= 1 + /// `alignment` is guaranteed to be a power of 2 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` -- cgit v1.2.3 From 1c41f1ca6252faaa7750936c67562f1866a48075 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 20:54:52 -0400 Subject: better error reporting for missing libc on windows closes #931 --- src/analyze.cpp | 9 ++++++--- src/os.cpp | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index d142b86326..5dd7b0d18c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4433,7 +4433,8 @@ void find_libc_lib_path(CodeGen *g) { if (g->msvc_lib_dir == nullptr) { Buf* vc_lib_dir = buf_alloc(); if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { - zig_panic("Unable to determine vcruntime path."); + fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir"); + exit(1); } g->msvc_lib_dir = vc_lib_dir; } @@ -4441,7 +4442,8 @@ void find_libc_lib_path(CodeGen *g) { if (g->libc_lib_dir == nullptr) { Buf* ucrt_lib_path = buf_alloc(); if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) { - zig_panic("Unable to determine ucrt path."); + fprintf(stderr, "Unable to determine ucrt path. --libc-lib-dir"); + exit(1); } g->libc_lib_dir = ucrt_lib_path; } @@ -4449,7 +4451,8 @@ void find_libc_lib_path(CodeGen *g) { if (g->kernel32_lib_dir == nullptr) { Buf* kern_lib_path = buf_alloc(); if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) { - zig_panic("Unable to determine kernel32 path."); + fprintf(stderr, "Unable to determine kernel32 path. --kernel32-lib-dir"); + exit(1); } g->kernel32_lib_dir = kern_lib_path; } diff --git a/src/os.cpp b/src/os.cpp index 97462bd658..d335d8d218 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1334,6 +1334,9 @@ com_done:; int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { #if defined(ZIG_OS_WINDOWS) + if (buf_len(sdk->path10) == 0 || buf_len(sdk->version10) == 0) { + return ErrorFileNotFound; + } buf_resize(output_buf, 0); buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); switch (platform_type) { -- cgit v1.2.3 From 25dff91fa099489858428a7071b8c76acf1f943d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 21:08:52 -0400 Subject: fix windows build broken by previous commit fixes build failure from 1c41f1ca6252faaa7750936c67562f1866a48075 --- src/os.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/os.cpp b/src/os.cpp index d335d8d218..c93e2887b7 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1334,7 +1334,7 @@ com_done:; int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { #if defined(ZIG_OS_WINDOWS) - if (buf_len(sdk->path10) == 0 || buf_len(sdk->version10) == 0) { + if (buf_len(&sdk->path10) == 0 || buf_len(&sdk->version10) == 0) { return ErrorFileNotFound; } buf_resize(output_buf, 0); -- cgit v1.2.3 From 75328e32045d1275c03f1f37058ee0a3b775c632 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 21:47:25 -0400 Subject: exit(1) instead of abort() for file not found --- src/analyze.cpp | 6 ++++-- src/codegen.cpp | 6 ++++-- src/os.cpp | 3 --- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 5dd7b0d18c..a598d7676e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4306,7 +4306,8 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { static ZigWindowsSDK *get_windows_sdk(CodeGen *g) { if (g->win_sdk == nullptr) { if (os_find_windows_sdk(&g->win_sdk)) { - zig_panic("Unable to determine Windows SDK path."); + fprintf(stderr, "unable to determine windows sdk path\n"); + exit(1); } } assert(g->win_sdk != nullptr); @@ -4408,7 +4409,8 @@ void find_libc_include_path(CodeGen *g) { ZigWindowsSDK *sdk = get_windows_sdk(g); g->libc_include_dir = buf_alloc(); if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) { - zig_panic("Unable to determine libc include path."); + fprintf(stderr, "Unable to determine libc include path. --libc-include-dir"); + exit(1); } } else if (g->zig_target.os == OsLinux) { g->libc_include_dir = get_linux_libc_include_path(); diff --git a/src/codegen.cpp b/src/codegen.cpp index 4581c3e2b3..2d8c385f44 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6638,12 +6638,14 @@ static void gen_root_source(CodeGen *g) { Buf *abs_full_path = buf_alloc(); int err; if ((err = os_path_real(rel_full_path, abs_full_path))) { - zig_panic("unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + exit(1); } Buf *source_code = buf_alloc(); if ((err = os_fetch_file_path(rel_full_path, source_code, true))) { - zig_panic("unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + exit(1); } g->root_import = add_source_file(g, g->root_package, abs_full_path, source_code); diff --git a/src/os.cpp b/src/os.cpp index c93e2887b7..97462bd658 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1334,9 +1334,6 @@ com_done:; int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { #if defined(ZIG_OS_WINDOWS) - if (buf_len(&sdk->path10) == 0 || buf_len(&sdk->version10) == 0) { - return ErrorFileNotFound; - } buf_resize(output_buf, 0); buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); switch (platform_type) { -- cgit v1.2.3 From 8503eff8c12697c35ab5c73d6651c4b996339706 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 23:46:55 -0400 Subject: add compile error for invalid deref on switch target closes #945 --- src/ir.cpp | 5 ++++- test/compile_errors.zig | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 865a6823be..cd00fc6230 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14782,7 +14782,10 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, return out_val->type; } - assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer); + if (target_value_ptr->value.type->id != TypeTableEntryIdPointer) { + ir_add_error(ira, target_value_ptr, buf_sprintf("invalid deref on switch target")); + return ira->codegen->builtin_types.entry_invalid; + } TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type; ConstExprValue *pointee_val = nullptr; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2c4e35c562..f8febc27b8 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,21 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("invalid deref on switch target", + \\comptime { + \\ var tile = Tile.Empty; + \\ switch (*tile) { + \\ Tile.Empty => {}, + \\ Tile.Filled => {}, + \\ } + \\} + \\const Tile = enum { + \\ Empty, + \\ Filled, + \\}; + , + ".tmp_source.zig:3:13: error: invalid deref on switch target"); + cases.add("invalid field access in comptime", \\comptime { var x = doesnt_exist.whatever; } , -- cgit v1.2.3 From d8ba1bc12054712dec731db0c4062a5df0d627c6 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Tue, 17 Apr 2018 22:58:10 +1200 Subject: Improve fmt float-printing - Fix errors printing very small numbers - Add explicit scientific output mode - Add rounding based on a specific precision for both decimal/exp modes. - Test and confirm exp/decimal against libc for all f32 values. Various changes to better match libc. --- std/fmt/errol/index.zig | 76 ++++++++- std/fmt/index.zig | 439 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 456 insertions(+), 59 deletions(-) diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index 42287bd25b..8204a6f0f6 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -12,13 +12,79 @@ pub const FloatDecimal = struct { exp: i32, }; +pub const RoundMode = enum { + // Round only the fractional portion (e.g. 1234.23 has precision 2) + Decimal, + // Round the entire whole/fractional portion (e.g. 1.23423e3 has precision 5) + Scientific, +}; + +/// Round a FloatDecimal as returned by errol3 to the specified fractional precision. +/// All digits after the specified precision should be considered invalid. +pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: RoundMode) void { + // The round digit refers to the index which we should look at to determine + // whether we need to round to match the specified precision. + var round_digit: usize = 0; + + switch (mode) { + RoundMode.Decimal => { + if (float_decimal.exp >= 0) { + round_digit = precision + usize(float_decimal.exp); + } else { + // if a small negative exp, then adjust we need to offset by the number + // of leading zeros that will occur. + const min_exp_required = usize(-float_decimal.exp); + if (precision > min_exp_required) { + round_digit = precision - min_exp_required; + } + } + }, + RoundMode.Scientific => { + round_digit = 1 + precision; + }, + } + + // It suffices to look at just this digit. We don't round and propagate say 0.04999 to 0.05 + // first, and then to 0.1 in the case of a {.1} single precision. + + // Find the digit which will signify the round point and start rounding backwards. + if (round_digit < float_decimal.digits.len and float_decimal.digits[round_digit] - '0' >= 5) { + assert(round_digit >= 0); + + var i = round_digit; + while (true) { + if (i == 0) { + // Rounded all the way past the start. This was of the form 9.999... + // Slot the new digit in place and increase the exponent. + float_decimal.exp += 1; + + // Re-size the buffer to use the reserved leading byte. + const one_before = @intToPtr(&u8, @ptrToInt(&float_decimal.digits[0]) - 1); + float_decimal.digits = one_before[0..float_decimal.digits.len + 1]; + float_decimal.digits[0] = '1'; + return; + } + + i -= 1; + + const new_value = (float_decimal.digits[i] - '0' + 1) % 10; + float_decimal.digits[i] = new_value + '0'; + + // must continue rounding until non-9 + if (new_value != 0) { + return; + } + } + } +} + /// Corrected Errol3 double to ASCII conversion. pub fn errol3(value: f64, buffer: []u8) FloatDecimal { const bits = @bitCast(u64, value); const i = tableLowerBound(bits); if (i < enum3.len and enum3[i] == bits) { const data = enum3_data[i]; - const digits = buffer[0..data.str.len]; + const digits = buffer[1..data.str.len + 1]; mem.copy(u8, digits, data.str); return FloatDecimal { .digits = digits, @@ -98,7 +164,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { } // digit generation - var buf_index: usize = 0; + + // We generate digits starting at index 1. If rounding a buffer later then it may be + // required to generate a preceeding digit in some cases (9.999) in which case we use + // the 0-index for this extra digit. + var buf_index: usize = 1; while (true) { var hdig = u8(math.floor(high.val)); if ((high.val == f64(hdig)) and (high.off < 0)) @@ -128,7 +198,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { buf_index += 1; return FloatDecimal { - .digits = buffer[0..buf_index], + .digits = buffer[1..buf_index], .exp = exp, }; } diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 7bb9829117..5d749bb4b8 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -4,7 +4,7 @@ const debug = std.debug; const assert = debug.assert; const mem = std.mem; const builtin = @import("builtin"); -const errol3 = @import("errol/index.zig").errol3; +const errol = @import("errol/index.zig"); const max_int_digits = 65; @@ -22,6 +22,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), IntegerWidth, Float, FloatWidth, + FloatScientific, + FloatScientificWidth, Character, Buf, BufWidth, @@ -87,6 +89,9 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), 's' => { state = State.Buf; }, + 'e' => { + state = State.FloatScientific; + }, '.' => { state = State.Float; }, @@ -133,9 +138,33 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), '0' ... '9' => {}, else => @compileError("Unexpected character in format string: " ++ []u8{c}), }, + State.FloatScientific => switch (c) { + '}' => { + try formatFloatScientific(args[next_arg], null, context, Errors, output); + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + '0' ... '9' => { + width_start = i; + state = State.FloatScientificWidth; + }, + else => @compileError("Unexpected character in format string: " ++ []u8{c}), + }, + State.FloatScientificWidth => switch (c) { + '}' => { + width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); + try formatFloatScientific(args[next_arg], width, context, Errors, output); + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + '0' ... '9' => {}, + else => @compileError("Unexpected character in format string: " ++ []u8{c}), + }, State.Float => switch (c) { '}' => { - try formatFloatDecimal(args[next_arg], 0, context, Errors, output); + try formatFloatDecimal(args[next_arg], null, context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; @@ -199,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ return formatInt(value, 10, false, 0, context, Errors, output); }, builtin.TypeId.Float => { - return formatFloat(value, context, Errors, output); + return formatFloatScientific(value, null, context, Errors, output); }, builtin.TypeId.Void => { return output(context, "void"); @@ -257,81 +286,237 @@ pub fn formatBuf(buf: []const u8, width: usize, } } -pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { +// Print a float in scientific notation to the specified precision. Null uses full precision. +// It should be the case that every full precision, printed value can be re-parsed back to the +// same type unambiguously. +pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { var x = f64(value); // Errol doesn't handle these special cases. - if (math.isNan(x)) { - return output(context, "NaN"); - } if (math.signbit(x)) { try output(context, "-"); x = -x; } + + if (math.isNan(x)) { + return output(context, "nan"); + } if (math.isPositiveInf(x)) { - return output(context, "Infinity"); + return output(context, "inf"); } if (x == 0.0) { - return output(context, "0.0"); + try output(context, "0"); + + if (maybe_precision) |precision| { + if (precision != 0) { + try output(context, "."); + var i: usize = 0; + while (i < precision) : (i += 1) { + try output(context, "0"); + } + } + } else { + try output(context, ".0"); + } + + try output(context, "e+00"); + return; } var buffer: [32]u8 = undefined; - const float_decimal = errol3(x, buffer[0..]); - try output(context, float_decimal.digits[0..1]); - try output(context, "."); - if (float_decimal.digits.len > 1) { - const num_digits = if (@typeOf(value) == f32) - math.min(usize(9), float_decimal.digits.len) - else - float_decimal.digits.len; - try output(context, float_decimal.digits[1 .. num_digits]); + var float_decimal = errol.errol3(x, buffer[0..]); + + if (maybe_precision) |precision| { + errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); + + try output(context, float_decimal.digits[0..1]); + + // {e0} case prints no `.` + if (precision != 0) { + try output(context, "."); + + var printed: usize = 0; + if (float_decimal.digits.len > 1) { + const num_digits = math.min(float_decimal.digits.len, precision + 1); + try output(context, float_decimal.digits[1 .. num_digits]); + printed += num_digits - 1; + } + + while (printed < precision) : (printed += 1) { + try output(context, "0"); + } + } } else { - try output(context, "0"); + try output(context, float_decimal.digits[0..1]); + try output(context, "."); + if (float_decimal.digits.len > 1) { + const num_digits = if (@typeOf(value) == f32) + math.min(usize(9), float_decimal.digits.len) + else + float_decimal.digits.len; + + try output(context, float_decimal.digits[1 .. num_digits]); + } else { + try output(context, "0"); + } } - if (float_decimal.exp != 1) { - try output(context, "e"); - try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output); + try output(context, "e"); + const exp = float_decimal.exp - 1; + + if (exp >= 0) { + try output(context, "+"); + if (exp > -10 and exp < 10) { + try output(context, "0"); + } + try formatInt(exp, 10, false, 0, context, Errors, output); + } else { + try output(context, "-"); + if (exp > -10 and exp < 10) { + try output(context, "0"); + } + try formatInt(-exp, 10, false, 0, context, Errors, output); } } -pub fn formatFloatDecimal(value: var, precision: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { +// Print a float of the format x.yyyyy where the number of y is specified by the precision argument. +// By default floats are printed at full precision (no rounding). +pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { var x = f64(value); // Errol doesn't handle these special cases. - if (math.isNan(x)) { - return output(context, "NaN"); - } if (math.signbit(x)) { try output(context, "-"); x = -x; } + + if (math.isNan(x)) { + return output(context, "nan"); + } if (math.isPositiveInf(x)) { - return output(context, "Infinity"); + return output(context, "inf"); } if (x == 0.0) { - return output(context, "0.0"); + try output(context, "0"); + + if (maybe_precision) |precision| { + if (precision != 0) { + try output(context, "."); + var i: usize = 0; + while (i < precision) : (i += 1) { + try output(context, "0"); + } + } else { + try output(context, ".0"); + } + } else { + try output(context, "0"); + } + + return; } + // non-special case, use errol3 var buffer: [32]u8 = undefined; - const float_decimal = errol3(x, buffer[0..]); - - const num_left_digits = if (float_decimal.exp > 0) usize(float_decimal.exp) else 1; - - try output(context, float_decimal.digits[0 .. num_left_digits]); - try output(context, "."); - if (float_decimal.digits.len > 1) { - const num_valid_digtis = if (@typeOf(value) == f32) math.min(usize(7), float_decimal.digits.len) - else - float_decimal.digits.len; - - const num_right_digits = if (precision != 0) - math.min(precision, (num_valid_digtis-num_left_digits)) - else - num_valid_digtis - num_left_digits; - try output(context, float_decimal.digits[num_left_digits .. (num_left_digits + num_right_digits)]); + var float_decimal = errol.errol3(x, buffer[0..]); + + if (maybe_precision) |precision| { + errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); + + // exp < 0 means the leading is always 0 as errol result is normalized. + var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0; + + // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. + var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); + + if (num_digits_whole > 0) { + // We may have to zero pad, for instance 1e4 requires zero padding. + try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]); + + var i = num_digits_whole_no_pad; + while (i < num_digits_whole) : (i += 1) { + try output(context, "0"); + } + } else { + try output(context , "0"); + } + + // {.0} special case doesn't want a trailing '.' + if (precision == 0) { + return; + } + + try output(context, "."); + + // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's. + var printed: usize = 0; + + // Zero-fill until we reach significant digits or run out of precision. + if (float_decimal.exp <= 0) { + const zero_digit_count = usize(-float_decimal.exp); + const zeros_to_print = math.min(zero_digit_count, precision); + + var i: usize = 0; + while (i < zeros_to_print) : (i += 1) { + try output(context, "0"); + printed += 1; + } + + if (printed >= precision) { + return; + } + } + + // Remaining fractional portion, zero-padding if insufficient. + debug.assert(precision >= printed); + if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { + try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); + return; + } else { + try output(context, float_decimal.digits[num_digits_whole_no_pad ..]); + printed += float_decimal.digits.len - num_digits_whole_no_pad; + + while (printed < precision) : (printed += 1) { + try output(context, "0"); + } + } } else { - try output(context, "0"); + // exp < 0 means the leading is always 0 as errol result is normalized. + var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0; + + // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. + var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); + + if (num_digits_whole > 0) { + // We may have to zero pad, for instance 1e4 requires zero padding. + try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]); + + var i = num_digits_whole_no_pad; + while (i < num_digits_whole) : (i += 1) { + try output(context, "0"); + } + } else { + try output(context , "0"); + } + + // Omit `.` if no fractional portion + if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) { + return; + } + + try output(context, "."); + + // Zero-fill until we reach significant digits or run out of precision. + if (float_decimal.exp < 0) { + const zero_digit_count = usize(-float_decimal.exp); + + var i: usize = 0; + while (i < zero_digit_count) : (i += 1) { + try output(context, "0"); + } + } + + try output(context, float_decimal.digits[num_digits_whole_no_pad ..]); } } @@ -598,32 +783,81 @@ test "fmt.format" { // TODO get these tests passing in release modes // https://github.com/zig-lang/zig/issues/564 if (builtin.mode == builtin.Mode.Debug) { + { + var buf1: [32]u8 = undefined; + const value: f32 = 1.34; + const result = try bufPrint(buf1[0..], "f32: {e}\n", value); + assert(mem.eql(u8, result, "f32: 1.34000003e+00\n")); + } { var buf1: [32]u8 = undefined; const value: f32 = 12.34; - const result = try bufPrint(buf1[0..], "f32: {}\n", value); - assert(mem.eql(u8, result, "f32: 1.23400001e1\n")); + const result = try bufPrint(buf1[0..], "f32: {e}\n", value); + assert(mem.eql(u8, result, "f32: 1.23400001e+01\n")); } { var buf1: [32]u8 = undefined; const value: f64 = -12.34e10; - const result = try bufPrint(buf1[0..], "f64: {}\n", value); - assert(mem.eql(u8, result, "f64: -1.234e11\n")); + const result = try bufPrint(buf1[0..], "f64: {e}\n", value); + assert(mem.eql(u8, result, "f64: -1.234e+11\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 9.999960e-40; + const result = try bufPrint(buf1[0..], "f64: {e}\n", value); + assert(mem.eql(u8, result, "f64: 9.99996e-40\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 1.409706e-42; + const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); + assert(mem.eql(u8, result, "f64: 1.40971e-42\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = @bitCast(f32, u32(814313563)); + const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); + assert(mem.eql(u8, result, "f64: 1.00000e-09\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = @bitCast(f32, u32(1006632960)); + const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); + assert(mem.eql(u8, result, "f64: 7.81250e-03\n")); + } + { + // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. + // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. + var buf1: [32]u8 = undefined; + const value: f64 = @bitCast(f32, u32(1203982400)); + const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); + assert(mem.eql(u8, result, "f64: 1.00001e+05\n")); } { var buf1: [32]u8 = undefined; const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); - assert(mem.eql(u8, result, "f64: NaN\n")); + assert(mem.eql(u8, result, "f64: nan\n")); + } + { + var buf1: [32]u8 = undefined; + const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64); + assert(mem.eql(u8, result, "f64: -nan\n")); } { var buf1: [32]u8 = undefined; const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); - assert(mem.eql(u8, result, "f64: Infinity\n")); + assert(mem.eql(u8, result, "f64: inf\n")); } { var buf1: [32]u8 = undefined; const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); - assert(mem.eql(u8, result, "f64: -Infinity\n")); + assert(mem.eql(u8, result, "f64: -inf\n")); + } + { + var buf1: [64]u8 = undefined; + const value: f64 = 1.52314e+29; + const result = try bufPrint(buf1[0..], "f64: {.}\n", value); + assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); } { var buf1: [32]u8 = undefined; @@ -635,20 +869,20 @@ test "fmt.format" { var buf1: [32]u8 = undefined; const value: f32 = 1234.567; const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); - assert(mem.eql(u8, result, "f32: 1234.56\n")); + assert(mem.eql(u8, result, "f32: 1234.57\n")); } { var buf1: [32]u8 = undefined; const value: f32 = -11.1234; const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). - // -11.12339... is truncated to -11.1233 - assert(mem.eql(u8, result, "f32: -11.1233\n")); + // -11.12339... is rounded back up to -11.1234 + assert(mem.eql(u8, result, "f32: -11.1234\n")); } { var buf1: [32]u8 = undefined; const value: f32 = 91.12345; - const result = try bufPrint(buf1[0..], "f32: {.}\n", value); + const result = try bufPrint(buf1[0..], "f32: {.5}\n", value); assert(mem.eql(u8, result, "f32: 91.12345\n")); } { @@ -657,7 +891,100 @@ test "fmt.format" { const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); assert(mem.eql(u8, result, "f64: 91.1234567890\n")); } + { + var buf1: [32]u8 = undefined; + const value: f64 = 0.0; + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.00000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 5.700; + const result = try bufPrint(buf1[0..], "f64: {.0}\n", value); + assert(mem.eql(u8, result, "f64: 6\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 9.999; + const result = try bufPrint(buf1[0..], "f64: {.1}\n", value); + assert(mem.eql(u8, result, "f64: 10.0\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 1.0; + const result = try bufPrint(buf1[0..], "f64: {.3}\n", value); + assert(mem.eql(u8, result, "f64: 1.000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 0.0003; + const result = try bufPrint(buf1[0..], "f64: {.8}\n", value); + assert(mem.eql(u8, result, "f64: 0.00030000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 1.40130e-45; + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.00000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 9.999960e-40; + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.00000\n")); + } + // libc checks + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(916964781))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.00001\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(925353389))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.00001\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(1036831278))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.10000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(1065353133))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 1.00000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(1092616192))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 10.00000\n")); + } + // libc differences + { + var buf1: [32]u8 = undefined; + // This is 0.015625 exactly according to gdb. We thus round down, + // however glibc rounds up for some reason. This occurs for all + // floats of the form x.yyyy25 on a precision point. + const value: f64 = f64(@bitCast(f32, u32(1015021568))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.01563\n")); + } + // std-windows-x86_64-Debug-bare test case fails + { + // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 + // also rounds to 630 so I'm inclined to believe libc is not + // optimal here. + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(1518338049))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); + } } } -- cgit v1.2.3 From e5175d432ef01e078ef247ea0a781243219ddfb6 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Mon, 23 Apr 2018 17:18:05 +1200 Subject: Fix release float printing errors Fixes #564. Fixes #669. Fixes #928. --- std/fmt/errol/index.zig | 3 + std/fmt/index.zig | 400 ++++++++++++++++++++++++------------------------ 2 files changed, 202 insertions(+), 201 deletions(-) diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index 8204a6f0f6..00c69cd294 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -259,6 +259,9 @@ fn gethi(in: f64) f64 { /// Normalize the number by factoring in the error. /// @hp: The float pair. fn hpNormalize(hp: &HP) void { + // Required to avoid segfaults causing buffer overrun during errol3 digit output termination. + @setFloatMode(this, @import("builtin").FloatMode.Strict); + const val = hp.val; hp.val += hp.off; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 5d749bb4b8..43e758038f 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -779,212 +779,210 @@ test "fmt.format" { const result = try bufPrint(buf1[0..], "pointer: {}\n", &value); assert(mem.startsWith(u8, result, "pointer: Struct@")); } - - // TODO get these tests passing in release modes - // https://github.com/zig-lang/zig/issues/564 - if (builtin.mode == builtin.Mode.Debug) { - { - var buf1: [32]u8 = undefined; - const value: f32 = 1.34; - const result = try bufPrint(buf1[0..], "f32: {e}\n", value); - assert(mem.eql(u8, result, "f32: 1.34000003e+00\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f32 = 12.34; - const result = try bufPrint(buf1[0..], "f32: {e}\n", value); - assert(mem.eql(u8, result, "f32: 1.23400001e+01\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = -12.34e10; - const result = try bufPrint(buf1[0..], "f64: {e}\n", value); - assert(mem.eql(u8, result, "f64: -1.234e+11\n")); - } - { + { + var buf1: [32]u8 = undefined; + const value: f32 = 1.34; + const result = try bufPrint(buf1[0..], "f32: {e}\n", value); + assert(mem.eql(u8, result, "f32: 1.34000003e+00\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f32 = 12.34; + const result = try bufPrint(buf1[0..], "f32: {e}\n", value); + assert(mem.eql(u8, result, "f32: 1.23400001e+01\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = -12.34e10; + const result = try bufPrint(buf1[0..], "f64: {e}\n", value); + assert(mem.eql(u8, result, "f64: -1.234e+11\n")); + } + { + // This fails on release due to a minor rounding difference. + // --release-fast outputs 9.999960000000001e-40 vs. the expected. + if (builtin.mode == builtin.Mode.Debug) { var buf1: [32]u8 = undefined; const value: f64 = 9.999960e-40; const result = try bufPrint(buf1[0..], "f64: {e}\n", value); assert(mem.eql(u8, result, "f64: 9.99996e-40\n")); } - { - var buf1: [32]u8 = undefined; - const value: f64 = 1.409706e-42; - const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); - assert(mem.eql(u8, result, "f64: 1.40971e-42\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = @bitCast(f32, u32(814313563)); - const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); - assert(mem.eql(u8, result, "f64: 1.00000e-09\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = @bitCast(f32, u32(1006632960)); - const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); - assert(mem.eql(u8, result, "f64: 7.81250e-03\n")); - } - { - // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. - // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. - var buf1: [32]u8 = undefined; - const value: f64 = @bitCast(f32, u32(1203982400)); - const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); - assert(mem.eql(u8, result, "f64: 1.00001e+05\n")); - } - { - var buf1: [32]u8 = undefined; - const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); - assert(mem.eql(u8, result, "f64: nan\n")); - } - { - var buf1: [32]u8 = undefined; - const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64); - assert(mem.eql(u8, result, "f64: -nan\n")); - } - { - var buf1: [32]u8 = undefined; - const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); - assert(mem.eql(u8, result, "f64: inf\n")); - } - { - var buf1: [32]u8 = undefined; - const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); - assert(mem.eql(u8, result, "f64: -inf\n")); - } - { - var buf1: [64]u8 = undefined; - const value: f64 = 1.52314e+29; - const result = try bufPrint(buf1[0..], "f64: {.}\n", value); - assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f32 = 1.1234; - const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); - assert(mem.eql(u8, result, "f32: 1.1\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f32 = 1234.567; - const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); - assert(mem.eql(u8, result, "f32: 1234.57\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f32 = -11.1234; - const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); - // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). - // -11.12339... is rounded back up to -11.1234 - assert(mem.eql(u8, result, "f32: -11.1234\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f32 = 91.12345; - const result = try bufPrint(buf1[0..], "f32: {.5}\n", value); - assert(mem.eql(u8, result, "f32: 91.12345\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 91.12345678901235; - const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); - assert(mem.eql(u8, result, "f64: 91.1234567890\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 0.0; - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.00000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 5.700; - const result = try bufPrint(buf1[0..], "f64: {.0}\n", value); - assert(mem.eql(u8, result, "f64: 6\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 9.999; - const result = try bufPrint(buf1[0..], "f64: {.1}\n", value); - assert(mem.eql(u8, result, "f64: 10.0\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 1.0; - const result = try bufPrint(buf1[0..], "f64: {.3}\n", value); - assert(mem.eql(u8, result, "f64: 1.000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 0.0003; - const result = try bufPrint(buf1[0..], "f64: {.8}\n", value); - assert(mem.eql(u8, result, "f64: 0.00030000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 1.40130e-45; - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.00000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 9.999960e-40; - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.00000\n")); - } - // libc checks - { - var buf1: [32]u8 = undefined; - const value: f64 = f64(@bitCast(f32, u32(916964781))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.00001\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = f64(@bitCast(f32, u32(925353389))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.00001\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = f64(@bitCast(f32, u32(1036831278))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.10000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = f64(@bitCast(f32, u32(1065353133))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 1.00000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = f64(@bitCast(f32, u32(1092616192))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 10.00000\n")); - } - // libc differences - { - var buf1: [32]u8 = undefined; - // This is 0.015625 exactly according to gdb. We thus round down, - // however glibc rounds up for some reason. This occurs for all - // floats of the form x.yyyy25 on a precision point. - const value: f64 = f64(@bitCast(f32, u32(1015021568))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 0.01563\n")); - } - - // std-windows-x86_64-Debug-bare test case fails - { - // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 - // also rounds to 630 so I'm inclined to believe libc is not - // optimal here. - var buf1: [32]u8 = undefined; - const value: f64 = f64(@bitCast(f32, u32(1518338049))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); - } + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 1.409706e-42; + const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); + assert(mem.eql(u8, result, "f64: 1.40971e-42\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = @bitCast(f32, u32(814313563)); + const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); + assert(mem.eql(u8, result, "f64: 1.00000e-09\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = @bitCast(f32, u32(1006632960)); + const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); + assert(mem.eql(u8, result, "f64: 7.81250e-03\n")); + } + { + // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. + // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. + var buf1: [32]u8 = undefined; + const value: f64 = @bitCast(f32, u32(1203982400)); + const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); + assert(mem.eql(u8, result, "f64: 1.00001e+05\n")); + } + { + var buf1: [32]u8 = undefined; + const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); + assert(mem.eql(u8, result, "f64: nan\n")); + } + { + var buf1: [32]u8 = undefined; + const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64); + assert(mem.eql(u8, result, "f64: -nan\n")); + } + { + var buf1: [32]u8 = undefined; + const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); + assert(mem.eql(u8, result, "f64: inf\n")); + } + { + var buf1: [32]u8 = undefined; + const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); + assert(mem.eql(u8, result, "f64: -inf\n")); + } + { + var buf1: [64]u8 = undefined; + const value: f64 = 1.52314e+29; + const result = try bufPrint(buf1[0..], "f64: {.}\n", value); + assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f32 = 1.1234; + const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); + assert(mem.eql(u8, result, "f32: 1.1\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f32 = 1234.567; + const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); + assert(mem.eql(u8, result, "f32: 1234.57\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f32 = -11.1234; + const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); + // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). + // -11.12339... is rounded back up to -11.1234 + assert(mem.eql(u8, result, "f32: -11.1234\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f32 = 91.12345; + const result = try bufPrint(buf1[0..], "f32: {.5}\n", value); + assert(mem.eql(u8, result, "f32: 91.12345\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 91.12345678901235; + const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); + assert(mem.eql(u8, result, "f64: 91.1234567890\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 0.0; + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.00000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 5.700; + const result = try bufPrint(buf1[0..], "f64: {.0}\n", value); + assert(mem.eql(u8, result, "f64: 6\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 9.999; + const result = try bufPrint(buf1[0..], "f64: {.1}\n", value); + assert(mem.eql(u8, result, "f64: 10.0\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 1.0; + const result = try bufPrint(buf1[0..], "f64: {.3}\n", value); + assert(mem.eql(u8, result, "f64: 1.000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 0.0003; + const result = try bufPrint(buf1[0..], "f64: {.8}\n", value); + assert(mem.eql(u8, result, "f64: 0.00030000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 1.40130e-45; + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.00000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 9.999960e-40; + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.00000\n")); + } + // libc checks + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(916964781))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.00001\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(925353389))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.00001\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(1036831278))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.10000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(1065353133))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 1.00000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(1092616192))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 10.00000\n")); + } + // libc differences + { + var buf1: [32]u8 = undefined; + // This is 0.015625 exactly according to gdb. We thus round down, + // however glibc rounds up for some reason. This occurs for all + // floats of the form x.yyyy25 on a precision point. + const value: f64 = f64(@bitCast(f32, u32(1015021568))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 0.01563\n")); + } + // std-windows-x86_64-Debug-bare test case fails + { + // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 + // also rounds to 630 so I'm inclined to believe libc is not + // optimal here. + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(1518338049))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); } } -- cgit v1.2.3 From 89a4c373d3756baeac9d2780b1249ada37961d16 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Apr 2018 12:06:18 -0400 Subject: fix bigint twos complement implementation closes #948 --- src/bigint.cpp | 5 +++++ test/cases/eval.zig | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/bigint.cpp b/src/bigint.cpp index 85e5dad4ad..2a688debd5 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -86,6 +86,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count) size_t digits_to_copy = bit_count / 64; size_t leftover_bits = bit_count % 64; dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1); + if (dest->digit_count == 1 && leftover_bits == 0) { + dest->data.digit = op_digits[0]; + if (dest->data.digit == 0) dest->digit_count = 0; + return; + } dest->data.digits = allocate_nonzero(dest->digit_count); for (size_t i = 0; i < digits_to_copy; i += 1) { uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0; diff --git a/test/cases/eval.zig b/test/cases/eval.zig index d6f7afe864..e13d4340e7 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -513,3 +513,19 @@ test "array concat of slices gives slice" { assert(std.mem.eql(u8, c, "aoeuasdf")); } } + +test "comptime shlWithOverflow" { + const ct_shifted: u64 = comptime amt: { + var amt = u64(0); + _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); + break :amt amt; + }; + + const rt_shifted: u64 = amt: { + var amt = u64(0); + _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); + break :amt amt; + }; + + assert(ct_shifted == rt_shifted); +} -- cgit v1.2.3 From 15bf0c1541479870dff1f8d64a3746c337f901ef Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Apr 2018 18:06:33 -0400 Subject: fix interaction between defer and labeled break closes #830 --- src/ir.cpp | 3 +++ test/cases/defer.zig | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index cd00fc6230..86c77758b2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3147,6 +3147,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) { return noreturn_return_value; } + + ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); + return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } else { incoming_blocks.append(irb->current_basic_block); incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node))); diff --git a/test/cases/defer.zig b/test/cases/defer.zig index a989af18c2..5470b4bbd0 100644 --- a/test/cases/defer.zig +++ b/test/cases/defer.zig @@ -41,3 +41,14 @@ fn testBreakContInDefer(x: usize) void { assert(i == 5); } } + +test "defer and labeled break" { + var i = usize(0); + + blk: { + defer i += 1; + break :blk; + } + + assert(i == 1); +} -- cgit v1.2.3 From 7eab62325b539be4aacb17fd64f5b4445c8409e7 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 01:49:22 +0300 Subject: One step towards @typeInfo --- src/all_types.hpp | 8 +++ src/codegen.cpp | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ir.cpp | 47 +++++++++++++++++ src/ir_print.cpp | 9 ++++ 4 files changed, 217 insertions(+) diff --git a/src/all_types.hpp b/src/all_types.hpp index d1b2ad61d2..332fc5204e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1293,6 +1293,7 @@ enum BuiltinFnId { BuiltinFnIdMemberType, BuiltinFnIdMemberName, BuiltinFnIdField, + BuiltinFnIdTypeInfo, BuiltinFnIdTypeof, BuiltinFnIdAddWithOverflow, BuiltinFnIdSubWithOverflow, @@ -2037,6 +2038,7 @@ enum IrInstructionId { IrInstructionIdTagType, IrInstructionIdFieldParentPtr, IrInstructionIdOffsetOf, + IrInstructionIdTypeInfo, IrInstructionIdTypeId, IrInstructionIdSetEvalBranchQuota, IrInstructionIdPtrTypeOf, @@ -2858,6 +2860,12 @@ struct IrInstructionOffsetOf { IrInstruction *field_name; }; +struct IrInstructionTypeInfo { + IrInstruction base; + + IrInstruction *type_value; +}; + struct IrInstructionTypeId { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 2d8c385f44..ea2d9f463e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4502,6 +4502,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdDeclRef: case IrInstructionIdSwitchVar: case IrInstructionIdOffsetOf: + case IrInstructionIdTypeInfo: case IrInstructionIdTypeId: case IrInstructionIdSetEvalBranchQuota: case IrInstructionIdPtrTypeOf: @@ -6125,6 +6126,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2); create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2); create_builtin_fn(g, BuiltinFnIdField, "field", 2); + create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1); create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); @@ -6344,6 +6346,157 @@ static void define_builtin_compile_vars(CodeGen *g) { } buf_appendf(contents, "};\n\n"); } + { + buf_appendf(contents, + "pub const IntInfo = struct {\n" + " is_signed: bool,\n" + " bits: u8,\n" + "};\n" + "\n" + "pub const FloatInfo = struct {\n" + " bits: u8,\n" + "};\n" + "\n" + "pub const PointerInfo = struct {\n" + " is_const: bool,\n" + " is_volatile: bool,\n" + " alignment: u32,\n" + " child: &TypeInfo,\n" + "};\n" + "\n" + "pub const ArrayInfo = struct {\n" + " len: u64,\n" + " child: &TypeInfo,\n" + "};\n" + "\n" + "pub const ContainerLayout = enum {\n" + " Auto,\n" + " Extern,\n" + " Packed,\n" + "};\n" + "\n" + "pub const StructFieldInfo = struct {\n" + " name: []const u8,\n" + " offset: usize,\n" + " type_info: TypeInfo,\n" + "};\n" + "\n" + "pub const StructInfo = struct {\n" + " layout: ContainerLayout,\n" + " fields: []StructFieldInfo,\n" + "};\n" + "\n" + "pub const NullableInfo = struct {\n" + " child: &TypeInfo,\n" + "};\n" + "\n" + "pub const ErrorUnionInfo = struct {\n" + " error_set: ErrorSetInfo,\n" + " payload: &TypeInfo,\n" + "};\n" + "\n" + "pub const ErrorInfo = struct {\n" + " name: []const u8,\n" + " value: usize,\n" + "};\n" + "\n" + "pub const ErrorSetInfo = struct {\n" + " errors: []ErrorInfo,\n" + "};\n" + "\n" + "pub const EnumFieldInfo = struct {\n" + " name: []const u8,\n" + " value: usize,\n" + "};\n" + "\n" + "pub const EnumInfo = struct {\n" + " layout: ContainerLayout,\n" + " tag_type: IntInfo,\n" + " fields: []EnumFieldInfo,\n" + "};\n" + "\n" + "pub const UnionFieldInfo = struct {\n" + " name: []const u8,\n" + " enum_field: EnumFieldInfo,\n" + " type_info: TypeInfo,\n" + "};\n" + "\n" + "pub const UnionInfo = struct {\n" + " layout: ContainerLayout,\n" + " tag_type: ?EnumInfo,\n" + " fields: []UnionFieldInfo,\n" + "};\n" + "\n" + "pub const CallingConvention = enum {\n" + " Unspecified,\n" + " C,\n" + " Cold,\n" + " Naked,\n" + " Stdcall,\n" + " Async,\n" + "};\n" + "\n" + "pub const FnArgInfo = struct {\n" + " is_comptime: bool,\n" + " name: []const u8,\n" + " type_info: TypeInfo,\n" + "};\n" + "\n" + "pub const FnInfo = struct {\n" + " calling_convention: CallingConvention,\n" + " is_generic: bool,\n" + " is_varargs: bool,\n" + " return_type: &TypeInfo,\n" + " args: []FnArgInfo,\n" + "};\n" + "\n" + "pub const BoundFnInfo = struct {\n" + " bound_type: &TypeInfo,\n" + " fn_info: FnInfo,\n" + "};\n" + "\n" + "pub const PromiseInfo = struct {\n" + " child: ?&TypeInfo,\n" + "};\n" + "\n" + "pub const TypeInfo = union(TypeId) {\n" + " Type: void,\n" + " Void: void,\n" + " Bool: void,\n" + " NoReturn: void,\n" + " Int: IntInfo,\n" + " Float: FloatInfo,\n" + " Pointer: PointerInfo,\n" + " Array: ArrayInfo,\n" + " Struct: StructInfo,\n" + " FloatLiteral: void,\n" + " IntLiteral: void,\n" + " UndefinedLiteral: void,\n" + " NullLiteral: void,\n" + " Nullable: NullableInfo,\n" + " ErrorUnion: ErrorUnionInfo,\n" + " ErrorSet: ErrorSetInfo,\n" + " Enum: EnumInfo,\n" + " Union: UnionInfo,\n" + " Fn: FnInfo,\n" + " Namespace: void,\n" + " Block: void,\n" + " BoundFn: BoundFnInfo,\n" + " ArgTuple: void,\n" + " Opaque: void,\n" + " Promise: PromiseInfo,\n" + "};\n\n"); + assert(ContainerLayoutAuto == 0); + assert(ContainerLayoutExtern == 1); + assert(ContainerLayoutPacked == 2); + + assert(CallingConventionUnspecified == 0); + assert(CallingConventionC == 1); + assert(CallingConventionCold == 2); + assert(CallingConventionNaked == 3); + assert(CallingConventionStdcall == 4); + assert(CallingConventionAsync == 5); + } { buf_appendf(contents, "pub const FloatMode = enum {\n" diff --git a/src/ir.cpp b/src/ir.cpp index cd00fc6230..69897f3b91 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -615,6 +615,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionOffsetOf *) { return IrInstructionIdOffsetOf; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) { + return IrInstructionIdTypeInfo; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeId *) { return IrInstructionIdTypeId; } @@ -2440,6 +2444,16 @@ static IrInstruction *ir_build_offset_of(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } +static IrInstruction *ir_build_type_info(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *type_value) { + IrInstructionTypeInfo *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; + + ir_ref_instruction(type_value, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_type_id(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { @@ -4080,6 +4094,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_load_ptr(irb, scope, node, ptr_instruction); } + case BuiltinFnIdTypeInfo: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *type_info = ir_build_type_info(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, type_info, lval); + } case BuiltinFnIdBreakpoint: return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval); case BuiltinFnIdReturnAddress: @@ -15669,6 +15693,26 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira, return ira->codegen->builtin_types.entry_num_lit_int; } +static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, + IrInstructionTypeInfo *instruction) +{ + IrInstruction *type_value = instruction->type_value->other; + TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); + if (type_is_invalid(type_entry)) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeInfo"); + assert(var_value->type->id == TypeTableEntryIdMetaType); + TypeTableEntry *result_type = var_value->data.x_type; + + // TODO: Check if we need to explicitely make a &const TypeInfo here, I think we don't. + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_struct.fields = create_const_vals(1); + // TODO: Fill the struct + zig_panic("Building TypeInfo..."); + return result_type; +} + static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira, IrInstructionTypeId *instruction) { @@ -18555,6 +18599,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction); case IrInstructionIdOffsetOf: return ir_analyze_instruction_offset_of(ira, (IrInstructionOffsetOf *)instruction); + case IrInstructionIdTypeInfo: + return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction); case IrInstructionIdTypeId: return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction); case IrInstructionIdSetEvalBranchQuota: @@ -18821,6 +18867,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdTagName: case IrInstructionIdFieldParentPtr: case IrInstructionIdOffsetOf: + case IrInstructionIdTypeInfo: case IrInstructionIdTypeId: case IrInstructionIdAlignCast: case IrInstructionIdOpaqueType: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a77ae244d4..9678120f1d 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -966,6 +966,12 @@ static void ir_print_offset_of(IrPrint *irp, IrInstructionOffsetOf *instruction) fprintf(irp->f, ")"); } +static void ir_print_type_info(IrPrint *irp, IrInstructionTypeInfo *instruction) { + fprintf(irp->f, "@typeInfo("); + ir_print_other_instruction(irp, instruction->type_value); + fprintf(irp->f, ")"); +} + static void ir_print_type_id(IrPrint *irp, IrInstructionTypeId *instruction) { fprintf(irp->f, "@typeId("); ir_print_other_instruction(irp, instruction->type_value); @@ -1536,6 +1542,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdOffsetOf: ir_print_offset_of(irp, (IrInstructionOffsetOf *)instruction); break; + case IrInstructionIdTypeInfo: + ir_print_type_info(irp, (IrInstructionTypeInfo *)instruction); + break; case IrInstructionIdTypeId: ir_print_type_id(irp, (IrInstructionTypeId *)instruction); break; -- cgit v1.2.3 From d5e99cc05ea05d701adffce55defc512d75e10d1 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Tue, 24 Apr 2018 19:18:31 +1200 Subject: Add initial complex-number support - Library type instead of builtin - All C complex functions implemented Partial WIP: Needs more tests for edge cases. --- CMakeLists.txt | 22 ++++++ std/math/complex/abs.zig | 18 +++++ std/math/complex/acos.zig | 24 +++++++ std/math/complex/acosh.zig | 24 +++++++ std/math/complex/arg.zig | 18 +++++ std/math/complex/asin.zig | 30 ++++++++ std/math/complex/asinh.zig | 25 +++++++ std/math/complex/atan.zig | 136 +++++++++++++++++++++++++++++++++++ std/math/complex/atanh.zig | 25 +++++++ std/math/complex/conj.zig | 17 +++++ std/math/complex/cos.zig | 24 +++++++ std/math/complex/cosh.zig | 171 ++++++++++++++++++++++++++++++++++++++++++++ std/math/complex/exp.zig | 146 ++++++++++++++++++++++++++++++++++++++ std/math/complex/index.zig | 172 +++++++++++++++++++++++++++++++++++++++++++++ std/math/complex/ldexp.zig | 75 ++++++++++++++++++++ std/math/complex/log.zig | 26 +++++++ std/math/complex/pow.zig | 25 +++++++ std/math/complex/proj.zig | 24 +++++++ std/math/complex/sin.zig | 25 +++++++ std/math/complex/sinh.zig | 170 ++++++++++++++++++++++++++++++++++++++++++++ std/math/complex/sqrt.zig | 140 ++++++++++++++++++++++++++++++++++++ std/math/complex/tan.zig | 25 +++++++ std/math/complex/tanh.zig | 117 ++++++++++++++++++++++++++++++ std/math/index.zig | 5 ++ 24 files changed, 1484 insertions(+) create mode 100644 std/math/complex/abs.zig create mode 100644 std/math/complex/acos.zig create mode 100644 std/math/complex/acosh.zig create mode 100644 std/math/complex/arg.zig create mode 100644 std/math/complex/asin.zig create mode 100644 std/math/complex/asinh.zig create mode 100644 std/math/complex/atan.zig create mode 100644 std/math/complex/atanh.zig create mode 100644 std/math/complex/conj.zig create mode 100644 std/math/complex/cos.zig create mode 100644 std/math/complex/cosh.zig create mode 100644 std/math/complex/exp.zig create mode 100644 std/math/complex/index.zig create mode 100644 std/math/complex/ldexp.zig create mode 100644 std/math/complex/log.zig create mode 100644 std/math/complex/pow.zig create mode 100644 std/math/complex/proj.zig create mode 100644 std/math/complex/sin.zig create mode 100644 std/math/complex/sinh.zig create mode 100644 std/math/complex/sqrt.zig create mode 100644 std/math/complex/tan.zig create mode 100644 std/math/complex/tanh.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index e692974719..9bf4bdd709 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -498,6 +498,28 @@ set(ZIG_STD_FILES "math/tan.zig" "math/tanh.zig" "math/trunc.zig" + "math/complex/abs.zig" + "math/complex/acosh.zig" + "math/complex/acos.zig" + "math/complex/arg.zig" + "math/complex/asinh.zig" + "math/complex/asin.zig" + "math/complex/atanh.zig" + "math/complex/atan.zig" + "math/complex/conj.zig" + "math/complex/cosh.zig" + "math/complex/cos.zig" + "math/complex/exp.zig" + "math/complex/index.zig" + "math/complex/ldexp.zig" + "math/complex/log.zig" + "math/complex/pow.zig" + "math/complex/proj.zig" + "math/complex/sinh.zig" + "math/complex/sin.zig" + "math/complex/sqrt.zig" + "math/complex/tanh.zig" + "math/complex/tan.zig" "mem.zig" "net.zig" "os/child_process.zig" diff --git a/std/math/complex/abs.zig b/std/math/complex/abs.zig new file mode 100644 index 0000000000..4cd095c46b --- /dev/null +++ b/std/math/complex/abs.zig @@ -0,0 +1,18 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn abs(z: var) @typeOf(z.re) { + const T = @typeOf(z.re); + return math.hypot(T, z.re, z.im); +} + +const epsilon = 0.0001; + +test "complex.cabs" { + const a = Complex(f32).new(5, 3); + const c = abs(a); + debug.assert(math.approxEq(f32, c, 5.83095, epsilon)); +} diff --git a/std/math/complex/acos.zig b/std/math/complex/acos.zig new file mode 100644 index 0000000000..cd37ddd92e --- /dev/null +++ b/std/math/complex/acos.zig @@ -0,0 +1,24 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn acos(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + const q = cmath.asin(z); + return Complex(T).new(T(math.pi) / 2 - q.re, -q.im); +} + +const epsilon = 0.0001; + +test "complex.cacos" { + const a = Complex(f32).new(5, 3); + const c = acos(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, 0.546975, epsilon)); + debug.assert(math.approxEq(f32, im, -2.452914, epsilon)); +} diff --git a/std/math/complex/acosh.zig b/std/math/complex/acosh.zig new file mode 100644 index 0000000000..830ff79e5f --- /dev/null +++ b/std/math/complex/acosh.zig @@ -0,0 +1,24 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn acosh(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + const q = cmath.acos(z); + return Complex(T).new(-q.im, q.re); +} + +const epsilon = 0.0001; + +test "complex.cacosh" { + const a = Complex(f32).new(5, 3); + const c = acosh(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, 2.452914, epsilon)); + debug.assert(math.approxEq(f32, im, 0.546975, epsilon)); +} diff --git a/std/math/complex/arg.zig b/std/math/complex/arg.zig new file mode 100644 index 0000000000..f24512ac73 --- /dev/null +++ b/std/math/complex/arg.zig @@ -0,0 +1,18 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn arg(z: var) @typeOf(z.re) { + const T = @typeOf(z.re); + return math.atan2(T, z.im, z.re); +} + +const epsilon = 0.0001; + +test "complex.carg" { + const a = Complex(f32).new(5, 3); + const c = arg(a); + debug.assert(math.approxEq(f32, c, 0.540420, epsilon)); +} diff --git a/std/math/complex/asin.zig b/std/math/complex/asin.zig new file mode 100644 index 0000000000..84cabc6941 --- /dev/null +++ b/std/math/complex/asin.zig @@ -0,0 +1,30 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn asin(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + const x = z.re; + const y = z.im; + + const p = Complex(T).new(1.0 - (x - y) * (x + y), -2.0 * x * y); + const q = Complex(T).new(-y, x); + const r = cmath.log(q.add(cmath.sqrt(p))); + + return Complex(T).new(r.im, -r.re); +} + +const epsilon = 0.0001; + +test "complex.casin" { + const a = Complex(f32).new(5, 3); + const c = asin(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, 1.023822, epsilon)); + debug.assert(math.approxEq(f32, im, 2.452914, epsilon)); +} diff --git a/std/math/complex/asinh.zig b/std/math/complex/asinh.zig new file mode 100644 index 0000000000..5cf086d520 --- /dev/null +++ b/std/math/complex/asinh.zig @@ -0,0 +1,25 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn asinh(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + const q = Complex(T).new(-z.im, z.re); + const r = cmath.asin(q); + return Complex(T).new(r.im, -r.re); +} + +const epsilon = 0.0001; + +test "complex.casinh" { + const a = Complex(f32).new(5, 3); + const c = asinh(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, 2.459831, epsilon)); + debug.assert(math.approxEq(f32, im, 0.533999, epsilon)); +} diff --git a/std/math/complex/atan.zig b/std/math/complex/atan.zig new file mode 100644 index 0000000000..2aa0211333 --- /dev/null +++ b/std/math/complex/atan.zig @@ -0,0 +1,136 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn atan(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + return switch (T) { + f32 => atan32(z), + f64 => atan64(z), + else => @compileError("atan not implemented for " ++ @typeName(z)), + }; +} + +fn redupif32(x: f32) f32 { + const DP1 = 3.140625; + const DP2 = 9.67502593994140625e-4; + const DP3 = 1.509957990978376432e-7; + + var t = x / math.pi; + if (t >= 0.0) { + t += 0.5; + } else { + t -= 0.5; + } + + const u = f32(i32(t)); + return ((x - u * DP1) - u * DP2) - t * DP3; +} + +fn atan32(z: &const Complex(f32)) Complex(f32) { + const maxnum = 1.0e38; + + const x = z.re; + const y = z.im; + + if ((x == 0.0) and (y > 1.0)) { + // overflow + return Complex(f32).new(maxnum, maxnum); + } + + const x2 = x * x; + var a = 1.0 - x2 - (y * y); + if (a == 0.0) { + // overflow + return Complex(f32).new(maxnum, maxnum); + } + + var t = 0.5 * math.atan2(f32, 2.0 * x, a); + var w = redupif32(t); + + t = y - 1.0; + a = x2 + t * t; + if (a == 0.0) { + // overflow + return Complex(f32).new(maxnum, maxnum); + } + + t = y + 1.0; + a = (x2 + (t * t)) / a; + return Complex(f32).new(w, 0.25 * math.ln(a)); +} + +fn redupif64(x: f64) f64 { + const DP1 = 3.14159265160560607910; + const DP2 = 1.98418714791870343106e-9; + const DP3 = 1.14423774522196636802e-17; + + var t = x / math.pi; + if (t >= 0.0) { + t += 0.5; + } else { + t -= 0.5; + } + + const u = f64(i64(t)); + return ((x - u * DP1) - u * DP2) - t * DP3; +} + +fn atan64(z: &const Complex(f64)) Complex(f64) { + const maxnum = 1.0e308; + + const x = z.re; + const y = z.im; + + if ((x == 0.0) and (y > 1.0)) { + // overflow + return Complex(f64).new(maxnum, maxnum); + } + + const x2 = x * x; + var a = 1.0 - x2 - (y * y); + if (a == 0.0) { + // overflow + return Complex(f64).new(maxnum, maxnum); + } + + var t = 0.5 * math.atan2(f64, 2.0 * x, a); + var w = redupif64(t); + + t = y - 1.0; + a = x2 + t * t; + if (a == 0.0) { + // overflow + return Complex(f64).new(maxnum, maxnum); + } + + t = y + 1.0; + a = (x2 + (t * t)) / a; + return Complex(f64).new(w, 0.25 * math.ln(a)); +} + +const epsilon = 0.0001; + +test "complex.ctan32" { + const a = Complex(f32).new(5, 3); + const c = atan(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, 1.423679, epsilon)); + debug.assert(math.approxEq(f32, im, 0.086569, epsilon)); +} + +test "complex.ctan64" { + const a = Complex(f64).new(5, 3); + const c = atan(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f64, re, 1.423679, epsilon)); + debug.assert(math.approxEq(f64, im, 0.086569, epsilon)); +} diff --git a/std/math/complex/atanh.zig b/std/math/complex/atanh.zig new file mode 100644 index 0000000000..5fbbe2eb4b --- /dev/null +++ b/std/math/complex/atanh.zig @@ -0,0 +1,25 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn atanh(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + const q = Complex(T).new(-z.im, z.re); + const r = cmath.atan(q); + return Complex(T).new(r.im, -r.re); +} + +const epsilon = 0.0001; + +test "complex.catanh" { + const a = Complex(f32).new(5, 3); + const c = atanh(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, 0.146947, epsilon)); + debug.assert(math.approxEq(f32, im, 1.480870, epsilon)); +} diff --git a/std/math/complex/conj.zig b/std/math/complex/conj.zig new file mode 100644 index 0000000000..ad3e8b5036 --- /dev/null +++ b/std/math/complex/conj.zig @@ -0,0 +1,17 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn conj(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + return Complex(T).new(z.re, -z.im); +} + +test "complex.conj" { + const a = Complex(f32).new(5, 3); + const c = a.conjugate(); + + debug.assert(c.re == 5 and c.im == -3); +} diff --git a/std/math/complex/cos.zig b/std/math/complex/cos.zig new file mode 100644 index 0000000000..35908898a6 --- /dev/null +++ b/std/math/complex/cos.zig @@ -0,0 +1,24 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn cos(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + const p = Complex(T).new(-z.im, z.re); + return cmath.cosh(p); +} + +const epsilon = 0.0001; + +test "complex.ccos" { + const a = Complex(f32).new(5, 3); + const c = cos(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, 2.855815, epsilon)); + debug.assert(math.approxEq(f32, im, 9.606383, epsilon)); +} diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig new file mode 100644 index 0000000000..1387ecb05f --- /dev/null +++ b/std/math/complex/cosh.zig @@ -0,0 +1,171 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; + +pub fn cosh(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + return switch (T) { + f32 => cosh32(z), + f64 => cosh64(z), + else => @compileError("cosh not implemented for " ++ @typeName(z)), + }; +} + +fn cosh32(z: &const Complex(f32)) Complex(f32) { + const x = z.re; + const y = z.im; + + const hx = @bitCast(u32, x); + const ix = hx & 0x7fffffff; + + const hy = @bitCast(u32, y); + const iy = hy & 0x7fffffff; + + if (ix < 0x7f800000 and iy < 0x7f800000) { + if (iy == 0) { + return Complex(f32).new(math.cosh(x), y); + } + // small x: normal case + if (ix < 0x41100000) { + return Complex(f32).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y)); + } + + // |x|>= 9, so cosh(x) ~= exp(|x|) + if (ix < 0x42b17218) { + // x < 88.7: exp(|x|) won't overflow + const h = math.exp(math.fabs(x)) * 0.5; + return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y)); + } + // x < 192.7: scale to avoid overflow + else if (ix < 0x4340b1e7) { + const v = Complex(f32).new(math.fabs(x), y); + const r = ldexp_cexp(v, -1); + return Complex(f32).new(x, y * math.copysign(f32, 1, x)); + } + // x >= 192.7: result always overflows + else { + const h = 0x1p127 * x; + return Complex(f32).new(h * h * math.cos(y), h * math.sin(y)); + } + } + + if (ix == 0 and iy >= 0x7f800000) { + return Complex(f32).new(y - y, math.copysign(f32, 0, x * (y - y))); + } + + if (iy == 0 and ix >= 0x7f800000) { + if (hx & 0x7fffff == 0) { + return Complex(f32).new(x * x, math.copysign(f32, 0, x) * y); + } + return Complex(f32).new(x, math.copysign(f32, 0, (x + x) * y)); + } + + if (ix < 0x7f800000 and iy >= 0x7f800000) { + return Complex(f32).new(y - y, x * (y - y)); + } + + if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) { + if (iy >= 0x7f800000) { + return Complex(f32).new(x * x, x * (y - y)); + } + return Complex(f32).new((x * x) * math.cos(y), x * math.sin(y)); + } + + return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y)); +} + +fn cosh64(z: &const Complex(f64)) Complex(f64) { + const x = z.re; + const y = z.im; + + const fx = @bitCast(u64, x); + const hx = u32(fx >> 32); + const lx = @truncate(u32, fx); + const ix = hx & 0x7fffffff; + + const fy = @bitCast(u64, y); + const hy = u32(fy >> 32); + const ly = @truncate(u32, fy); + const iy = hy & 0x7fffffff; + + // nearly non-exceptional case where x, y are finite + if (ix < 0x7ff00000 and iy < 0x7ff00000) { + if (iy | ly == 0) { + return Complex(f64).new(math.cosh(x), x * y); + } + // small x: normal case + if (ix < 0x40360000) { + return Complex(f64).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y)); + } + + // |x|>= 22, so cosh(x) ~= exp(|x|) + if (ix < 0x40862e42) { + // x < 710: exp(|x|) won't overflow + const h = math.exp(math.fabs(x)) * 0.5; + return Complex(f64).new(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y)); + } + // x < 1455: scale to avoid overflow + else if (ix < 0x4096bbaa) { + const v = Complex(f64).new(math.fabs(x), y); + const r = ldexp_cexp(v, -1); + return Complex(f64).new(x, y * math.copysign(f64, 1, x)); + } + // x >= 1455: result always overflows + else { + const h = 0x1p1023; + return Complex(f64).new(h * h * math.cos(y), h * math.sin(y)); + } + } + + if (ix | lx == 0 and iy >= 0x7ff00000) { + return Complex(f64).new(y - y, math.copysign(f64, 0, x * (y - y))); + } + + if (iy | ly == 0 and ix >= 0x7ff00000) { + if ((hx & 0xfffff) | lx == 0) { + return Complex(f64).new(x * x, math.copysign(f64, 0, x) * y); + } + return Complex(f64).new(x * x, math.copysign(f64, 0, (x + x) * y)); + } + + if (ix < 0x7ff00000 and iy >= 0x7ff00000) { + return Complex(f64).new(y - y, x * (y - y)); + } + + if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) { + if (iy >= 0x7ff00000) { + return Complex(f64).new(x * x, x * (y - y)); + } + return Complex(f64).new(x * x * math.cos(y), x * math.sin(y)); + } + + return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y)); +} + +const epsilon = 0.0001; + +test "complex.ccosh32" { + const a = Complex(f32).new(5, 3); + const c = cosh(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, -73.467300, epsilon)); + debug.assert(math.approxEq(f32, im, 10.471557, epsilon)); +} + +test "complex.ccosh64" { + const a = Complex(f64).new(5, 3); + const c = cosh(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f64, re, -73.467300, epsilon)); + debug.assert(math.approxEq(f64, im, 10.471557, epsilon)); +} diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig new file mode 100644 index 0000000000..ace596771a --- /dev/null +++ b/std/math/complex/exp.zig @@ -0,0 +1,146 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; + +pub fn exp(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + + return switch (T) { + f32 => exp32(z), + f64 => exp64(z), + else => @compileError("exp not implemented for " ++ @typeName(z)), + }; +} + +fn exp32(z: &const Complex(f32)) Complex(f32) { + @setFloatMode(this, @import("builtin").FloatMode.Strict); + + const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955 + const cexp_overflow = 0x43400074; // (max_exp - min_denom_exp) * ln2 + + const x = z.re; + const y = z.im; + + const hy = @bitCast(u32, y) & 0x7fffffff; + // cexp(x + i0) = exp(x) + i0 + if (hy == 0) { + return Complex(f32).new(math.exp(x), y); + } + + const hx = @bitCast(u32, x); + // cexp(0 + iy) = cos(y) + isin(y) + if ((hx & 0x7fffffff) == 0) { + return Complex(f32).new(math.cos(y), math.sin(y)); + } + + if (hy >= 0x7f800000) { + // cexp(finite|nan +- i inf|nan) = nan + i nan + if ((hx & 0x7fffffff) != 0x7f800000) { + return Complex(f32).new(y - y, y - y); + } + // cexp(-inf +- i inf|nan) = 0 + i0 + else if (hx & 0x80000000 != 0) { + return Complex(f32).new(0, 0); + } + // cexp(+inf +- i inf|nan) = inf + i nan + else { + return Complex(f32).new(x, y - y); + } + } + + // 88.7 <= x <= 192 so must scale + if (hx >= exp_overflow and hx <= cexp_overflow) { + return ldexp_cexp(z, 0); + } + // - x < exp_overflow => exp(x) won't overflow (common) + // - x > cexp_overflow, so exp(x) * s overflows for s > 0 + // - x = +-inf + // - x = nan + else { + const exp_x = math.exp(x); + return Complex(f32).new(exp_x * math.cos(y), exp_x * math.sin(y)); + } +} + +fn exp64(z: &const Complex(f64)) Complex(f64) { + const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710 + const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2 + + const x = z.re; + const y = z.im; + + const fy = @bitCast(u64, y); + const hy = u32(fy >> 32) & 0x7fffffff; + const ly = @truncate(u32, fy); + + // cexp(x + i0) = exp(x) + i0 + if (hy | ly == 0) { + return Complex(f64).new(math.exp(x), y); + } + + const fx = @bitCast(u64, x); + const hx = u32(fx >> 32); + const lx = @truncate(u32, fx); + + // cexp(0 + iy) = cos(y) + isin(y) + if ((hx & 0x7fffffff) | lx == 0) { + return Complex(f64).new(math.cos(y), math.sin(y)); + } + + if (hy >= 0x7ff00000) { + // cexp(finite|nan +- i inf|nan) = nan + i nan + if (lx != 0 or (hx & 0x7fffffff) != 0x7ff00000) { + return Complex(f64).new(y - y, y - y); + } + // cexp(-inf +- i inf|nan) = 0 + i0 + else if (hx & 0x80000000 != 0) { + return Complex(f64).new(0, 0); + } + // cexp(+inf +- i inf|nan) = inf + i nan + else { + return Complex(f64).new(x, y - y); + } + } + + // 709.7 <= x <= 1454.3 so must scale + if (hx >= exp_overflow and hx <= cexp_overflow) { + const r = ldexp_cexp(z, 0); + return *r; + } + // - x < exp_overflow => exp(x) won't overflow (common) + // - x > cexp_overflow, so exp(x) * s overflows for s > 0 + // - x = +-inf + // - x = nan + else { + const exp_x = math.exp(x); + return Complex(f64).new(exp_x * math.cos(y), exp_x * math.sin(y)); + } +} + +const epsilon = 0.0001; + +test "complex.cexp32" { + const a = Complex(f32).new(5, 3); + const c = exp(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, -146.927917, epsilon)); + debug.assert(math.approxEq(f32, im, 20.944065, epsilon)); +} + +test "complex.cexp64" { + const a = Complex(f32).new(5, 3); + const c = exp(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f64, re, -146.927917, epsilon)); + debug.assert(math.approxEq(f64, im, 20.944065, epsilon)); +} diff --git a/std/math/complex/index.zig b/std/math/complex/index.zig new file mode 100644 index 0000000000..24f726453f --- /dev/null +++ b/std/math/complex/index.zig @@ -0,0 +1,172 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; + +pub const abs = @import("abs.zig").abs; +pub const acosh = @import("acosh.zig").acosh; +pub const acos = @import("acos.zig").acos; +pub const arg = @import("arg.zig").arg; +pub const asinh = @import("asinh.zig").asinh; +pub const asin = @import("asin.zig").asin; +pub const atanh = @import("atanh.zig").atanh; +pub const atan = @import("atan.zig").atan; +pub const conj = @import("conj.zig").conj; +pub const cosh = @import("cosh.zig").cosh; +pub const cos = @import("cos.zig").cos; +pub const exp = @import("exp.zig").exp; +pub const log = @import("log.zig").log; +pub const pow = @import("pow.zig").pow; +pub const proj = @import("proj.zig").proj; +pub const sinh = @import("sinh.zig").sinh; +pub const sin = @import("sin.zig").sin; +pub const sqrt = @import("sqrt.zig").sqrt; +pub const tanh = @import("tanh.zig").tanh; +pub const tan = @import("tan.zig").tan; + +// NOTE: Make this a builtin at some point? +pub fn Complex(comptime T: type) type { + return struct { + const Self = this; + + re: T, + im: T, + + pub fn new(re: T, im: T) Self { + return Self { + .re = re, + .im = im, + }; + } + + pub fn add(self: &const Self, other: &const Self) Self { + return Self { + .re = self.re + other.re, + .im = self.im + other.im, + }; + } + + pub fn sub(self: &const Self, other: &const Self) Self { + return Self { + .re = self.re - other.re, + .im = self.im - other.im, + }; + } + + pub fn mul(self: &const Self, other: &const Self) Self { + return Self { + .re = self.re * other.re - self.im * other.im, + .im = self.im * other.re + self.re * other.im, + }; + } + + pub fn div(self: &const Self, other: &const Self) Self { + const re_num = self.re * other.re + self.im * other.im; + const im_num = self.im * other.re - self.re * other.im; + const den = other.re * other.re + other.im * other.im; + + return Self { + .re = re_num / den, + .im = im_num / den, + }; + } + + pub fn conjugate(self: &const Self) Self { + return Self { + .re = self.re, + .im = -self.im, + }; + } + + pub fn reciprocal(self: &const Self) Self { + const m = self.re * self.re + self.im * self.im; + return Self { + .re = self.re / m, + .im = -self.im / m, + }; + } + + pub fn magnitude(self: &const Self) T { + return math.sqrt(self.re * self.re + self.im * self.im); + } + }; +} + +const epsilon = 0.0001; + +test "complex.add" { + const a = Complex(f32).new(5, 3); + const b = Complex(f32).new(2, 7); + const c = a.add(b); + + debug.assert(c.re == 7 and c.im == 10); +} + +test "complex.sub" { + const a = Complex(f32).new(5, 3); + const b = Complex(f32).new(2, 7); + const c = a.sub(b); + + debug.assert(c.re == 3 and c.im == -4); +} + +test "complex.mul" { + const a = Complex(f32).new(5, 3); + const b = Complex(f32).new(2, 7); + const c = a.mul(b); + + debug.assert(c.re == -11 and c.im == 41); +} + +test "complex.div" { + const a = Complex(f32).new(5, 3); + const b = Complex(f32).new(2, 7); + const c = a.div(b); + + debug.assert(math.approxEq(f32, c.re, f32(31)/53, epsilon) and + math.approxEq(f32, c.im, f32(-29)/53, epsilon)); +} + +test "complex.conjugate" { + const a = Complex(f32).new(5, 3); + const c = a.conjugate(); + + debug.assert(c.re == 5 and c.im == -3); +} + +test "complex.reciprocal" { + const a = Complex(f32).new(5, 3); + const c = a.reciprocal(); + + debug.assert(math.approxEq(f32, c.re, f32(5)/34, epsilon) and + math.approxEq(f32, c.im, f32(-3)/34, epsilon)); +} + +test "complex.magnitude" { + const a = Complex(f32).new(5, 3); + const c = a.magnitude(); + + debug.assert(math.approxEq(f32, c, 5.83095, epsilon)); +} + +test "complex.cmath" { + _ = @import("abs.zig"); + _ = @import("acosh.zig"); + _ = @import("acos.zig"); + _ = @import("arg.zig"); + _ = @import("asinh.zig"); + _ = @import("asin.zig"); + _ = @import("atanh.zig"); + _ = @import("atan.zig"); + _ = @import("conj.zig"); + _ = @import("cosh.zig"); + _ = @import("cos.zig"); + _ = @import("exp.zig"); + _ = @import("log.zig"); + _ = @import("pow.zig"); + _ = @import("proj.zig"); + _ = @import("sinh.zig"); + _ = @import("sin.zig"); + _ = @import("sqrt.zig"); + _ = @import("tanh.zig"); + _ = @import("tan.zig"); +} diff --git a/std/math/complex/ldexp.zig b/std/math/complex/ldexp.zig new file mode 100644 index 0000000000..4fb5a6815f --- /dev/null +++ b/std/math/complex/ldexp.zig @@ -0,0 +1,75 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn ldexp_cexp(z: var, expt: i32) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + + return switch (T) { + f32 => ldexp_cexp32(z, expt), + f64 => ldexp_cexp64(z, expt), + else => unreachable, + }; +} + +fn frexp_exp32(x: f32, expt: &i32) f32 { + const k = 235; // reduction constant + const kln2 = 162.88958740; // k * ln2 + + const exp_x = math.exp(x - kln2); + const hx = @bitCast(u32, exp_x); + *expt = i32(hx >> 23) - (0x7f + 127) + k; + return @bitCast(f32, (hx & 0x7fffff) | ((0x7f + 127) << 23)); +} + +fn ldexp_cexp32(z: &const Complex(f32), expt: i32) Complex(f32) { + var ex_expt: i32 = undefined; + const exp_x = frexp_exp32(z.re, &ex_expt); + const exptf = expt + ex_expt; + + const half_expt1 = @divTrunc(exptf, 2); + const scale1 = @bitCast(f32, (0x7f + half_expt1) << 23); + + const half_expt2 = exptf - half_expt1; + const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23); + + return Complex(f32).new( + math.cos(z.im) * exp_x * scale1 * scale2, + math.sin(z.im) * exp_x * scale1 * scale2, + ); +} + +fn frexp_exp64(x: f64, expt: &i32) f64 { + const k = 1799; // reduction constant + const kln2 = 1246.97177782734161156; // k * ln2 + + const exp_x = math.exp(x - kln2); + + const fx = @bitCast(u64, x); + const hx = u32(fx >> 32); + const lx = @truncate(u32, fx); + + *expt = i32(hx >> 20) - (0x3ff + 1023) + k; + + const high_word = (hx & 0xfffff) | ((0x3ff + 1023) << 20); + return @bitCast(f64, (u64(high_word) << 32) | lx); +} + +fn ldexp_cexp64(z: &const Complex(f64), expt: i32) Complex(f64) { + var ex_expt: i32 = undefined; + const exp_x = frexp_exp64(z.re, &ex_expt); + const exptf = i64(expt + ex_expt); + + const half_expt1 = @divTrunc(exptf, 2); + const scale1 = @bitCast(f64, (0x3ff + half_expt1) << 20); + + const half_expt2 = exptf - half_expt1; + const scale2 = @bitCast(f64, (0x3ff + half_expt2) << 20); + + return Complex(f64).new( + math.cos(z.im) * exp_x * scale1 * scale2, + math.sin(z.im) * exp_x * scale1 * scale2, + ); +} diff --git a/std/math/complex/log.zig b/std/math/complex/log.zig new file mode 100644 index 0000000000..6fe4d6b50d --- /dev/null +++ b/std/math/complex/log.zig @@ -0,0 +1,26 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn log(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + const r = cmath.abs(z); + const phi = cmath.arg(z); + + return Complex(T).new(math.ln(r), phi); +} + +const epsilon = 0.0001; + +test "complex.clog" { + const a = Complex(f32).new(5, 3); + const c = log(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, 1.763180, epsilon)); + debug.assert(math.approxEq(f32, im, 0.540419, epsilon)); +} diff --git a/std/math/complex/pow.zig b/std/math/complex/pow.zig new file mode 100644 index 0000000000..34414254ce --- /dev/null +++ b/std/math/complex/pow.zig @@ -0,0 +1,25 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn pow(comptime T: type, z: &const T, c: &const T) T { + const p = cmath.log(z); + const q = c.mul(p); + return cmath.exp(q); +} + +const epsilon = 0.0001; + +test "complex.cpow" { + const a = Complex(f32).new(5, 3); + const b = Complex(f32).new(2.3, -1.3); + const c = pow(Complex(f32), a, b); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, 58.049110, epsilon)); + debug.assert(math.approxEq(f32, im, -101.003433, epsilon)); +} diff --git a/std/math/complex/proj.zig b/std/math/complex/proj.zig new file mode 100644 index 0000000000..b6c4cc046e --- /dev/null +++ b/std/math/complex/proj.zig @@ -0,0 +1,24 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn proj(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + + if (math.isInf(z.re) or math.isInf(z.im)) { + return Complex(T).new(math.inf(T), math.copysign(T, 0, z.re)); + } + + return Complex(T).new(z.re, z.im); +} + +const epsilon = 0.0001; + +test "complex.cproj" { + const a = Complex(f32).new(5, 3); + const c = proj(a); + + debug.assert(c.re == 5 and c.im == 3); +} diff --git a/std/math/complex/sin.zig b/std/math/complex/sin.zig new file mode 100644 index 0000000000..a334d6625c --- /dev/null +++ b/std/math/complex/sin.zig @@ -0,0 +1,25 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn sin(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + const p = Complex(T).new(-z.im, z.re); + const q = cmath.sinh(p); + return Complex(T).new(q.im, -q.re); +} + +const epsilon = 0.0001; + +test "complex.csin" { + const a = Complex(f32).new(5, 3); + const c = sin(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, -9.654126, epsilon)); + debug.assert(math.approxEq(f32, im, 2.841692, epsilon)); +} diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig new file mode 100644 index 0000000000..799ee9ad6e --- /dev/null +++ b/std/math/complex/sinh.zig @@ -0,0 +1,170 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; + +pub fn sinh(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + return switch (T) { + f32 => sinh32(z), + f64 => sinh64(z), + else => @compileError("tan not implemented for " ++ @typeName(z)), + }; +} + +fn sinh32(z: &const Complex(f32)) Complex(f32) { + const x = z.re; + const y = z.im; + + const hx = @bitCast(u32, x); + const ix = hx & 0x7fffffff; + + const hy = @bitCast(u32, y); + const iy = hy & 0x7fffffff; + + if (ix < 0x7f800000 and iy < 0x7f800000) { + if (iy == 0) { + return Complex(f32).new(math.sinh(x), y); + } + // small x: normal case + if (ix < 0x41100000) { + return Complex(f32).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y)); + } + + // |x|>= 9, so cosh(x) ~= exp(|x|) + if (ix < 0x42b17218) { + // x < 88.7: exp(|x|) won't overflow + const h = math.exp(math.fabs(x)) * 0.5; + return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y)); + } + // x < 192.7: scale to avoid overflow + else if (ix < 0x4340b1e7) { + const v = Complex(f32).new(math.fabs(x), y); + const r = ldexp_cexp(v, -1); + return Complex(f32).new(x * math.copysign(f32, 1, x), y); + } + // x >= 192.7: result always overflows + else { + const h = 0x1p127 * x; + return Complex(f32).new(h * math.cos(y), h * h * math.sin(y)); + } + } + + if (ix == 0 and iy >= 0x7f800000) { + return Complex(f32).new(math.copysign(f32, 0, x * (y - y)), y - y); + } + + if (iy == 0 and ix >= 0x7f800000) { + if (hx & 0x7fffff == 0) { + return Complex(f32).new(x, y); + } + return Complex(f32).new(x, math.copysign(f32, 0, y)); + } + + if (ix < 0x7f800000 and iy >= 0x7f800000) { + return Complex(f32).new(y - y, x * (y - y)); + } + + if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) { + if (iy >= 0x7f800000) { + return Complex(f32).new(x * x, x * (y - y)); + } + return Complex(f32).new(x * math.cos(y), math.inf_f32 * math.sin(y)); + } + + return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y)); +} + +fn sinh64(z: &const Complex(f64)) Complex(f64) { + const x = z.re; + const y = z.im; + + const fx = @bitCast(u64, x); + const hx = u32(fx >> 32); + const lx = @truncate(u32, fx); + const ix = hx & 0x7fffffff; + + const fy = @bitCast(u64, y); + const hy = u32(fy >> 32); + const ly = @truncate(u32, fy); + const iy = hy & 0x7fffffff; + + if (ix < 0x7ff00000 and iy < 0x7ff00000) { + if (iy | ly == 0) { + return Complex(f64).new(math.sinh(x), y); + } + // small x: normal case + if (ix < 0x40360000) { + return Complex(f64).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y)); + } + + // |x|>= 22, so cosh(x) ~= exp(|x|) + if (ix < 0x40862e42) { + // x < 710: exp(|x|) won't overflow + const h = math.exp(math.fabs(x)) * 0.5; + return Complex(f64).new(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y)); + } + // x < 1455: scale to avoid overflow + else if (ix < 0x4096bbaa) { + const v = Complex(f64).new(math.fabs(x), y); + const r = ldexp_cexp(v, -1); + return Complex(f64).new(x * math.copysign(f64, 1, x), y); + } + // x >= 1455: result always overflows + else { + const h = 0x1p1023 * x; + return Complex(f64).new(h * math.cos(y), h * h * math.sin(y)); + } + } + + if (ix | lx == 0 and iy >= 0x7ff00000) { + return Complex(f64).new(math.copysign(f64, 0, x * (y - y)), y - y); + } + + if (iy | ly == 0 and ix >= 0x7ff00000) { + if ((hx & 0xfffff) | lx == 0) { + return Complex(f64).new(x, y); + } + return Complex(f64).new(x, math.copysign(f64, 0, y)); + } + + if (ix < 0x7ff00000 and iy >= 0x7ff00000) { + return Complex(f64).new(y - y, x * (y - y)); + } + + if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) { + if (iy >= 0x7ff00000) { + return Complex(f64).new(x * x, x * (y - y)); + } + return Complex(f64).new(x * math.cos(y), math.inf_f64 * math.sin(y)); + } + + return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y)); +} + +const epsilon = 0.0001; + +test "complex.csinh32" { + const a = Complex(f32).new(5, 3); + const c = sinh(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, -73.460617, epsilon)); + debug.assert(math.approxEq(f32, im, 10.472508, epsilon)); +} + +test "complex.csinh64" { + const a = Complex(f64).new(5, 3); + const c = sinh(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f64, re, -73.460617, epsilon)); + debug.assert(math.approxEq(f64, im, 10.472508, epsilon)); +} diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig new file mode 100644 index 0000000000..03935bd3b3 --- /dev/null +++ b/std/math/complex/sqrt.zig @@ -0,0 +1,140 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +// NOTE: Returning @typeOf(z) here causes issues when trying to access the value. This is +// why we currently assign re, im parts to a new value explicitly for all tests. +pub fn sqrt(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + + return switch (T) { + f32 => sqrt32(z), + f64 => sqrt64(z), + else => @compileError("sqrt not implemented for " ++ @typeName(z)), + }; +} + +fn sqrt32(z: &const Complex(f32)) Complex(f32) { + const x = z.re; + const y = z.im; + + if (x == 0 and y == 0) { + return Complex(f32).new(0, y); + } + if (math.isInf(y)) { + return Complex(f32).new(math.inf(f32), y); + } + if (math.isNan(x)) { + // raise invalid if y is not nan + const t = (y - y) / (y - y); + return Complex(f32).new(x, t); + } + if (math.isInf(x)) { + // sqrt(inf + i nan) = inf + nan i + // sqrt(inf + iy) = inf + i0 + // sqrt(-inf + i nan) = nan +- inf i + // sqrt(-inf + iy) = 0 + inf i + if (math.signbit(x)) { + return Complex(f32).new(math.fabs(x - y), math.copysign(f32, x, y)); + } else { + return Complex(f32).new(x, math.copysign(f32, y - y, y)); + } + } + + // y = nan special case is handled fine below + + // double-precision avoids overflow with correct rounding. + const dx = f64(x); + const dy = f64(y); + + if (dx >= 0) { + const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5); + return Complex(f32).new(f32(t), f32(dy / (2.0 * t))); + } else { + const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); + return Complex(f32).new(f32(math.fabs(y) / (2.0 * t)), f32(math.copysign(f64, t, y))); + } +} + +fn sqrt64(z: &const Complex(f64)) Complex(f64) { + // may encounter overflow for im,re >= DBL_MAX / (1 + sqrt(2)) + const threshold = 0x1.a827999fcef32p+1022; + + var x = z.re; + var y = z.im; + + if (x == 0 and y == 0) { + return Complex(f64).new(0, y); + } + if (math.isInf(y)) { + return Complex(f64).new(math.inf(f64), y); + } + if (math.isNan(x)) { + // raise invalid if y is not nan + const t = (y - y) / (y - y); + return Complex(f64).new(x, t); + } + if (math.isInf(x)) { + // sqrt(inf + i nan) = inf + nan i + // sqrt(inf + iy) = inf + i0 + // sqrt(-inf + i nan) = nan +- inf i + // sqrt(-inf + iy) = 0 + inf i + if (math.signbit(x)) { + return Complex(f64).new(math.fabs(x - y), math.copysign(f64, x, y)); + } else { + return Complex(f64).new(x, math.copysign(f64, y - y, y)); + } + } + + // y = nan special case is handled fine below + + // scale to avoid overflow + var scale = false; + if (math.fabs(x) >= threshold or math.fabs(y) >= threshold) { + x *= 0.25; + y *= 0.25; + scale = true; + } + + var result: Complex(f64) = undefined; + if (x >= 0) { + const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5); + result = Complex(f64).new(t, y / (2.0 * t)); + } else { + const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5); + result = Complex(f64).new(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y)); + } + + if (scale) { + result.re *= 2; + result.im *= 2; + } + + return result; +} + +const epsilon = 0.0001; + +test "complex.csqrt32" { + const a = Complex(f32).new(5, 3); + const c = sqrt(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, 2.327117, epsilon)); + debug.assert(math.approxEq(f32, im, 0.644574, epsilon)); +} + +test "complex.csqrt64" { + const a = Complex(f64).new(5, 3); + const c = sqrt(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f64, re, 2.3271175190399496, epsilon)); + debug.assert(math.approxEq(f64, im, 0.6445742373246469, epsilon)); +} diff --git a/std/math/complex/tan.zig b/std/math/complex/tan.zig new file mode 100644 index 0000000000..cb43ff36dc --- /dev/null +++ b/std/math/complex/tan.zig @@ -0,0 +1,25 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn tan(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + const q = Complex(T).new(-z.im, z.re); + const r = cmath.tanh(q); + return Complex(T).new(r.im, -r.re); +} + +const epsilon = 0.0001; + +test "complex.ctan" { + const a = Complex(f32).new(5, 3); + const c = tan(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, -0.002708233, epsilon)); + debug.assert(math.approxEq(f32, im, 1.004165, epsilon)); +} diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig new file mode 100644 index 0000000000..bc68aafb01 --- /dev/null +++ b/std/math/complex/tanh.zig @@ -0,0 +1,117 @@ +const std = @import("../../index.zig"); +const debug = std.debug; +const math = std.math; +const cmath = math.complex; +const Complex = cmath.Complex; + +pub fn tanh(z: var) Complex(@typeOf(z.re)) { + const T = @typeOf(z.re); + return switch (T) { + f32 => tanh32(z), + f64 => tanh64(z), + else => @compileError("tan not implemented for " ++ @typeName(z)), + }; +} + +fn tanh32(z: &const Complex(f32)) Complex(f32) { + const x = z.re; + const y = z.im; + + const hx = @bitCast(u32, x); + const ix = hx & 0x7fffffff; + + if (ix >= 0x7f800000) { + if (ix & 0x7fffff != 0) { + const r = if (y == 0) y else x * y; + return Complex(f32).new(x, r); + } + const xx = @bitCast(f32, hx - 0x40000000); + const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); + return Complex(f32).new(xx, math.copysign(f32, 0, r)); + } + + if (!math.isFinite(y)) { + const r = if (ix != 0) y - y else x; + return Complex(f32).new(r, y - y); + } + + // x >= 11 + if (ix >= 0x41300000) { + const exp_mx = math.exp(-math.fabs(x)); + return Complex(f32).new(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); + } + + // Kahan's algorithm + const t = math.tan(y); + const beta = 1.0 + t * t; + const s = math.sinh(x); + const rho = math.sqrt(1 + s * s); + const den = 1 + beta * s * s; + + return Complex(f32).new((beta * rho * s) / den, t / den); +} + +fn tanh64(z: &const Complex(f64)) Complex(f64) { + const x = z.re; + const y = z.im; + + const fx = @bitCast(u64, x); + const hx = u32(fx >> 32); + const lx = @truncate(u32, fx); + const ix = hx & 0x7fffffff; + + if (ix >= 0x7ff00000) { + if ((ix & 0x7fffff) | lx != 0) { + const r = if (y == 0) y else x * y; + return Complex(f64).new(x, r); + } + + const xx = @bitCast(f64, (u64(hx - 0x40000000) << 32) | lx); + const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); + return Complex(f64).new(xx, math.copysign(f64, 0, r)); + } + + if (!math.isFinite(y)) { + const r = if (ix != 0) y - y else x; + return Complex(f64).new(r, y - y); + } + + // x >= 22 + if (ix >= 0x40360000) { + const exp_mx = math.exp(-math.fabs(x)); + return Complex(f64).new(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); + } + + // Kahan's algorithm + const t = math.tan(y); + const beta = 1.0 + t * t; + const s = math.sinh(x); + const rho = math.sqrt(1 + s * s); + const den = 1 + beta * s * s; + + return Complex(f64).new((beta * rho * s) / den, t / den); +} + +const epsilon = 0.0001; + +test "complex.ctanh32" { + const a = Complex(f32).new(5, 3); + const c = tanh(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f32, re, 0.999913, epsilon)); + debug.assert(math.approxEq(f32, im, -0.000025, epsilon)); +} + +test "complex.ctanh64" { + const a = Complex(f64).new(5, 3); + const c = tanh(a); + + const re = c.re; + const im = c.im; + + debug.assert(math.approxEq(f64, re, 0.999913, epsilon)); + debug.assert(math.approxEq(f64, im, -0.000025, epsilon)); +} diff --git a/std/math/index.zig b/std/math/index.zig index 477dafcbcc..83ba055329 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -129,6 +129,9 @@ pub const cos = @import("cos.zig").cos; pub const sin = @import("sin.zig").sin; pub const tan = @import("tan.zig").tan; +pub const complex = @import("complex/index.zig"); +pub const Complex = complex.Complex; + test "math" { _ = @import("nan.zig"); _ = @import("isnan.zig"); @@ -172,6 +175,8 @@ test "math" { _ = @import("sin.zig"); _ = @import("cos.zig"); _ = @import("tan.zig"); + + _ = @import("complex/index.zig"); } -- cgit v1.2.3 From fb88f5a0d21f68632a8c712e14a9640f9c4f6cb3 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 11:20:33 +0300 Subject: @typeInfo with void payloads now works! --- src/ir.cpp | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 69897f3b91..52136ec9c8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15705,11 +15705,32 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, assert(var_value->type->id == TypeTableEntryIdMetaType); TypeTableEntry *result_type = var_value->data.x_type; - // TODO: Check if we need to explicitely make a &const TypeInfo here, I think we don't. ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_struct.fields = create_const_vals(1); - // TODO: Fill the struct - zig_panic("Building TypeInfo..."); + bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); + out_val->data.x_union.parent.id = ConstParentIdNone; + + switch (type_entry->id) { + case TypeTableEntryIdInvalid: + zig_unreachable(); + case TypeTableEntryIdMetaType: + case TypeTableEntryIdVoid: + case TypeTableEntryIdBool: + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdUndefLit: + case TypeTableEntryIdNullLit: + case TypeTableEntryIdNamespace: + case TypeTableEntryIdBlock: + case TypeTableEntryIdArgTuple: + case TypeTableEntryIdOpaque: + // TODO: Check out this is the way to handle voids; + out_val->data.x_union.payload = nullptr; + break; + default: + zig_panic("@typeInfo unsupported for %s", buf_ptr(&type_entry->name)); + } + return result_type; } -- cgit v1.2.3 From 0501e066b51b659371739008c20e80642532497a Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Tue, 24 Apr 2018 23:54:27 +1200 Subject: crypto throughput test now uses os.time module --- std/crypto/throughput_test.zig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig index d086b555e9..0756f9a4eb 100644 --- a/std/crypto/throughput_test.zig +++ b/std/crypto/throughput_test.zig @@ -1,17 +1,13 @@ // Modify the HashFunction variable to the one wanted to test. // -// NOTE: The throughput measurement may be slightly lower than other measurements since we run -// through our block alignment functions as well. Be aware when comparing against other tests. -// // ``` -// zig build-exe --release-fast --library c throughput_test.zig +// zig build-exe --release-fast throughput_test.zig // ./throughput_test // ``` const std = @import("std"); -const c = @cImport({ - @cInclude("time.h"); -}); +const time = std.os.time; +const Timer = time.Timer; const HashFunction = @import("md5.zig").Md5; const MiB = 1024 * 1024; @@ -28,13 +24,14 @@ pub fn main() !void { var h = HashFunction.init(); var offset: usize = 0; - const start = c.clock(); + var timer = try Timer.start(); + const start = timer.lap(); while (offset < BytesToHash) : (offset += block.len) { h.update(block[0..]); } - const end = c.clock(); + const end = timer.read(); - const elapsed_s = f64(end - start) / f64(c.CLOCKS_PER_SEC); + const elapsed_s = f64(end - start) / time.ns_per_s; const throughput = u64(BytesToHash / elapsed_s); try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB)); -- cgit v1.2.3 From ec2a3ed500d4ba269c87b3ad0efe7bcc89eb2057 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 15:03:46 +0300 Subject: Attempt at adding comptime union field access --- src/codegen.cpp | 1 + src/ir.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index ea2d9f463e..60a459f822 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6347,6 +6347,7 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "};\n\n"); } { + // TODO: Add method info where methods are supported. buf_appendf(contents, "pub const IntInfo = struct {\n" " is_signed: bool,\n" diff --git a/src/ir.cpp b/src/ir.cpp index 52136ec9c8..2212efb101 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13473,6 +13473,41 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ } else if (bare_type->id == TypeTableEntryIdUnion) { TypeUnionField *field = find_union_type_field(bare_type, field_name); if (field) { + if (instr_is_comptime(container_ptr)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ConstExprValue *union_val = const_ptr_pointee(ira->codegen, ptr_val); + if (type_is_invalid(union_val->type)) + return ira->codegen->invalid_instruction; + + TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag); + if (actual_field == nullptr) + zig_unreachable(); + + if (field != actual_field) { + ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name), + buf_ptr(actual_field->name))); + return ira->codegen->invalid_instruction; + } + + ConstExprValue *payload_val = union_val->data.x_union.payload; + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, payload_val->type, is_const, is_volatile, + get_abi_alignment(ira->codegen, payload_val->type), 0, 0); + + IrInstruction *result = ir_get_const(ira, source_instr); + ConstExprValue *const_val = &result->value; + const_val->data.x_ptr.special = ConstPtrSpecialRef; + const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut; + const_val->data.x_ptr.data.ref.pointee = payload_val; + const_val->type = ptr_type; + return result; + } + } + IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, get_abi_alignment(ira->codegen, field->type_entry), 0, 0); @@ -15706,8 +15741,12 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, TypeTableEntry *result_type = var_value->data.x_type; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + // TODO: Do I need those? probably set in ir_build_const_from + //out_val->special = ConstValSpecialStatic; + //out_val->type = result_type; + bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); - out_val->data.x_union.parent.id = ConstParentIdNone; + //out_val->data.x_union.parent.id = ConstParentIdNone; switch (type_entry->id) { case TypeTableEntryIdInvalid: @@ -15724,9 +15763,34 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: - // TODO: Check out this is the way to handle voids; out_val->data.x_union.payload = nullptr; break; + case TypeTableEntryIdInt: + { + // Error from 'ir_resolve_const': "unable to evaluate constant expression" + ConstExprValue *payload = create_const_vals(1); + out_val->data.x_union.payload = payload; + + payload->special = ConstValSpecialStatic; + payload->type = get_builtin_value(ira->codegen, "IntInfo")->type; + + ConstExprValue *fields = create_const_vals(2); + payload->data.x_struct.fields = fields; + + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = out_val; + + // TODO: See what happens if we don't set the field type (set it to nullptr) + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_bool; + fields[0].data.x_bool = type_entry->data.integral.is_signed; + + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_u8; + bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); + + break; + } default: zig_panic("@typeInfo unsupported for %s", buf_ptr(&type_entry->name)); } -- cgit v1.2.3 From e9309d3b1310863d9571486b071b7a34bea1fb60 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 15:17:34 +0300 Subject: Fixed IntInfo generation. --- src/ir.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 2212efb101..39e48ac33b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15772,7 +15772,10 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, out_val->data.x_union.payload = payload; payload->special = ConstValSpecialStatic; - payload->type = get_builtin_value(ira->codegen, "IntInfo")->type; + + ConstExprValue *int_info_type = get_builtin_value(ira->codegen, "IntInfo"); + assert(int_info_type->type->id == TypeTableEntryIdMetaType); + payload->type = int_info_type->data.x_type; ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; -- cgit v1.2.3 From 0e5fb035e3d12879b4bd7a23578fdb8932deb4d1 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 16:23:22 +0300 Subject: Added (broken) pointer info, float info --- src/ir.cpp | 155 +++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 125 insertions(+), 30 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 39e48ac33b..3ba9bc4772 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15728,27 +15728,13 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira, return ira->codegen->builtin_types.entry_num_lit_int; } -static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, - IrInstructionTypeInfo *instruction) +static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, TypeTableEntry *type_entry) { - IrInstruction *type_value = instruction->type_value->other; - TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); - if (type_is_invalid(type_entry)) - return ira->codegen->builtin_types.entry_invalid; + assert(type_entry != nullptr); + assert(!type_is_invalid(type_entry)); - ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeInfo"); - assert(var_value->type->id == TypeTableEntryIdMetaType); - TypeTableEntry *result_type = var_value->data.x_type; - - ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - // TODO: Do I need those? probably set in ir_build_const_from - //out_val->special = ConstValSpecialStatic; - //out_val->type = result_type; - - bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); - //out_val->data.x_union.parent.id = ConstParentIdNone; - - switch (type_entry->id) { + switch (type_entry->id) + { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdMetaType: @@ -15763,14 +15749,11 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: - out_val->data.x_union.payload = nullptr; - break; + // TODO: Construct a valid void payload. + return nullptr; case TypeTableEntryIdInt: { - // Error from 'ir_resolve_const': "unable to evaluate constant expression" ConstExprValue *payload = create_const_vals(1); - out_val->data.x_union.payload = payload; - payload->special = ConstValSpecialStatic; ConstExprValue *int_info_type = get_builtin_value(ira->codegen, "IntInfo"); @@ -15780,23 +15763,135 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = out_val; + if (parent->type->id == TypeTableEntryIdStruct) + { + payload->data.x_struct.parent.id = ConstParentIdStruct; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else if (parent->type->id == TypeTableEntryIdUnion) + { + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else + { + payload->data.x_struct.parent.id = ConstParentIdNone; + } - // TODO: See what happens if we don't set the field type (set it to nullptr) + // is_signed: bool fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_bool; fields[0].data.x_bool = type_entry->data.integral.is_signed; - + // bits: u8 fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); - break; + return payload; + } + case TypeTableEntryIdFloat: + { + ConstExprValue *payload = create_const_vals(1); + payload->special = ConstValSpecialStatic; + + ConstExprValue *float_info_type = get_builtin_value(ira->codegen, "FloatInfo"); + assert(float_info_type->type->id == TypeTableEntryIdMetaType); + payload->type = float_info_type->data.x_type; + + ConstExprValue *fields = create_const_vals(1); + payload->data.x_struct.fields = fields; + + if (parent->type->id == TypeTableEntryIdStruct) + { + payload->data.x_struct.parent.id = ConstParentIdStruct; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else if (parent->type->id == TypeTableEntryIdUnion) + { + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else + { + payload->data.x_struct.parent.id = ConstParentIdNone; + } + // bits: u8 + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_u8; + bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); + + return payload; + } + case TypeTableEntryIdPointer: + { + ConstExprValue *payload = create_const_vals(1); + payload->special = ConstValSpecialStatic; + + ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "PointerInfo"); + assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); + payload->type = pointer_info_type->data.x_type; + + ConstExprValue *fields = create_const_vals(4); + payload->data.x_struct.fields = fields; + + if (parent->type->id == TypeTableEntryIdStruct) + { + payload->data.x_struct.parent.id = ConstParentIdStruct; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else if (parent->type->id == TypeTableEntryIdUnion) + { + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else + { + payload->data.x_struct.parent.id = ConstParentIdNone; + } + // is_const: bool + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_bool; + fields[0].data.x_bool = type_entry->data.pointer.is_const; + // is_volatile: bool + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_bool; + fields[1].data.x_bool = type_entry->data.pointer.is_volatile; + // alignment: u32 + fields[2].special = ConstValSpecialStatic; + fields[2].type = ira->codegen->builtin_types.entry_u32; + bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.pointer.alignment); + // child: &TypeInfo + ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_type->type->id == TypeTableEntryIdMetaType); + fields[3].special = ConstValSpecialStatic; + fields[3].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + fields[3].data.x_ptr.special = ConstPtrSpecialRef; + fields[3].data.x_ptr.mut = ConstPtrMutComptimeVar; + fields[3].data.x_ptr.data.ref.pointee = ir_make_type_info_value(ira, &fields[3], type_entry->data.pointer.child_type); + return payload; } default: - zig_panic("@typeInfo unsupported for %s", buf_ptr(&type_entry->name)); + zig_unreachable(); } +} + +static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, + IrInstructionTypeInfo *instruction) +{ + IrInstruction *type_value = instruction->type_value->other; + TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); + if (type_is_invalid(type_entry)) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeInfo"); + assert(var_value->type->id == TypeTableEntryIdMetaType); + TypeTableEntry *result_type = var_value->data.x_type; + + // TODO: We need to return a const pointer to the typeinfo, not the typeinfo by value. + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->type = result_type; + bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); + out_val->data.x_union.payload = ir_make_type_info_value(ira, out_val, type_entry); return result_type; } -- cgit v1.2.3 From 189e8e97bdd8dea8efa9ea30401b164ea619f2e4 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 16:50:36 +0300 Subject: PointerInfo child is a pointer to a TypeInfo union, still not working correctly --- src/ir.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 3ba9bc4772..86c718da81 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15867,7 +15867,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p fields[3].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); fields[3].data.x_ptr.special = ConstPtrSpecialRef; fields[3].data.x_ptr.mut = ConstPtrMutComptimeVar; - fields[3].data.x_ptr.data.ref.pointee = ir_make_type_info_value(ira, &fields[3], type_entry->data.pointer.child_type); + ConstExprValue *union_val = create_const_vals(1); + union_val->type = type_info_type->data.x_type; + bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.pointer.child_type->id)); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.pointer.child_type); + fields[3].data.x_ptr.data.ref.pointee = union_val; return payload; } default: -- cgit v1.2.3 From 2d8553c85333734a1690dfae30d881103dde0727 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 17:01:20 +0300 Subject: Fixed PointerInfo generation --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 86c718da81..e1208f03ad 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15859,7 +15859,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p // alignment: u32 fields[2].special = ConstValSpecialStatic; fields[2].type = ira->codegen->builtin_types.entry_u32; - bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.pointer.alignment); + bigint_init_unsigned(&fields[2].data.x_bigint, type_entry->data.pointer.alignment); // child: &TypeInfo ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_type->type->id == TypeTableEntryIdMetaType); -- cgit v1.2.3 From 09d7033d1db6ac0887aa21f87a2f96dad039f4e3 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 17:08:45 +0300 Subject: PointerInfo child is known at comptime --- src/ir.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ir.cpp b/src/ir.cpp index e1208f03ad..be61b0c280 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15868,6 +15868,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p fields[3].data.x_ptr.special = ConstPtrSpecialRef; fields[3].data.x_ptr.mut = ConstPtrMutComptimeVar; ConstExprValue *union_val = create_const_vals(1); + union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.pointer.child_type->id)); union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.pointer.child_type); -- cgit v1.2.3 From 182a9fad2d55973ab50f4a2170edb88d6708681d Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 17:38:30 +0300 Subject: Added ArrayInfo, NullableInfo, PromiseInfo generation --- src/codegen.cpp | 2 +- src/ir.cpp | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 60a459f822..2e7cc937b4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6366,7 +6366,7 @@ static void define_builtin_compile_vars(CodeGen *g) { "};\n" "\n" "pub const ArrayInfo = struct {\n" - " len: u64,\n" + " len: usize,\n" " child: &TypeInfo,\n" "};\n" "\n" diff --git a/src/ir.cpp b/src/ir.cpp index be61b0c280..a00d98efd1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15875,6 +15875,151 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p fields[3].data.x_ptr.data.ref.pointee = union_val; return payload; } + case TypeTableEntryIdArray: + { + ConstExprValue *payload = create_const_vals(1); + payload->special = ConstValSpecialStatic; + + ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "ArrayInfo"); + assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); + payload->type = pointer_info_type->data.x_type; + + ConstExprValue *fields = create_const_vals(2); + payload->data.x_struct.fields = fields; + + if (parent->type->id == TypeTableEntryIdStruct) + { + payload->data.x_struct.parent.id = ConstParentIdStruct; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else if (parent->type->id == TypeTableEntryIdUnion) + { + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else + { + payload->data.x_struct.parent.id = ConstParentIdNone; + } + // len: usize + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_usize; + bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); + // child: &TypeInfo + ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_type->type->id == TypeTableEntryIdMetaType); + fields[1].special = ConstValSpecialStatic; + fields[1].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + fields[1].data.x_ptr.special = ConstPtrSpecialRef; + fields[1].data.x_ptr.mut = ConstPtrMutComptimeVar; + ConstExprValue *union_val = create_const_vals(1); + union_val->special = ConstValSpecialStatic; + union_val->type = type_info_type->data.x_type; + bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.array.child_type->id)); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.array.child_type); + fields[1].data.x_ptr.data.ref.pointee = union_val; + return payload; + } + case TypeTableEntryIdMaybe: + { + ConstExprValue *payload = create_const_vals(1); + payload->special = ConstValSpecialStatic; + + ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "NullableInfo"); + assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); + payload->type = pointer_info_type->data.x_type; + + ConstExprValue *fields = create_const_vals(1); + payload->data.x_struct.fields = fields; + + if (parent->type->id == TypeTableEntryIdStruct) + { + payload->data.x_struct.parent.id = ConstParentIdStruct; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else if (parent->type->id == TypeTableEntryIdUnion) + { + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else + { + payload->data.x_struct.parent.id = ConstParentIdNone; + } + // child: &TypeInfo + ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_type->type->id == TypeTableEntryIdMetaType); + fields[0].special = ConstValSpecialStatic; + fields[0].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + fields[0].data.x_ptr.special = ConstPtrSpecialRef; + fields[0].data.x_ptr.mut = ConstPtrMutComptimeVar; + ConstExprValue *union_val = create_const_vals(1); + union_val->special = ConstValSpecialStatic; + union_val->type = type_info_type->data.x_type; + bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.maybe.child_type->id)); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.maybe.child_type); + fields[0].data.x_ptr.data.ref.pointee = union_val; + return payload; + } + case TypeTableEntryIdPromise: + { + ConstExprValue *payload = create_const_vals(1); + payload->special = ConstValSpecialStatic; + + ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "PromiseInfo"); + assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); + payload->type = pointer_info_type->data.x_type; + + ConstExprValue *fields = create_const_vals(1); + payload->data.x_struct.fields = fields; + + if (parent->type->id == TypeTableEntryIdStruct) + { + payload->data.x_struct.parent.id = ConstParentIdStruct; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else if (parent->type->id == TypeTableEntryIdUnion) + { + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else + { + payload->data.x_struct.parent.id = ConstParentIdNone; + } + // child: ?&TypeInfo + ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_type->type->id == TypeTableEntryIdMetaType); + + TypeTableEntry *type_info_ptr_type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + + fields[0].special = ConstValSpecialStatic; + fields[0].type = get_maybe_type(ira->codegen, type_info_ptr_type); + + if (type_entry->data.promise.result_type == nullptr) + { + fields[0].data.x_maybe = nullptr; + } + else + { + ConstExprValue *maybe_value = create_const_vals(1); + maybe_value->special = ConstValSpecialStatic; + maybe_value->type = type_info_ptr_type; + + maybe_value->data.x_ptr.special = ConstPtrSpecialRef; + maybe_value->data.x_ptr.mut = ConstPtrMutComptimeVar; + + ConstExprValue *union_val = create_const_vals(1); + union_val->special = ConstValSpecialStatic; + union_val->type = type_info_type->data.x_type; + bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.promise.result_type->id)); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.promise.result_type); + + maybe_value->data.x_ptr.data.ref.pointee = union_val; + fields[0].data.x_maybe = maybe_value; + } + return payload; + } default: zig_unreachable(); } -- cgit v1.2.3 From 778b931bf33c9b19502857af918fc159c0cd8e83 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Wed, 25 Apr 2018 02:50:18 +0300 Subject: Fixed comptime union void field access --- src/ir.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index a00d98efd1..aa456f5030 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13495,8 +13495,18 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ } ConstExprValue *payload_val = union_val->data.x_union.payload; - TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, payload_val->type, is_const, is_volatile, - get_abi_alignment(ira->codegen, payload_val->type), 0, 0); + + TypeTableEntry *field_type = field->type_entry; + if (field_type->id == TypeTableEntryIdVoid) + { + assert(payload_val == nullptr); + payload_val = create_const_vals(1); + payload_val->special = ConstValSpecialStatic; + payload_val->type = field_type; + } + + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile, + get_abi_alignment(ira->codegen, field_type), 0, 0); IrInstruction *result = ir_get_const(ira, source_instr); ConstExprValue *const_val = &result->value; @@ -15749,7 +15759,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: - // TODO: Construct a valid void payload. return nullptr; case TypeTableEntryIdInt: { -- cgit v1.2.3 From 13076d5f225cf8ca2cddf1b67478baebabe8b13f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Apr 2018 20:50:21 -0400 Subject: std.mem: add more slice manipulation functions * add std.mem.trimLeft * add std.mem.trimRight * add std.mem.trimRight * add std.mem.lastIndexOfScalar * add std.mem.lastIndexOfAny * add std.mem.lastIndexOf * add std.mem.endsWith closes #944 Thanks Braedon Wooding for the original PR --- std/mem.zig | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/std/mem.zig b/std/mem.zig index eb67ce83ef..cc3161cddd 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -20,7 +20,7 @@ pub const Allocator = struct { /// * alignment >= alignment of old_mem.ptr /// /// If `new_byte_count <= old_mem.len`: - /// * this function must return successfully. + /// * this function must return successfully. /// * alignment <= alignment of old_mem.ptr /// /// The returned newly allocated memory is undefined. @@ -174,6 +174,20 @@ pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T { return new_buf; } +/// Remove values from the beginning of a slice. +pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { + var begin: usize = 0; + while (begin < slice.len and indexOfScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {} + return slice[begin..]; +} + +/// Remove values from the end of a slice. +pub fn trimRight(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { + var end: usize = slice.len; + while (end > 0 and indexOfScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {} + return slice[0..end]; +} + /// Remove values from the beginning and end of a slice. pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { var begin: usize = 0; @@ -184,6 +198,8 @@ pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []co } test "mem.trim" { + assert(eql(u8, trimLeft(u8, " foo\n ", " \n"), "foo\n ")); + assert(eql(u8, trimRight(u8, " foo\n ", " \n"), " foo")); assert(eql(u8, trim(u8, " foo\n ", " \n"), "foo")); assert(eql(u8, trim(u8, "foo", " \n"), "foo")); } @@ -193,6 +209,17 @@ pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { return indexOfScalarPos(T, slice, 0, value); } +/// Linear search for the last index of a scalar value inside a slice. +pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { + var i: usize = slice.len; + while (i != 0) { + i -= 1; + if (slice[i] == value) + return i; + } + return null; +} + pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize { var i: usize = start_index; while (i < slice.len) : (i += 1) { @@ -206,6 +233,18 @@ pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize return indexOfAnyPos(T, slice, 0, values); } +pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize { + var i: usize = slice.len; + while (i != 0) { + i -= 1; + for (values) |value| { + if (slice[i] == value) + return i; + } + } + return null; +} + pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize { var i: usize = start_index; while (i < slice.len) : (i += 1) { @@ -221,6 +260,22 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize return indexOfPos(T, haystack, 0, needle); } +/// Find the index in a slice of a sub-slice, searching from the end backwards. +/// To start looking at a different index, slice the haystack first. +/// TODO is there even a better algorithm for this? +pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize { + if (needle.len > haystack.len) + return null; + + var i: usize = haystack.len - needle.len; + while (true) : (i -= 1) { + if (mem.eql(T, haystack[i..i+needle.len], needle)) + return i; + if (i == 0) + return null; + } +} + // TODO boyer-moore algorithm pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize { if (needle.len > haystack.len) @@ -237,9 +292,19 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee test "mem.indexOf" { assert(??indexOf(u8, "one two three four", "four") == 14); + assert(??lastIndexOf(u8, "one two three two four", "two") == 14); assert(indexOf(u8, "one two three four", "gour") == null); + assert(lastIndexOf(u8, "one two three four", "gour") == null); assert(??indexOf(u8, "foo", "foo") == 0); + assert(??lastIndexOf(u8, "foo", "foo") == 0); assert(indexOf(u8, "foo", "fool") == null); + assert(lastIndexOf(u8, "foo", "lfoo") == null); + assert(lastIndexOf(u8, "foo", "fool") == null); + + assert(??indexOf(u8, "foo foo", "foo") == 0); + assert(??lastIndexOf(u8, "foo foo", "foo") == 4); + assert(??lastIndexOfAny(u8, "boo, cat", "abo") == 6); + assert(??lastIndexOfScalar(u8, "boo", 'o') == 2); } /// Reads an integer from memory with size equal to bytes.len. @@ -359,9 +424,24 @@ pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle); } +test "mem.startsWith" { + assert(startsWith(u8, "Bob", "Bo")); + assert(!startsWith(u8, "Needle in haystack", "haystack")); +} + +pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool { + return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle); +} + + +test "mem.endsWith" { + assert(endsWith(u8, "Needle in haystack", "haystack")); + assert(!endsWith(u8, "Bob", "Bo")); +} + pub const SplitIterator = struct { buffer: []const u8, - split_bytes: []const u8, + split_bytes: []const u8, index: usize, pub fn next(self: &SplitIterator) ?[]const u8 { -- cgit v1.2.3 From 1d998d5dce73d8d500dee5e41a3dca92a7f9b03e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Apr 2018 21:14:12 -0400 Subject: clean up complex math tests --- std/math/complex/acos.zig | 7 ++----- std/math/complex/acosh.zig | 7 ++----- std/math/complex/asin.zig | 7 ++----- std/math/complex/asinh.zig | 7 ++----- std/math/complex/atan.zig | 18 ++++++------------ std/math/complex/atanh.zig | 7 ++----- std/math/complex/cos.zig | 7 ++----- std/math/complex/cosh.zig | 14 ++++---------- std/math/complex/exp.zig | 14 ++++---------- std/math/complex/log.zig | 7 ++----- std/math/complex/pow.zig | 7 ++----- std/math/complex/sin.zig | 7 ++----- std/math/complex/sinh.zig | 14 ++++---------- std/math/complex/sqrt.zig | 14 ++++---------- std/math/complex/tan.zig | 7 ++----- std/math/complex/tanh.zig | 14 ++++---------- 16 files changed, 46 insertions(+), 112 deletions(-) diff --git a/std/math/complex/acos.zig b/std/math/complex/acos.zig index cd37ddd92e..a5760b4ace 100644 --- a/std/math/complex/acos.zig +++ b/std/math/complex/acos.zig @@ -16,9 +16,6 @@ test "complex.cacos" { const a = Complex(f32).new(5, 3); const c = acos(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, 0.546975, epsilon)); - debug.assert(math.approxEq(f32, im, -2.452914, epsilon)); + debug.assert(math.approxEq(f32, c.re, 0.546975, epsilon)); + debug.assert(math.approxEq(f32, c.im, -2.452914, epsilon)); } diff --git a/std/math/complex/acosh.zig b/std/math/complex/acosh.zig index 830ff79e5f..8dd91b2836 100644 --- a/std/math/complex/acosh.zig +++ b/std/math/complex/acosh.zig @@ -16,9 +16,6 @@ test "complex.cacosh" { const a = Complex(f32).new(5, 3); const c = acosh(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, 2.452914, epsilon)); - debug.assert(math.approxEq(f32, im, 0.546975, epsilon)); + debug.assert(math.approxEq(f32, c.re, 2.452914, epsilon)); + debug.assert(math.approxEq(f32, c.im, 0.546975, epsilon)); } diff --git a/std/math/complex/asin.zig b/std/math/complex/asin.zig index 84cabc6941..584a3a1a9b 100644 --- a/std/math/complex/asin.zig +++ b/std/math/complex/asin.zig @@ -22,9 +22,6 @@ test "complex.casin" { const a = Complex(f32).new(5, 3); const c = asin(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, 1.023822, epsilon)); - debug.assert(math.approxEq(f32, im, 2.452914, epsilon)); + debug.assert(math.approxEq(f32, c.re, 1.023822, epsilon)); + debug.assert(math.approxEq(f32, c.im, 2.452914, epsilon)); } diff --git a/std/math/complex/asinh.zig b/std/math/complex/asinh.zig index 5cf086d520..0c4dc2b6e4 100644 --- a/std/math/complex/asinh.zig +++ b/std/math/complex/asinh.zig @@ -17,9 +17,6 @@ test "complex.casinh" { const a = Complex(f32).new(5, 3); const c = asinh(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, 2.459831, epsilon)); - debug.assert(math.approxEq(f32, im, 0.533999, epsilon)); + debug.assert(math.approxEq(f32, c.re, 2.459831, epsilon)); + debug.assert(math.approxEq(f32, c.im, 0.533999, epsilon)); } diff --git a/std/math/complex/atan.zig b/std/math/complex/atan.zig index 2aa0211333..b7bbf930eb 100644 --- a/std/math/complex/atan.zig +++ b/std/math/complex/atan.zig @@ -113,24 +113,18 @@ fn atan64(z: &const Complex(f64)) Complex(f64) { const epsilon = 0.0001; -test "complex.ctan32" { +test "complex.catan32" { const a = Complex(f32).new(5, 3); const c = atan(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, 1.423679, epsilon)); - debug.assert(math.approxEq(f32, im, 0.086569, epsilon)); + debug.assert(math.approxEq(f32, c.re, 1.423679, epsilon)); + debug.assert(math.approxEq(f32, c.im, 0.086569, epsilon)); } -test "complex.ctan64" { +test "complex.catan64" { const a = Complex(f64).new(5, 3); const c = atan(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f64, re, 1.423679, epsilon)); - debug.assert(math.approxEq(f64, im, 0.086569, epsilon)); + debug.assert(math.approxEq(f64, c.re, 1.423679, epsilon)); + debug.assert(math.approxEq(f64, c.im, 0.086569, epsilon)); } diff --git a/std/math/complex/atanh.zig b/std/math/complex/atanh.zig index 5fbbe2eb4b..f70c741765 100644 --- a/std/math/complex/atanh.zig +++ b/std/math/complex/atanh.zig @@ -17,9 +17,6 @@ test "complex.catanh" { const a = Complex(f32).new(5, 3); const c = atanh(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, 0.146947, epsilon)); - debug.assert(math.approxEq(f32, im, 1.480870, epsilon)); + debug.assert(math.approxEq(f32, c.re, 0.146947, epsilon)); + debug.assert(math.approxEq(f32, c.im, 1.480870, epsilon)); } diff --git a/std/math/complex/cos.zig b/std/math/complex/cos.zig index 35908898a6..96e4ffcdb0 100644 --- a/std/math/complex/cos.zig +++ b/std/math/complex/cos.zig @@ -16,9 +16,6 @@ test "complex.ccos" { const a = Complex(f32).new(5, 3); const c = cos(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, 2.855815, epsilon)); - debug.assert(math.approxEq(f32, im, 9.606383, epsilon)); + debug.assert(math.approxEq(f32, c.re, 2.855815, epsilon)); + debug.assert(math.approxEq(f32, c.im, 9.606383, epsilon)); } diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig index 1387ecb05f..96eac68556 100644 --- a/std/math/complex/cosh.zig +++ b/std/math/complex/cosh.zig @@ -152,20 +152,14 @@ test "complex.ccosh32" { const a = Complex(f32).new(5, 3); const c = cosh(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, -73.467300, epsilon)); - debug.assert(math.approxEq(f32, im, 10.471557, epsilon)); + debug.assert(math.approxEq(f32, c.re, -73.467300, epsilon)); + debug.assert(math.approxEq(f32, c.im, 10.471557, epsilon)); } test "complex.ccosh64" { const a = Complex(f64).new(5, 3); const c = cosh(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f64, re, -73.467300, epsilon)); - debug.assert(math.approxEq(f64, im, 10.471557, epsilon)); + debug.assert(math.approxEq(f64, c.re, -73.467300, epsilon)); + debug.assert(math.approxEq(f64, c.im, 10.471557, epsilon)); } diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig index ace596771a..03f7f9e41b 100644 --- a/std/math/complex/exp.zig +++ b/std/math/complex/exp.zig @@ -127,20 +127,14 @@ test "complex.cexp32" { const a = Complex(f32).new(5, 3); const c = exp(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, -146.927917, epsilon)); - debug.assert(math.approxEq(f32, im, 20.944065, epsilon)); + debug.assert(math.approxEq(f32, c.re, -146.927917, epsilon)); + debug.assert(math.approxEq(f32, c.im, 20.944065, epsilon)); } test "complex.cexp64" { const a = Complex(f32).new(5, 3); const c = exp(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f64, re, -146.927917, epsilon)); - debug.assert(math.approxEq(f64, im, 20.944065, epsilon)); + debug.assert(math.approxEq(f64, c.re, -146.927917, epsilon)); + debug.assert(math.approxEq(f64, c.im, 20.944065, epsilon)); } diff --git a/std/math/complex/log.zig b/std/math/complex/log.zig index 6fe4d6b50d..a4a1d1664f 100644 --- a/std/math/complex/log.zig +++ b/std/math/complex/log.zig @@ -18,9 +18,6 @@ test "complex.clog" { const a = Complex(f32).new(5, 3); const c = log(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, 1.763180, epsilon)); - debug.assert(math.approxEq(f32, im, 0.540419, epsilon)); + debug.assert(math.approxEq(f32, c.re, 1.763180, epsilon)); + debug.assert(math.approxEq(f32, c.im, 0.540419, epsilon)); } diff --git a/std/math/complex/pow.zig b/std/math/complex/pow.zig index 34414254ce..bef9fde542 100644 --- a/std/math/complex/pow.zig +++ b/std/math/complex/pow.zig @@ -17,9 +17,6 @@ test "complex.cpow" { const b = Complex(f32).new(2.3, -1.3); const c = pow(Complex(f32), a, b); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, 58.049110, epsilon)); - debug.assert(math.approxEq(f32, im, -101.003433, epsilon)); + debug.assert(math.approxEq(f32, c.re, 58.049110, epsilon)); + debug.assert(math.approxEq(f32, c.im, -101.003433, epsilon)); } diff --git a/std/math/complex/sin.zig b/std/math/complex/sin.zig index a334d6625c..d32b771d3b 100644 --- a/std/math/complex/sin.zig +++ b/std/math/complex/sin.zig @@ -17,9 +17,6 @@ test "complex.csin" { const a = Complex(f32).new(5, 3); const c = sin(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, -9.654126, epsilon)); - debug.assert(math.approxEq(f32, im, 2.841692, epsilon)); + debug.assert(math.approxEq(f32, c.re, -9.654126, epsilon)); + debug.assert(math.approxEq(f32, c.im, 2.841692, epsilon)); } diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig index 799ee9ad6e..09a62ca058 100644 --- a/std/math/complex/sinh.zig +++ b/std/math/complex/sinh.zig @@ -151,20 +151,14 @@ test "complex.csinh32" { const a = Complex(f32).new(5, 3); const c = sinh(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, -73.460617, epsilon)); - debug.assert(math.approxEq(f32, im, 10.472508, epsilon)); + debug.assert(math.approxEq(f32, c.re, -73.460617, epsilon)); + debug.assert(math.approxEq(f32, c.im, 10.472508, epsilon)); } test "complex.csinh64" { const a = Complex(f64).new(5, 3); const c = sinh(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f64, re, -73.460617, epsilon)); - debug.assert(math.approxEq(f64, im, 10.472508, epsilon)); + debug.assert(math.approxEq(f64, c.re, -73.460617, epsilon)); + debug.assert(math.approxEq(f64, c.im, 10.472508, epsilon)); } diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig index 03935bd3b3..02e6390572 100644 --- a/std/math/complex/sqrt.zig +++ b/std/math/complex/sqrt.zig @@ -121,20 +121,14 @@ test "complex.csqrt32" { const a = Complex(f32).new(5, 3); const c = sqrt(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, 2.327117, epsilon)); - debug.assert(math.approxEq(f32, im, 0.644574, epsilon)); + debug.assert(math.approxEq(f32, c.re, 2.327117, epsilon)); + debug.assert(math.approxEq(f32, c.im, 0.644574, epsilon)); } test "complex.csqrt64" { const a = Complex(f64).new(5, 3); const c = sqrt(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f64, re, 2.3271175190399496, epsilon)); - debug.assert(math.approxEq(f64, im, 0.6445742373246469, epsilon)); + debug.assert(math.approxEq(f64, c.re, 2.3271175190399496, epsilon)); + debug.assert(math.approxEq(f64, c.im, 0.6445742373246469, epsilon)); } diff --git a/std/math/complex/tan.zig b/std/math/complex/tan.zig index cb43ff36dc..4ea5182fa7 100644 --- a/std/math/complex/tan.zig +++ b/std/math/complex/tan.zig @@ -17,9 +17,6 @@ test "complex.ctan" { const a = Complex(f32).new(5, 3); const c = tan(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, -0.002708233, epsilon)); - debug.assert(math.approxEq(f32, im, 1.004165, epsilon)); + debug.assert(math.approxEq(f32, c.re, -0.002708233, epsilon)); + debug.assert(math.approxEq(f32, c.im, 1.004165, epsilon)); } diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig index bc68aafb01..6af62f48ae 100644 --- a/std/math/complex/tanh.zig +++ b/std/math/complex/tanh.zig @@ -98,20 +98,14 @@ test "complex.ctanh32" { const a = Complex(f32).new(5, 3); const c = tanh(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f32, re, 0.999913, epsilon)); - debug.assert(math.approxEq(f32, im, -0.000025, epsilon)); + debug.assert(math.approxEq(f32, c.re, 0.999913, epsilon)); + debug.assert(math.approxEq(f32, c.im, -0.000025, epsilon)); } test "complex.ctanh64" { const a = Complex(f64).new(5, 3); const c = tanh(a); - const re = c.re; - const im = c.im; - - debug.assert(math.approxEq(f64, re, 0.999913, epsilon)); - debug.assert(math.approxEq(f64, im, -0.000025, epsilon)); + debug.assert(math.approxEq(f64, c.re, 0.999913, epsilon)); + debug.assert(math.approxEq(f64, c.im, -0.000025, epsilon)); } -- cgit v1.2.3 From 84391af7b8951735ec4719a6495ac37c032a1e36 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Apr 2018 21:23:03 -0400 Subject: convert NOTE to TODO so we catch it later See #363 For Complex as a builtin type, see discussion in #949 --- std/math/complex/index.zig | 1 - std/math/complex/sqrt.zig | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/std/math/complex/index.zig b/std/math/complex/index.zig index 24f726453f..a4d493307e 100644 --- a/std/math/complex/index.zig +++ b/std/math/complex/index.zig @@ -23,7 +23,6 @@ pub const sqrt = @import("sqrt.zig").sqrt; pub const tanh = @import("tanh.zig").tanh; pub const tan = @import("tan.zig").tan; -// NOTE: Make this a builtin at some point? pub fn Complex(comptime T: type) type { return struct { const Self = this; diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig index 02e6390572..afda69f7c9 100644 --- a/std/math/complex/sqrt.zig +++ b/std/math/complex/sqrt.zig @@ -4,8 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; -// NOTE: Returning @typeOf(z) here causes issues when trying to access the value. This is -// why we currently assign re, im parts to a new value explicitly for all tests. +// TODO when #733 is solved this can be @typeOf(z) instead of Complex(@typeOf(z.re)) pub fn sqrt(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); -- cgit v1.2.3 From f6cbe9a9cca3718501272cea177ab1ad48852ffe Mon Sep 17 00:00:00 2001 From: Braedon Date: Wed, 25 Apr 2018 14:59:03 +1000 Subject: Utf8 Encode --- std/unicode.zig | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/std/unicode.zig b/std/unicode.zig index 356df824f0..e8a82e7f04 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -1,6 +1,17 @@ const std = @import("./index.zig"); const debug = std.debug; +// Given a Utf8-Codepoint returns how many (1-4) +// bytes there are if represented as an array of bytes. +pub fn utf8CodepointSequenceLength(c: u32) !u3 { + if (c < 0x80) return u3(1); + if (c < 0x800) return u3(2); + if (c -% 0xd800 < 0x800) return error.InvalidCodepoint; + if (c < 0x10000) return u3(3); + if (c < 0x110000) return u3(4); + return error.CodepointTooLarge; +} + /// Given the first byte of a UTF-8 codepoint, /// returns a number 1-4 indicating the total length of the codepoint in bytes. /// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte. @@ -12,6 +23,47 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 { return error.Utf8InvalidStartByte; } +/// Encodes a code point back into utf8 +/// c: the code point +/// out: the out buffer to write to +/// Notes: out has to have a len big enough for the bytes +/// however this limit is dependent on the code point +/// but giving it a minimum of 4 will ensure it will work +/// for all code points. +/// Errors: Will return an error if the code point is invalid. +pub fn utf8Encode(c: u32, out: []u8) !u3 { + if (utf8CodepointSequenceLength(c)) |length| { + debug.assert(out.len >= length); + switch (length) { + 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range + 2 => { + // 64 to convert the codepoint into its segments + out[0] = u8(0b11000000 + c / 64); + out[1] = u8(0b10000000 + c % 64); + }, + 3 => { + // Again using 64 as a conversion into their segments + // But using C / 4096 (64 * 64) as the first, (C/64) % 64 as the second, and just C % 64 as the last + out[0] = u8(0b11100000 + c / 4096); + out[1] = u8(0b10000000 + (c / 64) % 64); + out[2] = u8(0b10000000 + c % 64); + }, + 4 => { + // Same as previously but now its C / 64^3 (262144), (C / 4096) % 64, (C / 64) % 64 and C % 64 + out[0] = u8(0b11110000 + c / 262144); + out[1] = u8(0b10000000 + (c / 4096) % 64); + out[2] = u8(0b10000000 + (c / 64) % 64); + out[3] = u8(0b10000000 + c % 64); + }, + else => unreachable, + } + + return length; + } else |err| { + return err; + } +} + /// Decodes the UTF-8 codepoint encoded in the given slice of bytes. /// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable. /// If you already know the length at comptime, you can call one of @@ -25,6 +77,7 @@ pub fn utf8Decode(bytes: []const u8) !u32 { else => unreachable, }; } + pub fn utf8Decode2(bytes: []const u8) !u32 { debug.assert(bytes.len == 2); debug.assert(bytes[0] & 0b11100000 == 0b11000000); @@ -38,6 +91,7 @@ pub fn utf8Decode2(bytes: []const u8) !u32 { return value; } + pub fn utf8Decode3(bytes: []const u8) !u32 { debug.assert(bytes.len == 3); debug.assert(bytes[0] & 0b11110000 == 0b11100000); @@ -56,6 +110,7 @@ pub fn utf8Decode3(bytes: []const u8) !u32 { return value; } + pub fn utf8Decode4(bytes: []const u8) !u32 { debug.assert(bytes.len == 4); debug.assert(bytes[0] & 0b11111000 == 0b11110000); @@ -170,6 +225,42 @@ const Utf8Iterator = struct { } }; +test "utf8 encode" { + // A few taken from wikipedia a few taken elsewhere + var array: [4]u8 = undefined; + debug.assert((try utf8Encode(try utf8Decode("€"), array[0..])) == 3); + debug.assert(array[0] == 0b11100010); + debug.assert(array[1] == 0b10000010); + debug.assert(array[2] == 0b10101100); + + debug.assert((try utf8Encode(try utf8Decode("$"), array[0..])) == 1); + debug.assert(array[0] == 0b00100100); + + debug.assert((try utf8Encode(try utf8Decode("¢"), array[0..])) == 2); + debug.assert(array[0] == 0b11000010); + debug.assert(array[1] == 0b10100010); + + debug.assert((try utf8Encode(try utf8Decode("𐍈"), array[0..])) == 4); + debug.assert(array[0] == 0b11110000); + debug.assert(array[1] == 0b10010000); + debug.assert(array[2] == 0b10001101); + debug.assert(array[3] == 0b10001000); +} + +test "utf8 encode error" { + var array: [4]u8 = undefined; + testErrorEncode(0xFFFFFF, array[0..], error.CodepointTooLarge); + testErrorEncode(0xd900, array[0..], error.InvalidCodepoint); +} + +fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void { + if (utf8Encode(codePoint, array)) |_| { + unreachable; + } else |err| { + assert(err == expectedErr); + } +} + test "utf8 iterator on ascii" { const s = Utf8View.initComptime("abc"); -- cgit v1.2.3 From 07af6559d8a883dcb21ff99cddb4a836d2bb66bd Mon Sep 17 00:00:00 2001 From: Braedon Date: Wed, 25 Apr 2018 16:26:57 +1000 Subject: Changed to use shifting and masking --- std/unicode.zig | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/std/unicode.zig b/std/unicode.zig index e8a82e7f04..7650f83c83 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -35,25 +35,25 @@ pub fn utf8Encode(c: u32, out: []u8) !u3 { if (utf8CodepointSequenceLength(c)) |length| { debug.assert(out.len >= length); switch (length) { + // The pattern for each is the same + // - Increasing the initial shift by 6 each time + // - Each time after the first shorten the shifted + // value to a max of 0b111111 (63) 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range 2 => { - // 64 to convert the codepoint into its segments - out[0] = u8(0b11000000 + c / 64); - out[1] = u8(0b10000000 + c % 64); + out[0] = u8(0b11000000 | (c >> 6)); + out[1] = u8(0b10000000 | (c & 0b111111)); }, 3 => { - // Again using 64 as a conversion into their segments - // But using C / 4096 (64 * 64) as the first, (C/64) % 64 as the second, and just C % 64 as the last - out[0] = u8(0b11100000 + c / 4096); - out[1] = u8(0b10000000 + (c / 64) % 64); - out[2] = u8(0b10000000 + c % 64); + out[0] = u8(0b11100000 | (c >> 12)); + out[1] = u8(0b10000000 | ((c >> 6) & 0b111111)); + out[2] = u8(0b10000000 | (c & 0b111111)); }, 4 => { - // Same as previously but now its C / 64^3 (262144), (C / 4096) % 64, (C / 64) % 64 and C % 64 - out[0] = u8(0b11110000 + c / 262144); - out[1] = u8(0b10000000 + (c / 4096) % 64); - out[2] = u8(0b10000000 + (c / 64) % 64); - out[3] = u8(0b10000000 + c % 64); + out[0] = u8(0b11110000 | (c >> 18)); + out[1] = u8(0b10000000 | ((c >> 12) & 0b111111)); + out[2] = u8(0b10000000 | ((c >> 6) & 0b111111)); + out[3] = u8(0b10000000 | (c & 0b111111)); }, else => unreachable, } @@ -257,7 +257,7 @@ fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void { if (utf8Encode(codePoint, array)) |_| { unreachable; } else |err| { - assert(err == expectedErr); + debug.assert(err == expectedErr); } } -- cgit v1.2.3 From d68aea4f35fe935f3bf80c86dac4da7c4b88dc5b Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Wed, 25 Apr 2018 11:35:46 +0300 Subject: Added checks for field name/index mapping in TypeInfo generation. Abstracted the parent setting out. --- src/ir.cpp | 139 +++++++++++++++++++++++-------------------------------------- 1 file changed, 53 insertions(+), 86 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index aa456f5030..dee093c57e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13411,7 +13411,6 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, return ira->codegen->invalid_instruction; } - static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type) { @@ -15738,6 +15737,37 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira, return ira->codegen->builtin_types.entry_num_lit_int; } +static void ensure_field_index(TypeTableEntry *type, const char *field_name, size_t index) { + Buf *field_name_buf; + + assert(type != nullptr && !type_is_invalid(type)); + // Check for our field by creating a buffer in place then using the comma operator to free it so that we don't + // leak memory in debug mode. + assert(find_struct_type_field(type, field_name_buf = buf_create_from_str(field_name))->src_index == index && + (buf_deinit(field_name_buf), true)); +} + +static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExprValue *parent) { + assert(struct_val->type->id == TypeTableEntryIdStruct); + assert(parent->type != nullptr && !type_is_invalid(parent->type)); + + switch (parent->type->id) + { + case TypeTableEntryIdArray: + zig_panic("TODO - Only expected struct or union parent."); + case TypeTableEntryIdStruct: + struct_val->data.x_struct.parent.id = ConstParentIdStruct; + struct_val->data.x_struct.parent.data.p_union.union_val = parent; + break; + case TypeTableEntryIdUnion: + struct_val->data.x_struct.parent.id = ConstParentIdUnion; + struct_val->data.x_struct.parent.data.p_union.union_val = parent; + break; + default: + struct_val->data.x_struct.parent.id = ConstParentIdNone; + } +} + static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, TypeTableEntry *type_entry) { assert(type_entry != nullptr); @@ -15772,26 +15802,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; - if (parent->type->id == TypeTableEntryIdStruct) - { - payload->data.x_struct.parent.id = ConstParentIdStruct; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else if (parent->type->id == TypeTableEntryIdUnion) - { - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else - { - payload->data.x_struct.parent.id = ConstParentIdNone; - } + ir_type_info_struct_set_parent(payload, parent); // is_signed: bool + ensure_field_index(payload->type, "is_signed", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_bool; fields[0].data.x_bool = type_entry->data.integral.is_signed; // bits: u8 + ensure_field_index(payload->type, "bits", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); @@ -15810,21 +15829,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; - if (parent->type->id == TypeTableEntryIdStruct) - { - payload->data.x_struct.parent.id = ConstParentIdStruct; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else if (parent->type->id == TypeTableEntryIdUnion) - { - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else - { - payload->data.x_struct.parent.id = ConstParentIdNone; - } + ir_type_info_struct_set_parent(payload, parent); + // bits: u8 + ensure_field_index(payload->type, "bits", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); @@ -15843,33 +15851,25 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(4); payload->data.x_struct.fields = fields; - if (parent->type->id == TypeTableEntryIdStruct) - { - payload->data.x_struct.parent.id = ConstParentIdStruct; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else if (parent->type->id == TypeTableEntryIdUnion) - { - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else - { - payload->data.x_struct.parent.id = ConstParentIdNone; - } + ir_type_info_struct_set_parent(payload, parent); + // is_const: bool + ensure_field_index(payload->type, "is_const", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_bool; fields[0].data.x_bool = type_entry->data.pointer.is_const; // is_volatile: bool + ensure_field_index(payload->type, "is_volatile", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_bool; fields[1].data.x_bool = type_entry->data.pointer.is_volatile; // alignment: u32 + ensure_field_index(payload->type, "alignment", 2); fields[2].special = ConstValSpecialStatic; fields[2].type = ira->codegen->builtin_types.entry_u32; bigint_init_unsigned(&fields[2].data.x_bigint, type_entry->data.pointer.alignment); // child: &TypeInfo + ensure_field_index(payload->type, "child", 3); ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_type->type->id == TypeTableEntryIdMetaType); fields[3].special = ConstValSpecialStatic; @@ -15896,25 +15896,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; - if (parent->type->id == TypeTableEntryIdStruct) - { - payload->data.x_struct.parent.id = ConstParentIdStruct; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else if (parent->type->id == TypeTableEntryIdUnion) - { - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else - { - payload->data.x_struct.parent.id = ConstParentIdNone; - } + ir_type_info_struct_set_parent(payload, parent); + // len: usize + ensure_field_index(payload->type, "len", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_usize; bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); // child: &TypeInfo + ensure_field_index(payload->type, "child", 1); ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_type->type->id == TypeTableEntryIdMetaType); fields[1].special = ConstValSpecialStatic; @@ -15941,21 +15931,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; - if (parent->type->id == TypeTableEntryIdStruct) - { - payload->data.x_struct.parent.id = ConstParentIdStruct; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else if (parent->type->id == TypeTableEntryIdUnion) - { - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else - { - payload->data.x_struct.parent.id = ConstParentIdNone; - } + ir_type_info_struct_set_parent(payload, parent); + // child: &TypeInfo + ensure_field_index(payload->type, "child", 0); ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_type->type->id == TypeTableEntryIdMetaType); fields[0].special = ConstValSpecialStatic; @@ -15982,21 +15961,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; - if (parent->type->id == TypeTableEntryIdStruct) - { - payload->data.x_struct.parent.id = ConstParentIdStruct; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else if (parent->type->id == TypeTableEntryIdUnion) - { - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else - { - payload->data.x_struct.parent.id = ConstParentIdNone; - } + ir_type_info_struct_set_parent(payload, parent); + // child: ?&TypeInfo + ensure_field_index(payload->type, "child", 0); ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_type->type->id == TypeTableEntryIdMetaType); @@ -16046,7 +16014,6 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, assert(var_value->type->id == TypeTableEntryIdMetaType); TypeTableEntry *result_type = var_value->data.x_type; - // TODO: We need to return a const pointer to the typeinfo, not the typeinfo by value. ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = result_type; bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); -- cgit v1.2.3 From 2606993cb48e69015a9152b860e48fca29d330e1 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Wed, 25 Apr 2018 11:59:35 +0300 Subject: Fixed ir_type_info_struct_set_parent for struct parents. --- src/ir.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index dee093c57e..a68747fce4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15747,7 +15747,7 @@ static void ensure_field_index(TypeTableEntry *type, const char *field_name, siz (buf_deinit(field_name_buf), true)); } -static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExprValue *parent) { +static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExprValue *parent, ssize_t parent_field_index) { assert(struct_val->type->id == TypeTableEntryIdStruct); assert(parent->type != nullptr && !type_is_invalid(parent->type)); @@ -15756,10 +15756,13 @@ static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExpr case TypeTableEntryIdArray: zig_panic("TODO - Only expected struct or union parent."); case TypeTableEntryIdStruct: + assert(parent_field_index >= 0); struct_val->data.x_struct.parent.id = ConstParentIdStruct; - struct_val->data.x_struct.parent.data.p_union.union_val = parent; + struct_val->data.x_struct.parent.data.p_struct.struct_val = parent; + struct_val->data.x_struct.parent.data.p_struct.field_index = parent_field_index; break; case TypeTableEntryIdUnion: + assert(parent_field_index == -1); struct_val->data.x_struct.parent.id = ConstParentIdUnion; struct_val->data.x_struct.parent.data.p_union.union_val = parent; break; @@ -15768,7 +15771,8 @@ static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExpr } } -static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, TypeTableEntry *type_entry) +static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, + ssize_t parent_field_index, TypeTableEntry *type_entry) { assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); @@ -15802,7 +15806,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent); + ir_type_info_struct_set_parent(payload, parent, parent_field_index); // is_signed: bool ensure_field_index(payload->type, "is_signed", 0); @@ -15829,7 +15833,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent); + ir_type_info_struct_set_parent(payload, parent, parent_field_index); // bits: u8 ensure_field_index(payload->type, "bits", 0); @@ -15851,7 +15855,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(4); payload->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent); + ir_type_info_struct_set_parent(payload, parent, parent_field_index); // is_const: bool ensure_field_index(payload->type, "is_const", 0); @@ -15880,7 +15884,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.pointer.child_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.pointer.child_type); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.pointer.child_type); fields[3].data.x_ptr.data.ref.pointee = union_val; return payload; } @@ -15896,7 +15900,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent); + ir_type_info_struct_set_parent(payload, parent, parent_field_index); // len: usize ensure_field_index(payload->type, "len", 0); @@ -15915,7 +15919,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.array.child_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.array.child_type); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.array.child_type); fields[1].data.x_ptr.data.ref.pointee = union_val; return payload; } @@ -15931,7 +15935,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent); + ir_type_info_struct_set_parent(payload, parent, parent_field_index); // child: &TypeInfo ensure_field_index(payload->type, "child", 0); @@ -15945,7 +15949,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.maybe.child_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.maybe.child_type); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.maybe.child_type); fields[0].data.x_ptr.data.ref.pointee = union_val; return payload; } @@ -15961,7 +15965,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent); + ir_type_info_struct_set_parent(payload, parent, parent_field_index); // child: ?&TypeInfo ensure_field_index(payload->type, "child", 0); @@ -15990,7 +15994,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.promise.result_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.promise.result_type); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.promise.result_type); maybe_value->data.x_ptr.data.ref.pointee = union_val; fields[0].data.x_maybe = maybe_value; @@ -16017,7 +16021,7 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = result_type; bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); - out_val->data.x_union.payload = ir_make_type_info_value(ira, out_val, type_entry); + out_val->data.x_union.payload = ir_make_type_info_value(ira, out_val, -1, type_entry); return result_type; } -- cgit v1.2.3 From bc160821d33a9284cf8bb85cca6cbf161af71d3b Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Wed, 25 Apr 2018 17:50:11 +0300 Subject: Changed TypeInfo layout. --- src/codegen.cpp | 236 ++++++++++++++++++++++++++++---------------------------- src/ir.cpp | 89 +++++++++++++-------- 2 files changed, 174 insertions(+), 151 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 2e7cc937b4..7d83a38bbf 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6349,143 +6349,143 @@ static void define_builtin_compile_vars(CodeGen *g) { { // TODO: Add method info where methods are supported. buf_appendf(contents, - "pub const IntInfo = struct {\n" - " is_signed: bool,\n" - " bits: u8,\n" - "};\n" - "\n" - "pub const FloatInfo = struct {\n" - " bits: u8,\n" - "};\n" + "pub const TypeInfo = union(TypeId) {\n" + " Type: void,\n" + " Void: void,\n" + " Bool: void,\n" + " NoReturn: void,\n" + " Int: Int,\n" + " Float: Float,\n" + " Pointer: Pointer,\n" + " Array: Array,\n" + " Struct: Struct,\n" + " FloatLiteral: void,\n" + " IntLiteral: void,\n" + " UndefinedLiteral: void,\n" + " NullLiteral: void,\n" + " Nullable: Nullable,\n" + " ErrorUnion: ErrorUnion,\n" + " ErrorSet: ErrorSet,\n" + " Enum: Enum,\n" + " Union: Union,\n" + " Fn: Fn,\n" + " Namespace: void,\n" + " Block: void,\n" + " BoundFn: BoundFn,\n" + " ArgTuple: void,\n" + " Opaque: void,\n" + " Promise: Promise,\n" + "\n\n" + " pub const Int = struct {\n" + " is_signed: bool,\n" + " bits: u8,\n" + " };\n" "\n" - "pub const PointerInfo = struct {\n" - " is_const: bool,\n" - " is_volatile: bool,\n" - " alignment: u32,\n" - " child: &TypeInfo,\n" - "};\n" + " pub const Float = struct {\n" + " bits: u8,\n" + " };\n" "\n" - "pub const ArrayInfo = struct {\n" - " len: usize,\n" - " child: &TypeInfo,\n" - "};\n" + " pub const Pointer = struct {\n" + " is_const: bool,\n" + " is_volatile: bool,\n" + " alignment: u32,\n" + " child: &TypeInfo,\n" + " };\n" "\n" - "pub const ContainerLayout = enum {\n" - " Auto,\n" - " Extern,\n" - " Packed,\n" - "};\n" + " pub const Array = struct {\n" + " len: usize,\n" + " child: &TypeInfo,\n" + " };\n" "\n" - "pub const StructFieldInfo = struct {\n" - " name: []const u8,\n" - " offset: usize,\n" - " type_info: TypeInfo,\n" - "};\n" + " pub const ContainerLayout = enum {\n" + " Auto,\n" + " Extern,\n" + " Packed,\n" + " };\n" "\n" - "pub const StructInfo = struct {\n" - " layout: ContainerLayout,\n" - " fields: []StructFieldInfo,\n" - "};\n" + " pub const StructField = struct {\n" + " name: []const u8,\n" + " offset: usize,\n" + " type_info: TypeInfo,\n" + " };\n" "\n" - "pub const NullableInfo = struct {\n" - " child: &TypeInfo,\n" - "};\n" + " pub const Struct = struct {\n" + " layout: ContainerLayout,\n" + " fields: []StructField,\n" + " };\n" "\n" - "pub const ErrorUnionInfo = struct {\n" - " error_set: ErrorSetInfo,\n" - " payload: &TypeInfo,\n" - "};\n" + " pub const Nullable = struct {\n" + " child: &TypeInfo,\n" + " };\n" "\n" - "pub const ErrorInfo = struct {\n" - " name: []const u8,\n" - " value: usize,\n" - "};\n" + " pub const ErrorUnion = struct {\n" + " error_set: ErrorSet,\n" + " payload: &TypeInfo,\n" + " };\n" "\n" - "pub const ErrorSetInfo = struct {\n" - " errors: []ErrorInfo,\n" - "};\n" + " pub const Error = struct {\n" + " name: []const u8,\n" + " value: usize,\n" + " };\n" "\n" - "pub const EnumFieldInfo = struct {\n" - " name: []const u8,\n" - " value: usize,\n" - "};\n" + " pub const ErrorSet = struct {\n" + " errors: []Error,\n" + " };\n" "\n" - "pub const EnumInfo = struct {\n" - " layout: ContainerLayout,\n" - " tag_type: IntInfo,\n" - " fields: []EnumFieldInfo,\n" - "};\n" + " pub const EnumField = struct {\n" + " name: []const u8,\n" + " value: usize,\n" + " };\n" "\n" - "pub const UnionFieldInfo = struct {\n" - " name: []const u8,\n" - " enum_field: EnumFieldInfo,\n" - " type_info: TypeInfo,\n" - "};\n" + " pub const Enum = struct {\n" + " layout: ContainerLayout,\n" + " tag_type: Int,\n" + " fields: []EnumField,\n" + " };\n" "\n" - "pub const UnionInfo = struct {\n" - " layout: ContainerLayout,\n" - " tag_type: ?EnumInfo,\n" - " fields: []UnionFieldInfo,\n" - "};\n" + " pub const UnionField = struct {\n" + " name: []const u8,\n" + " enum_field: EnumField,\n" + " type_info: TypeInfo,\n" + " };\n" "\n" - "pub const CallingConvention = enum {\n" - " Unspecified,\n" - " C,\n" - " Cold,\n" - " Naked,\n" - " Stdcall,\n" - " Async,\n" - "};\n" + " pub const Union = struct {\n" + " layout: ContainerLayout,\n" + " tag_type: ?Enum,\n" + " fields: []UnionField,\n" + " };\n" "\n" - "pub const FnArgInfo = struct {\n" - " is_comptime: bool,\n" - " name: []const u8,\n" - " type_info: TypeInfo,\n" - "};\n" + " pub const CallingConvention = enum {\n" + " Unspecified,\n" + " C,\n" + " Cold,\n" + " Naked,\n" + " Stdcall,\n" + " Async,\n" + " };\n" "\n" - "pub const FnInfo = struct {\n" - " calling_convention: CallingConvention,\n" - " is_generic: bool,\n" - " is_varargs: bool,\n" - " return_type: &TypeInfo,\n" - " args: []FnArgInfo,\n" - "};\n" + " pub const FnArg = struct {\n" + " is_comptime: bool,\n" + " name: []const u8,\n" + " type_info: TypeInfo,\n" + " };\n" "\n" - "pub const BoundFnInfo = struct {\n" - " bound_type: &TypeInfo,\n" - " fn_info: FnInfo,\n" - "};\n" + " pub const Fn = struct {\n" + " calling_convention: CallingConvention,\n" + " is_generic: bool,\n" + " is_varargs: bool,\n" + " return_type: &TypeInfo,\n" + " args: []FnArg,\n" + " };\n" "\n" - "pub const PromiseInfo = struct {\n" - " child: ?&TypeInfo,\n" - "};\n" + " pub const BoundFn = struct {\n" + " bound_type: &TypeInfo,\n" + " fn_info: Fn,\n" + " };\n" "\n" - "pub const TypeInfo = union(TypeId) {\n" - " Type: void,\n" - " Void: void,\n" - " Bool: void,\n" - " NoReturn: void,\n" - " Int: IntInfo,\n" - " Float: FloatInfo,\n" - " Pointer: PointerInfo,\n" - " Array: ArrayInfo,\n" - " Struct: StructInfo,\n" - " FloatLiteral: void,\n" - " IntLiteral: void,\n" - " UndefinedLiteral: void,\n" - " NullLiteral: void,\n" - " Nullable: NullableInfo,\n" - " ErrorUnion: ErrorUnionInfo,\n" - " ErrorSet: ErrorSetInfo,\n" - " Enum: EnumInfo,\n" - " Union: UnionInfo,\n" - " Fn: FnInfo,\n" - " Namespace: void,\n" - " Block: void,\n" - " BoundFn: BoundFnInfo,\n" - " ArgTuple: void,\n" - " Opaque: void,\n" - " Promise: PromiseInfo,\n" + " pub const Promise = struct {\n" + " child: ?&TypeInfo,\n" + " };\n" "};\n\n"); assert(ContainerLayoutAuto == 0); assert(ContainerLayoutExtern == 1); diff --git a/src/ir.cpp b/src/ir.cpp index a68747fce4..1f2ef0e68f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15737,7 +15737,8 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira, return ira->codegen->builtin_types.entry_num_lit_int; } -static void ensure_field_index(TypeTableEntry *type, const char *field_name, size_t index) { +static void ensure_field_index(TypeTableEntry *type, const char *field_name, size_t index) +{ Buf *field_name_buf; assert(type != nullptr && !type_is_invalid(type)); @@ -15747,7 +15748,8 @@ static void ensure_field_index(TypeTableEntry *type, const char *field_name, siz (buf_deinit(field_name_buf), true)); } -static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExprValue *parent, ssize_t parent_field_index) { +static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExprValue *parent, ssize_t parent_field_index) +{ assert(struct_val->type->id == TypeTableEntryIdStruct); assert(parent->type != nullptr && !type_is_invalid(parent->type)); @@ -15771,6 +15773,37 @@ static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExpr } } +static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name) +{ + ConstExprValue *type_info_var = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_var->type->id == TypeTableEntryIdMetaType); + + TypeTableEntry *type_info_type = type_info_var->data.x_type; + assert(type_info_type->id == TypeTableEntryIdUnion); + + if (type_name == nullptr) + return type_info_type; + + // @TODO + + ScopeDecls *type_info_scope = get_container_scope(type_info_type); + assert(type_info_scope != nullptr); + + Buf field_name = BUF_INIT; + buf_init_from_str(&field_name, type_name); + auto entry = type_info_scope->decl_table.maybe_get(&field_name); + buf_deinit(&field_name); + assert(entry != nullptr); + + TldVar *tld = (TldVar *)entry->value; + assert(tld->base.id == TldIdVar); + + VariableTableEntry *var = tld->var; + + assert(var->value->type->id == TypeTableEntryIdMetaType); + return var->value->data.x_type; +} + static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, ssize_t parent_field_index, TypeTableEntry *type_entry) { @@ -15798,10 +15831,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p { ConstExprValue *payload = create_const_vals(1); payload->special = ConstValSpecialStatic; - - ConstExprValue *int_info_type = get_builtin_value(ira->codegen, "IntInfo"); - assert(int_info_type->type->id == TypeTableEntryIdMetaType); - payload->type = int_info_type->data.x_type; + payload->type = ir_type_info_get_type(ira, "Int"); ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; @@ -15825,10 +15855,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p { ConstExprValue *payload = create_const_vals(1); payload->special = ConstValSpecialStatic; - - ConstExprValue *float_info_type = get_builtin_value(ira->codegen, "FloatInfo"); - assert(float_info_type->type->id == TypeTableEntryIdMetaType); - payload->type = float_info_type->data.x_type; + payload->type = ir_type_info_get_type(ira, "Float"); ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; @@ -15847,10 +15874,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p { ConstExprValue *payload = create_const_vals(1); payload->special = ConstValSpecialStatic; - - ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "PointerInfo"); - assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); - payload->type = pointer_info_type->data.x_type; + payload->type = ir_type_info_get_type(ira, "Pointer"); ConstExprValue *fields = create_const_vals(4); payload->data.x_struct.fields = fields; @@ -15884,7 +15908,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.pointer.child_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.pointer.child_type); + + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, + type_entry->data.pointer.child_type); + fields[3].data.x_ptr.data.ref.pointee = union_val; return payload; } @@ -15892,10 +15919,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p { ConstExprValue *payload = create_const_vals(1); payload->special = ConstValSpecialStatic; - - ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "ArrayInfo"); - assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); - payload->type = pointer_info_type->data.x_type; + payload->type = ir_type_info_get_type(ira, "Array"); ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; @@ -15919,7 +15943,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.array.child_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.array.child_type); + + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, + type_entry->data.array.child_type); + fields[1].data.x_ptr.data.ref.pointee = union_val; return payload; } @@ -15927,10 +15954,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p { ConstExprValue *payload = create_const_vals(1); payload->special = ConstValSpecialStatic; - - ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "NullableInfo"); - assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); - payload->type = pointer_info_type->data.x_type; + payload->type = ir_type_info_get_type(ira, "Nullable"); ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; @@ -15949,7 +15973,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.maybe.child_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.maybe.child_type); + + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, + type_entry->data.maybe.child_type); + fields[0].data.x_ptr.data.ref.pointee = union_val; return payload; } @@ -15957,10 +15984,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p { ConstExprValue *payload = create_const_vals(1); payload->special = ConstValSpecialStatic; - - ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "PromiseInfo"); - assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); - payload->type = pointer_info_type->data.x_type; + payload->type = ir_type_info_get_type(ira, "Promise"); ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; @@ -15994,7 +16018,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.promise.result_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.promise.result_type); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, + type_entry->data.promise.result_type); maybe_value->data.x_ptr.data.ref.pointee = union_val; fields[0].data.x_maybe = maybe_value; @@ -16014,9 +16039,7 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, if (type_is_invalid(type_entry)) return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeInfo"); - assert(var_value->type->id == TypeTableEntryIdMetaType); - TypeTableEntry *result_type = var_value->data.x_type; + TypeTableEntry *result_type = ir_type_info_get_type(ira, nullptr); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = result_type; -- cgit v1.2.3 From dd88d7deda66a4e4e1527831e7b24a3cf358d1b7 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 13:27:16 +0300 Subject: Cleanup --- src/ir.cpp | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 1f2ef0e68f..cbd8d51b32 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15775,17 +15775,20 @@ static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExpr static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name) { - ConstExprValue *type_info_var = get_builtin_value(ira->codegen, "TypeInfo"); - assert(type_info_var->type->id == TypeTableEntryIdMetaType); + static ConstExprValue *type_info_var = nullptr; + static TypeTableEntry *type_info_type = nullptr; + if (type_info_var == nullptr) + { + type_info_var = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_var->type->id == TypeTableEntryIdMetaType); - TypeTableEntry *type_info_type = type_info_var->data.x_type; - assert(type_info_type->id == TypeTableEntryIdUnion); + type_info_type = type_info_var->data.x_type; + assert(type_info_type->id == TypeTableEntryIdUnion); + } if (type_name == nullptr) return type_info_type; - // @TODO - ScopeDecls *type_info_scope = get_container_scope(type_info_type); assert(type_info_scope != nullptr); @@ -15898,15 +15901,16 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p bigint_init_unsigned(&fields[2].data.x_bigint, type_entry->data.pointer.alignment); // child: &TypeInfo ensure_field_index(payload->type, "child", 3); - ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); - assert(type_info_type->type->id == TypeTableEntryIdMetaType); + + TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); + fields[3].special = ConstValSpecialStatic; - fields[3].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + fields[3].type = get_pointer_to_type(ira->codegen, type_info_type, false); fields[3].data.x_ptr.special = ConstPtrSpecialRef; fields[3].data.x_ptr.mut = ConstPtrMutComptimeVar; ConstExprValue *union_val = create_const_vals(1); union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type->data.x_type; + union_val->type = type_info_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.pointer.child_type->id)); union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, @@ -15933,15 +15937,16 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); // child: &TypeInfo ensure_field_index(payload->type, "child", 1); - ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); - assert(type_info_type->type->id == TypeTableEntryIdMetaType); + + TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); + fields[1].special = ConstValSpecialStatic; - fields[1].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + fields[1].type = get_pointer_to_type(ira->codegen, type_info_type, false); fields[1].data.x_ptr.special = ConstPtrSpecialRef; fields[1].data.x_ptr.mut = ConstPtrMutComptimeVar; ConstExprValue *union_val = create_const_vals(1); union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type->data.x_type; + union_val->type = type_info_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.array.child_type->id)); union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, @@ -15963,15 +15968,16 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p // child: &TypeInfo ensure_field_index(payload->type, "child", 0); - ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); - assert(type_info_type->type->id == TypeTableEntryIdMetaType); + + TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); + fields[0].special = ConstValSpecialStatic; - fields[0].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + fields[0].type = get_pointer_to_type(ira->codegen, type_info_type, false); fields[0].data.x_ptr.special = ConstPtrSpecialRef; fields[0].data.x_ptr.mut = ConstPtrMutComptimeVar; ConstExprValue *union_val = create_const_vals(1); union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type->data.x_type; + union_val->type = type_info_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.maybe.child_type->id)); union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, @@ -15993,10 +15999,9 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p // child: ?&TypeInfo ensure_field_index(payload->type, "child", 0); - ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); - assert(type_info_type->type->id == TypeTableEntryIdMetaType); - TypeTableEntry *type_info_ptr_type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); + TypeTableEntry *type_info_ptr_type = get_pointer_to_type(ira->codegen, type_info_type, false); fields[0].special = ConstValSpecialStatic; fields[0].type = get_maybe_type(ira->codegen, type_info_ptr_type); @@ -16016,7 +16021,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *union_val = create_const_vals(1); union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type->data.x_type; + union_val->type = type_info_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.promise.result_type->id)); union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.promise.result_type); -- cgit v1.2.3 From bb56360bfaa317809bd3c742a655b357bd5b6226 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 14:03:19 +0300 Subject: Added TypeInfo cache --- src/all_types.hpp | 1 + src/codegen.cpp | 10 ++++- src/ir.cpp | 109 ++++++++++++++++++++++++++++++------------------------ 3 files changed, 70 insertions(+), 50 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 332fc5204e..c1c6c9a1a5 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1507,6 +1507,7 @@ struct CodeGen { HashMap exported_symbol_names; HashMap external_prototypes; HashMap string_literals_table; + HashMap type_info_cache; ZigList import_queue; diff --git a/src/codegen.cpp b/src/codegen.cpp index 7d83a38bbf..71df7fcd65 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -88,6 +88,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->exported_symbol_names.init(8); g->external_prototypes.init(8); g->string_literals_table.init(16); + g->type_info_cache.init(32); g->is_test_build = false; g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib); buf_resize(&g->global_asm, 0); @@ -6347,7 +6348,8 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "};\n\n"); } { - // TODO: Add method info where methods are supported. + // @TODO Add method info where methods are supported. + // @TODO Add Namespace info. buf_appendf(contents, "pub const TypeInfo = union(TypeId) {\n" " Type: void,\n" @@ -6403,6 +6405,11 @@ static void define_builtin_compile_vars(CodeGen *g) { " Packed,\n" " };\n" "\n" + " pub const Method = struct {\n" + " name: []const u8,\n" + " fn_info: Fn,\n" + " };\n" + "\n" " pub const StructField = struct {\n" " name: []const u8,\n" " offset: usize,\n" @@ -6412,6 +6419,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const Struct = struct {\n" " layout: ContainerLayout,\n" " fields: []StructField,\n" + " methods: []Method,\n" " };\n" "\n" " pub const Nullable = struct {\n" diff --git a/src/ir.cpp b/src/ir.cpp index cbd8d51b32..9229e4e89d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15810,6 +15810,15 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, ssize_t parent_field_index, TypeTableEntry *type_entry) { + // Lookup an available value in our cache. + auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); + if (entry != nullptr) + return entry->value; + + ConstExprValue *result = nullptr; + + // @TODO + // We should probably cache the values generated with a type_entry key. assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); @@ -15832,75 +15841,73 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p return nullptr; case TypeTableEntryIdInt: { - ConstExprValue *payload = create_const_vals(1); - payload->special = ConstValSpecialStatic; - payload->type = ir_type_info_get_type(ira, "Int"); + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Int"); ConstExprValue *fields = create_const_vals(2); - payload->data.x_struct.fields = fields; + result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent, parent_field_index); + ir_type_info_struct_set_parent(result, parent, parent_field_index); // is_signed: bool - ensure_field_index(payload->type, "is_signed", 0); + ensure_field_index(result->type, "is_signed", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_bool; fields[0].data.x_bool = type_entry->data.integral.is_signed; // bits: u8 - ensure_field_index(payload->type, "bits", 1); + ensure_field_index(result->type, "bits", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); - - return payload; + break; } case TypeTableEntryIdFloat: { - ConstExprValue *payload = create_const_vals(1); - payload->special = ConstValSpecialStatic; - payload->type = ir_type_info_get_type(ira, "Float"); + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Float"); ConstExprValue *fields = create_const_vals(1); - payload->data.x_struct.fields = fields; + result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent, parent_field_index); + ir_type_info_struct_set_parent(result, parent, parent_field_index); // bits: u8 - ensure_field_index(payload->type, "bits", 0); + ensure_field_index(result->type, "bits", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); - - return payload; + break; } case TypeTableEntryIdPointer: { - ConstExprValue *payload = create_const_vals(1); - payload->special = ConstValSpecialStatic; - payload->type = ir_type_info_get_type(ira, "Pointer"); + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Pointer"); ConstExprValue *fields = create_const_vals(4); - payload->data.x_struct.fields = fields; + result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent, parent_field_index); + ir_type_info_struct_set_parent(result, parent, parent_field_index); // is_const: bool - ensure_field_index(payload->type, "is_const", 0); + ensure_field_index(result->type, "is_const", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_bool; fields[0].data.x_bool = type_entry->data.pointer.is_const; // is_volatile: bool - ensure_field_index(payload->type, "is_volatile", 1); + ensure_field_index(result->type, "is_volatile", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_bool; fields[1].data.x_bool = type_entry->data.pointer.is_volatile; // alignment: u32 - ensure_field_index(payload->type, "alignment", 2); + ensure_field_index(result->type, "alignment", 2); fields[2].special = ConstValSpecialStatic; fields[2].type = ira->codegen->builtin_types.entry_u32; bigint_init_unsigned(&fields[2].data.x_bigint, type_entry->data.pointer.alignment); // child: &TypeInfo - ensure_field_index(payload->type, "child", 3); + ensure_field_index(result->type, "child", 3); TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); @@ -15917,26 +15924,26 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p type_entry->data.pointer.child_type); fields[3].data.x_ptr.data.ref.pointee = union_val; - return payload; + break; } case TypeTableEntryIdArray: { - ConstExprValue *payload = create_const_vals(1); - payload->special = ConstValSpecialStatic; - payload->type = ir_type_info_get_type(ira, "Array"); + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Array"); ConstExprValue *fields = create_const_vals(2); - payload->data.x_struct.fields = fields; + result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent, parent_field_index); + ir_type_info_struct_set_parent(result, parent, parent_field_index); // len: usize - ensure_field_index(payload->type, "len", 0); + ensure_field_index(result->type, "len", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_usize; bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); // child: &TypeInfo - ensure_field_index(payload->type, "child", 1); + ensure_field_index(result->type, "child", 1); TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); @@ -15953,21 +15960,21 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p type_entry->data.array.child_type); fields[1].data.x_ptr.data.ref.pointee = union_val; - return payload; + break; } case TypeTableEntryIdMaybe: { - ConstExprValue *payload = create_const_vals(1); - payload->special = ConstValSpecialStatic; - payload->type = ir_type_info_get_type(ira, "Nullable"); + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Nullable"); ConstExprValue *fields = create_const_vals(1); - payload->data.x_struct.fields = fields; + result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent, parent_field_index); + ir_type_info_struct_set_parent(result, parent, parent_field_index); // child: &TypeInfo - ensure_field_index(payload->type, "child", 0); + ensure_field_index(result->type, "child", 0); TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); @@ -15984,21 +15991,21 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p type_entry->data.maybe.child_type); fields[0].data.x_ptr.data.ref.pointee = union_val; - return payload; + break; } case TypeTableEntryIdPromise: { - ConstExprValue *payload = create_const_vals(1); - payload->special = ConstValSpecialStatic; - payload->type = ir_type_info_get_type(ira, "Promise"); + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Promise"); ConstExprValue *fields = create_const_vals(1); - payload->data.x_struct.fields = fields; + result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent, parent_field_index); + ir_type_info_struct_set_parent(result, parent, parent_field_index); // child: ?&TypeInfo - ensure_field_index(payload->type, "child", 0); + ensure_field_index(result->type, "child", 0); TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); TypeTableEntry *type_info_ptr_type = get_pointer_to_type(ira->codegen, type_info_type, false); @@ -16029,11 +16036,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p maybe_value->data.x_ptr.data.ref.pointee = union_val; fields[0].data.x_maybe = maybe_value; } - return payload; + break; } default: zig_unreachable(); } + + assert(result != nullptr); + ira->codegen->type_info_cache.put(type_entry, result); + return result; } static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, -- cgit v1.2.3 From 7a91e4736a4151bd41b65b6e3b395cc51c443162 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 14:29:27 +0300 Subject: Reset parent on cached TypeInfo values if we need to. --- src/codegen.cpp | 3 ++- src/ir.cpp | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 71df7fcd65..a8c32044fc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6348,7 +6348,6 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "};\n\n"); } { - // @TODO Add method info where methods are supported. // @TODO Add Namespace info. buf_appendf(contents, "pub const TypeInfo = union(TypeId) {\n" @@ -6449,6 +6448,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " layout: ContainerLayout,\n" " tag_type: Int,\n" " fields: []EnumField,\n" + " methods: []Method,\n" " };\n" "\n" " pub const UnionField = struct {\n" @@ -6461,6 +6461,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " layout: ContainerLayout,\n" " tag_type: ?Enum,\n" " fields: []UnionField,\n" + " methods: []Method,\n" " };\n" "\n" " pub const CallingConvention = enum {\n" diff --git a/src/ir.cpp b/src/ir.cpp index 9229e4e89d..5cffb882f2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15810,17 +15810,61 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, ssize_t parent_field_index, TypeTableEntry *type_entry) { + assert(type_entry != nullptr); + assert(!type_is_invalid(type_entry)); + // Lookup an available value in our cache. auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); if (entry != nullptr) - return entry->value; + { + // Override the parent if we need to. + ConstExprValue *result = entry->value; - ConstExprValue *result = nullptr; + assert(result->type->id == TypeTableEntryIdStruct); - // @TODO - // We should probably cache the values generated with a type_entry key. - assert(type_entry != nullptr); - assert(!type_is_invalid(type_entry)); + ConstParent *curr_parent = &result->data.x_struct.parent; + if (curr_parent->id == ConstParentIdStruct) + { + if (curr_parent->data.p_struct.struct_val == parent && + parent_field_index != -1 && + curr_parent->data.p_struct.field_index == (size_t)parent_field_index) + { + return result; + } + ConstExprValue *new_result = create_const_vals(1); + copy_const_val(new_result, result, true); + ir_type_info_struct_set_parent(new_result, parent, parent_field_index); + return new_result; + } + else if (curr_parent->id == ConstParentIdUnion) + { + if (curr_parent->data.p_union.union_val == parent) + { + return result; + } + ConstExprValue *new_result = create_const_vals(1); + copy_const_val(new_result, result, true); + ir_type_info_struct_set_parent(new_result, parent, parent_field_index); + return new_result; + } + else if (curr_parent->id == ConstParentIdNone) + { + if (parent->type->id != TypeTableEntryIdStruct && + parent->type->id != TypeTableEntryIdArray && + parent->type->id != TypeTableEntryIdUnion) + { + return result; + } + ConstExprValue *new_result = create_const_vals(1); + copy_const_val(new_result, result, true); + ir_type_info_struct_set_parent(new_result, parent, parent_field_index); + return new_result; + } + + return result; + } + + ConstExprValue *result = nullptr; switch (type_entry->id) { @@ -16042,6 +16086,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p zig_unreachable(); } + // Cache the returned value. assert(result != nullptr); ira->codegen->type_info_cache.put(type_entry, result); return result; -- cgit v1.2.3 From f5977f68ebe344ec9c96507322218b87c24804a1 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 16:41:59 +0300 Subject: Added Enum TypeInfo except for methods --- src/codegen.cpp | 8 ++-- src/ir.cpp | 125 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 111 insertions(+), 22 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index a8c32044fc..9c4e5b1555 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6412,7 +6412,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const StructField = struct {\n" " name: []const u8,\n" " offset: usize,\n" - " type_info: TypeInfo,\n" + " type_info: &TypeInfo,\n" " };\n" "\n" " pub const Struct = struct {\n" @@ -6446,7 +6446,7 @@ static void define_builtin_compile_vars(CodeGen *g) { "\n" " pub const Enum = struct {\n" " layout: ContainerLayout,\n" - " tag_type: Int,\n" + " tag_type: &Int,\n" " fields: []EnumField,\n" " methods: []Method,\n" " };\n" @@ -6454,7 +6454,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const UnionField = struct {\n" " name: []const u8,\n" " enum_field: EnumField,\n" - " type_info: TypeInfo,\n" + " type_info: &TypeInfo,\n" " };\n" "\n" " pub const Union = struct {\n" @@ -6476,7 +6476,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const FnArg = struct {\n" " is_comptime: bool,\n" " name: []const u8,\n" - " type_info: TypeInfo,\n" + " type_info: &TypeInfo,\n" " };\n" "\n" " pub const Fn = struct {\n" diff --git a/src/ir.cpp b/src/ir.cpp index 5cffb882f2..9ca95d6222 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15864,7 +15864,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p return result; } - ConstExprValue *result = nullptr; + // If the value is not present in the cache, we will build it, add it and return it. + // We add values to the cache eagerly, as soon as we have filled out the root object's fields. + // That way, if we need to fetch the value in a recursive call down the line, even if we need to + // copy the value and reajust the parent, the value we get back still points to child values that + // will be filled later. switch (type_entry->id) { @@ -15885,7 +15889,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p return nullptr; case TypeTableEntryIdInt: { - result = create_const_vals(1); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Int"); @@ -15893,6 +15897,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p result->data.x_struct.fields = fields; ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); // is_signed: bool ensure_field_index(result->type, "is_signed", 0); @@ -15904,11 +15909,12 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); - break; + + return result; } case TypeTableEntryIdFloat: { - result = create_const_vals(1); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Float"); @@ -15916,17 +15922,19 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p result->data.x_struct.fields = fields; ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); // bits: u8 ensure_field_index(result->type, "bits", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); - break; + + return result; } case TypeTableEntryIdPointer: { - result = create_const_vals(1); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Pointer"); @@ -15934,6 +15942,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p result->data.x_struct.fields = fields; ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); // is_const: bool ensure_field_index(result->type, "is_const", 0); @@ -15968,11 +15977,12 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p type_entry->data.pointer.child_type); fields[3].data.x_ptr.data.ref.pointee = union_val; - break; + + return result; } case TypeTableEntryIdArray: { - result = create_const_vals(1); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Array"); @@ -15980,6 +15990,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p result->data.x_struct.fields = fields; ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); // len: usize ensure_field_index(result->type, "len", 0); @@ -15988,7 +15999,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); // child: &TypeInfo ensure_field_index(result->type, "child", 1); - TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); fields[1].special = ConstValSpecialStatic; @@ -16004,11 +16014,12 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p type_entry->data.array.child_type); fields[1].data.x_ptr.data.ref.pointee = union_val; - break; + + return result; } case TypeTableEntryIdMaybe: { - result = create_const_vals(1); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Nullable"); @@ -16016,6 +16027,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p result->data.x_struct.fields = fields; ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); // child: &TypeInfo ensure_field_index(result->type, "child", 0); @@ -16035,11 +16047,12 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p type_entry->data.maybe.child_type); fields[0].data.x_ptr.data.ref.pointee = union_val; - break; + + return result; } case TypeTableEntryIdPromise: { - result = create_const_vals(1); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Promise"); @@ -16047,6 +16060,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p result->data.x_struct.fields = fields; ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); // child: ?&TypeInfo ensure_field_index(result->type, "child", 0); @@ -16080,16 +16094,91 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p maybe_value->data.x_ptr.data.ref.pointee = union_val; fields[0].data.x_maybe = maybe_value; } - break; + + return result; + } + case TypeTableEntryIdEnum: + { + ConstExprValue *result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Enum"); + + ConstExprValue *fields = create_const_vals(4); + result->data.x_struct.fields = fields; + + ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); + + // layout: ContainerLayout + ensure_field_index(result->type, "layout", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ir_type_info_get_type(ira, "ContainerLayout"); + bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.enumeration.layout); + // tag_type: &TypeInfo.Int + ensure_field_index(result->type, "tag_type", 1); + + TypeTableEntry *type_info_int_type = ir_type_info_get_type(ira, "Int"); + + fields[1].special = ConstValSpecialStatic; + fields[1].type = get_pointer_to_type(ira->codegen, type_info_int_type, false); + fields[1].data.x_ptr.special = ConstPtrSpecialRef; + fields[1].data.x_ptr.mut = ConstPtrMutComptimeVar; + + ConstExprValue *tag_type_info_struct = ir_make_type_info_value(ira, &fields[1], -1, + type_entry->data.enumeration.tag_int_type); + assert(tag_type_info_struct->type == type_info_int_type); + fields[1].data.x_ptr.data.ref.pointee = tag_type_info_struct; + // fields: []TypeInfo.EnumField + ensure_field_index(result->type, "fields", 2); + + TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField"); + // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) + // ensure_field_index(type_info_enum_field_type, "name", 0); + // ensure_field_index(type_info_enum_field_type, "value", 1); + + uint32_t enum_field_count = type_entry->data.enumeration.src_field_count; + + ConstExprValue *enum_field_array = create_const_vals(1); + enum_field_array->special = ConstValSpecialStatic; + enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count); + enum_field_array->data.x_array.special = ConstArraySpecialNone; + enum_field_array->data.x_array.s_none.parent.id = ConstParentIdNone; + enum_field_array->data.x_array.s_none.elements = create_const_vals(enum_field_count); + + init_const_slice(ira->codegen, &fields[2], enum_field_array, 0, enum_field_count, false); + + for (uint32_t enum_field_index = 0; enum_field_index < enum_field_count; enum_field_index++) + { + TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; + ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index]; + + enum_field_val->special = ConstValSpecialStatic; + enum_field_val->type = type_info_enum_field_type; + + ConstExprValue *inner_fields = create_const_vals(2); + inner_fields[1].special = ConstValSpecialStatic; + inner_fields[1].type = ira->codegen->builtin_types.entry_usize; + + ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name); + init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true); + + bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value); + + enum_field_val->data.x_struct.fields = inner_fields; + enum_field_val->data.x_struct.parent.id = ConstParentIdArray; + enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; + enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; + } + + // @TODO + // methods: []TypeInfo.Method + return result; } default: zig_unreachable(); } - // Cache the returned value. - assert(result != nullptr); - ira->codegen->type_info_cache.put(type_entry, result); - return result; + zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, -- cgit v1.2.3 From 4aa5d87ada56f9887d49c7a2a80246e0bb9d5fb9 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 17:14:38 +0300 Subject: Added ErrorSet TypeInfo generation. --- src/codegen.cpp | 2 +- src/ir.cpp | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 9c4e5b1555..8c56af674c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6426,7 +6426,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " };\n" "\n" " pub const ErrorUnion = struct {\n" - " error_set: ErrorSet,\n" + " error_set: &ErrorSet,\n" " payload: &TypeInfo,\n" " };\n" "\n" diff --git a/src/ir.cpp b/src/ir.cpp index 9ca95d6222..92fd7a7018 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16172,6 +16172,63 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p // @TODO // methods: []TypeInfo.Method + return result; + } + case TypeTableEntryIdErrorSet: + { + ConstExprValue *result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "ErrorSet"); + + ConstExprValue *fields = create_const_vals(1); + result->data.x_struct.fields = fields; + + ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); + + // errors: []TypeInfo.Error + ensure_field_index(result->type, "errors", 0); + + TypeTableEntry *type_info_error_type = ir_type_info_get_type(ira, "Error"); + // @TODO Same as above in Enum TypeInfo generation. + // ensure_field_index(type_info_error_type, "name", 0); + // ensure_field_index(type_info_error_type, "value", 1); + + uint32_t error_count = type_entry->data.error_set.err_count; + ConstExprValue *error_array = create_const_vals(1); + error_array->special = ConstValSpecialStatic; + error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count); + error_array->data.x_array.special = ConstArraySpecialNone; + error_array->data.x_array.s_none.parent.id = ConstParentIdNone; + error_array->data.x_array.s_none.elements = create_const_vals(error_count); + + init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false); + for (uint32_t error_index = 0; error_index < error_count; error_index++) + { + ErrorTableEntry *error = type_entry->data.error_set.errors[error_index]; + ConstExprValue *error_val = &error_array->data.x_array.s_none.elements[error_index]; + + error_val->special = ConstValSpecialStatic; + error_val->type = type_info_error_type; + + ConstExprValue *inner_fields = create_const_vals(2); + inner_fields[1].special = ConstValSpecialStatic; + inner_fields[1].type = ira->codegen->builtin_types.entry_usize; + + ConstExprValue *name = nullptr; + if (error->cached_error_name_val != nullptr) + name = error->cached_error_name_val; + if (name == nullptr) + name = create_const_str_lit(ira->codegen, &error->name); + init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(&error->name), true); + bigint_init_unsigned(&inner_fields[1].data.x_bigint, error->value); + + error_val->data.x_struct.fields = inner_fields; + error_val->data.x_struct.parent.id = ConstParentIdArray; + error_val->data.x_struct.parent.data.p_array.array_val = error_array; + error_val->data.x_struct.parent.data.p_array.elem_index = error_index; + } + return result; } default: -- cgit v1.2.3 From fbbbee6b72991b65c31a8eac8f1edfc3d1a88b59 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 18:18:47 +0300 Subject: Switched to shallow TypeInfo. --- src/codegen.cpp | 24 ++--- src/ir.cpp | 271 ++++++++++++-------------------------------------------- 2 files changed, 69 insertions(+), 226 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 8c56af674c..c8bb502718 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6390,12 +6390,12 @@ static void define_builtin_compile_vars(CodeGen *g) { " is_const: bool,\n" " is_volatile: bool,\n" " alignment: u32,\n" - " child: &TypeInfo,\n" + " child: type,\n" " };\n" "\n" " pub const Array = struct {\n" " len: usize,\n" - " child: &TypeInfo,\n" + " child: type,\n" " };\n" "\n" " pub const ContainerLayout = enum {\n" @@ -6412,7 +6412,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const StructField = struct {\n" " name: []const u8,\n" " offset: usize,\n" - " type_info: &TypeInfo,\n" + " field_type: type,\n" " };\n" "\n" " pub const Struct = struct {\n" @@ -6422,12 +6422,12 @@ static void define_builtin_compile_vars(CodeGen *g) { " };\n" "\n" " pub const Nullable = struct {\n" - " child: &TypeInfo,\n" + " child: type,\n" " };\n" "\n" " pub const ErrorUnion = struct {\n" " error_set: &ErrorSet,\n" - " payload: &TypeInfo,\n" + " payload: type,\n" " };\n" "\n" " pub const Error = struct {\n" @@ -6446,7 +6446,7 @@ static void define_builtin_compile_vars(CodeGen *g) { "\n" " pub const Enum = struct {\n" " layout: ContainerLayout,\n" - " tag_type: &Int,\n" + " tag_type: type,\n" " fields: []EnumField,\n" " methods: []Method,\n" " };\n" @@ -6454,12 +6454,12 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const UnionField = struct {\n" " name: []const u8,\n" " enum_field: EnumField,\n" - " type_info: &TypeInfo,\n" + " field_type: type,\n" " };\n" "\n" " pub const Union = struct {\n" " layout: ContainerLayout,\n" - " tag_type: ?Enum,\n" + " tag_type: type,\n" " fields: []UnionField,\n" " methods: []Method,\n" " };\n" @@ -6476,24 +6476,24 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const FnArg = struct {\n" " is_comptime: bool,\n" " name: []const u8,\n" - " type_info: &TypeInfo,\n" + " arg_type: type,\n" " };\n" "\n" " pub const Fn = struct {\n" " calling_convention: CallingConvention,\n" " is_generic: bool,\n" " is_varargs: bool,\n" - " return_type: &TypeInfo,\n" + " return_type: type,\n" " args: []FnArg,\n" " };\n" "\n" " pub const BoundFn = struct {\n" - " bound_type: &TypeInfo,\n" + " bound_type: type,\n" " fn_info: Fn,\n" " };\n" "\n" " pub const Promise = struct {\n" - " child: ?&TypeInfo,\n" + " child: type,\n" " };\n" "};\n\n"); assert(ContainerLayoutAuto == 0); diff --git a/src/ir.cpp b/src/ir.cpp index 92fd7a7018..fdf1a0c8ab 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15748,31 +15748,6 @@ static void ensure_field_index(TypeTableEntry *type, const char *field_name, siz (buf_deinit(field_name_buf), true)); } -static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExprValue *parent, ssize_t parent_field_index) -{ - assert(struct_val->type->id == TypeTableEntryIdStruct); - assert(parent->type != nullptr && !type_is_invalid(parent->type)); - - switch (parent->type->id) - { - case TypeTableEntryIdArray: - zig_panic("TODO - Only expected struct or union parent."); - case TypeTableEntryIdStruct: - assert(parent_field_index >= 0); - struct_val->data.x_struct.parent.id = ConstParentIdStruct; - struct_val->data.x_struct.parent.data.p_struct.struct_val = parent; - struct_val->data.x_struct.parent.data.p_struct.field_index = parent_field_index; - break; - case TypeTableEntryIdUnion: - assert(parent_field_index == -1); - struct_val->data.x_struct.parent.id = ConstParentIdUnion; - struct_val->data.x_struct.parent.data.p_union.union_val = parent; - break; - default: - struct_val->data.x_struct.parent.id = ConstParentIdNone; - } -} - static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name) { static ConstExprValue *type_info_var = nullptr; @@ -15807,69 +15782,12 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na return var->value->data.x_type; } -static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, - ssize_t parent_field_index, TypeTableEntry *type_entry) +static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) { assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); - // Lookup an available value in our cache. - auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); - if (entry != nullptr) - { - // Override the parent if we need to. - ConstExprValue *result = entry->value; - - assert(result->type->id == TypeTableEntryIdStruct); - - ConstParent *curr_parent = &result->data.x_struct.parent; - if (curr_parent->id == ConstParentIdStruct) - { - if (curr_parent->data.p_struct.struct_val == parent && - parent_field_index != -1 && - curr_parent->data.p_struct.field_index == (size_t)parent_field_index) - { - return result; - } - ConstExprValue *new_result = create_const_vals(1); - copy_const_val(new_result, result, true); - ir_type_info_struct_set_parent(new_result, parent, parent_field_index); - return new_result; - } - else if (curr_parent->id == ConstParentIdUnion) - { - if (curr_parent->data.p_union.union_val == parent) - { - return result; - } - ConstExprValue *new_result = create_const_vals(1); - copy_const_val(new_result, result, true); - ir_type_info_struct_set_parent(new_result, parent, parent_field_index); - return new_result; - } - else if (curr_parent->id == ConstParentIdNone) - { - if (parent->type->id != TypeTableEntryIdStruct && - parent->type->id != TypeTableEntryIdArray && - parent->type->id != TypeTableEntryIdUnion) - { - return result; - } - ConstExprValue *new_result = create_const_vals(1); - copy_const_val(new_result, result, true); - ir_type_info_struct_set_parent(new_result, parent, parent_field_index); - return new_result; - } - - return result; - } - - // If the value is not present in the cache, we will build it, add it and return it. - // We add values to the cache eagerly, as soon as we have filled out the root object's fields. - // That way, if we need to fetch the value in a recursive call down the line, even if we need to - // copy the value and reajust the parent, the value we get back still points to child values that - // will be filled later. - + ConstExprValue *result = nullptr; switch (type_entry->id) { case TypeTableEntryIdInvalid: @@ -15887,18 +15805,24 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: return nullptr; + default: + { + // Lookup an available value in our cache. + auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); + if (entry != nullptr) + return entry->value; + + // Fallthrough if we don't find one. + } case TypeTableEntryIdInt: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Int"); ConstExprValue *fields = create_const_vals(2); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - // is_signed: bool ensure_field_index(result->type, "is_signed", 0); fields[0].special = ConstValSpecialStatic; @@ -15910,40 +15834,34 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p fields[1].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); - return result; + break; } case TypeTableEntryIdFloat: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Float"); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - // bits: u8 ensure_field_index(result->type, "bits", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); - return result; + break; } case TypeTableEntryIdPointer: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Pointer"); ConstExprValue *fields = create_const_vals(4); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - // is_const: bool ensure_field_index(result->type, "is_const", 0); fields[0].special = ConstValSpecialStatic; @@ -15959,175 +15877,94 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p fields[2].special = ConstValSpecialStatic; fields[2].type = ira->codegen->builtin_types.entry_u32; bigint_init_unsigned(&fields[2].data.x_bigint, type_entry->data.pointer.alignment); - // child: &TypeInfo + // child: type ensure_field_index(result->type, "child", 3); - - TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); - fields[3].special = ConstValSpecialStatic; - fields[3].type = get_pointer_to_type(ira->codegen, type_info_type, false); - fields[3].data.x_ptr.special = ConstPtrSpecialRef; - fields[3].data.x_ptr.mut = ConstPtrMutComptimeVar; - ConstExprValue *union_val = create_const_vals(1); - union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type; - bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.pointer.child_type->id)); + fields[3].type = ira->codegen->builtin_types.entry_type; + fields[3].data.x_type = type_entry->data.pointer.child_type; - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, - type_entry->data.pointer.child_type); - - fields[3].data.x_ptr.data.ref.pointee = union_val; - - return result; + break; } case TypeTableEntryIdArray: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Array"); ConstExprValue *fields = create_const_vals(2); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - // len: usize ensure_field_index(result->type, "len", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_usize; bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); - // child: &TypeInfo + // child: type ensure_field_index(result->type, "child", 1); - TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); - fields[1].special = ConstValSpecialStatic; - fields[1].type = get_pointer_to_type(ira->codegen, type_info_type, false); - fields[1].data.x_ptr.special = ConstPtrSpecialRef; - fields[1].data.x_ptr.mut = ConstPtrMutComptimeVar; - ConstExprValue *union_val = create_const_vals(1); - union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type; - bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.array.child_type->id)); + fields[1].type = ira->codegen->builtin_types.entry_type; + fields[1].data.x_type = type_entry->data.array.child_type; - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, - type_entry->data.array.child_type); - - fields[1].data.x_ptr.data.ref.pointee = union_val; - - return result; + break; } case TypeTableEntryIdMaybe: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Nullable"); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - - // child: &TypeInfo + // child: type ensure_field_index(result->type, "child", 0); - - TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); - fields[0].special = ConstValSpecialStatic; - fields[0].type = get_pointer_to_type(ira->codegen, type_info_type, false); - fields[0].data.x_ptr.special = ConstPtrSpecialRef; - fields[0].data.x_ptr.mut = ConstPtrMutComptimeVar; - ConstExprValue *union_val = create_const_vals(1); - union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type; - bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.maybe.child_type->id)); - - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, - type_entry->data.maybe.child_type); - - fields[0].data.x_ptr.data.ref.pointee = union_val; + fields[0].type = ira->codegen->builtin_types.entry_type; + fields[0].data.x_type = type_entry->data.maybe.child_type; - return result; + break; } case TypeTableEntryIdPromise: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Promise"); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - - // child: ?&TypeInfo + // @TODO ?type instead of using @typeOf(undefined) when we have no type. + // child: type ensure_field_index(result->type, "child", 0); - - TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); - TypeTableEntry *type_info_ptr_type = get_pointer_to_type(ira->codegen, type_info_type, false); - fields[0].special = ConstValSpecialStatic; - fields[0].type = get_maybe_type(ira->codegen, type_info_ptr_type); + fields[0].type = ira->codegen->builtin_types.entry_type; if (type_entry->data.promise.result_type == nullptr) - { - fields[0].data.x_maybe = nullptr; - } + fields[0].data.x_type = ira->codegen->builtin_types.entry_undef; else - { - ConstExprValue *maybe_value = create_const_vals(1); - maybe_value->special = ConstValSpecialStatic; - maybe_value->type = type_info_ptr_type; - - maybe_value->data.x_ptr.special = ConstPtrSpecialRef; - maybe_value->data.x_ptr.mut = ConstPtrMutComptimeVar; - - ConstExprValue *union_val = create_const_vals(1); - union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type; - bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.promise.result_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, - type_entry->data.promise.result_type); - - maybe_value->data.x_ptr.data.ref.pointee = union_val; - fields[0].data.x_maybe = maybe_value; - } + fields[0].data.x_type = type_entry->data.promise.result_type; - return result; + break; } case TypeTableEntryIdEnum: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Enum"); ConstExprValue *fields = create_const_vals(4); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - // layout: ContainerLayout ensure_field_index(result->type, "layout", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ir_type_info_get_type(ira, "ContainerLayout"); bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.enumeration.layout); - // tag_type: &TypeInfo.Int + // tag_type: type ensure_field_index(result->type, "tag_type", 1); - - TypeTableEntry *type_info_int_type = ir_type_info_get_type(ira, "Int"); - fields[1].special = ConstValSpecialStatic; - fields[1].type = get_pointer_to_type(ira->codegen, type_info_int_type, false); - fields[1].data.x_ptr.special = ConstPtrSpecialRef; - fields[1].data.x_ptr.mut = ConstPtrMutComptimeVar; - - ConstExprValue *tag_type_info_struct = ir_make_type_info_value(ira, &fields[1], -1, - type_entry->data.enumeration.tag_int_type); - assert(tag_type_info_struct->type == type_info_int_type); - fields[1].data.x_ptr.data.ref.pointee = tag_type_info_struct; + fields[1].type = ira->codegen->builtin_types.entry_type; + fields[1].data.x_type = type_entry->data.enumeration.tag_int_type; // fields: []TypeInfo.EnumField ensure_field_index(result->type, "fields", 2); @@ -16172,20 +16009,17 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p // @TODO // methods: []TypeInfo.Method - return result; + break; } case TypeTableEntryIdErrorSet: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "ErrorSet"); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - // errors: []TypeInfo.Error ensure_field_index(result->type, "errors", 0); @@ -16229,13 +16063,13 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p error_val->data.x_struct.parent.data.p_array.elem_index = error_index; } - return result; + break; } - default: - zig_unreachable(); } - zig_unreachable(); + assert(result != nullptr); + ira->codegen->type_info_cache.put(type_entry, result); + return result; } static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, @@ -16251,7 +16085,16 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = result_type; bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); - out_val->data.x_union.payload = ir_make_type_info_value(ira, out_val, -1, type_entry); + + ConstExprValue *payload = ir_make_type_info_value(ira, type_entry); + out_val->data.x_union.payload = payload; + + if (payload != nullptr) + { + assert(payload->type->id == TypeTableEntryIdStruct); + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = out_val; + } return result_type; } -- cgit v1.2.3 From 884e32d5c3aac470cdcd9b20e15554d6193f5501 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 19:56:34 +0300 Subject: Added ErrorUnion, Union TypeInfo generation --- src/codegen.cpp | 2 +- src/ir.cpp | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 127 insertions(+), 15 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index c8bb502718..77794c2ab9 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6426,7 +6426,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " };\n" "\n" " pub const ErrorUnion = struct {\n" - " error_set: &ErrorSet,\n" + " error_set: type,\n" " payload: type,\n" " };\n" "\n" diff --git a/src/ir.cpp b/src/ir.cpp index fdf1a0c8ab..f91d1c3edf 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15787,6 +15787,27 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); + const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field) { + TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField"); + // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) + // ensure_field_index(type_info_enum_field_type, "name", 0); + // ensure_field_index(type_info_enum_field_type, "value", 1); + + enum_field_val->special = ConstValSpecialStatic; + enum_field_val->type = type_info_enum_field_type; + + ConstExprValue *inner_fields = create_const_vals(2); + inner_fields[1].special = ConstValSpecialStatic; + inner_fields[1].type = ira->codegen->builtin_types.entry_usize; + + ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name); + init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true); + + bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value); + + enum_field_val->data.x_struct.fields = inner_fields; + }; + ConstExprValue *result = nullptr; switch (type_entry->id) { @@ -15988,20 +16009,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t { TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index]; - - enum_field_val->special = ConstValSpecialStatic; - enum_field_val->type = type_info_enum_field_type; - - ConstExprValue *inner_fields = create_const_vals(2); - inner_fields[1].special = ConstValSpecialStatic; - inner_fields[1].type = ira->codegen->builtin_types.entry_usize; - - ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name); - init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true); - - bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value); - - enum_field_val->data.x_struct.fields = inner_fields; + make_enum_field_val(enum_field_val, enum_field); enum_field_val->data.x_struct.parent.id = ConstParentIdArray; enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; @@ -16063,6 +16071,110 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t error_val->data.x_struct.parent.data.p_array.elem_index = error_index; } + break; + } + case TypeTableEntryIdErrorUnion: + { + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "ErrorUnion"); + + ConstExprValue *fields = create_const_vals(2); + result->data.x_struct.fields = fields; + + // error_set: type + ensure_field_index(result->type, "error_set", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_type; + fields[0].data.x_type = type_entry->data.error_union.err_set_type; + + // payload: type + ensure_field_index(result->type, "payload", 1); + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_type; + fields[1].data.x_type = type_entry->data.error_union.payload_type; + + break; + } + case TypeTableEntryIdUnion: + { + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Union"); + + ConstExprValue *fields = create_const_vals(4); + result->data.x_struct.fields = fields; + + // layout: ContainerLayout + ensure_field_index(result->type, "layout", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ir_type_info_get_type(ira, "ContainerLayout"); + bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.unionation.layout); + // tag_type: type + ensure_field_index(result->type, "tag_type", 1); + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_type; + // @TODO ?type instead of using @typeOf(undefined) when we have no type. + if (type_entry->data.unionation.tag_type == nullptr) + fields[1].data.x_type = ira->codegen->builtin_types.entry_undef; + else + fields[1].data.x_type = type_entry->data.unionation.tag_type; + + fields[1].data.x_type = type_entry->data.unionation.tag_type; + // fields: []TypeInfo.UnionField + ensure_field_index(result->type, "fields", 2); + + TypeTableEntry *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField"); + // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) + // ensure_field_index(type_info_union_field_type, "name", 0); + // ensure_field_index(type_info_union_field_type, "enum_field", 1); + // ensure_field_index(type_info_union_field_type, "field_type", 2); + + uint32_t union_field_count = type_entry->data.unionation.src_field_count; + + ConstExprValue *union_field_array = create_const_vals(1); + union_field_array->special = ConstValSpecialStatic; + union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count); + union_field_array->data.x_array.special = ConstArraySpecialNone; + union_field_array->data.x_array.s_none.parent.id = ConstParentIdNone; + union_field_array->data.x_array.s_none.elements = create_const_vals(union_field_count); + + init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false); + + for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) + { + TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index]; + ConstExprValue *union_field_val = &union_field_array->data.x_array.s_none.elements[union_field_index]; + + union_field_val->special = ConstValSpecialStatic; + union_field_val->type = type_info_union_field_type; + + ConstExprValue *inner_fields = create_const_vals(3); + inner_fields[1].special = ConstValSpecialStatic; + make_enum_field_val(&inner_fields[1], union_field->enum_field); + inner_fields[1].data.x_struct.parent.id = ConstParentIdStruct; + inner_fields[1].data.x_struct.parent.data.p_struct.struct_val = union_field_val; + inner_fields[1].data.x_struct.parent.data.p_struct.field_index = 1; + + inner_fields[2].special = ConstValSpecialStatic; + inner_fields[2].type = ira->codegen->builtin_types.entry_type; + inner_fields[2].data.x_type = union_field->type_entry; + + ConstExprValue *name = create_const_str_lit(ira->codegen, union_field->name); + init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true); + + + union_field_val->data.x_struct.fields = inner_fields; + union_field_val->data.x_struct.parent.id = ConstParentIdArray; + union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array; + union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; + + // @TODO Check if TypeUnionField::enum_field == nullptr when tag_type == nullptr + // If it is, make enum_field: ?EnumField, set it when available, done. + } + + // @TODO + // methods: []TypeInfo.Method break; } } -- cgit v1.2.3 From 9041d0d37e59e3f07d568f93bd680cc0065d1cd2 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Fri, 27 Apr 2018 02:05:24 +0300 Subject: Fixed enum tag type detection in TypeInfo generation. --- src/codegen.cpp | 8 +++++++- src/ir.cpp | 56 +++++++++++++++++++++++--------------------------------- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 77794c2ab9..b6236197cc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6349,6 +6349,12 @@ static void define_builtin_compile_vars(CodeGen *g) { } { // @TODO Add Namespace info. + // @TODO Methods -> definitions + // @TODO Includes type definitions (name + type bound) + functions + const variable definitions (+ type of variable) + // @TODO Type definitions are defined as variable definitions of type 'type' + // @TODO This should give us everything available. + // @TODO An alternative is exposing the value of every variable definition, check out if it's possible and wether we want that. + // @TODO I don't think so, @field gives it to us for free. buf_appendf(contents, "pub const TypeInfo = union(TypeId) {\n" " Type: void,\n" @@ -6453,7 +6459,7 @@ static void define_builtin_compile_vars(CodeGen *g) { "\n" " pub const UnionField = struct {\n" " name: []const u8,\n" - " enum_field: EnumField,\n" + " enum_field: ?EnumField,\n" " field_type: type,\n" " };\n" "\n" diff --git a/src/ir.cpp b/src/ir.cpp index f91d1c3edf..a6012b0a91 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15787,8 +15787,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); - const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field) { - TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField"); + const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field, + TypeTableEntry *type_info_enum_field_type) { // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) // ensure_field_index(type_info_enum_field_type, "name", 0); // ensure_field_index(type_info_enum_field_type, "value", 1); @@ -15990,10 +15990,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ensure_field_index(result->type, "fields", 2); TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField"); - // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) - // ensure_field_index(type_info_enum_field_type, "name", 0); - // ensure_field_index(type_info_enum_field_type, "value", 1); - uint32_t enum_field_count = type_entry->data.enumeration.src_field_count; ConstExprValue *enum_field_array = create_const_vals(1); @@ -16009,14 +16005,13 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t { TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index]; - make_enum_field_val(enum_field_val, enum_field); + make_enum_field_val(enum_field_val, enum_field, type_info_enum_field_type); enum_field_val->data.x_struct.parent.id = ConstParentIdArray; enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; } - // @TODO - // methods: []TypeInfo.Method + // @TODO Definitions break; } case TypeTableEntryIdErrorSet: @@ -16032,10 +16027,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ensure_field_index(result->type, "errors", 0); TypeTableEntry *type_info_error_type = ir_type_info_get_type(ira, "Error"); - // @TODO Same as above in Enum TypeInfo generation. - // ensure_field_index(type_info_error_type, "name", 0); - // ensure_field_index(type_info_error_type, "value", 1); - uint32_t error_count = type_entry->data.error_set.err_count; ConstExprValue *error_array = create_const_vals(1); error_array->special = ConstValSpecialStatic; @@ -16115,21 +16106,18 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_type; // @TODO ?type instead of using @typeOf(undefined) when we have no type. - if (type_entry->data.unionation.tag_type == nullptr) - fields[1].data.x_type = ira->codegen->builtin_types.entry_undef; - else + AstNode *union_decl_node = type_entry->data.unionation.decl_node; + if (union_decl_node->data.container_decl.auto_enum || + union_decl_node->data.container_decl.init_arg_expr != nullptr) + { fields[1].data.x_type = type_entry->data.unionation.tag_type; - - fields[1].data.x_type = type_entry->data.unionation.tag_type; + } + else + fields[1].data.x_type = ira->codegen->builtin_types.entry_undef; // fields: []TypeInfo.UnionField ensure_field_index(result->type, "fields", 2); TypeTableEntry *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField"); - // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) - // ensure_field_index(type_info_union_field_type, "name", 0); - // ensure_field_index(type_info_union_field_type, "enum_field", 1); - // ensure_field_index(type_info_union_field_type, "field_type", 2); - uint32_t union_field_count = type_entry->data.unionation.src_field_count; ConstExprValue *union_field_array = create_const_vals(1); @@ -16141,6 +16129,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false); + TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField"); + for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) { TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index]; @@ -16151,10 +16141,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ConstExprValue *inner_fields = create_const_vals(3); inner_fields[1].special = ConstValSpecialStatic; - make_enum_field_val(&inner_fields[1], union_field->enum_field); - inner_fields[1].data.x_struct.parent.id = ConstParentIdStruct; - inner_fields[1].data.x_struct.parent.data.p_struct.struct_val = union_field_val; - inner_fields[1].data.x_struct.parent.data.p_struct.field_index = 1; + inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type); + + if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) + inner_fields[1].data.x_maybe = nullptr; + else + { + inner_fields[1].data.x_maybe = create_const_vals(1); + make_enum_field_val(inner_fields[1].data.x_maybe, union_field->enum_field, type_info_enum_field_type); + } inner_fields[2].special = ConstValSpecialStatic; inner_fields[2].type = ira->codegen->builtin_types.entry_type; @@ -16163,18 +16158,13 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ConstExprValue *name = create_const_str_lit(ira->codegen, union_field->name); init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true); - union_field_val->data.x_struct.fields = inner_fields; union_field_val->data.x_struct.parent.id = ConstParentIdArray; union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array; union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; - - // @TODO Check if TypeUnionField::enum_field == nullptr when tag_type == nullptr - // If it is, make enum_field: ?EnumField, set it when available, done. } - // @TODO - // methods: []TypeInfo.Method + // @TODO Definitions break; } } -- cgit v1.2.3 From a2dadbc206f80dcf39b42f3be97a8d74795d0381 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Fri, 27 Apr 2018 02:52:09 +0300 Subject: Added struct TypeInfo generation. --- src/codegen.cpp | 2 +- src/ir.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index b6236197cc..873cf18072 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6417,7 +6417,7 @@ static void define_builtin_compile_vars(CodeGen *g) { "\n" " pub const StructField = struct {\n" " name: []const u8,\n" - " offset: usize,\n" + " offset: ?usize,\n" " field_type: type,\n" " };\n" "\n" diff --git a/src/ir.cpp b/src/ir.cpp index a6012b0a91..f167d76ca1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15787,6 +15787,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); + ensure_complete_type(ira->codegen, type_entry); + const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field, TypeTableEntry *type_info_enum_field_type) { // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) @@ -16164,6 +16166,72 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; } + // @TODO Definitions + break; + } + case TypeTableEntryIdStruct: + { + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Struct"); + + ConstExprValue *fields = create_const_vals(3); + result->data.x_struct.fields = fields; + + // layout: ContainerLayout + ensure_field_index(result->type, "layout", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ir_type_info_get_type(ira, "ContainerLayout"); + bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.structure.layout); + // fields: []TypeInfo.StructField + ensure_field_index(result->type, "fields", 1); + + TypeTableEntry *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField"); + uint32_t struct_field_count = type_entry->data.structure.src_field_count; + + ConstExprValue *struct_field_array = create_const_vals(1); + struct_field_array->special = ConstValSpecialStatic; + struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count); + struct_field_array->data.x_array.special = ConstArraySpecialNone; + struct_field_array->data.x_array.s_none.parent.id = ConstParentIdNone; + struct_field_array->data.x_array.s_none.elements = create_const_vals(struct_field_count); + + init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false); + + for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) + { + TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index]; + ConstExprValue *struct_field_val = &struct_field_array->data.x_array.s_none.elements[struct_field_index]; + + struct_field_val->special = ConstValSpecialStatic; + struct_field_val->type = type_info_struct_field_type; + + ConstExprValue *inner_fields = create_const_vals(3); + inner_fields[1].special = ConstValSpecialStatic; + inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize); + + if (!type_has_bits(struct_field->type_entry)) + inner_fields[1].data.x_maybe = nullptr; + else + { + size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index); + inner_fields[1].data.x_maybe = create_const_vals(1); + inner_fields[1].data.x_maybe->type = ira->codegen->builtin_types.entry_usize; + bigint_init_unsigned(&inner_fields[1].data.x_maybe->data.x_bigint, byte_offset); + } + + inner_fields[2].special = ConstValSpecialStatic; + inner_fields[2].type = ira->codegen->builtin_types.entry_type; + inner_fields[2].data.x_type = struct_field->type_entry; + + ConstExprValue *name = create_const_str_lit(ira->codegen, struct_field->name); + init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(struct_field->name), true); + + struct_field_val->data.x_struct.fields = inner_fields; + struct_field_val->data.x_struct.parent.id = ConstParentIdArray; + struct_field_val->data.x_struct.parent.data.p_array.array_val = struct_field_array; + struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index; + } // @TODO Definitions break; } -- cgit v1.2.3 From 8f703f919f13e23b2c09f577a446aabb7799e27c Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Fri, 27 Apr 2018 04:29:50 +0300 Subject: Added Fn TypeInfo generation. --- src/codegen.cpp | 7 +++-- src/ir.cpp | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 873cf18072..ee85d7d3c4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6480,16 +6480,17 @@ static void define_builtin_compile_vars(CodeGen *g) { " };\n" "\n" " pub const FnArg = struct {\n" - " is_comptime: bool,\n" - " name: []const u8,\n" + " is_generic: bool,\n" + " is_noalias: bool,\n" " arg_type: type,\n" " };\n" "\n" " pub const Fn = struct {\n" " calling_convention: CallingConvention,\n" " is_generic: bool,\n" - " is_varargs: bool,\n" + " is_var_args: bool,\n" " return_type: type,\n" + " async_allocator_type: type,\n" " args: []FnArg,\n" " };\n" "\n" diff --git a/src/ir.cpp b/src/ir.cpp index f167d76ca1..a7076a3243 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16233,6 +16233,98 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index; } // @TODO Definitions + break; + } + case TypeTableEntryIdFn: + { + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Fn"); + + ConstExprValue *fields = create_const_vals(5); + result->data.x_struct.fields = fields; + + // @TODO Fix type = undefined with ?type + + // calling_convention: TypeInfo.CallingConvention + ensure_field_index(result->type, "calling_convention", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ir_type_info_get_type(ira, "CallingConvention"); + bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); + // is_generic: bool + ensure_field_index(result->type, "is_generic", 1); + bool is_generic = type_entry->data.fn.is_generic; + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_bool; + fields[1].data.x_bool = is_generic; + // is_varargs: bool + ensure_field_index(result->type, "is_var_args", 2); + bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args; + fields[2].special = ConstValSpecialStatic; + fields[2].type = ira->codegen->builtin_types.entry_bool; + fields[2].data.x_bool = type_entry->data.fn.fn_type_id.is_var_args; + // return_type: type + ensure_field_index(result->type, "return_type", 3); + fields[3].special = ConstValSpecialStatic; + fields[3].type = ira->codegen->builtin_types.entry_type; + if (type_entry->data.fn.fn_type_id.return_type == nullptr) + fields[3].data.x_type = ira->codegen->builtin_types.entry_undef; + else + fields[3].data.x_type = type_entry->data.fn.fn_type_id.return_type; + // async_allocator_type: type + ensure_field_index(result->type, "async_allocator_type", 4); + fields[4].special = ConstValSpecialStatic; + fields[4].type = ira->codegen->builtin_types.entry_type; + if (type_entry->data.fn.fn_type_id.async_allocator_type == nullptr) + fields[4].data.x_type = ira->codegen->builtin_types.entry_undef; + else + fields[4].data.x_type = type_entry->data.fn.fn_type_id.async_allocator_type; + // args: []TypeInfo.FnArg + TypeTableEntry *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg"); + size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count - + (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC); + + ConstExprValue *fn_arg_array = create_const_vals(1); + fn_arg_array->special = ConstValSpecialStatic; + fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count); + fn_arg_array->data.x_array.special = ConstArraySpecialNone; + fn_arg_array->data.x_array.s_none.parent.id = ConstParentIdNone; + fn_arg_array->data.x_array.s_none.elements = create_const_vals(fn_arg_count); + + init_const_slice(ira->codegen, &fields[5], fn_arg_array, 0, fn_arg_count, false); + + for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) + { + FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index]; + ConstExprValue *fn_arg_val = &fn_arg_array->data.x_array.s_none.elements[fn_arg_index]; + + fn_arg_val->special = ConstValSpecialStatic; + fn_arg_val->type = type_info_fn_arg_type; + + bool arg_is_generic = fn_param_info->type == nullptr; + if (arg_is_generic) assert(is_generic); + + ConstExprValue *inner_fields = create_const_vals(3); + inner_fields[0].special = ConstValSpecialStatic; + inner_fields[0].type = ira->codegen->builtin_types.entry_bool; + inner_fields[0].data.x_bool = arg_is_generic; + inner_fields[1].special = ConstValSpecialStatic; + inner_fields[1].type = ira->codegen->builtin_types.entry_bool; + inner_fields[1].data.x_bool = fn_param_info->is_noalias; + inner_fields[2].special = ConstValSpecialStatic; + inner_fields[2].type = ira->codegen->builtin_types.entry_type; + + if (arg_is_generic) + inner_fields[2].data.x_type = ira->codegen->builtin_types.entry_undef; + else + inner_fields[2].data.x_type = fn_param_info->type; + + fn_arg_val->data.x_struct.fields = inner_fields; + fn_arg_val->data.x_struct.parent.id = ConstParentIdArray; + fn_arg_val->data.x_struct.parent.data.p_array.array_val = fn_arg_array; + fn_arg_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; + } + break; } } -- cgit v1.2.3 From ea2596280fc2c78f71b08921db3a4c9826eb93e0 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Fri, 27 Apr 2018 05:10:20 +0300 Subject: Added BoundFn TypeInfo generation. --- src/codegen.cpp | 7 +------ src/ir.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index ee85d7d3c4..fc872e1908 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6378,7 +6378,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " Fn: Fn,\n" " Namespace: void,\n" " Block: void,\n" - " BoundFn: BoundFn,\n" + " BoundFn: Fn,\n" " ArgTuple: void,\n" " Opaque: void,\n" " Promise: Promise,\n" @@ -6494,11 +6494,6 @@ static void define_builtin_compile_vars(CodeGen *g) { " args: []FnArg,\n" " };\n" "\n" - " pub const BoundFn = struct {\n" - " bound_type: type,\n" - " fn_info: Fn,\n" - " };\n" - "\n" " pub const Promise = struct {\n" " child: type,\n" " };\n" diff --git a/src/ir.cpp b/src/ir.cpp index a7076a3243..c2da83886a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16241,7 +16241,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Fn"); - ConstExprValue *fields = create_const_vals(5); + ConstExprValue *fields = create_const_vals(6); result->data.x_struct.fields = fields; // @TODO Fix type = undefined with ?type @@ -16325,6 +16325,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t fn_arg_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; } + break; + } + case TypeTableEntryIdBoundFn: + { + // @TODO figure out memory corruption error. + TypeTableEntry *fn_type = type_entry->data.bound_fn.fn_type; + assert(fn_type->id == TypeTableEntryIdFn); + result = ir_make_type_info_value(ira, fn_type); + break; } } -- cgit v1.2.3 From 3178528335cb5efbf237cecb9ea9eb3bfa31b21f Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 14:05:08 +0200 Subject: Removed zero sized error set optimization fixes #762 fixes #818 --- src/ir.cpp | 14 ++++---------- test/cases/error.zig | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 86c77758b2..ec7f41d748 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6166,16 +6166,10 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A buf_init_from_buf(&err_set_type->name, type_name); err_set_type->is_copyable = true; err_set_type->data.error_set.err_count = err_count; - - if (err_count == 0) { - err_set_type->zero_bits = true; - err_set_type->di_type = irb->codegen->builtin_types.entry_void->di_type; - } else { - err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref; - err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type; - irb->codegen->error_di_types.append(&err_set_type->di_type); - err_set_type->data.error_set.errors = allocate(err_count); - } + err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref; + err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type; + irb->codegen->error_di_types.append(&err_set_type->di_type); + err_set_type->data.error_set.errors = allocate(err_count); ErrorTableEntry **errors = allocate(irb->codegen->errors_by_index.length + err_count); diff --git a/test/cases/error.zig b/test/cases/error.zig index e64bf02c91..c64c835fc4 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -175,3 +175,30 @@ fn baz_1() !i32 { fn quux_1() !i32 { return error.C; } + + +test "error: fn returning empty error set can be passed as fn returning any error" { + entry(); + comptime entry(); +} + +fn entry() void { + foo2(bar2); +} + +fn foo2(f: fn()error!void) void { + const x = f(); +} + +fn bar2() (error{}!void) { } + + +test "error: Zero sized error set returned with value payload crash" { + _ = foo3(0); + _ = comptime foo3(0); +} + +const Error = error{}; +fn foo3(b: usize) Error!usize { + return b; +} -- cgit v1.2.3 From 61b01805968939669a29f7189f4ce7fab46ab2da Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Sat, 28 Apr 2018 17:01:19 +0300 Subject: Added definition TypeInfo generation, except for function definitions. --- src/codegen.cpp | 30 ++++++------ src/ir.cpp | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 154 insertions(+), 26 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index fc872e1908..be1d59c26a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6348,13 +6348,6 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "};\n\n"); } { - // @TODO Add Namespace info. - // @TODO Methods -> definitions - // @TODO Includes type definitions (name + type bound) + functions + const variable definitions (+ type of variable) - // @TODO Type definitions are defined as variable definitions of type 'type' - // @TODO This should give us everything available. - // @TODO An alternative is exposing the value of every variable definition, check out if it's possible and wether we want that. - // @TODO I don't think so, @field gives it to us for free. buf_appendf(contents, "pub const TypeInfo = union(TypeId) {\n" " Type: void,\n" @@ -6410,11 +6403,6 @@ static void define_builtin_compile_vars(CodeGen *g) { " Packed,\n" " };\n" "\n" - " pub const Method = struct {\n" - " name: []const u8,\n" - " fn_info: Fn,\n" - " };\n" - "\n" " pub const StructField = struct {\n" " name: []const u8,\n" " offset: ?usize,\n" @@ -6424,7 +6412,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const Struct = struct {\n" " layout: ContainerLayout,\n" " fields: []StructField,\n" - " methods: []Method,\n" + " defs: []Definition,\n" " };\n" "\n" " pub const Nullable = struct {\n" @@ -6454,7 +6442,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " layout: ContainerLayout,\n" " tag_type: type,\n" " fields: []EnumField,\n" - " methods: []Method,\n" + " defs: []Definition,\n" " };\n" "\n" " pub const UnionField = struct {\n" @@ -6467,7 +6455,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " layout: ContainerLayout,\n" " tag_type: type,\n" " fields: []UnionField,\n" - " methods: []Method,\n" + " defs: []Definition,\n" " };\n" "\n" " pub const CallingConvention = enum {\n" @@ -6497,6 +6485,18 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const Promise = struct {\n" " child: type,\n" " };\n" + "\n" + " pub const Definition = struct {\n" + " name: []const u8,\n" + " is_pub: bool,\n" + " data: Data,\n" + "\n" + " const Data = union(enum) {\n" + " Type: type,\n" + " Var: type,\n" + " Fn: void,\n" + " };\n" + " };\n" "};\n\n"); assert(ContainerLayoutAuto == 0); assert(ContainerLayoutExtern == 1); diff --git a/src/ir.cpp b/src/ir.cpp index c2da83886a..d8da5b3172 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15748,7 +15748,7 @@ static void ensure_field_index(TypeTableEntry *type, const char *field_name, siz (buf_deinit(field_name_buf), true)); } -static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name) +static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, TypeTableEntry *root = nullptr) { static ConstExprValue *type_info_var = nullptr; static TypeTableEntry *type_info_type = nullptr; @@ -15761,10 +15761,14 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na assert(type_info_type->id == TypeTableEntryIdUnion); } - if (type_name == nullptr) + if (type_name == nullptr && root == nullptr) return type_info_type; + else if (type_name == nullptr) + return root; - ScopeDecls *type_info_scope = get_container_scope(type_info_type); + TypeTableEntry *root_type = (root == nullptr) ? type_info_type : root; + + ScopeDecls *type_info_scope = get_container_scope(root_type); assert(type_info_scope != nullptr); Buf field_name = BUF_INIT; @@ -15782,6 +15786,128 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na return var->value->data.x_type; } +static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) +{ + TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition"); + ensure_complete_type(ira->codegen, type_info_definition_type); + ensure_field_index(type_info_definition_type, "name", 0); + ensure_field_index(type_info_definition_type, "is_pub", 1); + ensure_field_index(type_info_definition_type, "data", 2); + + TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type); + ensure_complete_type(ira->codegen, type_info_definition_data_type); + + // Loop through our definitions once to figure out how many definitions we will generate info for. + auto decl_it = decls_scope->decl_table.entry_iterator(); + decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr; + int definition_count = 0; + + while ((curr_entry = decl_it.next()) != nullptr) + { + // Skip comptime blocks. + if (curr_entry->value->id != TldIdCompTime) + { + definition_count += 1; + } + } + + ConstExprValue *definition_array = create_const_vals(1); + definition_array->special = ConstValSpecialStatic; + definition_array->type = get_array_type(ira->codegen, type_info_definition_type, definition_count); + definition_array->data.x_array.special = ConstArraySpecialNone; + definition_array->data.x_array.s_none.parent.id = ConstParentIdNone; + definition_array->data.x_array.s_none.elements = create_const_vals(definition_count); + init_const_slice(ira->codegen, out_val, definition_array, 0, definition_count, false); + + // Loop through the definitions and generate info. + decl_it = decls_scope->decl_table.entry_iterator(); + curr_entry = nullptr; + int definition_index = 0; + while ((curr_entry = decl_it.next()) != nullptr) + { + // Skip comptime blocks + if (curr_entry->value->id == TldIdCompTime) + continue; + + ConstExprValue *definition_val = &definition_array->data.x_array.s_none.elements[definition_index]; + + definition_val->special = ConstValSpecialStatic; + definition_val->type = type_info_definition_type; + + ConstExprValue *inner_fields = create_const_vals(3); + ConstExprValue *name = create_const_str_lit(ira->codegen, curr_entry->key); + init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(curr_entry->key), true); + inner_fields[1].special = ConstValSpecialStatic; + inner_fields[1].type = ira->codegen->builtin_types.entry_bool; + inner_fields[1].data.x_bool = curr_entry->value->visib_mod == VisibModPub; + inner_fields[2].special = ConstValSpecialStatic; + inner_fields[2].type = type_info_definition_data_type; + inner_fields[2].data.x_union.parent.id = ConstParentIdStruct; + inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val; + inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1; + + switch (curr_entry->value->id) + { + case TldIdVar: + { + VariableTableEntry *var = ((TldVar *)curr_entry->value)->var; + ensure_complete_type(ira->codegen, var->value->type); + if (var->value->type->id == TypeTableEntryIdMetaType) + { + // We have a variable of type 'type', so it's actually a type definition. + // 0: Data.Type: type + bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); + inner_fields[2].data.x_union.payload = var->value; + } + else + { + // We have a variable of another type, so we store the type of the variable. + // 1: Data.Var: type + bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 1); + + ConstExprValue *payload = create_const_vals(1); + payload->type = ira->codegen->builtin_types.entry_type; + payload->data.x_type = var->value->type; + + inner_fields[2].data.x_union.payload = payload; + } + + break; + } + case TldIdFn: + { + // 2: Data.Fn: Data.FnDef + bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 2); + // @TODO Data.FnDef + inner_fields[2].data.x_union.payload = nullptr; + break; + } + case TldIdContainer: + { + TypeTableEntry *type_entry = ((TldContainer *)curr_entry->value)->type_entry; + ensure_complete_type(ira->codegen, type_entry); + // This is a type. + bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); + + ConstExprValue *payload = create_const_vals(1); + payload->type = ira->codegen->builtin_types.entry_type; + payload->data.x_type = type_entry; + + inner_fields[2].data.x_union.payload = payload; + + break; + } + default: + zig_unreachable(); + } + + definition_val->data.x_struct.fields = inner_fields; + definition_index++; + } + + assert(definition_index == definition_count); +} + static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) { assert(type_entry != nullptr); @@ -15791,10 +15917,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field, TypeTableEntry *type_info_enum_field_type) { - // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) - // ensure_field_index(type_info_enum_field_type, "name", 0); - // ensure_field_index(type_info_enum_field_type, "value", 1); - enum_field_val->special = ConstValSpecialStatic; enum_field_val->type = type_info_enum_field_type; @@ -16012,8 +16134,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; } + // defs: []TypeInfo.Definition + ensure_field_index(result->type, "defs", 3); + ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope); - // @TODO Definitions break; } case TypeTableEntryIdErrorSet: @@ -16165,8 +16289,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array; union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; } + // defs: []TypeInfo.Definition + ensure_field_index(result->type, "defs", 3); + ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope); - // @TODO Definitions break; } case TypeTableEntryIdStruct: @@ -16232,7 +16358,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t struct_field_val->data.x_struct.parent.data.p_array.array_val = struct_field_array; struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index; } - // @TODO Definitions + // defs: []TypeInfo.Definition + ensure_field_index(result->type, "defs", 2); + ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope); + break; } case TypeTableEntryIdFn: @@ -16329,7 +16458,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } case TypeTableEntryIdBoundFn: { - // @TODO figure out memory corruption error. TypeTableEntry *fn_type = type_entry->data.bound_fn.fn_type; assert(fn_type->id == TypeTableEntryIdFn); result = ir_make_type_info_value(ira, fn_type); -- cgit v1.2.3 From 2fc34eaa581cc31827e978fbd973bf36d2c647e2 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 16:27:31 +0200 Subject: Functions with infered error set can now return literals fixes #852 --- src/analyze.cpp | 1 - src/ir.cpp | 36 +++++++++++++++++++----------------- test/cases/error.zig | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index a598d7676e..11715220c7 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6131,4 +6131,3 @@ bool type_can_fail(TypeTableEntry *type_entry) { bool fn_type_can_fail(FnTypeId *fn_type_id) { return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync; } - diff --git a/src/ir.cpp b/src/ir.cpp index ec7f41d748..d8156b214e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8111,7 +8111,7 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t * *errors = reallocate(*errors, old_errors_count, *errors_count); } -static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) { +static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, TypeTableEntry *expected_type, IrInstruction **instructions, size_t instruction_count) { assert(instruction_count >= 1); IrInstruction *prev_inst = instructions[0]; if (type_is_invalid(prev_inst->value.type)) { @@ -8158,16 +8158,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } - if (prev_type->id == TypeTableEntryIdNullLit) { - prev_inst = cur_inst; - continue; - } - - if (cur_type->id == TypeTableEntryIdNullLit) { - any_are_null = true; - continue; - } - if (prev_type->id == TypeTableEntryIdErrorSet) { assert(err_set_type != nullptr); if (cur_type->id == TypeTableEntryIdErrorSet) { @@ -8427,6 +8417,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } + if (prev_type->id == TypeTableEntryIdNullLit) { + prev_inst = cur_inst; + continue; + } + + if (cur_type->id == TypeTableEntryIdNullLit) { + any_are_null = true; + continue; + } + if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) { continue; } @@ -8610,6 +8610,10 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else if (err_set_type != nullptr) { if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { return err_set_type; + } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) { + return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type); + } else if (expected_type != nullptr && expected_type->id == TypeTableEntryIdErrorUnion) { + return get_error_union_type(ira->codegen, err_set_type, expected_type->data.error_union.payload_type); } else { if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || prev_inst->value.type->id == TypeTableEntryIdNumLitFloat) @@ -8621,8 +8625,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod ir_add_error_node(ira, source_node, buf_sprintf("unable to make error union out of null literal")); return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) { - return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type); } else { return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type); } @@ -10645,7 +10647,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp } IrInstruction *instructions[] = {op1, op2}; - TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, instructions, 2); + TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2); if (type_is_invalid(resolved_type)) return resolved_type; type_ensure_zero_bits_known(ira->codegen, resolved_type); @@ -11035,7 +11037,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp IrInstruction *op1 = bin_op_instruction->op1->other; IrInstruction *op2 = bin_op_instruction->op2->other; IrInstruction *instructions[] = {op1, op2}; - TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2); + TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, nullptr, instructions, 2); if (type_is_invalid(resolved_type)) return resolved_type; IrBinOp op_id = bin_op_instruction->op_id; @@ -13004,7 +13006,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP return first_value->value.type; } - TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, + TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, nullptr, new_incoming_values.items, new_incoming_values.length); if (type_is_invalid(resolved_type)) return resolved_type; @@ -18696,7 +18698,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl } else if (ira->src_implicit_return_type_list.length == 0) { return codegen->builtin_types.entry_unreachable; } else { - return ir_resolve_peer_types(ira, expected_type_source_node, ira->src_implicit_return_type_list.items, + return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, ira->src_implicit_return_type_list.length); } } diff --git a/test/cases/error.zig b/test/cases/error.zig index c64c835fc4..2a1433df5b 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -202,3 +202,42 @@ const Error = error{}; fn foo3(b: usize) Error!usize { return b; } + + +test "error: Infer error set from literals" { + _ = nullLiteral("n") catch |err| handleErrors(err); + _ = floatLiteral("n") catch |err| handleErrors(err); + _ = intLiteral("n") catch |err| handleErrors(err); + _ = comptime nullLiteral("n") catch |err| handleErrors(err); + _ = comptime floatLiteral("n") catch |err| handleErrors(err); + _ = comptime intLiteral("n") catch |err| handleErrors(err); +} + +fn handleErrors(err: var) noreturn { + switch (err) { + error.T => {} + } + + unreachable; +} + +fn nullLiteral(str: []const u8) !?i64 { + if (str[0] == 'n') + return null; + + return error.T; +} + +fn floatLiteral(str: []const u8) !?f64 { + if (str[0] == 'n') + return 1.0; + + return error.T; +} + +fn intLiteral(str: []const u8) !?i64 { + if (str[0] == 'n') + return 1; + + return error.T; +} -- cgit v1.2.3 From fba0347ec43fb5c06b5ac9bec541b740d95194fe Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 17:17:48 +0200 Subject: .ReturnType and @ArgType now emits errors on unresolved types related: #846 --- src/ir.cpp | 19 +++++++++++++++++++ test/compile_errors.zig | 17 +++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index d8156b214e..641b8fc30c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13859,6 +13859,15 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru } } else if (child_type->id == TypeTableEntryIdFn) { if (buf_eql_str(field_name, "ReturnType")) { + if (child_type->data.fn.fn_type_id.return_type == nullptr) { + // Return type can only ever be null, if the function is generic + assert(child_type->data.fn.is_generic); + + ir_add_error(ira, &field_ptr_instruction->base, + buf_sprintf("ReturnType has not been resolved because '%s' is generic", buf_ptr(&child_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, @@ -17860,6 +17869,16 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = fn_type_id->param_info[arg_index].type; + if (out_val->data.x_type == nullptr) { + // Args are only unresolved if our function is generic. + assert(fn_type->data.fn.is_generic); + + ir_add_error(ira, arg_index_inst, + buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_usize " because '%s' is generic", + arg_index, buf_ptr(&fn_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + return ira->codegen->builtin_types.entry_type; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f8febc27b8..52e063eb39 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3209,4 +3209,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset"); + + cases.add("getting return type of generic function", + \\fn generic(a: var) void {} + \\comptime { + \\ _ = @typeOf(generic).ReturnType; + \\} + , + ".tmp_source.zig:3:25: error: ReturnType has not been resolved because 'fn(var)var' is generic"); + + cases.add("getting @ArgType of generic function", + \\fn generic(a: var) void {} + \\comptime { + \\ _ = @ArgType(@typeOf(generic), 0); + \\} + , + ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic"); + } -- cgit v1.2.3 From 9ba400673d798ea6f0842e4c207039c9faffb27e Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Sat, 28 Apr 2018 18:38:38 +0300 Subject: Generating TypeInfo's now forces definitions to be resolved. --- src/ir.cpp | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index d8da5b3172..6a3b4953c4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15804,9 +15804,26 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop while ((curr_entry = decl_it.next()) != nullptr) { - // Skip comptime blocks. + // If the definition is unresolved, force it to be resolved again. + if (curr_entry->value->resolution == TldResolutionUnresolved) + { + resolve_top_level_decl(ira->codegen, curr_entry->value, false, curr_entry->value->source_node); + if (curr_entry->value->resolution != TldResolutionOk) + { + return; + } + } + + // Skip comptime blocks and test functions. if (curr_entry->value->id != TldIdCompTime) { + if (curr_entry->value->id == TldIdFn) + { + FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; + if (fn_entry->is_test) + continue; + } + definition_count += 1; } } @@ -15825,9 +15842,15 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop int definition_index = 0; while ((curr_entry = decl_it.next()) != nullptr) { - // Skip comptime blocks + // Skip comptime blocks and test functions. if (curr_entry->value->id == TldIdCompTime) continue; + else if (curr_entry->value->id == TldIdFn) + { + FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; + if (fn_entry->is_test) + continue; + } ConstExprValue *definition_val = &definition_array->data.x_array.s_none.elements[definition_index]; @@ -15878,7 +15901,10 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop { // 2: Data.Fn: Data.FnDef bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 2); - // @TODO Data.FnDef + + FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; + assert(!fn_entry->is_test); + inner_fields[2].data.x_union.payload = nullptr; break; } -- cgit v1.2.3 From 341f8c1e8680aa3cfbeba6833f85df00355a95ef Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 17:57:47 +0200 Subject: Fixed wrong formatting for arg_index when reporting @ArgType error --- src/ir.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 641b8fc30c..4bf8240472 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -17874,8 +17874,8 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc assert(fn_type->data.fn.is_generic); ir_add_error(ira, arg_index_inst, - buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_usize " because '%s' is generic", - arg_index, buf_ptr(&fn_type->name))); + buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic", + arg_index, buf_ptr(&fn_type->name))); return ira->codegen->builtin_types.entry_invalid; } -- cgit v1.2.3 From af73462da46611ea9293f283ce8a6920ad73b10f Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Sat, 28 Apr 2018 19:57:59 +0300 Subject: Started work on function definition TypeInfo generation. --- src/codegen.cpp | 18 +++++++++++++++++- src/ir.cpp | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index be1d59c26a..414a34d5cb 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6494,7 +6494,19 @@ static void define_builtin_compile_vars(CodeGen *g) { " const Data = union(enum) {\n" " Type: type,\n" " Var: type,\n" - " Fn: void,\n" + " Fn: FnDef,\n" + "\n" + " const FnDef = struct {\n" + " fn_type: type,\n" + " inline_type: Inline,\n" + " calling_convention: CallingConvention,\n" + "\n" + " const Inline = enum {\n" + " Auto,\n" + " Always,\n" + " Never,\n" + " };\n" + " };\n" " };\n" " };\n" "};\n\n"); @@ -6508,6 +6520,10 @@ static void define_builtin_compile_vars(CodeGen *g) { assert(CallingConventionNaked == 3); assert(CallingConventionStdcall == 4); assert(CallingConventionAsync == 5); + + assert(FnInlineAuto == 0); + assert(FnInlineAlways == 1); + assert(FnInlineNever == 2); } { buf_appendf(contents, diff --git a/src/ir.cpp b/src/ir.cpp index 6a3b4953c4..8fadc7f7a3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15797,6 +15797,12 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type); ensure_complete_type(ira->codegen, type_info_definition_data_type); + TypeTableEntry *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type); + ensure_complete_type(ira->codegen, type_info_fn_def_type); + + TypeTableEntry *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type); + ensure_complete_type(ira->codegen, type_info_fn_def_inline_type); + // Loop through our definitions once to figure out how many definitions we will generate info for. auto decl_it = decls_scope->decl_table.entry_iterator(); decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr; @@ -15905,7 +15911,35 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; assert(!fn_entry->is_test); - inner_fields[2].data.x_union.payload = nullptr; + AstNodeFnProto *fn_node = (AstNodeFnProto *)(fn_entry->proto_node); + + ConstExprValue *fn_def_val = create_const_vals(1); + fn_def_val->special = ConstValSpecialStatic; + fn_def_val->type = type_info_fn_def_type; + fn_def_val->data.x_struct.parent.id = ConstParentIdUnion; + fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2]; + + // @TODO Add fields + ConstExprValue *fn_def_fields = create_const_vals(3); + fn_def_val->data.x_struct.fields = fn_def_fields; + + // fn_type: type + ensure_field_index(fn_def_val->type, "fn_type", 0); + fn_def_fields[0].special = ConstValSpecialStatic; + fn_def_fields[0].type = ira->codegen->builtin_types.entry_type; + fn_def_fields[0].data.x_type = fn_entry->type_entry; + // inline_type: Data.FnDef.Inline + ensure_field_index(fn_def_val->type, "inline_type", 1); + fn_def_fields[1].special = ConstValSpecialStatic; + fn_def_fields[1].type = type_info_fn_def_inline_type; + bigint_init_unsigned(&fn_def_fields[1].data.x_enum_tag, fn_entry->fn_inline); + // calling_convention: TypeInfo.CallingConvention + ensure_field_index(fn_def_val->type, "calling_convention", 2); + fn_def_fields[2].special = ConstValSpecialStatic; + fn_def_fields[2].type = ir_type_info_get_type(ira, "CallingConvention"); + bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc); + + inner_fields[2].data.x_union.payload = fn_def_val; break; } case TldIdContainer: -- cgit v1.2.3 From 837166319dd1a5df14e5d4bebd62080bb6ebdaa1 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 19:02:46 +0200 Subject: Trying to fix osx build failing by setting param_info.type to nullptr --- src/analyze.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/analyze.cpp b/src/analyze.cpp index 11715220c7..29a2fc2560 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1261,6 +1261,10 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou fn_type_id->param_info = allocate_nonzero(param_count_alloc); fn_type_id->next_param_index = 0; fn_type_id->is_var_args = fn_proto->is_var_args; + + // We set param_info to 0, as param_info[i]->type is checked for null + // when checking if a parameters type has been resolved. + memset(fn_type_id->param_info, 0, sizeof(fn_type_id->param_info[i]) * fn_type_id->param_count); } static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { -- cgit v1.2.3 From d6f033b42dcb49cfe45cb61821f2f451e4004686 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 19:09:25 +0200 Subject: Fixed build error --- src/analyze.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 29a2fc2560..1003cf8edf 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1264,7 +1264,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou // We set param_info to 0, as param_info[i]->type is checked for null // when checking if a parameters type has been resolved. - memset(fn_type_id->param_info, 0, sizeof(fn_type_id->param_info[i]) * fn_type_id->param_count); + memset(fn_type_id->param_info, 0, sizeof(fn_type_id->param_info[0]) * fn_type_id->param_count); } static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { -- cgit v1.2.3 From 73bf897b5cc25ee3f1ec9d0ba1483d779de4b7c3 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 19:21:23 +0200 Subject: Using allocate instead of allocate_nonzero so we don't have to memset --- src/analyze.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 1003cf8edf..1ecfe32f4c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1258,13 +1258,9 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou } fn_type_id->param_count = fn_proto->params.length; - fn_type_id->param_info = allocate_nonzero(param_count_alloc); + fn_type_id->param_info = allocate(param_count_alloc); fn_type_id->next_param_index = 0; fn_type_id->is_var_args = fn_proto->is_var_args; - - // We set param_info to 0, as param_info[i]->type is checked for null - // when checking if a parameters type has been resolved. - memset(fn_type_id->param_info, 0, sizeof(fn_type_id->param_info[0]) * fn_type_id->param_count); } static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { -- cgit v1.2.3 From 4ac36d094c06b04c1c71d972d3f3e1187bccea95 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Apr 2018 19:27:58 -0400 Subject: add std.atomic.Stack and std.atomic.Queue --- CMakeLists.txt | 3 +++ std/atomic/index.zig | 7 +++++++ std/atomic/queue.zig | 37 +++++++++++++++++++++++++++++++++++++ std/atomic/stack.zig | 45 +++++++++++++++++++++++++++++++++++++++++++++ std/index.zig | 2 ++ 5 files changed, 94 insertions(+) create mode 100644 std/atomic/index.zig create mode 100644 std/atomic/queue.zig create mode 100644 std/atomic/stack.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bf4bdd709..721690e9dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -415,6 +415,9 @@ set(ZIG_CPP_SOURCES set(ZIG_STD_FILES "array_list.zig" + "atomic/index.zig" + "atomic/stack.zig" + "atomic/queue.zig" "base64.zig" "buf_map.zig" "buf_set.zig" diff --git a/std/atomic/index.zig b/std/atomic/index.zig new file mode 100644 index 0000000000..9d556a6415 --- /dev/null +++ b/std/atomic/index.zig @@ -0,0 +1,7 @@ +pub const Stack = @import("stack.zig").Stack; +pub const Queue = @import("queue.zig").Queue; + +test "std.atomic" { + _ = @import("stack.zig").Stack; + _ = @import("queue.zig").Queue; +} diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig new file mode 100644 index 0000000000..54981d4a61 --- /dev/null +++ b/std/atomic/queue.zig @@ -0,0 +1,37 @@ +/// Many reader, many writer, non-allocating, thread-safe, lock-free +pub fn Queue(comptime T: type) type { + return struct { + head: &Node, + tail: &Node, + root: Node, + + pub const Self = this; + + pub const Node = struct { + next: ?&Node, + data: T, + }; + + // TODO: well defined copy elision + pub fn init(self: &Self) void { + self.root.next = null; + self.head = &self.root; + self.tail = &self.root; + } + + pub fn put(self: &Self, node: &Node) void { + node.next = null; + + const tail = @atomicRmw(&Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); + _ = @atomicRmw(?&Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); + } + + pub fn get(self: &Self) ?&Node { + var head = @atomicLoad(&Node, &self.head, AtomicOrder.Acquire); + while (true) { + const node = head.next ?? return null; + head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? return node; + } + } + }; +} diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig new file mode 100644 index 0000000000..4ceecb7b1d --- /dev/null +++ b/std/atomic/stack.zig @@ -0,0 +1,45 @@ +/// Many reader, many writer, non-allocating, thread-safe, lock-free +pub fn Stack(comptime T: type) type { + return struct { + root: ?&Node, + + pub const Self = this; + + pub const Node = struct { + next: ?&Node, + data: T, + }; + + pub fn init() Self { + return Self { + .root = null, + }; + } + + /// push operation, but only if you are the first item in the stack. if you did not succeed in + /// being the first item in the stack, returns the other item that was there. + pub fn pushFirst(self: &Self, node: &Node) ?&Node { + node.next = null; + return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.AcqRel, AtomicOrder.AcqRel); + } + + pub fn push(self: &Self, node: &Node) void { + var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire); + while (true) { + node.next = root; + root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? break; + } + } + + pub fn pop(self: &Self) ?&Node { + var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire); + while (true) { + root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.Release, AtomicOrder.Acquire) ?? return root; + } + } + + pub fn isEmpty(self: &Self) bool { + return @atomicLoad(?&Node, &self.root, AtomicOrder.Relaxed) == null; + } + }; +} diff --git a/std/index.zig b/std/index.zig index 07c4360aab..d6a1e3c94d 100644 --- a/std/index.zig +++ b/std/index.zig @@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap; pub const LinkedList = @import("linked_list.zig").LinkedList; pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList; +pub const atomic = @import("atomic/index.zig"); pub const base64 = @import("base64.zig"); pub const build = @import("build.zig"); pub const c = @import("c/index.zig"); @@ -34,6 +35,7 @@ pub const zig = @import("zig/index.zig"); test "std" { // run tests from these + _ = @import("atomic/index.zig"); _ = @import("array_list.zig"); _ = @import("buf_map.zig"); _ = @import("buf_set.zig"); -- cgit v1.2.3 From 96ecb402590df7a02526009f1630f27e14a0e77c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Apr 2018 17:53:06 -0400 Subject: add fuzz tests for std.atomic.Stack --- src/ir.cpp | 5 +++ std/atomic/stack.zig | 87 +++++++++++++++++++++++++++++++++++++++++++++++++--- std/heap.zig | 64 +++++++++++++++++++++++++++++++++----- 3 files changed, 143 insertions(+), 13 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 4bf8240472..469900bf07 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18184,6 +18184,11 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr } else { if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering)) return ira->codegen->builtin_types.entry_invalid; + if (ordering == AtomicOrderUnordered) { + ir_add_error(ira, instruction->ordering, + buf_sprintf("@atomicRmw atomic ordering must not be Unordered")); + return ira->codegen->builtin_types.entry_invalid; + } } if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 4ceecb7b1d..a1e686155c 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -1,3 +1,6 @@ +const builtin = @import("builtin"); +const AtomicOrder = builtin.AtomicOrder; + /// Many reader, many writer, non-allocating, thread-safe, lock-free pub fn Stack(comptime T: type) type { return struct { @@ -20,26 +23,100 @@ pub fn Stack(comptime T: type) type { /// being the first item in the stack, returns the other item that was there. pub fn pushFirst(self: &Self, node: &Node) ?&Node { node.next = null; - return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.AcqRel, AtomicOrder.AcqRel); + return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst); } pub fn push(self: &Self, node: &Node) void { - var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire); + var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst); while (true) { node.next = root; - root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? break; + root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break; } } pub fn pop(self: &Self) ?&Node { var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire); while (true) { - root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.Release, AtomicOrder.Acquire) ?? return root; + root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root; } } pub fn isEmpty(self: &Self) bool { - return @atomicLoad(?&Node, &self.root, AtomicOrder.Relaxed) == null; + return @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst) == null; } }; } + +const std = @import("std"); +const Context = struct { + allocator: &std.mem.Allocator, + stack: &Stack(i32), + put_sum: isize, + get_sum: isize, + puts_done: u8, // TODO make this a bool +}; +const puts_per_thread = 1000; +const put_thread_count = 3; + +test "std.atomic.stack" { + var direct_allocator = std.heap.DirectAllocator.init(); + defer direct_allocator.deinit(); + + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024); + defer direct_allocator.allocator.free(plenty_of_memory); + + var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); + var a = &fixed_buffer_allocator.allocator; + + var stack = Stack(i32).init(); + var context = Context { + .allocator = a, + .stack = &stack, + .put_sum = 0, + .get_sum = 0, + .puts_done = 0, + }; + + var putters: [put_thread_count]&std.os.Thread = undefined; + for (putters) |*t| { + *t = try std.os.spawnThreadAllocator(a, &context, startPuts); + } + var getters: [put_thread_count]&std.os.Thread = undefined; + for (getters) |*t| { + *t = try std.os.spawnThreadAllocator(a, &context, startGets); + } + + for (putters) |t| t.wait(); + _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + for (getters) |t| t.wait(); + + std.debug.assert(context.put_sum == context.get_sum); +} + +fn startPuts(ctx: &Context) u8 { + var put_count: usize = puts_per_thread; + var r = std.rand.DefaultPrng.init(0xdeadbeef); + while (put_count != 0) : (put_count -= 1) { + std.os.time.sleep(0, 1); // let the os scheduler be our fuzz + const x = @bitCast(i32, r.random.scalar(u32)); + const node = ctx.allocator.create(Stack(i32).Node) catch unreachable; + node.data = x; + ctx.stack.push(node); + _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); + } + return 0; +} + +fn startGets(ctx: &Context) u8 { + while (true) { + while (ctx.stack.pop()) |node| { + std.os.time.sleep(0, 1); // let the os scheduler be our fuzz + _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); + } + + if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) { + break; + } + } + return 0; +} diff --git a/std/heap.zig b/std/heap.zig index b3a1e6bf27..d632b44cd1 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -47,13 +47,6 @@ pub const DirectAllocator = struct { const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void; - //pub const canary_bytes = []u8 {48, 239, 128, 46, 18, 49, 147, 9, 195, 59, 203, 3, 245, 54, 9, 122}; - //pub const want_safety = switch (builtin.mode) { - // builtin.Mode.Debug => true, - // builtin.Mode.ReleaseSafe => true, - // else => false, - //}; - pub fn init() DirectAllocator { return DirectAllocator { .allocator = Allocator { @@ -298,7 +291,7 @@ pub const FixedBufferAllocator = struct { fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); - const addr = @ptrToInt(&self.buffer[self.end_index]); + const addr = @ptrToInt(self.buffer.ptr) + self.end_index; const rem = @rem(addr, alignment); const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); const adjusted_index = self.end_index + march_forward_bytes; @@ -325,6 +318,54 @@ pub const FixedBufferAllocator = struct { fn free(allocator: &Allocator, bytes: []u8) void { } }; +/// lock free +pub const ThreadSafeFixedBufferAllocator = struct { + allocator: Allocator, + end_index: usize, + buffer: []u8, + + pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator { + return ThreadSafeFixedBufferAllocator { + .allocator = Allocator { + .allocFn = alloc, + .reallocFn = realloc, + .freeFn = free, + }, + .buffer = buffer, + .end_index = 0, + }; + } + + fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { + const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator); + var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst); + while (true) { + const addr = @ptrToInt(self.buffer.ptr) + end_index; + const rem = @rem(addr, alignment); + const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); + const adjusted_index = end_index + march_forward_bytes; + const new_end_index = adjusted_index + n; + if (new_end_index > self.buffer.len) { + return error.OutOfMemory; + } + end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, + builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index .. new_end_index]; + } + } + + fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + if (new_size <= old_mem.len) { + return old_mem[0..new_size]; + } else { + const result = try alloc(allocator, new_size, alignment); + mem.copy(u8, result, old_mem); + return result; + } + } + + fn free(allocator: &Allocator, bytes: []u8) void { } +}; + test "c_allocator" { @@ -363,6 +404,13 @@ test "FixedBufferAllocator" { try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); } +test "ThreadSafeFixedBufferAllocator" { + var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); + + try testAllocator(&fixed_buffer_allocator.allocator); + try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); +} + fn testAllocator(allocator: &mem.Allocator) !void { var slice = try allocator.alloc(&i32, 100); -- cgit v1.2.3 From 5d6e44b3f2bf37deaf09ce4a473fa5b52a6fb760 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Apr 2018 18:00:51 -0400 Subject: add tests for std.atomic Queue and Stack --- std/atomic/queue.zig | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++- std/atomic/stack.zig | 4 +++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 54981d4a61..c7ce00d6cf 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -1,3 +1,7 @@ +const builtin = @import("builtin"); +const AtomicOrder = builtin.AtomicOrder; +const AtomicRmwOp = builtin.AtomicRmwOp; + /// Many reader, many writer, non-allocating, thread-safe, lock-free pub fn Queue(comptime T: type) type { return struct { @@ -12,7 +16,7 @@ pub fn Queue(comptime T: type) type { data: T, }; - // TODO: well defined copy elision + // TODO: well defined copy elision: https://github.com/zig-lang/zig/issues/287 pub fn init(self: &Self) void { self.root.next = null; self.head = &self.root; @@ -35,3 +39,82 @@ pub fn Queue(comptime T: type) type { } }; } + +const std = @import("std"); +const Context = struct { + allocator: &std.mem.Allocator, + queue: &Queue(i32), + put_sum: isize, + get_sum: isize, + get_count: usize, + puts_done: u8, // TODO make this a bool +}; +const puts_per_thread = 10000; +const put_thread_count = 3; + +test "std.atomic.queue" { + var direct_allocator = std.heap.DirectAllocator.init(); + defer direct_allocator.deinit(); + + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024); + defer direct_allocator.allocator.free(plenty_of_memory); + + var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); + var a = &fixed_buffer_allocator.allocator; + + var queue: Queue(i32) = undefined; + queue.init(); + var context = Context { + .allocator = a, + .queue = &queue, + .put_sum = 0, + .get_sum = 0, + .puts_done = 0, + .get_count = 0, + }; + + var putters: [put_thread_count]&std.os.Thread = undefined; + for (putters) |*t| { + *t = try std.os.spawnThreadAllocator(a, &context, startPuts); + } + var getters: [put_thread_count]&std.os.Thread = undefined; + for (getters) |*t| { + *t = try std.os.spawnThreadAllocator(a, &context, startGets); + } + + for (putters) |t| t.wait(); + _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + for (getters) |t| t.wait(); + + std.debug.assert(context.put_sum == context.get_sum); + std.debug.assert(context.get_count == puts_per_thread * put_thread_count); +} + +fn startPuts(ctx: &Context) u8 { + var put_count: usize = puts_per_thread; + var r = std.rand.DefaultPrng.init(0xdeadbeef); + while (put_count != 0) : (put_count -= 1) { + std.os.time.sleep(0, 1); // let the os scheduler be our fuzz + const x = @bitCast(i32, r.random.scalar(u32)); + const node = ctx.allocator.create(Queue(i32).Node) catch unreachable; + node.data = x; + ctx.queue.put(node); + _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); + } + return 0; +} + +fn startGets(ctx: &Context) u8 { + while (true) { + while (ctx.queue.get()) |node| { + std.os.time.sleep(0, 1); // let the os scheduler be our fuzz + _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); + _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); + } + + if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) { + break; + } + } + return 0; +} diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index a1e686155c..a53f7682b0 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -53,6 +53,7 @@ const Context = struct { stack: &Stack(i32), put_sum: isize, get_sum: isize, + get_count: usize, puts_done: u8, // TODO make this a bool }; const puts_per_thread = 1000; @@ -75,6 +76,7 @@ test "std.atomic.stack" { .put_sum = 0, .get_sum = 0, .puts_done = 0, + .get_count = 0, }; var putters: [put_thread_count]&std.os.Thread = undefined; @@ -91,6 +93,7 @@ test "std.atomic.stack" { for (getters) |t| t.wait(); std.debug.assert(context.put_sum == context.get_sum); + std.debug.assert(context.get_count == puts_per_thread * put_thread_count); } fn startPuts(ctx: &Context) u8 { @@ -112,6 +115,7 @@ fn startGets(ctx: &Context) u8 { while (ctx.stack.pop()) |node| { std.os.time.sleep(0, 1); // let the os scheduler be our fuzz _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); + _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); } if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) { -- cgit v1.2.3 From a10351b439769c57454e19915365b21e43f408bc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Apr 2018 18:19:00 -0400 Subject: disable atomic stack and queue tests for non-linux --- std/atomic/queue.zig | 4 ++++ std/atomic/stack.zig | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index c7ce00d6cf..3866bad7ce 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -53,6 +53,10 @@ const puts_per_thread = 10000; const put_thread_count = 3; test "std.atomic.queue" { + if (builtin.os != builtin.Os.linux) { + // TODO implement kernel threads for windows and macos + return; + } var direct_allocator = std.heap.DirectAllocator.init(); defer direct_allocator.deinit(); diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index a53f7682b0..12de2edaaa 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -60,6 +60,10 @@ const puts_per_thread = 1000; const put_thread_count = 3; test "std.atomic.stack" { + if (builtin.os != builtin.Os.linux) { + // TODO implement kernel threads for windows and macos + return; + } var direct_allocator = std.heap.DirectAllocator.init(); defer direct_allocator.deinit(); -- cgit v1.2.3 From ec2a81a081f6c6d77de1bbeebf1d87754a4fae67 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Apr 2018 22:03:07 -0400 Subject: fix compiler-rt ABI for x86_64 windows --- std/os/time.zig | 2 +- std/special/compiler_rt/index.zig | 21 +++++++++++++++++---- std/special/compiler_rt/udivmodti4.zig | 6 ++++++ std/special/compiler_rt/udivti3.zig | 9 +++++++-- std/special/compiler_rt/umodti3.zig | 10 ++++++++-- test/cases/eval.zig | 7 +++++++ 6 files changed, 46 insertions(+), 9 deletions(-) diff --git a/std/os/time.zig b/std/os/time.zig index e9fbf9798c..4fd2c4e924 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -281,7 +281,7 @@ test "os.time.Timer" { debug.assert(time_0 > 0 and time_0 < margin); const time_1 = timer.lap(); - debug.assert(time_1 > time_0); + debug.assert(time_1 >= time_0); timer.reset(); debug.assert(timer.read() < time_1); diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index 81fe1ffec1..6ef43c4fed 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -32,10 +32,6 @@ comptime { @export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage); @export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage); - @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage); - - @export("__udivti3", @import("udivti3.zig").__udivti3, linkage); - @export("__umodti3", @import("umodti3.zig").__umodti3, linkage); @export("__udivsi3", __udivsi3, linkage); @export("__udivdi3", __udivdi3, linkage); @@ -62,9 +58,16 @@ comptime { @export("__chkstk", __chkstk, strong_linkage); @export("___chkstk_ms", ___chkstk_ms, linkage); } + @export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage); + @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage); + @export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage); }, else => {}, } + } else { + @export("__udivti3", @import("udivti3.zig").__udivti3, linkage); + @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage); + @export("__umodti3", @import("umodti3.zig").__umodti3, linkage); } } @@ -83,6 +86,16 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn } } +pub fn setXmm0(comptime T: type, value: T) void { + comptime assert(builtin.arch == builtin.Arch.x86_64); + const aligned_value: T align(16) = value; + asm volatile ( + \\movaps (%[ptr]), %%xmm0 + : + : [ptr] "r" (&aligned_value) + : "xmm0"); +} + extern fn __udivdi3(a: u64, b: u64) u64 { @setRuntimeSafety(is_test); return __udivmoddi4(a, b, null); diff --git a/std/special/compiler_rt/udivmodti4.zig b/std/special/compiler_rt/udivmodti4.zig index 196d067aef..f8fdebe4db 100644 --- a/std/special/compiler_rt/udivmodti4.zig +++ b/std/special/compiler_rt/udivmodti4.zig @@ -1,11 +1,17 @@ const udivmod = @import("udivmod.zig").udivmod; const builtin = @import("builtin"); +const compiler_rt = @import("index.zig"); pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 { @setRuntimeSafety(builtin.is_test); return udivmod(u128, a, b, maybe_rem); } +pub extern fn __udivmodti4_windows_x86_64(a: &const u128, b: &const u128, maybe_rem: ?&u128) void { + @setRuntimeSafety(builtin.is_test); + compiler_rt.setXmm0(u128, udivmod(u128, *a, *b, maybe_rem)); +} + test "import udivmodti4" { _ = @import("udivmodti4_test.zig"); } diff --git a/std/special/compiler_rt/udivti3.zig b/std/special/compiler_rt/udivti3.zig index eaecbac4d2..ad0f09e733 100644 --- a/std/special/compiler_rt/udivti3.zig +++ b/std/special/compiler_rt/udivti3.zig @@ -1,7 +1,12 @@ -const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4; +const udivmodti4 = @import("udivmodti4.zig"); const builtin = @import("builtin"); pub extern fn __udivti3(a: u128, b: u128) u128 { @setRuntimeSafety(builtin.is_test); - return __udivmodti4(a, b, null); + return udivmodti4.__udivmodti4(a, b, null); +} + +pub extern fn __udivti3_windows_x86_64(a: &const u128, b: &const u128) void { + @setRuntimeSafety(builtin.is_test); + udivmodti4.__udivmodti4_windows_x86_64(a, b, null); } diff --git a/std/special/compiler_rt/umodti3.zig b/std/special/compiler_rt/umodti3.zig index 26b306efa9..3e8b80058e 100644 --- a/std/special/compiler_rt/umodti3.zig +++ b/std/special/compiler_rt/umodti3.zig @@ -1,9 +1,15 @@ -const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4; +const udivmodti4 = @import("udivmodti4.zig"); const builtin = @import("builtin"); +const compiler_rt = @import("index.zig"); pub extern fn __umodti3(a: u128, b: u128) u128 { @setRuntimeSafety(builtin.is_test); var r: u128 = undefined; - _ = __udivmodti4(a, b, &r); + _ = udivmodti4.__udivmodti4(a, b, &r); return r; } + +pub extern fn __umodti3_windows_x86_64(a: &const u128, b: &const u128) void { + @setRuntimeSafety(builtin.is_test); + compiler_rt.setXmm0(u128, __umodti3(*a, *b)); +} diff --git a/test/cases/eval.zig b/test/cases/eval.zig index e13d4340e7..364db5e152 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -529,3 +529,10 @@ test "comptime shlWithOverflow" { assert(ct_shifted == rt_shifted); } + +test "runtime 128 bit integer division" { + var a: u128 = 152313999999999991610955792383; + var b: u128 = 10000000000000000000; + var c = a / b; + assert(c == 15231399999); +} -- cgit v1.2.3 From a344cb03bc3c48f3c7fec32dc19c1bcad0910941 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Apr 2018 23:30:13 -0400 Subject: *WIP* use pthreads when linking libc --- std/c/darwin.zig | 5 +++ std/c/index.zig | 10 +++++ std/c/linux.zig | 5 +++ std/os/index.zig | 112 +++++++++++++++++++++++++++++++++++++++++-------------- std/os/test.zig | 4 +- 5 files changed, 107 insertions(+), 29 deletions(-) diff --git a/std/c/darwin.zig b/std/c/darwin.zig index b958055ae8..7ac57514c9 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -81,3 +81,8 @@ pub const sockaddr = extern struct { }; pub const sa_family_t = u8; + +pub const pthread_attr_t = extern struct { + __sig: c_long, + __opaque: [56]u8, +}; diff --git a/std/c/index.zig b/std/c/index.zig index cff86f4041..5ea7145cd3 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -53,3 +53,13 @@ pub extern "c" fn malloc(usize) ?&c_void; pub extern "c" fn realloc(&c_void, usize) ?&c_void; pub extern "c" fn free(&c_void) void; pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int; + +pub extern "c" fn pthread_create(noalias newthread: &pthread_t, + noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void, + noalias arg: ?&c_void) c_int; +pub extern "c" fn pthread_attr_init(attr: &pthread_attr_t) c_int; +pub extern "c" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int; +pub extern "c" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int; +pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int; + +pub const pthread_t = &@OpaqueType(); diff --git a/std/c/linux.zig b/std/c/linux.zig index b2ac05eba5..7810fec130 100644 --- a/std/c/linux.zig +++ b/std/c/linux.zig @@ -3,3 +3,8 @@ pub use @import("../os/linux/errno.zig"); pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int; extern "c" fn __errno_location() &c_int; pub const _errno = __errno_location; + +pub const pthread_attr_t = extern struct { + __size: [56]u8, + __align: c_long, +}; diff --git a/std/os/index.zig b/std/os/index.zig index 0639490725..3669dca198 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2,6 +2,10 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); const Os = builtin.Os; const is_windows = builtin.os == Os.windows; +const is_posix = switch (builtin.os) { + builtin.Os.linux, builtin.Os.macosx => true, + else => false, +}; const os = this; test "std.os" { @@ -2343,21 +2347,39 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { } pub const Thread = struct { - pid: i32, + pid: pid_t, allocator: ?&mem.Allocator, stack: []u8, + pthread_handle: pthread_t, + + pub const use_pthreads = is_posix and builtin.link_libc; + const pthread_t = if (use_pthreads) c.pthread_t else void; + const pid_t = if (!use_pthreads) i32 else void; pub fn wait(self: &const Thread) void { - while (true) { - const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst); - if (pid_value == 0) break; - const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null); - switch (linux.getErrno(rc)) { - 0 => continue, - posix.EINTR => continue, - posix.EAGAIN => continue, + if (use_pthreads) { + const err = c.pthread_join(self.pthread_handle, null); + switch (err) { + 0 => {}, + posix.EINVAL => unreachable, + posix.ESRCH => unreachable, + posix.EDEADLK => unreachable, else => unreachable, } + } else if (builtin.os == builtin.Os.linux) { + while (true) { + const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst); + if (pid_value == 0) break; + const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null); + switch (linux.getErrno(rc)) { + 0 => continue, + posix.EINTR => continue, + posix.EAGAIN => continue, + else => unreachable, + } + } + } else { + @compileError("Unsupported OS"); } if (self.allocator) |a| { a.free(self.stack); @@ -2429,31 +2451,67 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread thread_ptr.stack = stack; thread_ptr.allocator = null; - const threadMain = struct { - extern fn threadMain(ctx_addr: usize) u8 { + const MainFuncs = struct { + extern fn linuxThreadMain(ctx_addr: usize) u8 { if (@sizeOf(Context) == 0) { return startFn({}); } else { return startFn(*@intToPtr(&const Context, ctx_addr)); } } - }.threadMain; + extern fn posixThreadMain(ctx: ?&c_void) ?&c_void { + if (@sizeOf(Context) == 0) { + _ = startFn({}); + return null; + } else { + _ = startFn(*@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx))); + return null; + } + } + }; + + if (builtin.os == builtin.Os.windows) { + // use windows API directly + @compileError("TODO support spawnThread for Windows"); + } else if (Thread.use_pthreads) { + // use pthreads + var attr: c.pthread_attr_t = undefined; + if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; + defer assert(c.pthread_attr_destroy(&attr) == 0); + + const stack_size = stack_end - @ptrToInt(stack.ptr); + if (c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size) != 0) { + return SpawnThreadError.SystemResources; + } - const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND - | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS - | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; - const newtls: usize = 0; - const rc = posix.clone(threadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid); - const err = posix.getErrno(rc); - switch (err) { - 0 => return thread_ptr, - posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, - posix.EINVAL => unreachable, - posix.ENOMEM => return SpawnThreadError.SystemResources, - posix.ENOSPC => unreachable, - posix.EPERM => unreachable, - posix.EUSERS => unreachable, - else => return unexpectedErrorPosix(err), + const err = c.pthread_create(&thread_ptr.pthread_handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); + switch (err) { + 0 => return thread_ptr, + posix.EAGAIN => return SpawnThreadError.SystemResources, + posix.EPERM => unreachable, + posix.EINVAL => unreachable, + else => return unexpectedErrorPosix(usize(err)), + } + } else if (builtin.os == builtin.Os.linux) { + // use linux API directly + const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND + | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS + | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; + const newtls: usize = 0; + const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid); + const err = posix.getErrno(rc); + switch (err) { + 0 => return thread_ptr, + posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, + posix.EINVAL => unreachable, + posix.ENOMEM => return SpawnThreadError.SystemResources, + posix.ENOSPC => unreachable, + posix.EPERM => unreachable, + posix.EUSERS => unreachable, + else => return unexpectedErrorPosix(err), + } + } else { + @compileError("Unsupported OS"); } } diff --git a/std/os/test.zig b/std/os/test.zig index 41afee004a..9a155c027a 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -44,8 +44,8 @@ test "access file" { } test "spawn threads" { - if (builtin.os != builtin.Os.linux) { - // TODO implement threads on macos and windows + if (builtin.os == builtin.Os.windows) { + // TODO implement threads on windows return; } -- cgit v1.2.3 From 998e25a01e8b3ada235aee4a9f785a7454de4b3f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Apr 2018 23:47:39 -0400 Subject: pthread support working --- src/all_types.hpp | 1 + src/analyze.cpp | 8 ++++++++ src/codegen.cpp | 2 ++ std/os/index.zig | 14 ++++++++------ std/os/test.zig | 4 ++-- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index d1b2ad61d2..f08b870b37 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1486,6 +1486,7 @@ struct CodeGen { ZigList link_libs_list; LinkLib *libc_link_lib; + LinkLib *pthread_link_lib; // add -framework [name] args to linker ZigList darwin_frameworks; diff --git a/src/analyze.cpp b/src/analyze.cpp index 1ecfe32f4c..8a9d236790 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6049,10 +6049,15 @@ LinkLib *create_link_lib(Buf *name) { LinkLib *add_link_lib(CodeGen *g, Buf *name) { bool is_libc = buf_eql_str(name, "c"); + bool is_pthread = buf_eql_str(name, "pthread"); if (is_libc && g->libc_link_lib != nullptr) return g->libc_link_lib; + if (is_pthread && g->pthread_link_lib != nullptr) { + return g->pthread_link_lib; + } + for (size_t i = 0; i < g->link_libs_list.length; i += 1) { LinkLib *existing_lib = g->link_libs_list.at(i); if (buf_eql_buf(existing_lib->name, name)) { @@ -6066,6 +6071,9 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) { if (is_libc) g->libc_link_lib = link_lib; + if (is_pthread) + g->pthread_link_lib = link_lib; + return link_lib; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 2d8c385f44..9f064d5f19 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -145,6 +145,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out { g->libc_link_lib = create_link_lib(buf_create_from_str("c")); g->link_libs_list.append(g->libc_link_lib); + g->pthread_link_lib = create_link_lib(buf_create_from_str("pthread")); } return g; @@ -6373,6 +6374,7 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); + buf_appendf(contents, "pub const link_pthread = %s;\n", bool_to_str(g->pthread_link_lib != nullptr)); buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n"); diff --git a/std/os/index.zig b/std/os/index.zig index 3669dca198..fa1cc418a5 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2352,12 +2352,11 @@ pub const Thread = struct { stack: []u8, pthread_handle: pthread_t, - pub const use_pthreads = is_posix and builtin.link_libc; - const pthread_t = if (use_pthreads) c.pthread_t else void; - const pid_t = if (!use_pthreads) i32 else void; + const pthread_t = if (builtin.link_pthread) c.pthread_t else void; + const pid_t = if (!builtin.link_pthread) i32 else void; pub fn wait(self: &const Thread) void { - if (use_pthreads) { + if (builtin.link_pthread) { const err = c.pthread_join(self.pthread_handle, null); switch (err) { 0 => {}, @@ -2407,6 +2406,9 @@ pub const SpawnThreadError = error { /// be copied. SystemResources, + /// pthreads requires at least 16384 bytes of stack space + StackTooSmall, + Unexpected, }; @@ -2473,7 +2475,7 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread if (builtin.os == builtin.Os.windows) { // use windows API directly @compileError("TODO support spawnThread for Windows"); - } else if (Thread.use_pthreads) { + } else if (builtin.link_pthread) { // use pthreads var attr: c.pthread_attr_t = undefined; if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; @@ -2481,7 +2483,7 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread const stack_size = stack_end - @ptrToInt(stack.ptr); if (c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size) != 0) { - return SpawnThreadError.SystemResources; + return SpawnThreadError.StackTooSmall; // pthreads requires at least 16384 bytes } const err = c.pthread_create(&thread_ptr.pthread_handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); diff --git a/std/os/test.zig b/std/os/test.zig index 9a155c027a..87486bde4f 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -57,8 +57,8 @@ test "spawn threads" { const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1); const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2); - var stack1: [1024]u8 = undefined; - var stack2: [1024]u8 = undefined; + var stack1: [20 * 1024]u8 = undefined; + var stack2: [20 * 1024]u8 = undefined; const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2); const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2); -- cgit v1.2.3 From a42542099392cf189b96bdd77ecd88feadfb6382 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 00:07:32 -0400 Subject: make pthreads threads work on darwin darwin pthreads adds a restriction that the stack start and end must be page aligned --- std/os/index.zig | 10 +++++++--- std/os/test.zig | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index fa1cc418a5..8681a018b9 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2421,7 +2421,7 @@ pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime st // TODO compile-time call graph analysis to determine stack upper bound // https://github.com/zig-lang/zig/issues/157 const default_stack_size = 8 * 1024 * 1024; - const stack_bytes = try allocator.alloc(u8, default_stack_size); + const stack_bytes = try allocator.alignedAlloc(u8, os.page_size, default_stack_size); const thread = try spawnThread(stack_bytes, context, startFn); thread.allocator = allocator; return thread; @@ -2431,7 +2431,7 @@ pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime st /// fn startFn(@typeOf(context)) T /// where T is u8, noreturn, void, or !void /// caller must call wait on the returned thread -pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThreadError!&Thread { +pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime startFn: var) SpawnThreadError!&Thread { const Context = @typeOf(context); comptime assert(@ArgType(@typeOf(startFn), 0) == Context); @@ -2481,8 +2481,12 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; defer assert(c.pthread_attr_destroy(&attr) == 0); + // align to page + stack_end -= stack_end % os.page_size; + const stack_size = stack_end - @ptrToInt(stack.ptr); - if (c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size) != 0) { + const setstack_err = c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size); + if (setstack_err != 0) { return SpawnThreadError.StackTooSmall; // pthreads requires at least 16384 bytes } diff --git a/std/os/test.zig b/std/os/test.zig index 87486bde4f..37e5bf4bb8 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -57,8 +57,8 @@ test "spawn threads" { const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1); const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2); - var stack1: [20 * 1024]u8 = undefined; - var stack2: [20 * 1024]u8 = undefined; + var stack1: [20 * 1024]u8 align(os.page_size) = undefined; + var stack2: [20 * 1024]u8 align(os.page_size) = undefined; const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2); const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2); -- cgit v1.2.3 From abf90eaa674782e092e49bb23c4c7da0f581f604 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 00:09:18 -0400 Subject: enable atomic queue and stack tests for macos --- std/atomic/queue.zig | 4 ++-- std/atomic/stack.zig | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 3866bad7ce..dd9b869f02 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -53,8 +53,8 @@ const puts_per_thread = 10000; const put_thread_count = 3; test "std.atomic.queue" { - if (builtin.os != builtin.Os.linux) { - // TODO implement kernel threads for windows and macos + if (builtin.os == builtin.Os.windows) { + // TODO implement kernel threads for windows return; } var direct_allocator = std.heap.DirectAllocator.init(); diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 12de2edaaa..9f2ceacfa3 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -60,8 +60,8 @@ const puts_per_thread = 1000; const put_thread_count = 3; test "std.atomic.stack" { - if (builtin.os != builtin.Os.linux) { - // TODO implement kernel threads for windows and macos + if (builtin.os == builtin.Os.windows) { + // TODO implement kernel threads for windows return; } var direct_allocator = std.heap.DirectAllocator.init(); -- cgit v1.2.3 From bf8e419d2b7853f5cb5aba4dcba45ae28a3840aa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 00:40:04 -0400 Subject: linux uses pthreads when linking against libc --- src/all_types.hpp | 1 - src/analyze.cpp | 8 -------- src/codegen.cpp | 2 -- std/c/index.zig | 10 +++++----- std/os/index.zig | 9 +++++---- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index f08b870b37..d1b2ad61d2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1486,7 +1486,6 @@ struct CodeGen { ZigList link_libs_list; LinkLib *libc_link_lib; - LinkLib *pthread_link_lib; // add -framework [name] args to linker ZigList darwin_frameworks; diff --git a/src/analyze.cpp b/src/analyze.cpp index 8a9d236790..1ecfe32f4c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6049,15 +6049,10 @@ LinkLib *create_link_lib(Buf *name) { LinkLib *add_link_lib(CodeGen *g, Buf *name) { bool is_libc = buf_eql_str(name, "c"); - bool is_pthread = buf_eql_str(name, "pthread"); if (is_libc && g->libc_link_lib != nullptr) return g->libc_link_lib; - if (is_pthread && g->pthread_link_lib != nullptr) { - return g->pthread_link_lib; - } - for (size_t i = 0; i < g->link_libs_list.length; i += 1) { LinkLib *existing_lib = g->link_libs_list.at(i); if (buf_eql_buf(existing_lib->name, name)) { @@ -6071,9 +6066,6 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) { if (is_libc) g->libc_link_lib = link_lib; - if (is_pthread) - g->pthread_link_lib = link_lib; - return link_lib; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 9f064d5f19..2d8c385f44 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -145,7 +145,6 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out { g->libc_link_lib = create_link_lib(buf_create_from_str("c")); g->link_libs_list.append(g->libc_link_lib); - g->pthread_link_lib = create_link_lib(buf_create_from_str("pthread")); } return g; @@ -6374,7 +6373,6 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); - buf_appendf(contents, "pub const link_pthread = %s;\n", bool_to_str(g->pthread_link_lib != nullptr)); buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n"); diff --git a/std/c/index.zig b/std/c/index.zig index 5ea7145cd3..34269d2aa2 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -54,12 +54,12 @@ pub extern "c" fn realloc(&c_void, usize) ?&c_void; pub extern "c" fn free(&c_void) void; pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int; -pub extern "c" fn pthread_create(noalias newthread: &pthread_t, +pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t, noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void, noalias arg: ?&c_void) c_int; -pub extern "c" fn pthread_attr_init(attr: &pthread_attr_t) c_int; -pub extern "c" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int; -pub extern "c" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int; -pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int; +pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int; +pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int; +pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int; +pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int; pub const pthread_t = &@OpaqueType(); diff --git a/std/os/index.zig b/std/os/index.zig index 8681a018b9..85e46a1bf9 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2352,11 +2352,12 @@ pub const Thread = struct { stack: []u8, pthread_handle: pthread_t, - const pthread_t = if (builtin.link_pthread) c.pthread_t else void; - const pid_t = if (!builtin.link_pthread) i32 else void; + pub const use_pthreads = is_posix and builtin.link_libc; + const pthread_t = if (use_pthreads) c.pthread_t else void; + const pid_t = if (!use_pthreads) i32 else void; pub fn wait(self: &const Thread) void { - if (builtin.link_pthread) { + if (use_pthreads) { const err = c.pthread_join(self.pthread_handle, null); switch (err) { 0 => {}, @@ -2475,7 +2476,7 @@ pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime start if (builtin.os == builtin.Os.windows) { // use windows API directly @compileError("TODO support spawnThread for Windows"); - } else if (builtin.link_pthread) { + } else if (Thread.use_pthreads) { // use pthreads var attr: c.pthread_attr_t = undefined; if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; -- cgit v1.2.3 From 6376d96824c5205ecc02b2c621bcef5dc78f1a81 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 02:40:22 -0400 Subject: support kernel threads for windows * remove std.os.spawnThreadAllocator - windows does not support an explicit stack, so using an allocator for a thread stack space does not work. * std.os.spawnThread - instead of accepting a stack argument, the implementation will directly allocate using OS-specific APIs. --- std/atomic/queue.zig | 8 +-- std/atomic/stack.zig | 8 +-- std/mem.zig | 1 + std/os/index.zig | 165 ++++++++++++++++++++++++++++++----------------- std/os/test.zig | 20 ++---- std/os/windows/index.zig | 6 ++ 6 files changed, 120 insertions(+), 88 deletions(-) diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index dd9b869f02..1acecbab2c 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -53,10 +53,6 @@ const puts_per_thread = 10000; const put_thread_count = 3; test "std.atomic.queue" { - if (builtin.os == builtin.Os.windows) { - // TODO implement kernel threads for windows - return; - } var direct_allocator = std.heap.DirectAllocator.init(); defer direct_allocator.deinit(); @@ -79,11 +75,11 @@ test "std.atomic.queue" { var putters: [put_thread_count]&std.os.Thread = undefined; for (putters) |*t| { - *t = try std.os.spawnThreadAllocator(a, &context, startPuts); + *t = try std.os.spawnThread(&context, startPuts); } var getters: [put_thread_count]&std.os.Thread = undefined; for (getters) |*t| { - *t = try std.os.spawnThreadAllocator(a, &context, startGets); + *t = try std.os.spawnThread(&context, startGets); } for (putters) |t| t.wait(); diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 9f2ceacfa3..accbcc942a 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -60,10 +60,6 @@ const puts_per_thread = 1000; const put_thread_count = 3; test "std.atomic.stack" { - if (builtin.os == builtin.Os.windows) { - // TODO implement kernel threads for windows - return; - } var direct_allocator = std.heap.DirectAllocator.init(); defer direct_allocator.deinit(); @@ -85,11 +81,11 @@ test "std.atomic.stack" { var putters: [put_thread_count]&std.os.Thread = undefined; for (putters) |*t| { - *t = try std.os.spawnThreadAllocator(a, &context, startPuts); + *t = try std.os.spawnThread(&context, startPuts); } var getters: [put_thread_count]&std.os.Thread = undefined; for (getters) |*t| { - *t = try std.os.spawnThreadAllocator(a, &context, startGets); + *t = try std.os.spawnThread(&context, startGets); } for (putters) |t| t.wait(); diff --git a/std/mem.zig b/std/mem.zig index cc3161cddd..0f66f549cc 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -32,6 +32,7 @@ pub const Allocator = struct { freeFn: fn (self: &Allocator, old_mem: []u8) void, fn create(self: &Allocator, comptime T: type) !&T { + if (@sizeOf(T) == 0) return &{}; const slice = try self.alloc(T, 1); return &slice[0]; } diff --git a/std/os/index.zig b/std/os/index.zig index 85e46a1bf9..6842fd0fb3 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2347,18 +2347,30 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { } pub const Thread = struct { - pid: pid_t, - allocator: ?&mem.Allocator, - stack: []u8, - pthread_handle: pthread_t, + data: Data, pub const use_pthreads = is_posix and builtin.link_libc; - const pthread_t = if (use_pthreads) c.pthread_t else void; - const pid_t = if (!use_pthreads) i32 else void; + const Data = if (use_pthreads) struct { + handle: c.pthread_t, + stack_addr: usize, + stack_len: usize, + } else switch (builtin.os) { + builtin.Os.linux => struct { + pid: i32, + stack_addr: usize, + stack_len: usize, + }, + builtin.Os.windows => struct { + handle: windows.HANDLE, + alloc_start: &c_void, + heap_handle: windows.HANDLE, + }, + else => @compileError("Unsupported OS"), + }; pub fn wait(self: &const Thread) void { if (use_pthreads) { - const err = c.pthread_join(self.pthread_handle, null); + const err = c.pthread_join(self.data.handle, null); switch (err) { 0 => {}, posix.EINVAL => unreachable, @@ -2366,23 +2378,27 @@ pub const Thread = struct { posix.EDEADLK => unreachable, else => unreachable, } - } else if (builtin.os == builtin.Os.linux) { - while (true) { - const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst); - if (pid_value == 0) break; - const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null); - switch (linux.getErrno(rc)) { - 0 => continue, - posix.EINTR => continue, - posix.EAGAIN => continue, - else => unreachable, + assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); + } else switch (builtin.os) { + builtin.Os.linux => { + while (true) { + const pid_value = @atomicLoad(i32, &self.data.pid, builtin.AtomicOrder.SeqCst); + if (pid_value == 0) break; + const rc = linux.futex_wait(@ptrToInt(&self.data.pid), linux.FUTEX_WAIT, pid_value, null); + switch (linux.getErrno(rc)) { + 0 => continue, + posix.EINTR => continue, + posix.EAGAIN => continue, + else => unreachable, + } } - } - } else { - @compileError("Unsupported OS"); - } - if (self.allocator) |a| { - a.free(self.stack); + assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); + }, + builtin.Os.windows => { + assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); + assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0); + }, + else => @compileError("Unsupported OS"), } } }; @@ -2407,52 +2423,60 @@ pub const SpawnThreadError = error { /// be copied. SystemResources, - /// pthreads requires at least 16384 bytes of stack space - StackTooSmall, + /// Not enough userland memory to spawn the thread. + OutOfMemory, Unexpected, }; -pub const SpawnThreadAllocatorError = SpawnThreadError || error{OutOfMemory}; - /// caller must call wait on the returned thread /// fn startFn(@typeOf(context)) T /// where T is u8, noreturn, void, or !void -pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime startFn: var) SpawnThreadAllocatorError!&Thread { +/// caller must call wait on the returned thread +pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread { // TODO compile-time call graph analysis to determine stack upper bound // https://github.com/zig-lang/zig/issues/157 const default_stack_size = 8 * 1024 * 1024; - const stack_bytes = try allocator.alignedAlloc(u8, os.page_size, default_stack_size); - const thread = try spawnThread(stack_bytes, context, startFn); - thread.allocator = allocator; - return thread; -} -/// stack must be big enough to store one Thread and one @typeOf(context), each with default alignment, at the end -/// fn startFn(@typeOf(context)) T -/// where T is u8, noreturn, void, or !void -/// caller must call wait on the returned thread -pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime startFn: var) SpawnThreadError!&Thread { const Context = @typeOf(context); comptime assert(@ArgType(@typeOf(startFn), 0) == Context); - var stack_end: usize = @ptrToInt(stack.ptr) + stack.len; - var arg: usize = undefined; - if (@sizeOf(Context) != 0) { - stack_end -= @sizeOf(Context); - stack_end -= stack_end % @alignOf(Context); - assert(stack_end >= @ptrToInt(stack.ptr)); - const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end)); - *context_ptr = context; - arg = stack_end; - } + if (builtin.os == builtin.Os.windows) { + const WinThread = struct { + const OuterContext = struct { + thread: Thread, + inner: Context, + }; + extern fn threadMain(arg: windows.LPVOID) windows.DWORD { + if (@sizeOf(Context) == 0) { + return startFn({}); + } else { + return startFn(*@ptrCast(&Context, @alignCast(@alignOf(Context), arg))); + } + } + }; - stack_end -= @sizeOf(Thread); - stack_end -= stack_end % @alignOf(Thread); - assert(stack_end >= @ptrToInt(stack.ptr)); - const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end)); - thread_ptr.stack = stack; - thread_ptr.allocator = null; + const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory; + const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); + const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory; + errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); + const bytes = @ptrCast(&u8, bytes_ptr)[0..byte_count]; + const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; + outer_context.inner = context; + outer_context.thread.data.heap_handle = heap_handle; + outer_context.thread.data.alloc_start = bytes_ptr; + + const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner); + outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, + parameter, 0, null) ?? + { + const err = windows.GetLastError(); + return switch (err) { + else => os.unexpectedErrorWindows(err), + }; + }; + return &outer_context.thread; + } const MainFuncs = struct { extern fn linuxThreadMain(ctx_addr: usize) u8 { @@ -2473,6 +2497,29 @@ pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime start } }; + const stack_len = default_stack_size; + const stack_addr = posix.mmap(null, stack_len, posix.PROT_READ|posix.PROT_WRITE, + posix.MAP_PRIVATE|posix.MAP_ANONYMOUS|posix.MAP_GROWSDOWN, -1, 0); + if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory; + errdefer _ = posix.munmap(stack_addr, stack_len); + + var stack_end: usize = stack_addr + stack_len; + var arg: usize = undefined; + if (@sizeOf(Context) != 0) { + stack_end -= @sizeOf(Context); + stack_end -= stack_end % @alignOf(Context); + assert(stack_end >= stack_addr); + const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end)); + *context_ptr = context; + arg = stack_end; + } + + stack_end -= @sizeOf(Thread); + stack_end -= stack_end % @alignOf(Thread); + assert(stack_end >= stack_addr); + const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end)); + + if (builtin.os == builtin.Os.windows) { // use windows API directly @compileError("TODO support spawnThread for Windows"); @@ -2484,14 +2531,12 @@ pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime start // align to page stack_end -= stack_end % os.page_size; + assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_len) == 0); - const stack_size = stack_end - @ptrToInt(stack.ptr); - const setstack_err = c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size); - if (setstack_err != 0) { - return SpawnThreadError.StackTooSmall; // pthreads requires at least 16384 bytes - } + thread_ptr.data.stack_addr = stack_addr; + thread_ptr.data.stack_len = stack_len; - const err = c.pthread_create(&thread_ptr.pthread_handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); + const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); switch (err) { 0 => return thread_ptr, posix.EAGAIN => return SpawnThreadError.SystemResources, diff --git a/std/os/test.zig b/std/os/test.zig index 37e5bf4bb8..56d6e8b309 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -44,24 +44,12 @@ test "access file" { } test "spawn threads" { - if (builtin.os == builtin.Os.windows) { - // TODO implement threads on windows - return; - } - - var direct_allocator = std.heap.DirectAllocator.init(); - defer direct_allocator.deinit(); - var shared_ctx: i32 = 1; - const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1); - const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2); - - var stack1: [20 * 1024]u8 align(os.page_size) = undefined; - var stack2: [20 * 1024]u8 align(os.page_size) = undefined; - - const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2); - const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2); + const thread1 = try std.os.spawnThread({}, start1); + const thread2 = try std.os.spawnThread(&shared_ctx, start2); + const thread3 = try std.os.spawnThread(&shared_ctx, start2); + const thread4 = try std.os.spawnThread(&shared_ctx, start2); thread1.wait(); thread2.wait(); diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index d6ef7205e8..e13ed0f131 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -28,6 +28,9 @@ pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lp pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR, dwFlags: DWORD) BOOLEAN; + +pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; + pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; @@ -318,6 +321,9 @@ pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; pub const HEAP_NO_SERIALIZE = 0x00000001; +pub const PTHREAD_START_ROUTINE = extern fn(LPVOID) DWORD; +pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; + test "import" { _ = @import("util.zig"); } -- cgit v1.2.3 From b21bcbd7755d236a313c06e6ff167f5395ab8ed4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 02:52:04 -0400 Subject: fix std threads for macos --- std/heap.zig | 6 +++--- std/os/darwin.zig | 8 ++++---- std/os/index.zig | 6 ++++-- std/os/linux/index.zig | 6 +++--- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/std/heap.zig b/std/heap.zig index d632b44cd1..bfdf62f658 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -91,7 +91,7 @@ pub const DirectAllocator = struct { const unused_start = addr; const unused_len = aligned_addr - 1 - unused_start; - var err = p.munmap(@intToPtr(&u8, unused_start), unused_len); + var err = p.munmap(unused_start, unused_len); debug.assert(p.getErrno(err) == 0); //It is impossible that there is an unoccupied page at the top of our @@ -132,7 +132,7 @@ pub const DirectAllocator = struct { const rem = @rem(new_addr_end, os.page_size); const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem); if (old_addr_end > new_addr_end_rounded) { - _ = os.posix.munmap(@intToPtr(&u8, new_addr_end_rounded), old_addr_end - new_addr_end_rounded); + _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded); } return old_mem[0..new_size]; } @@ -170,7 +170,7 @@ pub const DirectAllocator = struct { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { - _ = os.posix.munmap(bytes.ptr, bytes.len); + _ = os.posix.munmap(@ptrToInt(bytes.ptr), bytes.len); }, Os.windows => { const record_addr = @ptrToInt(bytes.ptr) + bytes.len; diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 44418649ab..0a62b03ab2 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -184,7 +184,7 @@ pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize { return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte)); } -pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, +pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { const ptr_result = c.mmap(@ptrCast(&c_void, address), length, @@ -193,8 +193,8 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, return errnoWrap(isize_result); } -pub fn munmap(address: &u8, length: usize) usize { - return errnoWrap(c.munmap(@ptrCast(&c_void, address), length)); +pub fn munmap(address: usize, length: usize) usize { + return errnoWrap(c.munmap(@intToPtr(&c_void, address), length)); } pub fn unlink(path: &const u8) usize { @@ -341,4 +341,4 @@ pub const timeval = c.timeval; pub const mach_timebase_info_data = c.mach_timebase_info_data; pub const mach_absolute_time = c.mach_absolute_time; -pub const mach_timebase_info = c.mach_timebase_info; \ No newline at end of file +pub const mach_timebase_info = c.mach_timebase_info; diff --git a/std/os/index.zig b/std/os/index.zig index 6842fd0fb3..5053a1b016 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2497,11 +2497,13 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread } }; + const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; + const stack_len = default_stack_size; const stack_addr = posix.mmap(null, stack_len, posix.PROT_READ|posix.PROT_WRITE, - posix.MAP_PRIVATE|posix.MAP_ANONYMOUS|posix.MAP_GROWSDOWN, -1, 0); + posix.MAP_PRIVATE|posix.MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0); if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory; - errdefer _ = posix.munmap(stack_addr, stack_len); + errdefer assert(posix.munmap(stack_addr, stack_len) == 0); var stack_end: usize = stack_addr + stack_len; var arg: usize = undefined; diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index dcd9532d1d..368f074b9b 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -706,13 +706,13 @@ pub fn umount2(special: &const u8, flags: u32) usize { return syscall2(SYS_umount2, @ptrToInt(special), flags); } -pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize { +pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd), @bitCast(usize, offset)); } -pub fn munmap(address: &u8, length: usize) usize { - return syscall2(SYS_munmap, @ptrToInt(address), length); +pub fn munmap(address: usize, length: usize) usize { + return syscall2(SYS_munmap, address, length); } pub fn read(fd: i32, buf: &u8, count: usize) usize { -- cgit v1.2.3 From c76b0a845fb4176479c8bbf915e57dbdfdb7a594 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 02:56:59 -0400 Subject: fix std threads for linux --- std/os/index.zig | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index 5053a1b016..ee9ff1516c 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2499,13 +2499,13 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; - const stack_len = default_stack_size; - const stack_addr = posix.mmap(null, stack_len, posix.PROT_READ|posix.PROT_WRITE, + const mmap_len = default_stack_size; + const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ|posix.PROT_WRITE, posix.MAP_PRIVATE|posix.MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0); if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory; - errdefer assert(posix.munmap(stack_addr, stack_len) == 0); + errdefer assert(posix.munmap(stack_addr, mmap_len) == 0); - var stack_end: usize = stack_addr + stack_len; + var stack_end: usize = stack_addr + mmap_len; var arg: usize = undefined; if (@sizeOf(Context) != 0) { stack_end -= @sizeOf(Context); @@ -2521,6 +2521,8 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread assert(stack_end >= stack_addr); const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end)); + thread_ptr.data.stack_addr = stack_addr; + thread_ptr.data.stack_len = mmap_len; if (builtin.os == builtin.Os.windows) { // use windows API directly @@ -2533,10 +2535,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread // align to page stack_end -= stack_end % os.page_size; - assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_len) == 0); - - thread_ptr.data.stack_addr = stack_addr; - thread_ptr.data.stack_len = stack_len; + assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_end - stack_addr) == 0); const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); switch (err) { @@ -2552,7 +2551,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; const newtls: usize = 0; - const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid); + const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid); const err = posix.getErrno(rc); switch (err) { 0 => return thread_ptr, -- cgit v1.2.3 From 66aa760f83529cf932d35090adfa6fb84264ff7a Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Sun, 29 Apr 2018 14:03:55 +0300 Subject: More FnDef TypeInfo generation. --- src/codegen.cpp | 4 ++++ src/ir.cpp | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 414a34d5cb..507b9afe8b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6500,6 +6500,10 @@ static void define_builtin_compile_vars(CodeGen *g) { " fn_type: type,\n" " inline_type: Inline,\n" " calling_convention: CallingConvention,\n" + " is_var_args: bool,\n" + " is_extern: bool,\n" + " is_export: bool,\n" + " lib_name: ?[]const u8,\n" "\n" " const Inline = enum {\n" " Auto,\n" diff --git a/src/ir.cpp b/src/ir.cpp index 8fadc7f7a3..e3add4d612 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15920,7 +15920,7 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2]; // @TODO Add fields - ConstExprValue *fn_def_fields = create_const_vals(3); + ConstExprValue *fn_def_fields = create_const_vals(7); fn_def_val->data.x_struct.fields = fn_def_fields; // fn_type: type @@ -15938,6 +15938,40 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop fn_def_fields[2].special = ConstValSpecialStatic; fn_def_fields[2].type = ir_type_info_get_type(ira, "CallingConvention"); bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc); + // is_var_args: bool + ensure_field_index(fn_def_val->type, "is_var_args", 3); + fn_def_fields[3].special = ConstValSpecialStatic; + fn_def_fields[3].type = ira->codegen->builtin_types.entry_bool; + fn_def_fields[3].data.x_bool = fn_node->is_var_args; + // is_extern: bool + ensure_field_index(fn_def_val->type, "is_extern", 4); + fn_def_fields[4].special = ConstValSpecialStatic; + fn_def_fields[4].type = ira->codegen->builtin_types.entry_bool; + fn_def_fields[4].data.x_bool = fn_node->is_extern; + // is_export: bool + ensure_field_index(fn_def_val->type, "is_export", 5); + fn_def_fields[5].special = ConstValSpecialStatic; + fn_def_fields[5].type = ira->codegen->builtin_types.entry_bool; + fn_def_fields[5].data.x_bool = fn_node->is_export; + // lib_name: ?[]const u8 + ensure_field_index(fn_def_val->type, "lib_name", 6); + fn_def_fields[6].special = ConstValSpecialStatic; + fn_def_fields[6].type = get_maybe_type(ira->codegen, + get_slice_type(ira->codegen, get_pointer_to_type(ira->codegen, + ira->codegen->builtin_types.entry_u8, true))); + + + if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) + { + fn_def_fields[6].data.x_maybe = create_const_vals(1); + // @TODO Figure out if lib_name is always non-null for extern fns. + ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); + init_const_slice(ira->codegen, fn_def_fields[6].data.x_maybe, lib_name, 0, buf_len(fn_node->lib_name), true); + } + else + { + fn_def_fields[6].data.x_maybe = nullptr; + } inner_fields[2].data.x_union.payload = fn_def_val; break; -- cgit v1.2.3 From 013f548202ae1ffb584c211a0ea2cea53b745583 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Sun, 29 Apr 2018 15:40:26 +0300 Subject: Finished FnDef TypeInfo generation (warning: may be buggy). --- src/codegen.cpp | 8 +++++--- src/ir.cpp | 47 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 507b9afe8b..db69708e9a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6491,12 +6491,12 @@ static void define_builtin_compile_vars(CodeGen *g) { " is_pub: bool,\n" " data: Data,\n" "\n" - " const Data = union(enum) {\n" + " pub const Data = union(enum) {\n" " Type: type,\n" " Var: type,\n" " Fn: FnDef,\n" "\n" - " const FnDef = struct {\n" + " pub const FnDef = struct {\n" " fn_type: type,\n" " inline_type: Inline,\n" " calling_convention: CallingConvention,\n" @@ -6504,8 +6504,10 @@ static void define_builtin_compile_vars(CodeGen *g) { " is_extern: bool,\n" " is_export: bool,\n" " lib_name: ?[]const u8,\n" + " return_type: type,\n" + " arg_names: [][] const u8,\n" "\n" - " const Inline = enum {\n" + " pub const Inline = enum {\n" " Auto,\n" " Always,\n" " Never,\n" diff --git a/src/ir.cpp b/src/ir.cpp index e3add4d612..3bce9c51cd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15911,6 +15911,10 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; assert(!fn_entry->is_test); + analyze_fn_body(ira->codegen, fn_entry); + if (fn_entry->anal_state == FnAnalStateInvalid) + return; + AstNodeFnProto *fn_node = (AstNodeFnProto *)(fn_entry->proto_node); ConstExprValue *fn_def_val = create_const_vals(1); @@ -15919,8 +15923,7 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop fn_def_val->data.x_struct.parent.id = ConstParentIdUnion; fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2]; - // @TODO Add fields - ConstExprValue *fn_def_fields = create_const_vals(7); + ConstExprValue *fn_def_fields = create_const_vals(9); fn_def_val->data.x_struct.fields = fn_def_fields; // fn_type: type @@ -15940,9 +15943,10 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc); // is_var_args: bool ensure_field_index(fn_def_val->type, "is_var_args", 3); + bool is_varargs = fn_node->is_var_args; fn_def_fields[3].special = ConstValSpecialStatic; fn_def_fields[3].type = ira->codegen->builtin_types.entry_bool; - fn_def_fields[3].data.x_bool = fn_node->is_var_args; + fn_def_fields[3].data.x_bool = is_varargs; // is_extern: bool ensure_field_index(fn_def_val->type, "is_extern", 4); fn_def_fields[4].special = ConstValSpecialStatic; @@ -15959,18 +15963,47 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true))); - - if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { fn_def_fields[6].data.x_maybe = create_const_vals(1); - // @TODO Figure out if lib_name is always non-null for extern fns. ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); init_const_slice(ira->codegen, fn_def_fields[6].data.x_maybe, lib_name, 0, buf_len(fn_node->lib_name), true); } else - { fn_def_fields[6].data.x_maybe = nullptr; + // return_type: type + ensure_field_index(fn_def_val->type, "return_type", 7); + fn_def_fields[7].special = ConstValSpecialStatic; + fn_def_fields[7].type = ira->codegen->builtin_types.entry_type; + // @TODO Check whether this is correct. + if (fn_entry->src_implicit_return_type != nullptr) + fn_def_fields[7].data.x_type = fn_entry->src_implicit_return_type; + else if (fn_entry->type_entry->data.fn.gen_return_type != nullptr) + fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.gen_return_type; + else + fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; + // arg_names: [][] const u8 + ensure_field_index(fn_def_val->type, "arg_names", 8); + size_t fn_arg_count = fn_entry->variable_list.length; + ConstExprValue *fn_arg_name_array = create_const_vals(1); + fn_arg_name_array->special = ConstValSpecialStatic; + fn_arg_name_array->type = get_array_type(ira->codegen, get_slice_type(ira->codegen, + get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true)), fn_arg_count); + fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; + fn_arg_name_array->data.x_array.s_none.parent.id = ConstParentIdNone; + fn_arg_name_array->data.x_array.s_none.elements = create_const_vals(fn_arg_count); + + init_const_slice(ira->codegen, &fn_def_fields[8], fn_arg_name_array, 0, fn_arg_count, false); + + for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) + { + VariableTableEntry *arg_var = fn_entry->variable_list.at(fn_arg_index); + ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.s_none.elements[fn_arg_index]; + ConstExprValue *arg_name = create_const_str_lit(ira->codegen, &arg_var->name); + init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, buf_len(&arg_var->name), true); + fn_arg_name_val->data.x_struct.parent.id = ConstParentIdArray; + fn_arg_name_val->data.x_struct.parent.data.p_array.array_val = fn_arg_name_array; + fn_arg_name_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; } inner_fields[2].data.x_union.payload = fn_def_val; -- cgit v1.2.3 From b7095912c77900eab6ad667a6eeb1add18ac8071 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 15:48:53 -0400 Subject: zig fmt: respect comments before statements --- std/mem.zig | 18 +++- std/zig/ast.zig | 4 +- std/zig/parser.zig | 291 ++++++++++++++++++++++++++++++----------------------- 3 files changed, 180 insertions(+), 133 deletions(-) diff --git a/std/mem.zig b/std/mem.zig index 0f66f549cc..d874f8a6c9 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -37,6 +37,20 @@ pub const Allocator = struct { return &slice[0]; } + // TODO once #733 is solved, this will replace create + fn construct(self: &Allocator, init: var) t: { + // TODO this is a workaround for type getting parsed as Error!&const T + const T = @typeOf(init).Child; + break :t Error!&T; + } { + const T = @typeOf(init).Child; + if (@sizeOf(T) == 0) return &{}; + const slice = try self.alloc(T, 1); + const ptr = &slice[0]; + *ptr = *init; + return ptr; + } + fn destroy(self: &Allocator, ptr: var) void { self.free(ptr[0..1]); } @@ -54,7 +68,7 @@ pub const Allocator = struct { const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; const byte_slice = try self.allocFn(self, byte_count, alignment); assert(byte_slice.len == byte_count); - // This loop should get optimized out in ReleaseFast mode + // This loop gets optimized out in ReleaseFast mode for (byte_slice) |*byte| { *byte = undefined; } @@ -81,7 +95,7 @@ pub const Allocator = struct { const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment); assert(byte_slice.len == byte_count); if (n > old_mem.len) { - // This loop should get optimized out in ReleaseFast mode + // This loop gets optimized out in ReleaseFast mode for (byte_slice[old_byte_slice.len..]) |*byte| { *byte = undefined; } diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 76977a979a..66c0455e8c 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -6,6 +6,7 @@ const mem = std.mem; pub const Node = struct { id: Id, + comments: ?&LineComment, pub const Id = enum { // Top level @@ -139,7 +140,6 @@ pub const Node = struct { pub const VarDecl = struct { base: Node, - comments: ?&LineComment, visib_token: ?Token, name_token: Token, eq_token: Token, @@ -421,7 +421,6 @@ pub const Node = struct { pub const FnProto = struct { base: Node, - comments: ?&LineComment, visib_token: ?Token, fn_token: Token, name_token: ?Token, @@ -1732,7 +1731,6 @@ pub const Node = struct { pub const TestDecl = struct { base: Node, - comments: ?&LineComment, test_token: Token, name: &Node, body_node: &Node, diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 7f45cce28b..b5fba6a9b9 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -182,6 +182,11 @@ pub const Parser = struct { } }; + const AddCommentsCtx = struct { + node_ptr: &&ast.Node, + comments: ?&ast.Node.LineComment, + }; + const State = union(enum) { TopLevel, TopLevelExtern: TopLevelDeclCtx, @@ -221,6 +226,7 @@ pub const Parser = struct { Statement: &ast.Node.Block, ComptimeStatement: ComptimeStatementCtx, Semicolon: &&ast.Node, + AddComments: AddCommentsCtx, AsmOutputItems: &ArrayList(&ast.Node.AsmOutput), AsmOutputReturnOrType: &ast.Node.AsmOutput, @@ -345,24 +351,26 @@ pub const Parser = struct { Token.Id.Keyword_test => { stack.append(State.TopLevel) catch unreachable; - const block = try self.createNode(arena, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = null, - .lbrace = undefined, - .statements = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, - } - ); - const test_node = try self.createAttachNode(arena, &root_node.decls, ast.Node.TestDecl, - ast.Node.TestDecl { - .base = undefined, + const block = try arena.construct(ast.Node.Block { + .base = ast.Node { + .id = ast.Node.Id.Block, + .comments = null, + }, + .label = null, + .lbrace = undefined, + .statements = ArrayList(&ast.Node).init(arena), + .rbrace = undefined, + }); + const test_node = try arena.construct(ast.Node.TestDecl { + .base = ast.Node { + .id = ast.Node.Id.TestDecl, .comments = comments, - .test_token = token, - .name = undefined, - .body_node = &block.base, - } - ); + }, + .test_token = token, + .name = undefined, + .body_node = &block.base, + }); + try root_node.decls.append(&test_node.base); stack.append(State { .Block = block }) catch unreachable; try stack.append(State { .ExpectTokenSave = ExpectTokenSave { @@ -530,24 +538,25 @@ pub const Parser = struct { }, Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { - const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.Node.FnProto, - ast.Node.FnProto { - .base = undefined, + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, .comments = comments, - .visib_token = ctx.visib_token, - .name_token = null, - .fn_token = undefined, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = ctx.extern_export_inline_token, - .cc_token = null, - .async_attr = null, - .body_node = null, - .lib_name = ctx.lib_name, - .align_expr = null, - } - ); + }, + .visib_token = ctx.visib_token, + .name_token = null, + .fn_token = undefined, + .params = ArrayList(&ast.Node).init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = ctx.extern_export_inline_token, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = ctx.lib_name, + .align_expr = null, + }); + try ctx.decls.append(&fn_proto.base); stack.append(State { .FnDef = fn_proto }) catch unreachable; try stack.append(State { .FnProto = fn_proto }); @@ -789,24 +798,25 @@ pub const Parser = struct { State.VarDecl => |ctx| { - const var_decl = try self.createAttachNode(arena, ctx.list, ast.Node.VarDecl, - ast.Node.VarDecl { - .base = undefined, + const var_decl = try arena.construct(ast.Node.VarDecl { + .base = ast.Node { + .id = ast.Node.Id.VarDecl, .comments = ctx.comments, - .visib_token = ctx.visib_token, - .mut_token = ctx.mut_token, - .comptime_token = ctx.comptime_token, - .extern_export_token = ctx.extern_export_token, - .type_node = null, - .align_node = null, - .init_node = null, - .lib_name = ctx.lib_name, - // initialized later - .name_token = undefined, - .eq_token = undefined, - .semicolon_token = undefined, - } - ); + }, + .visib_token = ctx.visib_token, + .mut_token = ctx.mut_token, + .comptime_token = ctx.comptime_token, + .extern_export_token = ctx.extern_export_token, + .type_node = null, + .align_node = null, + .init_node = null, + .lib_name = ctx.lib_name, + // initialized later + .name_token = undefined, + .eq_token = undefined, + .semicolon_token = undefined, + }); + try ctx.list.append(&var_decl.base); stack.append(State { .VarDeclAlign = var_decl }) catch unreachable; try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); @@ -1218,19 +1228,23 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { - const node = try self.createAttachNode(arena, &block.statements, ast.Node.Defer, - ast.Node.Defer { - .base = undefined, - .defer_token = token, - .kind = switch (token.id) { - Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, - Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, - else => unreachable, - }, - .expr = undefined, - } - ); - stack.append(State { .Semicolon = &&node.base }) catch unreachable; + const node = try arena.construct(ast.Node.Defer { + .base = ast.Node { + .id = ast.Node.Id.Defer, + .comments = comments, + }, + .defer_token = token, + .kind = switch (token.id) { + Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, + Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, + else => unreachable, + }, + .expr = undefined, + }); + const node_ptr = try block.statements.addOne(); + *node_ptr = &node.base; + + stack.append(State { .Semicolon = node_ptr }) catch unreachable; try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); continue; }, @@ -1249,9 +1263,13 @@ pub const Parser = struct { }, else => { self.putBackToken(token); - const statememt = try block.statements.addOne(); - stack.append(State { .Semicolon = statememt }) catch unreachable; - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statememt } }); + const statement = try block.statements.addOne(); + stack.append(State { .Semicolon = statement }) catch unreachable; + try stack.append(State { .AddComments = AddCommentsCtx { + .node_ptr = statement, + .comments = comments, + }}); + try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); continue; } } @@ -1293,6 +1311,12 @@ pub const Parser = struct { continue; }, + State.AddComments => |add_comments_ctx| { + const node = *add_comments_ctx.node_ptr; + node.comments = add_comments_ctx.comments; + continue; + }, + State.AsmOutputItems => |items| { const lbracket = self.getNextToken(); @@ -1576,24 +1600,25 @@ pub const Parser = struct { State.ExternType => |ctx| { if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| { - const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.FnProto, - ast.Node.FnProto { - .base = undefined, + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, .comments = ctx.comments, - .visib_token = null, - .name_token = null, - .fn_token = fn_token, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = ctx.extern_token, - .cc_token = null, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - } - ); + }, + .visib_token = null, + .name_token = null, + .fn_token = fn_token, + .params = ArrayList(&ast.Node).init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = ctx.extern_token, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + }); + ctx.opt_ctx.store(&fn_proto.base); stack.append(State { .FnProto = fn_proto }) catch unreachable; continue; } @@ -2546,46 +2571,48 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_fn => { - const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto, - ast.Node.FnProto { - .base = undefined, + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, .comments = null, - .visib_token = null, - .name_token = null, - .fn_token = token, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = null, - .cc_token = null, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - } - ); + }, + .visib_token = null, + .name_token = null, + .fn_token = token, + .params = ArrayList(&ast.Node).init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = null, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + }); + opt_ctx.store(&fn_proto.base); stack.append(State { .FnProto = fn_proto }) catch unreachable; continue; }, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto, - ast.Node.FnProto { - .base = undefined, + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, .comments = null, - .visib_token = null, - .name_token = null, - .fn_token = undefined, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = null, - .cc_token = token, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - } - ); + }, + .visib_token = null, + .name_token = null, + .fn_token = undefined, + .params = ArrayList(&ast.Node).init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = null, + .cc_token = token, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + }); + opt_ctx.store(&fn_proto.base); stack.append(State { .FnProto = fn_proto }) catch unreachable; try stack.append(State { .ExpectTokenSave = ExpectTokenSave { @@ -2747,13 +2774,13 @@ pub const Parser = struct { if (result) |comment_node| { break :blk comment_node; } else { - const comment_node = try arena.create(ast.Node.LineComment); - *comment_node = ast.Node.LineComment { + const comment_node = try arena.construct(ast.Node.LineComment { .base = ast.Node { .id = ast.Node.Id.LineComment, + .comments = null, }, .lines = ArrayList(Token).init(arena), - }; + }); result = comment_node; break :blk comment_node; } @@ -3096,7 +3123,7 @@ pub const Parser = struct { *node = *init_to; node.base = blk: { const id = ast.Node.typeToId(T); - break :blk ast.Node {.id = id}; + break :blk ast.Node {.id = id, .comments = null}; }; return node; @@ -3269,7 +3296,7 @@ pub const Parser = struct { switch (decl.id) { ast.Node.Id.FnProto => { const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); - try self.renderComments(stream, fn_proto, indent); + try self.renderComments(stream, &fn_proto.base, indent); if (fn_proto.body_node) |body_node| { stack.append(RenderState { .Expression = body_node}) catch unreachable; @@ -3295,7 +3322,7 @@ pub const Parser = struct { }, ast.Node.Id.TestDecl => { const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); - try self.renderComments(stream, test_decl, indent); + try self.renderComments(stream, &test_decl.base, indent); try stream.print("test "); try stack.append(RenderState { .Expression = test_decl.body_node }); try stack.append(RenderState { .Text = " " }); @@ -3338,7 +3365,6 @@ pub const Parser = struct { }, RenderState.FieldInitializer => |field_init| { - //TODO try self.renderComments(stream, field_init, indent); try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token)); try stream.print(" = "); try stack.append(RenderState { .Expression = field_init.expr }); @@ -3385,7 +3411,6 @@ pub const Parser = struct { RenderState.ParamDecl => |base| { const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); - // TODO try self.renderComments(stream, param_decl, indent); if (param_decl.comptime_token) |comptime_token| { try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token)); } @@ -4328,10 +4353,10 @@ pub const Parser = struct { ast.Node.Id.ParamDecl => unreachable, }, RenderState.Statement => |base| { + try self.renderComments(stream, base, indent); switch (base.id) { ast.Node.Id.VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); - try self.renderComments(stream, var_decl, indent); try stack.append(RenderState { .VarDecl = var_decl}); }, else => { @@ -4348,7 +4373,7 @@ pub const Parser = struct { } } - fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void { + fn renderComments(self: &Parser, stream: var, node: &ast.Node, indent: usize) !void { const comment = node.comments ?? return; for (comment.lines.toSliceConst()) |line_token| { try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token)); @@ -4430,6 +4455,16 @@ fn testCanonical(source: []const u8) !void { } } +test "zig fmt: preserve comments before statements" { + try testCanonical( + \\test "std" { + \\ // statement comment + \\ _ = @import("foo/bar.zig"); + \\} + \\ + ); +} + test "zig fmt: preserve top level comments" { try testCanonical( \\// top level comment -- cgit v1.2.3 From 5e5eceb0de4e0afb3f16abb453214aa171c630af Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 15:50:56 -0400 Subject: fix bootstrap_lib for windows --- std/special/bootstrap_lib.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/special/bootstrap_lib.zig b/std/special/bootstrap_lib.zig index 40b6588838..a7aede28ec 100644 --- a/std/special/bootstrap_lib.zig +++ b/std/special/bootstrap_lib.zig @@ -3,7 +3,7 @@ const std = @import("std"); comptime { - @export("_DllMainCRTStartup", _DllMainCRTStartup); + @export("_DllMainCRTStartup", _DllMainCRTStartup, builtin.GlobalLinkage.Strong); } stdcallcc fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD, -- cgit v1.2.3 From a0e9f1e0c3ba3d5e240492723ff1aec8f6b2ba50 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 15:51:23 -0400 Subject: fix bootstrap_lib for windows, take 2 --- std/special/bootstrap_lib.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/std/special/bootstrap_lib.zig b/std/special/bootstrap_lib.zig index a7aede28ec..f55aaed96a 100644 --- a/std/special/bootstrap_lib.zig +++ b/std/special/bootstrap_lib.zig @@ -1,6 +1,7 @@ // This file is included in the compilation unit when exporting a library on windows. const std = @import("std"); +const builtin = @import("builtin"); comptime { @export("_DllMainCRTStartup", _DllMainCRTStartup, builtin.GlobalLinkage.Strong); -- cgit v1.2.3 From ad4ee47d9fec15945d445f637987d487405e7b22 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 16:24:12 -0400 Subject: zig fmt: preserve comments before global variables --- std/zig/parser.zig | 93 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index b5fba6a9b9..8b9dee59f7 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -55,6 +55,7 @@ pub const Parser = struct { visib_token: ?Token, extern_export_inline_token: ?Token, lib_name: ?&ast.Node, + comments: ?&ast.Node.LineComment, }; const VarDeclCtx = struct { @@ -70,6 +71,7 @@ pub const Parser = struct { const TopLevelExternOrFieldCtx = struct { visib_token: Token, container_decl: &ast.Node.ContainerDecl, + comments: ?&ast.Node.LineComment, }; const ExternTypeCtx = struct { @@ -393,6 +395,7 @@ pub const Parser = struct { .visib_token = token, .extern_export_inline_token = null, .lib_name = null, + .comments = comments, } }); continue; @@ -433,6 +436,7 @@ pub const Parser = struct { .visib_token = null, .extern_export_inline_token = null, .lib_name = null, + .comments = comments, } }); continue; @@ -449,6 +453,7 @@ pub const Parser = struct { .visib_token = ctx.visib_token, .extern_export_inline_token = token, .lib_name = null, + .comments = ctx.comments, }, }) catch unreachable; continue; @@ -460,6 +465,7 @@ pub const Parser = struct { .visib_token = ctx.visib_token, .extern_export_inline_token = token, .lib_name = null, + .comments = ctx.comments, }, }) catch unreachable; continue; @@ -486,12 +492,12 @@ pub const Parser = struct { .visib_token = ctx.visib_token, .extern_export_inline_token = ctx.extern_export_inline_token, .lib_name = lib_name, + .comments = ctx.comments, }, }) catch unreachable; continue; }, State.TopLevelDecl => |ctx| { - const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_use => { @@ -525,7 +531,7 @@ pub const Parser = struct { stack.append(State { .VarDecl = VarDeclCtx { - .comments = comments, + .comments = ctx.comments, .visib_token = ctx.visib_token, .lib_name = ctx.lib_name, .comptime_token = null, @@ -541,7 +547,7 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .comments = comments, + .comments = ctx.comments, }, .visib_token = ctx.visib_token, .name_token = null, @@ -628,6 +634,7 @@ pub const Parser = struct { .visib_token = ctx.visib_token, .extern_export_inline_token = null, .lib_name = null, + .comments = ctx.comments, } }); continue; @@ -746,6 +753,7 @@ pub const Parser = struct { .TopLevelExternOrField = TopLevelExternOrFieldCtx { .visib_token = token, .container_decl = container_decl, + .comments = null, } }); continue; @@ -758,6 +766,7 @@ pub const Parser = struct { .visib_token = token, .extern_export_inline_token = null, .lib_name = null, + .comments = null, } }); continue; @@ -772,6 +781,7 @@ pub const Parser = struct { .visib_token = token, .extern_export_inline_token = null, .lib_name = null, + .comments = null, } }); continue; @@ -789,6 +799,7 @@ pub const Parser = struct { .visib_token = null, .extern_export_inline_token = null, .lib_name = null, + .comments = null, } }); continue; @@ -3318,6 +3329,7 @@ pub const Parser = struct { }, ast.Node.Id.VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); + try self.renderComments(stream, &var_decl.base, indent); try stack.append(RenderState { .VarDecl = var_decl}); }, ast.Node.Id.TestDecl => { @@ -3827,41 +3839,45 @@ pub const Parser = struct { ast.Node.ContainerDecl.Kind.Union => try stream.print("union"), } - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n"}); - const fields_and_decls = container_decl.fields_and_decls.toSliceConst(); - var i = fields_and_decls.len; - while (i != 0) { - i -= 1; - const node = fields_and_decls[i]; - switch (node.id) { - ast.Node.Id.StructField, - ast.Node.Id.UnionTag, - ast.Node.Id.EnumTag => { - try stack.append(RenderState { .Text = "," }); - }, - else => { } - } - try stack.append(RenderState { .TopLevelDecl = node}); + if (fields_and_decls.len == 0) { + try stack.append(RenderState { .Text = "{}"}); + } else { + try stack.append(RenderState { .Text = "}"}); try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = fields_and_decls[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; + try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Text = "\n"}); + + var i = fields_and_decls.len; + while (i != 0) { + i -= 1; + const node = fields_and_decls[i]; + switch (node.id) { + ast.Node.Id.StructField, + ast.Node.Id.UnionTag, + ast.Node.Id.EnumTag => { + try stack.append(RenderState { .Text = "," }); + }, + else => { } + } + try stack.append(RenderState { .TopLevelDecl = node}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { + .Text = blk: { + if (i != 0) { + const prev_node = fields_and_decls[i - 1]; + const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } } - } - break :blk "\n"; - }, - }); + break :blk "\n"; + }, + }); + } + try stack.append(RenderState { .Indent = indent + indent_delta}); + try stack.append(RenderState { .Text = "{"}); } - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = "{"}); switch (container_decl.init_arg_expr) { ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}), @@ -4455,6 +4471,15 @@ fn testCanonical(source: []const u8) !void { } } +test "zig fmt: preserve comments before global variables" { + try testCanonical( + \\/// Foo copies keys and values before they go into the map, and + \\/// frees them when they get removed. + \\pub const Foo = struct {}; + \\ + ); +} + test "zig fmt: preserve comments before statements" { try testCanonical( \\test "std" { -- cgit v1.2.3 From 2387292f204c59259fc64d7c960d201e808af5a9 Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Sun, 29 Apr 2018 17:28:11 -0400 Subject: move some checks around in utf8Encode logic to be more zig idiomatic --- std/unicode.zig | 79 +++++++++++++++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/std/unicode.zig b/std/unicode.zig index 7650f83c83..9548576785 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -1,12 +1,11 @@ const std = @import("./index.zig"); const debug = std.debug; -// Given a Utf8-Codepoint returns how many (1-4) -// bytes there are if represented as an array of bytes. +/// Returns how many bytes the UTF-8 representation would require +/// for the given codepoint. pub fn utf8CodepointSequenceLength(c: u32) !u3 { if (c < 0x80) return u3(1); if (c < 0x800) return u3(2); - if (c -% 0xd800 < 0x800) return error.InvalidCodepoint; if (c < 0x10000) return u3(3); if (c < 0x110000) return u3(4); return error.CodepointTooLarge; @@ -23,45 +22,39 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 { return error.Utf8InvalidStartByte; } -/// Encodes a code point back into utf8 -/// c: the code point -/// out: the out buffer to write to -/// Notes: out has to have a len big enough for the bytes -/// however this limit is dependent on the code point -/// but giving it a minimum of 4 will ensure it will work -/// for all code points. -/// Errors: Will return an error if the code point is invalid. +/// Encodes the given codepoint into a UTF-8 byte sequence. +/// c: the codepoint. +/// out: the out buffer to write to. Must have a len >= utf8CodepointSequenceLength(c). +/// Errors: if c cannot be encoded in UTF-8. +/// Returns: the number of bytes written to out. pub fn utf8Encode(c: u32, out: []u8) !u3 { - if (utf8CodepointSequenceLength(c)) |length| { - debug.assert(out.len >= length); - switch (length) { - // The pattern for each is the same - // - Increasing the initial shift by 6 each time - // - Each time after the first shorten the shifted - // value to a max of 0b111111 (63) - 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range - 2 => { - out[0] = u8(0b11000000 | (c >> 6)); - out[1] = u8(0b10000000 | (c & 0b111111)); - }, - 3 => { - out[0] = u8(0b11100000 | (c >> 12)); - out[1] = u8(0b10000000 | ((c >> 6) & 0b111111)); - out[2] = u8(0b10000000 | (c & 0b111111)); - }, - 4 => { - out[0] = u8(0b11110000 | (c >> 18)); - out[1] = u8(0b10000000 | ((c >> 12) & 0b111111)); - out[2] = u8(0b10000000 | ((c >> 6) & 0b111111)); - out[3] = u8(0b10000000 | (c & 0b111111)); - }, - else => unreachable, - } - - return length; - } else |err| { - return err; + const length = try utf8CodepointSequenceLength(c); + debug.assert(out.len >= length); + switch (length) { + // The pattern for each is the same + // - Increasing the initial shift by 6 each time + // - Each time after the first shorten the shifted + // value to a max of 0b111111 (63) + 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range + 2 => { + out[0] = u8(0b11000000 | (c >> 6)); + out[1] = u8(0b10000000 | (c & 0b111111)); + }, + 3 => { + if (0xd800 <= c and c <= 0xdfff) return error.Utf8CannotEncodeSurrogateHalf; + out[0] = u8(0b11100000 | (c >> 12)); + out[1] = u8(0b10000000 | ((c >> 6) & 0b111111)); + out[2] = u8(0b10000000 | (c & 0b111111)); + }, + 4 => { + out[0] = u8(0b11110000 | (c >> 18)); + out[1] = u8(0b10000000 | ((c >> 12) & 0b111111)); + out[2] = u8(0b10000000 | ((c >> 6) & 0b111111)); + out[3] = u8(0b10000000 | (c & 0b111111)); + }, + else => unreachable, } + return length; } /// Decodes the UTF-8 codepoint encoded in the given slice of bytes. @@ -249,8 +242,10 @@ test "utf8 encode" { test "utf8 encode error" { var array: [4]u8 = undefined; - testErrorEncode(0xFFFFFF, array[0..], error.CodepointTooLarge); - testErrorEncode(0xd900, array[0..], error.InvalidCodepoint); + testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf); + testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf); + testErrorEncode(0x110000, array[0..], error.CodepointTooLarge); + testErrorEncode(0xffffffff, array[0..], error.CodepointTooLarge); } fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void { -- cgit v1.2.3 From c03b9010db55e52dfa227b17e35203b93b5ee1df Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 17:37:02 -0400 Subject: zig fmt: preserve same-line comment after statement --- std/zig/ast.zig | 3 ++- std/zig/parser.zig | 79 +++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 68 insertions(+), 14 deletions(-) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 66c0455e8c..75c3696bfe 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -6,7 +6,8 @@ const mem = std.mem; pub const Node = struct { id: Id, - comments: ?&LineComment, + before_comments: ?&LineComment, + same_line_comment: ?&Token, pub const Id = enum { // Top level diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 8b9dee59f7..cfc880ff11 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -229,6 +229,7 @@ pub const Parser = struct { ComptimeStatement: ComptimeStatementCtx, Semicolon: &&ast.Node, AddComments: AddCommentsCtx, + LookForSameLineComment: &&ast.Node, AsmOutputItems: &ArrayList(&ast.Node.AsmOutput), AsmOutputReturnOrType: &ast.Node.AsmOutput, @@ -356,7 +357,8 @@ pub const Parser = struct { const block = try arena.construct(ast.Node.Block { .base = ast.Node { .id = ast.Node.Id.Block, - .comments = null, + .before_comments = null, + .same_line_comment = null, }, .label = null, .lbrace = undefined, @@ -366,7 +368,8 @@ pub const Parser = struct { const test_node = try arena.construct(ast.Node.TestDecl { .base = ast.Node { .id = ast.Node.Id.TestDecl, - .comments = comments, + .before_comments = comments, + .same_line_comment = null, }, .test_token = token, .name = undefined, @@ -547,7 +550,8 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .comments = ctx.comments, + .before_comments = ctx.comments, + .same_line_comment = null, }, .visib_token = ctx.visib_token, .name_token = null, @@ -812,7 +816,8 @@ pub const Parser = struct { const var_decl = try arena.construct(ast.Node.VarDecl { .base = ast.Node { .id = ast.Node.Id.VarDecl, - .comments = ctx.comments, + .before_comments = ctx.comments, + .same_line_comment = null, }, .visib_token = ctx.visib_token, .mut_token = ctx.mut_token, @@ -1242,7 +1247,8 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.Defer { .base = ast.Node { .id = ast.Node.Id.Defer, - .comments = comments, + .before_comments = comments, + .same_line_comment = null, }, .defer_token = token, .kind = switch (token.id) { @@ -1275,7 +1281,8 @@ pub const Parser = struct { else => { self.putBackToken(token); const statement = try block.statements.addOne(); - stack.append(State { .Semicolon = statement }) catch unreachable; + stack.append(State { .LookForSameLineComment = statement }) catch unreachable; + try stack.append(State { .Semicolon = statement }); try stack.append(State { .AddComments = AddCommentsCtx { .node_ptr = statement, .comments = comments, @@ -1324,7 +1331,28 @@ pub const Parser = struct { State.AddComments => |add_comments_ctx| { const node = *add_comments_ctx.node_ptr; - node.comments = add_comments_ctx.comments; + node.before_comments = add_comments_ctx.comments; + continue; + }, + + State.LookForSameLineComment => |node_ptr| { + const node = *node_ptr; + const node_last_token = node.lastToken(); + + const line_comment_token = self.getNextToken(); + if (line_comment_token.id != Token.Id.LineComment) { + self.putBackToken(line_comment_token); + continue; + } + + const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token); + const different_line = offset_loc.line != 0; + if (different_line) { + self.putBackToken(line_comment_token); + continue; + } + + node.same_line_comment = try arena.construct(line_comment_token); continue; }, @@ -1614,7 +1642,8 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .comments = ctx.comments, + .before_comments = ctx.comments, + .same_line_comment = null, }, .visib_token = null, .name_token = null, @@ -2585,7 +2614,8 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .comments = null, + .before_comments = null, + .same_line_comment = null, }, .visib_token = null, .name_token = null, @@ -2608,7 +2638,8 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .comments = null, + .before_comments = null, + .same_line_comment = null, }, .visib_token = null, .name_token = null, @@ -2788,7 +2819,8 @@ pub const Parser = struct { const comment_node = try arena.construct(ast.Node.LineComment { .base = ast.Node { .id = ast.Node.Id.LineComment, - .comments = null, + .before_comments = null, + .same_line_comment = null, }, .lines = ArrayList(Token).init(arena), }); @@ -3134,7 +3166,11 @@ pub const Parser = struct { *node = *init_to; node.base = blk: { const id = ast.Node.typeToId(T); - break :blk ast.Node {.id = id, .comments = null}; + break :blk ast.Node { + .id = id, + .before_comments = null, + .same_line_comment = null, + }; }; return node; @@ -3270,6 +3306,7 @@ pub const Parser = struct { FieldInitializer: &ast.Node.FieldInitializer, PrintIndent, Indent: usize, + PrintSameLineComment: ?&Token, }; pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { @@ -4370,6 +4407,7 @@ pub const Parser = struct { }, RenderState.Statement => |base| { try self.renderComments(stream, base, indent); + try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment } ); switch (base.id) { ast.Node.Id.VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); @@ -4385,12 +4423,16 @@ pub const Parser = struct { }, RenderState.Indent => |new_indent| indent = new_indent, RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent), + RenderState.PrintSameLineComment => |maybe_comment| blk: { + const comment_token = maybe_comment ?? break :blk; + try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token)); + }, } } } fn renderComments(self: &Parser, stream: var, node: &ast.Node, indent: usize) !void { - const comment = node.comments ?? return; + const comment = node.before_comments ?? return; for (comment.lines.toSliceConst()) |line_token| { try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token)); try stream.writeByteNTimes(' ', indent); @@ -4471,6 +4513,17 @@ fn testCanonical(source: []const u8) !void { } } +test "zig fmt: preserve same-line comment after a statement" { + try testCanonical( + \\test "" { + \\ a = b; + \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption + \\ a = b; + \\} + \\ + ); +} + test "zig fmt: preserve comments before global variables" { try testCanonical( \\/// Foo copies keys and values before they go into the map, and -- cgit v1.2.3 From 9543c0a7cc0bbdccc405370e224b546c56b76a0f Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Sun, 29 Apr 2018 17:38:41 -0400 Subject: use explicit error sets for utf8Decode functions and run unicode tests at comptime also --- std/unicode.zig | 77 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/std/unicode.zig b/std/unicode.zig index 9548576785..300e129647 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -57,11 +57,12 @@ pub fn utf8Encode(c: u32, out: []u8) !u3 { return length; } +const Utf8DecodeError = Utf8Decode2Error || Utf8Decode3Error || Utf8Decode4Error; /// Decodes the UTF-8 codepoint encoded in the given slice of bytes. /// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable. /// If you already know the length at comptime, you can call one of /// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function. -pub fn utf8Decode(bytes: []const u8) !u32 { +pub fn utf8Decode(bytes: []const u8) Utf8DecodeError!u32 { return switch (bytes.len) { 1 => u32(bytes[0]), 2 => utf8Decode2(bytes), @@ -71,7 +72,11 @@ pub fn utf8Decode(bytes: []const u8) !u32 { }; } -pub fn utf8Decode2(bytes: []const u8) !u32 { +const Utf8Decode2Error = error{ + Utf8ExpectedContinuation, + Utf8OverlongEncoding, +}; +pub fn utf8Decode2(bytes: []const u8) Utf8Decode2Error!u32 { debug.assert(bytes.len == 2); debug.assert(bytes[0] & 0b11100000 == 0b11000000); var value: u32 = bytes[0] & 0b00011111; @@ -85,7 +90,12 @@ pub fn utf8Decode2(bytes: []const u8) !u32 { return value; } -pub fn utf8Decode3(bytes: []const u8) !u32 { +const Utf8Decode3Error = error{ + Utf8ExpectedContinuation, + Utf8OverlongEncoding, + Utf8EncodesSurrogateHalf, +}; +pub fn utf8Decode3(bytes: []const u8) Utf8Decode3Error!u32 { debug.assert(bytes.len == 3); debug.assert(bytes[0] & 0b11110000 == 0b11100000); var value: u32 = bytes[0] & 0b00001111; @@ -104,7 +114,12 @@ pub fn utf8Decode3(bytes: []const u8) !u32 { return value; } -pub fn utf8Decode4(bytes: []const u8) !u32 { +const Utf8Decode4Error = error{ + Utf8ExpectedContinuation, + Utf8OverlongEncoding, + Utf8CodepointTooLarge, +}; +pub fn utf8Decode4(bytes: []const u8) Utf8Decode4Error!u32 { debug.assert(bytes.len == 4); debug.assert(bytes[0] & 0b11111000 == 0b11110000); var value: u32 = bytes[0] & 0b00000111; @@ -206,19 +221,21 @@ const Utf8Iterator = struct { pub fn nextCodepoint(it: &Utf8Iterator) ?u32 { const slice = it.nextCodepointSlice() ?? return null; - const r = switch (slice.len) { - 1 => u32(slice[0]), - 2 => utf8Decode2(slice), - 3 => utf8Decode3(slice), - 4 => utf8Decode4(slice), + switch (slice.len) { + 1 => return u32(slice[0]), + 2 => return utf8Decode2(slice) catch unreachable, + 3 => return utf8Decode3(slice) catch unreachable, + 4 => return utf8Decode4(slice) catch unreachable, else => unreachable, - }; - - return r catch unreachable; + } } }; test "utf8 encode" { + comptime testUtf8Encode() catch unreachable; + try testUtf8Encode(); +} +fn testUtf8Encode() !void { // A few taken from wikipedia a few taken elsewhere var array: [4]u8 = undefined; debug.assert((try utf8Encode(try utf8Decode("€"), array[0..])) == 3); @@ -241,6 +258,10 @@ test "utf8 encode" { } test "utf8 encode error" { + comptime testUtf8EncodeError(); + testUtf8EncodeError(); +} +fn testUtf8EncodeError() void { var array: [4]u8 = undefined; testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf); testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf); @@ -257,6 +278,10 @@ fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void { } test "utf8 iterator on ascii" { + comptime testUtf8IteratorOnAscii(); + testUtf8IteratorOnAscii(); +} +fn testUtf8IteratorOnAscii() void { const s = Utf8View.initComptime("abc"); var it1 = s.iterator(); @@ -273,6 +298,10 @@ test "utf8 iterator on ascii" { } test "utf8 view bad" { + comptime testUtf8ViewBad(); + testUtf8ViewBad(); +} +fn testUtf8ViewBad() void { // Compile-time error. // const s3 = Utf8View.initComptime("\xfe\xf2"); @@ -281,6 +310,10 @@ test "utf8 view bad" { } test "utf8 view ok" { + comptime testUtf8ViewOk(); + testUtf8ViewOk(); +} +fn testUtf8ViewOk() void { const s = Utf8View.initComptime("東京市"); var it1 = s.iterator(); @@ -297,6 +330,10 @@ test "utf8 view ok" { } test "bad utf8 slice" { + comptime testBadUtf8Slice(); + testBadUtf8Slice(); +} +fn testBadUtf8Slice() void { debug.assert(utf8ValidateSlice("abc")); debug.assert(!utf8ValidateSlice("abc\xc0")); debug.assert(!utf8ValidateSlice("abc\xc0abc")); @@ -304,6 +341,10 @@ test "bad utf8 slice" { } test "valid utf8" { + comptime testValidUtf8(); + testValidUtf8(); +} +fn testValidUtf8() void { testValid("\x00", 0x0); testValid("\x20", 0x20); testValid("\x7f", 0x7f); @@ -319,6 +360,10 @@ test "valid utf8" { } test "invalid utf8 continuation bytes" { + comptime testInvalidUtf8ContinuationBytes(); + testInvalidUtf8ContinuationBytes(); +} +fn testInvalidUtf8ContinuationBytes() void { // unexpected continuation testError("\x80", error.Utf8InvalidStartByte); testError("\xbf", error.Utf8InvalidStartByte); @@ -347,6 +392,10 @@ test "invalid utf8 continuation bytes" { } test "overlong utf8 codepoint" { + comptime testOverlongUtf8Codepoint(); + testOverlongUtf8Codepoint(); +} +fn testOverlongUtf8Codepoint() void { testError("\xc0\x80", error.Utf8OverlongEncoding); testError("\xc1\xbf", error.Utf8OverlongEncoding); testError("\xe0\x80\x80", error.Utf8OverlongEncoding); @@ -356,6 +405,10 @@ test "overlong utf8 codepoint" { } test "misc invalid utf8" { + comptime testMiscInvalidUtf8(); + testMiscInvalidUtf8(); +} +fn testMiscInvalidUtf8() void { // codepoint out of bounds testError("\xf4\x90\x80\x80", error.Utf8CodepointTooLarge); testError("\xf7\xbf\xbf\xbf", error.Utf8CodepointTooLarge); -- cgit v1.2.3 From 3fa0bed985b8de950859d4c482efe9cb30fdaf27 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 18:22:24 -0400 Subject: zig fmt: array literal with 1 item on 1 line --- std/zig/parser.zig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index cfc880ff11..6a6bd49f6e 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -3730,6 +3730,16 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = suffix_op.lhs }); continue; } + if (exprs.len == 1) { + const expr = exprs.at(0); + + try stack.append(RenderState { .Text = "}" }); + try stack.append(RenderState { .Expression = expr }); + try stack.append(RenderState { .Text = " {" }); + try stack.append(RenderState { .Expression = suffix_op.lhs }); + continue; + } + try stack.append(RenderState { .Text = "}"}); try stack.append(RenderState.PrintIndent); try stack.append(RenderState { .Indent = indent }); @@ -4513,6 +4523,13 @@ fn testCanonical(source: []const u8) !void { } } +test "zig fmt: array literal with 1 item on 1 line" { + try testCanonical( + \\var s = []const u64 {0} ** 25; + \\ + ); +} + test "zig fmt: preserve same-line comment after a statement" { try testCanonical( \\test "" { -- cgit v1.2.3 From 3235eb03f979cbcc38cbe8b69d55761079e6d864 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 19:23:19 -0400 Subject: zig fmt: preserve same line comment after struct field --- std/zig/parser.zig | 1069 +++-------------------------------------------- std/zig/parser_test.zig | 975 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1039 insertions(+), 1005 deletions(-) create mode 100644 std/zig/parser_test.zig diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 6a6bd49f6e..5f0928ee42 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -376,7 +376,7 @@ pub const Parser = struct { .body_node = &block.base, }); try root_node.decls.append(&test_node.base); - stack.append(State { .Block = block }) catch unreachable; + try stack.append(State { .Block = block }); try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.LBrace, @@ -616,14 +616,18 @@ pub const Parser = struct { State.TopLevelExternOrField => |ctx| { if (self.eatToken(Token.Id.Identifier)) |identifier| { std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct); - const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.Node.StructField, - ast.Node.StructField { - .base = undefined, - .visib_token = ctx.visib_token, - .name_token = identifier, - .type_expr = undefined, - } - ); + const node = try arena.construct(ast.Node.StructField { + .base = ast.Node { + .id = ast.Node.Id.StructField, + .before_comments = null, + .same_line_comment = null, + }, + .visib_token = ctx.visib_token, + .name_token = identifier, + .type_expr = undefined, + }); + const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); + *node_ptr = &node.base; stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } }); @@ -706,16 +710,20 @@ pub const Parser = struct { Token.Id.Identifier => { switch (container_decl.kind) { ast.Node.ContainerDecl.Kind.Struct => { - const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.StructField, - ast.Node.StructField { - .base = undefined, - .visib_token = null, - .name_token = token, - .type_expr = undefined, - } - ); + const node = try arena.construct(ast.Node.StructField { + .base = ast.Node { + .id = ast.Node.Id.StructField, + .before_comments = null, + .same_line_comment = null, + }, + .visib_token = null, + .name_token = token, + .type_expr = undefined, + }); + const node_ptr = try container_decl.fields_and_decls.addOne(); + *node_ptr = &node.base; - stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; + try stack.append(State { .FieldListCommaOrEnd = container_decl }); try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } }); try stack.append(State { .ExpectToken = Token.Id.Colon }); continue; @@ -1336,23 +1344,7 @@ pub const Parser = struct { }, State.LookForSameLineComment => |node_ptr| { - const node = *node_ptr; - const node_last_token = node.lastToken(); - - const line_comment_token = self.getNextToken(); - if (line_comment_token.id != Token.Id.LineComment) { - self.putBackToken(line_comment_token); - continue; - } - - const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token); - const different_line = offset_loc.line != 0; - if (different_line) { - self.putBackToken(line_comment_token); - continue; - } - - node.same_line_comment = try arena.construct(line_comment_token); + try self.lookForSameLineComment(arena, *node_ptr); continue; }, @@ -1463,14 +1455,16 @@ pub const Parser = struct { continue; } - const node = try self.createNode(arena, ast.Node.FieldInitializer, - ast.Node.FieldInitializer { - .base = undefined, - .period_token = undefined, - .name_token = undefined, - .expr = undefined, - } - ); + const node = try arena.construct(ast.Node.FieldInitializer { + .base = ast.Node { + .id = ast.Node.Id.FieldInitializer, + .before_comments = null, + .same_line_comment = null, + }, + .period_token = undefined, + .name_token = undefined, + .expr = undefined, + }); try list_state.list.append(node); stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; @@ -1504,7 +1498,8 @@ pub const Parser = struct { container_decl.rbrace_token = end; continue; } else { - stack.append(State { .ContainerDecl = container_decl }) catch unreachable; + try self.lookForSameLineComment(arena, container_decl.fields_and_decls.toSlice()[container_decl.fields_and_decls.len - 1]); + try stack.append(State { .ContainerDecl = container_decl }); continue; } }, @@ -2908,6 +2903,25 @@ pub const Parser = struct { } } + fn lookForSameLineComment(self: &Parser, arena: &mem.Allocator, node: &ast.Node) !void { + const node_last_token = node.lastToken(); + + const line_comment_token = self.getNextToken(); + if (line_comment_token.id != Token.Id.LineComment) { + self.putBackToken(line_comment_token); + return; + } + + const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token); + const different_line = offset_loc.line != 0; + if (different_line) { + self.putBackToken(line_comment_token); + return; + } + + node.same_line_comment = try arena.construct(line_comment_token); + } + fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node { switch (token.id) { Token.Id.StringLiteral => { @@ -3341,6 +3355,7 @@ pub const Parser = struct { while (stack.popOrNull()) |state| { switch (state) { RenderState.TopLevelDecl => |decl| { + try stack.append(RenderState { .PrintSameLineComment = decl.same_line_comment } ); switch (decl.id) { ast.Node.Id.FnProto => { const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); @@ -3383,12 +3398,14 @@ pub const Parser = struct { try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token)); } try stream.print("{}: ", self.tokenizer.getTokenSlice(field.name_token)); + try stack.append(RenderState { .Text = "," }); try stack.append(RenderState { .Expression = field.type_expr}); }, ast.Node.Id.UnionTag => { const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); + try stack.append(RenderState { .Text = "," }); if (tag.type_expr) |type_expr| { try stream.print(": "); try stack.append(RenderState { .Expression = type_expr}); @@ -3398,6 +3415,7 @@ pub const Parser = struct { const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); + try stack.append(RenderState { .Text = "," }); if (tag.value) |value| { try stream.print(" = "); try stack.append(RenderState { .Expression = value}); @@ -3899,14 +3917,6 @@ pub const Parser = struct { while (i != 0) { i -= 1; const node = fields_and_decls[i]; - switch (node.id) { - ast.Node.Id.StructField, - ast.Node.Id.UnionTag, - ast.Node.Id.EnumTag => { - try stack.append(RenderState { .Text = "," }); - }, - else => { } - } try stack.append(RenderState { .TopLevelDecl = node}); try stack.append(RenderState.PrintIndent); try stack.append(RenderState { @@ -4466,957 +4476,6 @@ pub const Parser = struct { }; -var fixed_buffer_mem: [100 * 1024]u8 = undefined; - -fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { - var tokenizer = Tokenizer.init(source); - var parser = Parser.init(&tokenizer, allocator, "(memory buffer)"); - defer parser.deinit(); - - var tree = try parser.parse(); - defer tree.deinit(); - - var buffer = try std.Buffer.initSize(allocator, 0); - errdefer buffer.deinit(); - - var buffer_out_stream = io.BufferOutStream.init(&buffer); - try parser.renderSource(&buffer_out_stream.stream, tree.root_node); - return buffer.toOwnedSlice(); -} - -fn testCanonical(source: []const u8) !void { - const needed_alloc_count = x: { - // Try it once with unlimited memory, make sure it works - var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize)); - const result_source = try testParse(source, &failing_allocator.allocator); - if (!mem.eql(u8, result_source, source)) { - warn("\n====== expected this output: =========\n"); - warn("{}", source); - warn("\n======== instead found this: =========\n"); - warn("{}", result_source); - warn("\n======================================\n"); - return error.TestFailed; - } - failing_allocator.allocator.free(result_source); - break :x failing_allocator.index; - }; - - var fail_index: usize = 0; - while (fail_index < needed_alloc_count) : (fail_index += 1) { - var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index); - if (testParse(source, &failing_allocator.allocator)) |_| { - return error.NondeterministicMemoryUsage; - } else |err| switch (err) { - error.OutOfMemory => { - if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) { - warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n", - fail_index, needed_alloc_count, - failing_allocator.allocated_bytes, failing_allocator.freed_bytes, - failing_allocator.index, failing_allocator.deallocations); - return error.MemoryLeakDetected; - } - }, - error.ParseError => @panic("test failed"), - } - } -} - -test "zig fmt: array literal with 1 item on 1 line" { - try testCanonical( - \\var s = []const u64 {0} ** 25; - \\ - ); -} - -test "zig fmt: preserve same-line comment after a statement" { - try testCanonical( - \\test "" { - \\ a = b; - \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption - \\ a = b; - \\} - \\ - ); -} - -test "zig fmt: preserve comments before global variables" { - try testCanonical( - \\/// Foo copies keys and values before they go into the map, and - \\/// frees them when they get removed. - \\pub const Foo = struct {}; - \\ - ); -} - -test "zig fmt: preserve comments before statements" { - try testCanonical( - \\test "std" { - \\ // statement comment - \\ _ = @import("foo/bar.zig"); - \\} - \\ - ); -} - -test "zig fmt: preserve top level comments" { - try testCanonical( - \\// top level comment - \\test "hi" {} - \\ - ); -} - -test "zig fmt: get stdout or fail" { - try testCanonical( - \\const std = @import("std"); - \\ - \\pub fn main() !void { - \\ // If this program is run without stdout attached, exit with an error. - \\ // another comment - \\ var stdout_file = try std.io.getStdOut; - \\} - \\ - ); -} - -test "zig fmt: preserve spacing" { - try testCanonical( - \\const std = @import("std"); - \\ - \\pub fn main() !void { - \\ var stdout_file = try std.io.getStdOut; - \\ var stdout_file = try std.io.getStdOut; - \\ - \\ var stdout_file = try std.io.getStdOut; - \\ var stdout_file = try std.io.getStdOut; - \\} - \\ - ); -} - -test "zig fmt: return types" { - try testCanonical( - \\pub fn main() !void {} - \\pub fn main() var {} - \\pub fn main() i32 {} - \\ - ); -} - -test "zig fmt: imports" { - try testCanonical( - \\const std = @import("std"); - \\const std = @import(); - \\ - ); -} - -test "zig fmt: global declarations" { - try testCanonical( - \\const a = b; - \\pub const a = b; - \\var a = b; - \\pub var a = b; - \\const a: i32 = b; - \\pub const a: i32 = b; - \\var a: i32 = b; - \\pub var a: i32 = b; - \\extern const a: i32 = b; - \\pub extern const a: i32 = b; - \\extern var a: i32 = b; - \\pub extern var a: i32 = b; - \\extern "a" const a: i32 = b; - \\pub extern "a" const a: i32 = b; - \\extern "a" var a: i32 = b; - \\pub extern "a" var a: i32 = b; - \\ - ); -} - -test "zig fmt: extern declaration" { - try testCanonical( - \\extern var foo: c_int; - \\ - ); -} - -test "zig fmt: alignment" { - try testCanonical( - \\var foo: c_int align(1); - \\ - ); -} - -test "zig fmt: C main" { - try testCanonical( - \\fn main(argc: c_int, argv: &&u8) c_int { - \\ const a = b; - \\} - \\ - ); -} - -test "zig fmt: return" { - try testCanonical( - \\fn foo(argc: c_int, argv: &&u8) c_int { - \\ return 0; - \\} - \\ - \\fn bar() void { - \\ return; - \\} - \\ - ); -} - -test "zig fmt: pointer attributes" { - try testCanonical( - \\extern fn f1(s: &align(&u8) u8) c_int; - \\extern fn f2(s: &&align(1) &const &volatile u8) c_int; - \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int; - \\extern fn f4(s: &align(1) const volatile u8) c_int; - \\ - ); -} - -test "zig fmt: slice attributes" { - try testCanonical( - \\extern fn f1(s: &align(&u8) u8) c_int; - \\extern fn f2(s: &&align(1) &const &volatile u8) c_int; - \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int; - \\extern fn f4(s: &align(1) const volatile u8) c_int; - \\ - ); -} - -test "zig fmt: test declaration" { - try testCanonical( - \\test "test name" { - \\ const a = 1; - \\ var b = 1; - \\} - \\ - ); -} - -test "zig fmt: infix operators" { - try testCanonical( - \\test "infix operators" { - \\ var i = undefined; - \\ i = 2; - \\ i *= 2; - \\ i |= 2; - \\ i ^= 2; - \\ i <<= 2; - \\ i >>= 2; - \\ i &= 2; - \\ i *= 2; - \\ i *%= 2; - \\ i -= 2; - \\ i -%= 2; - \\ i += 2; - \\ i +%= 2; - \\ i /= 2; - \\ i %= 2; - \\ _ = i == i; - \\ _ = i != i; - \\ _ = i != i; - \\ _ = i.i; - \\ _ = i || i; - \\ _ = i!i; - \\ _ = i ** i; - \\ _ = i ++ i; - \\ _ = i ?? i; - \\ _ = i % i; - \\ _ = i / i; - \\ _ = i *% i; - \\ _ = i * i; - \\ _ = i -% i; - \\ _ = i - i; - \\ _ = i +% i; - \\ _ = i + i; - \\ _ = i << i; - \\ _ = i >> i; - \\ _ = i & i; - \\ _ = i ^ i; - \\ _ = i | i; - \\ _ = i >= i; - \\ _ = i <= i; - \\ _ = i > i; - \\ _ = i < i; - \\ _ = i and i; - \\ _ = i or i; - \\} - \\ - ); -} - -test "zig fmt: precedence" { - try testCanonical( - \\test "precedence" { - \\ a!b(); - \\ (a!b)(); - \\ !a!b; - \\ !(a!b); - \\ !a{}; - \\ !(a{}); - \\ a + b{}; - \\ (a + b){}; - \\ a << b + c; - \\ (a << b) + c; - \\ a & b << c; - \\ (a & b) << c; - \\ a ^ b & c; - \\ (a ^ b) & c; - \\ a | b ^ c; - \\ (a | b) ^ c; - \\ a == b | c; - \\ (a == b) | c; - \\ a and b == c; - \\ (a and b) == c; - \\ a or b and c; - \\ (a or b) and c; - \\ (a or b) and c; - \\} - \\ - ); -} - -test "zig fmt: prefix operators" { - try testCanonical( - \\test "prefix operators" { - \\ try return --%~??!*&0; - \\} - \\ - ); -} - -test "zig fmt: call expression" { - try testCanonical( - \\test "test calls" { - \\ a(); - \\ a(1); - \\ a(1, 2); - \\ a(1, 2) + a(1, 2); - \\} - \\ - ); -} - -test "zig fmt: var args" { - try testCanonical( - \\fn print(args: ...) void {} - \\ - ); -} - -test "zig fmt: var type" { - try testCanonical( - \\fn print(args: var) var {} - \\const Var = var; - \\const i: var = 0; - \\ - ); -} - -test "zig fmt: functions" { - try testCanonical( - \\extern fn puts(s: &const u8) c_int; - \\extern "c" fn puts(s: &const u8) c_int; - \\export fn puts(s: &const u8) c_int; - \\inline fn puts(s: &const u8) c_int; - \\pub extern fn puts(s: &const u8) c_int; - \\pub extern "c" fn puts(s: &const u8) c_int; - \\pub export fn puts(s: &const u8) c_int; - \\pub inline fn puts(s: &const u8) c_int; - \\pub extern fn puts(s: &const u8) align(2 + 2) c_int; - \\pub extern "c" fn puts(s: &const u8) align(2 + 2) c_int; - \\pub export fn puts(s: &const u8) align(2 + 2) c_int; - \\pub inline fn puts(s: &const u8) align(2 + 2) c_int; - \\ - ); -} - -test "zig fmt: multiline string" { - try testCanonical( - \\const s = - \\ \\ something - \\ \\ something else - \\ ; - \\ - ); -} - -test "zig fmt: values" { - try testCanonical( - \\test "values" { - \\ 1; - \\ 1.0; - \\ "string"; - \\ c"cstring"; - \\ 'c'; - \\ true; - \\ false; - \\ null; - \\ undefined; - \\ error; - \\ this; - \\ unreachable; - \\} - \\ - ); -} - -test "zig fmt: indexing" { - try testCanonical( - \\test "test index" { - \\ a[0]; - \\ a[0 + 5]; - \\ a[0..]; - \\ a[0..5]; - \\ a[a[0]]; - \\ a[a[0..]]; - \\ a[a[0..5]]; - \\ a[a[0]..]; - \\ a[a[0..5]..]; - \\ a[a[0]..a[0]]; - \\ a[a[0..5]..a[0]]; - \\ a[a[0..5]..a[0..5]]; - \\} - \\ - ); -} - -test "zig fmt: struct declaration" { - try testCanonical( - \\const S = struct { - \\ const Self = this; - \\ f1: u8, - \\ pub f3: u8, - \\ - \\ fn method(self: &Self) Self { - \\ return *self; - \\ } - \\ - \\ f2: u8, - \\}; - \\ - \\const Ps = packed struct { - \\ a: u8, - \\ pub b: u8, - \\ - \\ c: u8, - \\}; - \\ - \\const Es = extern struct { - \\ a: u8, - \\ pub b: u8, - \\ - \\ c: u8, - \\}; - \\ - ); -} - -test "zig fmt: enum declaration" { - try testCanonical( - \\const E = enum { - \\ Ok, - \\ SomethingElse = 0, - \\}; - \\ - \\const E2 = enum(u8) { - \\ Ok, - \\ SomethingElse = 255, - \\ SomethingThird, - \\}; - \\ - \\const Ee = extern enum { - \\ Ok, - \\ SomethingElse, - \\ SomethingThird, - \\}; - \\ - \\const Ep = packed enum { - \\ Ok, - \\ SomethingElse, - \\ SomethingThird, - \\}; - \\ - ); -} - -test "zig fmt: union declaration" { - try testCanonical( - \\const U = union { - \\ Int: u8, - \\ Float: f32, - \\ None, - \\ Bool: bool, - \\}; - \\ - \\const Ue = union(enum) { - \\ Int: u8, - \\ Float: f32, - \\ None, - \\ Bool: bool, - \\}; - \\ - \\const E = enum { - \\ Int, - \\ Float, - \\ None, - \\ Bool, - \\}; - \\ - \\const Ue2 = union(E) { - \\ Int: u8, - \\ Float: f32, - \\ None, - \\ Bool: bool, - \\}; - \\ - \\const Eu = extern union { - \\ Int: u8, - \\ Float: f32, - \\ None, - \\ Bool: bool, - \\}; - \\ - ); -} - -test "zig fmt: error set declaration" { - try testCanonical( - \\const E = error { - \\ A, - \\ B, - \\ - \\ C, - \\}; - \\ - ); -} - -test "zig fmt: arrays" { - try testCanonical( - \\test "test array" { - \\ const a: [2]u8 = [2]u8 { - \\ 1, - \\ 2, - \\ }; - \\ const a: [2]u8 = []u8 { - \\ 1, - \\ 2, - \\ }; - \\ const a: [0]u8 = []u8{}; - \\} - \\ - ); -} - -test "zig fmt: container initializers" { - try testCanonical( - \\const a1 = []u8{}; - \\const a2 = []u8 { - \\ 1, - \\ 2, - \\ 3, - \\ 4, - \\}; - \\const s1 = S{}; - \\const s2 = S { - \\ .a = 1, - \\ .b = 2, - \\}; - \\ - ); -} - -test "zig fmt: catch" { - try testCanonical( - \\test "catch" { - \\ const a: error!u8 = 0; - \\ _ = a catch return; - \\ _ = a catch |err| return; - \\} - \\ - ); -} - -test "zig fmt: blocks" { - try testCanonical( - \\test "blocks" { - \\ { - \\ const a = 0; - \\ const b = 0; - \\ } - \\ - \\ blk: { - \\ const a = 0; - \\ const b = 0; - \\ } - \\ - \\ const r = blk: { - \\ const a = 0; - \\ const b = 0; - \\ }; - \\} - \\ - ); -} - -test "zig fmt: switch" { - try testCanonical( - \\test "switch" { - \\ switch (0) { - \\ 0 => {}, - \\ 1 => unreachable, - \\ 2, - \\ 3 => {}, - \\ 4 ... 7 => {}, - \\ 1 + 4 * 3 + 22 => {}, - \\ else => { - \\ const a = 1; - \\ const b = a; - \\ }, - \\ } - \\ - \\ const res = switch (0) { - \\ 0 => 0, - \\ 1 => 2, - \\ 1 => a = 4, - \\ else => 4, - \\ }; - \\ - \\ const Union = union(enum) { - \\ Int: i64, - \\ Float: f64, - \\ }; - \\ - \\ const u = Union { - \\ .Int = 0, - \\ }; - \\ switch (u) { - \\ Union.Int => |int| {}, - \\ Union.Float => |*float| unreachable, - \\ } - \\} - \\ - ); -} - -test "zig fmt: while" { - try testCanonical( - \\test "while" { - \\ while (10 < 1) { - \\ unreachable; - \\ } - \\ - \\ while (10 < 1) - \\ unreachable; - \\ - \\ var i: usize = 0; - \\ while (i < 10) : (i += 1) { - \\ continue; - \\ } - \\ - \\ i = 0; - \\ while (i < 10) : (i += 1) - \\ continue; - \\ - \\ i = 0; - \\ var j: usize = 0; - \\ while (i < 10) : ({ - \\ i += 1; - \\ j += 1; - \\ }) { - \\ continue; - \\ } - \\ - \\ var a: ?u8 = 2; - \\ while (a) |v| : (a = null) { - \\ continue; - \\ } - \\ - \\ while (a) |v| : (a = null) - \\ unreachable; - \\ - \\ label: while (10 < 0) { - \\ unreachable; - \\ } - \\ - \\ const res = while (0 < 10) { - \\ break 7; - \\ } else { - \\ unreachable; - \\ }; - \\ - \\ const res = while (0 < 10) - \\ break 7 - \\ else - \\ unreachable; - \\ - \\ var a: error!u8 = 0; - \\ while (a) |v| { - \\ a = error.Err; - \\ } else |err| { - \\ i = 1; - \\ } - \\ - \\ comptime var k: usize = 0; - \\ inline while (i < 10) : (i += 1) - \\ j += 2; - \\} - \\ - ); -} - -test "zig fmt: for" { - try testCanonical( - \\test "for" { - \\ const a = []u8 { - \\ 1, - \\ 2, - \\ 3, - \\ }; - \\ for (a) |v| { - \\ continue; - \\ } - \\ - \\ for (a) |v| - \\ continue; - \\ - \\ for (a) |*v| - \\ continue; - \\ - \\ for (a) |v, i| { - \\ continue; - \\ } - \\ - \\ for (a) |v, i| - \\ continue; - \\ - \\ const res = for (a) |v, i| { - \\ break v; - \\ } else { - \\ unreachable; - \\ }; - \\ - \\ var num: usize = 0; - \\ inline for (a) |v, i| { - \\ num += v; - \\ num += i; - \\ } - \\} - \\ - ); -} - -test "zig fmt: if" { - try testCanonical( - \\test "if" { - \\ if (10 < 0) { - \\ unreachable; - \\ } - \\ - \\ if (10 < 0) unreachable; - \\ - \\ if (10 < 0) { - \\ unreachable; - \\ } else { - \\ const a = 20; - \\ } - \\ - \\ if (10 < 0) { - \\ unreachable; - \\ } else if (5 < 0) { - \\ unreachable; - \\ } else { - \\ const a = 20; - \\ } - \\ - \\ const is_world_broken = if (10 < 0) true else false; - \\ const some_number = 1 + if (10 < 0) 2 else 3; - \\ - \\ const a: ?u8 = 10; - \\ const b: ?u8 = null; - \\ if (a) |v| { - \\ const some = v; - \\ } else if (b) |*v| { - \\ unreachable; - \\ } else { - \\ const some = 10; - \\ } - \\ - \\ const non_null_a = if (a) |v| v else 0; - \\ - \\ const a_err: error!u8 = 0; - \\ if (a_err) |v| { - \\ const p = v; - \\ } else |err| { - \\ unreachable; - \\ } - \\} - \\ - ); -} - -test "zig fmt: defer" { - try testCanonical( - \\test "defer" { - \\ var i: usize = 0; - \\ defer i = 1; - \\ defer { - \\ i += 2; - \\ i *= i; - \\ } - \\ - \\ errdefer i += 3; - \\ errdefer { - \\ i += 2; - \\ i /= i; - \\ } - \\} - \\ - ); -} - -test "zig fmt: comptime" { - try testCanonical( - \\fn a() u8 { - \\ return 5; - \\} - \\ - \\fn b(comptime i: u8) u8 { - \\ return i; - \\} - \\ - \\const av = comptime a(); - \\const av2 = comptime blk: { - \\ var res = a(); - \\ res *= b(2); - \\ break :blk res; - \\}; - \\ - \\comptime { - \\ _ = a(); - \\} - \\ - \\test "comptime" { - \\ const av3 = comptime a(); - \\ const av4 = comptime blk: { - \\ var res = a(); - \\ res *= a(); - \\ break :blk res; - \\ }; - \\ - \\ comptime var i = 0; - \\ comptime { - \\ i = a(); - \\ i += b(i); - \\ } - \\} - \\ - ); -} - -test "zig fmt: fn type" { - try testCanonical( - \\fn a(i: u8) u8 { - \\ return i + 1; - \\} - \\ - \\const a: fn(u8) u8 = undefined; - \\const b: extern fn(u8) u8 = undefined; - \\const c: nakedcc fn(u8) u8 = undefined; - \\const ap: fn(u8) u8 = a; - \\ - ); -} - -test "zig fmt: inline asm" { - try testCanonical( - \\pub fn syscall1(number: usize, arg1: usize) usize { - \\ return asm volatile ("syscall" - \\ : [ret] "={rax}" (-> usize) - \\ : [number] "{rax}" (number), - \\ [arg1] "{rdi}" (arg1) - \\ : "rcx", "r11"); - \\} - \\ - ); -} - -test "zig fmt: coroutines" { - try testCanonical( - \\async fn simpleAsyncFn() void { - \\ const a = async a.b(); - \\ x += 1; - \\ suspend; - \\ x += 1; - \\ suspend |p| {} - \\ const p = async simpleAsyncFn() catch unreachable; - \\ await p; - \\} - \\ - \\test "coroutine suspend, resume, cancel" { - \\ const p = try async testAsyncSeq(); - \\ resume p; - \\ cancel p; - \\} - \\ - ); -} - -test "zig fmt: Block after if" { - try testCanonical( - \\test "Block after if" { - \\ if (true) { - \\ const a = 0; - \\ } - \\ - \\ { - \\ const a = 0; - \\ } - \\} - \\ - ); -} - -test "zig fmt: use" { - try testCanonical( - \\use @import("std"); - \\pub use @import("std"); - \\ - ); -} - -test "zig fmt: string identifier" { - try testCanonical( - \\const @"a b" = @"c d".@"e f"; - \\fn @"g h"() void {} - \\ - ); -} - -test "zig fmt: error return" { - try testCanonical( - \\fn err() error { - \\ call(); - \\ return error.InvalidArgs; - \\} - \\ - ); -} - -test "zig fmt: struct literals with fields on each line" { - try testCanonical( - \\var self = BufSet { - \\ .hash_map = BufSetHashMap.init(a), - \\}; - \\ - ); +test "std.zig.parser" { + _ = @import("parser_test.zig"); } diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig new file mode 100644 index 0000000000..4edce98439 --- /dev/null +++ b/std/zig/parser_test.zig @@ -0,0 +1,975 @@ +test "zig fmt: line comment after field decl" { + try testCanonical( + \\pub const dirent = extern struct { + \\ d_name: u8, + \\ d_name: u8, // comment 1 + \\ d_name: u8, + \\ d_name: u8, // comment 2 + \\ d_name: u8, + \\}; + \\ + ); +} + +test "zig fmt: array literal with 1 item on 1 line" { + try testCanonical( + \\var s = []const u64 {0} ** 25; + \\ + ); +} + +test "zig fmt: preserve same-line comment after a statement" { + try testCanonical( + \\test "" { + \\ a = b; + \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption + \\ a = b; + \\} + \\ + ); +} + +test "zig fmt: preserve comments before global variables" { + try testCanonical( + \\/// Foo copies keys and values before they go into the map, and + \\/// frees them when they get removed. + \\pub const Foo = struct {}; + \\ + ); +} + +test "zig fmt: preserve comments before statements" { + try testCanonical( + \\test "std" { + \\ // statement comment + \\ _ = @import("foo/bar.zig"); + \\} + \\ + ); +} + +test "zig fmt: preserve top level comments" { + try testCanonical( + \\// top level comment + \\test "hi" {} + \\ + ); +} + +test "zig fmt: get stdout or fail" { + try testCanonical( + \\const std = @import("std"); + \\ + \\pub fn main() !void { + \\ // If this program is run without stdout attached, exit with an error. + \\ // another comment + \\ var stdout_file = try std.io.getStdOut; + \\} + \\ + ); +} + +test "zig fmt: preserve spacing" { + try testCanonical( + \\const std = @import("std"); + \\ + \\pub fn main() !void { + \\ var stdout_file = try std.io.getStdOut; + \\ var stdout_file = try std.io.getStdOut; + \\ + \\ var stdout_file = try std.io.getStdOut; + \\ var stdout_file = try std.io.getStdOut; + \\} + \\ + ); +} + +test "zig fmt: return types" { + try testCanonical( + \\pub fn main() !void {} + \\pub fn main() var {} + \\pub fn main() i32 {} + \\ + ); +} + +test "zig fmt: imports" { + try testCanonical( + \\const std = @import("std"); + \\const std = @import(); + \\ + ); +} + +test "zig fmt: global declarations" { + try testCanonical( + \\const a = b; + \\pub const a = b; + \\var a = b; + \\pub var a = b; + \\const a: i32 = b; + \\pub const a: i32 = b; + \\var a: i32 = b; + \\pub var a: i32 = b; + \\extern const a: i32 = b; + \\pub extern const a: i32 = b; + \\extern var a: i32 = b; + \\pub extern var a: i32 = b; + \\extern "a" const a: i32 = b; + \\pub extern "a" const a: i32 = b; + \\extern "a" var a: i32 = b; + \\pub extern "a" var a: i32 = b; + \\ + ); +} + +test "zig fmt: extern declaration" { + try testCanonical( + \\extern var foo: c_int; + \\ + ); +} + +test "zig fmt: alignment" { + try testCanonical( + \\var foo: c_int align(1); + \\ + ); +} + +test "zig fmt: C main" { + try testCanonical( + \\fn main(argc: c_int, argv: &&u8) c_int { + \\ const a = b; + \\} + \\ + ); +} + +test "zig fmt: return" { + try testCanonical( + \\fn foo(argc: c_int, argv: &&u8) c_int { + \\ return 0; + \\} + \\ + \\fn bar() void { + \\ return; + \\} + \\ + ); +} + +test "zig fmt: pointer attributes" { + try testCanonical( + \\extern fn f1(s: &align(&u8) u8) c_int; + \\extern fn f2(s: &&align(1) &const &volatile u8) c_int; + \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int; + \\extern fn f4(s: &align(1) const volatile u8) c_int; + \\ + ); +} + +test "zig fmt: slice attributes" { + try testCanonical( + \\extern fn f1(s: &align(&u8) u8) c_int; + \\extern fn f2(s: &&align(1) &const &volatile u8) c_int; + \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int; + \\extern fn f4(s: &align(1) const volatile u8) c_int; + \\ + ); +} + +test "zig fmt: test declaration" { + try testCanonical( + \\test "test name" { + \\ const a = 1; + \\ var b = 1; + \\} + \\ + ); +} + +test "zig fmt: infix operators" { + try testCanonical( + \\test "infix operators" { + \\ var i = undefined; + \\ i = 2; + \\ i *= 2; + \\ i |= 2; + \\ i ^= 2; + \\ i <<= 2; + \\ i >>= 2; + \\ i &= 2; + \\ i *= 2; + \\ i *%= 2; + \\ i -= 2; + \\ i -%= 2; + \\ i += 2; + \\ i +%= 2; + \\ i /= 2; + \\ i %= 2; + \\ _ = i == i; + \\ _ = i != i; + \\ _ = i != i; + \\ _ = i.i; + \\ _ = i || i; + \\ _ = i!i; + \\ _ = i ** i; + \\ _ = i ++ i; + \\ _ = i ?? i; + \\ _ = i % i; + \\ _ = i / i; + \\ _ = i *% i; + \\ _ = i * i; + \\ _ = i -% i; + \\ _ = i - i; + \\ _ = i +% i; + \\ _ = i + i; + \\ _ = i << i; + \\ _ = i >> i; + \\ _ = i & i; + \\ _ = i ^ i; + \\ _ = i | i; + \\ _ = i >= i; + \\ _ = i <= i; + \\ _ = i > i; + \\ _ = i < i; + \\ _ = i and i; + \\ _ = i or i; + \\} + \\ + ); +} + +test "zig fmt: precedence" { + try testCanonical( + \\test "precedence" { + \\ a!b(); + \\ (a!b)(); + \\ !a!b; + \\ !(a!b); + \\ !a{}; + \\ !(a{}); + \\ a + b{}; + \\ (a + b){}; + \\ a << b + c; + \\ (a << b) + c; + \\ a & b << c; + \\ (a & b) << c; + \\ a ^ b & c; + \\ (a ^ b) & c; + \\ a | b ^ c; + \\ (a | b) ^ c; + \\ a == b | c; + \\ (a == b) | c; + \\ a and b == c; + \\ (a and b) == c; + \\ a or b and c; + \\ (a or b) and c; + \\ (a or b) and c; + \\} + \\ + ); +} + +test "zig fmt: prefix operators" { + try testCanonical( + \\test "prefix operators" { + \\ try return --%~??!*&0; + \\} + \\ + ); +} + +test "zig fmt: call expression" { + try testCanonical( + \\test "test calls" { + \\ a(); + \\ a(1); + \\ a(1, 2); + \\ a(1, 2) + a(1, 2); + \\} + \\ + ); +} + +test "zig fmt: var args" { + try testCanonical( + \\fn print(args: ...) void {} + \\ + ); +} + +test "zig fmt: var type" { + try testCanonical( + \\fn print(args: var) var {} + \\const Var = var; + \\const i: var = 0; + \\ + ); +} + +test "zig fmt: functions" { + try testCanonical( + \\extern fn puts(s: &const u8) c_int; + \\extern "c" fn puts(s: &const u8) c_int; + \\export fn puts(s: &const u8) c_int; + \\inline fn puts(s: &const u8) c_int; + \\pub extern fn puts(s: &const u8) c_int; + \\pub extern "c" fn puts(s: &const u8) c_int; + \\pub export fn puts(s: &const u8) c_int; + \\pub inline fn puts(s: &const u8) c_int; + \\pub extern fn puts(s: &const u8) align(2 + 2) c_int; + \\pub extern "c" fn puts(s: &const u8) align(2 + 2) c_int; + \\pub export fn puts(s: &const u8) align(2 + 2) c_int; + \\pub inline fn puts(s: &const u8) align(2 + 2) c_int; + \\ + ); +} + +test "zig fmt: multiline string" { + try testCanonical( + \\const s = + \\ \\ something + \\ \\ something else + \\ ; + \\ + ); +} + +test "zig fmt: values" { + try testCanonical( + \\test "values" { + \\ 1; + \\ 1.0; + \\ "string"; + \\ c"cstring"; + \\ 'c'; + \\ true; + \\ false; + \\ null; + \\ undefined; + \\ error; + \\ this; + \\ unreachable; + \\} + \\ + ); +} + +test "zig fmt: indexing" { + try testCanonical( + \\test "test index" { + \\ a[0]; + \\ a[0 + 5]; + \\ a[0..]; + \\ a[0..5]; + \\ a[a[0]]; + \\ a[a[0..]]; + \\ a[a[0..5]]; + \\ a[a[0]..]; + \\ a[a[0..5]..]; + \\ a[a[0]..a[0]]; + \\ a[a[0..5]..a[0]]; + \\ a[a[0..5]..a[0..5]]; + \\} + \\ + ); +} + +test "zig fmt: struct declaration" { + try testCanonical( + \\const S = struct { + \\ const Self = this; + \\ f1: u8, + \\ pub f3: u8, + \\ + \\ fn method(self: &Self) Self { + \\ return *self; + \\ } + \\ + \\ f2: u8, + \\}; + \\ + \\const Ps = packed struct { + \\ a: u8, + \\ pub b: u8, + \\ + \\ c: u8, + \\}; + \\ + \\const Es = extern struct { + \\ a: u8, + \\ pub b: u8, + \\ + \\ c: u8, + \\}; + \\ + ); +} + +test "zig fmt: enum declaration" { + try testCanonical( + \\const E = enum { + \\ Ok, + \\ SomethingElse = 0, + \\}; + \\ + \\const E2 = enum(u8) { + \\ Ok, + \\ SomethingElse = 255, + \\ SomethingThird, + \\}; + \\ + \\const Ee = extern enum { + \\ Ok, + \\ SomethingElse, + \\ SomethingThird, + \\}; + \\ + \\const Ep = packed enum { + \\ Ok, + \\ SomethingElse, + \\ SomethingThird, + \\}; + \\ + ); +} + +test "zig fmt: union declaration" { + try testCanonical( + \\const U = union { + \\ Int: u8, + \\ Float: f32, + \\ None, + \\ Bool: bool, + \\}; + \\ + \\const Ue = union(enum) { + \\ Int: u8, + \\ Float: f32, + \\ None, + \\ Bool: bool, + \\}; + \\ + \\const E = enum { + \\ Int, + \\ Float, + \\ None, + \\ Bool, + \\}; + \\ + \\const Ue2 = union(E) { + \\ Int: u8, + \\ Float: f32, + \\ None, + \\ Bool: bool, + \\}; + \\ + \\const Eu = extern union { + \\ Int: u8, + \\ Float: f32, + \\ None, + \\ Bool: bool, + \\}; + \\ + ); +} + +test "zig fmt: error set declaration" { + try testCanonical( + \\const E = error { + \\ A, + \\ B, + \\ + \\ C, + \\}; + \\ + ); +} + +test "zig fmt: arrays" { + try testCanonical( + \\test "test array" { + \\ const a: [2]u8 = [2]u8 { + \\ 1, + \\ 2, + \\ }; + \\ const a: [2]u8 = []u8 { + \\ 1, + \\ 2, + \\ }; + \\ const a: [0]u8 = []u8{}; + \\} + \\ + ); +} + +test "zig fmt: container initializers" { + try testCanonical( + \\const a1 = []u8{}; + \\const a2 = []u8 { + \\ 1, + \\ 2, + \\ 3, + \\ 4, + \\}; + \\const s1 = S{}; + \\const s2 = S { + \\ .a = 1, + \\ .b = 2, + \\}; + \\ + ); +} + +test "zig fmt: catch" { + try testCanonical( + \\test "catch" { + \\ const a: error!u8 = 0; + \\ _ = a catch return; + \\ _ = a catch |err| return; + \\} + \\ + ); +} + +test "zig fmt: blocks" { + try testCanonical( + \\test "blocks" { + \\ { + \\ const a = 0; + \\ const b = 0; + \\ } + \\ + \\ blk: { + \\ const a = 0; + \\ const b = 0; + \\ } + \\ + \\ const r = blk: { + \\ const a = 0; + \\ const b = 0; + \\ }; + \\} + \\ + ); +} + +test "zig fmt: switch" { + try testCanonical( + \\test "switch" { + \\ switch (0) { + \\ 0 => {}, + \\ 1 => unreachable, + \\ 2, + \\ 3 => {}, + \\ 4 ... 7 => {}, + \\ 1 + 4 * 3 + 22 => {}, + \\ else => { + \\ const a = 1; + \\ const b = a; + \\ }, + \\ } + \\ + \\ const res = switch (0) { + \\ 0 => 0, + \\ 1 => 2, + \\ 1 => a = 4, + \\ else => 4, + \\ }; + \\ + \\ const Union = union(enum) { + \\ Int: i64, + \\ Float: f64, + \\ }; + \\ + \\ const u = Union { + \\ .Int = 0, + \\ }; + \\ switch (u) { + \\ Union.Int => |int| {}, + \\ Union.Float => |*float| unreachable, + \\ } + \\} + \\ + ); +} + +test "zig fmt: while" { + try testCanonical( + \\test "while" { + \\ while (10 < 1) { + \\ unreachable; + \\ } + \\ + \\ while (10 < 1) + \\ unreachable; + \\ + \\ var i: usize = 0; + \\ while (i < 10) : (i += 1) { + \\ continue; + \\ } + \\ + \\ i = 0; + \\ while (i < 10) : (i += 1) + \\ continue; + \\ + \\ i = 0; + \\ var j: usize = 0; + \\ while (i < 10) : ({ + \\ i += 1; + \\ j += 1; + \\ }) { + \\ continue; + \\ } + \\ + \\ var a: ?u8 = 2; + \\ while (a) |v| : (a = null) { + \\ continue; + \\ } + \\ + \\ while (a) |v| : (a = null) + \\ unreachable; + \\ + \\ label: while (10 < 0) { + \\ unreachable; + \\ } + \\ + \\ const res = while (0 < 10) { + \\ break 7; + \\ } else { + \\ unreachable; + \\ }; + \\ + \\ const res = while (0 < 10) + \\ break 7 + \\ else + \\ unreachable; + \\ + \\ var a: error!u8 = 0; + \\ while (a) |v| { + \\ a = error.Err; + \\ } else |err| { + \\ i = 1; + \\ } + \\ + \\ comptime var k: usize = 0; + \\ inline while (i < 10) : (i += 1) + \\ j += 2; + \\} + \\ + ); +} + +test "zig fmt: for" { + try testCanonical( + \\test "for" { + \\ const a = []u8 { + \\ 1, + \\ 2, + \\ 3, + \\ }; + \\ for (a) |v| { + \\ continue; + \\ } + \\ + \\ for (a) |v| + \\ continue; + \\ + \\ for (a) |*v| + \\ continue; + \\ + \\ for (a) |v, i| { + \\ continue; + \\ } + \\ + \\ for (a) |v, i| + \\ continue; + \\ + \\ const res = for (a) |v, i| { + \\ break v; + \\ } else { + \\ unreachable; + \\ }; + \\ + \\ var num: usize = 0; + \\ inline for (a) |v, i| { + \\ num += v; + \\ num += i; + \\ } + \\} + \\ + ); +} + +test "zig fmt: if" { + try testCanonical( + \\test "if" { + \\ if (10 < 0) { + \\ unreachable; + \\ } + \\ + \\ if (10 < 0) unreachable; + \\ + \\ if (10 < 0) { + \\ unreachable; + \\ } else { + \\ const a = 20; + \\ } + \\ + \\ if (10 < 0) { + \\ unreachable; + \\ } else if (5 < 0) { + \\ unreachable; + \\ } else { + \\ const a = 20; + \\ } + \\ + \\ const is_world_broken = if (10 < 0) true else false; + \\ const some_number = 1 + if (10 < 0) 2 else 3; + \\ + \\ const a: ?u8 = 10; + \\ const b: ?u8 = null; + \\ if (a) |v| { + \\ const some = v; + \\ } else if (b) |*v| { + \\ unreachable; + \\ } else { + \\ const some = 10; + \\ } + \\ + \\ const non_null_a = if (a) |v| v else 0; + \\ + \\ const a_err: error!u8 = 0; + \\ if (a_err) |v| { + \\ const p = v; + \\ } else |err| { + \\ unreachable; + \\ } + \\} + \\ + ); +} + +test "zig fmt: defer" { + try testCanonical( + \\test "defer" { + \\ var i: usize = 0; + \\ defer i = 1; + \\ defer { + \\ i += 2; + \\ i *= i; + \\ } + \\ + \\ errdefer i += 3; + \\ errdefer { + \\ i += 2; + \\ i /= i; + \\ } + \\} + \\ + ); +} + +test "zig fmt: comptime" { + try testCanonical( + \\fn a() u8 { + \\ return 5; + \\} + \\ + \\fn b(comptime i: u8) u8 { + \\ return i; + \\} + \\ + \\const av = comptime a(); + \\const av2 = comptime blk: { + \\ var res = a(); + \\ res *= b(2); + \\ break :blk res; + \\}; + \\ + \\comptime { + \\ _ = a(); + \\} + \\ + \\test "comptime" { + \\ const av3 = comptime a(); + \\ const av4 = comptime blk: { + \\ var res = a(); + \\ res *= a(); + \\ break :blk res; + \\ }; + \\ + \\ comptime var i = 0; + \\ comptime { + \\ i = a(); + \\ i += b(i); + \\ } + \\} + \\ + ); +} + +test "zig fmt: fn type" { + try testCanonical( + \\fn a(i: u8) u8 { + \\ return i + 1; + \\} + \\ + \\const a: fn(u8) u8 = undefined; + \\const b: extern fn(u8) u8 = undefined; + \\const c: nakedcc fn(u8) u8 = undefined; + \\const ap: fn(u8) u8 = a; + \\ + ); +} + +test "zig fmt: inline asm" { + try testCanonical( + \\pub fn syscall1(number: usize, arg1: usize) usize { + \\ return asm volatile ("syscall" + \\ : [ret] "={rax}" (-> usize) + \\ : [number] "{rax}" (number), + \\ [arg1] "{rdi}" (arg1) + \\ : "rcx", "r11"); + \\} + \\ + ); +} + +test "zig fmt: coroutines" { + try testCanonical( + \\async fn simpleAsyncFn() void { + \\ const a = async a.b(); + \\ x += 1; + \\ suspend; + \\ x += 1; + \\ suspend |p| {} + \\ const p = async simpleAsyncFn() catch unreachable; + \\ await p; + \\} + \\ + \\test "coroutine suspend, resume, cancel" { + \\ const p = try async testAsyncSeq(); + \\ resume p; + \\ cancel p; + \\} + \\ + ); +} + +test "zig fmt: Block after if" { + try testCanonical( + \\test "Block after if" { + \\ if (true) { + \\ const a = 0; + \\ } + \\ + \\ { + \\ const a = 0; + \\ } + \\} + \\ + ); +} + +test "zig fmt: use" { + try testCanonical( + \\use @import("std"); + \\pub use @import("std"); + \\ + ); +} + +test "zig fmt: string identifier" { + try testCanonical( + \\const @"a b" = @"c d".@"e f"; + \\fn @"g h"() void {} + \\ + ); +} + +test "zig fmt: error return" { + try testCanonical( + \\fn err() error { + \\ call(); + \\ return error.InvalidArgs; + \\} + \\ + ); +} + +test "zig fmt: struct literals with fields on each line" { + try testCanonical( + \\var self = BufSet { + \\ .hash_map = BufSetHashMap.init(a), + \\}; + \\ + ); +} + +const std = @import("std"); +const mem = std.mem; +const warn = std.debug.warn; +const Tokenizer = std.zig.Tokenizer; +const Parser = std.zig.Parser; +const io = std.io; + +var fixed_buffer_mem: [100 * 1024]u8 = undefined; + +fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { + var tokenizer = Tokenizer.init(source); + var parser = Parser.init(&tokenizer, allocator, "(memory buffer)"); + defer parser.deinit(); + + var tree = try parser.parse(); + defer tree.deinit(); + + var buffer = try std.Buffer.initSize(allocator, 0); + errdefer buffer.deinit(); + + var buffer_out_stream = io.BufferOutStream.init(&buffer); + try parser.renderSource(&buffer_out_stream.stream, tree.root_node); + return buffer.toOwnedSlice(); +} + +fn testCanonical(source: []const u8) !void { + const needed_alloc_count = x: { + // Try it once with unlimited memory, make sure it works + var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); + var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize)); + const result_source = try testParse(source, &failing_allocator.allocator); + if (!mem.eql(u8, result_source, source)) { + warn("\n====== expected this output: =========\n"); + warn("{}", source); + warn("\n======== instead found this: =========\n"); + warn("{}", result_source); + warn("\n======================================\n"); + return error.TestFailed; + } + failing_allocator.allocator.free(result_source); + break :x failing_allocator.index; + }; + + var fail_index: usize = 0; + while (fail_index < needed_alloc_count) : (fail_index += 1) { + var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); + var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index); + if (testParse(source, &failing_allocator.allocator)) |_| { + return error.NondeterministicMemoryUsage; + } else |err| switch (err) { + error.OutOfMemory => { + if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) { + warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n", + fail_index, needed_alloc_count, + failing_allocator.allocated_bytes, failing_allocator.freed_bytes, + failing_allocator.index, failing_allocator.deallocations); + return error.MemoryLeakDetected; + } + }, + error.ParseError => @panic("test failed"), + } + } +} + -- cgit v1.2.3 From c53209a8a8cee014382a735b3baa7cd439106c6f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 19:55:57 -0400 Subject: zig fmt: comments before var decl in struct --- std/zig/parser.zig | 26 +++++++++++++++++--------- std/zig/parser_test.zig | 33 ++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 5f0928ee42..cd4b9c136e 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -230,6 +230,7 @@ pub const Parser = struct { Semicolon: &&ast.Node, AddComments: AddCommentsCtx, LookForSameLineComment: &&ast.Node, + LookForSameLineCommentDirect: &ast.Node, AsmOutputItems: &ArrayList(&ast.Node.AsmOutput), AsmOutputReturnOrType: &ast.Node.AsmOutput, @@ -532,7 +533,7 @@ pub const Parser = struct { } } - stack.append(State { + try stack.append(State { .VarDecl = VarDeclCtx { .comments = ctx.comments, .visib_token = ctx.visib_token, @@ -542,7 +543,7 @@ pub const Parser = struct { .mut_token = token, .list = ctx.decls } - }) catch unreachable; + }); continue; }, Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, @@ -705,6 +706,7 @@ pub const Parser = struct { continue; }, State.ContainerDecl => |container_decl| { + const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Identifier => { @@ -713,7 +715,7 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.StructField { .base = ast.Node { .id = ast.Node.Id.StructField, - .before_comments = null, + .before_comments = comments, .same_line_comment = null, }, .visib_token = null, @@ -765,7 +767,7 @@ pub const Parser = struct { .TopLevelExternOrField = TopLevelExternOrFieldCtx { .visib_token = token, .container_decl = container_decl, - .comments = null, + .comments = comments, } }); continue; @@ -778,7 +780,7 @@ pub const Parser = struct { .visib_token = token, .extern_export_inline_token = null, .lib_name = null, - .comments = null, + .comments = comments, } }); continue; @@ -793,7 +795,7 @@ pub const Parser = struct { .visib_token = token, .extern_export_inline_token = null, .lib_name = null, - .comments = null, + .comments = comments, } }); continue; @@ -811,7 +813,7 @@ pub const Parser = struct { .visib_token = null, .extern_export_inline_token = null, .lib_name = null, - .comments = null, + .comments = comments, } }); continue; @@ -842,7 +844,8 @@ pub const Parser = struct { }); try ctx.list.append(&var_decl.base); - stack.append(State { .VarDeclAlign = var_decl }) catch unreachable; + try stack.append(State { .LookForSameLineCommentDirect = &var_decl.base }); + try stack.append(State { .VarDeclAlign = var_decl }); try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); try stack.append(State { .IfToken = Token.Id.Colon }); try stack.append(State { @@ -854,7 +857,7 @@ pub const Parser = struct { continue; }, State.VarDeclAlign => |var_decl| { - stack.append(State { .VarDeclEq = var_decl }) catch unreachable; + try stack.append(State { .VarDeclEq = var_decl }); const next_token = self.getNextToken(); if (next_token.id == Token.Id.Keyword_align) { @@ -1348,6 +1351,11 @@ pub const Parser = struct { continue; }, + State.LookForSameLineCommentDirect => |node| { + try self.lookForSameLineComment(arena, node); + continue; + }, + State.AsmOutputItems => |items| { const lbracket = self.getNextToken(); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 4edce98439..78b08d91cb 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,4 +1,27 @@ -test "zig fmt: line comment after field decl" { +test "zig fmt: comments before var decl in struct" { + try testCanonical( + \\pub const vfs_cap_data = extern struct { + \\ // All of these are mandated as little endian + \\ // when on disk. + \\ const Data = struct { + \\ permitted: u32, + \\ inheritable: u32, + \\ }; + \\}; + \\ + ); +} + +test "zig fmt: same-line comment after var decl in struct" { + try testCanonical( + \\pub const vfs_cap_data = extern struct { + \\ const Data = struct {}; // when on disk. + \\}; + \\ + ); +} + +test "zig fmt: same-line comment after field decl" { try testCanonical( \\pub const dirent = extern struct { \\ d_name: u8, @@ -18,7 +41,7 @@ test "zig fmt: array literal with 1 item on 1 line" { ); } -test "zig fmt: preserve same-line comment after a statement" { +test "zig fmt: same-line comment after a statement" { try testCanonical( \\test "" { \\ a = b; @@ -29,7 +52,7 @@ test "zig fmt: preserve same-line comment after a statement" { ); } -test "zig fmt: preserve comments before global variables" { +test "zig fmt: comments before global variables" { try testCanonical( \\/// Foo copies keys and values before they go into the map, and \\/// frees them when they get removed. @@ -38,7 +61,7 @@ test "zig fmt: preserve comments before global variables" { ); } -test "zig fmt: preserve comments before statements" { +test "zig fmt: comments before statements" { try testCanonical( \\test "std" { \\ // statement comment @@ -48,7 +71,7 @@ test "zig fmt: preserve comments before statements" { ); } -test "zig fmt: preserve top level comments" { +test "zig fmt: comments before test decl" { try testCanonical( \\// top level comment \\test "hi" {} -- cgit v1.2.3 From a912c7d75f61fb127ff8552e7a10ffe4672ac1aa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 21:27:44 -0400 Subject: zig fmt: same-line comment after switch prong --- std/zig/parser.zig | 41 ++++++++++++++++++++++++----------------- std/zig/parser_test.zig | 12 ++++++++++++ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index cd4b9c136e..aae34f39b1 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -1505,11 +1505,11 @@ pub const Parser = struct { if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { container_decl.rbrace_token = end; continue; - } else { - try self.lookForSameLineComment(arena, container_decl.fields_and_decls.toSlice()[container_decl.fields_and_decls.len - 1]); - try stack.append(State { .ContainerDecl = container_decl }); - continue; } + + try self.lookForSameLineComment(arena, container_decl.fields_and_decls.toSlice()[container_decl.fields_and_decls.len - 1]); + try stack.append(State { .ContainerDecl = container_decl }); + continue; }, State.IdentifierListItemOrEnd => |list_state| { if (self.eatToken(Token.Id.RBrace)) |rbrace| { @@ -1536,30 +1536,36 @@ pub const Parser = struct { continue; } - const node = try self.createNode(arena, ast.Node.SwitchCase, - ast.Node.SwitchCase { - .base = undefined, - .items = ArrayList(&ast.Node).init(arena), - .payload = null, - .expr = undefined, - } - ); + const node = try arena.construct(ast.Node.SwitchCase { + .base = ast.Node { + .id = ast.Node.Id.SwitchCase, + .before_comments = null, + .same_line_comment = null, + }, + .items = ArrayList(&ast.Node).init(arena), + .payload = null, + .expr = undefined, + }); try list_state.list.append(node); - stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable; + try stack.append(State { .SwitchCaseCommaOrEnd = list_state }); try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); try stack.append(State { .SwitchCaseFirstItem = &node.items }); continue; }, + State.SwitchCaseCommaOrEnd => |list_state| { if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { *list_state.ptr = end; continue; - } else { - stack.append(State { .SwitchCaseOrEnd = list_state }) catch unreachable; - continue; } + + const switch_case = list_state.list.toSlice()[list_state.list.len - 1]; + try self.lookForSameLineComment(arena, &switch_case.base); + try stack.append(State { .SwitchCaseOrEnd = list_state }); + continue; }, + State.SwitchCaseFirstItem => |case_items| { const token = self.getNextToken(); if (token.id == Token.Id.Keyword_else) { @@ -4095,7 +4101,6 @@ pub const Parser = struct { while (i != 0) { i -= 1; const node = cases[i]; - try stack.append(RenderState { .Text = ","}); try stack.append(RenderState { .Expression = &node.base}); try stack.append(RenderState.PrintIndent); try stack.append(RenderState { @@ -4118,6 +4123,8 @@ pub const Parser = struct { ast.Node.Id.SwitchCase => { const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); + try stack.append(RenderState { .PrintSameLineComment = switch_case.base.same_line_comment }); + try stack.append(RenderState { .Text = "," }); try stack.append(RenderState { .Expression = switch_case.expr }); if (switch_case.payload) |payload| { try stack.append(RenderState { .Text = " " }); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 78b08d91cb..e37cc72392 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,15 @@ +test "zig fmt: same-line comment after switch prong" { + try testCanonical( + \\test "" { + \\ switch (err) { + \\ error.PathAlreadyExists => {}, // comment 2 + \\ else => return err, // comment 1 + \\ } + \\} + \\ + ); +} + test "zig fmt: comments before var decl in struct" { try testCanonical( \\pub const vfs_cap_data = extern struct { -- cgit v1.2.3 From f04015c080b8e4e97166d6ac9356f818e766fc48 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 21:47:54 -0400 Subject: zig fmt: comments before switch prong --- std/zig/parser.zig | 8 ++++++-- std/zig/parser_test.zig | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index aae34f39b1..454ab445ce 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -1536,10 +1536,11 @@ pub const Parser = struct { continue; } + const comments = try self.eatComments(arena); const node = try arena.construct(ast.Node.SwitchCase { .base = ast.Node { .id = ast.Node.Id.SwitchCase, - .before_comments = null, + .before_comments = comments, .same_line_comment = null, }, .items = ArrayList(&ast.Node).init(arena), @@ -1551,6 +1552,7 @@ pub const Parser = struct { try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); try stack.append(State { .SwitchCaseFirstItem = &node.items }); + continue; }, @@ -4123,7 +4125,9 @@ pub const Parser = struct { ast.Node.Id.SwitchCase => { const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); - try stack.append(RenderState { .PrintSameLineComment = switch_case.base.same_line_comment }); + try self.renderComments(stream, base, indent); + + try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment }); try stack.append(RenderState { .Text = "," }); try stack.append(RenderState { .Expression = switch_case.expr }); if (switch_case.payload) |payload| { diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index e37cc72392..e70b6adb0a 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,18 @@ +test "zig fmt: comments before switch prong" { + try testCanonical( + \\test "" { + \\ switch (err) { + \\ error.PathAlreadyExists => continue, + \\ + \\ // comment 1 + \\ // comment 2 + \\ else => return err, + \\ } + \\} + \\ + ); +} + test "zig fmt: same-line comment after switch prong" { try testCanonical( \\test "" { -- cgit v1.2.3 From 4e23fb7f062322e604d74ebb7e62d707ecf7e7b6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 22:12:17 -0400 Subject: zig fmt: comments before error set decl --- std/zig/parser.zig | 36 ++++++++++++++++++++++++++---------- std/zig/parser_test.zig | 13 +++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 454ab445ce..ffbc0a223c 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -1517,8 +1517,15 @@ pub const Parser = struct { continue; } - stack.append(State { .IdentifierListCommaOrEnd = list_state }) catch unreachable; - try stack.append(State { .Identifier = OptionalCtx { .Required = try list_state.list.addOne() } }); + const comments = try self.eatComments(arena); + const node_ptr = try list_state.list.addOne(); + + try stack.append(State { .AddComments = AddCommentsCtx { + .node_ptr = node_ptr, + .comments = comments, + }}); + try stack.append(State { .IdentifierListCommaOrEnd = list_state }); + try stack.append(State { .Identifier = OptionalCtx { .Required = node_ptr } }); continue; }, State.IdentifierListCommaOrEnd => |list_state| { @@ -2739,14 +2746,17 @@ pub const Parser = struct { continue; } - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ErrorSetDecl, - ast.Node.ErrorSetDecl { - .base = undefined, - .error_token = ctx.error_token, - .decls = ArrayList(&ast.Node).init(arena), - .rbrace_token = undefined, - } - ); + const node = try arena.construct(ast.Node.ErrorSetDecl { + .base = ast.Node { + .id = ast.Node.Id.ErrorSetDecl, + .before_comments = null, + .same_line_comment = null, + }, + .error_token = ctx.error_token, + .decls = ArrayList(&ast.Node).init(arena), + .rbrace_token = undefined, + }); + ctx.opt_ctx.store(&node.base); stack.append(State { .IdentifierListItemOrEnd = ListSave(&ast.Node) { @@ -3337,6 +3347,7 @@ pub const Parser = struct { PrintIndent, Indent: usize, PrintSameLineComment: ?&Token, + PrintComments: &ast.Node, }; pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { @@ -3978,6 +3989,7 @@ pub const Parser = struct { const node = decls[i]; try stack.append(RenderState { .Text = "," }); try stack.append(RenderState { .Expression = node }); + try stack.append(RenderState { .PrintComments = node }); try stack.append(RenderState.PrintIndent); try stack.append(RenderState { .Text = blk: { @@ -4466,6 +4478,10 @@ pub const Parser = struct { const comment_token = maybe_comment ?? break :blk; try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token)); }, + + RenderState.PrintComments => |node| blk: { + try self.renderComments(stream, node, indent); + }, } } } diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index e70b6adb0a..a914775dc7 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,16 @@ +test "zig fmt: comments before error set decl" { + try testCanonical( + \\const UnexpectedError = error { + \\ /// The Operating System returned an undocumented error code. + \\ Unexpected, + \\ + \\ // another + \\ Another, + \\}; + \\ + ); +} + test "zig fmt: comments before switch prong" { try testCanonical( \\test "" { -- cgit v1.2.3 From 39befc35a851f6cf206c7e3b19d66f7df00440b0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 22:31:42 -0400 Subject: update comment in std/os/index.zig --- std/os/index.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index ee9ff1516c..99aa7891cb 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2546,9 +2546,9 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread else => return unexpectedErrorPosix(usize(err)), } } else if (builtin.os == builtin.Os.linux) { - // use linux API directly + // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND - | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS + | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; const newtls: usize = 0; const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid); -- cgit v1.2.3 From fd2cd38bdb831ef78a0d4ab0973020dfbd348c1f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 00:19:55 -0400 Subject: zig fmt: support line comments and doc comments line comments can go anywhere a list of something is allowed --- std/zig/ast.zig | 29 ++++++++-- std/zig/parser.zig | 142 ++++++++++++++++++++++++++++++++---------------- std/zig/parser_test.zig | 36 +++++++++++- std/zig/tokenizer.zig | 17 +++++- 4 files changed, 168 insertions(+), 56 deletions(-) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 75c3696bfe..716ed8eb7d 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -6,7 +6,7 @@ const mem = std.mem; pub const Node = struct { id: Id, - before_comments: ?&LineComment, + doc_comments: ?&DocComment, same_line_comment: ?&Token, pub const Id = enum { @@ -59,6 +59,7 @@ pub const Node = struct { // Misc LineComment, + DocComment, SwitchCase, SwitchElse, Else, @@ -718,7 +719,8 @@ pub const Node = struct { base: Node, switch_token: Token, expr: &Node, - cases: ArrayList(&SwitchCase), + /// these can be SwitchCase nodes or LineComment nodes + cases: ArrayList(&Node), rbrace: Token, pub fn iterate(self: &Switch, index: usize) ?&Node { @@ -727,7 +729,7 @@ pub const Node = struct { if (i < 1) return self.expr; i -= 1; - if (i < self.cases.len) return &self.cases.at(i).base; + if (i < self.cases.len) return self.cases.at(i); i -= self.cases.len; return null; @@ -1715,17 +1717,34 @@ pub const Node = struct { pub const LineComment = struct { base: Node, - lines: ArrayList(Token), + token: Token, pub fn iterate(self: &LineComment, index: usize) ?&Node { return null; } pub fn firstToken(self: &LineComment) Token { - return self.lines.at(0); + return self.token; } pub fn lastToken(self: &LineComment) Token { + return self.token; + } + }; + + pub const DocComment = struct { + base: Node, + lines: ArrayList(Token), + + pub fn iterate(self: &DocComment, index: usize) ?&Node { + return null; + } + + pub fn firstToken(self: &DocComment) Token { + return self.lines.at(0); + } + + pub fn lastToken(self: &DocComment) Token { return self.lines.at(self.lines.len - 1); } }; diff --git a/std/zig/parser.zig b/std/zig/parser.zig index ffbc0a223c..b5849c3e96 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -55,7 +55,7 @@ pub const Parser = struct { visib_token: ?Token, extern_export_inline_token: ?Token, lib_name: ?&ast.Node, - comments: ?&ast.Node.LineComment, + comments: ?&ast.Node.DocComment, }; const VarDeclCtx = struct { @@ -65,19 +65,19 @@ pub const Parser = struct { extern_export_token: ?Token, lib_name: ?&ast.Node, list: &ArrayList(&ast.Node), - comments: ?&ast.Node.LineComment, + comments: ?&ast.Node.DocComment, }; const TopLevelExternOrFieldCtx = struct { visib_token: Token, container_decl: &ast.Node.ContainerDecl, - comments: ?&ast.Node.LineComment, + comments: ?&ast.Node.DocComment, }; const ExternTypeCtx = struct { opt_ctx: OptionalCtx, extern_token: Token, - comments: ?&ast.Node.LineComment, + comments: ?&ast.Node.DocComment, }; const ContainerKindCtx = struct { @@ -186,7 +186,7 @@ pub const Parser = struct { const AddCommentsCtx = struct { node_ptr: &&ast.Node, - comments: ?&ast.Node.LineComment, + comments: ?&ast.Node.DocComment, }; const State = union(enum) { @@ -244,8 +244,8 @@ pub const Parser = struct { FieldListCommaOrEnd: &ast.Node.ContainerDecl, IdentifierListItemOrEnd: ListSave(&ast.Node), IdentifierListCommaOrEnd: ListSave(&ast.Node), - SwitchCaseOrEnd: ListSave(&ast.Node.SwitchCase), - SwitchCaseCommaOrEnd: ListSave(&ast.Node.SwitchCase), + SwitchCaseOrEnd: ListSave(&ast.Node), + SwitchCaseCommaOrEnd: ListSave(&ast.Node), SwitchCaseFirstItem: &ArrayList(&ast.Node), SwitchCaseItem: &ArrayList(&ast.Node), SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node), @@ -349,6 +349,10 @@ pub const Parser = struct { switch (state) { State.TopLevel => { + while (try self.eatLineComment(arena)) |line_comment| { + try root_node.decls.append(&line_comment.base); + } + const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { @@ -358,7 +362,7 @@ pub const Parser = struct { const block = try arena.construct(ast.Node.Block { .base = ast.Node { .id = ast.Node.Id.Block, - .before_comments = null, + .doc_comments = null, .same_line_comment = null, }, .label = null, @@ -369,7 +373,7 @@ pub const Parser = struct { const test_node = try arena.construct(ast.Node.TestDecl { .base = ast.Node { .id = ast.Node.Id.TestDecl, - .before_comments = comments, + .doc_comments = comments, .same_line_comment = null, }, .test_token = token, @@ -551,7 +555,7 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .before_comments = ctx.comments, + .doc_comments = ctx.comments, .same_line_comment = null, }, .visib_token = ctx.visib_token, @@ -620,7 +624,7 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.StructField { .base = ast.Node { .id = ast.Node.Id.StructField, - .before_comments = null, + .doc_comments = null, .same_line_comment = null, }, .visib_token = ctx.visib_token, @@ -706,6 +710,10 @@ pub const Parser = struct { continue; }, State.ContainerDecl => |container_decl| { + while (try self.eatLineComment(arena)) |line_comment| { + try container_decl.fields_and_decls.append(&line_comment.base); + } + const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { @@ -715,7 +723,7 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.StructField { .base = ast.Node { .id = ast.Node.Id.StructField, - .before_comments = comments, + .doc_comments = comments, .same_line_comment = null, }, .visib_token = null, @@ -826,7 +834,7 @@ pub const Parser = struct { const var_decl = try arena.construct(ast.Node.VarDecl { .base = ast.Node { .id = ast.Node.Id.VarDecl, - .before_comments = ctx.comments, + .doc_comments = ctx.comments, .same_line_comment = null, }, .visib_token = ctx.visib_token, @@ -1222,6 +1230,14 @@ pub const Parser = struct { else => { self.putBackToken(token); stack.append(State { .Block = block }) catch unreachable; + + var any_comments = false; + while (try self.eatLineComment(arena)) |line_comment| { + try block.statements.append(&line_comment.base); + any_comments = true; + } + if (any_comments) continue; + try stack.append(State { .Statement = block }); continue; }, @@ -1258,7 +1274,7 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.Defer { .base = ast.Node { .id = ast.Node.Id.Defer, - .before_comments = comments, + .doc_comments = comments, .same_line_comment = null, }, .defer_token = token, @@ -1342,7 +1358,7 @@ pub const Parser = struct { State.AddComments => |add_comments_ctx| { const node = *add_comments_ctx.node_ptr; - node.before_comments = add_comments_ctx.comments; + node.doc_comments = add_comments_ctx.comments; continue; }, @@ -1466,7 +1482,7 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.FieldInitializer { .base = ast.Node { .id = ast.Node.Id.FieldInitializer, - .before_comments = null, + .doc_comments = null, .same_line_comment = null, }, .period_token = undefined, @@ -1512,6 +1528,10 @@ pub const Parser = struct { continue; }, State.IdentifierListItemOrEnd => |list_state| { + while (try self.eatLineComment(arena)) |line_comment| { + try list_state.list.append(&line_comment.base); + } + if (self.eatToken(Token.Id.RBrace)) |rbrace| { *list_state.ptr = rbrace; continue; @@ -1538,6 +1558,10 @@ pub const Parser = struct { } }, State.SwitchCaseOrEnd => |list_state| { + while (try self.eatLineComment(arena)) |line_comment| { + try list_state.list.append(&line_comment.base); + } + if (self.eatToken(Token.Id.RBrace)) |rbrace| { *list_state.ptr = rbrace; continue; @@ -1547,14 +1571,14 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.SwitchCase { .base = ast.Node { .id = ast.Node.Id.SwitchCase, - .before_comments = comments, + .doc_comments = comments, .same_line_comment = null, }, .items = ArrayList(&ast.Node).init(arena), .payload = null, .expr = undefined, }); - try list_state.list.append(node); + try list_state.list.append(&node.base); try stack.append(State { .SwitchCaseCommaOrEnd = list_state }); try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); @@ -1569,8 +1593,8 @@ pub const Parser = struct { continue; } - const switch_case = list_state.list.toSlice()[list_state.list.len - 1]; - try self.lookForSameLineComment(arena, &switch_case.base); + const node = list_state.list.toSlice()[list_state.list.len - 1]; + try self.lookForSameLineComment(arena, node); try stack.append(State { .SwitchCaseOrEnd = list_state }); continue; }, @@ -1660,7 +1684,7 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .before_comments = ctx.comments, + .doc_comments = ctx.comments, .same_line_comment = null, }, .visib_token = null, @@ -2632,7 +2656,7 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .before_comments = null, + .doc_comments = null, .same_line_comment = null, }, .visib_token = null, @@ -2656,7 +2680,7 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .before_comments = null, + .doc_comments = null, .same_line_comment = null, }, .visib_token = null, @@ -2749,7 +2773,7 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.ErrorSetDecl { .base = ast.Node { .id = ast.Node.Id.ErrorSetDecl, - .before_comments = null, + .doc_comments = null, .same_line_comment = null, }, .error_token = ctx.error_token, @@ -2829,18 +2853,18 @@ pub const Parser = struct { } } - fn eatComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment { - var result: ?&ast.Node.LineComment = null; + fn eatComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.DocComment { + var result: ?&ast.Node.DocComment = null; while (true) { - if (self.eatToken(Token.Id.LineComment)) |line_comment| { + if (self.eatToken(Token.Id.DocComment)) |line_comment| { const node = blk: { if (result) |comment_node| { break :blk comment_node; } else { - const comment_node = try arena.construct(ast.Node.LineComment { + const comment_node = try arena.construct(ast.Node.DocComment { .base = ast.Node { - .id = ast.Node.Id.LineComment, - .before_comments = null, + .id = ast.Node.Id.DocComment, + .doc_comments = null, .same_line_comment = null, }, .lines = ArrayList(Token).init(arena), @@ -2857,6 +2881,18 @@ pub const Parser = struct { return result; } + fn eatLineComment(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment { + const token = self.eatToken(Token.Id.LineComment) ?? return null; + return try arena.construct(ast.Node.LineComment { + .base = ast.Node { + .id = ast.Node.Id.LineComment, + .doc_comments = null, + .same_line_comment = null, + }, + .token = token, + }); + } + fn requireSemiColon(node: &const ast.Node) bool { var n = node; while (true) { @@ -2874,6 +2910,7 @@ pub const Parser = struct { ast.Node.Id.SwitchCase, ast.Node.Id.SwitchElse, ast.Node.Id.FieldInitializer, + ast.Node.Id.DocComment, ast.Node.Id.LineComment, ast.Node.Id.TestDecl => return false, ast.Node.Id.While => { @@ -2933,7 +2970,7 @@ pub const Parser = struct { const node_last_token = node.lastToken(); const line_comment_token = self.getNextToken(); - if (line_comment_token.id != Token.Id.LineComment) { + if (line_comment_token.id != Token.Id.DocComment and line_comment_token.id != Token.Id.LineComment) { self.putBackToken(line_comment_token); return; } @@ -3038,18 +3075,21 @@ pub const Parser = struct { return true; }, Token.Id.Keyword_switch => { - const node = try self.createToCtxNode(arena, ctx, ast.Node.Switch, - ast.Node.Switch { - .base = undefined, - .switch_token = *token, - .expr = undefined, - .cases = ArrayList(&ast.Node.SwitchCase).init(arena), - .rbrace = undefined, - } - ); + const node = try arena.construct(ast.Node.Switch { + .base = ast.Node { + .id = ast.Node.Id.Switch, + .doc_comments = null, + .same_line_comment = null, + }, + .switch_token = *token, + .expr = undefined, + .cases = ArrayList(&ast.Node).init(arena), + .rbrace = undefined, + }); + ctx.store(&node.base); stack.append(State { - .SwitchCaseOrEnd = ListSave(&ast.Node.SwitchCase) { + .SwitchCaseOrEnd = ListSave(&ast.Node) { .list = &node.cases, .ptr = &node.rbrace, }, @@ -3208,7 +3248,7 @@ pub const Parser = struct { const id = ast.Node.typeToId(T); break :blk ast.Node { .id = id, - .before_comments = null, + .doc_comments = null, .same_line_comment = null, }; }; @@ -3454,6 +3494,10 @@ pub const Parser = struct { } try stack.append(RenderState { .Expression = decl }); }, + ast.Node.Id.LineComment => { + const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl); + try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token)); + }, else => unreachable, } }, @@ -3987,7 +4031,9 @@ pub const Parser = struct { while (i != 0) { i -= 1; const node = decls[i]; - try stack.append(RenderState { .Text = "," }); + if (node.id != ast.Node.Id.LineComment) { + try stack.append(RenderState { .Text = "," }); + } try stack.append(RenderState { .Expression = node }); try stack.append(RenderState { .PrintComments = node }); try stack.append(RenderState.PrintIndent); @@ -4100,7 +4146,11 @@ pub const Parser = struct { try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) }); } }, - ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"), + ast.Node.Id.LineComment => { + const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base); + try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token)); + }, + ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes ast.Node.Id.Switch => { const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token)); @@ -4115,7 +4165,7 @@ pub const Parser = struct { while (i != 0) { i -= 1; const node = cases[i]; - try stack.append(RenderState { .Expression = &node.base}); + try stack.append(RenderState { .Expression = node}); try stack.append(RenderState.PrintIndent); try stack.append(RenderState { .Text = blk: { @@ -4487,7 +4537,7 @@ pub const Parser = struct { } fn renderComments(self: &Parser, stream: var, node: &ast.Node, indent: usize) !void { - const comment = node.before_comments ?? return; + const comment = node.doc_comments ?? return; for (comment.lines.toSliceConst()) |line_token| { try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token)); try stream.writeByteNTimes(' ', indent); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index a914775dc7..b4cbef33ba 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -3,9 +3,12 @@ test "zig fmt: comments before error set decl" { \\const UnexpectedError = error { \\ /// The Operating System returned an undocumented error code. \\ Unexpected, - \\ \\ // another \\ Another, + \\ + \\ // in between + \\ + \\ // at end \\}; \\ ); @@ -18,8 +21,10 @@ test "zig fmt: comments before switch prong" { \\ error.PathAlreadyExists => continue, \\ \\ // comment 1 + \\ \\ // comment 2 \\ else => return err, + \\ // at end \\ } \\} \\ @@ -47,6 +52,17 @@ test "zig fmt: comments before var decl in struct" { \\ permitted: u32, \\ inheritable: u32, \\ }; + \\ + \\ // in between + \\ + \\ /// All of these are mandated as little endian + \\ /// when on disk. + \\ const Data = struct { + \\ permitted: u32, + \\ inheritable: u32, + \\ }; + \\ + \\ // at end \\}; \\ ); @@ -106,6 +122,10 @@ test "zig fmt: comments before statements" { \\test "std" { \\ // statement comment \\ _ = @import("foo/bar.zig"); + \\ + \\ // middle + \\ + \\ // end \\} \\ ); @@ -113,17 +133,27 @@ test "zig fmt: comments before statements" { test "zig fmt: comments before test decl" { try testCanonical( - \\// top level comment + \\/// top level doc comment \\test "hi" {} \\ + \\// top level normal comment + \\test "hi" {} + \\ + \\// middle + \\ + \\// end + \\ ); } -test "zig fmt: get stdout or fail" { +test "zig fmt: comments before variable declarations" { try testCanonical( \\const std = @import("std"); \\ \\pub fn main() !void { + \\ /// If this program is run without stdout attached, exit with an error. + \\ /// another comment + \\ var stdout_file = try std.io.getStdOut; \\ // If this program is run without stdout attached, exit with an error. \\ // another comment \\ var stdout_file = try std.io.getStdOut; diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index a2c4def9e0..1d24a88d29 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -137,6 +137,7 @@ pub const Token = struct { IntegerLiteral, FloatLiteral, LineComment, + DocComment, Keyword_align, Keyword_and, Keyword_asm, @@ -257,6 +258,7 @@ pub const Tokenizer = struct { Asterisk, AsteriskPercent, Slash, + LineCommentStart, LineComment, Zero, IntegerLiteral, @@ -822,8 +824,7 @@ pub const Tokenizer = struct { State.Slash => switch (c) { '/' => { - result.id = Token.Id.LineComment; - state = State.LineComment; + state = State.LineCommentStart; }, '=' => { result.id = Token.Id.SlashEqual; @@ -835,6 +836,17 @@ pub const Tokenizer = struct { break; }, }, + State.LineCommentStart => switch (c) { + '/' => { + result.id = Token.Id.DocComment; + state = State.LineComment; + }, + '\n' => { + result.id = Token.Id.LineComment; + break; + }, + else => self.checkLiteralCharacter(), + }, State.LineComment => switch (c) { '\n' => break, else => self.checkLiteralCharacter(), @@ -920,6 +932,7 @@ pub const Tokenizer = struct { result.id = id; } }, + State.LineCommentStart, State.LineComment => { result.id = Token.Id.Eof; }, -- cgit v1.2.3 From 0bf7ebcfea7934cb972aef84b25494c92f0dcf6f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 00:52:09 -0400 Subject: std.zig.tokenizer: fix handling of line comment / doc comment --- std/zig/parser_test.zig | 1 + std/zig/tokenizer.zig | 115 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 91 insertions(+), 25 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index b4cbef33ba..74a49a70e3 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -124,6 +124,7 @@ test "zig fmt: comments before statements" { \\ _ = @import("foo/bar.zig"); \\ \\ // middle + \\ // middle2 \\ \\ // end \\} diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 1d24a88d29..9f5712fa99 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -260,6 +260,7 @@ pub const Tokenizer = struct { Slash, LineCommentStart, LineComment, + DocComment, Zero, IntegerLiteral, IntegerLiteralWithRadix, @@ -825,6 +826,7 @@ pub const Tokenizer = struct { State.Slash => switch (c) { '/' => { state = State.LineCommentStart; + result.id = Token.Id.LineComment; }, '=' => { result.id = Token.Id.SlashEqual; @@ -839,15 +841,15 @@ pub const Tokenizer = struct { State.LineCommentStart => switch (c) { '/' => { result.id = Token.Id.DocComment; - state = State.LineComment; + state = State.DocComment; }, - '\n' => { - result.id = Token.Id.LineComment; - break; + '\n' => break, + else => { + state = State.LineComment; + self.checkLiteralCharacter(); }, - else => self.checkLiteralCharacter(), }, - State.LineComment => switch (c) { + State.LineComment, State.DocComment => switch (c) { '\n' => break, else => self.checkLiteralCharacter(), }, @@ -934,7 +936,10 @@ pub const Tokenizer = struct { }, State.LineCommentStart, State.LineComment => { - result.id = Token.Id.Eof; + result.id = Token.Id.LineComment; + }, + State.DocComment => { + result.id = Token.Id.DocComment; }, State.NumberDot, @@ -1105,41 +1110,77 @@ test "tokenizer - invalid literal/comment characters" { Token.Id.Invalid, }); testTokenize("//\x00", []Token.Id { + Token.Id.LineComment, Token.Id.Invalid, }); testTokenize("//\x1f", []Token.Id { + Token.Id.LineComment, Token.Id.Invalid, }); testTokenize("//\x7f", []Token.Id { + Token.Id.LineComment, Token.Id.Invalid, }); } test "tokenizer - utf8" { - testTokenize("//\xc2\x80", []Token.Id{}); - testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{}); + testTokenize("//\xc2\x80", []Token.Id{Token.Id.LineComment}); + testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{Token.Id.LineComment}); } test "tokenizer - invalid utf8" { - testTokenize("//\x80", []Token.Id{Token.Id.Invalid}); - testTokenize("//\xbf", []Token.Id{Token.Id.Invalid}); - testTokenize("//\xf8", []Token.Id{Token.Id.Invalid}); - testTokenize("//\xff", []Token.Id{Token.Id.Invalid}); - testTokenize("//\xc2\xc0", []Token.Id{Token.Id.Invalid}); - testTokenize("//\xe0", []Token.Id{Token.Id.Invalid}); - testTokenize("//\xf0", []Token.Id{Token.Id.Invalid}); - testTokenize("//\xf0\x90\x80\xc0", []Token.Id{Token.Id.Invalid}); + testTokenize("//\x80", []Token.Id{ + Token.Id.LineComment, + Token.Id.Invalid, + }); + testTokenize("//\xbf", []Token.Id{ + Token.Id.LineComment, + Token.Id.Invalid, + }); + testTokenize("//\xf8", []Token.Id{ + Token.Id.LineComment, + Token.Id.Invalid, + }); + testTokenize("//\xff", []Token.Id{ + Token.Id.LineComment, + Token.Id.Invalid, + }); + testTokenize("//\xc2\xc0", []Token.Id{ + Token.Id.LineComment, + Token.Id.Invalid, + }); + testTokenize("//\xe0", []Token.Id{ + Token.Id.LineComment, + Token.Id.Invalid, + }); + testTokenize("//\xf0", []Token.Id{ + Token.Id.LineComment, + Token.Id.Invalid, + }); + testTokenize("//\xf0\x90\x80\xc0", []Token.Id{ + Token.Id.LineComment, + Token.Id.Invalid, + }); } test "tokenizer - illegal unicode codepoints" { // unicode newline characters.U+0085, U+2028, U+2029 - testTokenize("//\xc2\x84", []Token.Id{}); - testTokenize("//\xc2\x85", []Token.Id{Token.Id.Invalid}); - testTokenize("//\xc2\x86", []Token.Id{}); - testTokenize("//\xe2\x80\xa7", []Token.Id{}); - testTokenize("//\xe2\x80\xa8", []Token.Id{Token.Id.Invalid}); - testTokenize("//\xe2\x80\xa9", []Token.Id{Token.Id.Invalid}); - testTokenize("//\xe2\x80\xaa", []Token.Id{}); + testTokenize("//\xc2\x84", []Token.Id{Token.Id.LineComment}); + testTokenize("//\xc2\x85", []Token.Id{ + Token.Id.LineComment, + Token.Id.Invalid, + }); + testTokenize("//\xc2\x86", []Token.Id{Token.Id.LineComment}); + testTokenize("//\xe2\x80\xa7", []Token.Id{Token.Id.LineComment}); + testTokenize("//\xe2\x80\xa8", []Token.Id{ + Token.Id.LineComment, + Token.Id.Invalid, + }); + testTokenize("//\xe2\x80\xa9", []Token.Id{ + Token.Id.LineComment, + Token.Id.Invalid, + }); + testTokenize("//\xe2\x80\xaa", []Token.Id{Token.Id.LineComment}); } test "tokenizer - string identifier and builtin fns" { @@ -1166,11 +1207,35 @@ test "tokenizer - pipe and then invalid" { }); } +test "tokenizer - line comment and doc comment" { + testTokenize("//", []Token.Id{Token.Id.LineComment}); + testTokenize("// a / b", []Token.Id{Token.Id.LineComment}); + testTokenize("// /", []Token.Id{Token.Id.LineComment}); + testTokenize("/// a", []Token.Id{Token.Id.DocComment}); + testTokenize("///", []Token.Id{Token.Id.DocComment}); +} + +test "tokenizer - line comment followed by identifier" { + testTokenize( + \\ Unexpected, + \\ // another + \\ Another, + , []Token.Id{ + Token.Id.Identifier, + Token.Id.Comma, + Token.Id.LineComment, + Token.Id.Identifier, + Token.Id.Comma, + }); +} + fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { var tokenizer = Tokenizer.init(source); for (expected_tokens) |expected_token_id| { const token = tokenizer.next(); - std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id)); + if (@TagType(Token.Id)(token.id) != @TagType(Token.Id)(expected_token_id)) { + std.debug.panic("expected {}, found {}\n", @tagName(@TagType(Token.Id)(expected_token_id)), @tagName(@TagType(Token.Id)(token.id))); + } switch (expected_token_id) { Token.Id.StringLiteral => |expected_kind| { std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable }); -- cgit v1.2.3 From 54987c3d8f358e4ded9113a9ee5fa04dc1372a56 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 00:56:59 -0400 Subject: std.zig.tokenizer: 3 slashes is doc comment, 4 is line comment --- std/zig/tokenizer.zig | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 9f5712fa99..92a0fbc5d5 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -260,6 +260,7 @@ pub const Tokenizer = struct { Slash, LineCommentStart, LineComment, + DocCommentStart, DocComment, Zero, IntegerLiteral, @@ -840,8 +841,7 @@ pub const Tokenizer = struct { }, State.LineCommentStart => switch (c) { '/' => { - result.id = Token.Id.DocComment; - state = State.DocComment; + state = State.DocCommentStart; }, '\n' => break, else => { @@ -849,6 +849,20 @@ pub const Tokenizer = struct { self.checkLiteralCharacter(); }, }, + State.DocCommentStart => switch (c) { + '/' => { + state = State.LineComment; + }, + '\n' => { + result.id = Token.Id.DocComment; + break; + }, + else => { + state = State.DocComment; + result.id = Token.Id.DocComment; + self.checkLiteralCharacter(); + }, + }, State.LineComment, State.DocComment => switch (c) { '\n' => break, else => self.checkLiteralCharacter(), @@ -938,7 +952,7 @@ pub const Tokenizer = struct { State.LineComment => { result.id = Token.Id.LineComment; }, - State.DocComment => { + State.DocComment, State.DocCommentStart => { result.id = Token.Id.DocComment; }, @@ -1213,6 +1227,7 @@ test "tokenizer - line comment and doc comment" { testTokenize("// /", []Token.Id{Token.Id.LineComment}); testTokenize("/// a", []Token.Id{Token.Id.DocComment}); testTokenize("///", []Token.Id{Token.Id.DocComment}); + testTokenize("////", []Token.Id{Token.Id.LineComment}); } test "tokenizer - line comment followed by identifier" { -- cgit v1.2.3 From e14db2366160840e0c25f3a467ff984304831e4c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 01:03:38 -0400 Subject: run zig fmt on std/os/index.zig --- std/os/index.zig | 328 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 202 insertions(+), 126 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index 99aa7891cb..4f1826021f 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -3,7 +3,8 @@ const builtin = @import("builtin"); const Os = builtin.Os; const is_windows = builtin.os == Os.windows; const is_posix = switch (builtin.os) { - builtin.Os.linux, builtin.Os.macosx => true, + builtin.Os.linux, + builtin.Os.macosx => true, else => false, }; const os = this; @@ -24,9 +25,10 @@ pub const windows = @import("windows/index.zig"); pub const darwin = @import("darwin.zig"); pub const linux = @import("linux/index.zig"); pub const zen = @import("zen.zig"); -pub const posix = switch(builtin.os) { +pub const posix = switch (builtin.os) { Os.linux => linux, - Os.macosx, Os.ios => darwin, + Os.macosx, + Os.ios => darwin, Os.zen => zen, else => @compileError("Unsupported OS"), }; @@ -58,7 +60,7 @@ pub const windowsWrite = windows_util.windowsWrite; pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; pub const windowsOpen = windows_util.windowsOpen; pub const windowsLoadDll = windows_util.windowsLoadDll; -pub const windowsUnloadDll = windows_util.windowsUnloadDll; +pub const windowsUnloadDll = windows_util.windowsUnloadDll; pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; pub const WindowsWaitError = windows_util.WaitError; @@ -97,9 +99,9 @@ pub fn getRandomBytes(buf: []u8) !void { switch (err) { posix.EINVAL => unreachable, posix.EFAULT => unreachable, - posix.EINTR => continue, + posix.EINTR => continue, posix.ENOSYS => { - const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0); + const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); defer close(fd); try posixRead(fd, buf); @@ -110,8 +112,9 @@ pub fn getRandomBytes(buf: []u8) !void { } return; }, - Os.macosx, Os.ios => { - const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0); + Os.macosx, + Os.ios => { + const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); defer close(fd); try posixRead(fd, buf); @@ -134,7 +137,20 @@ pub fn getRandomBytes(buf: []u8) !void { } }, Os.zen => { - const randomness = []u8 {42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45}; + const randomness = []u8 { + 42, + 1, + 7, + 12, + 22, + 17, + 99, + 16, + 26, + 87, + 41, + 45, + }; var i: usize = 0; while (i < buf.len) : (i += 1) { if (i > randomness.len) return error.Unknown; @@ -159,7 +175,9 @@ pub fn abort() noreturn { c.abort(); } switch (builtin.os) { - Os.linux, Os.macosx, Os.ios => { + Os.linux, + Os.macosx, + Os.ios => { _ = posix.raise(posix.SIGABRT); _ = posix.raise(posix.SIGKILL); while (true) {} @@ -181,7 +199,9 @@ pub fn exit(status: u8) noreturn { c.exit(status); } switch (builtin.os) { - Os.linux, Os.macosx, Os.ios => { + Os.linux, + Os.macosx, + Os.ios => { posix.exit(status); }, Os.windows => { @@ -230,12 +250,14 @@ pub fn posixRead(fd: i32, buf: []u8) !void { if (err > 0) { return switch (err) { posix.EINTR => continue, - posix.EINVAL, posix.EFAULT => unreachable, + posix.EINVAL, + posix.EFAULT => unreachable, posix.EAGAIN => error.WouldBlock, posix.EBADF => error.FileClosed, posix.EIO => error.InputOutput, posix.EISDIR => error.IsDir, - posix.ENOBUFS, posix.ENOMEM => error.SystemResources, + posix.ENOBUFS, + posix.ENOMEM => error.SystemResources, else => unexpectedErrorPosix(err), }; } @@ -269,18 +291,19 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { const write_err = posix.getErrno(rc); if (write_err > 0) { return switch (write_err) { - posix.EINTR => continue, - posix.EINVAL, posix.EFAULT => unreachable, + posix.EINTR => continue, + posix.EINVAL, + posix.EFAULT => unreachable, posix.EAGAIN => PosixWriteError.WouldBlock, posix.EBADF => PosixWriteError.FileClosed, posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired, posix.EDQUOT => PosixWriteError.DiskQuota, - posix.EFBIG => PosixWriteError.FileTooBig, - posix.EIO => PosixWriteError.InputOutput, + posix.EFBIG => PosixWriteError.FileTooBig, + posix.EIO => PosixWriteError.InputOutput, posix.ENOSPC => PosixWriteError.NoSpaceLeft, - posix.EPERM => PosixWriteError.AccessDenied, - posix.EPIPE => PosixWriteError.BrokenPipe, - else => unexpectedErrorPosix(write_err), + posix.EPERM => PosixWriteError.AccessDenied, + posix.EPIPE => PosixWriteError.BrokenPipe, + else => unexpectedErrorPosix(write_err), }; } index += rc; @@ -326,7 +349,8 @@ pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 { posix.EFAULT => unreachable, posix.EINVAL => unreachable, posix.EACCES => return PosixOpenError.AccessDenied, - posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig, + posix.EFBIG, + posix.EOVERFLOW => return PosixOpenError.FileTooBig, posix.EISDIR => return PosixOpenError.IsDir, posix.ELOOP => return PosixOpenError.SymLinkLoop, posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded, @@ -351,7 +375,8 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void { const err = posix.getErrno(posix.dup2(old_fd, new_fd)); if (err > 0) { return switch (err) { - posix.EBUSY, posix.EINTR => continue, + posix.EBUSY, + posix.EINTR => continue, posix.EMFILE => error.ProcessFdQuotaExceeded, posix.EINVAL => unreachable, else => unexpectedErrorPosix(err), @@ -386,7 +411,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void { for (envp_buf) |env| { - const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; + const env_buf = if (env) |ptr| ptr[0..cstr.len(ptr) + 1] else break; allocator.free(env_buf); } allocator.free(envp_buf); @@ -397,9 +422,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void { /// pointers after the args and after the environment variables. /// `argv[0]` is the executable path. /// This function also uses the PATH environment variable to get the full path to the executable. -pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, - allocator: &Allocator) !void -{ +pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, allocator: &Allocator) !void { const argv_buf = try allocator.alloc(?&u8, argv.len + 1); mem.set(?&u8, argv_buf, null); defer { @@ -438,7 +461,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, while (it.next()) |search_path| { mem.copy(u8, path_buf, search_path); path_buf[search_path.len] = '/'; - mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); + mem.copy(u8, path_buf[search_path.len + 1..], exe_path); path_buf[search_path.len + exe_path.len + 1] = 0; err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); assert(err > 0); @@ -470,10 +493,17 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { assert(err > 0); return switch (err) { posix.EFAULT => unreachable, - posix.E2BIG, posix.EMFILE, posix.ENAMETOOLONG, posix.ENFILE, posix.ENOMEM => error.SystemResources, - posix.EACCES, posix.EPERM => error.AccessDenied, - posix.EINVAL, posix.ENOEXEC => error.InvalidExe, - posix.EIO, posix.ELOOP => error.FileSystem, + posix.E2BIG, + posix.EMFILE, + posix.ENAMETOOLONG, + posix.ENFILE, + posix.ENOMEM => error.SystemResources, + posix.EACCES, + posix.EPERM => error.AccessDenied, + posix.EINVAL, + posix.ENOEXEC => error.InvalidExe, + posix.EIO, + posix.ELOOP => error.FileSystem, posix.EISDIR => error.IsDir, posix.ENOENT => error.FileNotFound, posix.ENOTDIR => error.NotDir, @@ -482,7 +512,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { }; } -pub var linux_aux_raw = []usize{0} ** 38; +pub var linux_aux_raw = []usize {0} ** 38; pub var posix_environ_raw: []&u8 = undefined; /// Caller must free result when done. @@ -496,8 +526,7 @@ pub fn getEnvMap(allocator: &Allocator) !BufMap { var i: usize = 0; while (true) { - if (ptr[i] == 0) - return result; + if (ptr[i] == 0) return result; const key_start = i; @@ -535,8 +564,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 { var line_i: usize = 0; while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} const this_key = ptr[0..line_i]; - if (!mem.eql(u8, key, this_key)) - continue; + if (!mem.eql(u8, key, this_key)) continue; var end_i: usize = line_i; while (ptr[end_i] != 0) : (end_i += 1) {} @@ -689,8 +717,10 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr)); if (err > 0) { return switch (err) { - posix.EFAULT, posix.EINVAL => unreachable, - posix.EACCES, posix.EPERM => error.AccessDenied, + posix.EFAULT, + posix.EINVAL => unreachable, + posix.EACCES, + posix.EPERM => error.AccessDenied, posix.EDQUOT => error.DiskQuota, posix.EEXIST => error.PathAlreadyExists, posix.EIO => error.FileSystem, @@ -707,9 +737,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: } // here we replace the standard +/ with -_ so that it can be used in a file name -const b64_fs_encoder = base64.Base64Encoder.init( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", - base64.standard_pad_char); +const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { if (symLink(allocator, existing_path, new_path)) { @@ -728,7 +756,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: tmp_path[dirname.len] = os.path.sep; while (true) { try getRandomBytes(rand_buf[0..]); - b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); + b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf); if (symLink(allocator, existing_path, tmp_path)) { return rename(allocator, tmp_path, new_path); @@ -737,7 +765,6 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: else => return err, // TODO zig should know this set does not include PathAlreadyExists } } - } pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void { @@ -760,7 +787,8 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void { return switch (err) { windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, windows.ERROR.ACCESS_DENIED => error.AccessDenied, - windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong, + windows.ERROR.FILENAME_EXCED_RANGE, + windows.ERROR.INVALID_PARAMETER => error.NameTooLong, else => unexpectedErrorWindows(err), }; } @@ -776,9 +804,11 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void { const err = posix.getErrno(posix.unlink(buf.ptr)); if (err > 0) { return switch (err) { - posix.EACCES, posix.EPERM => error.AccessDenied, + posix.EACCES, + posix.EPERM => error.AccessDenied, posix.EBUSY => error.FileBusy, - posix.EFAULT, posix.EINVAL => unreachable, + posix.EFAULT, + posix.EINVAL => unreachable, posix.EIO => error.FileSystem, posix.EISDIR => error.IsDir, posix.ELOOP => error.SymLinkLoop, @@ -856,7 +886,7 @@ pub const AtomicFile = struct { while (true) { try getRandomBytes(rand_buf[0..]); - b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); + b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf); const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) { error.PathAlreadyExists => continue, @@ -907,7 +937,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) new_buf[new_path.len] = 0; if (is_windows) { - const flags = windows.MOVEFILE_REPLACE_EXISTING|windows.MOVEFILE_WRITE_THROUGH; + const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) { const err = windows.GetLastError(); return switch (err) { @@ -918,10 +948,12 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr)); if (err > 0) { return switch (err) { - posix.EACCES, posix.EPERM => error.AccessDenied, + posix.EACCES, + posix.EPERM => error.AccessDenied, posix.EBUSY => error.FileBusy, posix.EDQUOT => error.DiskQuota, - posix.EFAULT, posix.EINVAL => unreachable, + posix.EFAULT, + posix.EINVAL => unreachable, posix.EISDIR => error.IsDir, posix.ELOOP => error.SymLinkLoop, posix.EMLINK => error.LinkQuotaExceeded, @@ -930,7 +962,8 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) posix.ENOTDIR => error.NotDir, posix.ENOMEM => error.SystemResources, posix.ENOSPC => error.NoSpaceLeft, - posix.EEXIST, posix.ENOTEMPTY => error.PathAlreadyExists, + posix.EEXIST, + posix.ENOTEMPTY => error.PathAlreadyExists, posix.EROFS => error.ReadOnlyFileSystem, posix.EXDEV => error.RenameAcrossMountPoints, else => unexpectedErrorPosix(err), @@ -968,7 +1001,8 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void { const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755)); if (err > 0) { return switch (err) { - posix.EACCES, posix.EPERM => error.AccessDenied, + posix.EACCES, + posix.EPERM => error.AccessDenied, posix.EDQUOT => error.DiskQuota, posix.EEXIST => error.PathAlreadyExists, posix.EFAULT => unreachable, @@ -998,27 +1032,23 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) !void { // TODO stat the file and return an error if it's not a directory // this is important because otherwise a dangling symlink // could cause an infinite loop - if (end_index == resolved_path.len) - return; + if (end_index == resolved_path.len) return; } else if (err == error.FileNotFound) { // march end_index backward until next path component while (true) { end_index -= 1; - if (os.path.isSep(resolved_path[end_index])) - break; + if (os.path.isSep(resolved_path[end_index])) break; } continue; } else { return err; } }; - if (end_index == resolved_path.len) - return; + if (end_index == resolved_path.len) return; // march end_index forward until next path component while (true) { end_index += 1; - if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) - break; + if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break; } } } @@ -1035,15 +1065,18 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void { const err = posix.getErrno(posix.rmdir(path_buf.ptr)); if (err > 0) { return switch (err) { - posix.EACCES, posix.EPERM => error.AccessDenied, + posix.EACCES, + posix.EPERM => error.AccessDenied, posix.EBUSY => error.FileBusy, - posix.EFAULT, posix.EINVAL => unreachable, + posix.EFAULT, + posix.EINVAL => unreachable, posix.ELOOP => error.SymLinkLoop, posix.ENAMETOOLONG => error.NameTooLong, posix.ENOENT => error.FileNotFound, posix.ENOMEM => error.SystemResources, posix.ENOTDIR => error.NotDir, - posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty, + posix.EEXIST, + posix.ENOTEMPTY => error.DirNotEmpty, posix.EROFS => error.ReadOnlyFileSystem, else => unexpectedErrorPosix(err), }; @@ -1053,7 +1086,7 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void { /// Whether ::full_path describes a symlink, file, or directory, this function /// removes it. If it cannot be removed because it is a non-empty directory, /// this function recursively removes its entries and then tries again. -// TODO non-recursive implementation +/// TODO non-recursive implementation const DeleteTreeError = error { OutOfMemory, AccessDenied, @@ -1095,8 +1128,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! error.NotDir, error.FileSystem, error.FileBusy, - error.Unexpected - => return err, + error.Unexpected => return err, } { var dir = Dir.open(allocator, full_path) catch |err| switch (err) { @@ -1120,8 +1152,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! error.SystemResources, error.NoSpaceLeft, error.PathAlreadyExists, - error.Unexpected - => return err, + error.Unexpected => return err, }; defer dir.close(); @@ -1151,7 +1182,8 @@ pub const Dir = struct { end_index: usize, const darwin_seek_t = switch (builtin.os) { - Os.macosx, Os.ios => i64, + Os.macosx, + Os.ios => i64, else => void, }; @@ -1175,12 +1207,14 @@ pub const Dir = struct { pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir { const fd = switch (builtin.os) { Os.windows => @compileError("TODO support Dir.open for windows"), - Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0), - Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0), + Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0), + Os.macosx, + Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, 0), else => @compileError("Dir.open is not supported for this platform"), }; const darwin_seek_init = switch (builtin.os) { - Os.macosx, Os.ios => 0, + Os.macosx, + Os.ios => 0, else => {}, }; return Dir { @@ -1203,7 +1237,8 @@ pub const Dir = struct { pub fn next(self: &Dir) !?Entry { switch (builtin.os) { Os.linux => return self.nextLinux(), - Os.macosx, Os.ios => return self.nextDarwin(), + Os.macosx, + Os.ios => return self.nextDarwin(), Os.windows => return self.nextWindows(), else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)), } @@ -1217,12 +1252,13 @@ pub const Dir = struct { } while (true) { - const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, - &self.darwin_seek); + const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, &self.darwin_seek); const err = posix.getErrno(result); if (err > 0) { switch (err) { - posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, + posix.EBADF, + posix.EFAULT, + posix.ENOTDIR => unreachable, posix.EINVAL => { self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2); continue; @@ -1230,14 +1266,13 @@ pub const Dir = struct { else => return unexpectedErrorPosix(err), } } - if (result == 0) - return null; + if (result == 0) return null; self.index = 0; self.end_index = result; break; } } - const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]); + const darwin_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]); const next_index = self.index + darwin_entry.d_reclen; self.index = next_index; @@ -1282,7 +1317,9 @@ pub const Dir = struct { const err = posix.getErrno(result); if (err > 0) { switch (err) { - posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, + posix.EBADF, + posix.EFAULT, + posix.ENOTDIR => unreachable, posix.EINVAL => { self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2); continue; @@ -1290,14 +1327,13 @@ pub const Dir = struct { else => return unexpectedErrorPosix(err), } } - if (result == 0) - return null; + if (result == 0) return null; self.index = 0; self.end_index = result; break; } } - const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]); + const linux_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]); const next_index = self.index + linux_entry.d_reclen; self.index = next_index; @@ -1366,7 +1402,8 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 { if (err > 0) { return switch (err) { posix.EACCES => error.AccessDenied, - posix.EFAULT, posix.EINVAL => unreachable, + posix.EFAULT, + posix.EINVAL => unreachable, posix.EIO => error.FileSystem, posix.ELOOP => error.SymLinkLoop, posix.ENAMETOOLONG => error.NameTooLong, @@ -1459,8 +1496,7 @@ pub const ArgIteratorPosix = struct { } pub fn next(self: &ArgIteratorPosix) ?[]const u8 { - if (self.index == self.count) - return null; + if (self.index == self.count) return null; const s = raw[self.index]; self.index += 1; @@ -1468,8 +1504,7 @@ pub const ArgIteratorPosix = struct { } pub fn skip(self: &ArgIteratorPosix) bool { - if (self.index == self.count) - return false; + if (self.index == self.count) return false; self.index += 1; return true; @@ -1487,7 +1522,9 @@ pub const ArgIteratorWindows = struct { quote_count: usize, seen_quote_count: usize, - pub const NextError = error{OutOfMemory}; + pub const NextError = error { + OutOfMemory, + }; pub fn init() ArgIteratorWindows { return initWithCmdLine(windows.GetCommandLineA()); @@ -1510,7 +1547,8 @@ pub const ArgIteratorWindows = struct { const byte = self.cmd_line[self.index]; switch (byte) { 0 => return null, - ' ', '\t' => continue, + ' ', + '\t' => continue, else => break, } } @@ -1524,7 +1562,8 @@ pub const ArgIteratorWindows = struct { const byte = self.cmd_line[self.index]; switch (byte) { 0 => return false, - ' ', '\t' => continue, + ' ', + '\t' => continue, else => break, } } @@ -1543,7 +1582,8 @@ pub const ArgIteratorWindows = struct { '\\' => { backslash_count += 1; }, - ' ', '\t' => { + ' ', + '\t' => { if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) { return true; } @@ -1583,7 +1623,8 @@ pub const ArgIteratorWindows = struct { '\\' => { backslash_count += 1; }, - ' ', '\t' => { + ' ', + '\t' => { try self.emitBackslashes(&buf, backslash_count); backslash_count = 0; if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) { @@ -1627,7 +1668,6 @@ pub const ArgIteratorWindows = struct { } } } - }; pub const ArgIterator = struct { @@ -1642,7 +1682,7 @@ pub const ArgIterator = struct { } pub const NextError = ArgIteratorWindows.NextError; - + /// You must free the returned memory when done. pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) { if (builtin.os == Os.windows) { @@ -1717,15 +1757,47 @@ pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void { } test "windows arg parsing" { - testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"}); - testWindowsCmdLine(c"\"abc\" d e", [][]const u8{"abc", "d", "e"}); - testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{"a\\\\\\b", "de fg", "h"}); - testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{"a\\\"b", "c", "d"}); - testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{"a\\\\b c", "d", "e"}); - testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{"a", "b", "c", "\"d", "f"}); - - testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", - [][]const u8{".\\..\\zig-cache\\build", "bin\\zig.exe", ".\\..", ".\\..\\zig-cache", "--help"}); + testWindowsCmdLine(c"a b\tc d", [][]const u8 { + "a", + "b", + "c", + "d", + }); + testWindowsCmdLine(c"\"abc\" d e", [][]const u8 { + "abc", + "d", + "e", + }); + testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8 { + "a\\\\\\b", + "de fg", + "h", + }); + testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8 { + "a\\\"b", + "c", + "d", + }); + testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8 { + "a\\\\b c", + "d", + "e", + }); + testWindowsCmdLine(c"a b\tc \"d f", [][]const u8 { + "a", + "b", + "c", + "\"d", + "f", + }); + + testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8 { + ".\\..\\zig-cache\\build", + "bin\\zig.exe", + ".\\..", + ".\\..\\zig-cache", + "--help", + }); } fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const u8) void { @@ -1772,7 +1844,8 @@ pub fn openSelfExe() !os.File { var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); return os.File.openRead(&fixed_allocator.allocator, proc_file_path); }, - Os.macosx, Os.ios => { + Os.macosx, + Os.ios => { var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined; var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); const self_exe_path = try selfExePath(&fixed_allocator.allocator); @@ -1784,8 +1857,10 @@ pub fn openSelfExe() !os.File { test "openSelfExe" { switch (builtin.os) { - Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(), - else => return, // Unsupported OS. + Os.linux, + Os.macosx, + Os.ios => (try openSelfExe()).close(), + else => return, // Unsupported OS. } } @@ -1822,7 +1897,8 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 { try out_path.resize(new_len); } }, - Os.macosx, Os.ios => { + Os.macosx, + Os.ios => { var u32_len: u32 = 0; const ret1 = c._NSGetExecutablePath(undefined, &u32_len); assert(ret1 != 0); @@ -1850,7 +1926,9 @@ pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 { const dir = path.dirname(full_exe_path); return allocator.shrink(u8, full_exe_path, dir.len); }, - Os.windows, Os.macosx, Os.ios => { + Os.windows, + Os.macosx, + Os.ios => { const self_exe_path = try selfExePath(allocator); errdefer allocator.free(self_exe_path); const dirname = os.path.dirname(self_exe_path); @@ -1907,7 +1985,8 @@ pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded, posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded, - posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources, + posix.ENOBUFS, + posix.ENOMEM => return PosixSocketError.SystemResources, posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported, else => return unexpectedErrorPosix(err), } @@ -1938,7 +2017,7 @@ pub const PosixBindError = error { /// A nonexistent interface was requested or the requested address was not local. AddressNotAvailable, - + /// addr points outside the user's accessible address space. PageFault, @@ -2027,7 +2106,7 @@ pub const PosixAcceptError = error { FileDescriptorClosed, ConnectionAborted, - + /// The addr argument is not in a writable part of the user address space. PageFault, @@ -2040,7 +2119,7 @@ pub const PosixAcceptError = error { /// The system-wide limit on the total number of open files has been reached. SystemFdQuotaExceeded, - + /// Not enough free memory. This often means that the memory allocation is limited /// by the socket buffer limits, not by the system memory. SystemResources, @@ -2076,7 +2155,8 @@ pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError! posix.EINVAL => return PosixAcceptError.InvalidSyscall, posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, - posix.ENOBUFS, posix.ENOMEM => return PosixAcceptError.SystemResources, + posix.ENOBUFS, + posix.ENOMEM => return PosixAcceptError.SystemResources, posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, posix.EPROTO => return PosixAcceptError.ProtocolFailure, @@ -2287,7 +2367,8 @@ pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConn const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); const err = posix.getErrno(rc); switch (err) { - 0, posix.EINPROGRESS => return, + 0, + posix.EINPROGRESS => return, else => return unexpectedErrorPosix(err), posix.EACCES => return PosixConnectError.PermissionDenied, @@ -2351,9 +2432,9 @@ pub const Thread = struct { pub const use_pthreads = is_posix and builtin.link_libc; const Data = if (use_pthreads) struct { - handle: c.pthread_t, - stack_addr: usize, - stack_len: usize, + handle: c.pthread_t, + stack_addr: usize, + stack_len: usize, } else switch (builtin.os) { builtin.Os.linux => struct { pid: i32, @@ -2467,9 +2548,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread outer_context.thread.data.alloc_start = bytes_ptr; const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner); - outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, - parameter, 0, null) ?? - { + outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? { const err = windows.GetLastError(); return switch (err) { else => os.unexpectedErrorWindows(err), @@ -2500,8 +2579,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; const mmap_len = default_stack_size; - const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ|posix.PROT_WRITE, - posix.MAP_PRIVATE|posix.MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0); + const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory; errdefer assert(posix.munmap(stack_addr, mmap_len) == 0); @@ -2547,9 +2625,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread } } else if (builtin.os == builtin.Os.linux) { // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly - const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND - | posix.CLONE_THREAD | posix.CLONE_SYSVSEM - | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; + const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; const newtls: usize = 0; const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid); const err = posix.getErrno(rc); -- cgit v1.2.3 From 76ab1d2b6c9eedd861920ae6b6f8ee06aa482159 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 14:20:56 -0400 Subject: support foo.* for ptr deref See #770 --- doc/langref.html.in | 6 ++++-- src/all_types.hpp | 6 ++++++ src/analyze.cpp | 1 + src/ast_render.cpp | 9 +++++++++ src/ir.cpp | 12 ++++++++++-- src/parser.cpp | 30 ++++++++++++++++++++++++------ test/behavior.zig | 1 + test/cases/pointers.zig | 14 ++++++++++++++ 8 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 test/cases/pointers.zig diff --git a/doc/langref.html.in b/doc/langref.html.in index 16fafdaad9..9fb2ebf9f5 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5958,10 +5958,12 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression -SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) +SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | PtrDerefExpression) FieldAccessExpression = "." Symbol +PtrDerefExpression = ".*" + FnCallExpression = "(" list(Expression, ",") ")" ArrayAccessExpression = "[" Expression "]" @@ -5974,7 +5976,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",") StructLiteralField = "." Symbol "=" Expression -PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" +PrefixOp = "!" | "-" | "~" | ("*" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType diff --git a/src/all_types.hpp b/src/all_types.hpp index d1b2ad61d2..2993589f7b 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -379,6 +379,7 @@ enum NodeType { NodeTypeArrayAccessExpr, NodeTypeSliceExpr, NodeTypeFieldAccessExpr, + NodeTypePtrDeref, NodeTypeUse, NodeTypeBoolLiteral, NodeTypeNullLiteral, @@ -603,6 +604,10 @@ struct AstNodeFieldAccessExpr { Buf *field_name; }; +struct AstNodePtrDerefExpr { + AstNode *target; +}; + enum PrefixOp { PrefixOpInvalid, PrefixOpBoolNot, @@ -911,6 +916,7 @@ struct AstNode { AstNodeCompTime comptime_expr; AstNodeAsmExpr asm_expr; AstNodeFieldAccessExpr field_access_expr; + AstNodePtrDerefExpr ptr_deref_expr; AstNodeContainerDecl container_decl; AstNodeStructField struct_field; AstNodeStringLiteral string_literal; diff --git a/src/analyze.cpp b/src/analyze.cpp index 1ecfe32f4c..99712cbfaf 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3275,6 +3275,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeUnreachable: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: + case NodeTypePtrDeref: case NodeTypeStructField: case NodeTypeContainerInitExpr: case NodeTypeStructValueField: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 2c3e1fc873..3e5ef0fcdb 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -222,6 +222,8 @@ static const char *node_type_str(NodeType node_type) { return "AsmExpr"; case NodeTypeFieldAccessExpr: return "FieldAccessExpr"; + case NodeTypePtrDeref: + return "PtrDerefExpr"; case NodeTypeContainerDecl: return "ContainerDecl"; case NodeTypeStructField: @@ -696,6 +698,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { print_symbol(ar, rhs); break; } + case NodeTypePtrDeref: + { + AstNode *lhs = node->data.ptr_deref_expr.target; + render_node_ungrouped(ar, lhs); + fprintf(ar->f, ".*"); + break; + } case NodeTypeUndefinedLiteral: fprintf(ar->f, "undefined"); break; diff --git a/src/ir.cpp b/src/ir.cpp index 469900bf07..8c7232722e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4548,8 +4548,14 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode } static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) { - assert(node->type == NodeTypePrefixOpExpr); - AstNode *expr_node = node->data.prefix_op_expr.primary_expr; + AstNode *expr_node; + if (node->type == NodeTypePrefixOpExpr) { + expr_node = node->data.prefix_op_expr.primary_expr; + } else if (node->type == NodeTypePtrDeref) { + expr_node = node->data.ptr_deref_expr.target; + } else { + zig_unreachable(); + } IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); if (value == irb->codegen->invalid_instruction) @@ -6527,6 +6533,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_build_load_ptr(irb, scope, node, ptr_instruction); } + case NodeTypePtrDeref: + return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval); case NodeTypeThisLiteral: return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval); case NodeTypeBoolLiteral: diff --git a/src/parser.cpp b/src/parser.cpp index 4b70e904b8..c02546a99d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1046,11 +1046,12 @@ static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index } /* -SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) +SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | PtrDerefExpression | SliceExpression) FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) ArrayAccessExpression : token(LBracket) Expression token(RBracket) SliceExpression = "[" Expression ".." option(Expression) "]" FieldAccessExpression : token(Dot) token(Symbol) +PtrDerefExpression = ".*" StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression */ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { @@ -1131,13 +1132,27 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, } else if (first_token->id == TokenIdDot) { *token_index += 1; - Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); + Token *token = &pc->tokens->at(*token_index); + + if (token->id == TokenIdSymbol) { + *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token); - node->data.field_access_expr.struct_expr = primary_expr; - node->data.field_access_expr.field_name = token_buf(name_token); + AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token); + node->data.field_access_expr.struct_expr = primary_expr; + node->data.field_access_expr.field_name = token_buf(token); + + primary_expr = node; + } else if (token->id == TokenIdStar) { + *token_index += 1; + + AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token); + node->data.ptr_deref_expr.target = primary_expr; + + primary_expr = node; + } else { + ast_invalid_token_error(pc, token); + } - primary_expr = node; } else { return primary_expr; } @@ -3012,6 +3027,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeFieldAccessExpr: visit_field(&node->data.field_access_expr.struct_expr, visit, context); break; + case NodeTypePtrDeref: + visit_field(&node->data.ptr_deref_expr.target, visit, context); + break; case NodeTypeUse: visit_field(&node->data.use.expr, visit, context); break; diff --git a/test/behavior.zig b/test/behavior.zig index 2c10c6d71b..cb484b39a5 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -33,6 +33,7 @@ comptime { _ = @import("cases/misc.zig"); _ = @import("cases/namespace_depends_on_compile_var/index.zig"); _ = @import("cases/null.zig"); + _ = @import("cases/pointers.zig"); _ = @import("cases/pub_enum/index.zig"); _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("cases/reflection.zig"); diff --git a/test/cases/pointers.zig b/test/cases/pointers.zig new file mode 100644 index 0000000000..87b3d25a74 --- /dev/null +++ b/test/cases/pointers.zig @@ -0,0 +1,14 @@ +const std = @import("std"); +const assert = std.debug.assert; + +test "dereference pointer" { + comptime testDerefPtr(); + testDerefPtr(); +} + +fn testDerefPtr() void { + var x: i32 = 1234; + var y = &x; + y.* += 1; + assert(x == 1235); +} -- cgit v1.2.3 From 47680cc0d806775dd9576faaff6303e88b14fb5a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 15:10:55 -0400 Subject: zig fmt: better multiline string handling --- std/zig/parser.zig | 5 +++-- std/zig/parser_test.zig | 14 +++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index b5849c3e96..025bd31715 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -3512,7 +3512,8 @@ pub const Parser = struct { try stack.append(RenderState { .Text = ";" }); if (var_decl.init_node) |init_node| { try stack.append(RenderState { .Expression = init_node }); - try stack.append(RenderState { .Text = " = " }); + const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = "; + try stack.append(RenderState { .Text = text }); } if (var_decl.align_node) |align_node| { try stack.append(RenderState { .Text = ")" }); @@ -4063,7 +4064,7 @@ pub const Parser = struct { try stream.writeByteNTimes(' ', indent + indent_delta); try stream.print("{}", self.tokenizer.getTokenSlice(t)); } - try stream.writeByteNTimes(' ', indent + indent_delta); + try stream.writeByteNTimes(' ', indent); }, ast.Node.Id.UndefinedLiteral => { const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 74a49a70e3..4c238254fa 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -423,10 +423,18 @@ test "zig fmt: functions" { test "zig fmt: multiline string" { try testCanonical( - \\const s = - \\ \\ something - \\ \\ something else + \\test "" { + \\ const s1 = + \\ \\one + \\ \\two) + \\ \\three + \\ ; + \\ const s2 = + \\ c\\one + \\ c\\two) + \\ c\\three \\ ; + \\} \\ ); } -- cgit v1.2.3 From 37d3ef28351027a83249080d3238d61d9346f6db Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 16:16:58 -0400 Subject: zig fmt: support promise->T --- std/zig/ast.zig | 32 ++++++++++++++++++++++++++++++++ std/zig/parser.zig | 32 ++++++++++++++++++++++++++++++++ std/zig/parser_test.zig | 4 ++-- std/zig/tokenizer.zig | 2 ++ 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 716ed8eb7d..b8b399afc6 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -36,6 +36,7 @@ pub const Node = struct { VarType, ErrorType, FnProto, + PromiseType, // Primary expressions IntegerLiteral, @@ -495,6 +496,37 @@ pub const Node = struct { } }; + pub const PromiseType = struct { + base: Node, + promise_token: Token, + result: ?Result, + + pub const Result = struct { + arrow_token: Token, + return_type: &Node, + }; + + pub fn iterate(self: &PromiseType, index: usize) ?&Node { + var i = index; + + if (self.result) |result| { + if (i < 1) return result.return_type; + i -= 1; + } + + return null; + } + + pub fn firstToken(self: &PromiseType) Token { + return self.promise_token; + } + + pub fn lastToken(self: &PromiseType) Token { + if (self.result) |result| return result.return_type.lastToken(); + return self.promise_token; + } + }; + pub const ParamDecl = struct { base: Node, comptime_token: ?Token, diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 025bd31715..afef6704cf 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -2550,6 +2550,30 @@ pub const Parser = struct { _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token); continue; }, + Token.Id.Keyword_promise => { + const node = try arena.construct(ast.Node.PromiseType { + .base = ast.Node { + .id = ast.Node.Id.PromiseType, + .doc_comments = null, + .same_line_comment = null, + }, + .promise_token = token, + .result = null, + }); + opt_ctx.store(&node.base); + const next_token = self.getNextToken(); + if (next_token.id != Token.Id.Arrow) { + self.putBackToken(next_token); + continue; + } + node.result = ast.Node.PromiseType.Result { + .arrow_token = next_token, + .return_type = undefined, + }; + const return_type_ptr = &((??node.result).return_type); + try stack.append(State { .Expression = OptionalCtx { .Required = return_type_ptr, } }); + continue; + }, Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable); continue; @@ -4147,6 +4171,14 @@ pub const Parser = struct { try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) }); } }, + ast.Node.Id.PromiseType => { + const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base); + try stream.write(self.tokenizer.getTokenSlice(promise_type.promise_token)); + if (promise_type.result) |result| { + try stream.write(self.tokenizer.getTokenSlice(result.arrow_token)); + try stack.append(RenderState { .Expression = result.return_type}); + } + }, ast.Node.Id.LineComment => { const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base); try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token)); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 4c238254fa..85fd6c807e 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -948,12 +948,12 @@ test "zig fmt: coroutines" { \\ suspend; \\ x += 1; \\ suspend |p| {} - \\ const p = async simpleAsyncFn() catch unreachable; + \\ const p: promise->void = async simpleAsyncFn() catch unreachable; \\ await p; \\} \\ \\test "coroutine suspend, resume, cancel" { - \\ const p = try async testAsyncSeq(); + \\ const p: promise = try async testAsyncSeq(); \\ resume p; \\ cancel p; \\} diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 92a0fbc5d5..6fc26bc5fd 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -40,6 +40,7 @@ pub const Token = struct { KeywordId{.bytes="null", .id = Id.Keyword_null}, KeywordId{.bytes="or", .id = Id.Keyword_or}, KeywordId{.bytes="packed", .id = Id.Keyword_packed}, + KeywordId{.bytes="promise", .id = Id.Keyword_promise}, KeywordId{.bytes="pub", .id = Id.Keyword_pub}, KeywordId{.bytes="resume", .id = Id.Keyword_resume}, KeywordId{.bytes="return", .id = Id.Keyword_return}, @@ -166,6 +167,7 @@ pub const Token = struct { Keyword_null, Keyword_or, Keyword_packed, + Keyword_promise, Keyword_pub, Keyword_resume, Keyword_return, -- cgit v1.2.3 From 7dc8d433abbf697f05eb1ad2003b6335f750557b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 17:30:57 -0400 Subject: zig fmt: support labeled suspend --- std/zig/ast.zig | 2 ++ std/zig/parser.zig | 21 +++++++++++++++++++++ std/zig/parser_test.zig | 11 +++++++++++ 3 files changed, 34 insertions(+) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index b8b399afc6..70758ece58 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1371,6 +1371,7 @@ pub const Node = struct { pub const Suspend = struct { base: Node, + label: ?Token, suspend_token: Token, payload: ?&Node, body: ?&Node, @@ -1392,6 +1393,7 @@ pub const Node = struct { } pub fn firstToken(self: &Suspend) Token { + if (self.label) |label| return label; return self.suspend_token; } diff --git a/std/zig/parser.zig b/std/zig/parser.zig index afef6704cf..ebc0d5b06e 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -1093,6 +1093,23 @@ pub const Parser = struct { }) catch unreachable; continue; }, + Token.Id.Keyword_suspend => { + const node = try arena.construct(ast.Node.Suspend { + .base = ast.Node { + .id = ast.Node.Id.Suspend, + .doc_comments = null, + .same_line_comment = null, + }, + .label = ctx.label, + .suspend_token = token, + .payload = null, + .body = null, + }); + ctx.opt_ctx.store(&node.base); + stack.append(State { .SuspendBody = node }) catch unreachable; + try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + continue; + }, Token.Id.Keyword_inline => { stack.append(State { .Inline = InlineCtx { @@ -3046,6 +3063,7 @@ pub const Parser = struct { const node = try self.createToCtxNode(arena, ctx, ast.Node.Suspend, ast.Node.Suspend { .base = undefined, + .label = null, .suspend_token = *token, .payload = null, .body = null, @@ -3655,6 +3673,9 @@ pub const Parser = struct { }, ast.Node.Id.Suspend => { const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); + if (suspend_node.label) |label| { + try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); + } try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token)); if (suspend_node.body) |body| { diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 85fd6c807e..c82030d22b 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,14 @@ +test "zig fmt: labeled suspend" { + try testCanonical( + \\fn foo() void { + \\ s: suspend |p| { + \\ break :s; + \\ } + \\} + \\ + ); +} + test "zig fmt: comments before error set decl" { try testCanonical( \\const UnexpectedError = error { -- cgit v1.2.3 From 1d06915f275ac59918c32206ad944fd10cc997d4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 18:20:27 -0400 Subject: zig fmt: support union(enum(tag)) and enum init values --- std/zig/ast.zig | 11 ++++++++++- std/zig/parser.zig | 40 ++++++++++++++++++++++++++++++++++++++-- std/zig/parser_test.zig | 12 ++++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 70758ece58..f0d2f92e29 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -261,7 +261,7 @@ pub const Node = struct { const InitArg = union(enum) { None, - Enum, + Enum: ?&Node, Type: &Node, }; @@ -321,6 +321,7 @@ pub const Node = struct { base: Node, name_token: Token, type_expr: ?&Node, + value_expr: ?&Node, pub fn iterate(self: &UnionTag, index: usize) ?&Node { var i = index; @@ -330,6 +331,11 @@ pub const Node = struct { i -= 1; } + if (self.value_expr) |value_expr| { + if (i < 1) return value_expr; + i -= 1; + } + return null; } @@ -338,6 +344,9 @@ pub const Node = struct { } pub fn lastToken(self: &UnionTag) Token { + if (self.value_expr) |value_expr| { + return value_expr.lastToken(); + } if (self.type_expr) |type_expr| { return type_expr.lastToken(); } diff --git a/std/zig/parser.zig b/std/zig/parser.zig index ebc0d5b06e..12b1c315e8 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -242,6 +242,7 @@ pub const Parser = struct { FieldInitListItemOrEnd: ListSave(&ast.Node.FieldInitializer), FieldInitListCommaOrEnd: ListSave(&ast.Node.FieldInitializer), FieldListCommaOrEnd: &ast.Node.ContainerDecl, + FieldInitValue: OptionalCtx, IdentifierListItemOrEnd: ListSave(&ast.Node), IdentifierListCommaOrEnd: ListSave(&ast.Node), SwitchCaseOrEnd: ListSave(&ast.Node), @@ -653,6 +654,15 @@ pub const Parser = struct { continue; }, + State.FieldInitValue => |ctx| { + const eq_tok = self.getNextToken(); + if (eq_tok.id != Token.Id.Equal) { + self.putBackToken(eq_tok); + continue; + } + stack.append(State { .Expression = ctx }) catch unreachable; + continue; + }, State.ContainerKind => |ctx| { const token = self.getNextToken(); @@ -699,7 +709,16 @@ pub const Parser = struct { const init_arg_token = self.getNextToken(); switch (init_arg_token.id) { Token.Id.Keyword_enum => { - container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg.Enum; + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null}; + const lparen_tok = self.getNextToken(); + if (lparen_tok.id == Token.Id.LParen) { + try stack.append(State { .ExpectToken = Token.Id.RParen } ); + try stack.append(State { .Expression = OptionalCtx { + .RequiredNull = &container_decl.init_arg_expr.Enum, + } }); + } else { + self.putBackToken(lparen_tok); + } }, else => { self.putBackToken(init_arg_token); @@ -709,6 +728,7 @@ pub const Parser = struct { } continue; }, + State.ContainerDecl => |container_decl| { while (try self.eatLineComment(arena)) |line_comment| { try container_decl.fields_and_decls.append(&line_comment.base); @@ -744,10 +764,12 @@ pub const Parser = struct { .base = undefined, .name_token = token, .type_expr = null, + .value_expr = null, } ); stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; + try stack.append(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } }); try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } }); try stack.append(State { .IfToken = Token.Id.Colon }); continue; @@ -3515,6 +3537,12 @@ pub const Parser = struct { try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); try stack.append(RenderState { .Text = "," }); + + if (tag.value_expr) |value_expr| { + try stack.append(RenderState { .Expression = value_expr }); + try stack.append(RenderState { .Text = " = " }); + } + if (tag.type_expr) |type_expr| { try stream.print(": "); try stack.append(RenderState { .Expression = type_expr}); @@ -4055,7 +4083,15 @@ pub const Parser = struct { switch (container_decl.init_arg_expr) { ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}), - ast.Node.ContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}), + ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { + if (enum_tag_type) |expr| { + try stack.append(RenderState { .Text = ")) "}); + try stack.append(RenderState { .Expression = expr}); + try stack.append(RenderState { .Text = "(enum("}); + } else { + try stack.append(RenderState { .Text = "(enum) "}); + } + }, ast.Node.ContainerDecl.InitArg.Type => |type_expr| { try stack.append(RenderState { .Text = ") "}); try stack.append(RenderState { .Expression = type_expr}); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index c82030d22b..f8b89ebe44 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,15 @@ +test "zig fmt: union(enum(u32)) with assigned enum values" { + try testCanonical( + \\const MultipleChoice = union(enum(u32)) { + \\ A = 20, + \\ B = 40, + \\ C = 60, + \\ D = 1000, + \\}; + \\ + ); +} + test "zig fmt: labeled suspend" { try testCanonical( \\fn foo() void { -- cgit v1.2.3 From eed49a210474af98f64961ed603feb6dfa60acde Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 18:30:47 -0400 Subject: zig fmt: aggregate type init with only 1 field --- std/zig/parser.zig | 9 +++++++++ std/zig/parser_test.zig | 14 +++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 12b1c315e8..5e3f3bff59 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -3872,6 +3872,15 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = suffix_op.lhs }); continue; } + if (field_inits.len == 1) { + const field_init = field_inits.at(0); + + try stack.append(RenderState { .Text = "}" }); + try stack.append(RenderState { .FieldInitializer = field_init }); + try stack.append(RenderState { .Text = " {" }); + try stack.append(RenderState { .Expression = suffix_op.lhs }); + continue; + } try stack.append(RenderState { .Text = "}"}); try stack.append(RenderState.PrintIndent); try stack.append(RenderState { .Indent = indent }); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index f8b89ebe44..8b0002e6bd 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,12 @@ +test "zig fmt: aggregate type init with only 1 field" { + try testCanonical( + \\comptime { + \\ assert(bar(Payload {.A = 1234}) == -10); + \\} + \\ + ); +} + test "zig fmt: union(enum(u32)) with assigned enum values" { try testCanonical( \\const MultipleChoice = union(enum(u32)) { @@ -709,9 +718,7 @@ test "zig fmt: switch" { \\ Float: f64, \\ }; \\ - \\ const u = Union { - \\ .Int = 0, - \\ }; + \\ const u = Union {.Int = 0}; \\ switch (u) { \\ Union.Int => |int| {}, \\ Union.Float => |*float| unreachable, @@ -1029,6 +1036,7 @@ test "zig fmt: struct literals with fields on each line" { try testCanonical( \\var self = BufSet { \\ .hash_map = BufSetHashMap.init(a), + \\ .hash_map2 = xyz, \\}; \\ ); -- cgit v1.2.3 From 3e61c45f8936f6211ae9b61b2b0358c560344f7b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 18:49:05 -0400 Subject: zig fmt: consistent spacing for container inits --- std/zig/parser.zig | 10 +++++----- std/zig/parser_test.zig | 41 +++++++++-------------------------------- 2 files changed, 14 insertions(+), 37 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 5e3f3bff59..b50150580e 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -3875,9 +3875,9 @@ pub const Parser = struct { if (field_inits.len == 1) { const field_init = field_inits.at(0); - try stack.append(RenderState { .Text = "}" }); + try stack.append(RenderState { .Text = " }" }); try stack.append(RenderState { .FieldInitializer = field_init }); - try stack.append(RenderState { .Text = " {" }); + try stack.append(RenderState { .Text = "{ " }); try stack.append(RenderState { .Expression = suffix_op.lhs }); continue; } @@ -3893,7 +3893,7 @@ pub const Parser = struct { try stack.append(RenderState.PrintIndent); } try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = " {\n"}); + try stack.append(RenderState { .Text = "{\n"}); try stack.append(RenderState { .Expression = suffix_op.lhs }); }, ast.Node.SuffixOp.Op.ArrayInitializer => |exprs| { @@ -3907,7 +3907,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "}" }); try stack.append(RenderState { .Expression = expr }); - try stack.append(RenderState { .Text = " {" }); + try stack.append(RenderState { .Text = "{" }); try stack.append(RenderState { .Expression = suffix_op.lhs }); continue; } @@ -3924,7 +3924,7 @@ pub const Parser = struct { try stack.append(RenderState.PrintIndent); } try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = " {\n"}); + try stack.append(RenderState { .Text = "{\n"}); try stack.append(RenderState { .Expression = suffix_op.lhs }); }, } diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 8b0002e6bd..81f9e56223 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,12 +1,3 @@ -test "zig fmt: aggregate type init with only 1 field" { - try testCanonical( - \\comptime { - \\ assert(bar(Payload {.A = 1234}) == -10); - \\} - \\ - ); -} - test "zig fmt: union(enum(u32)) with assigned enum values" { try testCanonical( \\const MultipleChoice = union(enum(u32)) { @@ -124,7 +115,7 @@ test "zig fmt: same-line comment after field decl" { test "zig fmt: array literal with 1 item on 1 line" { try testCanonical( - \\var s = []const u64 {0} ** 25; + \\var s = []const u64{0} ** 25; \\ ); } @@ -625,11 +616,11 @@ test "zig fmt: error set declaration" { test "zig fmt: arrays" { try testCanonical( \\test "test array" { - \\ const a: [2]u8 = [2]u8 { + \\ const a: [2]u8 = [2]u8{ \\ 1, \\ 2, \\ }; - \\ const a: [2]u8 = []u8 { + \\ const a: [2]u8 = []u8{ \\ 1, \\ 2, \\ }; @@ -641,15 +632,17 @@ test "zig fmt: arrays" { test "zig fmt: container initializers" { try testCanonical( - \\const a1 = []u8{}; - \\const a2 = []u8 { + \\const a0 = []u8{}; + \\const a1 = []u8{1}; + \\const a2 = []u8{ \\ 1, \\ 2, \\ 3, \\ 4, \\}; - \\const s1 = S{}; - \\const s2 = S { + \\const s0 = S{}; + \\const s1 = S{ .a = 1 }; + \\const s2 = S{ \\ .a = 1, \\ .b = 2, \\}; @@ -718,7 +711,6 @@ test "zig fmt: switch" { \\ Float: f64, \\ }; \\ - \\ const u = Union {.Int = 0}; \\ switch (u) { \\ Union.Int => |int| {}, \\ Union.Float => |*float| unreachable, @@ -797,11 +789,6 @@ test "zig fmt: while" { test "zig fmt: for" { try testCanonical( \\test "for" { - \\ const a = []u8 { - \\ 1, - \\ 2, - \\ 3, - \\ }; \\ for (a) |v| { \\ continue; \\ } @@ -1032,16 +1019,6 @@ test "zig fmt: error return" { ); } -test "zig fmt: struct literals with fields on each line" { - try testCanonical( - \\var self = BufSet { - \\ .hash_map = BufSetHashMap.init(a), - \\ .hash_map2 = xyz, - \\}; - \\ - ); -} - const std = @import("std"); const mem = std.mem; const warn = std.debug.warn; -- cgit v1.2.3 From 4cc1008c2d2b438b9847944898ad2d7cfbbdab8b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 19:16:46 -0400 Subject: zig fmt: error set decls --- std/zig/parser.zig | 21 ++++++++++++++++++--- std/zig/parser_test.zig | 46 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index b50150580e..c1bae7f86f 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -4110,14 +4110,30 @@ pub const Parser = struct { }, ast.Node.Id.ErrorSetDecl => { const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); - try stream.print("error "); + + const decls = err_set_decl.decls.toSliceConst(); + if (decls.len == 0) { + try stream.write("error{}"); + continue; + } + + if (decls.len == 1) blk: { + const node = decls[0]; + if (node.same_line_comment != null or node.doc_comments != null) break :blk; + + try stream.write("error{"); + try stack.append(RenderState { .Text = "}" }); + try stack.append(RenderState { .Expression = node }); + continue; + } + + try stream.write("error{"); try stack.append(RenderState { .Text = "}"}); try stack.append(RenderState.PrintIndent); try stack.append(RenderState { .Indent = indent }); try stack.append(RenderState { .Text = "\n"}); - const decls = err_set_decl.decls.toSliceConst(); var i = decls.len; while (i != 0) { i -= 1; @@ -4142,7 +4158,6 @@ pub const Parser = struct { }); } try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = "{"}); }, ast.Node.Id.MultilineStringLiteral => { const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 81f9e56223..e5e3c97526 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,5 +1,35 @@ -test "zig fmt: union(enum(u32)) with assigned enum values" { +test "zig fmt: error set declaration" { try testCanonical( + \\const E = error{ + \\ A, + \\ B, + \\ + \\ C, + \\}; + \\ + \\const Error = error{ + \\ /// no more memory + \\ OutOfMemory, + \\}; + \\ + \\const Error = error{ + \\ /// no more memory + \\ OutOfMemory, + \\ + \\ /// another + \\ Another, + \\ + \\ // end + \\}; + \\ + \\const Error = error{OutOfMemory}; + \\const Error = error{}; + \\ + ); +} + +test "zig fmt: union(enum(u32)) with assigned enum values" { + try testCanonical( \\const MultipleChoice = union(enum(u32)) { \\ A = 20, \\ B = 40, @@ -23,7 +53,7 @@ test "zig fmt: labeled suspend" { test "zig fmt: comments before error set decl" { try testCanonical( - \\const UnexpectedError = error { + \\const UnexpectedError = error{ \\ /// The Operating System returned an undocumented error code. \\ Unexpected, \\ // another @@ -601,18 +631,6 @@ test "zig fmt: union declaration" { ); } -test "zig fmt: error set declaration" { - try testCanonical( - \\const E = error { - \\ A, - \\ B, - \\ - \\ C, - \\}; - \\ - ); -} - test "zig fmt: arrays" { try testCanonical( \\test "test array" { -- cgit v1.2.3 From 61a726c290a4e569ae28da59c94ba6a40df59a20 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 19:27:14 -0400 Subject: zig fmt: comments in field decls --- std/zig/parser.zig | 3 +++ std/zig/parser_test.zig | 13 ++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index c1bae7f86f..1ea3021357 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -3525,6 +3525,7 @@ pub const Parser = struct { }, ast.Node.Id.StructField => { const field = @fieldParentPtr(ast.Node.StructField, "base", decl); + try self.renderComments(stream, &field.base, indent); if (field.visib_token) |visib_token| { try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token)); } @@ -3534,6 +3535,7 @@ pub const Parser = struct { }, ast.Node.Id.UnionTag => { const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); + try self.renderComments(stream, &tag.base, indent); try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); try stack.append(RenderState { .Text = "," }); @@ -3550,6 +3552,7 @@ pub const Parser = struct { }, ast.Node.Id.EnumTag => { const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); + try self.renderComments(stream, &tag.base, indent); try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); try stack.append(RenderState { .Text = "," }); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index e5e3c97526..5d4383d6ac 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,5 +1,16 @@ +test "zig fmt: doc comments before struct field" { + try testCanonical( + \\pub const Allocator = struct { + \\ /// Allocate byte_count bytes and return them in a slice, with the + \\ /// slice's pointer aligned at least to alignment bytes. + \\ allocFn: fn() void, + \\}; + \\ + ); +} + test "zig fmt: error set declaration" { - try testCanonical( + try testCanonical( \\const E = error{ \\ A, \\ B, -- cgit v1.2.3 From 7c822869feac7713063da320c12a960f3ae58298 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 20:25:54 -0400 Subject: zig fmt: only some docs have doc comments --- std/zig/ast.zig | 43 +++++++++++++- std/zig/parser.zig | 153 ++++++++++++++++++++++++++---------------------- std/zig/parser_test.zig | 18 +----- 3 files changed, 125 insertions(+), 89 deletions(-) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index f0d2f92e29..a311aa1d71 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -6,7 +6,6 @@ const mem = std.mem; pub const Node = struct { id: Id, - doc_comments: ?&DocComment, same_line_comment: ?&Token, pub const Id = enum { @@ -70,6 +69,7 @@ pub const Node = struct { StructField, UnionTag, EnumTag, + ErrorTag, AsmInput, AsmOutput, AsyncAttribute, @@ -77,6 +77,13 @@ pub const Node = struct { FieldInitializer, }; + pub fn cast(base: &Node, comptime T: type) ?&T { + if (base.id == comptime typeToId(T)) { + return @fieldParentPtr(T, "base", base); + } + return null; + } + pub fn iterate(base: &Node, index: usize) ?&Node { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { @@ -122,6 +129,7 @@ pub const Node = struct { pub const Root = struct { base: Node, + doc_comments: ?&DocComment, decls: ArrayList(&Node), eof_token: Token, @@ -143,6 +151,7 @@ pub const Node = struct { pub const VarDecl = struct { base: Node, + doc_comments: ?&DocComment, visib_token: ?Token, name_token: Token, eq_token: Token, @@ -191,6 +200,7 @@ pub const Node = struct { pub const Use = struct { base: Node, + doc_comments: ?&DocComment, visib_token: ?Token, expr: &Node, semicolon_token: Token, @@ -294,6 +304,7 @@ pub const Node = struct { pub const StructField = struct { base: Node, + doc_comments: ?&DocComment, visib_token: ?Token, name_token: Token, type_expr: &Node, @@ -319,6 +330,7 @@ pub const Node = struct { pub const UnionTag = struct { base: Node, + doc_comments: ?&DocComment, name_token: Token, type_expr: ?&Node, value_expr: ?&Node, @@ -357,6 +369,7 @@ pub const Node = struct { pub const EnumTag = struct { base: Node, + doc_comments: ?&DocComment, name_token: Token, value: ?&Node, @@ -384,6 +397,31 @@ pub const Node = struct { } }; + pub const ErrorTag = struct { + base: Node, + doc_comments: ?&DocComment, + name_token: Token, + + pub fn iterate(self: &ErrorTag, index: usize) ?&Node { + var i = index; + + if (self.doc_comments) |comments| { + if (i < 1) return &comments.base; + i -= 1; + } + + return null; + } + + pub fn firstToken(self: &ErrorTag) Token { + return self.name_token; + } + + pub fn lastToken(self: &ErrorTag) Token { + return self.name_token; + } + }; + pub const Identifier = struct { base: Node, token: Token, @@ -433,6 +471,7 @@ pub const Node = struct { pub const FnProto = struct { base: Node, + doc_comments: ?&DocComment, visib_token: ?Token, fn_token: Token, name_token: ?Token, @@ -626,6 +665,7 @@ pub const Node = struct { pub const Comptime = struct { base: Node, + doc_comments: ?&DocComment, comptime_token: Token, expr: &Node, @@ -1794,6 +1834,7 @@ pub const Node = struct { pub const TestDecl = struct { base: Node, + doc_comments: ?&DocComment, test_token: Token, name: &Node, body_node: &Node, diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 1ea3021357..ed6a1a4256 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -228,7 +228,6 @@ pub const Parser = struct { Statement: &ast.Node.Block, ComptimeStatement: ComptimeStatementCtx, Semicolon: &&ast.Node, - AddComments: AddCommentsCtx, LookForSameLineComment: &&ast.Node, LookForSameLineCommentDirect: &ast.Node, @@ -243,8 +242,8 @@ pub const Parser = struct { FieldInitListCommaOrEnd: ListSave(&ast.Node.FieldInitializer), FieldListCommaOrEnd: &ast.Node.ContainerDecl, FieldInitValue: OptionalCtx, - IdentifierListItemOrEnd: ListSave(&ast.Node), - IdentifierListCommaOrEnd: ListSave(&ast.Node), + ErrorTagListItemOrEnd: ListSave(&ast.Node), + ErrorTagListCommaOrEnd: ListSave(&ast.Node), SwitchCaseOrEnd: ListSave(&ast.Node), SwitchCaseCommaOrEnd: ListSave(&ast.Node), SwitchCaseFirstItem: &ArrayList(&ast.Node), @@ -301,6 +300,7 @@ pub const Parser = struct { ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx, StringLiteral: OptionalCtx, Identifier: OptionalCtx, + ErrorTag: &&ast.Node, IfToken: @TagType(Token.Id), @@ -325,6 +325,7 @@ pub const Parser = struct { ast.Node.Root { .base = undefined, .decls = ArrayList(&ast.Node).init(arena), + .doc_comments = null, // initialized when we get the eof token .eof_token = undefined, } @@ -354,7 +355,7 @@ pub const Parser = struct { try root_node.decls.append(&line_comment.base); } - const comments = try self.eatComments(arena); + const comments = try self.eatDocComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_test => { @@ -363,7 +364,6 @@ pub const Parser = struct { const block = try arena.construct(ast.Node.Block { .base = ast.Node { .id = ast.Node.Id.Block, - .doc_comments = null, .same_line_comment = null, }, .label = null, @@ -374,9 +374,9 @@ pub const Parser = struct { const test_node = try arena.construct(ast.Node.TestDecl { .base = ast.Node { .id = ast.Node.Id.TestDecl, - .doc_comments = comments, .same_line_comment = null, }, + .doc_comments = comments, .test_token = token, .name = undefined, .body_node = &block.base, @@ -394,7 +394,11 @@ pub const Parser = struct { }, Token.Id.Eof => { root_node.eof_token = token; - return Tree {.root_node = root_node, .arena_allocator = arena_allocator}; + root_node.doc_comments = comments; + return Tree { + .root_node = root_node, + .arena_allocator = arena_allocator, + }; }, Token.Id.Keyword_pub => { stack.append(State.TopLevel) catch unreachable; @@ -424,6 +428,7 @@ pub const Parser = struct { .base = undefined, .comptime_token = token, .expr = &block.base, + .doc_comments = comments, } ); stack.append(State.TopLevel) catch unreachable; @@ -520,6 +525,7 @@ pub const Parser = struct { .visib_token = ctx.visib_token, .expr = undefined, .semicolon_token = undefined, + .doc_comments = ctx.comments, } ); stack.append(State { @@ -556,9 +562,9 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .doc_comments = ctx.comments, .same_line_comment = null, }, + .doc_comments = ctx.comments, .visib_token = ctx.visib_token, .name_token = null, .fn_token = undefined, @@ -625,9 +631,9 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.StructField { .base = ast.Node { .id = ast.Node.Id.StructField, - .doc_comments = null, .same_line_comment = null, }, + .doc_comments = ctx.comments, .visib_token = ctx.visib_token, .name_token = identifier, .type_expr = undefined, @@ -734,7 +740,7 @@ pub const Parser = struct { try container_decl.fields_and_decls.append(&line_comment.base); } - const comments = try self.eatComments(arena); + const comments = try self.eatDocComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Identifier => { @@ -743,9 +749,9 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.StructField { .base = ast.Node { .id = ast.Node.Id.StructField, - .doc_comments = comments, .same_line_comment = null, }, + .doc_comments = comments, .visib_token = null, .name_token = token, .type_expr = undefined, @@ -765,6 +771,7 @@ pub const Parser = struct { .name_token = token, .type_expr = null, .value_expr = null, + .doc_comments = comments, } ); @@ -780,6 +787,7 @@ pub const Parser = struct { .base = undefined, .name_token = token, .value = null, + .doc_comments = comments, } ); @@ -831,6 +839,9 @@ pub const Parser = struct { continue; }, Token.Id.RBrace => { + if (comments != null) { + return self.parseError(token, "doc comments must be attached to a node"); + } container_decl.rbrace_token = token; continue; }, @@ -856,9 +867,9 @@ pub const Parser = struct { const var_decl = try arena.construct(ast.Node.VarDecl { .base = ast.Node { .id = ast.Node.Id.VarDecl, - .doc_comments = ctx.comments, .same_line_comment = null, }, + .doc_comments = ctx.comments, .visib_token = ctx.visib_token, .mut_token = ctx.mut_token, .comptime_token = ctx.comptime_token, @@ -1119,7 +1130,6 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.Suspend { .base = ast.Node { .id = ast.Node.Id.Suspend, - .doc_comments = null, .same_line_comment = null, }, .label = ctx.label, @@ -1283,7 +1293,6 @@ pub const Parser = struct { } }, State.Statement => |block| { - const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_comptime => { @@ -1298,7 +1307,7 @@ pub const Parser = struct { Token.Id.Keyword_var, Token.Id.Keyword_const => { stack.append(State { .VarDecl = VarDeclCtx { - .comments = comments, + .comments = null, .visib_token = null, .comptime_token = null, .extern_export_token = null, @@ -1313,7 +1322,6 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.Defer { .base = ast.Node { .id = ast.Node.Id.Defer, - .doc_comments = comments, .same_line_comment = null, }, .defer_token = token, @@ -1349,23 +1357,18 @@ pub const Parser = struct { const statement = try block.statements.addOne(); stack.append(State { .LookForSameLineComment = statement }) catch unreachable; try stack.append(State { .Semicolon = statement }); - try stack.append(State { .AddComments = AddCommentsCtx { - .node_ptr = statement, - .comments = comments, - }}); try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); continue; } } }, State.ComptimeStatement => |ctx| { - const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_var, Token.Id.Keyword_const => { stack.append(State { .VarDecl = VarDeclCtx { - .comments = comments, + .comments = null, .visib_token = null, .comptime_token = ctx.comptime_token, .extern_export_token = null, @@ -1395,12 +1398,6 @@ pub const Parser = struct { continue; }, - State.AddComments => |add_comments_ctx| { - const node = *add_comments_ctx.node_ptr; - node.doc_comments = add_comments_ctx.comments; - continue; - }, - State.LookForSameLineComment => |node_ptr| { try self.lookForSameLineComment(arena, *node_ptr); continue; @@ -1521,7 +1518,6 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.FieldInitializer { .base = ast.Node { .id = ast.Node.Id.FieldInitializer, - .doc_comments = null, .same_line_comment = null, }, .period_token = undefined, @@ -1566,7 +1562,7 @@ pub const Parser = struct { try stack.append(State { .ContainerDecl = container_decl }); continue; }, - State.IdentifierListItemOrEnd => |list_state| { + State.ErrorTagListItemOrEnd => |list_state| { while (try self.eatLineComment(arena)) |line_comment| { try list_state.list.append(&line_comment.base); } @@ -1576,23 +1572,18 @@ pub const Parser = struct { continue; } - const comments = try self.eatComments(arena); const node_ptr = try list_state.list.addOne(); - try stack.append(State { .AddComments = AddCommentsCtx { - .node_ptr = node_ptr, - .comments = comments, - }}); - try stack.append(State { .IdentifierListCommaOrEnd = list_state }); - try stack.append(State { .Identifier = OptionalCtx { .Required = node_ptr } }); + try stack.append(State { .ErrorTagListCommaOrEnd = list_state }); + try stack.append(State { .ErrorTag = node_ptr }); continue; }, - State.IdentifierListCommaOrEnd => |list_state| { + State.ErrorTagListCommaOrEnd => |list_state| { if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { *list_state.ptr = end; continue; } else { - stack.append(State { .IdentifierListItemOrEnd = list_state }) catch unreachable; + stack.append(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable; continue; } }, @@ -1606,11 +1597,10 @@ pub const Parser = struct { continue; } - const comments = try self.eatComments(arena); + const comments = try self.eatDocComments(arena); const node = try arena.construct(ast.Node.SwitchCase { .base = ast.Node { .id = ast.Node.Id.SwitchCase, - .doc_comments = comments, .same_line_comment = null, }, .items = ArrayList(&ast.Node).init(arena), @@ -1723,9 +1713,9 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .doc_comments = ctx.comments, .same_line_comment = null, }, + .doc_comments = ctx.comments, .visib_token = null, .name_token = null, .fn_token = fn_token, @@ -2593,7 +2583,6 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.PromiseType { .base = ast.Node { .id = ast.Node.Id.PromiseType, - .doc_comments = null, .same_line_comment = null, }, .promise_token = token, @@ -2719,9 +2708,9 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .doc_comments = null, .same_line_comment = null, }, + .doc_comments = null, .visib_token = null, .name_token = null, .fn_token = token, @@ -2743,9 +2732,9 @@ pub const Parser = struct { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, - .doc_comments = null, .same_line_comment = null, }, + .doc_comments = null, .visib_token = null, .name_token = null, .fn_token = undefined, @@ -2836,7 +2825,6 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.ErrorSetDecl { .base = ast.Node { .id = ast.Node.Id.ErrorSetDecl, - .doc_comments = null, .same_line_comment = null, }, .error_token = ctx.error_token, @@ -2846,7 +2834,7 @@ pub const Parser = struct { ctx.opt_ctx.store(&node.base); stack.append(State { - .IdentifierListItemOrEnd = ListSave(&ast.Node) { + .ErrorTagListItemOrEnd = ListSave(&ast.Node) { .list = &node.decls, .ptr = &node.rbrace_token, } @@ -2866,6 +2854,7 @@ pub const Parser = struct { } ); }, + State.Identifier => |opt_ctx| { if (self.eatToken(Token.Id.Identifier)) |ident_token| { _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token); @@ -2878,6 +2867,25 @@ pub const Parser = struct { } }, + State.ErrorTag => |node_ptr| { + const comments = try self.eatDocComments(arena); + const ident_token = self.getNextToken(); + if (ident_token.id != Token.Id.Identifier) { + return self.parseError(ident_token, "expected {}, found {}", + @tagName(Token.Id.Identifier), @tagName(ident_token.id)); + } + + const node = try arena.construct(ast.Node.ErrorTag { + .base = ast.Node { + .id = ast.Node.Id.ErrorTag, + .same_line_comment = null, + }, + .doc_comments = comments, + .name_token = ident_token, + }); + *node_ptr = &node.base; + continue; + }, State.ExpectToken => |token_id| { _ = try self.expectToken(token_id); @@ -2916,7 +2924,7 @@ pub const Parser = struct { } } - fn eatComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.DocComment { + fn eatDocComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.DocComment { var result: ?&ast.Node.DocComment = null; while (true) { if (self.eatToken(Token.Id.DocComment)) |line_comment| { @@ -2927,7 +2935,6 @@ pub const Parser = struct { const comment_node = try arena.construct(ast.Node.DocComment { .base = ast.Node { .id = ast.Node.Id.DocComment, - .doc_comments = null, .same_line_comment = null, }, .lines = ArrayList(Token).init(arena), @@ -2949,7 +2956,6 @@ pub const Parser = struct { return try arena.construct(ast.Node.LineComment { .base = ast.Node { .id = ast.Node.Id.LineComment, - .doc_comments = null, .same_line_comment = null, }, .token = token, @@ -3142,7 +3148,6 @@ pub const Parser = struct { const node = try arena.construct(ast.Node.Switch { .base = ast.Node { .id = ast.Node.Id.Switch, - .doc_comments = null, .same_line_comment = null, }, .switch_token = *token, @@ -3170,6 +3175,7 @@ pub const Parser = struct { .base = undefined, .comptime_token = *token, .expr = undefined, + .doc_comments = null, } ); try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); @@ -3312,7 +3318,6 @@ pub const Parser = struct { const id = ast.Node.typeToId(T); break :blk ast.Node { .id = id, - .doc_comments = null, .same_line_comment = null, }; }; @@ -3451,7 +3456,6 @@ pub const Parser = struct { PrintIndent, Indent: usize, PrintSameLineComment: ?&Token, - PrintComments: &ast.Node, }; pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { @@ -3490,7 +3494,7 @@ pub const Parser = struct { switch (decl.id) { ast.Node.Id.FnProto => { const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); - try self.renderComments(stream, &fn_proto.base, indent); + try self.renderComments(stream, fn_proto, indent); if (fn_proto.body_node) |body_node| { stack.append(RenderState { .Expression = body_node}) catch unreachable; @@ -3512,12 +3516,12 @@ pub const Parser = struct { }, ast.Node.Id.VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); - try self.renderComments(stream, &var_decl.base, indent); + try self.renderComments(stream, var_decl, indent); try stack.append(RenderState { .VarDecl = var_decl}); }, ast.Node.Id.TestDecl => { const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); - try self.renderComments(stream, &test_decl.base, indent); + try self.renderComments(stream, test_decl, indent); try stream.print("test "); try stack.append(RenderState { .Expression = test_decl.body_node }); try stack.append(RenderState { .Text = " " }); @@ -3525,7 +3529,7 @@ pub const Parser = struct { }, ast.Node.Id.StructField => { const field = @fieldParentPtr(ast.Node.StructField, "base", decl); - try self.renderComments(stream, &field.base, indent); + try self.renderComments(stream, field, indent); if (field.visib_token) |visib_token| { try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token)); } @@ -3535,7 +3539,7 @@ pub const Parser = struct { }, ast.Node.Id.UnionTag => { const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); - try self.renderComments(stream, &tag.base, indent); + try self.renderComments(stream, tag, indent); try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); try stack.append(RenderState { .Text = "," }); @@ -3552,7 +3556,7 @@ pub const Parser = struct { }, ast.Node.Id.EnumTag => { const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); - try self.renderComments(stream, &tag.base, indent); + try self.renderComments(stream, tag, indent); try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); try stack.append(RenderState { .Text = "," }); @@ -3561,6 +3565,11 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = value}); } }, + ast.Node.Id.ErrorTag => { + const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl); + try self.renderComments(stream, tag, indent); + try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); + }, ast.Node.Id.Comptime => { if (requireSemiColon(decl)) { try stack.append(RenderState { .Text = ";" }); @@ -4122,11 +4131,20 @@ pub const Parser = struct { if (decls.len == 1) blk: { const node = decls[0]; - if (node.same_line_comment != null or node.doc_comments != null) break :blk; + + // if there are any doc comments or same line comments + // don't try to put it all on one line + if (node.same_line_comment != null) break :blk; + if (node.cast(ast.Node.ErrorTag)) |tag| { + if (tag.doc_comments != null) break :blk; + } else { + break :blk; + } + try stream.write("error{"); try stack.append(RenderState { .Text = "}" }); - try stack.append(RenderState { .Expression = node }); + try stack.append(RenderState { .TopLevelDecl = node }); continue; } @@ -4144,8 +4162,7 @@ pub const Parser = struct { if (node.id != ast.Node.Id.LineComment) { try stack.append(RenderState { .Text = "," }); } - try stack.append(RenderState { .Expression = node }); - try stack.append(RenderState { .PrintComments = node }); + try stack.append(RenderState { .TopLevelDecl = node }); try stack.append(RenderState.PrintIndent); try stack.append(RenderState { .Text = blk: { @@ -4304,8 +4321,6 @@ pub const Parser = struct { ast.Node.Id.SwitchCase => { const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); - try self.renderComments(stream, base, indent); - try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment }); try stack.append(RenderState { .Text = "," }); try stack.append(RenderState { .Expression = switch_case.expr }); @@ -4617,6 +4632,7 @@ pub const Parser = struct { ast.Node.Id.StructField, ast.Node.Id.UnionTag, ast.Node.Id.EnumTag, + ast.Node.Id.ErrorTag, ast.Node.Id.Root, ast.Node.Id.VarDecl, ast.Node.Id.Use, @@ -4624,7 +4640,6 @@ pub const Parser = struct { ast.Node.Id.ParamDecl => unreachable, }, RenderState.Statement => |base| { - try self.renderComments(stream, base, indent); try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment } ); switch (base.id) { ast.Node.Id.VarDecl => { @@ -4645,15 +4660,11 @@ pub const Parser = struct { const comment_token = maybe_comment ?? break :blk; try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token)); }, - - RenderState.PrintComments => |node| blk: { - try self.renderComments(stream, node, indent); - }, } } } - fn renderComments(self: &Parser, stream: var, node: &ast.Node, indent: usize) !void { + fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void { const comment = node.doc_comments ?? return; for (comment.lines.toSliceConst()) |line_token| { try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token)); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 5d4383d6ac..07ae0a7965 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -181,7 +181,7 @@ test "zig fmt: comments before global variables" { ); } -test "zig fmt: comments before statements" { +test "zig fmt: comments in statements" { try testCanonical( \\test "std" { \\ // statement comment @@ -211,22 +211,6 @@ test "zig fmt: comments before test decl" { ); } -test "zig fmt: comments before variable declarations" { - try testCanonical( - \\const std = @import("std"); - \\ - \\pub fn main() !void { - \\ /// If this program is run without stdout attached, exit with an error. - \\ /// another comment - \\ var stdout_file = try std.io.getStdOut; - \\ // If this program is run without stdout attached, exit with an error. - \\ // another comment - \\ var stdout_file = try std.io.getStdOut; - \\} - \\ - ); -} - test "zig fmt: preserve spacing" { try testCanonical( \\const std = @import("std"); -- cgit v1.2.3 From a35b366eb64272c6d4646aedc035a837ed0c3cb0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 20:35:54 -0400 Subject: [breaking] delete ptr deref prefix op start using zig-fmt-pointer-reform branch build of zig fmt to fix code to use the new syntax all of test/cases/* are processed, but there are more left to be done - all the std lib used by the behavior tests --- src/all_types.hpp | 1 - src/ast_render.cpp | 1 - src/ir.cpp | 2 - src/parser.cpp | 13 +- src/translate_c.cpp | 83 ++- std/debug/index.zig | 233 ++++---- std/math/index.zig | 75 +-- std/mem.zig | 197 ++++--- std/zig/parser.zig | 9 +- test/cases/align.zig | 86 ++- test/cases/alignof.zig | 6 +- test/cases/array.zig | 41 +- test/cases/bitcast.zig | 8 +- test/cases/bugs/394.zig | 17 +- test/cases/bugs/655.zig | 2 +- test/cases/bugs/656.zig | 11 +- test/cases/bugs/828.zig | 10 +- test/cases/bugs/920.zig | 19 +- test/cases/cast.zig | 72 ++- test/cases/coroutines.zig | 18 +- test/cases/defer.zig | 15 +- test/cases/enum.zig | 606 +++++++++++++++++++-- test/cases/enum_with_members.zig | 10 +- test/cases/error.zig | 56 +- test/cases/eval.zig | 143 +++-- test/cases/fn.zig | 44 +- test/cases/for.zig | 44 +- test/cases/generics.zig | 42 +- test/cases/if.zig | 1 - test/cases/import/a_namespace.zig | 4 +- test/cases/ir_block_deps.zig | 4 +- test/cases/math.zig | 92 ++-- test/cases/misc.zig | 225 +++++--- .../namespace_depends_on_compile_var/index.zig | 2 +- test/cases/null.zig | 27 +- .../ref_var_in_if_after_if_2nd_switch_prong.zig | 2 +- test/cases/reflection.zig | 5 +- test/cases/slice.zig | 8 +- test/cases/struct.zig | 83 +-- test/cases/struct_contains_null_ptr_itself.zig | 1 - test/cases/struct_contains_slice_of_itself.zig | 2 +- test/cases/switch.zig | 49 +- test/cases/switch_prong_err_enum.zig | 8 +- test/cases/switch_prong_implicit_cast.zig | 8 +- test/cases/try.zig | 8 +- test/cases/undefined.zig | 4 +- test/cases/union.zig | 87 +-- test/cases/var_args.zig | 25 +- test/cases/while.zig | 65 ++- 49 files changed, 1694 insertions(+), 880 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 2993589f7b..a25c99edda 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -614,7 +614,6 @@ enum PrefixOp { PrefixOpBinNot, PrefixOpNegation, PrefixOpNegationWrap, - PrefixOpDereference, PrefixOpMaybe, PrefixOpUnwrapMaybe, }; diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 3e5ef0fcdb..0cb8bf4e93 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -66,7 +66,6 @@ static const char *prefix_op_str(PrefixOp prefix_op) { case PrefixOpNegationWrap: return "-%"; case PrefixOpBoolNot: return "!"; case PrefixOpBinNot: return "~"; - case PrefixOpDereference: return "*"; case PrefixOpMaybe: return "?"; case PrefixOpUnwrapMaybe: return "??"; } diff --git a/src/ir.cpp b/src/ir.cpp index 8c7232722e..ff5afe138c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4696,8 +4696,6 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval); case PrefixOpNegationWrap: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval); - case PrefixOpDereference: - return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval); case PrefixOpMaybe: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe), lval); case PrefixOpUnwrapMaybe: diff --git a/src/parser.cpp b/src/parser.cpp index c02546a99d..4763d3b987 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1165,10 +1165,8 @@ static PrefixOp tok_to_prefix_op(Token *token) { case TokenIdDash: return PrefixOpNegation; case TokenIdMinusPercent: return PrefixOpNegationWrap; case TokenIdTilde: return PrefixOpBinNot; - case TokenIdStar: return PrefixOpDereference; case TokenIdMaybe: return PrefixOpMaybe; case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe; - case TokenIdStarStar: return PrefixOpDereference; default: return PrefixOpInvalid; } } @@ -1214,7 +1212,7 @@ static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) { /* PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression -PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" +PrefixOp = "!" | "-" | "~" | ("*" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" */ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1237,15 +1235,6 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token); AstNode *parent_node = node; - if (token->id == TokenIdStarStar) { - // pretend that we got 2 star tokens - - parent_node = ast_create_node(pc, NodeTypePrefixOpExpr, token); - parent_node->data.prefix_op_expr.primary_expr = node; - parent_node->data.prefix_op_expr.prefix_op = PrefixOpDereference; - - node->column += 1; - } AstNode *prefix_op_expr = ast_parse_error_set_expr(pc, token_index, true); node->data.prefix_op_expr.primary_expr = prefix_op_expr; diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 965a8963bd..70a98dcc2e 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -247,6 +247,12 @@ static AstNode *trans_create_node_field_access_str(Context *c, AstNode *containe return trans_create_node_field_access(c, container, buf_create_from_str(field_name)); } +static AstNode *trans_create_node_ptr_deref(Context *c, AstNode *child_node) { + AstNode *node = trans_create_node(c, NodeTypePtrDeref); + node->data.ptr_deref_expr.target = child_node; + return node; +} + static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *child_node) { AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr); node->data.prefix_op_expr.prefix_op = op; @@ -1412,8 +1418,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result AstNode *operation_type_cast = trans_c_cast(c, rhs_location, stmt->getComputationLHSType(), stmt->getLHS()->getType(), - trans_create_node_prefix_op(c, PrefixOpDereference, - trans_create_node_symbol(c, tmp_var_name))); + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name))); // result_type(... >> u5(rhs)) AstNode *result_type_cast = trans_c_cast(c, rhs_location, @@ -1426,7 +1431,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result // *_ref = ... AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name)), BinOpTypeAssign, result_type_cast); @@ -1436,7 +1441,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result // break :x *_ref child_scope->node->data.block.statements.append( trans_create_node_break(c, label_name, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name)))); } @@ -1483,11 +1488,11 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, if (rhs == nullptr) return nullptr; AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name)), BinOpTypeAssign, trans_create_node_bin_op(c, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name)), bin_op, rhs)); @@ -1496,7 +1501,7 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, // break :x *_ref child_scope->node->data.block.statements.append( trans_create_node_break(c, label_name, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name)))); return child_scope->node; @@ -1817,13 +1822,13 @@ static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, Tr // const _tmp = *_ref; Buf* tmp_var_name = buf_create_from_str("_tmp"); AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, ref_var_name))); child_scope->node->data.block.statements.append(tmp_var_decl); // *_ref += 1; AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, ref_var_name)), assign_op, trans_create_node_unsigned(c, 1)); @@ -1871,14 +1876,14 @@ static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, Tra // *_ref += 1; AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, ref_var_name)), assign_op, trans_create_node_unsigned(c, 1)); child_scope->node->data.block.statements.append(assign_statement); // break :x *_ref - AstNode *deref_expr = trans_create_node_prefix_op(c, PrefixOpDereference, + AstNode *deref_expr = trans_create_node_ptr_deref(c, trans_create_node_symbol(c, ref_var_name)); child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, deref_expr)); @@ -1923,7 +1928,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc if (is_fn_ptr) return value_node; AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node); - return trans_create_node_prefix_op(c, PrefixOpDereference, unwrapped); + return trans_create_node_ptr_deref(c, unwrapped); } case UO_Plus: emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Plus"); @@ -4469,27 +4474,45 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t } } -static PrefixOp ctok_to_prefix_op(CTok *token) { - switch (token->id) { - case CTokIdBang: return PrefixOpBoolNot; - case CTokIdMinus: return PrefixOpNegation; - case CTokIdTilde: return PrefixOpBinNot; - case CTokIdAsterisk: return PrefixOpDereference; - default: return PrefixOpInvalid; - } -} static AstNode *parse_ctok_prefix_op_expr(Context *c, CTokenize *ctok, size_t *tok_i) { CTok *op_tok = &ctok->tokens.at(*tok_i); - PrefixOp prefix_op = ctok_to_prefix_op(op_tok); - if (prefix_op == PrefixOpInvalid) { - return parse_ctok_suffix_op_expr(c, ctok, tok_i); - } - *tok_i += 1; - AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); - if (prefix_op_expr == nullptr) - return nullptr; - return trans_create_node_prefix_op(c, prefix_op, prefix_op_expr); + switch (op_tok->id) { + case CTokIdBang: + { + *tok_i += 1; + AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); + if (prefix_op_expr == nullptr) + return nullptr; + return trans_create_node_prefix_op(c, PrefixOpBoolNot, prefix_op_expr); + } + case CTokIdMinus: + { + *tok_i += 1; + AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); + if (prefix_op_expr == nullptr) + return nullptr; + return trans_create_node_prefix_op(c, PrefixOpNegation, prefix_op_expr); + } + case CTokIdTilde: + { + *tok_i += 1; + AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); + if (prefix_op_expr == nullptr) + return nullptr; + return trans_create_node_prefix_op(c, PrefixOpBinNot, prefix_op_expr); + } + case CTokIdAsterisk: + { + *tok_i += 1; + AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); + if (prefix_op_expr == nullptr) + return nullptr; + return trans_create_node_ptr_deref(c, prefix_op_expr); + } + default: + return parse_ctok_suffix_op_expr(c, ctok, tok_i); + } } static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *char_ptr) { diff --git a/std/debug/index.zig b/std/debug/index.zig index 9057f157de..36ac2e8a3f 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -104,9 +104,7 @@ pub fn panic(comptime format: []const u8, args: ...) noreturn { var panicking: u8 = 0; // TODO make this a bool -pub fn panicExtra(trace: ?&const builtin.StackTrace, first_trace_addr: ?usize, - comptime format: []const u8, args: ...) noreturn -{ +pub fn panicExtra(trace: ?&const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn { @setCold(true); if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { @@ -132,9 +130,7 @@ const WHITE = "\x1b[37;1m"; const DIM = "\x1b[2m"; const RESET = "\x1b[0m"; -pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, allocator: &mem.Allocator, - debug_info: &ElfStackTrace, tty_color: bool) !void -{ +pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, allocator: &mem.Allocator, debug_info: &ElfStackTrace, tty_color: bool) !void { var frame_index: usize = undefined; var frames_left: usize = undefined; if (stack_trace.index < stack_trace.instruction_addresses.len) { @@ -154,9 +150,7 @@ pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, } } -pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, - debug_info: &ElfStackTrace, tty_color: bool, start_addr: ?usize) !void -{ +pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, debug_info: &ElfStackTrace, tty_color: bool, start_addr: ?usize) !void { const AddressState = union(enum) { NotLookingForStartAddress, LookingForStartAddress: usize, @@ -166,14 +160,14 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, // else AddressState.NotLookingForStartAddress; var addr_state: AddressState = undefined; if (start_addr) |addr| { - addr_state = AddressState { .LookingForStartAddress = addr }; + addr_state = AddressState{ .LookingForStartAddress = addr }; } else { addr_state = AddressState.NotLookingForStartAddress; } var fp = @ptrToInt(@frameAddress()); - while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) { - const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize)); + while (fp != 0) : (fp = @intToPtr(&const usize, fp).*) { + const return_address = @intToPtr(&const usize, fp + @sizeOf(usize)).*; switch (addr_state) { AddressState.NotLookingForStartAddress => {}, @@ -200,32 +194,32 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: us // in practice because the compiler dumps everything in a single // object file. Future improvement: use external dSYM data when // available. - const unknown = macho.Symbol { .name = "???", .address = address }; + const unknown = macho.Symbol{ + .name = "???", + .address = address, + }; const symbol = debug_info.symbol_table.search(address) ?? &unknown; - try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ - DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", - symbol.name, address); + try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address); }, else => { const compile_unit = findCompileUnit(debug_info, address) catch { - try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", - address); + try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", address); return; }; const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| { defer line_info.deinit(); - try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ - DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", - line_info.file_name, line_info.line, line_info.column, - address, compile_unit_name); + try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", line_info.file_name, line_info.line, line_info.column, address, compile_unit_name); if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { if (line_info.column == 0) { try out_stream.write("\n"); } else { - {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { - try out_stream.writeByte(' '); - }} + { + var col_i: usize = 1; + while (col_i < line_info.column) : (col_i += 1) { + try out_stream.writeByte(' '); + } + } try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); } } else |err| switch (err) { @@ -233,7 +227,8 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: us else => return err, } } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { + error.MissingDebugInfo, + error.InvalidDebugInfo => { try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name); }, else => return err, @@ -247,7 +242,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { builtin.ObjectFormat.elf => { const st = try allocator.create(ElfStackTrace); errdefer allocator.destroy(st); - *st = ElfStackTrace { + st.* = ElfStackTrace{ .self_exe_file = undefined, .elf = undefined, .debug_info = undefined, @@ -279,9 +274,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { const st = try allocator.create(ElfStackTrace); errdefer allocator.destroy(st); - *st = ElfStackTrace { - .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)), - }; + st.* = ElfStackTrace{ .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)) }; return st; }, @@ -325,8 +318,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con } } - if (amt_read < buf.len) - return error.EndOfFile; + if (amt_read < buf.len) return error.EndOfFile; } } @@ -418,10 +410,8 @@ const Constant = struct { signed: bool, fn asUnsignedLe(self: &const Constant) !u64 { - if (self.payload.len > @sizeOf(u64)) - return error.InvalidDebugInfo; - if (self.signed) - return error.InvalidDebugInfo; + if (self.payload.len > @sizeOf(u64)) return error.InvalidDebugInfo; + if (self.signed) return error.InvalidDebugInfo; return mem.readInt(self.payload, u64, builtin.Endian.Little); } }; @@ -438,15 +428,14 @@ const Die = struct { fn getAttr(self: &const Die, id: u64) ?&const FormValue { for (self.attrs.toSliceConst()) |*attr| { - if (attr.id == id) - return &attr.value; + if (attr.id == id) return &attr.value; } return null; } fn getAttrAddr(self: &const Die, id: u64) !u64 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; - return switch (*form_value) { + return switch (form_value.*) { FormValue.Address => |value| value, else => error.InvalidDebugInfo, }; @@ -454,7 +443,7 @@ const Die = struct { fn getAttrSecOffset(self: &const Die, id: u64) !u64 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; - return switch (*form_value) { + return switch (form_value.*) { FormValue.Const => |value| value.asUnsignedLe(), FormValue.SecOffset => |value| value, else => error.InvalidDebugInfo, @@ -463,7 +452,7 @@ const Die = struct { fn getAttrUnsignedLe(self: &const Die, id: u64) !u64 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; - return switch (*form_value) { + return switch (form_value.*) { FormValue.Const => |value| value.asUnsignedLe(), else => error.InvalidDebugInfo, }; @@ -471,7 +460,7 @@ const Die = struct { fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) ![]u8 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; - return switch (*form_value) { + return switch (form_value.*) { FormValue.String => |value| value, FormValue.StrPtr => |offset| getString(st, offset), else => error.InvalidDebugInfo, @@ -518,10 +507,8 @@ const LineNumberProgram = struct { prev_basic_block: bool, prev_end_sequence: bool, - pub fn init(is_stmt: bool, include_dirs: []const []const u8, - file_entries: &ArrayList(FileEntry), target_address: usize) LineNumberProgram - { - return LineNumberProgram { + pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: &ArrayList(FileEntry), target_address: usize) LineNumberProgram { + return LineNumberProgram{ .address = 0, .file = 1, .line = 1, @@ -548,14 +535,16 @@ const LineNumberProgram = struct { return error.MissingDebugInfo; } else if (self.prev_file - 1 >= self.file_entries.len) { return error.InvalidDebugInfo; - } else &self.file_entries.items[self.prev_file - 1]; + } else + &self.file_entries.items[self.prev_file - 1]; const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { return error.InvalidDebugInfo; - } else self.include_dirs[file_entry.dir_index]; + } else + self.include_dirs[file_entry.dir_index]; const file_name = try os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name); errdefer self.file_entries.allocator.free(file_name); - return LineInfo { + return LineInfo{ .line = if (self.prev_line >= 0) usize(self.prev_line) else 0, .column = self.prev_column, .file_name = file_name, @@ -578,8 +567,7 @@ fn readStringRaw(allocator: &mem.Allocator, in_stream: var) ![]u8 { var buf = ArrayList(u8).init(allocator); while (true) { const byte = try in_stream.readByte(); - if (byte == 0) - break; + if (byte == 0) break; try buf.append(byte); } return buf.toSlice(); @@ -600,7 +588,7 @@ fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { const buf = try readAllocBytes(allocator, in_stream, size); - return FormValue { .Block = buf }; + return FormValue{ .Block = buf }; } fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { @@ -609,26 +597,23 @@ fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) ! } fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue { - return FormValue { .Const = Constant { + return FormValue{ .Const = Constant{ .signed = signed, .payload = try readAllocBytes(allocator, in_stream, size), - }}; + } }; } fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 { - return if (is_64) try in_stream.readIntLe(u64) - else u64(try in_stream.readIntLe(u32)) ; + return if (is_64) try in_stream.readIntLe(u64) else u64(try in_stream.readIntLe(u32)); } fn parseFormValueTargetAddrSize(in_stream: var) !u64 { - return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32)) - else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64) - else unreachable; + return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64) else unreachable; } fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { const buf = try readAllocBytes(allocator, in_stream, size); - return FormValue { .Ref = buf }; + return FormValue{ .Ref = buf }; } fn parseFormValueRef(allocator: &mem.Allocator, in_stream: var, comptime T: type) !FormValue { @@ -646,11 +631,9 @@ const ParseFormValueError = error { OutOfMemory, }; -fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64: bool) - ParseFormValueError!FormValue -{ +fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { return switch (form_id) { - DW.FORM_addr => FormValue { .Address = try parseFormValueTargetAddrSize(in_stream) }, + DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) }, DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2), DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4), @@ -662,7 +645,8 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64 DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2), DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4), DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8), - DW.FORM_udata, DW.FORM_sdata => { + DW.FORM_udata, + DW.FORM_sdata => { const block_len = try readULeb128(in_stream); const signed = form_id == DW.FORM_sdata; return parseFormValueConstant(allocator, in_stream, signed, block_len); @@ -670,11 +654,11 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64 DW.FORM_exprloc => { const size = try readULeb128(in_stream); const buf = try readAllocBytes(allocator, in_stream, size); - return FormValue { .ExprLoc = buf }; + return FormValue{ .ExprLoc = buf }; }, - DW.FORM_flag => FormValue { .Flag = (try in_stream.readByte()) != 0 }, - DW.FORM_flag_present => FormValue { .Flag = true }, - DW.FORM_sec_offset => FormValue { .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, + DW.FORM_flag => FormValue{ .Flag = (try in_stream.readByte()) != 0 }, + DW.FORM_flag_present => FormValue{ .Flag = true }, + DW.FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, u8), DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, u16), @@ -685,11 +669,11 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64 return parseFormValueRefLen(allocator, in_stream, ref_len); }, - DW.FORM_ref_addr => FormValue { .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, - DW.FORM_ref_sig8 => FormValue { .RefSig8 = try in_stream.readIntLe(u64) }, + DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, + DW.FORM_ref_sig8 => FormValue{ .RefSig8 = try in_stream.readIntLe(u64) }, - DW.FORM_string => FormValue { .String = try readStringRaw(allocator, in_stream) }, - DW.FORM_strp => FormValue { .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, + DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) }, + DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_indirect => { const child_form_id = try readULeb128(in_stream); return parseFormValue(allocator, in_stream, child_form_id, is_64); @@ -705,9 +689,8 @@ fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable { var result = AbbrevTable.init(st.allocator()); while (true) { const abbrev_code = try readULeb128(in_stream); - if (abbrev_code == 0) - return result; - try result.append(AbbrevTableEntry { + if (abbrev_code == 0) return result; + try result.append(AbbrevTableEntry{ .abbrev_code = abbrev_code, .tag_id = try readULeb128(in_stream), .has_children = (try in_stream.readByte()) == DW.CHILDREN_yes, @@ -718,9 +701,8 @@ fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable { while (true) { const attr_id = try readULeb128(in_stream); const form_id = try readULeb128(in_stream); - if (attr_id == 0 and form_id == 0) - break; - try attrs.append(AbbrevAttr { + if (attr_id == 0 and form_id == 0) break; + try attrs.append(AbbrevAttr{ .attr_id = attr_id, .form_id = form_id, }); @@ -737,7 +719,7 @@ fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) !&const AbbrevTable { } } try st.self_exe_file.seekTo(st.debug_abbrev.offset + abbrev_offset); - try st.abbrev_table_list.append(AbbrevTableHeader { + try st.abbrev_table_list.append(AbbrevTableHeader{ .offset = abbrev_offset, .table = try parseAbbrevTable(st), }); @@ -746,8 +728,7 @@ fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) !&const AbbrevTable { fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) ?&const AbbrevTableEntry { for (abbrev_table.toSliceConst()) |*table_entry| { - if (table_entry.abbrev_code == abbrev_code) - return table_entry; + if (table_entry.abbrev_code == abbrev_code) return table_entry; } return null; } @@ -759,14 +740,14 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) ! const abbrev_code = try readULeb128(in_stream); const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo; - var result = Die { + var result = Die{ .tag_id = table_entry.tag_id, .has_children = table_entry.has_children, .attrs = ArrayList(Die.Attr).init(st.allocator()), }; try result.attrs.resize(table_entry.attrs.len); for (table_entry.attrs.toSliceConst()) |attr, i| { - result.attrs.items[i] = Die.Attr { + result.attrs.items[i] = Die.Attr{ .id = attr.attr_id, .value = try parseFormValue(st.allocator(), in_stream, attr.form_id, is_64), }; @@ -790,8 +771,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe var is_64: bool = undefined; const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64); - if (unit_length == 0) - return error.MissingDebugInfo; + if (unit_length == 0) return error.MissingDebugInfo; const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); if (compile_unit.index != this_index) { @@ -803,8 +783,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe // TODO support 3 and 5 if (version != 2 and version != 4) return error.InvalidDebugInfo; - const prologue_length = if (is_64) try in_stream.readInt(st.elf.endian, u64) - else try in_stream.readInt(st.elf.endian, u32); + const prologue_length = if (is_64) try in_stream.readInt(st.elf.endian, u64) else try in_stream.readInt(st.elf.endian, u32); const prog_start_offset = (try in_file.getPos()) + prologue_length; const minimum_instruction_length = try in_stream.readByte(); @@ -819,38 +798,37 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe const line_base = try in_stream.readByteSigned(); const line_range = try in_stream.readByte(); - if (line_range == 0) - return error.InvalidDebugInfo; + if (line_range == 0) return error.InvalidDebugInfo; const opcode_base = try in_stream.readByte(); const standard_opcode_lengths = try st.allocator().alloc(u8, opcode_base - 1); - {var i: usize = 0; while (i < opcode_base - 1) : (i += 1) { - standard_opcode_lengths[i] = try in_stream.readByte(); - }} + { + var i: usize = 0; + while (i < opcode_base - 1) : (i += 1) { + standard_opcode_lengths[i] = try in_stream.readByte(); + } + } var include_directories = ArrayList([]u8).init(st.allocator()); try include_directories.append(compile_unit_cwd); while (true) { const dir = try st.readString(); - if (dir.len == 0) - break; + if (dir.len == 0) break; try include_directories.append(dir); } var file_entries = ArrayList(FileEntry).init(st.allocator()); - var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), - &file_entries, target_address); + var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); while (true) { const file_name = try st.readString(); - if (file_name.len == 0) - break; + if (file_name.len == 0) break; const dir_index = try readULeb128(in_stream); const mtime = try readULeb128(in_stream); const len_bytes = try readULeb128(in_stream); - try file_entries.append(FileEntry { + try file_entries.append(FileEntry{ .file_name = file_name, .dir_index = dir_index, .mtime = mtime, @@ -866,8 +844,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe var sub_op: u8 = undefined; // TODO move this to the correct scope and fix the compiler crash if (opcode == DW.LNS_extended_op) { const op_size = try readULeb128(in_stream); - if (op_size < 1) - return error.InvalidDebugInfo; + if (op_size < 1) return error.InvalidDebugInfo; sub_op = try in_stream.readByte(); switch (sub_op) { DW.LNE_end_sequence => { @@ -884,7 +861,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe const dir_index = try readULeb128(in_stream); const mtime = try readULeb128(in_stream); const len_bytes = try readULeb128(in_stream); - try file_entries.append(FileEntry { + try file_entries.append(FileEntry{ .file_name = file_name, .dir_index = dir_index, .mtime = mtime, @@ -941,11 +918,9 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe const arg = try in_stream.readInt(st.elf.endian, u16); prog.address += arg; }, - DW.LNS_set_prologue_end => { - }, + DW.LNS_set_prologue_end => {}, else => { - if (opcode - 1 >= standard_opcode_lengths.len) - return error.InvalidDebugInfo; + if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; const len_bytes = standard_opcode_lengths[opcode - 1]; try in_file.seekForward(len_bytes); }, @@ -972,16 +947,13 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void { var is_64: bool = undefined; const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64); - if (unit_length == 0) - return; + if (unit_length == 0) return; const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); const version = try in_stream.readInt(st.elf.endian, u16); if (version < 2 or version > 5) return error.InvalidDebugInfo; - const debug_abbrev_offset = - if (is_64) try in_stream.readInt(st.elf.endian, u64) - else try in_stream.readInt(st.elf.endian, u32); + const debug_abbrev_offset = if (is_64) try in_stream.readInt(st.elf.endian, u64) else try in_stream.readInt(st.elf.endian, u32); const address_size = try in_stream.readByte(); if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; @@ -992,15 +964,14 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void { try st.self_exe_file.seekTo(compile_unit_pos); const compile_unit_die = try st.allocator().create(Die); - *compile_unit_die = try parseDie(st, abbrev_table, is_64); + compile_unit_die.* = try parseDie(st, abbrev_table, is_64); - if (compile_unit_die.tag_id != DW.TAG_compile_unit) - return error.InvalidDebugInfo; + if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; const pc_range = x: { if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| { - const pc_end = switch (*high_pc_value) { + const pc_end = switch (high_pc_value.*) { FormValue.Address => |value| value, FormValue.Const => |value| b: { const offset = try value.asUnsignedLe(); @@ -1008,7 +979,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void { }, else => return error.InvalidDebugInfo, }; - break :x PcRange { + break :x PcRange{ .start = low_pc, .end = pc_end, }; @@ -1016,13 +987,12 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void { break :x null; } } else |err| { - if (err != error.MissingDebugInfo) - return err; + if (err != error.MissingDebugInfo) return err; break :x null; } }; - try st.compile_unit_list.append(CompileUnit { + try st.compile_unit_list.append(CompileUnit{ .version = version, .is_64 = is_64, .pc_range = pc_range, @@ -1040,8 +1010,7 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit const in_stream = &in_file_stream.stream; for (st.compile_unit_list.toSlice()) |*compile_unit| { if (compile_unit.pc_range) |range| { - if (target_address >= range.start and target_address < range.end) - return compile_unit; + if (target_address >= range.start and target_address < range.end) return compile_unit; } if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { var base_address: usize = 0; @@ -1063,8 +1032,7 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit } } } else |err| { - if (err != error.MissingDebugInfo) - return err; + if (err != error.MissingDebugInfo) return err; continue; } } @@ -1073,8 +1041,8 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit fn readInitialLength(comptime E: type, in_stream: &io.InStream(E), is_64: &bool) !u64 { const first_32_bits = try in_stream.readIntLe(u32); - *is_64 = (first_32_bits == 0xffffffff); - if (*is_64) { + is_64.* = (first_32_bits == 0xffffffff); + if (is_64.*) { return in_stream.readIntLe(u64); } else { if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; @@ -1091,13 +1059,11 @@ fn readULeb128(in_stream: var) !u64 { var operand: u64 = undefined; - if (@shlWithOverflow(u64, byte & 0b01111111, u6(shift), &operand)) - return error.InvalidDebugInfo; + if (@shlWithOverflow(u64, byte & 0b01111111, u6(shift), &operand)) return error.InvalidDebugInfo; result |= operand; - if ((byte & 0b10000000) == 0) - return result; + if ((byte & 0b10000000) == 0) return result; shift += 7; } @@ -1112,15 +1078,13 @@ fn readILeb128(in_stream: var) !i64 { var operand: i64 = undefined; - if (@shlWithOverflow(i64, byte & 0b01111111, u6(shift), &operand)) - return error.InvalidDebugInfo; + if (@shlWithOverflow(i64, byte & 0b01111111, u6(shift), &operand)) return error.InvalidDebugInfo; result |= operand; shift += 7; if ((byte & 0b10000000) == 0) { - if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) - result |= -(i64(1) << u6(shift)); + if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << u6(shift)); return result; } } @@ -1131,7 +1095,6 @@ pub const global_allocator = &global_fixed_allocator.allocator; var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]); var global_allocator_mem: [100 * 1024]u8 = undefined; - // TODO make thread safe var debug_info_allocator: ?&mem.Allocator = null; var debug_info_direct_allocator: std.heap.DirectAllocator = undefined; diff --git a/std/math/index.zig b/std/math/index.zig index 83ba055329..05de604c6c 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -47,12 +47,12 @@ pub fn forceEval(value: var) void { f32 => { var x: f32 = undefined; const p = @ptrCast(&volatile f32, &x); - *p = x; + p.* = x; }, f64 => { var x: f64 = undefined; const p = @ptrCast(&volatile f64, &x); - *p = x; + p.* = x; }, else => { @compileError("forceEval not implemented for " ++ @typeName(T)); @@ -179,7 +179,6 @@ test "math" { _ = @import("complex/index.zig"); } - pub fn min(x: var, y: var) @typeOf(x + y) { return if (x < y) x else y; } @@ -280,10 +279,10 @@ pub fn rotr(comptime T: type, x: T, r: var) T { } test "math.rotr" { - assert(rotr(u8, 0b00000001, usize(0)) == 0b00000001); - assert(rotr(u8, 0b00000001, usize(9)) == 0b10000000); - assert(rotr(u8, 0b00000001, usize(8)) == 0b00000001); - assert(rotr(u8, 0b00000001, usize(4)) == 0b00010000); + assert(rotr(u8, 0b00000001, usize(0)) == 0b00000001); + assert(rotr(u8, 0b00000001, usize(9)) == 0b10000000); + assert(rotr(u8, 0b00000001, usize(8)) == 0b00000001); + assert(rotr(u8, 0b00000001, usize(4)) == 0b00010000); assert(rotr(u8, 0b00000001, isize(-1)) == 0b00000010); } @@ -299,14 +298,13 @@ pub fn rotl(comptime T: type, x: T, r: var) T { } test "math.rotl" { - assert(rotl(u8, 0b00000001, usize(0)) == 0b00000001); - assert(rotl(u8, 0b00000001, usize(9)) == 0b00000010); - assert(rotl(u8, 0b00000001, usize(8)) == 0b00000001); - assert(rotl(u8, 0b00000001, usize(4)) == 0b00010000); + assert(rotl(u8, 0b00000001, usize(0)) == 0b00000001); + assert(rotl(u8, 0b00000001, usize(9)) == 0b00000010); + assert(rotl(u8, 0b00000001, usize(8)) == 0b00000001); + assert(rotl(u8, 0b00000001, usize(4)) == 0b00010000); assert(rotl(u8, 0b00000001, isize(-1)) == 0b10000000); } - pub fn Log2Int(comptime T: type) type { return @IntType(false, log2(T.bit_count)); } @@ -323,14 +321,14 @@ fn testOverflow() void { assert((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000); } - pub fn absInt(x: var) !@typeOf(x) { const T = @typeOf(x); comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt comptime assert(T.is_signed); // must pass a signed integer to absInt - if (x == @minValue(@typeOf(x))) + + if (x == @minValue(@typeOf(x))) { return error.Overflow; - { + } else { @setRuntimeSafety(false); return if (x < 0) -x else x; } @@ -349,10 +347,8 @@ pub const absFloat = @import("fabs.zig").fabs; pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); - if (denominator == 0) - return error.DivisionByZero; - if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) - return error.Overflow; + if (denominator == 0) return error.DivisionByZero; + if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) return error.Overflow; return @divTrunc(numerator, denominator); } @@ -372,10 +368,8 @@ fn testDivTrunc() void { pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); - if (denominator == 0) - return error.DivisionByZero; - if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) - return error.Overflow; + if (denominator == 0) return error.DivisionByZero; + if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) return error.Overflow; return @divFloor(numerator, denominator); } @@ -395,13 +389,10 @@ fn testDivFloor() void { pub fn divExact(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); - if (denominator == 0) - return error.DivisionByZero; - if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) - return error.Overflow; + if (denominator == 0) return error.DivisionByZero; + if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) return error.Overflow; const result = @divTrunc(numerator, denominator); - if (result * denominator != numerator) - return error.UnexpectedRemainder; + if (result * denominator != numerator) return error.UnexpectedRemainder; return result; } @@ -423,10 +414,8 @@ fn testDivExact() void { pub fn mod(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); - if (denominator == 0) - return error.DivisionByZero; - if (denominator < 0) - return error.NegativeDenominator; + if (denominator == 0) return error.DivisionByZero; + if (denominator < 0) return error.NegativeDenominator; return @mod(numerator, denominator); } @@ -448,10 +437,8 @@ fn testMod() void { pub fn rem(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); - if (denominator == 0) - return error.DivisionByZero; - if (denominator < 0) - return error.NegativeDenominator; + if (denominator == 0) return error.DivisionByZero; + if (denominator < 0) return error.NegativeDenominator; return @rem(numerator, denominator); } @@ -475,8 +462,7 @@ fn testRem() void { /// Result is an unsigned integer. pub fn absCast(x: var) @IntType(false, @typeOf(x).bit_count) { const uint = @IntType(false, @typeOf(x).bit_count); - if (x >= 0) - return uint(x); + if (x >= 0) return uint(x); return uint(-(x + 1)) + 1; } @@ -495,15 +481,12 @@ test "math.absCast" { /// Returns the negation of the integer parameter. /// Result is a signed integer. pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) { - if (@typeOf(x).is_signed) - return negate(x); + if (@typeOf(x).is_signed) return negate(x); const int = @IntType(true, @typeOf(x).bit_count); - if (x > -@minValue(int)) - return error.Overflow; + if (x > -@minValue(int)) return error.Overflow; - if (x == -@minValue(int)) - return @minValue(int); + if (x == -@minValue(int)) return @minValue(int); return -int(x); } @@ -546,7 +529,7 @@ pub fn floorPowerOfTwo(comptime T: type, value: T) T { var x = value; comptime var i = 1; - inline while(T.bit_count > i) : (i *= 2) { + inline while (T.bit_count > i) : (i *= 2) { x |= (x >> i); } diff --git a/std/mem.zig b/std/mem.zig index d874f8a6c9..3ca87b35d3 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -6,14 +6,14 @@ const builtin = @import("builtin"); const mem = this; pub const Allocator = struct { - const Error = error {OutOfMemory}; + const Error = error{OutOfMemory}; /// Allocate byte_count bytes and return them in a slice, with the /// slice's pointer aligned at least to alignment bytes. /// The returned newly allocated memory is undefined. /// `alignment` is guaranteed to be >= 1 /// `alignment` is guaranteed to be a power of 2 - allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8, + allocFn: fn(self: &Allocator, byte_count: usize, alignment: u29) Error![]u8, /// If `new_byte_count > old_mem.len`: /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn. @@ -26,10 +26,10 @@ pub const Allocator = struct { /// The returned newly allocated memory is undefined. /// `alignment` is guaranteed to be >= 1 /// `alignment` is guaranteed to be a power of 2 - reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, + reallocFn: fn(self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` - freeFn: fn (self: &Allocator, old_mem: []u8) void, + freeFn: fn(self: &Allocator, old_mem: []u8) void, fn create(self: &Allocator, comptime T: type) !&T { if (@sizeOf(T) == 0) return &{}; @@ -47,7 +47,7 @@ pub const Allocator = struct { if (@sizeOf(T) == 0) return &{}; const slice = try self.alloc(T, 1); const ptr = &slice[0]; - *ptr = *init; + ptr.* = init.*; return ptr; } @@ -59,9 +59,7 @@ pub const Allocator = struct { return self.alignedAlloc(T, @alignOf(T), n); } - fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29, - n: usize) ![]align(alignment) T - { + fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { if (n == 0) { return (&align(alignment) T)(undefined)[0..0]; } @@ -70,7 +68,7 @@ pub const Allocator = struct { assert(byte_slice.len == byte_count); // This loop gets optimized out in ReleaseFast mode for (byte_slice) |*byte| { - *byte = undefined; + byte.* = undefined; } return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); } @@ -79,9 +77,7 @@ pub const Allocator = struct { return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } - fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29, - old_mem: []align(alignment) T, n: usize) ![]align(alignment) T - { + fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { if (old_mem.len == 0) { return self.alloc(T, n); } @@ -97,7 +93,7 @@ pub const Allocator = struct { if (n > old_mem.len) { // This loop gets optimized out in ReleaseFast mode for (byte_slice[old_byte_slice.len..]) |*byte| { - *byte = undefined; + byte.* = undefined; } } return ([]T)(@alignCast(alignment, byte_slice)); @@ -110,9 +106,7 @@ pub const Allocator = struct { return self.alignedShrink(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } - fn alignedShrink(self: &Allocator, comptime T: type, comptime alignment: u29, - old_mem: []align(alignment) T, n: usize) []align(alignment) T - { + fn alignedShrink(self: &Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) []align(alignment) T { if (n == 0) { self.free(old_mem); return old_mem[0..0]; @@ -131,8 +125,7 @@ pub const Allocator = struct { fn free(self: &Allocator, memory: var) void { const bytes = ([]const u8)(memory); - if (bytes.len == 0) - return; + if (bytes.len == 0) return; const non_const_ptr = @intToPtr(&u8, @ptrToInt(bytes.ptr)); self.freeFn(self, non_const_ptr[0..bytes.len]); } @@ -146,11 +139,13 @@ pub fn copy(comptime T: type, dest: []T, source: []const T) void { // this and automatically omit safety checks for loops @setRuntimeSafety(false); assert(dest.len >= source.len); - for (source) |s, i| dest[i] = s; + for (source) |s, i| + dest[i] = s; } pub fn set(comptime T: type, dest: []T, value: T) void { - for (dest) |*d| *d = value; + for (dest) |*d| + d.* = value; } /// Returns true if lhs < rhs, false otherwise @@ -229,8 +224,7 @@ pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { var i: usize = slice.len; while (i != 0) { i -= 1; - if (slice[i] == value) - return i; + if (slice[i] == value) return i; } return null; } @@ -238,8 +232,7 @@ pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize { var i: usize = start_index; while (i < slice.len) : (i += 1) { - if (slice[i] == value) - return i; + if (slice[i] == value) return i; } return null; } @@ -253,8 +246,7 @@ pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?us while (i != 0) { i -= 1; for (values) |value| { - if (slice[i] == value) - return i; + if (slice[i] == value) return i; } } return null; @@ -264,8 +256,7 @@ pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, val var i: usize = start_index; while (i < slice.len) : (i += 1) { for (values) |value| { - if (slice[i] == value) - return i; + if (slice[i] == value) return i; } } return null; @@ -279,28 +270,23 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize /// To start looking at a different index, slice the haystack first. /// TODO is there even a better algorithm for this? pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize { - if (needle.len > haystack.len) - return null; + if (needle.len > haystack.len) return null; var i: usize = haystack.len - needle.len; while (true) : (i -= 1) { - if (mem.eql(T, haystack[i..i+needle.len], needle)) - return i; - if (i == 0) - return null; + if (mem.eql(T, haystack[i..i + needle.len], needle)) return i; + if (i == 0) return null; } } // TODO boyer-moore algorithm pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize { - if (needle.len > haystack.len) - return null; + if (needle.len > haystack.len) return null; var i: usize = start_index; const end = haystack.len - needle.len; while (i <= end) : (i += 1) { - if (eql(T, haystack[i .. i + needle.len], needle)) - return i; + if (eql(T, haystack[i..i + needle.len], needle)) return i; } return null; } @@ -355,9 +341,12 @@ pub fn readIntBE(comptime T: type, bytes: []const u8) T { } assert(bytes.len == @sizeOf(T)); var result: T = 0; - {comptime var i = 0; inline while (i < @sizeOf(T)) : (i += 1) { - result = (result << 8) | T(bytes[i]); - }} + { + comptime var i = 0; + inline while (i < @sizeOf(T)) : (i += 1) { + result = (result << 8) | T(bytes[i]); + } + } return result; } @@ -369,9 +358,12 @@ pub fn readIntLE(comptime T: type, bytes: []const u8) T { } assert(bytes.len == @sizeOf(T)); var result: T = 0; - {comptime var i = 0; inline while (i < @sizeOf(T)) : (i += 1) { - result |= T(bytes[i]) << i * 8; - }} + { + comptime var i = 0; + inline while (i < @sizeOf(T)) : (i += 1) { + result |= T(bytes[i]) << i * 8; + } + } return result; } @@ -393,7 +385,7 @@ pub fn writeInt(buf: []u8, value: var, endian: builtin.Endian) void { }, builtin.Endian.Little => { for (buf) |*b| { - *b = @truncate(u8, bits); + b.* = @truncate(u8, bits); bits >>= 8; } }, @@ -401,7 +393,6 @@ pub fn writeInt(buf: []u8, value: var, endian: builtin.Endian) void { assert(bits == 0); } - pub fn hash_slice_u8(k: []const u8) u32 { // FNV 32-bit hash var h: u32 = 2166136261; @@ -420,7 +411,7 @@ pub fn eql_slice_u8(a: []const u8, b: []const u8) bool { /// split(" abc def ghi ", " ") /// Will return slices for "abc", "def", "ghi", null, in that order. pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator { - return SplitIterator { + return SplitIterator{ .index = 0, .buffer = buffer, .split_bytes = split_bytes, @@ -436,7 +427,7 @@ test "mem.split" { } pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { - return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle); + return if (needle.len > haystack.len) false else eql(T, haystack[0..needle.len], needle); } test "mem.startsWith" { @@ -445,10 +436,9 @@ test "mem.startsWith" { } pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool { - return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle); + return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len..], needle); } - test "mem.endsWith" { assert(endsWith(u8, "Needle in haystack", "haystack")); assert(!endsWith(u8, "Bob", "Bo")); @@ -542,29 +532,47 @@ test "testReadInt" { } fn testReadIntImpl() void { { - const bytes = []u8{ 0x12, 0x34, 0x56, 0x78 }; - assert(readInt(bytes, u32, builtin.Endian.Big) == 0x12345678); - assert(readIntBE(u32, bytes) == 0x12345678); - assert(readIntBE(i32, bytes) == 0x12345678); + const bytes = []u8{ + 0x12, + 0x34, + 0x56, + 0x78, + }; + assert(readInt(bytes, u32, builtin.Endian.Big) == 0x12345678); + assert(readIntBE(u32, bytes) == 0x12345678); + assert(readIntBE(i32, bytes) == 0x12345678); assert(readInt(bytes, u32, builtin.Endian.Little) == 0x78563412); - assert(readIntLE(u32, bytes) == 0x78563412); - assert(readIntLE(i32, bytes) == 0x78563412); + assert(readIntLE(u32, bytes) == 0x78563412); + assert(readIntLE(i32, bytes) == 0x78563412); } { - const buf = []u8{0x00, 0x00, 0x12, 0x34}; + const buf = []u8{ + 0x00, + 0x00, + 0x12, + 0x34, + }; const answer = readInt(buf, u64, builtin.Endian.Big); assert(answer == 0x00001234); } { - const buf = []u8{0x12, 0x34, 0x00, 0x00}; + const buf = []u8{ + 0x12, + 0x34, + 0x00, + 0x00, + }; const answer = readInt(buf, u64, builtin.Endian.Little); assert(answer == 0x00003412); } { - const bytes = []u8{0xff, 0xfe}; - assert(readIntBE(u16, bytes) == 0xfffe); + const bytes = []u8{ + 0xff, + 0xfe, + }; + assert(readIntBE(u16, bytes) == 0xfffe); assert(readIntBE(i16, bytes) == -0x0002); - assert(readIntLE(u16, bytes) == 0xfeff); + assert(readIntLE(u16, bytes) == 0xfeff); assert(readIntLE(i16, bytes) == -0x0101); } } @@ -577,19 +585,38 @@ fn testWriteIntImpl() void { var bytes: [4]u8 = undefined; writeInt(bytes[0..], u32(0x12345678), builtin.Endian.Big); - assert(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, 0x78 })); + assert(eql(u8, bytes, []u8{ + 0x12, + 0x34, + 0x56, + 0x78, + })); writeInt(bytes[0..], u32(0x78563412), builtin.Endian.Little); - assert(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, 0x78 })); + assert(eql(u8, bytes, []u8{ + 0x12, + 0x34, + 0x56, + 0x78, + })); writeInt(bytes[0..], u16(0x1234), builtin.Endian.Big); - assert(eql(u8, bytes, []u8{ 0x00, 0x00, 0x12, 0x34 })); + assert(eql(u8, bytes, []u8{ + 0x00, + 0x00, + 0x12, + 0x34, + })); writeInt(bytes[0..], u16(0x1234), builtin.Endian.Little); - assert(eql(u8, bytes, []u8{ 0x34, 0x12, 0x00, 0x00 })); + assert(eql(u8, bytes, []u8{ + 0x34, + 0x12, + 0x00, + 0x00, + })); } - pub fn min(comptime T: type, slice: []const T) T { var best = slice[0]; for (slice[1..]) |item| { @@ -615,9 +642,9 @@ test "mem.max" { } pub fn swap(comptime T: type, a: &T, b: &T) void { - const tmp = *a; - *a = *b; - *b = tmp; + const tmp = a.*; + a.* = b.*; + b.* = tmp; } /// In-place order reversal of a slice @@ -630,10 +657,22 @@ pub fn reverse(comptime T: type, items: []T) void { } test "std.mem.reverse" { - var arr = []i32{ 5, 3, 1, 2, 4 }; + var arr = []i32{ + 5, + 3, + 1, + 2, + 4, + }; reverse(i32, arr[0..]); - assert(eql(i32, arr, []i32{ 4, 2, 1, 3, 5 })); + assert(eql(i32, arr, []i32{ + 4, + 2, + 1, + 3, + 5, + })); } /// In-place rotation of the values in an array ([0 1 2 3] becomes [1 2 3 0] if we rotate by 1) @@ -645,10 +684,22 @@ pub fn rotate(comptime T: type, items: []T, amount: usize) void { } test "std.mem.rotate" { - var arr = []i32{ 5, 3, 1, 2, 4 }; + var arr = []i32{ + 5, + 3, + 1, + 2, + 4, + }; rotate(i32, arr[0..], 2); - assert(eql(i32, arr, []i32{ 1, 2, 4, 5, 3 })); + assert(eql(i32, arr, []i32{ + 1, + 2, + 4, + 5, + 3, + })); } // TODO: When https://github.com/zig-lang/zig/issues/649 is solved these can be done by diff --git a/std/zig/parser.zig b/std/zig/parser.zig index b5849c3e96..79a38f00ee 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -3705,7 +3705,9 @@ pub const Parser = struct { }, ast.Node.Id.PrefixOp => { const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); - try stack.append(RenderState { .Expression = prefix_op_node.rhs }); + if (prefix_op_node.op != ast.Node.PrefixOp.Op.Deref) { + try stack.append(RenderState { .Expression = prefix_op_node.rhs }); + } switch (prefix_op_node.op) { ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { try stream.write("&"); @@ -3742,7 +3744,10 @@ pub const Parser = struct { }, ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), - ast.Node.PrefixOp.Op.Deref => try stream.write("*"), + ast.Node.PrefixOp.Op.Deref => { + try stack.append(RenderState { .Text = ".*" }); + try stack.append(RenderState { .Expression = prefix_op_node.rhs }); + }, ast.Node.PrefixOp.Op.Negation => try stream.write("-"), ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"), ast.Node.PrefixOp.Op.Try => try stream.write("try "), diff --git a/test/cases/align.zig b/test/cases/align.zig index ad3a66a2e0..a1259e96bf 100644 --- a/test/cases/align.zig +++ b/test/cases/align.zig @@ -10,7 +10,9 @@ test "global variable alignment" { assert(@typeOf(slice) == []align(4) u8); } -fn derp() align(@sizeOf(usize) * 2) i32 { return 1234; } +fn derp() align(@sizeOf(usize) * 2) i32 { + return 1234; +} fn noop1() align(1) void {} fn noop4() align(4) void {} @@ -22,7 +24,6 @@ test "function alignment" { noop4(); } - var baz: packed struct { a: u32, b: u32, @@ -32,7 +33,6 @@ test "packed struct alignment" { assert(@typeOf(&baz.b) == &align(1) u32); } - const blah: packed struct { a: u3, b: u3, @@ -53,29 +53,43 @@ test "implicitly decreasing pointer alignment" { assert(addUnaligned(&a, &b) == 7); } -fn addUnaligned(a: &align(1) const u32, b: &align(1) const u32) u32 { return *a + *b; } +fn addUnaligned(a: &align(1) const u32, b: &align(1) const u32) u32 { + return a.* + b.*; +} test "implicitly decreasing slice alignment" { const a: u32 align(4) = 3; const b: u32 align(8) = 4; assert(addUnalignedSlice((&a)[0..1], (&b)[0..1]) == 7); } -fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { return a[0] + b[0]; } +fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { + return a[0] + b[0]; +} test "specifying alignment allows pointer cast" { testBytesAlign(0x33); } fn testBytesAlign(b: u8) void { - var bytes align(4) = []u8{b, b, b, b}; + var bytes align(4) = []u8 { + b, + b, + b, + b, + }; const ptr = @ptrCast(&u32, &bytes[0]); - assert(*ptr == 0x33333333); + assert(ptr.* == 0x33333333); } test "specifying alignment allows slice cast" { testBytesAlignSlice(0x33); } fn testBytesAlignSlice(b: u8) void { - var bytes align(4) = []u8{b, b, b, b}; + var bytes align(4) = []u8 { + b, + b, + b, + b, + }; const slice = ([]u32)(bytes[0..]); assert(slice[0] == 0x33333333); } @@ -89,11 +103,14 @@ fn expectsOnly1(x: &align(1) u32) void { expects4(@alignCast(4, x)); } fn expects4(x: &align(4) u32) void { - *x += 1; + x.* += 1; } test "@alignCast slices" { - var array align(4) = []u32{1, 1}; + var array align(4) = []u32 { + 1, + 1, + }; const slice = array[0..]; sliceExpectsOnly1(slice); assert(slice[0] == 2); @@ -105,31 +122,34 @@ fn sliceExpects4(slice: []align(4) u32) void { slice[0] += 1; } - test "implicitly decreasing fn alignment" { testImplicitlyDecreaseFnAlign(alignedSmall, 1234); testImplicitlyDecreaseFnAlign(alignedBig, 5678); } -fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) void { +fn testImplicitlyDecreaseFnAlign(ptr: fn() align(1) i32, answer: i32) void { assert(ptr() == answer); } -fn alignedSmall() align(8) i32 { return 1234; } -fn alignedBig() align(16) i32 { return 5678; } - +fn alignedSmall() align(8) i32 { + return 1234; +} +fn alignedBig() align(16) i32 { + return 5678; +} test "@alignCast functions" { assert(fnExpectsOnly1(simple4) == 0x19); } -fn fnExpectsOnly1(ptr: fn()align(1) i32) i32 { +fn fnExpectsOnly1(ptr: fn() align(1) i32) i32 { return fnExpects4(@alignCast(4, ptr)); } -fn fnExpects4(ptr: fn()align(4) i32) i32 { +fn fnExpects4(ptr: fn() align(4) i32) i32 { return ptr(); } -fn simple4() align(4) i32 { return 0x19; } - +fn simple4() align(4) i32 { + return 0x19; +} test "generic function with align param" { assert(whyWouldYouEverDoThis(1) == 0x1); @@ -137,8 +157,9 @@ test "generic function with align param" { assert(whyWouldYouEverDoThis(8) == 0x1); } -fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { return 0x1; } - +fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { + return 0x1; +} test "@ptrCast preserves alignment of bigger source" { var x: u32 align(16) = 1234; @@ -146,24 +167,38 @@ test "@ptrCast preserves alignment of bigger source" { assert(@typeOf(ptr) == &align(16) u8); } - test "compile-time known array index has best alignment possible" { // take full advantage of over-alignment - var array align(4) = []u8 {1, 2, 3, 4}; + var array align(4) = []u8 { + 1, + 2, + 3, + 4, + }; assert(@typeOf(&array[0]) == &align(4) u8); assert(@typeOf(&array[1]) == &u8); assert(@typeOf(&array[2]) == &align(2) u8); assert(@typeOf(&array[3]) == &u8); // because align is too small but we still figure out to use 2 - var bigger align(2) = []u64{1, 2, 3, 4}; + var bigger align(2) = []u64 { + 1, + 2, + 3, + 4, + }; assert(@typeOf(&bigger[0]) == &align(2) u64); assert(@typeOf(&bigger[1]) == &align(2) u64); assert(@typeOf(&bigger[2]) == &align(2) u64); assert(@typeOf(&bigger[3]) == &align(2) u64); // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 - var smaller align(2) = []u32{1, 2, 3, 4}; + var smaller align(2) = []u32 { + 1, + 2, + 3, + 4, + }; testIndex(&smaller[0], 0, &align(2) u32); testIndex(&smaller[0], 1, &align(2) u32); testIndex(&smaller[0], 2, &align(2) u32); @@ -182,7 +217,6 @@ fn testIndex2(ptr: &align(4) u8, index: usize, comptime T: type) void { assert(@typeOf(&ptr[index]) == T); } - test "alignstack" { assert(fnWithAlignedStack() == 1234); } diff --git a/test/cases/alignof.zig b/test/cases/alignof.zig index 27b95c7fdc..130a2a5b44 100644 --- a/test/cases/alignof.zig +++ b/test/cases/alignof.zig @@ -1,7 +1,11 @@ const assert = @import("std").debug.assert; const builtin = @import("builtin"); -const Foo = struct { x: u32, y: u32, z: u32, }; +const Foo = struct { + x: u32, + y: u32, + z: u32, +}; test "@alignOf(T) before referencing T" { comptime assert(@alignOf(Foo) != @maxValue(usize)); diff --git a/test/cases/array.zig b/test/cases/array.zig index 577161dd16..0fb61b2a9f 100644 --- a/test/cases/array.zig +++ b/test/cases/array.zig @@ -2,9 +2,9 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; test "arrays" { - var array : [5]u32 = undefined; + var array: [5]u32 = undefined; - var i : u32 = 0; + var i: u32 = 0; while (i < 5) { array[i] = i + 1; i = array[i]; @@ -34,24 +34,41 @@ test "void arrays" { } test "array literal" { - const hex_mult = []u16{4096, 256, 16, 1}; + const hex_mult = []u16 { + 4096, + 256, + 16, + 1, + }; assert(hex_mult.len == 4); assert(hex_mult[1] == 256); } test "array dot len const expr" { - assert(comptime x: {break :x some_array.len == 4;}); + assert(comptime x: { + break :x some_array.len == 4; + }); } const ArrayDotLenConstExpr = struct { y: [some_array.len]u8, }; -const some_array = []u8 {0, 1, 2, 3}; - +const some_array = []u8 { + 0, + 1, + 2, + 3, +}; test "nested arrays" { - const array_of_strings = [][]const u8 {"hello", "this", "is", "my", "thing"}; + const array_of_strings = [][]const u8 { + "hello", + "this", + "is", + "my", + "thing", + }; for (array_of_strings) |s, i| { if (i == 0) assert(mem.eql(u8, s, "hello")); if (i == 1) assert(mem.eql(u8, s, "this")); @@ -61,7 +78,6 @@ test "nested arrays" { } } - var s_array: [8]Sub = undefined; const Sub = struct { b: u8, @@ -70,7 +86,9 @@ const Str = struct { a: []Sub, }; test "set global var array via slice embedded in struct" { - var s = Str { .a = s_array[0..]}; + var s = Str { + .a = s_array[0..], + }; s.a[0].b = 1; s.a[1].b = 2; @@ -82,7 +100,10 @@ test "set global var array via slice embedded in struct" { } test "array literal with specified size" { - var array = [2]u8{1, 2}; + var array = [2]u8 { + 1, + 2, + }; assert(array[0] == 1); assert(array[1] == 2); } diff --git a/test/cases/bitcast.zig b/test/cases/bitcast.zig index f1f2ccd672..878140954a 100644 --- a/test/cases/bitcast.zig +++ b/test/cases/bitcast.zig @@ -10,5 +10,9 @@ fn testBitCast_i32_u32() void { assert(conv2(@maxValue(u32)) == -1); } -fn conv(x: i32) u32 { return @bitCast(u32, x); } -fn conv2(x: u32) i32 { return @bitCast(i32, x); } +fn conv(x: i32) u32 { + return @bitCast(u32, x); +} +fn conv2(x: u32) i32 { + return @bitCast(i32, x); +} diff --git a/test/cases/bugs/394.zig b/test/cases/bugs/394.zig index 071619d59c..a99bd18b28 100644 --- a/test/cases/bugs/394.zig +++ b/test/cases/bugs/394.zig @@ -1,9 +1,20 @@ -const E = union(enum) { A: [9]u8, B: u64, }; -const S = struct { x: u8, y: E, }; +const E = union(enum) { + A: [9]u8, + B: u64, +}; +const S = struct { + x: u8, + y: E, +}; const assert = @import("std").debug.assert; test "bug 394 fixed" { - const x = S { .x = 3, .y = E {.B = 1 } }; + const x = S { + .x = 3, + .y = E { + .B = 1, + }, + }; assert(x.x == 3); } diff --git a/test/cases/bugs/655.zig b/test/cases/bugs/655.zig index e6a275004c..4431767d5c 100644 --- a/test/cases/bugs/655.zig +++ b/test/cases/bugs/655.zig @@ -8,5 +8,5 @@ test "function with &const parameter with type dereferenced by namespace" { } fn foo(x: &const other_file.Integer) void { - std.debug.assert(*x == 1234); + std.debug.assert(x.* == 1234); } diff --git a/test/cases/bugs/656.zig b/test/cases/bugs/656.zig index ce3eec8046..24a28bf411 100644 --- a/test/cases/bugs/656.zig +++ b/test/cases/bugs/656.zig @@ -14,12 +14,15 @@ test "nullable if after an if in a switch prong of a switch with 2 prongs in an } fn foo(a: bool, b: bool) void { - var prefix_op = PrefixOp { .AddrOf = Value { .align_expr = 1234 } }; - if (a) { - } else { + var prefix_op = PrefixOp { + .AddrOf = Value { + .align_expr = 1234, + }, + }; + if (a) {} else { switch (prefix_op) { PrefixOp.AddrOf => |addr_of_info| { - if (b) { } + if (b) {} if (addr_of_info.align_expr) |align_expr| { assert(align_expr == 1234); } diff --git a/test/cases/bugs/828.zig b/test/cases/bugs/828.zig index c46548cb7a..8f329e4f82 100644 --- a/test/cases/bugs/828.zig +++ b/test/cases/bugs/828.zig @@ -1,10 +1,10 @@ const CountBy = struct { a: usize, - + const One = CountBy { .a = 1, }; - + pub fn counter(self: &const CountBy) Counter { return Counter { .i = 0, @@ -14,7 +14,7 @@ const CountBy = struct { const Counter = struct { i: usize, - + pub fn count(self: &Counter) bool { self.i += 1; return self.i <= 10; @@ -24,8 +24,8 @@ const Counter = struct { fn constCount(comptime cb: &const CountBy, comptime unused: u32) void { comptime { var cnt = cb.counter(); - if(cnt.i != 0) @compileError("Counter instance reused!"); - while(cnt.count()){} + if (cnt.i != 0) @compileError("Counter instance reused!"); + while (cnt.count()) {} } } diff --git a/test/cases/bugs/920.zig b/test/cases/bugs/920.zig index 13c03a304f..c2b6816e94 100644 --- a/test/cases/bugs/920.zig +++ b/test/cases/bugs/920.zig @@ -12,8 +12,7 @@ const ZigTable = struct { zero_case: fn(&Random, f64) f64, }; -fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64, - comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable { +fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64, comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable { var tables: ZigTable = undefined; tables.is_symmetric = is_symmetric; @@ -26,12 +25,12 @@ fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, co for (tables.x[2..256]) |*entry, i| { const last = tables.x[2 + i - 1]; - *entry = f_inv(v / last + f(last)); + entry.* = f_inv(v / last + f(last)); } tables.x[256] = 0; for (tables.f[0..]) |*entry, i| { - *entry = f(tables.x[i]); + entry.* = f(tables.x[i]); } return tables; @@ -40,9 +39,15 @@ fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, co const norm_r = 3.6541528853610088; const norm_v = 0.00492867323399; -fn norm_f(x: f64) f64 { return math.exp(-x * x / 2.0); } -fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); } -fn norm_zero_case(random: &Random, u: f64) f64 { return 0.0; } +fn norm_f(x: f64) f64 { + return math.exp(-x * x / 2.0); +} +fn norm_f_inv(y: f64) f64 { + return math.sqrt(-2.0 * math.ln(y)); +} +fn norm_zero_case(random: &Random, u: f64) f64 { + return 0.0; +} const NormalDist = blk: { @setEvalBranchQuota(30000); diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 024ece0055..547cca5797 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -14,10 +14,10 @@ test "integer literal to pointer cast" { } test "pointer reinterpret const float to int" { - const float: f64 = 5.99999999999994648725e-01; + const float: f64 = 5.99999999999994648725e - 01; const float_ptr = &float; const int_ptr = @ptrCast(&const i32, float_ptr); - const int_val = *int_ptr; + const int_val = int_ptr.*; assert(int_val == 858993411); } @@ -29,25 +29,31 @@ test "implicitly cast a pointer to a const pointer of it" { } fn funcWithConstPtrPtr(x: &const &i32) void { - **x += 1; + x.*.* += 1; } test "implicitly cast a container to a const pointer of it" { - const z = Struct(void) { .x = void{} }; + const z = Struct(void) { + .x = void{}, + }; assert(0 == @sizeOf(@typeOf(z))); assert(void{} == Struct(void).pointer(z).x); assert(void{} == Struct(void).pointer(&z).x); assert(void{} == Struct(void).maybePointer(z).x); assert(void{} == Struct(void).maybePointer(&z).x); assert(void{} == Struct(void).maybePointer(null).x); - const s = Struct(u8) { .x = 42 }; + const s = Struct(u8) { + .x = 42, + }; assert(0 != @sizeOf(@typeOf(s))); assert(42 == Struct(u8).pointer(s).x); assert(42 == Struct(u8).pointer(&s).x); assert(42 == Struct(u8).maybePointer(s).x); assert(42 == Struct(u8).maybePointer(&s).x); assert(0 == Struct(u8).maybePointer(null).x); - const u = Union { .x = 42 }; + const u = Union { + .x = 42, + }; assert(42 == Union.pointer(u).x); assert(42 == Union.pointer(&u).x); assert(42 == Union.maybePointer(u).x); @@ -67,12 +73,14 @@ fn Struct(comptime T: type) type { x: T, fn pointer(self: &const Self) Self { - return *self; + return self.*; } fn maybePointer(self: ?&const Self) Self { - const none = Self { .x = if (T == void) void{} else 0 }; - return *(self ?? &none); + const none = Self { + .x = if (T == void) void{} else 0, + }; + return (self ?? &none).*; } }; } @@ -81,12 +89,14 @@ const Union = union { x: u8, fn pointer(self: &const Union) Union { - return *self; + return self.*; } fn maybePointer(self: ?&const Union) Union { - const none = Union { .x = 0 }; - return *(self ?? &none); + const none = Union { + .x = 0, + }; + return (self ?? &none).*; } }; @@ -95,11 +105,11 @@ const Enum = enum { Some, fn pointer(self: &const Enum) Enum { - return *self; + return self.*; } fn maybePointer(self: ?&const Enum) Enum { - return *(self ?? &Enum.None); + return (self ?? &Enum.None).*; } }; @@ -108,19 +118,21 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" { const Self = this; x: u8, fn constConst(p: &const &const Self) u8 { - return (*p).x; + return (p.*).x; } fn maybeConstConst(p: ?&const &const Self) u8 { - return (*??p).x; + return (??p.*).x; } fn constConstConst(p: &const &const &const Self) u8 { - return (**p).x; + return (p.*.*).x; } fn maybeConstConstConst(p: ?&const &const &const Self) u8 { - return (**??p).x; + return (??p.*.*).x; } }; - const s = S { .x = 42 }; + const s = S { + .x = 42, + }; const p = &s; const q = &p; const r = &q; @@ -154,7 +166,6 @@ fn boolToStr(b: bool) []const u8 { return if (b) "true" else "false"; } - test "peer resolve array and const slice" { testPeerResolveArrayConstSlice(true); comptime testPeerResolveArrayConstSlice(true); @@ -168,12 +179,12 @@ fn testPeerResolveArrayConstSlice(b: bool) void { test "integer literal to &const int" { const x: &const i32 = 3; - assert(*x == 3); + assert(x.* == 3); } test "string literal to &const []const u8" { const x: &const []const u8 = "hello"; - assert(mem.eql(u8, *x, "hello")); + assert(mem.eql(u8, x.*, "hello")); } test "implicitly cast from T to error!?T" { @@ -191,7 +202,9 @@ fn castToMaybeTypeError(z: i32) void { const f = z; const g: error!?i32 = f; - const a = A{ .a = z }; + const a = A { + .a = z, + }; const b: error!?A = a; assert((??(b catch unreachable)).a == 1); } @@ -205,7 +218,6 @@ fn implicitIntLitToMaybe() void { const g: error!?i32 = 1; } - test "return null from fn() error!?&T" { const a = returnNullFromMaybeTypeErrorRef(); const b = returnNullLitFromMaybeTypeErrorRef(); @@ -235,7 +247,6 @@ fn peerTypeTAndMaybeT(c: bool, b: bool) ?usize { return usize(3); } - test "peer type resolution: [0]u8 and []const u8" { assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); @@ -246,7 +257,7 @@ test "peer type resolution: [0]u8 and []const u8" { } fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { if (a) { - return []const u8 {}; + return []const u8{}; } return slice[0..1]; @@ -261,7 +272,6 @@ fn castToMaybeSlice() ?[]const u8 { return "hi"; } - test "implicitly cast from [0]T to error![]T" { testCastZeroArrayToErrSliceMut(); comptime testCastZeroArrayToErrSliceMut(); @@ -329,7 +339,6 @@ fn foo(args: ...) void { assert(@typeOf(args[0]) == &const [5]u8); } - test "peer type resolution: error and [N]T" { // TODO: implicit error!T to error!U where T can implicitly cast to U //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); @@ -378,7 +387,12 @@ fn cast128Float(x: u128) f128 { } test "const slice widen cast" { - const bytes align(4) = []u8{0x12, 0x12, 0x12, 0x12}; + const bytes align(4) = []u8 { + 0x12, + 0x12, + 0x12, + 0x12, + }; const u32_value = ([]const u32)(bytes[0..])[0]; assert(u32_value == 0x12121212); diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 46055d7469..d00617eb7c 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -36,7 +36,7 @@ async fn testAsyncSeq() void { suspend; seq('d'); } -var points = []u8{0} ** "abcdefg".len; +var points = []u8 {0} ** "abcdefg".len; var index: usize = 0; fn seq(c: u8) void { @@ -94,7 +94,7 @@ async fn await_another() i32 { return 1234; } -var await_points = []u8{0} ** "abcdefghi".len; +var await_points = []u8 {0} ** "abcdefghi".len; var await_seq_index: usize = 0; fn await_seq(c: u8) void { @@ -102,7 +102,6 @@ fn await_seq(c: u8) void { await_seq_index += 1; } - var early_final_result: i32 = 0; test "coroutine await early return" { @@ -126,7 +125,7 @@ async fn early_another() i32 { return 1234; } -var early_points = []u8{0} ** "abcdef".len; +var early_points = []u8 {0} ** "abcdef".len; var early_seq_index: usize = 0; fn early_seq(c: u8) void { @@ -175,8 +174,8 @@ test "async fn pointer in a struct field" { } async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void { - defer *y += 2; - *y += 1; + defer y.* += 2; + y.* += 1; suspend; } @@ -205,7 +204,8 @@ test "error return trace across suspend points - async return" { cancel p2; } -fn nonFailing() promise->error!void { +// TODO https://github.com/zig-lang/zig/issues/760 +fn nonFailing() (promise->error!void) { return async suspendThenFail() catch unreachable; } @@ -238,7 +238,7 @@ async fn testBreakFromSuspend(my_result: &i32) void { s: suspend |p| { break :s; } - *my_result += 1; + my_result.* += 1; suspend; - *my_result += 1; + my_result.* += 1; } diff --git a/test/cases/defer.zig b/test/cases/defer.zig index 5470b4bbd0..d2b00d1f91 100644 --- a/test/cases/defer.zig +++ b/test/cases/defer.zig @@ -5,9 +5,18 @@ var index: usize = undefined; fn runSomeErrorDefers(x: bool) !bool { index = 0; - defer {result[index] = 'a'; index += 1;} - errdefer {result[index] = 'b'; index += 1;} - defer {result[index] = 'c'; index += 1;} + defer { + result[index] = 'a'; + index += 1; + } + errdefer { + result[index] = 'b'; + index += 1; + } + defer { + result[index] = 'c'; + index += 1; + } return if (x) x else error.FalseNotAllowed; } diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 644c989b04..872e753f20 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -2,8 +2,15 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; test "enum type" { - const foo1 = Foo{ .One = 13}; - const foo2 = Foo{. Two = Point { .x = 1234, .y = 5678, }}; + const foo1 = Foo { + .One = 13, + }; + const foo2 = Foo { + .Two = Point { + .x = 1234, + .y = 5678, + }, + }; const bar = Bar.B; assert(bar == Bar.B); @@ -41,26 +48,31 @@ const Bar = enum { }; fn returnAnInt(x: i32) Foo { - return Foo { .One = x }; + return Foo { + .One = x, + }; } - test "constant enum with payload" { - var empty = AnEnumWithPayload {.Empty = {}}; - var full = AnEnumWithPayload {.Full = 13}; + var empty = AnEnumWithPayload { + .Empty = {}, + }; + var full = AnEnumWithPayload { + .Full = 13, + }; shouldBeEmpty(empty); shouldBeNotEmpty(full); } fn shouldBeEmpty(x: &const AnEnumWithPayload) void { - switch (*x) { + switch (x.*) { AnEnumWithPayload.Empty => {}, else => unreachable, } } fn shouldBeNotEmpty(x: &const AnEnumWithPayload) void { - switch (*x) { + switch (x.*) { AnEnumWithPayload.Empty => unreachable, else => {}, } @@ -71,8 +83,6 @@ const AnEnumWithPayload = union(enum) { Full: i32, }; - - const Number = enum { Zero, One, @@ -93,7 +103,6 @@ fn shouldEqual(n: Number, expected: u3) void { assert(u3(n) == expected); } - test "int to enum" { testIntToEnumEval(3); } @@ -108,7 +117,6 @@ const IntToEnumNumber = enum { Four, }; - test "@tagName" { assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); comptime assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); @@ -124,7 +132,6 @@ const BareNumber = enum { Three, }; - test "enum alignment" { comptime { assert(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); @@ -137,47 +144,529 @@ const AlignTestEnum = union(enum) { B: u64, }; -const ValueCount1 = enum { I0 }; -const ValueCount2 = enum { I0, I1 }; +const ValueCount1 = enum { + I0, +}; +const ValueCount2 = enum { + I0, + I1, +}; const ValueCount256 = enum { - I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, - I16, I17, I18, I19, I20, I21, I22, I23, I24, I25, I26, I27, I28, I29, I30, I31, - I32, I33, I34, I35, I36, I37, I38, I39, I40, I41, I42, I43, I44, I45, I46, I47, - I48, I49, I50, I51, I52, I53, I54, I55, I56, I57, I58, I59, I60, I61, I62, I63, - I64, I65, I66, I67, I68, I69, I70, I71, I72, I73, I74, I75, I76, I77, I78, I79, - I80, I81, I82, I83, I84, I85, I86, I87, I88, I89, I90, I91, I92, I93, I94, I95, - I96, I97, I98, I99, I100, I101, I102, I103, I104, I105, I106, I107, I108, I109, - I110, I111, I112, I113, I114, I115, I116, I117, I118, I119, I120, I121, I122, I123, - I124, I125, I126, I127, I128, I129, I130, I131, I132, I133, I134, I135, I136, I137, - I138, I139, I140, I141, I142, I143, I144, I145, I146, I147, I148, I149, I150, I151, - I152, I153, I154, I155, I156, I157, I158, I159, I160, I161, I162, I163, I164, I165, - I166, I167, I168, I169, I170, I171, I172, I173, I174, I175, I176, I177, I178, I179, - I180, I181, I182, I183, I184, I185, I186, I187, I188, I189, I190, I191, I192, I193, - I194, I195, I196, I197, I198, I199, I200, I201, I202, I203, I204, I205, I206, I207, - I208, I209, I210, I211, I212, I213, I214, I215, I216, I217, I218, I219, I220, I221, - I222, I223, I224, I225, I226, I227, I228, I229, I230, I231, I232, I233, I234, I235, - I236, I237, I238, I239, I240, I241, I242, I243, I244, I245, I246, I247, I248, I249, - I250, I251, I252, I253, I254, I255 + I0, + I1, + I2, + I3, + I4, + I5, + I6, + I7, + I8, + I9, + I10, + I11, + I12, + I13, + I14, + I15, + I16, + I17, + I18, + I19, + I20, + I21, + I22, + I23, + I24, + I25, + I26, + I27, + I28, + I29, + I30, + I31, + I32, + I33, + I34, + I35, + I36, + I37, + I38, + I39, + I40, + I41, + I42, + I43, + I44, + I45, + I46, + I47, + I48, + I49, + I50, + I51, + I52, + I53, + I54, + I55, + I56, + I57, + I58, + I59, + I60, + I61, + I62, + I63, + I64, + I65, + I66, + I67, + I68, + I69, + I70, + I71, + I72, + I73, + I74, + I75, + I76, + I77, + I78, + I79, + I80, + I81, + I82, + I83, + I84, + I85, + I86, + I87, + I88, + I89, + I90, + I91, + I92, + I93, + I94, + I95, + I96, + I97, + I98, + I99, + I100, + I101, + I102, + I103, + I104, + I105, + I106, + I107, + I108, + I109, + I110, + I111, + I112, + I113, + I114, + I115, + I116, + I117, + I118, + I119, + I120, + I121, + I122, + I123, + I124, + I125, + I126, + I127, + I128, + I129, + I130, + I131, + I132, + I133, + I134, + I135, + I136, + I137, + I138, + I139, + I140, + I141, + I142, + I143, + I144, + I145, + I146, + I147, + I148, + I149, + I150, + I151, + I152, + I153, + I154, + I155, + I156, + I157, + I158, + I159, + I160, + I161, + I162, + I163, + I164, + I165, + I166, + I167, + I168, + I169, + I170, + I171, + I172, + I173, + I174, + I175, + I176, + I177, + I178, + I179, + I180, + I181, + I182, + I183, + I184, + I185, + I186, + I187, + I188, + I189, + I190, + I191, + I192, + I193, + I194, + I195, + I196, + I197, + I198, + I199, + I200, + I201, + I202, + I203, + I204, + I205, + I206, + I207, + I208, + I209, + I210, + I211, + I212, + I213, + I214, + I215, + I216, + I217, + I218, + I219, + I220, + I221, + I222, + I223, + I224, + I225, + I226, + I227, + I228, + I229, + I230, + I231, + I232, + I233, + I234, + I235, + I236, + I237, + I238, + I239, + I240, + I241, + I242, + I243, + I244, + I245, + I246, + I247, + I248, + I249, + I250, + I251, + I252, + I253, + I254, + I255, }; const ValueCount257 = enum { - I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, - I16, I17, I18, I19, I20, I21, I22, I23, I24, I25, I26, I27, I28, I29, I30, I31, - I32, I33, I34, I35, I36, I37, I38, I39, I40, I41, I42, I43, I44, I45, I46, I47, - I48, I49, I50, I51, I52, I53, I54, I55, I56, I57, I58, I59, I60, I61, I62, I63, - I64, I65, I66, I67, I68, I69, I70, I71, I72, I73, I74, I75, I76, I77, I78, I79, - I80, I81, I82, I83, I84, I85, I86, I87, I88, I89, I90, I91, I92, I93, I94, I95, - I96, I97, I98, I99, I100, I101, I102, I103, I104, I105, I106, I107, I108, I109, - I110, I111, I112, I113, I114, I115, I116, I117, I118, I119, I120, I121, I122, I123, - I124, I125, I126, I127, I128, I129, I130, I131, I132, I133, I134, I135, I136, I137, - I138, I139, I140, I141, I142, I143, I144, I145, I146, I147, I148, I149, I150, I151, - I152, I153, I154, I155, I156, I157, I158, I159, I160, I161, I162, I163, I164, I165, - I166, I167, I168, I169, I170, I171, I172, I173, I174, I175, I176, I177, I178, I179, - I180, I181, I182, I183, I184, I185, I186, I187, I188, I189, I190, I191, I192, I193, - I194, I195, I196, I197, I198, I199, I200, I201, I202, I203, I204, I205, I206, I207, - I208, I209, I210, I211, I212, I213, I214, I215, I216, I217, I218, I219, I220, I221, - I222, I223, I224, I225, I226, I227, I228, I229, I230, I231, I232, I233, I234, I235, - I236, I237, I238, I239, I240, I241, I242, I243, I244, I245, I246, I247, I248, I249, - I250, I251, I252, I253, I254, I255, I256 + I0, + I1, + I2, + I3, + I4, + I5, + I6, + I7, + I8, + I9, + I10, + I11, + I12, + I13, + I14, + I15, + I16, + I17, + I18, + I19, + I20, + I21, + I22, + I23, + I24, + I25, + I26, + I27, + I28, + I29, + I30, + I31, + I32, + I33, + I34, + I35, + I36, + I37, + I38, + I39, + I40, + I41, + I42, + I43, + I44, + I45, + I46, + I47, + I48, + I49, + I50, + I51, + I52, + I53, + I54, + I55, + I56, + I57, + I58, + I59, + I60, + I61, + I62, + I63, + I64, + I65, + I66, + I67, + I68, + I69, + I70, + I71, + I72, + I73, + I74, + I75, + I76, + I77, + I78, + I79, + I80, + I81, + I82, + I83, + I84, + I85, + I86, + I87, + I88, + I89, + I90, + I91, + I92, + I93, + I94, + I95, + I96, + I97, + I98, + I99, + I100, + I101, + I102, + I103, + I104, + I105, + I106, + I107, + I108, + I109, + I110, + I111, + I112, + I113, + I114, + I115, + I116, + I117, + I118, + I119, + I120, + I121, + I122, + I123, + I124, + I125, + I126, + I127, + I128, + I129, + I130, + I131, + I132, + I133, + I134, + I135, + I136, + I137, + I138, + I139, + I140, + I141, + I142, + I143, + I144, + I145, + I146, + I147, + I148, + I149, + I150, + I151, + I152, + I153, + I154, + I155, + I156, + I157, + I158, + I159, + I160, + I161, + I162, + I163, + I164, + I165, + I166, + I167, + I168, + I169, + I170, + I171, + I172, + I173, + I174, + I175, + I176, + I177, + I178, + I179, + I180, + I181, + I182, + I183, + I184, + I185, + I186, + I187, + I188, + I189, + I190, + I191, + I192, + I193, + I194, + I195, + I196, + I197, + I198, + I199, + I200, + I201, + I202, + I203, + I204, + I205, + I206, + I207, + I208, + I209, + I210, + I211, + I212, + I213, + I214, + I215, + I216, + I217, + I218, + I219, + I220, + I221, + I222, + I223, + I224, + I225, + I226, + I227, + I228, + I229, + I230, + I231, + I232, + I233, + I234, + I235, + I236, + I237, + I238, + I239, + I240, + I241, + I242, + I243, + I244, + I245, + I246, + I247, + I248, + I249, + I250, + I251, + I252, + I253, + I254, + I255, + I256, }; test "enum sizes" { @@ -189,11 +678,11 @@ test "enum sizes" { } } -const Small2 = enum (u2) { +const Small2 = enum(u2) { One, Two, }; -const Small = enum (u2) { +const Small = enum(u2) { One, Two, Three, @@ -213,8 +702,7 @@ test "set enum tag type" { } } - -const A = enum (u3) { +const A = enum(u3) { One, Two, Three, @@ -225,7 +713,7 @@ const A = enum (u3) { Four2, }; -const B = enum (u3) { +const B = enum(u3) { One3, Two3, Three3, @@ -236,7 +724,7 @@ const B = enum (u3) { Four23, }; -const C = enum (u2) { +const C = enum(u2) { One4, Two4, Three4, @@ -389,6 +877,8 @@ test "enum with tag values don't require parens" { } test "enum with 1 field but explicit tag type should still have the tag type" { - const Enum = enum(u8) { B = 2 }; + const Enum = enum(u8) { + B = 2, + }; comptime @import("std").debug.assert(@sizeOf(Enum) == @sizeOf(u8)); } diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index 0c2ae1c383..9e3e031f92 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -7,7 +7,7 @@ const ET = union(enum) { UINT: u32, pub fn print(a: &const ET, buf: []u8) error!usize { - return switch (*a) { + return switch (a.*) { ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), }; @@ -15,8 +15,12 @@ const ET = union(enum) { }; test "enum with members" { - const a = ET { .SINT = -42 }; - const b = ET { .UINT = 42 }; + const a = ET { + .SINT = -42, + }; + const b = ET { + .UINT = 42, + }; var buf: [20]u8 = undefined; assert((a.print(buf[0..]) catch unreachable) == 3); diff --git a/test/cases/error.zig b/test/cases/error.zig index 2a1433df5b..70d96e4d01 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -30,14 +30,12 @@ test "@errorName" { assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); } - test "error values" { const a = i32(error.err1); const b = i32(error.err2); assert(a != b); } - test "redefinition of error values allowed" { shouldBeNotEqual(error.AnError, error.SecondError); } @@ -45,7 +43,6 @@ fn shouldBeNotEqual(a: error, b: error) void { if (a == b) unreachable; } - test "error binary operator" { const a = errBinaryOperatorG(true) catch 3; const b = errBinaryOperatorG(false) catch 3; @@ -56,20 +53,20 @@ fn errBinaryOperatorG(x: bool) error!isize { return if (x) error.ItBroke else isize(10); } - test "unwrap simple value from error" { const i = unwrapSimpleValueFromErrorDo() catch unreachable; assert(i == 13); } -fn unwrapSimpleValueFromErrorDo() error!isize { return 13; } - +fn unwrapSimpleValueFromErrorDo() error!isize { + return 13; +} test "error return in assignment" { doErrReturnInAssignment() catch unreachable; } fn doErrReturnInAssignment() error!void { - var x : i32 = undefined; + var x: i32 = undefined; x = try makeANonErr(); } @@ -95,7 +92,10 @@ test "error set type " { comptime testErrorSetType(); } -const MyErrSet = error {OutOfMemory, FileNotFound}; +const MyErrSet = error { + OutOfMemory, + FileNotFound, +}; fn testErrorSetType() void { assert(@memberCount(MyErrSet) == 2); @@ -109,14 +109,19 @@ fn testErrorSetType() void { } } - test "explicit error set cast" { testExplicitErrorSetCast(Set1.A); comptime testExplicitErrorSetCast(Set1.A); } -const Set1 = error{A, B}; -const Set2 = error{A, C}; +const Set1 = error { + A, + B, +}; +const Set2 = error { + A, + C, +}; fn testExplicitErrorSetCast(set1: Set1) void { var x = Set2(set1); @@ -129,7 +134,8 @@ test "comptime test error for empty error set" { comptime testComptimeTestErrorEmptySet(1234); } -const EmptyErrorSet = error {}; +const EmptyErrorSet = error { +}; fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { if (x) |v| assert(v == 1234) else |err| @compileError("bad"); @@ -145,7 +151,9 @@ test "comptime err to int of error set with only 1 possible value" { testErrToIntWithOnePossibleValue(error.A, u32(error.A)); comptime testErrToIntWithOnePossibleValue(error.A, u32(error.A)); } -fn testErrToIntWithOnePossibleValue(x: error{A}, comptime value: u32) void { +fn testErrToIntWithOnePossibleValue(x: error { + A, +}, comptime value: u32) void { if (u32(x) != value) { @compileError("bad"); } @@ -176,7 +184,6 @@ fn quux_1() !i32 { return error.C; } - test "error: fn returning empty error set can be passed as fn returning any error" { entry(); comptime entry(); @@ -186,24 +193,24 @@ fn entry() void { foo2(bar2); } -fn foo2(f: fn()error!void) void { +fn foo2(f: fn() error!void) void { const x = f(); } -fn bar2() (error{}!void) { } - +fn bar2() (error { +}!void) {} test "error: Zero sized error set returned with value payload crash" { _ = foo3(0); _ = comptime foo3(0); } -const Error = error{}; +const Error = error { +}; fn foo3(b: usize) Error!usize { return b; } - test "error: Infer error set from literals" { _ = nullLiteral("n") catch |err| handleErrors(err); _ = floatLiteral("n") catch |err| handleErrors(err); @@ -215,29 +222,26 @@ test "error: Infer error set from literals" { fn handleErrors(err: var) noreturn { switch (err) { - error.T => {} + error.T => {}, } unreachable; } fn nullLiteral(str: []const u8) !?i64 { - if (str[0] == 'n') - return null; + if (str[0] == 'n') return null; return error.T; } fn floatLiteral(str: []const u8) !?f64 { - if (str[0] == 'n') - return 1.0; + if (str[0] == 'n') return 1.0; return error.T; } fn intLiteral(str: []const u8) !?i64 { - if (str[0] == 'n') - return 1; + if (str[0] == 'n') return 1; return error.T; } diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 364db5e152..2571686b0b 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -11,8 +11,6 @@ fn fibonacci(x: i32) i32 { return fibonacci(x - 1) + fibonacci(x - 2); } - - fn unwrapAndAddOne(blah: ?i32) i32 { return ??blah + 1; } @@ -40,13 +38,13 @@ test "inline variable gets result of const if" { assert(gimme1or2(false) == 2); } - test "static function evaluation" { assert(statically_added_number == 3); } const statically_added_number = staticAdd(1, 2); -fn staticAdd(a: i32, b: i32) i32 { return a + b; } - +fn staticAdd(a: i32, b: i32) i32 { + return a + b; +} test "const expr eval on single expr blocks" { assert(constExprEvalOnSingleExprBlocksFn(1, true) == 3); @@ -64,9 +62,6 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { return result; } - - - test "statically initialized list" { assert(static_point_list[0].x == 1); assert(static_point_list[0].y == 2); @@ -77,7 +72,10 @@ const Point = struct { x: i32, y: i32, }; -const static_point_list = []Point { makePoint(1, 2), makePoint(3, 4) }; +const static_point_list = []Point { + makePoint(1, 2), + makePoint(3, 4), +}; fn makePoint(x: i32, y: i32) Point { return Point { .x = x, @@ -85,7 +83,6 @@ fn makePoint(x: i32, y: i32) Point { }; } - test "static eval list init" { assert(static_vec3.data[2] == 1.0); assert(vec3(0.0, 0.0, 3.0).data[2] == 3.0); @@ -96,17 +93,19 @@ pub const Vec3 = struct { }; pub fn vec3(x: f32, y: f32, z: f32) Vec3 { return Vec3 { - .data = []f32 { x, y, z, }, + .data = []f32 { + x, + y, + z, + }, }; } - test "constant expressions" { - var array : [array_size]u8 = undefined; + var array: [array_size]u8 = undefined; assert(@sizeOf(@typeOf(array)) == 20); } -const array_size : u8 = 20; - +const array_size: u8 = 20; test "constant struct with negation" { assert(vertices[0].x == -0.6); @@ -119,12 +118,29 @@ const Vertex = struct { b: f32, }; const vertices = []Vertex { - Vertex { .x = -0.6, .y = -0.4, .r = 1.0, .g = 0.0, .b = 0.0 }, - Vertex { .x = 0.6, .y = -0.4, .r = 0.0, .g = 1.0, .b = 0.0 }, - Vertex { .x = 0.0, .y = 0.6, .r = 0.0, .g = 0.0, .b = 1.0 }, + Vertex { + .x = -0.6, + .y = -0.4, + .r = 1.0, + .g = 0.0, + .b = 0.0, + }, + Vertex { + .x = 0.6, + .y = -0.4, + .r = 0.0, + .g = 1.0, + .b = 0.0, + }, + Vertex { + .x = 0.0, + .y = 0.6, + .r = 0.0, + .g = 0.0, + .b = 1.0, + }, }; - test "statically initialized struct" { st_init_str_foo.x += 1; assert(st_init_str_foo.x == 14); @@ -133,15 +149,21 @@ const StInitStrFoo = struct { x: i32, y: bool, }; -var st_init_str_foo = StInitStrFoo { .x = 13, .y = true, }; - +var st_init_str_foo = StInitStrFoo { + .x = 13, + .y = true, +}; test "statically initalized array literal" { - const y : [4]u8 = st_init_arr_lit_x; + const y: [4]u8 = st_init_arr_lit_x; assert(y[3] == 4); } -const st_init_arr_lit_x = []u8{1,2,3,4}; - +const st_init_arr_lit_x = []u8 { + 1, + 2, + 3, + 4, +}; test "const slice" { comptime { @@ -198,14 +220,29 @@ const CmdFn = struct { func: fn(i32) i32, }; -const cmd_fns = []CmdFn{ - CmdFn {.name = "one", .func = one}, - CmdFn {.name = "two", .func = two}, - CmdFn {.name = "three", .func = three}, +const cmd_fns = []CmdFn { + CmdFn { + .name = "one", + .func = one, + }, + CmdFn { + .name = "two", + .func = two, + }, + CmdFn { + .name = "three", + .func = three, + }, }; -fn one(value: i32) i32 { return value + 1; } -fn two(value: i32) i32 { return value + 2; } -fn three(value: i32) i32 { return value + 3; } +fn one(value: i32) i32 { + return value + 1; +} +fn two(value: i32) i32 { + return value + 2; +} +fn three(value: i32) i32 { + return value + 3; +} fn performFn(comptime prefix_char: u8, start_value: i32) i32 { var result: i32 = start_value; @@ -229,7 +266,7 @@ test "eval @setRuntimeSafety at compile-time" { assert(result == 1234); } -fn fnWithSetRuntimeSafety() i32{ +fn fnWithSetRuntimeSafety() i32 { @setRuntimeSafety(true); return 1234; } @@ -244,7 +281,6 @@ fn fnWithFloatMode() f32 { return 1234.0; } - const SimpleStruct = struct { field: i32, @@ -253,7 +289,9 @@ const SimpleStruct = struct { } }; -var simple_struct = SimpleStruct{ .field = 1234, }; +var simple_struct = SimpleStruct { + .field = 1234, +}; const bound_fn = simple_struct.method; @@ -261,8 +299,6 @@ test "call method on bound fn referring to var instance" { assert(bound_fn() == 1237); } - - test "ptr to local array argument at comptime" { comptime { var bytes: [10]u8 = undefined; @@ -277,7 +313,6 @@ fn modifySomeBytes(bytes: []u8) void { bytes[9] = 'b'; } - test "comparisons 0 <= uint and 0 > uint should be comptime" { testCompTimeUIntComparisons(1234); } @@ -296,8 +331,6 @@ fn testCompTimeUIntComparisons(x: u32) void { } } - - test "const ptr to variable data changes at runtime" { assert(foo_ref.name[0] == 'a'); foo_ref.name = "b"; @@ -308,11 +341,11 @@ const Foo = struct { name: []const u8, }; -var foo_contents = Foo { .name = "a", }; +var foo_contents = Foo { + .name = "a", +}; const foo_ref = &foo_contents; - - test "create global array with for loop" { assert(global_array[5] == 5 * 5); assert(global_array[9] == 9 * 9); @@ -321,7 +354,7 @@ test "create global array with for loop" { const global_array = x: { var result: [10]usize = undefined; for (result) |*item, index| { - *item = index * index; + item.* = index * index; } break :x result; }; @@ -379,7 +412,7 @@ test "f128 at compile time is lossy" { pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { return struct { - pub const Node = struct { }; + pub const Node = struct {}; }; } @@ -401,10 +434,10 @@ fn copyWithPartialInline(s: []u32, b: []u8) void { comptime var i: usize = 0; inline while (i < 4) : (i += 1) { s[i] = 0; - s[i] |= u32(b[i*4+0]) << 24; - s[i] |= u32(b[i*4+1]) << 16; - s[i] |= u32(b[i*4+2]) << 8; - s[i] |= u32(b[i*4+3]) << 0; + s[i] |= u32(b[i * 4 + 0]) << 24; + s[i] |= u32(b[i * 4 + 1]) << 16; + s[i] |= u32(b[i * 4 + 2]) << 8; + s[i] |= u32(b[i * 4 + 3]) << 0; } } @@ -413,7 +446,7 @@ test "binary math operator in partially inlined function" { var b: [16]u8 = undefined; for (b) |*r, i| - *r = u8(i + 1); + r.* = u8(i + 1); copyWithPartialInline(s[0..], b[0..]); assert(s[0] == 0x1020304); @@ -422,7 +455,6 @@ test "binary math operator in partially inlined function" { assert(s[3] == 0xd0e0f10); } - test "comptime function with the same args is memoized" { comptime { assert(MakeType(i32) == MakeType(i32)); @@ -447,12 +479,12 @@ test "comptime function with mutable pointer is not memoized" { } fn increment(value: &i32) void { - *value += 1; + value.* += 1; } fn generateTable(comptime T: type) [1010]T { - var res : [1010]T = undefined; - var i : usize = 0; + var res: [1010]T = undefined; + var i: usize = 0; while (i < 1010) : (i += 1) { res[i] = T(i); } @@ -496,9 +528,10 @@ const SingleFieldStruct = struct { } }; test "const ptr to comptime mutable data is not memoized" { - comptime { - var foo = SingleFieldStruct {.x = 1}; + var foo = SingleFieldStruct { + .x = 1, + }; assert(foo.read_x() == 1); foo.x = 2; assert(foo.read_x() == 2); diff --git a/test/cases/fn.zig b/test/cases/fn.zig index 5388deac10..6d47dafad4 100644 --- a/test/cases/fn.zig +++ b/test/cases/fn.zig @@ -7,7 +7,6 @@ fn testParamsAdd(a: i32, b: i32) i32 { return a + b; } - test "local variables" { testLocVars(2); } @@ -16,7 +15,6 @@ fn testLocVars(b: i32) void { if (a + b != 3) unreachable; } - test "void parameters" { voidFun(1, void{}, 2, {}); } @@ -27,9 +25,8 @@ fn voidFun(a: i32, b: void, c: i32, d: void) void { return vv; } - test "mutable local variables" { - var zero : i32 = 0; + var zero: i32 = 0; assert(zero == 0); var i = i32(0); @@ -41,7 +38,7 @@ test "mutable local variables" { test "separate block scopes" { { - const no_conflict : i32 = 5; + const no_conflict: i32 = 5; assert(no_conflict == 5); } @@ -56,8 +53,7 @@ test "call function with empty string" { acceptsString(""); } -fn acceptsString(foo: []u8) void { } - +fn acceptsString(foo: []u8) void {} fn @"weird function name"() i32 { return 1234; @@ -70,31 +66,43 @@ test "implicit cast function unreachable return" { wantsFnWithVoid(fnWithUnreachable); } -fn wantsFnWithVoid(f: fn() void) void { } +fn wantsFnWithVoid(f: fn() void) void {} fn fnWithUnreachable() noreturn { unreachable; } - test "function pointers" { - const fns = []@typeOf(fn1) { fn1, fn2, fn3, fn4, }; + const fns = []@typeOf(fn1) { + fn1, + fn2, + fn3, + fn4, + }; for (fns) |f, i| { assert(f() == u32(i) + 5); } } -fn fn1() u32 {return 5;} -fn fn2() u32 {return 6;} -fn fn3() u32 {return 7;} -fn fn4() u32 {return 8;} - +fn fn1() u32 { + return 5; +} +fn fn2() u32 { + return 6; +} +fn fn3() u32 { + return 7; +} +fn fn4() u32 { + return 8; +} test "inline function call" { assert(@inlineCall(add, 3, 9) == 12); } -fn add(a: i32, b: i32) i32 { return a + b; } - +fn add(a: i32, b: i32) i32 { + return a + b; +} test "number literal as an argument" { numberLiteralArg(3); @@ -110,4 +118,4 @@ test "assign inline fn to const variable" { a(); } -inline fn inlineFn() void { } +inline fn inlineFn() void {} diff --git a/test/cases/for.zig b/test/cases/for.zig index 7bb0d7c9fa..f13e6ec6e5 100644 --- a/test/cases/for.zig +++ b/test/cases/for.zig @@ -3,8 +3,14 @@ const assert = std.debug.assert; const mem = std.mem; test "continue in for loop" { - const array = []i32 {1, 2, 3, 4, 5}; - var sum : i32 = 0; + const array = []i32 { + 1, + 2, + 3, + 4, + 5, + }; + var sum: i32 = 0; for (array) |x| { sum += x; if (x < 3) { @@ -24,17 +30,39 @@ test "for loop with pointer elem var" { } fn mangleString(s: []u8) void { for (s) |*c| { - *c += 1; + c.* += 1; } } test "basic for loop" { - const expected_result = []u8{9, 8, 7, 6, 0, 1, 2, 3, 9, 8, 7, 6, 0, 1, 2, 3 }; + const expected_result = []u8 { + 9, + 8, + 7, + 6, + 0, + 1, + 2, + 3, + 9, + 8, + 7, + 6, + 0, + 1, + 2, + 3, + }; var buffer: [expected_result.len]u8 = undefined; var buf_index: usize = 0; - const array = []u8 {9, 8, 7, 6}; + const array = []u8 { + 9, + 8, + 7, + 6, + }; for (array) |item| { buffer[buf_index] = item; buf_index += 1; @@ -65,7 +93,8 @@ fn testBreakOuter() void { var array = "aoeu"; var count: usize = 0; outer: for (array) |_| { - for (array) |_2| { // TODO shouldn't get error for redeclaring "_" + // TODO shouldn't get error for redeclaring "_" + for (array) |_2| { count += 1; break :outer; } @@ -82,7 +111,8 @@ fn testContinueOuter() void { var array = "aoeu"; var counter: usize = 0; outer: for (array) |_| { - for (array) |_2| { // TODO shouldn't get error for redeclaring "_" + // TODO shouldn't get error for redeclaring "_" + for (array) |_2| { counter += 1; continue :outer; } diff --git a/test/cases/generics.zig b/test/cases/generics.zig index 19b4a598d8..da8a7dcad6 100644 --- a/test/cases/generics.zig +++ b/test/cases/generics.zig @@ -37,7 +37,6 @@ test "fn with comptime args" { assert(sameButWithFloats(0.43, 0.49) == 0.49); } - test "var params" { assert(max_i32(12, 34) == 34); assert(max_f64(1.2, 3.4) == 3.4); @@ -60,7 +59,6 @@ fn max_f64(a: f64, b: f64) f64 { return max_var(a, b); } - pub fn List(comptime T: type) type { return SmallList(T, 8); } @@ -82,10 +80,15 @@ test "function with return type type" { assert(list2.prealloc_items.len == 8); } - test "generic struct" { - var a1 = GenNode(i32) {.value = 13, .next = null,}; - var b1 = GenNode(bool) {.value = true, .next = null,}; + var a1 = GenNode(i32) { + .value = 13, + .next = null, + }; + var b1 = GenNode(bool) { + .value = true, + .next = null, + }; assert(a1.value == 13); assert(a1.value == a1.getVal()); assert(b1.getVal()); @@ -94,7 +97,9 @@ fn GenNode(comptime T: type) type { return struct { value: T, next: ?&GenNode(T), - fn getVal(n: &const GenNode(T)) T { return n.value; } + fn getVal(n: &const GenNode(T)) T { + return n.value; + } }; } @@ -107,7 +112,6 @@ fn GenericDataThing(comptime count: isize) type { }; } - test "use generic param in generic param" { assert(aGenericFn(i32, 3, 4) == 7); } @@ -115,21 +119,31 @@ fn aGenericFn(comptime T: type, comptime a: T, b: T) T { return a + b; } - test "generic fn with implicit cast" { assert(getFirstByte(u8, []u8 {13}) == 13); - assert(getFirstByte(u16, []u16 {0, 13}) == 0); + assert(getFirstByte(u16, []u16 { + 0, + 13, + }) == 0); +} +fn getByte(ptr: ?&const u8) u8 { + return ??ptr.*; } -fn getByte(ptr: ?&const u8) u8 {return *??ptr;} fn getFirstByte(comptime T: type, mem: []const T) u8 { return getByte(@ptrCast(&const u8, &mem[0])); } +const foos = []fn(var) bool { + foo1, + foo2, +}; -const foos = []fn(var) bool { foo1, foo2 }; - -fn foo1(arg: var) bool { return arg; } -fn foo2(arg: var) bool { return !arg; } +fn foo1(arg: var) bool { + return arg; +} +fn foo2(arg: var) bool { + return !arg; +} test "array of generic fns" { assert(foos[0](true)); diff --git a/test/cases/if.zig b/test/cases/if.zig index 2caae7448c..808936bfa5 100644 --- a/test/cases/if.zig +++ b/test/cases/if.zig @@ -23,7 +23,6 @@ fn firstEqlThird(a: i32, b: i32, c: i32) void { } } - test "else if expression" { assert(elseIfExpressionF(1) == 1); } diff --git a/test/cases/import/a_namespace.zig b/test/cases/import/a_namespace.zig index 5cf906cf91..042f1867a5 100644 --- a/test/cases/import/a_namespace.zig +++ b/test/cases/import/a_namespace.zig @@ -1 +1,3 @@ -pub fn foo() i32 { return 1234; } +pub fn foo() i32 { + return 1234; +} diff --git a/test/cases/ir_block_deps.zig b/test/cases/ir_block_deps.zig index 202df19f62..c017eca508 100644 --- a/test/cases/ir_block_deps.zig +++ b/test/cases/ir_block_deps.zig @@ -11,7 +11,9 @@ fn foo(id: u64) !i32 { }; } -fn getErrInt() error!i32 { return 0; } +fn getErrInt() error!i32 { + return 0; +} test "ir block deps" { assert((foo(1) catch unreachable) == 0); diff --git a/test/cases/math.zig b/test/cases/math.zig index 47d001a590..dfc5946fdb 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -28,25 +28,12 @@ fn testDivision() void { assert(divTrunc(f32, -5.0, 3.0) == -1.0); comptime { - assert( - 1194735857077236777412821811143690633098347576 % - 508740759824825164163191790951174292733114988 == - 177254337427586449086438229241342047632117600); - assert(@rem(-1194735857077236777412821811143690633098347576, - 508740759824825164163191790951174292733114988) == - -177254337427586449086438229241342047632117600); - assert(1194735857077236777412821811143690633098347576 / - 508740759824825164163191790951174292733114988 == - 2); - assert(@divTrunc(-1194735857077236777412821811143690633098347576, - 508740759824825164163191790951174292733114988) == - -2); - assert(@divTrunc(1194735857077236777412821811143690633098347576, - -508740759824825164163191790951174292733114988) == - -2); - assert(@divTrunc(-1194735857077236777412821811143690633098347576, - -508740759824825164163191790951174292733114988) == - 2); + assert(1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600); + assert(@rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600); + assert(1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2); + assert(@divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2); + assert(@divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2); + assert(@divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2); assert(4126227191251978491697987544882340798050766755606969681711 % 10 == 1); } } @@ -114,18 +101,28 @@ fn ctz(x: var) usize { test "assignment operators" { var i: u32 = 0; - i += 5; assert(i == 5); - i -= 2; assert(i == 3); - i *= 20; assert(i == 60); - i /= 3; assert(i == 20); - i %= 11; assert(i == 9); - i <<= 1; assert(i == 18); - i >>= 2; assert(i == 4); + i += 5; + assert(i == 5); + i -= 2; + assert(i == 3); + i *= 20; + assert(i == 60); + i /= 3; + assert(i == 20); + i %= 11; + assert(i == 9); + i <<= 1; + assert(i == 18); + i >>= 2; + assert(i == 4); i = 6; - i &= 5; assert(i == 4); - i ^= 6; assert(i == 2); + i &= 5; + assert(i == 4); + i ^= 6; + assert(i == 2); i = 6; - i |= 3; assert(i == 7); + i |= 3; + assert(i == 7); } test "three expr in a row" { @@ -138,7 +135,7 @@ fn testThreeExprInARow(f: bool, t: bool) void { assertFalse(1 | 2 | 4 != 7); assertFalse(3 ^ 6 ^ 8 != 13); assertFalse(7 & 14 & 28 != 4); - assertFalse(9 << 1 << 2 != 9 << 3); + assertFalse(9 << 1 << 2 != 9 << 3); assertFalse(90 >> 1 >> 2 != 90 >> 3); assertFalse(100 - 1 + 1000 != 1099); assertFalse(5 * 4 / 2 % 3 != 1); @@ -150,7 +147,6 @@ fn assertFalse(b: bool) void { assert(!b); } - test "const number literal" { const one = 1; const eleven = ten + one; @@ -159,8 +155,6 @@ test "const number literal" { } const ten = 10; - - test "unsigned wrapping" { testUnsignedWrappingEval(@maxValue(u32)); comptime testUnsignedWrappingEval(@maxValue(u32)); @@ -214,8 +208,12 @@ const DivResult = struct { }; test "binary not" { - assert(comptime x: {break :x ~u16(0b1010101010101010) == 0b0101010101010101;}); - assert(comptime x: {break :x ~u64(2147483647) == 18446744071562067968;}); + assert(comptime x: { + break :x ~u16(0b1010101010101010) == 0b0101010101010101; + }); + assert(comptime x: { + break :x ~u64(2147483647) == 18446744071562067968; + }); testBinaryNot(0b1010101010101010); } @@ -319,27 +317,15 @@ fn testShrExact(x: u8) void { test "big number addition" { comptime { - assert( - 35361831660712422535336160538497375248 + - 101752735581729509668353361206450473702 == - 137114567242441932203689521744947848950); - assert( - 594491908217841670578297176641415611445982232488944558774612 + - 390603545391089362063884922208143568023166603618446395589768 == - 985095453608931032642182098849559179469148836107390954364380); + assert(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); + assert(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); } } test "big number multiplication" { comptime { - assert( - 45960427431263824329884196484953148229 * - 128339149605334697009938835852565949723 == - 5898522172026096622534201617172456926982464453350084962781392314016180490567); - assert( - 594491908217841670578297176641415611445982232488944558774612 * - 390603545391089362063884922208143568023166603618446395589768 == - 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016); + assert(45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567); + assert(594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016); } } @@ -380,7 +366,9 @@ test "f128" { comptime test_f128(); } -fn make_f128(x: f128) f128 { return x; } +fn make_f128(x: f128) f128 { + return x; +} fn test_f128() void { assert(@sizeOf(f128) == 16); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 95a9a46bff..66487a4946 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -4,6 +4,7 @@ const cstr = @import("std").cstr; const builtin = @import("builtin"); // normal comment + /// this is a documentation comment /// doc comment line 2 fn emptyFunctionWithComments() void {} @@ -16,8 +17,7 @@ comptime { @export("disabledExternFn", disabledExternFn, builtin.GlobalLinkage.Internal); } -extern fn disabledExternFn() void { -} +extern fn disabledExternFn() void {} test "call disabled extern fn" { disabledExternFn(); @@ -110,17 +110,29 @@ fn testShortCircuit(f: bool, t: bool) void { var hit_3 = f; var hit_4 = f; - if (t or x: {assert(f); break :x f;}) { + if (t or x: { + assert(f); + break :x f; + }) { hit_1 = t; } - if (f or x: { hit_2 = t; break :x f; }) { + if (f or x: { + hit_2 = t; + break :x f; + }) { assert(f); } - if (t and x: { hit_3 = t; break :x f; }) { + if (t and x: { + hit_3 = t; + break :x f; + }) { assert(f); } - if (f and x: {assert(f); break :x f;}) { + if (f and x: { + assert(f); + break :x f; + }) { assert(f); } else { hit_4 = t; @@ -146,8 +158,8 @@ test "return string from function" { assert(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); } -const g1 : i32 = 1233 + 1; -var g2 : i32 = 0; +const g1: i32 = 1233 + 1; +var g2: i32 = 0; test "global variables" { assert(g2 == 0); @@ -155,10 +167,9 @@ test "global variables" { assert(g2 == 1234); } - test "memcpy and memset intrinsics" { - var foo : [20]u8 = undefined; - var bar : [20]u8 = undefined; + var foo: [20]u8 = undefined; + var bar: [20]u8 = undefined; @memset(&foo[0], 'A', foo.len); @memcpy(&bar[0], &foo[0], bar.len); @@ -167,12 +178,14 @@ test "memcpy and memset intrinsics" { } test "builtin static eval" { - const x : i32 = comptime x: {break :x 1 + 2 + 3;}; + const x: i32 = comptime x: { + break :x 1 + 2 + 3; + }; assert(x == comptime 6); } test "slicing" { - var array : [20]i32 = undefined; + var array: [20]i32 = undefined; array[5] = 1234; @@ -187,15 +200,15 @@ test "slicing" { if (slice_rest.len != 10) unreachable; } - test "constant equal function pointers" { const alias = emptyFn; - assert(comptime x: {break :x emptyFn == alias;}); + assert(comptime x: { + break :x emptyFn == alias; + }); } fn emptyFn() void {} - test "hex escape" { assert(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); } @@ -219,7 +232,7 @@ test "string escapes" { } test "multiline string" { - const s1 = + const s1 = \\one \\two) \\three @@ -229,7 +242,7 @@ test "multiline string" { } test "multiline C string" { - const s1 = + const s1 = c\\one c\\two) c\\three @@ -238,18 +251,16 @@ test "multiline C string" { assert(cstr.cmp(s1, s2) == 0); } - test "type equality" { assert(&const u8 != &u8); } - const global_a: i32 = 1234; const global_b: &const i32 = &global_a; const global_c: &const f32 = @ptrCast(&const f32, global_b); test "compile time global reinterpret" { const d = @ptrCast(&const i32, global_c); - assert(*d == 1234); + assert(d.* == 1234); } test "explicit cast maybe pointers" { @@ -261,12 +272,11 @@ test "generic malloc free" { const a = memAlloc(u8, 10) catch unreachable; memFree(u8, a); } -var some_mem : [100]u8 = undefined; +var some_mem: [100]u8 = undefined; fn memAlloc(comptime T: type, n: usize) error![]T { return @ptrCast(&T, &some_mem[0])[0..n]; } -fn memFree(comptime T: type, memory: []T) void { } - +fn memFree(comptime T: type, memory: []T) void {} test "cast undefined" { const array: [100]u8 = undefined; @@ -275,32 +285,35 @@ test "cast undefined" { } fn testCastUndefined(x: []const u8) void {} - test "cast small unsigned to larger signed" { assert(castSmallUnsignedToLargerSigned1(200) == i16(200)); assert(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); } -fn castSmallUnsignedToLargerSigned1(x: u8) i16 { return x; } -fn castSmallUnsignedToLargerSigned2(x: u16) i64 { return x; } - +fn castSmallUnsignedToLargerSigned1(x: u8) i16 { + return x; +} +fn castSmallUnsignedToLargerSigned2(x: u16) i64 { + return x; +} test "implicit cast after unreachable" { assert(outer() == 1234); } -fn inner() i32 { return 1234; } +fn inner() i32 { + return 1234; +} fn outer() i64 { return inner(); } - test "pointer dereferencing" { var x = i32(3); const y = &x; - *y += 1; + y.* += 1; assert(x == 4); - assert(*y == 4); + assert(y.* == 4); } test "call result of if else expression" { @@ -310,9 +323,12 @@ test "call result of if else expression" { fn f2(x: bool) []const u8 { return (if (x) fA else fB)(); } -fn fA() []const u8 { return "a"; } -fn fB() []const u8 { return "b"; } - +fn fA() []const u8 { + return "a"; +} +fn fB() []const u8 { + return "b"; +} test "const expression eval handling of variables" { var x = true; @@ -321,8 +337,6 @@ test "const expression eval handling of variables" { } } - - test "constant enum initialization with differing sizes" { test3_1(test3_foo); test3_2(test3_bar); @@ -336,10 +350,17 @@ const Test3Point = struct { x: i32, y: i32, }; -const test3_foo = Test3Foo { .Three = Test3Point {.x = 3, .y = 4}}; -const test3_bar = Test3Foo { .Two = 13}; +const test3_foo = Test3Foo { + .Three = Test3Point { + .x = 3, + .y = 4, + }, +}; +const test3_bar = Test3Foo { + .Two = 13, +}; fn test3_1(f: &const Test3Foo) void { - switch (*f) { + switch (f.*) { Test3Foo.Three => |pt| { assert(pt.x == 3); assert(pt.y == 4); @@ -348,7 +369,7 @@ fn test3_1(f: &const Test3Foo) void { } } fn test3_2(f: &const Test3Foo) void { - switch (*f) { + switch (f.*) { Test3Foo.Two => |x| { assert(x == 13); }, @@ -356,23 +377,19 @@ fn test3_2(f: &const Test3Foo) void { } } - test "character literals" { assert('\'' == single_quote); } const single_quote = '\''; - - test "take address of parameter" { testTakeAddressOfParameter(12.34); } fn testTakeAddressOfParameter(f: f32) void { const f_ptr = &f; - assert(*f_ptr == 12.34); + assert(f_ptr.* == 12.34); } - test "pointer comparison" { const a = ([]const u8)("a"); const b = &a; @@ -382,23 +399,30 @@ fn ptrEql(a: &const []const u8, b: &const []const u8) bool { return a == b; } - test "C string concatenation" { const a = c"OK" ++ c" IT " ++ c"WORKED"; const b = c"OK IT WORKED"; const len = cstr.len(b); const len_with_null = len + 1; - {var i: u32 = 0; while (i < len_with_null) : (i += 1) { - assert(a[i] == b[i]); - }} + { + var i: u32 = 0; + while (i < len_with_null) : (i += 1) { + assert(a[i] == b[i]); + } + } assert(a[len] == 0); assert(b[len] == 0); } test "cast slice to u8 slice" { assert(@sizeOf(i32) == 4); - var big_thing_array = []i32{1, 2, 3, 4}; + var big_thing_array = []i32 { + 1, + 2, + 3, + 4, + }; const big_thing_slice: []i32 = big_thing_array[0..]; const bytes = ([]u8)(big_thing_slice); assert(bytes.len == 4 * 4); @@ -421,25 +445,22 @@ test "pointer to void return type" { } fn testPointerToVoidReturnType() error!void { const a = testPointerToVoidReturnType2(); - return *a; + return a.*; } const test_pointer_to_void_return_type_x = void{}; fn testPointerToVoidReturnType2() &const void { return &test_pointer_to_void_return_type_x; } - test "non const ptr to aliased type" { const int = i32; assert(?&int == ?&i32); } - - test "array 2D const double ptr" { const rect_2d_vertexes = [][1]f32 { - []f32{1.0}, - []f32{2.0}, + []f32 {1.0}, + []f32 {2.0}, }; testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]); } @@ -450,10 +471,21 @@ fn testArray2DConstDoublePtr(ptr: &const f32) void { } const Tid = builtin.TypeId; -const AStruct = struct { x: i32, }; -const AnEnum = enum { One, Two, }; -const AUnionEnum = union(enum) { One: i32, Two: void, }; -const AUnion = union { One: void, Two: void }; +const AStruct = struct { + x: i32, +}; +const AnEnum = enum { + One, + Two, +}; +const AUnionEnum = union(enum) { + One: i32, + Two: void, +}; +const AUnion = union { + One: void, + Two: void, +}; test "@typeId" { comptime { @@ -481,9 +513,11 @@ test "@typeId" { assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); assert(@typeId(AUnionEnum) == Tid.Union); assert(@typeId(AUnion) == Tid.Union); - assert(@typeId(fn()void) == Tid.Fn); + assert(@typeId(fn() void) == Tid.Fn); assert(@typeId(@typeOf(builtin)) == Tid.Namespace); - assert(@typeId(@typeOf(x: {break :x this;})) == Tid.Block); + assert(@typeId(@typeOf(x: { + break :x this; + })) == Tid.Block); // TODO bound fn // TODO arg tuple // TODO opaque @@ -499,8 +533,7 @@ test "@canImplicitCast" { } test "@typeName" { - const Struct = struct { - }; + const Struct = struct {}; const Union = union { unused: u8, }; @@ -525,14 +558,19 @@ fn TypeFromFn(comptime T: type) type { test "volatile load and store" { var number: i32 = 1234; const ptr = (&volatile i32)(&number); - *ptr += 1; - assert(*ptr == 1235); + ptr.* += 1; + assert(ptr.* == 1235); } test "slice string literal has type []const u8" { comptime { assert(@typeOf("aoeu"[0..]) == []const u8); - const array = []i32{1, 2, 3, 4}; + const array = []i32 { + 1, + 2, + 3, + 4, + }; assert(@typeOf(array[0..]) == []const i32); } } @@ -544,12 +582,15 @@ const GDTEntry = struct { field: i32, }; var gdt = []GDTEntry { - GDTEntry {.field = 1}, - GDTEntry {.field = 2}, + GDTEntry { + .field = 1, + }, + GDTEntry { + .field = 2, + }, }; var global_ptr = &gdt[0]; - // can't really run this test but we can make sure it has no compile error // and generates code const vram = @intToPtr(&volatile u8, 0x20000000)[0..0x8000]; @@ -584,7 +625,7 @@ test "comptime if inside runtime while which unconditionally breaks" { } fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void { while (cond) { - if (false) { } + if (false) {} break; } } @@ -607,7 +648,9 @@ fn testStructInFn() void { kind: BlockKind, }; - var block = Block { .kind = 1234 }; + var block = Block { + .kind = 1234, + }; block.kind += 1; @@ -617,7 +660,9 @@ fn testStructInFn() void { fn fnThatClosesOverLocalConst() type { const c = 1; return struct { - fn g() i32 { return c; } + fn g() i32 { + return c; + } }; } @@ -635,22 +680,29 @@ fn thisIsAColdFn() void { @setCold(true); } - -const PackedStruct = packed struct { a: u8, b: u8, }; -const PackedUnion = packed union { a: u8, b: u32, }; -const PackedEnum = packed enum { A, B, }; +const PackedStruct = packed struct { + a: u8, + b: u8, +}; +const PackedUnion = packed union { + a: u8, + b: u32, +}; +const PackedEnum = packed enum { + A, + B, +}; test "packed struct, enum, union parameters in extern function" { - testPackedStuff( - PackedStruct{.a = 1, .b = 2}, - PackedUnion{.a = 1}, - PackedEnum.A, - ); -} - -export fn testPackedStuff(a: &const PackedStruct, b: &const PackedUnion, c: PackedEnum) void { + testPackedStuff(PackedStruct { + .a = 1, + .b = 2, + }, PackedUnion { + .a = 1, + }, PackedEnum.A); } +export fn testPackedStuff(a: &const PackedStruct, b: &const PackedUnion, c: PackedEnum) void {} test "slicing zero length array" { const s1 = ""[0..]; @@ -661,7 +713,6 @@ test "slicing zero length array" { assert(mem.eql(u32, s2, []u32{})); } - const addr1 = @ptrCast(&const u8, emptyFn); test "comptime cast fn to ptr" { const addr2 = @ptrCast(&const u8, emptyFn); diff --git a/test/cases/namespace_depends_on_compile_var/index.zig b/test/cases/namespace_depends_on_compile_var/index.zig index 95209dcef3..ccc49d9367 100644 --- a/test/cases/namespace_depends_on_compile_var/index.zig +++ b/test/cases/namespace_depends_on_compile_var/index.zig @@ -8,7 +8,7 @@ test "namespace depends on compile var" { assert(!some_namespace.a_bool); } } -const some_namespace = switch(builtin.os) { +const some_namespace = switch (builtin.os) { builtin.Os.linux => @import("a.zig"), else => @import("b.zig"), }; diff --git a/test/cases/null.zig b/test/cases/null.zig index 35d72b729c..96a62ab1ed 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -1,7 +1,7 @@ const assert = @import("std").debug.assert; test "nullable type" { - const x : ?bool = true; + const x: ?bool = true; if (x) |y| { if (y) { @@ -13,13 +13,13 @@ test "nullable type" { unreachable; } - const next_x : ?i32 = null; + const next_x: ?i32 = null; const z = next_x ?? 1234; assert(z == 1234); - const final_x : ?i32 = 13; + const final_x: ?i32 = 13; const num = final_x ?? unreachable; @@ -30,19 +30,17 @@ test "test maybe object and get a pointer to the inner value" { var maybe_bool: ?bool = true; if (maybe_bool) |*b| { - *b = false; + b.* = false; } assert(??maybe_bool == false); } - test "rhs maybe unwrap return" { const x: ?bool = true; const y = x ?? return; } - test "maybe return" { maybeReturnImpl(); comptime maybeReturnImpl(); @@ -50,8 +48,7 @@ test "maybe return" { fn maybeReturnImpl() void { assert(??foo(1235)); - if (foo(null) != null) - unreachable; + if (foo(null) != null) unreachable; assert(!??foo(1234)); } @@ -60,12 +57,16 @@ fn foo(x: ?i32) ?bool { return value > 1234; } - test "if var maybe pointer" { - assert(shouldBeAPlus1(Particle {.a = 14, .b = 1, .c = 1, .d = 1}) == 15); + assert(shouldBeAPlus1(Particle { + .a = 14, + .b = 1, + .c = 1, + .d = 1, + }) == 15); } fn shouldBeAPlus1(p: &const Particle) u64 { - var maybe_particle: ?Particle = *p; + var maybe_particle: ?Particle = p.*; if (maybe_particle) |*particle| { particle.a += 1; } @@ -81,7 +82,6 @@ const Particle = struct { d: u64, }; - test "null literal outside function" { const is_null = here_is_a_null_literal.context == null; assert(is_null); @@ -96,7 +96,6 @@ const here_is_a_null_literal = SillyStruct { .context = null, }; - test "test null runtime" { testTestNullRuntime(null); } @@ -123,8 +122,6 @@ fn bar(x: ?void) ?void { } } - - const StructWithNullable = struct { field: ?i32, }; diff --git a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig b/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig index 76cff3731a..3c94bb0d49 100644 --- a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig +++ b/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig @@ -23,7 +23,7 @@ fn foo(c: bool, k: Num, c2: bool, b: []const u8) void { if (c) { const output_path = b; - if (c2) { } + if (c2) {} a(output_path); } diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index 0abc46c9de..f9b64c80eb 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -23,7 +23,9 @@ test "reflection: function return type, var args, and param types" { } } -fn dummy(a: bool, b: i32, c: f32) i32 { return 1234; } +fn dummy(a: bool, b: i32, c: f32) i32 { + return 1234; +} fn dummy_varargs(args: ...) void {} test "reflection: struct member types and names" { @@ -54,7 +56,6 @@ test "reflection: enum member types and names" { assert(mem.eql(u8, @memberName(Bar, 2), "Three")); assert(mem.eql(u8, @memberName(Bar, 3), "Four")); } - } test "reflection: @field" { diff --git a/test/cases/slice.zig b/test/cases/slice.zig index ea708ba3b5..4ca194672c 100644 --- a/test/cases/slice.zig +++ b/test/cases/slice.zig @@ -18,7 +18,11 @@ test "slice child property" { } test "runtime safety lets us slice from len..len" { - var an_array = []u8{1, 2, 3}; + var an_array = []u8 { + 1, + 2, + 3, + }; assert(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); } @@ -27,7 +31,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { } test "implicitly cast array of size 0 to slice" { - var msg = []u8 {}; + var msg = []u8{}; assertLenIsZero(msg); } diff --git a/test/cases/struct.zig b/test/cases/struct.zig index c3df97678b..c474d99f2b 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -2,9 +2,11 @@ const assert = @import("std").debug.assert; const builtin = @import("builtin"); const StructWithNoFields = struct { - fn add(a: i32, b: i32) i32 { return a + b; } + fn add(a: i32, b: i32) i32 { + return a + b; + } }; -const empty_global_instance = StructWithNoFields {}; +const empty_global_instance = StructWithNoFields{}; test "call struct static method" { const result = StructWithNoFields.add(3, 4); @@ -34,12 +36,11 @@ test "void struct fields" { assert(@sizeOf(VoidStructFieldsFoo) == 4); } const VoidStructFieldsFoo = struct { - a : void, - b : i32, - c : void, + a: void, + b: i32, + c: void, }; - test "structs" { var foo: StructFoo = undefined; @memset(@ptrCast(&u8, &foo), 0, @sizeOf(StructFoo)); @@ -50,9 +51,9 @@ test "structs" { assert(foo.c == 100); } const StructFoo = struct { - a : i32, - b : bool, - c : f32, + a: i32, + b: bool, + c: f32, }; fn testFoo(foo: &const StructFoo) void { assert(foo.b); @@ -61,7 +62,6 @@ fn testMutation(foo: &StructFoo) void { foo.c = 100; } - const Node = struct { val: Val, next: &Node, @@ -72,10 +72,10 @@ const Val = struct { }; test "struct point to self" { - var root : Node = undefined; + var root: Node = undefined; root.val.x = 1; - var node : Node = undefined; + var node: Node = undefined; node.next = &root; node.val.x = 2; @@ -85,8 +85,8 @@ test "struct point to self" { } test "struct byval assign" { - var foo1 : StructFoo = undefined; - var foo2 : StructFoo = undefined; + var foo1: StructFoo = undefined; + var foo2: StructFoo = undefined; foo1.a = 1234; foo2.a = 0; @@ -96,46 +96,57 @@ test "struct byval assign" { } fn structInitializer() void { - const val = Val { .x = 42 }; + const val = Val { + .x = 42, + }; assert(val.x == 42); } - test "fn call of struct field" { - assert(callStructField(Foo {.ptr = aFunc,}) == 13); + assert(callStructField(Foo { + .ptr = aFunc, + }) == 13); } const Foo = struct { ptr: fn() i32, }; -fn aFunc() i32 { return 13; } +fn aFunc() i32 { + return 13; +} fn callStructField(foo: &const Foo) i32 { return foo.ptr(); } - test "store member function in variable" { - const instance = MemberFnTestFoo { .x = 1234, }; + const instance = MemberFnTestFoo { + .x = 1234, + }; const memberFn = MemberFnTestFoo.member; const result = memberFn(instance); assert(result == 1234); } const MemberFnTestFoo = struct { x: i32, - fn member(foo: &const MemberFnTestFoo) i32 { return foo.x; } + fn member(foo: &const MemberFnTestFoo) i32 { + return foo.x; + } }; - test "call member function directly" { - const instance = MemberFnTestFoo { .x = 1234, }; + const instance = MemberFnTestFoo { + .x = 1234, + }; const result = MemberFnTestFoo.member(instance); assert(result == 1234); } test "member functions" { - const r = MemberFnRand {.seed = 1234}; + const r = MemberFnRand { + .seed = 1234, + }; assert(r.getSeed() == 1234); } const MemberFnRand = struct { @@ -170,17 +181,16 @@ const EmptyStruct = struct { } }; - test "return empty struct from fn" { _ = testReturnEmptyStructFromFn(); } const EmptyStruct2 = struct {}; fn testReturnEmptyStructFromFn() EmptyStruct2 { - return EmptyStruct2 {}; + return EmptyStruct2{}; } test "pass slice of empty struct to fn" { - assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{ EmptyStruct2{} }) == 1); + assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2 {EmptyStruct2{}}) == 1); } fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { return slice.len; @@ -201,7 +211,6 @@ test "packed struct" { assert(four == 4); } - const BitField1 = packed struct { a: u3, b: u3, @@ -301,7 +310,7 @@ test "packed array 24bits" { assert(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); } - var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); + var bytes = []u8 {0} ** (@sizeOf(FooArray24Bits) + 1); bytes[bytes.len - 1] = 0xaa; const ptr = &([]FooArray24Bits)(bytes[0..bytes.len - 1])[0]; assert(ptr.a == 0); @@ -351,7 +360,7 @@ test "aligned array of packed struct" { assert(@sizeOf(FooArrayOfAligned) == 2 * 2); } - var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); + var bytes = []u8 {0xbb} ** @sizeOf(FooArrayOfAligned); const ptr = &([]FooArrayOfAligned)(bytes[0..bytes.len])[0]; assert(ptr.a[0].a == 0xbb); @@ -360,11 +369,15 @@ test "aligned array of packed struct" { assert(ptr.a[1].b == 0xbb); } - - test "runtime struct initialization of bitfield" { - const s1 = Nibbles { .x = x1, .y = x1 }; - const s2 = Nibbles { .x = u4(x2), .y = u4(x2) }; + const s1 = Nibbles { + .x = x1, + .y = x1, + }; + const s2 = Nibbles { + .x = u4(x2), + .y = u4(x2), + }; assert(s1.x == x1); assert(s1.y == x1); @@ -394,7 +407,7 @@ test "native bit field understands endianness" { var all: u64 = 0x7765443322221111; var bytes: [8]u8 = undefined; @memcpy(&bytes[0], @ptrCast(&u8, &all), 8); - var bitfields = *@ptrCast(&Bitfields, &bytes[0]); + var bitfields = @ptrCast(&Bitfields, &bytes[0]).*; assert(bitfields.f1 == 0x1111); assert(bitfields.f2 == 0x2222); diff --git a/test/cases/struct_contains_null_ptr_itself.zig b/test/cases/struct_contains_null_ptr_itself.zig index 5864ef4038..b6cb1a94cc 100644 --- a/test/cases/struct_contains_null_ptr_itself.zig +++ b/test/cases/struct_contains_null_ptr_itself.zig @@ -19,4 +19,3 @@ pub const Node = struct { pub const NodeLineComment = struct { base: Node, }; - diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/cases/struct_contains_slice_of_itself.zig index 45ec56c1e2..ee34c16baf 100644 --- a/test/cases/struct_contains_slice_of_itself.zig +++ b/test/cases/struct_contains_slice_of_itself.zig @@ -6,7 +6,7 @@ const Node = struct { }; test "struct contains slice of itself" { - var other_nodes = []Node{ + var other_nodes = []Node { Node { .payload = 31, .children = []Node{}, diff --git a/test/cases/switch.zig b/test/cases/switch.zig index a0ac646160..b870297f18 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -6,7 +6,10 @@ test "switch with numbers" { fn testSwitchWithNumbers(x: u32) void { const result = switch (x) { - 1, 2, 3, 4 ... 8 => false, + 1, + 2, + 3, + 4 ... 8 => false, 13 => true, else => false, }; @@ -34,8 +37,10 @@ test "implicit comptime switch" { const result = switch (x) { 3 => 10, 4 => 11, - 5, 6 => 12, - 7, 8 => 13, + 5, + 6 => 12, + 7, + 8 => 13, else => 14, }; @@ -61,7 +66,6 @@ fn nonConstSwitchOnEnum(fruit: Fruit) void { } } - test "switch statement" { nonConstSwitch(SwitchStatmentFoo.C); } @@ -81,11 +85,16 @@ const SwitchStatmentFoo = enum { D, }; - test "switch prong with variable" { - switchProngWithVarFn(SwitchProngWithVarEnum { .One = 13}); - switchProngWithVarFn(SwitchProngWithVarEnum { .Two = 13.0}); - switchProngWithVarFn(SwitchProngWithVarEnum { .Meh = {}}); + switchProngWithVarFn(SwitchProngWithVarEnum { + .One = 13, + }); + switchProngWithVarFn(SwitchProngWithVarEnum { + .Two = 13.0, + }); + switchProngWithVarFn(SwitchProngWithVarEnum { + .Meh = {}, + }); } const SwitchProngWithVarEnum = union(enum) { One: i32, @@ -93,7 +102,7 @@ const SwitchProngWithVarEnum = union(enum) { Meh: void, }; fn switchProngWithVarFn(a: &const SwitchProngWithVarEnum) void { - switch(*a) { + switch (a.*) { SwitchProngWithVarEnum.One => |x| { assert(x == 13); }, @@ -112,9 +121,11 @@ test "switch on enum using pointer capture" { } fn testSwitchEnumPtrCapture() void { - var value = SwitchProngWithVarEnum { .One = 1234 }; + var value = SwitchProngWithVarEnum { + .One = 1234, + }; switch (value) { - SwitchProngWithVarEnum.One => |*x| *x += 1, + SwitchProngWithVarEnum.One => |*x| x.* += 1, else => unreachable, } switch (value) { @@ -125,8 +136,12 @@ fn testSwitchEnumPtrCapture() void { test "switch with multiple expressions" { const x = switch (returnsFive()) { - 1, 2, 3 => 1, - 4, 5, 6 => 2, + 1, + 2, + 3 => 1, + 4, + 5, + 6 => 2, else => i32(3), }; assert(x == 2); @@ -135,14 +150,15 @@ fn returnsFive() i32 { return 5; } - const Number = union(enum) { One: u64, Two: u8, Three: f32, }; -const number = Number { .Three = 1.23 }; +const number = Number { + .Three = 1.23, +}; fn returnsFalse() bool { switch (number) { @@ -198,7 +214,8 @@ fn testSwitchHandleAllCasesRange(x: u8) u8 { return switch (x) { 0 ... 100 => u8(0), 101 ... 200 => 1, - 201, 203 => 2, + 201, + 203 => 2, 202 => 4, 204 ... 255 => 3, }; diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig index 136e8834e6..2d28d2f4c7 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/cases/switch_prong_err_enum.zig @@ -14,14 +14,18 @@ const FormValue = union(enum) { fn doThing(form_id: u64) error!FormValue { return switch (form_id) { - 17 => FormValue { .Address = try readOnce() }, + 17 => FormValue { + .Address = try readOnce(), + }, else => error.InvalidDebugInfo, }; } test "switch prong returns error enum" { switch (doThing(17) catch unreachable) { - FormValue.Address => |payload| { assert(payload == 1); }, + FormValue.Address => |payload| { + assert(payload == 1); + }, else => unreachable, } assert(read_count == 1); diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/cases/switch_prong_implicit_cast.zig index 335feeef43..3d80f3fdb2 100644 --- a/test/cases/switch_prong_implicit_cast.zig +++ b/test/cases/switch_prong_implicit_cast.zig @@ -7,8 +7,12 @@ const FormValue = union(enum) { fn foo(id: u64) !FormValue { return switch (id) { - 2 => FormValue { .Two = true }, - 1 => FormValue { .One = {} }, + 2 => FormValue { + .Two = true, + }, + 1 => FormValue { + .One = {}, + }, else => return error.Whatever, }; } diff --git a/test/cases/try.zig b/test/cases/try.zig index 4a0425e22e..483bf6a915 100644 --- a/test/cases/try.zig +++ b/test/cases/try.zig @@ -3,14 +3,12 @@ const assert = @import("std").debug.assert; test "try on error union" { tryOnErrorUnionImpl(); comptime tryOnErrorUnionImpl(); - } fn tryOnErrorUnionImpl() void { - const x = if (returnsTen()) |val| - val + 1 - else |err| switch (err) { - error.ItBroke, error.NoMem => 1, + const x = if (returnsTen()) |val| val + 1 else |err| switch (err) { + error.ItBroke, + error.NoMem => 1, error.CrappedOut => i32(2), else => unreachable, }; diff --git a/test/cases/undefined.zig b/test/cases/undefined.zig index bc81f9cf84..f1af10e532 100644 --- a/test/cases/undefined.zig +++ b/test/cases/undefined.zig @@ -63,6 +63,6 @@ test "assign undefined to struct with method" { } test "type name of undefined" { - const x = undefined; - assert(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); + const x = undefined; + assert(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); } diff --git a/test/cases/union.zig b/test/cases/union.zig index dc2a7c3414..50cf8004b9 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -10,38 +10,41 @@ const Agg = struct { val2: Value, }; -const v1 = Value { .Int = 1234 }; -const v2 = Value { .Array = []u8{3} ** 9 }; +const v1 = Value{ .Int = 1234 }; +const v2 = Value{ .Array = []u8{3} ** 9 }; -const err = (error!Agg)(Agg { +const err = (error!Agg)(Agg{ .val1 = v1, .val2 = v2, }); -const array = []Value { v1, v2, v1, v2}; - +const array = []Value{ + v1, + v2, + v1, + v2, +}; test "unions embedded in aggregate types" { switch (array[1]) { Value.Array => |arr| assert(arr[4] == 3), else => unreachable, } - switch((err catch unreachable).val1) { + switch ((err catch unreachable).val1) { Value.Int => |x| assert(x == 1234), else => unreachable, } } - const Foo = union { float: f64, int: i32, }; test "basic unions" { - var foo = Foo { .int = 1 }; + var foo = Foo{ .int = 1 }; assert(foo.int == 1); - foo = Foo {.float = 12.34}; + foo = Foo{ .float = 12.34 }; assert(foo.float == 12.34); } @@ -56,11 +59,11 @@ test "init union with runtime value" { } fn setFloat(foo: &Foo, x: f64) void { - *foo = Foo { .float = x }; + foo.* = Foo{ .float = x }; } fn setInt(foo: &Foo, x: i32) void { - *foo = Foo { .int = x }; + foo.* = Foo{ .int = x }; } const FooExtern = extern union { @@ -69,13 +72,12 @@ const FooExtern = extern union { }; test "basic extern unions" { - var foo = FooExtern { .int = 1 }; + var foo = FooExtern{ .int = 1 }; assert(foo.int == 1); foo.float = 12.34; assert(foo.float == 12.34); } - const Letter = enum { A, B, @@ -93,12 +95,12 @@ test "union with specified enum tag" { } fn doTest() void { - assert(bar(Payload {.A = 1234}) == -10); + assert(bar(Payload{ .A = 1234 }) == -10); } fn bar(value: &const Payload) i32 { - assert(Letter(*value) == Letter.A); - return switch (*value) { + assert(Letter(value.*) == Letter.A); + return switch (value.*) { Payload.A => |x| return x - 1244, Payload.B => |x| if (x == 12.34) i32(20) else 21, Payload.C => |x| if (x) i32(30) else 31, @@ -131,13 +133,13 @@ const MultipleChoice2 = union(enum(u32)) { test "union(enum(u32)) with specified and unspecified tag values" { comptime assert(@TagType(@TagType(MultipleChoice2)) == u32); - testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2 {.C = 123}); - comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2 { .C = 123} ); + testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); + comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) void { - assert(u32(@TagType(MultipleChoice2)(*x)) == 60); - assert(1123 == switch (*x) { + assert(u32(@TagType(MultipleChoice2)(x.*)) == 60); + assert(1123 == switch (x.*) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => |v| i32(1000) + v, @@ -150,10 +152,9 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) void }); } - const ExternPtrOrInt = extern union { ptr: &u8, - int: u64 + int: u64, }; test "extern union size" { comptime assert(@sizeOf(ExternPtrOrInt) == 8); @@ -161,7 +162,7 @@ test "extern union size" { const PackedPtrOrInt = packed union { ptr: &u8, - int: u64 + int: u64, }; test "extern union size" { comptime assert(@sizeOf(PackedPtrOrInt) == 8); @@ -174,8 +175,16 @@ test "union with only 1 field which is void should be zero bits" { comptime assert(@sizeOf(ZeroBits) == 0); } -const TheTag = enum {A, B, C}; -const TheUnion = union(TheTag) { A: i32, B: i32, C: i32 }; +const TheTag = enum { + A, + B, + C, +}; +const TheUnion = union(TheTag) { + A: i32, + B: i32, + C: i32, +}; test "union field access gives the enum values" { assert(TheUnion.A == TheTag.A); assert(TheUnion.B == TheTag.B); @@ -183,20 +192,28 @@ test "union field access gives the enum values" { } test "cast union to tag type of union" { - testCastUnionToTagType(TheUnion {.B = 1234}); - comptime testCastUnionToTagType(TheUnion {.B = 1234}); + testCastUnionToTagType(TheUnion{ .B = 1234 }); + comptime testCastUnionToTagType(TheUnion{ .B = 1234 }); } fn testCastUnionToTagType(x: &const TheUnion) void { - assert(TheTag(*x) == TheTag.B); + assert(TheTag(x.*) == TheTag.B); } test "cast tag type of union to union" { var x: Value2 = Letter2.B; assert(Letter2(x) == Letter2.B); } -const Letter2 = enum { A, B, C }; -const Value2 = union(Letter2) { A: i32, B, C, }; +const Letter2 = enum { + A, + B, + C, +}; +const Value2 = union(Letter2) { + A: i32, + B, + C, +}; test "implicit cast union to its tag type" { var x: Value2 = Letter2.B; @@ -217,19 +234,16 @@ const TheUnion2 = union(enum) { }; fn assertIsTheUnion2Item1(value: &const TheUnion2) void { - assert(*value == TheUnion2.Item1); + assert(value.* == TheUnion2.Item1); } - pub const PackThis = union(enum) { Invalid: bool, StringLiteral: u2, }; test "constant packed union" { - testConstPackedUnion([]PackThis { - PackThis { .StringLiteral = 1 }, - }); + testConstPackedUnion([]PackThis{PackThis{ .StringLiteral = 1 }}); } fn testConstPackedUnion(expected_tokens: []const PackThis) void { @@ -242,7 +256,7 @@ test "switch on union with only 1 field" { switch (r) { PartialInst.Compiled => { var z: PartialInstWithPayload = undefined; - z = PartialInstWithPayload { .Compiled = 1234 }; + z = PartialInstWithPayload{ .Compiled = 1234 }; switch (z) { PartialInstWithPayload.Compiled => |x| { assert(x == 1234); @@ -261,4 +275,3 @@ const PartialInst = union(enum) { const PartialInstWithPayload = union(enum) { Compiled: i32, }; - diff --git a/test/cases/var_args.zig b/test/cases/var_args.zig index cead9eb8bf..81f800568c 100644 --- a/test/cases/var_args.zig +++ b/test/cases/var_args.zig @@ -2,9 +2,12 @@ const assert = @import("std").debug.assert; fn add(args: ...) i32 { var sum = i32(0); - {comptime var i: usize = 0; inline while (i < args.len) : (i += 1) { - sum += args[i]; - }} + { + comptime var i: usize = 0; + inline while (i < args.len) : (i += 1) { + sum += args[i]; + } + } return sum; } @@ -55,18 +58,23 @@ fn extraFn(extra: u32, args: ...) usize { return args.len; } +const foos = []fn(...) bool { + foo1, + foo2, +}; -const foos = []fn(...) bool { foo1, foo2 }; - -fn foo1(args: ...) bool { return true; } -fn foo2(args: ...) bool { return false; } +fn foo1(args: ...) bool { + return true; +} +fn foo2(args: ...) bool { + return false; +} test "array of var args functions" { assert(foos[0]()); assert(!foos[1]()); } - test "pass array and slice of same array to var args should have same pointers" { const array = "hi"; const slice: []const u8 = array; @@ -79,7 +87,6 @@ fn assertSlicePtrsEql(args: ...) void { assert(s1.ptr == s2.ptr); } - test "pass zero length array to var args param" { doNothingWithFirstArg(""); } diff --git a/test/cases/while.zig b/test/cases/while.zig index 33d5a5623a..574a7b7e76 100644 --- a/test/cases/while.zig +++ b/test/cases/while.zig @@ -1,7 +1,7 @@ const assert = @import("std").debug.assert; test "while loop" { - var i : i32 = 0; + var i: i32 = 0; while (i < 4) { i += 1; } @@ -35,7 +35,7 @@ test "continue and break" { } var continue_and_break_counter: i32 = 0; fn runContinueAndBreakTest() void { - var i : i32 = 0; + var i: i32 = 0; while (true) { continue_and_break_counter += 2; i += 1; @@ -58,10 +58,13 @@ fn returnWithImplicitCastFromWhileLoopTest() error!void { test "while with continue expression" { var sum: i32 = 0; - {var i: i32 = 0; while (i < 10) : (i += 1) { - if (i == 5) continue; - sum += i; - }} + { + var i: i32 = 0; + while (i < 10) : (i += 1) { + if (i == 5) continue; + sum += i; + } + } assert(sum == 40); } @@ -117,17 +120,13 @@ test "while with error union condition" { var numbers_left: i32 = undefined; fn getNumberOrErr() error!i32 { - return if (numbers_left == 0) - error.OutOfNumbers - else x: { + return if (numbers_left == 0) error.OutOfNumbers else x: { numbers_left -= 1; break :x numbers_left; }; } fn getNumberOrNull() ?i32 { - return if (numbers_left == 0) - null - else x: { + return if (numbers_left == 0) null else x: { numbers_left -= 1; break :x numbers_left; }; @@ -136,42 +135,48 @@ fn getNumberOrNull() ?i32 { test "while on nullable with else result follow else prong" { const result = while (returnNull()) |value| { break value; - } else i32(2); + } else + i32(2); assert(result == 2); } test "while on nullable with else result follow break prong" { const result = while (returnMaybe(10)) |value| { break value; - } else i32(2); + } else + i32(2); assert(result == 10); } test "while on error union with else result follow else prong" { const result = while (returnError()) |value| { break value; - } else |err| i32(2); + } else|err| + i32(2); assert(result == 2); } test "while on error union with else result follow break prong" { const result = while (returnSuccess(10)) |value| { break value; - } else |err| i32(2); + } else|err| + i32(2); assert(result == 10); } test "while on bool with else result follow else prong" { const result = while (returnFalse()) { break i32(10); - } else i32(2); + } else + i32(2); assert(result == 2); } test "while on bool with else result follow break prong" { const result = while (returnTrue()) { break i32(10); - } else i32(2); + } else + i32(2); assert(result == 10); } @@ -202,9 +207,21 @@ fn testContinueOuter() void { } } -fn returnNull() ?i32 { return null; } -fn returnMaybe(x: i32) ?i32 { return x; } -fn returnError() error!i32 { return error.YouWantedAnError; } -fn returnSuccess(x: i32) error!i32 { return x; } -fn returnFalse() bool { return false; } -fn returnTrue() bool { return true; } +fn returnNull() ?i32 { + return null; +} +fn returnMaybe(x: i32) ?i32 { + return x; +} +fn returnError() error!i32 { + return error.YouWantedAnError; +} +fn returnSuccess(x: i32) error!i32 { + return x; +} +fn returnFalse() bool { + return false; +} +fn returnTrue() bool { + return true; +} -- cgit v1.2.3 From 3a8dc4e90ddf6b3dc2bdf640c89061c00eee7d45 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 1 May 2018 01:30:53 -0400 Subject: zig fmt: line comments in struct initializer --- std/zig/ast.zig | 4 ++-- std/zig/parser.zig | 40 +++++++++++++++++++++++++--------------- std/zig/parser_test.zig | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index a311aa1d71..d1d7fe7914 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1269,7 +1269,7 @@ pub const Node = struct { ArrayAccess: &Node, Slice: SliceRange, ArrayInitializer: ArrayList(&Node), - StructInitializer: ArrayList(&FieldInitializer), + StructInitializer: ArrayList(&Node), }; const CallInfo = struct { @@ -1311,7 +1311,7 @@ pub const Node = struct { i -= exprs.len; }, Op.StructInitializer => |fields| { - if (i < fields.len) return &fields.at(i).base; + if (i < fields.len) return fields.at(i); i -= fields.len; }, } diff --git a/std/zig/parser.zig b/std/zig/parser.zig index ed6a1a4256..fa42807907 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -238,8 +238,8 @@ pub const Parser = struct { ExprListItemOrEnd: ExprListCtx, ExprListCommaOrEnd: ExprListCtx, - FieldInitListItemOrEnd: ListSave(&ast.Node.FieldInitializer), - FieldInitListCommaOrEnd: ListSave(&ast.Node.FieldInitializer), + FieldInitListItemOrEnd: ListSave(&ast.Node), + FieldInitListCommaOrEnd: ListSave(&ast.Node), FieldListCommaOrEnd: &ast.Node.ContainerDecl, FieldInitValue: OptionalCtx, ErrorTagListItemOrEnd: ListSave(&ast.Node), @@ -1510,6 +1510,10 @@ pub const Parser = struct { } }, State.FieldInitListItemOrEnd => |list_state| { + while (try self.eatLineComment(arena)) |line_comment| { + try list_state.list.append(&line_comment.base); + } + if (self.eatToken(Token.Id.RBrace)) |rbrace| { *list_state.ptr = rbrace; continue; @@ -1524,7 +1528,7 @@ pub const Parser = struct { .name_token = undefined, .expr = undefined, }); - try list_state.list.append(node); + try list_state.list.append(&node.base); stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } }); @@ -2346,7 +2350,7 @@ pub const Parser = struct { .base = undefined, .lhs = lhs, .op = ast.Node.SuffixOp.Op { - .StructInitializer = ArrayList(&ast.Node.FieldInitializer).init(arena), + .StructInitializer = ArrayList(&ast.Node).init(arena), }, .rtoken = undefined, } @@ -2354,7 +2358,7 @@ pub const Parser = struct { stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .IfToken = Token.Id.LBrace }); try stack.append(State { - .FieldInitListItemOrEnd = ListSave(&ast.Node.FieldInitializer) { + .FieldInitListItemOrEnd = ListSave(&ast.Node) { .list = &node.op.StructInitializer, .ptr = &node.rtoken, } @@ -3452,7 +3456,6 @@ pub const Parser = struct { Expression: &ast.Node, VarDecl: &ast.Node.VarDecl, Statement: &ast.Node, - FieldInitializer: &ast.Node.FieldInitializer, PrintIndent, Indent: usize, PrintSameLineComment: ?&Token, @@ -3584,12 +3587,6 @@ pub const Parser = struct { } }, - RenderState.FieldInitializer => |field_init| { - try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token)); - try stream.print(" = "); - try stack.append(RenderState { .Expression = field_init.expr }); - }, - RenderState.VarDecl => |var_decl| { try stack.append(RenderState { .Text = ";" }); if (var_decl.init_node) |init_node| { @@ -3888,7 +3885,7 @@ pub const Parser = struct { const field_init = field_inits.at(0); try stack.append(RenderState { .Text = " }" }); - try stack.append(RenderState { .FieldInitializer = field_init }); + try stack.append(RenderState { .Expression = field_init }); try stack.append(RenderState { .Text = "{ " }); try stack.append(RenderState { .Expression = suffix_op.lhs }); continue; @@ -3896,13 +3893,26 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "}"}); try stack.append(RenderState.PrintIndent); try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Text = "\n" }); var i = field_inits.len; while (i != 0) { i -= 1; const field_init = field_inits.at(i); - try stack.append(RenderState { .Text = ",\n" }); - try stack.append(RenderState { .FieldInitializer = field_init }); + if (field_init.id != ast.Node.Id.LineComment) { + try stack.append(RenderState { .Text = "," }); + } + try stack.append(RenderState { .Expression = field_init }); try stack.append(RenderState.PrintIndent); + if (i != 0) { + try stack.append(RenderState { .Text = blk: { + const prev_node = field_inits.at(i - 1); + const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, field_init.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + break :blk "\n"; + }}); + } } try stack.append(RenderState { .Indent = indent + indent_delta }); try stack.append(RenderState { .Text = "{\n"}); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 07ae0a7965..4688939485 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,44 @@ +test "zig fmt: line comments in struct initializer" { + try testCanonical( + \\fn foo() void { + \\ return Self{ + \\ .a = b, + \\ + \\ // Initialize these two fields to buffer_size so that + \\ // in `readFn` we treat the state as being able to read + \\ .start_index = buffer_size, + \\ .end_index = buffer_size, + \\ + \\ // middle + \\ + \\ .a = b, + \\ + \\ // end + \\ }; + \\} + \\ + ); +} + +//TODO +//test "zig fmt: same-line comptime" { +// try testCanonical( +// \\test "" { +// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt +// \\} +// \\ +// ); +//} + + +//TODO +//test "zig fmt: number literals" { +// try testCanonical( +// \\pub const f64_true_min = 4.94065645841246544177e-324; +// \\ +// ); +//} + test "zig fmt: doc comments before struct field" { try testCanonical( \\pub const Allocator = struct { -- cgit v1.2.3 From ac4d55dec1e32ddef945bfa246eb78f20f31ec44 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 1 May 2018 01:53:04 -0400 Subject: behavior tests passing with new pointer deref syntax --- std/array_list.zig | 54 ++- std/fmt/index.zig | 93 ++-- std/heap.zig | 106 +++-- std/io.zig | 65 +-- std/linked_list.zig | 95 ++-- std/os/index.zig | 80 ++-- std/os/linux/index.zig | 379 ++++++++------- std/special/bootstrap.zig | 8 +- std/special/compiler_rt/fixuint.zig | 6 +- std/special/compiler_rt/fixunsdfdi.zig | 1 - std/special/compiler_rt/fixunsdfsi.zig | 1 - std/special/compiler_rt/fixunssfti.zig | 1 - std/special/compiler_rt/fixunstfti.zig | 1 - std/special/compiler_rt/index.zig | 818 +++++++++++++++++++++++++++------ std/special/compiler_rt/udivmod.zig | 43 +- std/special/compiler_rt/udivmodti4.zig | 2 +- std/special/compiler_rt/umodti3.zig | 2 +- test/cases/cast.zig | 6 +- test/cases/generics.zig | 2 +- 19 files changed, 1132 insertions(+), 631 deletions(-) diff --git a/std/array_list.zig b/std/array_list.zig index 2a44b66518..8c8426e1e5 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -8,7 +8,7 @@ pub fn ArrayList(comptime T: type) type { return AlignedArrayList(T, @alignOf(T)); } -pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ +pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return struct { const Self = this; @@ -21,7 +21,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ /// Deinitialize with `deinit` or use `toOwnedSlice`. pub fn init(allocator: &Allocator) Self { - return Self { + return Self{ .items = []align(A) T{}, .len = 0, .allocator = allocator, @@ -48,7 +48,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ /// allocated with `allocator`. /// Deinitialize with `deinit` or use `toOwnedSlice`. pub fn fromOwnedSlice(allocator: &Allocator, slice: []align(A) T) Self { - return Self { + return Self{ .items = slice, .len = slice.len, .allocator = allocator, @@ -59,7 +59,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ pub fn toOwnedSlice(self: &Self) []align(A) T { const allocator = self.allocator; const result = allocator.alignedShrink(T, A, self.items, self.len); - *self = init(allocator); + self.* = init(allocator); return result; } @@ -67,21 +67,21 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ try l.ensureCapacity(l.len + 1); l.len += 1; - mem.copy(T, l.items[n+1..l.len], l.items[n..l.len-1]); - l.items[n] = *item; + mem.copy(T, l.items[n + 1..l.len], l.items[n..l.len - 1]); + l.items[n] = item.*; } pub fn insertSlice(l: &Self, n: usize, items: []align(A) const T) !void { try l.ensureCapacity(l.len + items.len); l.len += items.len; - mem.copy(T, l.items[n+items.len..l.len], l.items[n..l.len-items.len]); - mem.copy(T, l.items[n..n+items.len], items); + mem.copy(T, l.items[n + items.len..l.len], l.items[n..l.len - items.len]); + mem.copy(T, l.items[n..n + items.len], items); } pub fn append(l: &Self, item: &const T) !void { const new_item_ptr = try l.addOne(); - *new_item_ptr = *item; + new_item_ptr.* = item.*; } pub fn appendSlice(l: &Self, items: []align(A) const T) !void { @@ -124,8 +124,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ } pub fn popOrNull(self: &Self) ?T { - if (self.len == 0) - return null; + if (self.len == 0) return null; return self.pop(); } }; @@ -135,25 +134,35 @@ test "basic ArrayList test" { var list = ArrayList(i32).init(debug.global_allocator); defer list.deinit(); - {var i: usize = 0; while (i < 10) : (i += 1) { - list.append(i32(i + 1)) catch unreachable; - }} + { + var i: usize = 0; + while (i < 10) : (i += 1) { + list.append(i32(i + 1)) catch unreachable; + } + } - {var i: usize = 0; while (i < 10) : (i += 1) { - assert(list.items[i] == i32(i + 1)); - }} + { + var i: usize = 0; + while (i < 10) : (i += 1) { + assert(list.items[i] == i32(i + 1)); + } + } assert(list.pop() == 10); assert(list.len == 9); - list.appendSlice([]const i32 { 1, 2, 3 }) catch unreachable; + list.appendSlice([]const i32{ + 1, + 2, + 3, + }) catch unreachable; assert(list.len == 12); assert(list.pop() == 3); assert(list.pop() == 2); assert(list.pop() == 1); assert(list.len == 9); - list.appendSlice([]const i32 {}) catch unreachable; + list.appendSlice([]const i32{}) catch unreachable; assert(list.len == 9); } @@ -166,12 +175,15 @@ test "insert ArrayList test" { assert(list.items[0] == 5); assert(list.items[1] == 1); - try list.insertSlice(1, []const i32 { 9, 8 }); + try list.insertSlice(1, []const i32{ + 9, + 8, + }); assert(list.items[0] == 5); assert(list.items[1] == 9); assert(list.items[2] == 8); - const items = []const i32 { 1 }; + const items = []const i32{1}; try list.insertSlice(0, items[0..0]); assert(list.items[0] == 5); } diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 43e758038f..804fd802f1 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -11,9 +11,7 @@ const max_int_digits = 65; /// Renders fmt string with args, calling output with slices of bytes. /// If `output` returns an error, the error is returned from `format` and /// `output` is not called again. -pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void, - comptime fmt: []const u8, args: ...) Errors!void -{ +pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void { const State = enum { Start, OpenBrace, @@ -221,7 +219,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), } } -pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { +pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { const T = @typeOf(value); switch (@typeId(T)) { builtin.TypeId.Int => { @@ -256,7 +254,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ }, builtin.TypeId.Pointer => { if (@typeId(T.Child) == builtin.TypeId.Array and T.Child.Child == u8) { - return output(context, (*value)[0..]); + return output(context, (value.*)[0..]); } else { return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); } @@ -270,13 +268,11 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ } } -pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { +pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { return output(context, (&c)[0..1]); } -pub fn formatBuf(buf: []const u8, width: usize, - context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void -{ +pub fn formatBuf(buf: []const u8, width: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { try output(context, buf); var leftover_padding = if (width > buf.len) (width - buf.len) else return; @@ -289,7 +285,7 @@ pub fn formatBuf(buf: []const u8, width: usize, // Print a float in scientific notation to the specified precision. Null uses full precision. // It should be the case that every full precision, printed value can be re-parsed back to the // same type unambiguously. -pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { +pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { var x = f64(value); // Errol doesn't handle these special cases. @@ -338,7 +334,7 @@ pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, var printed: usize = 0; if (float_decimal.digits.len > 1) { const num_digits = math.min(float_decimal.digits.len, precision + 1); - try output(context, float_decimal.digits[1 .. num_digits]); + try output(context, float_decimal.digits[1..num_digits]); printed += num_digits - 1; } @@ -350,12 +346,9 @@ pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, try output(context, float_decimal.digits[0..1]); try output(context, "."); if (float_decimal.digits.len > 1) { - const num_digits = if (@typeOf(value) == f32) - math.min(usize(9), float_decimal.digits.len) - else - float_decimal.digits.len; + const num_digits = if (@typeOf(value) == f32) math.min(usize(9), float_decimal.digits.len) else float_decimal.digits.len; - try output(context, float_decimal.digits[1 .. num_digits]); + try output(context, float_decimal.digits[1..num_digits]); } else { try output(context, "0"); } @@ -381,7 +374,7 @@ pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, // Print a float of the format x.yyyyy where the number of y is specified by the precision argument. // By default floats are printed at full precision (no rounding). -pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { +pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { var x = f64(value); // Errol doesn't handle these special cases. @@ -431,14 +424,14 @@ pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, com if (num_digits_whole > 0) { // We may have to zero pad, for instance 1e4 requires zero padding. - try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]); + try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); var i = num_digits_whole_no_pad; while (i < num_digits_whole) : (i += 1) { try output(context, "0"); } } else { - try output(context , "0"); + try output(context, "0"); } // {.0} special case doesn't want a trailing '.' @@ -470,10 +463,10 @@ pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, com // Remaining fractional portion, zero-padding if insufficient. debug.assert(precision >= printed); if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { - try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); + try output(context, float_decimal.digits[num_digits_whole_no_pad..num_digits_whole_no_pad + precision - printed]); return; } else { - try output(context, float_decimal.digits[num_digits_whole_no_pad ..]); + try output(context, float_decimal.digits[num_digits_whole_no_pad..]); printed += float_decimal.digits.len - num_digits_whole_no_pad; while (printed < precision) : (printed += 1) { @@ -489,14 +482,14 @@ pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, com if (num_digits_whole > 0) { // We may have to zero pad, for instance 1e4 requires zero padding. - try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]); + try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); var i = num_digits_whole_no_pad; while (i < num_digits_whole) : (i += 1) { try output(context, "0"); } } else { - try output(context , "0"); + try output(context, "0"); } // Omit `.` if no fractional portion @@ -516,14 +509,11 @@ pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, com } } - try output(context, float_decimal.digits[num_digits_whole_no_pad ..]); + try output(context, float_decimal.digits[num_digits_whole_no_pad..]); } } - -pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, - context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void -{ +pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { if (@typeOf(value).is_signed) { return formatIntSigned(value, base, uppercase, width, context, Errors, output); } else { @@ -531,9 +521,7 @@ pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, } } -fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, - context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void -{ +fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { const uint = @IntType(false, @typeOf(value).bit_count); if (value < 0) { const minus_sign: u8 = '-'; @@ -552,9 +540,7 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, } } -fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, - context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void -{ +fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { // max_int_digits accounts for the minus sign. when printing an unsigned // number we don't need to do that. var buf: [max_int_digits - 1]u8 = undefined; @@ -566,8 +552,7 @@ fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, index -= 1; buf[index] = digitToChar(u8(digit), uppercase); a /= base; - if (a == 0) - break; + if (a == 0) break; } const digits_buf = buf[index..]; @@ -579,8 +564,7 @@ fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, while (true) { try output(context, (&zero_byte)[0..1]); leftover_padding -= 1; - if (leftover_padding == 0) - break; + if (leftover_padding == 0) break; } mem.set(u8, buf[0..index], '0'); return output(context, buf); @@ -592,7 +576,7 @@ fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, } pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: usize) usize { - var context = FormatIntBuf { + var context = FormatIntBuf{ .out_buf = out_buf, .index = 0, }; @@ -609,10 +593,8 @@ fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) (error{}!void) { } pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { - if (!T.is_signed) - return parseUnsigned(T, buf, radix); - if (buf.len == 0) - return T(0); + if (!T.is_signed) return parseUnsigned(T, buf, radix); + if (buf.len == 0) return T(0); if (buf[0] == '-') { return math.negate(try parseUnsigned(T, buf[1..], radix)); } else if (buf[0] == '+') { @@ -632,9 +614,10 @@ test "fmt.parseInt" { assert(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); } -const ParseUnsignedError = error { +const ParseUnsignedError = error{ /// The result cannot fit in the type specified Overflow, + /// The input had a byte that was not a digit InvalidCharacter, }; @@ -659,8 +642,7 @@ pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { else => return error.InvalidCharacter, }; - if (value >= radix) - return error.InvalidCharacter; + if (value >= radix) return error.InvalidCharacter; return value; } @@ -684,20 +666,21 @@ fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) !void { } pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { - var context = BufPrintContext { .remaining = buf, }; + var context = BufPrintContext{ .remaining = buf }; try format(&context, error{BufferTooSmall}, bufPrintWrite, fmt, args); return buf[0..buf.len - context.remaining.len]; } pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { var size: usize = 0; - format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; + format(&size, error{}, countSize, fmt, args) catch |err| switch (err) { + }; const buf = try allocator.alloc(u8, size); return bufPrint(buf, fmt, args); } fn countSize(size: &usize, bytes: []const u8) (error{}!void) { - *size += bytes.len; + size.* += bytes.len; } test "buf print int" { @@ -773,9 +756,7 @@ test "fmt.format" { unused: u8, }; var buf1: [32]u8 = undefined; - const value = Struct { - .unused = 42, - }; + const value = Struct{ .unused = 42 }; const result = try bufPrint(buf1[0..], "pointer: {}\n", &value); assert(mem.startsWith(u8, result, "pointer: Struct@")); } @@ -988,7 +969,7 @@ test "fmt.format" { pub fn trim(buf: []const u8) []const u8 { var start: usize = 0; - while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) { } + while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} var end: usize = buf.len; while (true) { @@ -1000,7 +981,6 @@ pub fn trim(buf: []const u8) []const u8 { } } break; - } return buf[start..end]; } @@ -1015,7 +995,10 @@ test "fmt.trim" { pub fn isWhiteSpace(byte: u8) bool { return switch (byte) { - ' ', '\t', '\n', '\r' => true, + ' ', + '\t', + '\n', + '\r' => true, else => false, }; } diff --git a/std/heap.zig b/std/heap.zig index bfdf62f658..7b753524a6 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -10,7 +10,7 @@ const c = std.c; const Allocator = mem.Allocator; pub const c_allocator = &c_allocator_state; -var c_allocator_state = Allocator { +var c_allocator_state = Allocator{ .allocFn = cAlloc, .reallocFn = cRealloc, .freeFn = cFree, @@ -18,10 +18,7 @@ var c_allocator_state = Allocator { fn cAlloc(self: &Allocator, n: usize, alignment: u29) ![]u8 { assert(alignment <= @alignOf(c_longdouble)); - return if (c.malloc(n)) |buf| - @ptrCast(&u8, buf)[0..n] - else - error.OutOfMemory; + return if (c.malloc(n)) |buf| @ptrCast(&u8, buf)[0..n] else error.OutOfMemory; } fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { @@ -48,8 +45,8 @@ pub const DirectAllocator = struct { const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void; pub fn init() DirectAllocator { - return DirectAllocator { - .allocator = Allocator { + return DirectAllocator{ + .allocator = Allocator{ .allocFn = alloc, .reallocFn = realloc, .freeFn = free, @@ -71,39 +68,39 @@ pub const DirectAllocator = struct { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { - Os.linux, Os.macosx, Os.ios => { + Os.linux, + Os.macosx, + Os.ios => { const p = os.posix; - const alloc_size = if(alignment <= os.page_size) n else n + alignment; - const addr = p.mmap(null, alloc_size, p.PROT_READ|p.PROT_WRITE, - p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0); - if(addr == p.MAP_FAILED) return error.OutOfMemory; - - if(alloc_size == n) return @intToPtr(&u8, addr)[0..n]; - + const alloc_size = if (alignment <= os.page_size) n else n + alignment; + const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0); + if (addr == p.MAP_FAILED) return error.OutOfMemory; + + if (alloc_size == n) return @intToPtr(&u8, addr)[0..n]; + var aligned_addr = addr & ~usize(alignment - 1); aligned_addr += alignment; - + //We can unmap the unused portions of our mmap, but we must only // pass munmap bytes that exist outside our allocated pages or it // will happily eat us too - + //Since alignment > page_size, we are by definition on a page boundry const unused_start = addr; const unused_len = aligned_addr - 1 - unused_start; var err = p.munmap(unused_start, unused_len); debug.assert(p.getErrno(err) == 0); - + //It is impossible that there is an unoccupied page at the top of our // mmap. - + return @intToPtr(&u8, aligned_addr)[0..n]; }, Os.windows => { const amt = n + alignment + @sizeOf(usize); const heap_handle = self.heap_handle ?? blk: { - const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) - ?? return error.OutOfMemory; + const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) ?? return error.OutOfMemory; self.heap_handle = hh; break :blk hh; }; @@ -113,7 +110,7 @@ pub const DirectAllocator = struct { const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); const adjusted_addr = root_addr + march_forward_bytes; const record_addr = adjusted_addr + n; - *@intToPtr(&align(1) usize, record_addr) = root_addr; + @intToPtr(&align(1) usize, record_addr).* = root_addr; return @intToPtr(&u8, adjusted_addr)[0..n]; }, else => @compileError("Unsupported OS"), @@ -124,7 +121,9 @@ pub const DirectAllocator = struct { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { - Os.linux, Os.macosx, Os.ios => { + Os.linux, + Os.macosx, + Os.ios => { if (new_size <= old_mem.len) { const base_addr = @ptrToInt(old_mem.ptr); const old_addr_end = base_addr + old_mem.len; @@ -144,13 +143,13 @@ pub const DirectAllocator = struct { Os.windows => { const old_adjusted_addr = @ptrToInt(old_mem.ptr); const old_record_addr = old_adjusted_addr + old_mem.len; - const root_addr = *@intToPtr(&align(1) usize, old_record_addr); + const root_addr = @intToPtr(&align(1) usize, old_record_addr).*; const old_ptr = @intToPtr(os.windows.LPVOID, root_addr); const amt = new_size + alignment + @sizeOf(usize); const new_ptr = os.windows.HeapReAlloc(??self.heap_handle, 0, old_ptr, amt) ?? blk: { if (new_size > old_mem.len) return error.OutOfMemory; const new_record_addr = old_record_addr - new_size + old_mem.len; - *@intToPtr(&align(1) usize, new_record_addr) = root_addr; + @intToPtr(&align(1) usize, new_record_addr).* = root_addr; return old_mem[0..new_size]; }; const offset = old_adjusted_addr - root_addr; @@ -158,7 +157,7 @@ pub const DirectAllocator = struct { const new_adjusted_addr = new_root_addr + offset; assert(new_adjusted_addr % alignment == 0); const new_record_addr = new_adjusted_addr + new_size; - *@intToPtr(&align(1) usize, new_record_addr) = new_root_addr; + @intToPtr(&align(1) usize, new_record_addr).* = new_root_addr; return @intToPtr(&u8, new_adjusted_addr)[0..new_size]; }, else => @compileError("Unsupported OS"), @@ -169,12 +168,14 @@ pub const DirectAllocator = struct { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { - Os.linux, Os.macosx, Os.ios => { + Os.linux, + Os.macosx, + Os.ios => { _ = os.posix.munmap(@ptrToInt(bytes.ptr), bytes.len); }, Os.windows => { const record_addr = @ptrToInt(bytes.ptr) + bytes.len; - const root_addr = *@intToPtr(&align(1) usize, record_addr); + const root_addr = @intToPtr(&align(1) usize, record_addr).*; const ptr = @intToPtr(os.windows.LPVOID, root_addr); _ = os.windows.HeapFree(??self.heap_handle, 0, ptr); }, @@ -195,8 +196,8 @@ pub const ArenaAllocator = struct { const BufNode = std.LinkedList([]u8).Node; pub fn init(child_allocator: &Allocator) ArenaAllocator { - return ArenaAllocator { - .allocator = Allocator { + return ArenaAllocator{ + .allocator = Allocator{ .allocFn = alloc, .reallocFn = realloc, .freeFn = free, @@ -228,7 +229,7 @@ pub const ArenaAllocator = struct { const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len); const buf_node_slice = ([]BufNode)(buf[0..@sizeOf(BufNode)]); const buf_node = &buf_node_slice[0]; - *buf_node = BufNode { + buf_node.* = BufNode{ .data = buf, .prev = null, .next = null, @@ -253,7 +254,7 @@ pub const ArenaAllocator = struct { cur_node = try self.createNode(cur_buf.len, n + alignment); continue; } - const result = cur_buf[adjusted_index .. new_end_index]; + const result = cur_buf[adjusted_index..new_end_index]; self.end_index = new_end_index; return result; } @@ -269,7 +270,7 @@ pub const ArenaAllocator = struct { } } - fn free(allocator: &Allocator, bytes: []u8) void { } + fn free(allocator: &Allocator, bytes: []u8) void {} }; pub const FixedBufferAllocator = struct { @@ -278,8 +279,8 @@ pub const FixedBufferAllocator = struct { buffer: []u8, pub fn init(buffer: []u8) FixedBufferAllocator { - return FixedBufferAllocator { - .allocator = Allocator { + return FixedBufferAllocator{ + .allocator = Allocator{ .allocFn = alloc, .reallocFn = realloc, .freeFn = free, @@ -299,7 +300,7 @@ pub const FixedBufferAllocator = struct { if (new_end_index > self.buffer.len) { return error.OutOfMemory; } - const result = self.buffer[adjusted_index .. new_end_index]; + const result = self.buffer[adjusted_index..new_end_index]; self.end_index = new_end_index; return result; @@ -315,7 +316,7 @@ pub const FixedBufferAllocator = struct { } } - fn free(allocator: &Allocator, bytes: []u8) void { } + fn free(allocator: &Allocator, bytes: []u8) void {} }; /// lock free @@ -325,8 +326,8 @@ pub const ThreadSafeFixedBufferAllocator = struct { buffer: []u8, pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator { - return ThreadSafeFixedBufferAllocator { - .allocator = Allocator { + return ThreadSafeFixedBufferAllocator{ + .allocator = Allocator{ .allocFn = alloc, .reallocFn = realloc, .freeFn = free, @@ -348,8 +349,7 @@ pub const ThreadSafeFixedBufferAllocator = struct { if (new_end_index > self.buffer.len) { return error.OutOfMemory; } - end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, - builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index .. new_end_index]; + end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index..new_end_index]; } } @@ -363,11 +363,9 @@ pub const ThreadSafeFixedBufferAllocator = struct { } } - fn free(allocator: &Allocator, bytes: []u8) void { } + fn free(allocator: &Allocator, bytes: []u8) void {} }; - - test "c_allocator" { if (builtin.link_libc) { var slice = c_allocator.alloc(u8, 50) catch return; @@ -415,8 +413,8 @@ fn testAllocator(allocator: &mem.Allocator) !void { var slice = try allocator.alloc(&i32, 100); for (slice) |*item, i| { - *item = try allocator.create(i32); - **item = i32(i); + item.* = try allocator.create(i32); + item.*.* = i32(i); } for (slice) |item, i| { @@ -434,26 +432,26 @@ fn testAllocator(allocator: &mem.Allocator) !void { fn testAllocatorLargeAlignment(allocator: &mem.Allocator) mem.Allocator.Error!void { //Maybe a platform's page_size is actually the same as or // very near usize? - if(os.page_size << 2 > @maxValue(usize)) return; - + if (os.page_size << 2 > @maxValue(usize)) return; + const USizeShift = @IntType(false, std.math.log2(usize.bit_count)); const large_align = u29(os.page_size << 2); - + var align_mask: usize = undefined; _ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(large_align)), &align_mask); - + var slice = try allocator.allocFn(allocator, 500, large_align); debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); - + slice = try allocator.reallocFn(allocator, slice, 100, large_align); debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); - + slice = try allocator.reallocFn(allocator, slice, 5000, large_align); debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); - + slice = try allocator.reallocFn(allocator, slice, 10, large_align); debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); - + slice = try allocator.reallocFn(allocator, slice, 20000, large_align); debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); diff --git a/std/io.zig b/std/io.zig index 7b72af15e4..7c997fdf42 100644 --- a/std/io.zig +++ b/std/io.zig @@ -18,32 +18,17 @@ const is_windows = builtin.os == builtin.Os.windows; const GetStdIoErrs = os.WindowsGetStdHandleErrs; pub fn getStdErr() GetStdIoErrs!File { - const handle = if (is_windows) - try os.windowsGetStdHandle(os.windows.STD_ERROR_HANDLE) - else if (is_posix) - os.posix.STDERR_FILENO - else - unreachable; + const handle = if (is_windows) try os.windowsGetStdHandle(os.windows.STD_ERROR_HANDLE) else if (is_posix) os.posix.STDERR_FILENO else unreachable; return File.openHandle(handle); } pub fn getStdOut() GetStdIoErrs!File { - const handle = if (is_windows) - try os.windowsGetStdHandle(os.windows.STD_OUTPUT_HANDLE) - else if (is_posix) - os.posix.STDOUT_FILENO - else - unreachable; + const handle = if (is_windows) try os.windowsGetStdHandle(os.windows.STD_OUTPUT_HANDLE) else if (is_posix) os.posix.STDOUT_FILENO else unreachable; return File.openHandle(handle); } pub fn getStdIn() GetStdIoErrs!File { - const handle = if (is_windows) - try os.windowsGetStdHandle(os.windows.STD_INPUT_HANDLE) - else if (is_posix) - os.posix.STDIN_FILENO - else - unreachable; + const handle = if (is_windows) try os.windowsGetStdHandle(os.windows.STD_INPUT_HANDLE) else if (is_posix) os.posix.STDIN_FILENO else unreachable; return File.openHandle(handle); } @@ -56,11 +41,9 @@ pub const FileInStream = struct { pub const Stream = InStream(Error); pub fn init(file: &File) FileInStream { - return FileInStream { + return FileInStream{ .file = file, - .stream = Stream { - .readFn = readFn, - }, + .stream = Stream{ .readFn = readFn }, }; } @@ -79,11 +62,9 @@ pub const FileOutStream = struct { pub const Stream = OutStream(Error); pub fn init(file: &File) FileOutStream { - return FileOutStream { + return FileOutStream{ .file = file, - .stream = Stream { - .writeFn = writeFn, - }, + .stream = Stream{ .writeFn = writeFn }, }; } @@ -121,8 +102,7 @@ pub fn InStream(comptime ReadError: type) type { } const new_buf_size = math.min(max_size, actual_buf_len + os.page_size); - if (new_buf_size == actual_buf_len) - return error.StreamTooLong; + if (new_buf_size == actual_buf_len) return error.StreamTooLong; try buffer.resize(new_buf_size); } } @@ -165,9 +145,7 @@ pub fn InStream(comptime ReadError: type) type { /// memory would be greater than `max_size`, returns `error.StreamTooLong`. /// Caller owns returned memory. /// If this function returns an error, the contents from the stream read so far are lost. - pub fn readUntilDelimiterAlloc(self: &Self, allocator: &mem.Allocator, - delimiter: u8, max_size: usize) ![]u8 - { + pub fn readUntilDelimiterAlloc(self: &Self, allocator: &mem.Allocator, delimiter: u8, max_size: usize) ![]u8 { var buf = Buffer.initNull(allocator); defer buf.deinit(); @@ -283,7 +261,7 @@ pub fn BufferedInStream(comptime Error: type) type { pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) type { return struct { const Self = this; - const Stream = InStream(Error); + const Stream = InStream(Error); pub stream: Stream, @@ -294,7 +272,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) end_index: usize, pub fn init(unbuffered_in_stream: &Stream) Self { - return Self { + return Self{ .unbuffered_in_stream = unbuffered_in_stream, .buffer = undefined, @@ -305,9 +283,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) .start_index = buffer_size, .end_index = buffer_size, - .stream = Stream { - .readFn = readFn, - }, + .stream = Stream{ .readFn = readFn }, }; } @@ -368,13 +344,11 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr index: usize, pub fn init(unbuffered_out_stream: &Stream) Self { - return Self { + return Self{ .unbuffered_out_stream = unbuffered_out_stream, .buffer = undefined, .index = 0, - .stream = Stream { - .writeFn = writeFn, - }, + .stream = Stream{ .writeFn = writeFn }, }; } @@ -416,11 +390,9 @@ pub const BufferOutStream = struct { pub const Stream = OutStream(Error); pub fn init(buffer: &Buffer) BufferOutStream { - return BufferOutStream { + return BufferOutStream{ .buffer = buffer, - .stream = Stream { - .writeFn = writeFn, - }, + .stream = Stream{ .writeFn = writeFn }, }; } @@ -430,7 +402,6 @@ pub const BufferOutStream = struct { } }; - pub const BufferedAtomicFile = struct { atomic_file: os.AtomicFile, file_stream: FileOutStream, @@ -441,7 +412,7 @@ pub const BufferedAtomicFile = struct { var self = try allocator.create(BufferedAtomicFile); errdefer allocator.destroy(self); - *self = BufferedAtomicFile { + self.* = BufferedAtomicFile{ .atomic_file = undefined, .file_stream = undefined, .buffered_stream = undefined, @@ -489,7 +460,7 @@ pub fn readLine(buf: []u8) !usize { '\r' => { // trash the following \n _ = stream.readByte() catch return error.EndOfFile; - return index; + return index; }, '\n' => return index, else => { diff --git a/std/linked_list.zig b/std/linked_list.zig index 45595f3efb..c6be08171e 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -26,10 +26,10 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na data: T, pub fn init(value: &const T) Node { - return Node { + return Node{ .prev = null, .next = null, - .data = *value, + .data = value.*, }; } @@ -45,18 +45,18 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na }; first: ?&Node, - last: ?&Node, - len: usize, + last: ?&Node, + len: usize, /// Initialize a linked list. /// /// Returns: /// An empty linked list. pub fn init() Self { - return Self { + return Self{ .first = null, - .last = null, - .len = 0, + .last = null, + .len = 0, }; } @@ -131,7 +131,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na } else { // Empty list. list.first = new_node; - list.last = new_node; + list.last = new_node; new_node.prev = null; new_node.next = null; @@ -217,7 +217,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) !&Node { comptime assert(!isIntrusive()); var node = try list.allocateNode(allocator); - *node = Node.init(data); + node.* = Node.init(data); return node; } }; @@ -227,11 +227,11 @@ test "basic linked list test" { const allocator = debug.global_allocator; var list = LinkedList(u32).init(); - var one = try list.createNode(1, allocator); - var two = try list.createNode(2, allocator); + var one = try list.createNode(1, allocator); + var two = try list.createNode(2, allocator); var three = try list.createNode(3, allocator); - var four = try list.createNode(4, allocator); - var five = try list.createNode(5, allocator); + var four = try list.createNode(4, allocator); + var five = try list.createNode(5, allocator); defer { list.destroyNode(one, allocator); list.destroyNode(two, allocator); @@ -240,11 +240,11 @@ test "basic linked list test" { list.destroyNode(five, allocator); } - list.append(two); // {2} - list.append(five); // {2, 5} - list.prepend(one); // {1, 2, 5} - list.insertBefore(five, four); // {1, 2, 4, 5} - list.insertAfter(two, three); // {1, 2, 3, 4, 5} + list.append(two); // {2} + list.append(five); // {2, 5} + list.prepend(one); // {1, 2, 5} + list.insertBefore(five, four); // {1, 2, 4, 5} + list.insertAfter(two, three); // {1, 2, 3, 4, 5} // Traverse forwards. { @@ -266,13 +266,13 @@ test "basic linked list test" { } } - var first = list.popFirst(); // {2, 3, 4, 5} - var last = list.pop(); // {2, 3, 4} - list.remove(three); // {2, 4} + var first = list.popFirst(); // {2, 3, 4, 5} + var last = list.pop(); // {2, 3, 4} + list.remove(three); // {2, 4} - assert ((??list.first).data == 2); - assert ((??list.last ).data == 4); - assert (list.len == 2); + assert((??list.first).data == 2); + assert((??list.last).data == 4); + assert(list.len == 2); } const ElementList = IntrusiveLinkedList(Element, "link"); @@ -285,17 +285,32 @@ test "basic intrusive linked list test" { const allocator = debug.global_allocator; var list = ElementList.init(); - var one = Element { .value = 1, .link = ElementList.Node.initIntrusive() }; - var two = Element { .value = 2, .link = ElementList.Node.initIntrusive() }; - var three = Element { .value = 3, .link = ElementList.Node.initIntrusive() }; - var four = Element { .value = 4, .link = ElementList.Node.initIntrusive() }; - var five = Element { .value = 5, .link = ElementList.Node.initIntrusive() }; + var one = Element{ + .value = 1, + .link = ElementList.Node.initIntrusive(), + }; + var two = Element{ + .value = 2, + .link = ElementList.Node.initIntrusive(), + }; + var three = Element{ + .value = 3, + .link = ElementList.Node.initIntrusive(), + }; + var four = Element{ + .value = 4, + .link = ElementList.Node.initIntrusive(), + }; + var five = Element{ + .value = 5, + .link = ElementList.Node.initIntrusive(), + }; - list.append(&two.link); // {2} - list.append(&five.link); // {2, 5} - list.prepend(&one.link); // {1, 2, 5} - list.insertBefore(&five.link, &four.link); // {1, 2, 4, 5} - list.insertAfter(&two.link, &three.link); // {1, 2, 3, 4, 5} + list.append(&two.link); // {2} + list.append(&five.link); // {2, 5} + list.prepend(&one.link); // {1, 2, 5} + list.insertBefore(&five.link, &four.link); // {1, 2, 4, 5} + list.insertAfter(&two.link, &three.link); // {1, 2, 3, 4, 5} // Traverse forwards. { @@ -317,11 +332,11 @@ test "basic intrusive linked list test" { } } - var first = list.popFirst(); // {2, 3, 4, 5} - var last = list.pop(); // {2, 3, 4} - list.remove(&three.link); // {2, 4} + var first = list.popFirst(); // {2, 3, 4, 5} + var last = list.pop(); // {2, 3, 4} + list.remove(&three.link); // {2, 4} - assert ((??list.first).toData().value == 2); - assert ((??list.last ).toData().value == 4); - assert (list.len == 2); + assert((??list.first).toData().value == 2); + assert((??list.last).toData().value == 4); + assert(list.len == 2); } diff --git a/std/os/index.zig b/std/os/index.zig index 4f1826021f..8728f4a6f6 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -137,7 +137,7 @@ pub fn getRandomBytes(buf: []u8) !void { } }, Os.zen => { - const randomness = []u8 { + const randomness = []u8{ 42, 1, 7, @@ -265,7 +265,7 @@ pub fn posixRead(fd: i32, buf: []u8) !void { } } -pub const PosixWriteError = error { +pub const PosixWriteError = error{ WouldBlock, FileClosed, DestinationAddressRequired, @@ -310,7 +310,7 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { } } -pub const PosixOpenError = error { +pub const PosixOpenError = error{ OutOfMemory, AccessDenied, FileTooBig, @@ -477,7 +477,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, allocator: return posixExecveErrnoToErr(err); } -pub const PosixExecveError = error { +pub const PosixExecveError = error{ SystemResources, AccessDenied, InvalidExe, @@ -512,7 +512,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { }; } -pub var linux_aux_raw = []usize {0} ** 38; +pub var linux_aux_raw = []usize{0} ** 38; pub var posix_environ_raw: []&u8 = undefined; /// Caller must free result when done. @@ -667,7 +667,7 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con } } -pub const WindowsSymLinkError = error { +pub const WindowsSymLinkError = error{ OutOfMemory, Unexpected, }; @@ -686,7 +686,7 @@ pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path } } -pub const PosixSymLinkError = error { +pub const PosixSymLinkError = error{ OutOfMemory, AccessDenied, DiskQuota, @@ -895,7 +895,7 @@ pub const AtomicFile = struct { else => return err, }; - return AtomicFile { + return AtomicFile{ .allocator = allocator, .file = file, .tmp_path = tmp_path, @@ -1087,7 +1087,7 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void { /// removes it. If it cannot be removed because it is a non-empty directory, /// this function recursively removes its entries and then tries again. /// TODO non-recursive implementation -const DeleteTreeError = error { +const DeleteTreeError = error{ OutOfMemory, AccessDenied, FileTooBig, @@ -1217,7 +1217,7 @@ pub const Dir = struct { Os.ios => 0, else => {}, }; - return Dir { + return Dir{ .allocator = allocator, .fd = fd, .darwin_seek = darwin_seek_init, @@ -1294,7 +1294,7 @@ pub const Dir = struct { posix.DT_WHT => Entry.Kind.Whiteout, else => Entry.Kind.Unknown, }; - return Entry { + return Entry{ .name = name, .kind = entry_kind, }; @@ -1355,7 +1355,7 @@ pub const Dir = struct { posix.DT_SOCK => Entry.Kind.UnixDomainSocket, else => Entry.Kind.Unknown, }; - return Entry { + return Entry{ .name = name, .kind = entry_kind, }; @@ -1465,7 +1465,7 @@ pub fn posix_setregid(rgid: u32, egid: u32) !void { }; } -pub const WindowsGetStdHandleErrs = error { +pub const WindowsGetStdHandleErrs = error{ NoStdHandles, Unexpected, }; @@ -1489,7 +1489,7 @@ pub const ArgIteratorPosix = struct { count: usize, pub fn init() ArgIteratorPosix { - return ArgIteratorPosix { + return ArgIteratorPosix{ .index = 0, .count = raw.len, }; @@ -1522,16 +1522,14 @@ pub const ArgIteratorWindows = struct { quote_count: usize, seen_quote_count: usize, - pub const NextError = error { - OutOfMemory, - }; + pub const NextError = error{OutOfMemory}; pub fn init() ArgIteratorWindows { return initWithCmdLine(windows.GetCommandLineA()); } pub fn initWithCmdLine(cmd_line: &const u8) ArgIteratorWindows { - return ArgIteratorWindows { + return ArgIteratorWindows{ .index = 0, .cmd_line = cmd_line, .in_quote = false, @@ -1676,9 +1674,7 @@ pub const ArgIterator = struct { inner: InnerType, pub fn init() ArgIterator { - return ArgIterator { - .inner = InnerType.init(), - }; + return ArgIterator{ .inner = InnerType.init() }; } pub const NextError = ArgIteratorWindows.NextError; @@ -1757,33 +1753,33 @@ pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void { } test "windows arg parsing" { - testWindowsCmdLine(c"a b\tc d", [][]const u8 { + testWindowsCmdLine(c"a b\tc d", [][]const u8{ "a", "b", "c", "d", }); - testWindowsCmdLine(c"\"abc\" d e", [][]const u8 { + testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ "abc", "d", "e", }); - testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8 { + testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ "a\\\\\\b", "de fg", "h", }); - testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8 { + testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ "a\\\"b", "c", "d", }); - testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8 { + testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ "a\\\\b c", "d", "e", }); - testWindowsCmdLine(c"a b\tc \"d f", [][]const u8 { + testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{ "a", "b", "c", @@ -1791,7 +1787,7 @@ test "windows arg parsing" { "f", }); - testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8 { + testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8{ ".\\..\\zig-cache\\build", "bin\\zig.exe", ".\\..", @@ -1811,7 +1807,7 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const // TODO make this a build variable that you can set const unexpected_error_tracing = false; -const UnexpectedError = error { +const UnexpectedError = error{ /// The Operating System returned an undocumented error code. Unexpected, }; @@ -1950,7 +1946,7 @@ pub fn isTty(handle: FileHandle) bool { } } -pub const PosixSocketError = error { +pub const PosixSocketError = error{ /// Permission to create a socket of the specified type and/or /// pro‐tocol is denied. PermissionDenied, @@ -1992,7 +1988,7 @@ pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { } } -pub const PosixBindError = error { +pub const PosixBindError = error{ /// The address is protected, and the user is not the superuser. /// For UNIX domain sockets: Search permission is denied on a component /// of the path prefix. @@ -2065,7 +2061,7 @@ pub fn posixBind(fd: i32, addr: &const posix.sockaddr) PosixBindError!void { } } -const PosixListenError = error { +const PosixListenError = error{ /// Another socket is already listening on the same port. /// For Internet domain sockets, the socket referred to by sockfd had not previously /// been bound to an address and, upon attempting to bind it to an ephemeral port, it @@ -2098,7 +2094,7 @@ pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void { } } -pub const PosixAcceptError = error { +pub const PosixAcceptError = error{ /// The socket is marked nonblocking and no connections are present to be accepted. WouldBlock, @@ -2165,7 +2161,7 @@ pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError! } } -pub const LinuxEpollCreateError = error { +pub const LinuxEpollCreateError = error{ /// Invalid value specified in flags. InvalidSyscall, @@ -2198,7 +2194,7 @@ pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 { } } -pub const LinuxEpollCtlError = error { +pub const LinuxEpollCtlError = error{ /// epfd or fd is not a valid file descriptor. InvalidFileDescriptor, @@ -2271,7 +2267,7 @@ pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usiz } } -pub const PosixGetSockNameError = error { +pub const PosixGetSockNameError = error{ /// Insufficient resources were available in the system to perform the operation. SystemResources, @@ -2295,7 +2291,7 @@ pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr { } } -pub const PosixConnectError = error { +pub const PosixConnectError = error{ /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket /// file, or search permission is denied for one of the directories in the path prefix. /// or @@ -2484,7 +2480,7 @@ pub const Thread = struct { } }; -pub const SpawnThreadError = error { +pub const SpawnThreadError = error{ /// A system-imposed limit on the number of threads was encountered. /// There are a number of limits that may trigger this error: /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), @@ -2532,7 +2528,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread if (@sizeOf(Context) == 0) { return startFn({}); } else { - return startFn(*@ptrCast(&Context, @alignCast(@alignOf(Context), arg))); + return startFn(@ptrCast(&Context, @alignCast(@alignOf(Context), arg)).*); } } }; @@ -2562,7 +2558,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread if (@sizeOf(Context) == 0) { return startFn({}); } else { - return startFn(*@intToPtr(&const Context, ctx_addr)); + return startFn(@intToPtr(&const Context, ctx_addr).*); } } extern fn posixThreadMain(ctx: ?&c_void) ?&c_void { @@ -2570,7 +2566,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread _ = startFn({}); return null; } else { - _ = startFn(*@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx))); + _ = startFn(@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx)).*); return null; } } @@ -2590,7 +2586,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread stack_end -= stack_end % @alignOf(Context); assert(stack_end >= stack_addr); const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end)); - *context_ptr = context; + context_ptr.* = context; arg = stack_end; } diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 368f074b9b..4e0e9ee5d5 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -30,96 +30,95 @@ pub const FUTEX_PRIVATE_FLAG = 128; pub const FUTEX_CLOCK_REALTIME = 256; - -pub const PROT_NONE = 0; -pub const PROT_READ = 1; -pub const PROT_WRITE = 2; -pub const PROT_EXEC = 4; +pub const PROT_NONE = 0; +pub const PROT_READ = 1; +pub const PROT_WRITE = 2; +pub const PROT_EXEC = 4; pub const PROT_GROWSDOWN = 0x01000000; -pub const PROT_GROWSUP = 0x02000000; - -pub const MAP_FAILED = @maxValue(usize); -pub const MAP_SHARED = 0x01; -pub const MAP_PRIVATE = 0x02; -pub const MAP_TYPE = 0x0f; -pub const MAP_FIXED = 0x10; -pub const MAP_ANONYMOUS = 0x20; -pub const MAP_NORESERVE = 0x4000; -pub const MAP_GROWSDOWN = 0x0100; -pub const MAP_DENYWRITE = 0x0800; +pub const PROT_GROWSUP = 0x02000000; + +pub const MAP_FAILED = @maxValue(usize); +pub const MAP_SHARED = 0x01; +pub const MAP_PRIVATE = 0x02; +pub const MAP_TYPE = 0x0f; +pub const MAP_FIXED = 0x10; +pub const MAP_ANONYMOUS = 0x20; +pub const MAP_NORESERVE = 0x4000; +pub const MAP_GROWSDOWN = 0x0100; +pub const MAP_DENYWRITE = 0x0800; pub const MAP_EXECUTABLE = 0x1000; -pub const MAP_LOCKED = 0x2000; -pub const MAP_POPULATE = 0x8000; -pub const MAP_NONBLOCK = 0x10000; -pub const MAP_STACK = 0x20000; -pub const MAP_HUGETLB = 0x40000; -pub const MAP_FILE = 0; +pub const MAP_LOCKED = 0x2000; +pub const MAP_POPULATE = 0x8000; +pub const MAP_NONBLOCK = 0x10000; +pub const MAP_STACK = 0x20000; +pub const MAP_HUGETLB = 0x40000; +pub const MAP_FILE = 0; pub const F_OK = 0; pub const X_OK = 1; pub const W_OK = 2; pub const R_OK = 4; -pub const WNOHANG = 1; -pub const WUNTRACED = 2; -pub const WSTOPPED = 2; -pub const WEXITED = 4; +pub const WNOHANG = 1; +pub const WUNTRACED = 2; +pub const WSTOPPED = 2; +pub const WEXITED = 4; pub const WCONTINUED = 8; -pub const WNOWAIT = 0x1000000; - -pub const SA_NOCLDSTOP = 1; -pub const SA_NOCLDWAIT = 2; -pub const SA_SIGINFO = 4; -pub const SA_ONSTACK = 0x08000000; -pub const SA_RESTART = 0x10000000; -pub const SA_NODEFER = 0x40000000; -pub const SA_RESETHAND = 0x80000000; -pub const SA_RESTORER = 0x04000000; - -pub const SIGHUP = 1; -pub const SIGINT = 2; -pub const SIGQUIT = 3; -pub const SIGILL = 4; -pub const SIGTRAP = 5; -pub const SIGABRT = 6; -pub const SIGIOT = SIGABRT; -pub const SIGBUS = 7; -pub const SIGFPE = 8; -pub const SIGKILL = 9; -pub const SIGUSR1 = 10; -pub const SIGSEGV = 11; -pub const SIGUSR2 = 12; -pub const SIGPIPE = 13; -pub const SIGALRM = 14; -pub const SIGTERM = 15; +pub const WNOWAIT = 0x1000000; + +pub const SA_NOCLDSTOP = 1; +pub const SA_NOCLDWAIT = 2; +pub const SA_SIGINFO = 4; +pub const SA_ONSTACK = 0x08000000; +pub const SA_RESTART = 0x10000000; +pub const SA_NODEFER = 0x40000000; +pub const SA_RESETHAND = 0x80000000; +pub const SA_RESTORER = 0x04000000; + +pub const SIGHUP = 1; +pub const SIGINT = 2; +pub const SIGQUIT = 3; +pub const SIGILL = 4; +pub const SIGTRAP = 5; +pub const SIGABRT = 6; +pub const SIGIOT = SIGABRT; +pub const SIGBUS = 7; +pub const SIGFPE = 8; +pub const SIGKILL = 9; +pub const SIGUSR1 = 10; +pub const SIGSEGV = 11; +pub const SIGUSR2 = 12; +pub const SIGPIPE = 13; +pub const SIGALRM = 14; +pub const SIGTERM = 15; pub const SIGSTKFLT = 16; -pub const SIGCHLD = 17; -pub const SIGCONT = 18; -pub const SIGSTOP = 19; -pub const SIGTSTP = 20; -pub const SIGTTIN = 21; -pub const SIGTTOU = 22; -pub const SIGURG = 23; -pub const SIGXCPU = 24; -pub const SIGXFSZ = 25; +pub const SIGCHLD = 17; +pub const SIGCONT = 18; +pub const SIGSTOP = 19; +pub const SIGTSTP = 20; +pub const SIGTTIN = 21; +pub const SIGTTOU = 22; +pub const SIGURG = 23; +pub const SIGXCPU = 24; +pub const SIGXFSZ = 25; pub const SIGVTALRM = 26; -pub const SIGPROF = 27; -pub const SIGWINCH = 28; -pub const SIGIO = 29; -pub const SIGPOLL = 29; -pub const SIGPWR = 30; -pub const SIGSYS = 31; +pub const SIGPROF = 27; +pub const SIGWINCH = 28; +pub const SIGIO = 29; +pub const SIGPOLL = 29; +pub const SIGPWR = 30; +pub const SIGSYS = 31; pub const SIGUNUSED = SIGSYS; pub const O_RDONLY = 0o0; pub const O_WRONLY = 0o1; -pub const O_RDWR = 0o2; +pub const O_RDWR = 0o2; pub const SEEK_SET = 0; pub const SEEK_CUR = 1; pub const SEEK_END = 2; -pub const SIG_BLOCK = 0; +pub const SIG_BLOCK = 0; pub const SIG_UNBLOCK = 1; pub const SIG_SETMASK = 2; @@ -408,7 +407,6 @@ pub const DT_LNK = 10; pub const DT_SOCK = 12; pub const DT_WHT = 14; - pub const TCGETS = 0x5401; pub const TCSETS = 0x5402; pub const TCSETSW = 0x5403; @@ -539,23 +537,23 @@ pub const MS_BIND = 4096; pub const MS_MOVE = 8192; pub const MS_REC = 16384; pub const MS_SILENT = 32768; -pub const MS_POSIXACL = (1<<16); -pub const MS_UNBINDABLE = (1<<17); -pub const MS_PRIVATE = (1<<18); -pub const MS_SLAVE = (1<<19); -pub const MS_SHARED = (1<<20); -pub const MS_RELATIME = (1<<21); -pub const MS_KERNMOUNT = (1<<22); -pub const MS_I_VERSION = (1<<23); -pub const MS_STRICTATIME = (1<<24); -pub const MS_LAZYTIME = (1<<25); -pub const MS_NOREMOTELOCK = (1<<27); -pub const MS_NOSEC = (1<<28); -pub const MS_BORN = (1<<29); -pub const MS_ACTIVE = (1<<30); -pub const MS_NOUSER = (1<<31); - -pub const MS_RMT_MASK = (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|MS_LAZYTIME); +pub const MS_POSIXACL = (1 << 16); +pub const MS_UNBINDABLE = (1 << 17); +pub const MS_PRIVATE = (1 << 18); +pub const MS_SLAVE = (1 << 19); +pub const MS_SHARED = (1 << 20); +pub const MS_RELATIME = (1 << 21); +pub const MS_KERNMOUNT = (1 << 22); +pub const MS_I_VERSION = (1 << 23); +pub const MS_STRICTATIME = (1 << 24); +pub const MS_LAZYTIME = (1 << 25); +pub const MS_NOREMOTELOCK = (1 << 27); +pub const MS_NOSEC = (1 << 28); +pub const MS_BORN = (1 << 29); +pub const MS_ACTIVE = (1 << 30); +pub const MS_NOUSER = (1 << 31); + +pub const MS_RMT_MASK = (MS_RDONLY | MS_SYNCHRONOUS | MS_MANDLOCK | MS_I_VERSION | MS_LAZYTIME); pub const MS_MGC_VAL = 0xc0ed0000; pub const MS_MGC_MSK = 0xffff0000; @@ -565,7 +563,6 @@ pub const MNT_DETACH = 2; pub const MNT_EXPIRE = 4; pub const UMOUNT_NOFOLLOW = 8; - pub const S_IFMT = 0o170000; pub const S_IFDIR = 0o040000; @@ -626,15 +623,30 @@ pub const TFD_CLOEXEC = O_CLOEXEC; pub const TFD_TIMER_ABSTIME = 1; pub const TFD_TIMER_CANCEL_ON_SET = (1 << 1); -fn unsigned(s: i32) u32 { return @bitCast(u32, s); } -fn signed(s: u32) i32 { return @bitCast(i32, s); } -pub fn WEXITSTATUS(s: i32) i32 { return signed((unsigned(s) & 0xff00) >> 8); } -pub fn WTERMSIG(s: i32) i32 { return signed(unsigned(s) & 0x7f); } -pub fn WSTOPSIG(s: i32) i32 { return WEXITSTATUS(s); } -pub fn WIFEXITED(s: i32) bool { return WTERMSIG(s) == 0; } -pub fn WIFSTOPPED(s: i32) bool { return (u16)(((unsigned(s)&0xffff)*%0x10001)>>8) > 0x7f00; } -pub fn WIFSIGNALED(s: i32) bool { return (unsigned(s)&0xffff)-%1 < 0xff; } - +fn unsigned(s: i32) u32 { + return @bitCast(u32, s); +} +fn signed(s: u32) i32 { + return @bitCast(i32, s); +} +pub fn WEXITSTATUS(s: i32) i32 { + return signed((unsigned(s) & 0xff00) >> 8); +} +pub fn WTERMSIG(s: i32) i32 { + return signed(unsigned(s) & 0x7f); +} +pub fn WSTOPSIG(s: i32) i32 { + return WEXITSTATUS(s); +} +pub fn WIFEXITED(s: i32) bool { + return WTERMSIG(s) == 0; +} +pub fn WIFSTOPPED(s: i32) bool { + return (u16)(((unsigned(s) & 0xffff) *% 0x10001) >> 8) > 0x7f00; +} +pub fn WIFSIGNALED(s: i32) bool { + return (unsigned(s) & 0xffff) -% 1 < 0xff; +} pub const winsize = extern struct { ws_row: u16, @@ -707,8 +719,7 @@ pub fn umount2(special: &const u8, flags: u32) usize { } pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd), - @bitCast(usize, offset)); + return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd), @bitCast(usize, offset)); } pub fn munmap(address: usize, length: usize) usize { @@ -812,7 +823,8 @@ pub fn clock_gettime(clk_id: i32, tp: ×pec) usize { if (@ptrToInt(f) != 0) { const rc = f(clk_id, tp); switch (rc) { - 0, @bitCast(usize, isize(-EINVAL)) => return rc, + 0, + @bitCast(usize, isize(-EINVAL)) => return rc, else => {}, } } @@ -823,8 +835,7 @@ var vdso_clock_gettime = init_vdso_clock_gettime; extern fn init_vdso_clock_gettime(clk: i32, ts: ×pec) usize { const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM); var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr); - _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f, - builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic); + _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f, builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic); if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS)); return f(clk, ts); } @@ -918,18 +929,18 @@ pub fn getpid() i32 { } pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize { - return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8); + return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); } pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigaction) usize { assert(sig >= 1); assert(sig != SIGKILL); assert(sig != SIGSTOP); - var ksa = k_sigaction { + var ksa = k_sigaction{ .handler = act.handler, .flags = act.flags | SA_RESTORER, .mask = undefined, - .restorer = @ptrCast(extern fn()void, restore_rt), + .restorer = @ptrCast(extern fn() void, restore_rt), }; var ksa_old: k_sigaction = undefined; @memcpy(@ptrCast(&u8, &ksa.mask), @ptrCast(&const u8, &act.mask), 8); @@ -952,22 +963,22 @@ const all_mask = []usize{@maxValue(usize)}; const app_mask = []usize{0xfffffffc7fffffff}; const k_sigaction = extern struct { - handler: extern fn(i32)void, + handler: extern fn(i32) void, flags: usize, - restorer: extern fn()void, + restorer: extern fn() void, mask: [2]u32, }; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = struct { - handler: extern fn(i32)void, + handler: extern fn(i32) void, mask: sigset_t, flags: u32, }; -pub const SIG_ERR = @intToPtr(extern fn(i32)void, @maxValue(usize)); -pub const SIG_DFL = @intToPtr(extern fn(i32)void, 0); -pub const SIG_IGN = @intToPtr(extern fn(i32)void, 1); +pub const SIG_ERR = @intToPtr(extern fn(i32) void, @maxValue(usize)); +pub const SIG_DFL = @intToPtr(extern fn(i32) void, 0); +pub const SIG_IGN = @intToPtr(extern fn(i32) void, 1); pub const empty_sigset = []usize{0} ** sigset_t.len; pub fn raise(sig: i32) usize { @@ -980,25 +991,25 @@ pub fn raise(sig: i32) usize { } fn blockAllSignals(set: &sigset_t) void { - _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG/8); + _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); } fn blockAppSignals(set: &sigset_t) void { - _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG/8); + _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8); } fn restoreSignals(set: &sigset_t) void { - _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG/8); + _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8); } pub fn sigaddset(set: &sigset_t, sig: u6) void { const s = sig - 1; - (*set)[usize(s) / usize.bit_count] |= usize(1) << (s & (usize.bit_count - 1)); + (set.*)[usize(s) / usize.bit_count] |= usize(1) << (s & (usize.bit_count - 1)); } pub fn sigismember(set: &const sigset_t, sig: u6) bool { const s = sig - 1; - return ((*set)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0; + return ((set.*)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0; } pub const in_port_t = u16; @@ -1062,9 +1073,7 @@ pub fn recvmsg(fd: i32, msg: &msghdr, flags: u32) usize { return syscall3(SYS_recvmsg, usize(fd), @ptrToInt(msg), flags); } -pub fn recvfrom(fd: i32, noalias buf: &u8, len: usize, flags: u32, - noalias addr: ?&sockaddr, noalias alen: ?&socklen_t) usize -{ +pub fn recvfrom(fd: i32, noalias buf: &u8, len: usize, flags: u32, noalias addr: ?&sockaddr, noalias alen: ?&socklen_t) usize { return syscall6(SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); } @@ -1132,25 +1141,16 @@ pub fn fgetxattr(fd: usize, name: &const u8, value: &void, size: usize) usize { return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); } -pub fn setxattr(path: &const u8, name: &const u8, value: &const void, - size: usize, flags: usize) usize { - - return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), - size, flags); +pub fn setxattr(path: &const u8, name: &const u8, value: &const void, size: usize, flags: usize) usize { + return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn lsetxattr(path: &const u8, name: &const u8, value: &const void, - size: usize, flags: usize) usize { - - return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), - size, flags); +pub fn lsetxattr(path: &const u8, name: &const u8, value: &const void, size: usize, flags: usize) usize { + return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn fsetxattr(fd: usize, name: &const u8, value: &const void, - size: usize, flags: usize) usize { - - return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), - size, flags); +pub fn fsetxattr(fd: usize, name: &const u8, value: &const void, size: usize, flags: usize) usize { + return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); } pub fn removexattr(path: &const u8, name: &const u8) usize { @@ -1199,7 +1199,7 @@ pub fn timerfd_create(clockid: i32, flags: u32) usize { pub const itimerspec = extern struct { it_interval: timespec, - it_value: timespec + it_value: timespec, }; pub fn timerfd_gettime(fd: i32, curr_value: &itimerspec) usize { @@ -1211,30 +1211,30 @@ pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_va } pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330; -pub const _LINUX_CAPABILITY_U32S_1 = 1; +pub const _LINUX_CAPABILITY_U32S_1 = 1; pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026; -pub const _LINUX_CAPABILITY_U32S_2 = 2; +pub const _LINUX_CAPABILITY_U32S_2 = 2; pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522; -pub const _LINUX_CAPABILITY_U32S_3 = 2; +pub const _LINUX_CAPABILITY_U32S_3 = 2; -pub const VFS_CAP_REVISION_MASK = 0xFF000000; -pub const VFS_CAP_REVISION_SHIFT = 24; -pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK; +pub const VFS_CAP_REVISION_MASK = 0xFF000000; +pub const VFS_CAP_REVISION_SHIFT = 24; +pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK; pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001; pub const VFS_CAP_REVISION_1 = 0x01000000; -pub const VFS_CAP_U32_1 = 1; -pub const XATTR_CAPS_SZ_1 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_1); +pub const VFS_CAP_U32_1 = 1; +pub const XATTR_CAPS_SZ_1 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_1); pub const VFS_CAP_REVISION_2 = 0x02000000; -pub const VFS_CAP_U32_2 = 2; -pub const XATTR_CAPS_SZ_2 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_2); +pub const VFS_CAP_U32_2 = 2; +pub const XATTR_CAPS_SZ_2 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_2); -pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2; -pub const VFS_CAP_U32 = VFS_CAP_U32_2; -pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2; +pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2; +pub const VFS_CAP_U32 = VFS_CAP_U32_2; +pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2; pub const vfs_cap_data = extern struct { //all of these are mandated as little endian @@ -1245,49 +1245,48 @@ pub const vfs_cap_data = extern struct { }; magic_etc: u32, - data: [VFS_CAP_U32]Data, + data: [VFS_CAP_U32]Data, }; - -pub const CAP_CHOWN = 0; -pub const CAP_DAC_OVERRIDE = 1; -pub const CAP_DAC_READ_SEARCH = 2; -pub const CAP_FOWNER = 3; -pub const CAP_FSETID = 4; -pub const CAP_KILL = 5; -pub const CAP_SETGID = 6; -pub const CAP_SETUID = 7; -pub const CAP_SETPCAP = 8; -pub const CAP_LINUX_IMMUTABLE = 9; -pub const CAP_NET_BIND_SERVICE = 10; -pub const CAP_NET_BROADCAST = 11; -pub const CAP_NET_ADMIN = 12; -pub const CAP_NET_RAW = 13; -pub const CAP_IPC_LOCK = 14; -pub const CAP_IPC_OWNER = 15; -pub const CAP_SYS_MODULE = 16; -pub const CAP_SYS_RAWIO = 17; -pub const CAP_SYS_CHROOT = 18; -pub const CAP_SYS_PTRACE = 19; -pub const CAP_SYS_PACCT = 20; -pub const CAP_SYS_ADMIN = 21; -pub const CAP_SYS_BOOT = 22; -pub const CAP_SYS_NICE = 23; -pub const CAP_SYS_RESOURCE = 24; -pub const CAP_SYS_TIME = 25; -pub const CAP_SYS_TTY_CONFIG = 26; -pub const CAP_MKNOD = 27; -pub const CAP_LEASE = 28; -pub const CAP_AUDIT_WRITE = 29; -pub const CAP_AUDIT_CONTROL = 30; -pub const CAP_SETFCAP = 31; -pub const CAP_MAC_OVERRIDE = 32; -pub const CAP_MAC_ADMIN = 33; -pub const CAP_SYSLOG = 34; -pub const CAP_WAKE_ALARM = 35; -pub const CAP_BLOCK_SUSPEND = 36; -pub const CAP_AUDIT_READ = 37; -pub const CAP_LAST_CAP = CAP_AUDIT_READ; +pub const CAP_CHOWN = 0; +pub const CAP_DAC_OVERRIDE = 1; +pub const CAP_DAC_READ_SEARCH = 2; +pub const CAP_FOWNER = 3; +pub const CAP_FSETID = 4; +pub const CAP_KILL = 5; +pub const CAP_SETGID = 6; +pub const CAP_SETUID = 7; +pub const CAP_SETPCAP = 8; +pub const CAP_LINUX_IMMUTABLE = 9; +pub const CAP_NET_BIND_SERVICE = 10; +pub const CAP_NET_BROADCAST = 11; +pub const CAP_NET_ADMIN = 12; +pub const CAP_NET_RAW = 13; +pub const CAP_IPC_LOCK = 14; +pub const CAP_IPC_OWNER = 15; +pub const CAP_SYS_MODULE = 16; +pub const CAP_SYS_RAWIO = 17; +pub const CAP_SYS_CHROOT = 18; +pub const CAP_SYS_PTRACE = 19; +pub const CAP_SYS_PACCT = 20; +pub const CAP_SYS_ADMIN = 21; +pub const CAP_SYS_BOOT = 22; +pub const CAP_SYS_NICE = 23; +pub const CAP_SYS_RESOURCE = 24; +pub const CAP_SYS_TIME = 25; +pub const CAP_SYS_TTY_CONFIG = 26; +pub const CAP_MKNOD = 27; +pub const CAP_LEASE = 28; +pub const CAP_AUDIT_WRITE = 29; +pub const CAP_AUDIT_CONTROL = 30; +pub const CAP_SETFCAP = 31; +pub const CAP_MAC_OVERRIDE = 32; +pub const CAP_MAC_ADMIN = 33; +pub const CAP_SYSLOG = 34; +pub const CAP_WAKE_ALARM = 35; +pub const CAP_BLOCK_SUSPEND = 36; +pub const CAP_AUDIT_READ = 37; +pub const CAP_LAST_CAP = CAP_AUDIT_READ; pub fn cap_valid(u8: x) bool { return x >= 0 and x <= CAP_LAST_CAP; diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 1dc7e24869..056ab35003 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -27,10 +27,10 @@ extern fn zen_start() noreturn { nakedcc fn _start() noreturn { switch (builtin.arch) { builtin.Arch.x86_64 => { - argc_ptr = asm("lea (%%rsp), %[argc]": [argc] "=r" (-> &usize)); + argc_ptr = asm ("lea (%%rsp), %[argc]" : [argc] "=r" (-> &usize)); }, builtin.Arch.i386 => { - argc_ptr = asm("lea (%%esp), %[argc]": [argc] "=r" (-> &usize)); + argc_ptr = asm ("lea (%%esp), %[argc]" : [argc] "=r" (-> &usize)); }, else => @compileError("unsupported arch"), } @@ -46,7 +46,7 @@ extern fn WinMainCRTStartup() noreturn { } fn posixCallMainAndExit() noreturn { - const argc = *argc_ptr; + const argc = argc_ptr.*; const argv = @ptrCast(&&u8, &argc_ptr[1]); const envp_nullable = @ptrCast(&?&u8, &argv[argc + 1]); var envp_count: usize = 0; @@ -56,7 +56,7 @@ fn posixCallMainAndExit() noreturn { const auxv = &@ptrCast(&usize, envp.ptr)[envp_count + 1]; var i: usize = 0; while (auxv[i] != 0) : (i += 2) { - if (auxv[i] < std.os.linux_aux_raw.len) std.os.linux_aux_raw[auxv[i]] = auxv[i+1]; + if (auxv[i] < std.os.linux_aux_raw.len) std.os.linux_aux_raw[auxv[i]] = auxv[i + 1]; } std.debug.assert(std.os.linux_aux_raw[std.elf.AT_PAGESZ] == std.os.page_size); } diff --git a/std/special/compiler_rt/fixuint.zig b/std/special/compiler_rt/fixuint.zig index b01bc48118..d9a7393fe4 100644 --- a/std/special/compiler_rt/fixuint.zig +++ b/std/special/compiler_rt/fixuint.zig @@ -36,12 +36,10 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t const significand: rep_t = (aAbs & significandMask) | implicitBit; // If either the value or the exponent is negative, the result is zero. - if (sign == -1 or exponent < 0) - return 0; + if (sign == -1 or exponent < 0) return 0; // If the value is too large for the integer type, saturate. - if (c_uint(exponent) >= fixuint_t.bit_count) - return ~fixuint_t(0); + if (c_uint(exponent) >= fixuint_t.bit_count) return ~fixuint_t(0); // If 0 <= exponent < significandBits, right shift to get the result. // Otherwise, shift left. diff --git a/std/special/compiler_rt/fixunsdfdi.zig b/std/special/compiler_rt/fixunsdfdi.zig index 37a8a01a50..1fa7ed758e 100644 --- a/std/special/compiler_rt/fixunsdfdi.zig +++ b/std/special/compiler_rt/fixunsdfdi.zig @@ -9,4 +9,3 @@ pub extern fn __fixunsdfdi(a: f64) u64 { test "import fixunsdfdi" { _ = @import("fixunsdfdi_test.zig"); } - diff --git a/std/special/compiler_rt/fixunsdfsi.zig b/std/special/compiler_rt/fixunsdfsi.zig index 0b5aebb7f6..a77cb8df89 100644 --- a/std/special/compiler_rt/fixunsdfsi.zig +++ b/std/special/compiler_rt/fixunsdfsi.zig @@ -9,4 +9,3 @@ pub extern fn __fixunsdfsi(a: f64) u32 { test "import fixunsdfsi" { _ = @import("fixunsdfsi_test.zig"); } - diff --git a/std/special/compiler_rt/fixunssfti.zig b/std/special/compiler_rt/fixunssfti.zig index 0abd73fe76..f0cd788d2e 100644 --- a/std/special/compiler_rt/fixunssfti.zig +++ b/std/special/compiler_rt/fixunssfti.zig @@ -9,4 +9,3 @@ pub extern fn __fixunssfti(a: f32) u128 { test "import fixunssfti" { _ = @import("fixunssfti_test.zig"); } - diff --git a/std/special/compiler_rt/fixunstfti.zig b/std/special/compiler_rt/fixunstfti.zig index fea99eb6e8..cd6178164a 100644 --- a/std/special/compiler_rt/fixunstfti.zig +++ b/std/special/compiler_rt/fixunstfti.zig @@ -9,4 +9,3 @@ pub extern fn __fixunstfti(a: f128) u128 { test "import fixunstfti" { _ = @import("fixunstfti_test.zig"); } - diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index 6ef43c4fed..44466a407d 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -91,9 +91,10 @@ pub fn setXmm0(comptime T: type, value: T) void { const aligned_value: T align(16) = value; asm volatile ( \\movaps (%[ptr]), %%xmm0 - : - : [ptr] "r" (&aligned_value) - : "xmm0"); + + : + : [ptr] "r" (&aligned_value) + : "xmm0"); } extern fn __udivdi3(a: u64, b: u64) u64 { @@ -282,26 +283,27 @@ extern fn __udivmodsi4(a: u32, b: u32, rem: &u32) u32 { @setRuntimeSafety(is_test); const d = __udivsi3(a, b); - *rem = u32(i32(a) -% (i32(d) * i32(b))); + rem.* = u32(i32(a) -% (i32(d) * i32(b))); return d; } - extern fn __udivsi3(n: u32, d: u32) u32 { @setRuntimeSafety(is_test); const n_uword_bits: c_uint = u32.bit_count; // special cases - if (d == 0) - return 0; // ?! - if (n == 0) - return 0; + if (d == 0) return 0; // ?! + if (n == 0) return 0; var sr = @bitCast(c_uint, c_int(@clz(d)) - c_int(@clz(n))); // 0 <= sr <= n_uword_bits - 1 or sr large - if (sr > n_uword_bits - 1) // d > r + if (sr > n_uword_bits - 1) { + // d > r return 0; - if (sr == n_uword_bits - 1) // d == 1 + } + if (sr == n_uword_bits - 1) { + // d == 1 return n; + } sr += 1; // 1 <= sr <= n_uword_bits - 1 // Not a special case @@ -340,139 +342,667 @@ fn test_one_umoddi3(a: u64, b: u64, expected_r: u64) void { } test "test_udivsi3" { - const cases = [][3]u32 { - []u32{0x00000000, 0x00000001, 0x00000000}, - []u32{0x00000000, 0x00000002, 0x00000000}, - []u32{0x00000000, 0x00000003, 0x00000000}, - []u32{0x00000000, 0x00000010, 0x00000000}, - []u32{0x00000000, 0x078644FA, 0x00000000}, - []u32{0x00000000, 0x0747AE14, 0x00000000}, - []u32{0x00000000, 0x7FFFFFFF, 0x00000000}, - []u32{0x00000000, 0x80000000, 0x00000000}, - []u32{0x00000000, 0xFFFFFFFD, 0x00000000}, - []u32{0x00000000, 0xFFFFFFFE, 0x00000000}, - []u32{0x00000000, 0xFFFFFFFF, 0x00000000}, - []u32{0x00000001, 0x00000001, 0x00000001}, - []u32{0x00000001, 0x00000002, 0x00000000}, - []u32{0x00000001, 0x00000003, 0x00000000}, - []u32{0x00000001, 0x00000010, 0x00000000}, - []u32{0x00000001, 0x078644FA, 0x00000000}, - []u32{0x00000001, 0x0747AE14, 0x00000000}, - []u32{0x00000001, 0x7FFFFFFF, 0x00000000}, - []u32{0x00000001, 0x80000000, 0x00000000}, - []u32{0x00000001, 0xFFFFFFFD, 0x00000000}, - []u32{0x00000001, 0xFFFFFFFE, 0x00000000}, - []u32{0x00000001, 0xFFFFFFFF, 0x00000000}, - []u32{0x00000002, 0x00000001, 0x00000002}, - []u32{0x00000002, 0x00000002, 0x00000001}, - []u32{0x00000002, 0x00000003, 0x00000000}, - []u32{0x00000002, 0x00000010, 0x00000000}, - []u32{0x00000002, 0x078644FA, 0x00000000}, - []u32{0x00000002, 0x0747AE14, 0x00000000}, - []u32{0x00000002, 0x7FFFFFFF, 0x00000000}, - []u32{0x00000002, 0x80000000, 0x00000000}, - []u32{0x00000002, 0xFFFFFFFD, 0x00000000}, - []u32{0x00000002, 0xFFFFFFFE, 0x00000000}, - []u32{0x00000002, 0xFFFFFFFF, 0x00000000}, - []u32{0x00000003, 0x00000001, 0x00000003}, - []u32{0x00000003, 0x00000002, 0x00000001}, - []u32{0x00000003, 0x00000003, 0x00000001}, - []u32{0x00000003, 0x00000010, 0x00000000}, - []u32{0x00000003, 0x078644FA, 0x00000000}, - []u32{0x00000003, 0x0747AE14, 0x00000000}, - []u32{0x00000003, 0x7FFFFFFF, 0x00000000}, - []u32{0x00000003, 0x80000000, 0x00000000}, - []u32{0x00000003, 0xFFFFFFFD, 0x00000000}, - []u32{0x00000003, 0xFFFFFFFE, 0x00000000}, - []u32{0x00000003, 0xFFFFFFFF, 0x00000000}, - []u32{0x00000010, 0x00000001, 0x00000010}, - []u32{0x00000010, 0x00000002, 0x00000008}, - []u32{0x00000010, 0x00000003, 0x00000005}, - []u32{0x00000010, 0x00000010, 0x00000001}, - []u32{0x00000010, 0x078644FA, 0x00000000}, - []u32{0x00000010, 0x0747AE14, 0x00000000}, - []u32{0x00000010, 0x7FFFFFFF, 0x00000000}, - []u32{0x00000010, 0x80000000, 0x00000000}, - []u32{0x00000010, 0xFFFFFFFD, 0x00000000}, - []u32{0x00000010, 0xFFFFFFFE, 0x00000000}, - []u32{0x00000010, 0xFFFFFFFF, 0x00000000}, - []u32{0x078644FA, 0x00000001, 0x078644FA}, - []u32{0x078644FA, 0x00000002, 0x03C3227D}, - []u32{0x078644FA, 0x00000003, 0x028216FE}, - []u32{0x078644FA, 0x00000010, 0x0078644F}, - []u32{0x078644FA, 0x078644FA, 0x00000001}, - []u32{0x078644FA, 0x0747AE14, 0x00000001}, - []u32{0x078644FA, 0x7FFFFFFF, 0x00000000}, - []u32{0x078644FA, 0x80000000, 0x00000000}, - []u32{0x078644FA, 0xFFFFFFFD, 0x00000000}, - []u32{0x078644FA, 0xFFFFFFFE, 0x00000000}, - []u32{0x078644FA, 0xFFFFFFFF, 0x00000000}, - []u32{0x0747AE14, 0x00000001, 0x0747AE14}, - []u32{0x0747AE14, 0x00000002, 0x03A3D70A}, - []u32{0x0747AE14, 0x00000003, 0x026D3A06}, - []u32{0x0747AE14, 0x00000010, 0x00747AE1}, - []u32{0x0747AE14, 0x078644FA, 0x00000000}, - []u32{0x0747AE14, 0x0747AE14, 0x00000001}, - []u32{0x0747AE14, 0x7FFFFFFF, 0x00000000}, - []u32{0x0747AE14, 0x80000000, 0x00000000}, - []u32{0x0747AE14, 0xFFFFFFFD, 0x00000000}, - []u32{0x0747AE14, 0xFFFFFFFE, 0x00000000}, - []u32{0x0747AE14, 0xFFFFFFFF, 0x00000000}, - []u32{0x7FFFFFFF, 0x00000001, 0x7FFFFFFF}, - []u32{0x7FFFFFFF, 0x00000002, 0x3FFFFFFF}, - []u32{0x7FFFFFFF, 0x00000003, 0x2AAAAAAA}, - []u32{0x7FFFFFFF, 0x00000010, 0x07FFFFFF}, - []u32{0x7FFFFFFF, 0x078644FA, 0x00000011}, - []u32{0x7FFFFFFF, 0x0747AE14, 0x00000011}, - []u32{0x7FFFFFFF, 0x7FFFFFFF, 0x00000001}, - []u32{0x7FFFFFFF, 0x80000000, 0x00000000}, - []u32{0x7FFFFFFF, 0xFFFFFFFD, 0x00000000}, - []u32{0x7FFFFFFF, 0xFFFFFFFE, 0x00000000}, - []u32{0x7FFFFFFF, 0xFFFFFFFF, 0x00000000}, - []u32{0x80000000, 0x00000001, 0x80000000}, - []u32{0x80000000, 0x00000002, 0x40000000}, - []u32{0x80000000, 0x00000003, 0x2AAAAAAA}, - []u32{0x80000000, 0x00000010, 0x08000000}, - []u32{0x80000000, 0x078644FA, 0x00000011}, - []u32{0x80000000, 0x0747AE14, 0x00000011}, - []u32{0x80000000, 0x7FFFFFFF, 0x00000001}, - []u32{0x80000000, 0x80000000, 0x00000001}, - []u32{0x80000000, 0xFFFFFFFD, 0x00000000}, - []u32{0x80000000, 0xFFFFFFFE, 0x00000000}, - []u32{0x80000000, 0xFFFFFFFF, 0x00000000}, - []u32{0xFFFFFFFD, 0x00000001, 0xFFFFFFFD}, - []u32{0xFFFFFFFD, 0x00000002, 0x7FFFFFFE}, - []u32{0xFFFFFFFD, 0x00000003, 0x55555554}, - []u32{0xFFFFFFFD, 0x00000010, 0x0FFFFFFF}, - []u32{0xFFFFFFFD, 0x078644FA, 0x00000022}, - []u32{0xFFFFFFFD, 0x0747AE14, 0x00000023}, - []u32{0xFFFFFFFD, 0x7FFFFFFF, 0x00000001}, - []u32{0xFFFFFFFD, 0x80000000, 0x00000001}, - []u32{0xFFFFFFFD, 0xFFFFFFFD, 0x00000001}, - []u32{0xFFFFFFFD, 0xFFFFFFFE, 0x00000000}, - []u32{0xFFFFFFFD, 0xFFFFFFFF, 0x00000000}, - []u32{0xFFFFFFFE, 0x00000001, 0xFFFFFFFE}, - []u32{0xFFFFFFFE, 0x00000002, 0x7FFFFFFF}, - []u32{0xFFFFFFFE, 0x00000003, 0x55555554}, - []u32{0xFFFFFFFE, 0x00000010, 0x0FFFFFFF}, - []u32{0xFFFFFFFE, 0x078644FA, 0x00000022}, - []u32{0xFFFFFFFE, 0x0747AE14, 0x00000023}, - []u32{0xFFFFFFFE, 0x7FFFFFFF, 0x00000002}, - []u32{0xFFFFFFFE, 0x80000000, 0x00000001}, - []u32{0xFFFFFFFE, 0xFFFFFFFD, 0x00000001}, - []u32{0xFFFFFFFE, 0xFFFFFFFE, 0x00000001}, - []u32{0xFFFFFFFE, 0xFFFFFFFF, 0x00000000}, - []u32{0xFFFFFFFF, 0x00000001, 0xFFFFFFFF}, - []u32{0xFFFFFFFF, 0x00000002, 0x7FFFFFFF}, - []u32{0xFFFFFFFF, 0x00000003, 0x55555555}, - []u32{0xFFFFFFFF, 0x00000010, 0x0FFFFFFF}, - []u32{0xFFFFFFFF, 0x078644FA, 0x00000022}, - []u32{0xFFFFFFFF, 0x0747AE14, 0x00000023}, - []u32{0xFFFFFFFF, 0x7FFFFFFF, 0x00000002}, - []u32{0xFFFFFFFF, 0x80000000, 0x00000001}, - []u32{0xFFFFFFFF, 0xFFFFFFFD, 0x00000001}, - []u32{0xFFFFFFFF, 0xFFFFFFFE, 0x00000001}, - []u32{0xFFFFFFFF, 0xFFFFFFFF, 0x00000001}, + const cases = [][3]u32{ + []u32{ + 0x00000000, + 0x00000001, + 0x00000000, + }, + []u32{ + 0x00000000, + 0x00000002, + 0x00000000, + }, + []u32{ + 0x00000000, + 0x00000003, + 0x00000000, + }, + []u32{ + 0x00000000, + 0x00000010, + 0x00000000, + }, + []u32{ + 0x00000000, + 0x078644FA, + 0x00000000, + }, + []u32{ + 0x00000000, + 0x0747AE14, + 0x00000000, + }, + []u32{ + 0x00000000, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000000, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x00000000, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x00000000, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x00000000, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x00000001, + 0x00000001, + }, + []u32{ + 0x00000001, + 0x00000002, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x00000003, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x00000010, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x078644FA, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x0747AE14, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x00000001, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x00000001, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x00000001, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000002, + 0x00000001, + 0x00000002, + }, + []u32{ + 0x00000002, + 0x00000002, + 0x00000001, + }, + []u32{ + 0x00000002, + 0x00000003, + 0x00000000, + }, + []u32{ + 0x00000002, + 0x00000010, + 0x00000000, + }, + []u32{ + 0x00000002, + 0x078644FA, + 0x00000000, + }, + []u32{ + 0x00000002, + 0x0747AE14, + 0x00000000, + }, + []u32{ + 0x00000002, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000002, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x00000002, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x00000002, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x00000002, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000003, + 0x00000001, + 0x00000003, + }, + []u32{ + 0x00000003, + 0x00000002, + 0x00000001, + }, + []u32{ + 0x00000003, + 0x00000003, + 0x00000001, + }, + []u32{ + 0x00000003, + 0x00000010, + 0x00000000, + }, + []u32{ + 0x00000003, + 0x078644FA, + 0x00000000, + }, + []u32{ + 0x00000003, + 0x0747AE14, + 0x00000000, + }, + []u32{ + 0x00000003, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000003, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x00000003, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x00000003, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x00000003, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000010, + 0x00000001, + 0x00000010, + }, + []u32{ + 0x00000010, + 0x00000002, + 0x00000008, + }, + []u32{ + 0x00000010, + 0x00000003, + 0x00000005, + }, + []u32{ + 0x00000010, + 0x00000010, + 0x00000001, + }, + []u32{ + 0x00000010, + 0x078644FA, + 0x00000000, + }, + []u32{ + 0x00000010, + 0x0747AE14, + 0x00000000, + }, + []u32{ + 0x00000010, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000010, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x00000010, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x00000010, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x00000010, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x078644FA, + 0x00000001, + 0x078644FA, + }, + []u32{ + 0x078644FA, + 0x00000002, + 0x03C3227D, + }, + []u32{ + 0x078644FA, + 0x00000003, + 0x028216FE, + }, + []u32{ + 0x078644FA, + 0x00000010, + 0x0078644F, + }, + []u32{ + 0x078644FA, + 0x078644FA, + 0x00000001, + }, + []u32{ + 0x078644FA, + 0x0747AE14, + 0x00000001, + }, + []u32{ + 0x078644FA, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x078644FA, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x078644FA, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x078644FA, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x078644FA, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x0747AE14, + 0x00000001, + 0x0747AE14, + }, + []u32{ + 0x0747AE14, + 0x00000002, + 0x03A3D70A, + }, + []u32{ + 0x0747AE14, + 0x00000003, + 0x026D3A06, + }, + []u32{ + 0x0747AE14, + 0x00000010, + 0x00747AE1, + }, + []u32{ + 0x0747AE14, + 0x078644FA, + 0x00000000, + }, + []u32{ + 0x0747AE14, + 0x0747AE14, + 0x00000001, + }, + []u32{ + 0x0747AE14, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x0747AE14, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x0747AE14, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x0747AE14, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x0747AE14, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x7FFFFFFF, + 0x00000001, + 0x7FFFFFFF, + }, + []u32{ + 0x7FFFFFFF, + 0x00000002, + 0x3FFFFFFF, + }, + []u32{ + 0x7FFFFFFF, + 0x00000003, + 0x2AAAAAAA, + }, + []u32{ + 0x7FFFFFFF, + 0x00000010, + 0x07FFFFFF, + }, + []u32{ + 0x7FFFFFFF, + 0x078644FA, + 0x00000011, + }, + []u32{ + 0x7FFFFFFF, + 0x0747AE14, + 0x00000011, + }, + []u32{ + 0x7FFFFFFF, + 0x7FFFFFFF, + 0x00000001, + }, + []u32{ + 0x7FFFFFFF, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x7FFFFFFF, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x7FFFFFFF, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x7FFFFFFF, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x80000000, + 0x00000001, + 0x80000000, + }, + []u32{ + 0x80000000, + 0x00000002, + 0x40000000, + }, + []u32{ + 0x80000000, + 0x00000003, + 0x2AAAAAAA, + }, + []u32{ + 0x80000000, + 0x00000010, + 0x08000000, + }, + []u32{ + 0x80000000, + 0x078644FA, + 0x00000011, + }, + []u32{ + 0x80000000, + 0x0747AE14, + 0x00000011, + }, + []u32{ + 0x80000000, + 0x7FFFFFFF, + 0x00000001, + }, + []u32{ + 0x80000000, + 0x80000000, + 0x00000001, + }, + []u32{ + 0x80000000, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x80000000, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x80000000, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0xFFFFFFFD, + 0x00000001, + 0xFFFFFFFD, + }, + []u32{ + 0xFFFFFFFD, + 0x00000002, + 0x7FFFFFFE, + }, + []u32{ + 0xFFFFFFFD, + 0x00000003, + 0x55555554, + }, + []u32{ + 0xFFFFFFFD, + 0x00000010, + 0x0FFFFFFF, + }, + []u32{ + 0xFFFFFFFD, + 0x078644FA, + 0x00000022, + }, + []u32{ + 0xFFFFFFFD, + 0x0747AE14, + 0x00000023, + }, + []u32{ + 0xFFFFFFFD, + 0x7FFFFFFF, + 0x00000001, + }, + []u32{ + 0xFFFFFFFD, + 0x80000000, + 0x00000001, + }, + []u32{ + 0xFFFFFFFD, + 0xFFFFFFFD, + 0x00000001, + }, + []u32{ + 0xFFFFFFFD, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0xFFFFFFFD, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0xFFFFFFFE, + 0x00000001, + 0xFFFFFFFE, + }, + []u32{ + 0xFFFFFFFE, + 0x00000002, + 0x7FFFFFFF, + }, + []u32{ + 0xFFFFFFFE, + 0x00000003, + 0x55555554, + }, + []u32{ + 0xFFFFFFFE, + 0x00000010, + 0x0FFFFFFF, + }, + []u32{ + 0xFFFFFFFE, + 0x078644FA, + 0x00000022, + }, + []u32{ + 0xFFFFFFFE, + 0x0747AE14, + 0x00000023, + }, + []u32{ + 0xFFFFFFFE, + 0x7FFFFFFF, + 0x00000002, + }, + []u32{ + 0xFFFFFFFE, + 0x80000000, + 0x00000001, + }, + []u32{ + 0xFFFFFFFE, + 0xFFFFFFFD, + 0x00000001, + }, + []u32{ + 0xFFFFFFFE, + 0xFFFFFFFE, + 0x00000001, + }, + []u32{ + 0xFFFFFFFE, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0xFFFFFFFF, + 0x00000001, + 0xFFFFFFFF, + }, + []u32{ + 0xFFFFFFFF, + 0x00000002, + 0x7FFFFFFF, + }, + []u32{ + 0xFFFFFFFF, + 0x00000003, + 0x55555555, + }, + []u32{ + 0xFFFFFFFF, + 0x00000010, + 0x0FFFFFFF, + }, + []u32{ + 0xFFFFFFFF, + 0x078644FA, + 0x00000022, + }, + []u32{ + 0xFFFFFFFF, + 0x0747AE14, + 0x00000023, + }, + []u32{ + 0xFFFFFFFF, + 0x7FFFFFFF, + 0x00000002, + }, + []u32{ + 0xFFFFFFFF, + 0x80000000, + 0x00000001, + }, + []u32{ + 0xFFFFFFFF, + 0xFFFFFFFD, + 0x00000001, + }, + []u32{ + 0xFFFFFFFF, + 0xFFFFFFFE, + 0x00000001, + }, + []u32{ + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x00000001, + }, }; for (cases) |case| { diff --git a/std/special/compiler_rt/udivmod.zig b/std/special/compiler_rt/udivmod.zig index 07eaef583c..e8a86c5c44 100644 --- a/std/special/compiler_rt/udivmod.zig +++ b/std/special/compiler_rt/udivmod.zig @@ -1,7 +1,10 @@ const builtin = @import("builtin"); const is_test = builtin.is_test; -const low = switch (builtin.endian) { builtin.Endian.Big => 1, builtin.Endian.Little => 0 }; +const low = switch (builtin.endian) { + builtin.Endian.Big => 1, + builtin.Endian.Little => 0, +}; const high = 1 - low; pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: ?&DoubleInt) DoubleInt { @@ -11,8 +14,8 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: const SignedDoubleInt = @IntType(true, DoubleInt.bit_count); const Log2SingleInt = @import("../../math/index.zig").Log2Int(SingleInt); - const n = *@ptrCast(&const [2]SingleInt, &a); // TODO issue #421 - const d = *@ptrCast(&const [2]SingleInt, &b); // TODO issue #421 + const n = @ptrCast(&const [2]SingleInt, &a).*; // TODO issue #421 + const d = @ptrCast(&const [2]SingleInt, &b).*; // TODO issue #421 var q: [2]SingleInt = undefined; var r: [2]SingleInt = undefined; var sr: c_uint = undefined; @@ -23,7 +26,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // --- // 0 X if (maybe_rem) |rem| { - *rem = n[low] % d[low]; + rem.* = n[low] % d[low]; } return n[low] / d[low]; } @@ -31,7 +34,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // --- // K X if (maybe_rem) |rem| { - *rem = n[low]; + rem.* = n[low]; } return 0; } @@ -42,7 +45,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // --- // 0 0 if (maybe_rem) |rem| { - *rem = n[high] % d[low]; + rem.* = n[high] % d[low]; } return n[high] / d[low]; } @@ -54,7 +57,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: if (maybe_rem) |rem| { r[high] = n[high] % d[high]; r[low] = 0; - *rem = *@ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &r[0]); // TODO issue #421 + rem.* = @ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 } return n[high] / d[high]; } @@ -66,7 +69,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: if (maybe_rem) |rem| { r[low] = n[low]; r[high] = n[high] & (d[high] - 1); - *rem = *@ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &r[0]); // TODO issue #421 + rem.* = @ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 } return n[high] >> Log2SingleInt(@ctz(d[high])); } @@ -77,7 +80,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // 0 <= sr <= SingleInt.bit_count - 2 or sr large if (sr > SingleInt.bit_count - 2) { if (maybe_rem) |rem| { - *rem = a; + rem.* = a; } return 0; } @@ -98,7 +101,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: if ((d[low] & (d[low] - 1)) == 0) { // d is a power of 2 if (maybe_rem) |rem| { - *rem = n[low] & (d[low] - 1); + rem.* = n[low] & (d[low] - 1); } if (d[low] == 1) { return a; @@ -106,7 +109,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: sr = @ctz(d[low]); q[high] = n[high] >> Log2SingleInt(sr); q[low] = (n[high] << Log2SingleInt(SingleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr)); - return *@ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &q[0]); // TODO issue #421 + return @ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &q[0]).*; // TODO issue #421 } // K X // --- @@ -141,7 +144,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // 0 <= sr <= SingleInt.bit_count - 1 or sr large if (sr > SingleInt.bit_count - 1) { if (maybe_rem) |rem| { - *rem = a; + rem.* = a; } return 0; } @@ -170,25 +173,25 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: var r_all: DoubleInt = undefined; while (sr > 0) : (sr -= 1) { // r:q = ((r:q) << 1) | carry - r[high] = (r[high] << 1) | (r[low] >> (SingleInt.bit_count - 1)); - r[low] = (r[low] << 1) | (q[high] >> (SingleInt.bit_count - 1)); - q[high] = (q[high] << 1) | (q[low] >> (SingleInt.bit_count - 1)); - q[low] = (q[low] << 1) | carry; + r[high] = (r[high] << 1) | (r[low] >> (SingleInt.bit_count - 1)); + r[low] = (r[low] << 1) | (q[high] >> (SingleInt.bit_count - 1)); + q[high] = (q[high] << 1) | (q[low] >> (SingleInt.bit_count - 1)); + q[low] = (q[low] << 1) | carry; // carry = 0; // if (r.all >= b) // { // r.all -= b; // carry = 1; // } - r_all = *@ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &r[0]); // TODO issue #421 + r_all = @ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 const s: SignedDoubleInt = SignedDoubleInt(b -% r_all -% 1) >> (DoubleInt.bit_count - 1); carry = u32(s & 1); r_all -= b & @bitCast(DoubleInt, s); - r = *@ptrCast(&[2]SingleInt, &r_all); // TODO issue #421 + r = @ptrCast(&[2]SingleInt, &r_all).*; // TODO issue #421 } - const q_all = ((*@ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &q[0])) << 1) | carry; // TODO issue #421 + const q_all = ((@ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &q[0]).*) << 1) | carry; // TODO issue #421 if (maybe_rem) |rem| { - *rem = r_all; + rem.* = r_all; } return q_all; } diff --git a/std/special/compiler_rt/udivmodti4.zig b/std/special/compiler_rt/udivmodti4.zig index f8fdebe4db..816f82b900 100644 --- a/std/special/compiler_rt/udivmodti4.zig +++ b/std/special/compiler_rt/udivmodti4.zig @@ -9,7 +9,7 @@ pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 { pub extern fn __udivmodti4_windows_x86_64(a: &const u128, b: &const u128, maybe_rem: ?&u128) void { @setRuntimeSafety(builtin.is_test); - compiler_rt.setXmm0(u128, udivmod(u128, *a, *b, maybe_rem)); + compiler_rt.setXmm0(u128, udivmod(u128, a.*, b.*, maybe_rem)); } test "import udivmodti4" { diff --git a/std/special/compiler_rt/umodti3.zig b/std/special/compiler_rt/umodti3.zig index 3e8b80058e..11e2955bb3 100644 --- a/std/special/compiler_rt/umodti3.zig +++ b/std/special/compiler_rt/umodti3.zig @@ -11,5 +11,5 @@ pub extern fn __umodti3(a: u128, b: u128) u128 { pub extern fn __umodti3_windows_x86_64(a: &const u128, b: &const u128) void { @setRuntimeSafety(builtin.is_test); - compiler_rt.setXmm0(u128, __umodti3(*a, *b)); + compiler_rt.setXmm0(u128, __umodti3(a.*, b.*)); } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 547cca5797..8b6afb4310 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -14,7 +14,7 @@ test "integer literal to pointer cast" { } test "pointer reinterpret const float to int" { - const float: f64 = 5.99999999999994648725e - 01; + const float: f64 = 5.99999999999994648725e-01; const float_ptr = &float; const int_ptr = @ptrCast(&const i32, float_ptr); const int_val = int_ptr.*; @@ -121,13 +121,13 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" { return (p.*).x; } fn maybeConstConst(p: ?&const &const Self) u8 { - return (??p.*).x; + return ((??p).*).x; } fn constConstConst(p: &const &const &const Self) u8 { return (p.*.*).x; } fn maybeConstConstConst(p: ?&const &const &const Self) u8 { - return (??p.*.*).x; + return ((??p).*.*).x; } }; const s = S { diff --git a/test/cases/generics.zig b/test/cases/generics.zig index da8a7dcad6..3fb33d0495 100644 --- a/test/cases/generics.zig +++ b/test/cases/generics.zig @@ -127,7 +127,7 @@ test "generic fn with implicit cast" { }) == 0); } fn getByte(ptr: ?&const u8) u8 { - return ??ptr.*; + return (??ptr).*; } fn getFirstByte(comptime T: type, mem: []const T) u8 { return getByte(@ptrCast(&const u8, &mem[0])); -- cgit v1.2.3 From ff1c4e1f13943b63dcb6d257a2ee58ae88d4b12a Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 1 May 2018 13:00:39 +0300 Subject: Added tests. --- src/ir.cpp | 1 - test/cases/union.zig | 10 ++++++++++ test/compile_errors.zig | 12 ++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 3bce9c51cd..dfffb41873 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15975,7 +15975,6 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop ensure_field_index(fn_def_val->type, "return_type", 7); fn_def_fields[7].special = ConstValSpecialStatic; fn_def_fields[7].type = ira->codegen->builtin_types.entry_type; - // @TODO Check whether this is correct. if (fn_entry->src_implicit_return_type != nullptr) fn_def_fields[7].data.x_type = fn_entry->src_implicit_return_type; else if (fn_entry->type_entry->data.fn.gen_return_type != nullptr) diff --git a/test/cases/union.zig b/test/cases/union.zig index dc2a7c3414..e7d9c23d77 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -45,6 +45,16 @@ test "basic unions" { assert(foo.float == 12.34); } +test "comptime union field access" { + comptime { + var foo = Foo { .int = 0 }; + assert(foo.int == 0); + + foo = Foo { .float = 42.42 }; + assert(foo.float == 42.42); + } +} + test "init union with runtime value" { var foo: Foo = undefined; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f8febc27b8..d9454adf2c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3209,4 +3209,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset"); + + cases.add("invalid union field access in comptime", + \\const Foo = union { + \\ Bar: u8, + \\ Baz: void, + \\}; + \\comptime { + \\ var foo = Foo {.Baz = {}}; + \\ const bar_val = foo.Bar; + \\} + , + ".tmp_source.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set"); } -- cgit v1.2.3 From e1535ee0a9f9d01d16a79c380b493f676ae57121 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 1 May 2018 13:01:54 +0300 Subject: Added typeInfo tests --- test/cases/type_info.zig | 181 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 test/cases/type_info.zig diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig new file mode 100644 index 0000000000..8e6721beae --- /dev/null +++ b/test/cases/type_info.zig @@ -0,0 +1,181 @@ +const assert = @import("std").debug.assert; +const mem = @import("std").mem; +const TypeInfo = @import("builtin").TypeInfo; +const TypeId = @import("builtin").TypeId; + +test "type info: tag type, void info" { + comptime { + assert(@TagType(TypeInfo) == TypeId); + const void_info = @typeInfo(void); + assert(TypeId(void_info) == TypeId.Void); + assert(void_info.Void == {}); + } +} + +test "type info: integer, floating point type info" { + comptime { + const u8_info = @typeInfo(u8); + assert(TypeId(u8_info) == TypeId.Int); + assert(!u8_info.Int.is_signed); + assert(u8_info.Int.bits == 8); + + const f64_info = @typeInfo(f64); + assert(TypeId(f64_info) == TypeId.Float); + assert(f64_info.Float.bits == 64); + } +} + +test "type info: pointer, array and nullable type info" { + comptime { + const u32_ptr_info = @typeInfo(&u32); + assert(TypeId(u32_ptr_info) == TypeId.Pointer); + assert(u32_ptr_info.Pointer.is_const == false); + assert(u32_ptr_info.Pointer.is_volatile == false); + assert(u32_ptr_info.Pointer.alignment == 4); + assert(u32_ptr_info.Pointer.child == u32); + + const arr_info = @typeInfo([42]bool); + assert(TypeId(arr_info) == TypeId.Array); + assert(arr_info.Array.len == 42); + assert(arr_info.Array.child == bool); + + const null_info = @typeInfo(?void); + assert(TypeId(null_info) == TypeId.Nullable); + assert(null_info.Nullable.child == void); + } +} + +test "type info: promise info" { + comptime { + const null_promise_info = @typeInfo(promise); + assert(TypeId(null_promise_info) == TypeId.Promise); + assert(null_promise_info.Promise.child == @typeOf(undefined)); + + const promise_info = @typeInfo(promise->usize); + assert(TypeId(promise_info) == TypeId.Promise); + assert(promise_info.Promise.child == usize); + } + +} + +test "type info: error set, error union info" { + comptime { + const TestErrorSet = error { + First, + Second, + Third, + }; + + const error_set_info = @typeInfo(TestErrorSet); + assert(TypeId(error_set_info) == TypeId.ErrorSet); + assert(error_set_info.ErrorSet.errors.len == 3); + assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); + assert(error_set_info.ErrorSet.errors[2].value == 3); + + const error_union_info = @typeInfo(TestErrorSet!usize); + assert(TypeId(error_union_info) == TypeId.ErrorUnion); + assert(error_union_info.ErrorUnion.error_set == TestErrorSet); + assert(error_union_info.ErrorUnion.payload == usize); + } +} + +test "type info: enum info" { + comptime { + const Os = @import("builtin").Os; + + const os_info = @typeInfo(Os); + assert(TypeId(os_info) == TypeId.Enum); + assert(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); + assert(os_info.Enum.fields.len == 32); + assert(mem.eql(u8, os_info.Enum.fields[1].name, "ananas")); + assert(os_info.Enum.fields[10].value == 10); + assert(os_info.Enum.tag_type == u5); + assert(os_info.Enum.defs.len == 0); + } +} + +test "type info: union info" { + comptime { + const typeinfo_info = @typeInfo(TypeInfo); + assert(TypeId(typeinfo_info) == TypeId.Union); + assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assert(typeinfo_info.Union.tag_type == TypeId); + assert(typeinfo_info.Union.fields.len == 25); + assert(typeinfo_info.Union.fields[4].enum_field != null); + assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); + assert(typeinfo_info.Union.fields[4].field_type == @typeOf(u8_info.Int)); + assert(typeinfo_info.Union.defs.len == 20); + + const TestNoTagUnion = union { + Foo: void, + Bar: u32, + }; + + const notag_union_info = @typeInfo(TestNoTagUnion); + assert(TypeId(notag_union_info) == TypeId.Union); + assert(notag_union_info.Union.tag_type == @typeOf(undefined)); + assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assert(notag_union_info.Union.fields.len == 2); + assert(notag_union_info.Union.fields[0].enum_field == null); + assert(notag_union_info.Union.fields[1].field_type == u32); + + const TestExternUnion = extern union { + foo: &c_void, + }; + + const extern_union_info = @typeInfo(TestExternUnion); + assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); + assert(extern_union_info.Union.tag_type == @typeOf(undefined)); + assert(extern_union_info.Union.fields[0].enum_field == null); + assert(extern_union_info.Union.fields[0].field_type == &c_void); + } +} + +test "type info: struct info" { + comptime { + const struct_info = @typeInfo(TestStruct); + assert(TypeId(struct_info) == TypeId.Struct); + assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); + assert(struct_info.Struct.fields.len == 3); + assert(struct_info.Struct.fields[1].offset == null); + assert(struct_info.Struct.fields[2].field_type == &TestStruct); + assert(struct_info.Struct.defs.len == 2); + assert(struct_info.Struct.defs[0].is_pub); + assert(!struct_info.Struct.defs[0].data.Fn.is_extern); + assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); + assert(struct_info.Struct.defs[0].data.Fn.return_type == void); + assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn(&const TestStruct)void); + } +} + +const TestStruct = packed struct { + const Self = this; + + fieldA: usize, + fieldB: void, + fieldC: &Self, + + pub fn foo(self: &const Self) void {} +}; + +test "type info: function type info" { + comptime { + const fn_info = @typeInfo(@typeOf(foo)); + assert(TypeId(fn_info) == TypeId.Fn); + assert(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); + assert(fn_info.Fn.is_generic); + assert(fn_info.Fn.args.len == 2); + assert(fn_info.Fn.is_var_args); + assert(fn_info.Fn.return_type == @typeOf(undefined)); + assert(fn_info.Fn.async_allocator_type == @typeOf(undefined)); + + const test_instance: TestStruct = undefined; + const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); + assert(TypeId(bound_fn_info) == TypeId.BoundFn); + assert(bound_fn_info.BoundFn.args[0].arg_type == &const TestStruct); + } +} + +fn foo(comptime a: usize, b: bool, args: ...) usize { + return 0; +} -- cgit v1.2.3 From 1b6e97355d58fbd8aa961d02c3e75c9ca34ed1b9 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 1 May 2018 13:31:14 +0300 Subject: Added type info tests to behavior test listing --- test/behavior.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/test/behavior.zig b/test/behavior.zig index 2c10c6d71b..3efd65e5dd 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -36,6 +36,7 @@ comptime { _ = @import("cases/pub_enum/index.zig"); _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("cases/reflection.zig"); + _ = @import("cases/type_info.zig"); _ = @import("cases/sizeof_and_typeof.zig"); _ = @import("cases/slice.zig"); _ = @import("cases/struct.zig"); -- cgit v1.2.3 From 7d239414f7ed5abad8cdd8b1262590d8f50dd56a Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 1 May 2018 13:42:20 +0300 Subject: Fixed type info test, added documentation. --- doc/langref.html.in | 391 ++++++++++++++++++++++++++++++++++++++++++++++- test/cases/type_info.zig | 4 +- 2 files changed, 389 insertions(+), 6 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 16fafdaad9..db41ca6a1f 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4809,6 +4809,182 @@ pub const TypeId = enum { BoundFn, ArgTuple, Opaque, +}; + {#code_end#} + {#header_close#} + {#header_open|@typeInfo#} +
@typeInfo(comptime T: type) -> @import("builtin").TypeInfo
+

+ Returns information on the type. Returns a value of the following union: +

+ {#code_begin|syntax#} +pub const TypeInfo = union(TypeId) { + Type: void, + Void: void, + Bool: void, + NoReturn: void, + Int: Int, + Float: Float, + Pointer: Pointer, + Array: Array, + Struct: Struct, + FloatLiteral: void, + IntLiteral: void, + UndefinedLiteral: void, + NullLiteral: void, + Nullable: Nullable, + ErrorUnion: ErrorUnion, + ErrorSet: ErrorSet, + Enum: Enum, + Union: Union, + Fn: Fn, + Namespace: void, + Block: void, + BoundFn: Fn, + ArgTuple: void, + Opaque: void, + Promise: Promise, + + + pub const Int = struct { + is_signed: bool, + bits: u8, + }; + + pub const Float = struct { + bits: u8, + }; + + pub const Pointer = struct { + is_const: bool, + is_volatile: bool, + alignment: u32, + child: type, + }; + + pub const Array = struct { + len: usize, + child: type, + }; + + pub const ContainerLayout = enum { + Auto, + Extern, + Packed, + }; + + pub const StructField = struct { + name: []const u8, + offset: ?usize, + field_type: type, + }; + + pub const Struct = struct { + layout: ContainerLayout, + fields: []StructField, + defs: []Definition, + }; + + pub const Nullable = struct { + child: type, + }; + + pub const ErrorUnion = struct { + error_set: type, + payload: type, + }; + + pub const Error = struct { + name: []const u8, + value: usize, + }; + + pub const ErrorSet = struct { + errors: []Error, + }; + + pub const EnumField = struct { + name: []const u8, + value: usize, + }; + + pub const Enum = struct { + layout: ContainerLayout, + tag_type: type, + fields: []EnumField, + defs: []Definition, + }; + + pub const UnionField = struct { + name: []const u8, + enum_field: ?EnumField, + field_type: type, + }; + + pub const Union = struct { + layout: ContainerLayout, + tag_type: type, + fields: []UnionField, + defs: []Definition, + }; + + pub const CallingConvention = enum { + Unspecified, + C, + Cold, + Naked, + Stdcall, + Async, + }; + + pub const FnArg = struct { + is_generic: bool, + is_noalias: bool, + arg_type: type, + }; + + pub const Fn = struct { + calling_convention: CallingConvention, + is_generic: bool, + is_var_args: bool, + return_type: type, + async_allocator_type: type, + args: []FnArg, + }; + + pub const Promise = struct { + child: type, + }; + + pub const Definition = struct { + name: []const u8, + is_pub: bool, + data: Data, + + pub const Data = union(enum) { + Type: type, + Var: type, + Fn: FnDef, + + pub const FnDef = struct { + fn_type: type, + inline_type: Inline, + calling_convention: CallingConvention, + is_var_args: bool, + is_extern: bool, + is_export: bool, + lib_name: ?[]const u8, + return_type: type, + arg_names: [][] const u8, + + pub const Inline = enum { + Auto, + Always, + Never, + }; + }; + }; + }; }; {#code_end#} {#header_close#} @@ -5226,7 +5402,6 @@ pub const Os = enum { rtems, nacl, cnk, - bitrig, aix, cuda, nvcl, @@ -5237,10 +5412,12 @@ pub const Os = enum { watchos, mesa3d, contiki, + amdpal, zen, }; pub const Arch = enum { + armv8_3a, armv8_2a, armv8_1a, armv8, @@ -5260,9 +5437,29 @@ pub const Arch = enum { armv5, armv5te, armv4t, - armeb, + armebv8_3a, + armebv8_2a, + armebv8_1a, + armebv8, + armebv8r, + armebv8m_baseline, + armebv8m_mainline, + armebv7, + armebv7em, + armebv7m, + armebv7s, + armebv7k, + armebv7ve, + armebv6, + armebv6m, + armebv6k, + armebv6t2, + armebv5, + armebv5te, + armebv4t, aarch64, aarch64_be, + arc, avr, bpfel, bpfeb, @@ -5315,6 +5512,7 @@ pub const Arch = enum { pub const Environ = enum { unknown, gnu, + gnuabin32, gnuabi64, gnueabi, gnueabihf, @@ -5332,6 +5530,7 @@ pub const Environ = enum { amdopencl, coreclr, opencl, + simulator, }; pub const ObjectFormat = enum { @@ -5358,10 +5557,23 @@ pub const AtomicOrder = enum { SeqCst, }; +pub const AtomicRmwOp = enum { + Xchg, + Add, + Sub, + And, + Nand, + Or, + Xor, + Max, + Min, +}; + pub const Mode = enum { Debug, ReleaseSafe, ReleaseFast, + ReleaseSmall, }; pub const TypeId = enum { @@ -5380,7 +5592,7 @@ pub const TypeId = enum { NullLiteral, Nullable, ErrorUnion, - Error, + ErrorSet, Enum, Union, Fn, @@ -5389,6 +5601,176 @@ pub const TypeId = enum { BoundFn, ArgTuple, Opaque, + Promise, +}; + +pub const TypeInfo = union(TypeId) { + Type: void, + Void: void, + Bool: void, + NoReturn: void, + Int: Int, + Float: Float, + Pointer: Pointer, + Array: Array, + Struct: Struct, + FloatLiteral: void, + IntLiteral: void, + UndefinedLiteral: void, + NullLiteral: void, + Nullable: Nullable, + ErrorUnion: ErrorUnion, + ErrorSet: ErrorSet, + Enum: Enum, + Union: Union, + Fn: Fn, + Namespace: void, + Block: void, + BoundFn: Fn, + ArgTuple: void, + Opaque: void, + Promise: Promise, + + + pub const Int = struct { + is_signed: bool, + bits: u8, + }; + + pub const Float = struct { + bits: u8, + }; + + pub const Pointer = struct { + is_const: bool, + is_volatile: bool, + alignment: u32, + child: type, + }; + + pub const Array = struct { + len: usize, + child: type, + }; + + pub const ContainerLayout = enum { + Auto, + Extern, + Packed, + }; + + pub const StructField = struct { + name: []const u8, + offset: ?usize, + field_type: type, + }; + + pub const Struct = struct { + layout: ContainerLayout, + fields: []StructField, + defs: []Definition, + }; + + pub const Nullable = struct { + child: type, + }; + + pub const ErrorUnion = struct { + error_set: type, + payload: type, + }; + + pub const Error = struct { + name: []const u8, + value: usize, + }; + + pub const ErrorSet = struct { + errors: []Error, + }; + + pub const EnumField = struct { + name: []const u8, + value: usize, + }; + + pub const Enum = struct { + layout: ContainerLayout, + tag_type: type, + fields: []EnumField, + defs: []Definition, + }; + + pub const UnionField = struct { + name: []const u8, + enum_field: ?EnumField, + field_type: type, + }; + + pub const Union = struct { + layout: ContainerLayout, + tag_type: type, + fields: []UnionField, + defs: []Definition, + }; + + pub const CallingConvention = enum { + Unspecified, + C, + Cold, + Naked, + Stdcall, + Async, + }; + + pub const FnArg = struct { + is_generic: bool, + is_noalias: bool, + arg_type: type, + }; + + pub const Fn = struct { + calling_convention: CallingConvention, + is_generic: bool, + is_var_args: bool, + return_type: type, + async_allocator_type: type, + args: []FnArg, + }; + + pub const Promise = struct { + child: type, + }; + + pub const Definition = struct { + name: []const u8, + is_pub: bool, + data: Data, + + pub const Data = union(enum) { + Type: type, + Var: type, + Fn: FnDef, + + pub const FnDef = struct { + fn_type: type, + inline_type: Inline, + calling_convention: CallingConvention, + is_var_args: bool, + is_extern: bool, + is_export: bool, + lib_name: ?[]const u8, + return_type: type, + arg_names: [][] const u8, + + pub const Inline = enum { + Auto, + Always, + Never, + }; + }; + }; + }; }; pub const FloatMode = enum { @@ -5402,7 +5784,7 @@ pub const Endian = enum { }; pub const endian = Endian.Little; -pub const is_test = false; +pub const is_test = true; pub const os = Os.linux; pub const arch = Arch.x86_64; pub const environ = Environ.gnu; @@ -5410,6 +5792,7 @@ pub const object_format = ObjectFormat.elf; pub const mode = Mode.Debug; pub const link_libc = false; pub const have_error_return_tracing = true; +pub const __zig_test_fn_slice = {}; // overwritten later {#code_end#} {#see_also|Build Mode#} {#header_close#} diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index 8e6721beae..c9b15157e8 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -70,7 +70,7 @@ test "type info: error set, error union info" { assert(TypeId(error_set_info) == TypeId.ErrorSet); assert(error_set_info.ErrorSet.errors.len == 3); assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); - assert(error_set_info.ErrorSet.errors[2].value == 3); + assert(error_set_info.ErrorSet.errors[2].value == usize(TestErrorSet.Third)); const error_union_info = @typeInfo(TestErrorSet!usize); assert(TypeId(error_union_info) == TypeId.ErrorUnion); @@ -103,7 +103,7 @@ test "type info: union info" { assert(typeinfo_info.Union.fields.len == 25); assert(typeinfo_info.Union.fields[4].enum_field != null); assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); - assert(typeinfo_info.Union.fields[4].field_type == @typeOf(u8_info.Int)); + assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); assert(typeinfo_info.Union.defs.len == 20); const TestNoTagUnion = union { -- cgit v1.2.3 From 57940837e7a178577c22b89c5173082e4fb8e618 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 1 May 2018 13:44:19 +0300 Subject: Added typeInfo to langref built_ins --- doc/langref.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index db41ca6a1f..b867ff0b35 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6451,7 +6451,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; -- cgit v1.2.3 From 849ea61fa11460b1a6df2529063a6b0cabc6e5e4 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 1 May 2018 17:10:50 +0300 Subject: Small fix. --- src/ir.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index ff81ebfd86..cffee9bebc 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15765,6 +15765,7 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na type_info_var = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_var->type->id == TypeTableEntryIdMetaType); + ensure_complete_type(ira->codegen, type_info_var->data.x_type); type_info_type = type_info_var->data.x_type; assert(type_info_type->id == TypeTableEntryIdUnion); } @@ -15790,6 +15791,7 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na VariableTableEntry *var = tld->var; + ensure_complete_type(ira->codegen, var->value->type); assert(var->value->type->id == TypeTableEntryIdMetaType); return var->value->data.x_type; } -- cgit v1.2.3 From 86a428a4a52172df5e49436212e9f769248e0b15 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 May 2018 10:03:58 -0400 Subject: windows threading: add missing call to CloseHandle --- std/os/index.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/std/os/index.zig b/std/os/index.zig index 4f1826021f..93c5f70f1e 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2477,6 +2477,7 @@ pub const Thread = struct { }, builtin.Os.windows => { assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); + assert(windows.CloseHandle(self.data.handle) != 0); assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0); }, else => @compileError("Unsupported OS"), -- cgit v1.2.3 From 1a9403f38a89d4a55f746d077d725424b8852d44 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Wed, 2 May 2018 21:50:41 +0200 Subject: Added better support for none pure enums in tranlate C --- src/ast_render.cpp | 2 +- src/translate_c.cpp | 108 +++++++++++++++++++-------------------------------- test/translate_c.zig | 22 +++++++++++ 3 files changed, 62 insertions(+), 70 deletions(-) diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 2c3e1fc873..7f44cb7b65 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -728,7 +728,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { render_node_grouped(ar, field_node->data.struct_field.type); } if (field_node->data.struct_field.value != nullptr) { - fprintf(ar->f, "= "); + fprintf(ar->f, " = "); render_node_grouped(ar, field_node->data.struct_field.value); } fprintf(ar->f, ",\n"); diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 965a8963bd..c0a76b8969 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -3744,6 +3744,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { return demote_enum_to_opaque(c, enum_decl, full_type_name, bare_name); } + bool pure_enum = true; uint32_t field_count = 0; for (auto it = enum_def->enumerator_begin(), @@ -3755,84 +3756,53 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { pure_enum = false; } } - AstNode *tag_int_type = trans_qual_type(c, enum_decl->getIntegerType(), enum_decl->getLocation()); assert(tag_int_type); - if (pure_enum) { - AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl); - enum_node->data.container_decl.kind = ContainerKindEnum; - enum_node->data.container_decl.layout = ContainerLayoutExtern; - // TODO only emit this tag type if the enum tag type is not the default. - // I don't know what the default is, need to figure out how clang is deciding. - // it appears to at least be different across gcc/msvc - if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) && - !c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int)) - { - enum_node->data.container_decl.init_arg_expr = tag_int_type; - } - - enum_node->data.container_decl.fields.resize(field_count); - uint32_t i = 0; - for (auto it = enum_def->enumerator_begin(), - it_end = enum_def->enumerator_end(); - it != it_end; ++it, i += 1) - { - const EnumConstantDecl *enum_const = *it; - - Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); - Buf *field_name; - if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) { - field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name)); - } else { - field_name = enum_val_name; - } - - AstNode *field_node = trans_create_node(c, NodeTypeStructField); - field_node->data.struct_field.name = field_name; - field_node->data.struct_field.type = nullptr; - enum_node->data.container_decl.fields.items[i] = field_node; - - // in C each enum value is in the global namespace. so we put them there too. - // at this point we can rely on the enum emitting successfully - if (is_anonymous) { - AstNode *lit_node = trans_create_node_unsigned(c, i); - add_global_var(c, enum_val_name, lit_node); - } else { - AstNode *field_access_node = trans_create_node_field_access(c, - trans_create_node_symbol(c, full_type_name), field_name); - add_global_var(c, enum_val_name, field_access_node); - } - } - - if (is_anonymous) { - c->decl_table.put(enum_decl->getCanonicalDecl(), enum_node); - return enum_node; - } else { - AstNode *symbol_node = trans_create_node_symbol(c, full_type_name); - add_global_weak_alias(c, bare_name, full_type_name); - add_global_var(c, full_type_name, enum_node); - c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node); - return enum_node; - } + AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl); + enum_node->data.container_decl.kind = ContainerKindEnum; + enum_node->data.container_decl.layout = ContainerLayoutExtern; + // TODO only emit this tag type if the enum tag type is not the default. + // I don't know what the default is, need to figure out how clang is deciding. + // it appears to at least be different across gcc/msvc + if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) && + !c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int)) + { + enum_node->data.container_decl.init_arg_expr = tag_int_type; } - - // TODO after issue #305 is solved, make this be an enum with tag_int_type - // as the integer type and set the custom enum values - AstNode *enum_node = tag_int_type; - - - // add variables for all the values with enum_node + enum_node->data.container_decl.fields.resize(field_count); + uint32_t i = 0; for (auto it = enum_def->enumerator_begin(), it_end = enum_def->enumerator_end(); - it != it_end; ++it) + it != it_end; ++it, i += 1) { const EnumConstantDecl *enum_const = *it; Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); - AstNode *int_node = trans_create_node_apint(c, enum_const->getInitVal()); - AstNode *var_node = add_global_var(c, enum_val_name, int_node); - var_node->data.variable_declaration.type = tag_int_type; + Buf *field_name; + if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) { + field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name)); + } else { + field_name = enum_val_name; + } + + AstNode *int_node = pure_enum && !is_anonymous ? nullptr : trans_create_node_apint(c, enum_const->getInitVal()); + AstNode *field_node = trans_create_node(c, NodeTypeStructField); + field_node->data.struct_field.name = field_name; + field_node->data.struct_field.type = nullptr; + field_node->data.struct_field.value = int_node; + enum_node->data.container_decl.fields.items[i] = field_node; + + // in C each enum value is in the global namespace. so we put them there too. + // at this point we can rely on the enum emitting successfully + if (is_anonymous) { + Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); + add_global_var(c, enum_val_name, int_node); + } else { + AstNode *field_access_node = trans_create_node_field_access(c, + trans_create_node_symbol(c, full_type_name), field_name); + add_global_var(c, enum_val_name, field_access_node); + } } if (is_anonymous) { @@ -3843,7 +3813,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { add_global_weak_alias(c, bare_name, full_type_name); add_global_var(c, full_type_name, enum_node); c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node); - return symbol_node; + return enum_node; } } diff --git a/test/translate_c.zig b/test/translate_c.zig index 9a69c2b03e..a5b5f3ae2a 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -53,6 +53,28 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\pub const Foo = enum_Foo; ); + cases.add("enums", + \\enum Foo { + \\ FooA = 2, + \\ FooB = 5, + \\ Foo1, + \\}; + , + \\pub const enum_Foo = extern enum { + \\ A = 2, + \\ B = 5, + \\ @"1" = 6, + \\}; + , + \\pub const FooA = enum_Foo.A; + , + \\pub const FooB = enum_Foo.B; + , + \\pub const Foo1 = enum_Foo.@"1"; + , + \\pub const Foo = enum_Foo; + ); + cases.add("restrict -> noalias", \\void foo(void *restrict bar, void *restrict); , -- cgit v1.2.3 From c186cd187e9b75ee1514a5f3371abeffb36d871a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 May 2018 20:19:26 -0400 Subject: std.atomic - use AtomicOrder.SeqCst for everything also use less memory for the tests --- std/atomic/queue.zig | 6 +++--- std/atomic/stack.zig | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 1acecbab2c..69c82236d1 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -31,10 +31,10 @@ pub fn Queue(comptime T: type) type { } pub fn get(self: &Self) ?&Node { - var head = @atomicLoad(&Node, &self.head, AtomicOrder.Acquire); + var head = @atomicLoad(&Node, &self.head, AtomicOrder.SeqCst); while (true) { const node = head.next ?? return null; - head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? return node; + head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return node; } } }; @@ -56,7 +56,7 @@ test "std.atomic.queue" { var direct_allocator = std.heap.DirectAllocator.init(); defer direct_allocator.deinit(); - var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024); + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 600 * 1024); defer direct_allocator.allocator.free(plenty_of_memory); var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index accbcc942a..96185d308b 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -35,7 +35,7 @@ pub fn Stack(comptime T: type) type { } pub fn pop(self: &Self) ?&Node { - var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire); + var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst); while (true) { root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root; } @@ -63,7 +63,7 @@ test "std.atomic.stack" { var direct_allocator = std.heap.DirectAllocator.init(); defer direct_allocator.deinit(); - var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024); + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 600 * 1024); defer direct_allocator.allocator.free(plenty_of_memory); var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); -- cgit v1.2.3 From 02c1b9df3bcf2e0324adf02fd6c0ed56fe58c6d3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 May 2018 21:34:34 -0400 Subject: fix compiler-rt tests accidentally running std tests also reduce the aggressiveness of std.atomic.stack and std.atomic.queue fuzz testing. appveyor has 1 core and 10,000 iterations is too much for 6 threads to thrash over --- std/atomic/queue.zig | 10 +++++-- std/atomic/stack.zig | 9 ++++-- std/special/compiler_rt/fixuint.zig | 2 +- std/special/compiler_rt/fixunsdfdi_test.zig | 2 +- std/special/compiler_rt/fixunsdfsi_test.zig | 2 +- std/special/compiler_rt/fixunsdfti_test.zig | 2 +- std/special/compiler_rt/fixunssfdi_test.zig | 2 +- std/special/compiler_rt/fixunssfsi_test.zig | 2 +- std/special/compiler_rt/fixunssfti_test.zig | 2 +- std/special/compiler_rt/fixunstfdi_test.zig | 2 +- std/special/compiler_rt/fixunstfsi_test.zig | 2 +- std/special/compiler_rt/fixunstfti_test.zig | 2 +- std/special/compiler_rt/index.zig | 5 ++-- std/special/compiler_rt/udivmod.zig | 2 +- std/zig/parser_test.zig | 46 +++++++++++++++++------------ 15 files changed, 56 insertions(+), 36 deletions(-) diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 69c82236d1..e25c8e6b17 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -49,14 +49,20 @@ const Context = struct { get_count: usize, puts_done: u8, // TODO make this a bool }; -const puts_per_thread = 10000; + +// TODO add lazy evaluated build options and then put puts_per_thread behind +// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor +// CI we would use a less aggressive setting since at 1 core, while we still +// want this test to pass, we need a smaller value since there is so much thrashing +// we would also use a less aggressive setting when running in valgrind +const puts_per_thread = 500; const put_thread_count = 3; test "std.atomic.queue" { var direct_allocator = std.heap.DirectAllocator.init(); defer direct_allocator.deinit(); - var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 600 * 1024); + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024); defer direct_allocator.allocator.free(plenty_of_memory); var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 96185d308b..4a3dbef32b 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -56,14 +56,19 @@ const Context = struct { get_count: usize, puts_done: u8, // TODO make this a bool }; -const puts_per_thread = 1000; +// TODO add lazy evaluated build options and then put puts_per_thread behind +// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor +// CI we would use a less aggressive setting since at 1 core, while we still +// want this test to pass, we need a smaller value since there is so much thrashing +// we would also use a less aggressive setting when running in valgrind +const puts_per_thread = 500; const put_thread_count = 3; test "std.atomic.stack" { var direct_allocator = std.heap.DirectAllocator.init(); defer direct_allocator.deinit(); - var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 600 * 1024); + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024); defer direct_allocator.allocator.free(plenty_of_memory); var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); diff --git a/std/special/compiler_rt/fixuint.zig b/std/special/compiler_rt/fixuint.zig index b01bc48118..37cec446bc 100644 --- a/std/special/compiler_rt/fixuint.zig +++ b/std/special/compiler_rt/fixuint.zig @@ -1,5 +1,5 @@ const is_test = @import("builtin").is_test; -const Log2Int = @import("../../math/index.zig").Log2Int; +const Log2Int = @import("std").math.Log2Int; pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t { @setRuntimeSafety(is_test); diff --git a/std/special/compiler_rt/fixunsdfdi_test.zig b/std/special/compiler_rt/fixunsdfdi_test.zig index 3443a4938e..e59d09f8de 100644 --- a/std/special/compiler_rt/fixunsdfdi_test.zig +++ b/std/special/compiler_rt/fixunsdfdi_test.zig @@ -1,5 +1,5 @@ const __fixunsdfdi = @import("fixunsdfdi.zig").__fixunsdfdi; -const assert = @import("../../index.zig").debug.assert; +const assert = @import("std").debug.assert; fn test__fixunsdfdi(a: f64, expected: u64) void { const x = __fixunsdfdi(a); diff --git a/std/special/compiler_rt/fixunsdfsi_test.zig b/std/special/compiler_rt/fixunsdfsi_test.zig index 3c74bc5f4c..db6e32e23d 100644 --- a/std/special/compiler_rt/fixunsdfsi_test.zig +++ b/std/special/compiler_rt/fixunsdfsi_test.zig @@ -1,5 +1,5 @@ const __fixunsdfsi = @import("fixunsdfsi.zig").__fixunsdfsi; -const assert = @import("../../index.zig").debug.assert; +const assert = @import("std").debug.assert; fn test__fixunsdfsi(a: f64, expected: u32) void { const x = __fixunsdfsi(a); diff --git a/std/special/compiler_rt/fixunsdfti_test.zig b/std/special/compiler_rt/fixunsdfti_test.zig index 3cb7687887..7283b35c0e 100644 --- a/std/special/compiler_rt/fixunsdfti_test.zig +++ b/std/special/compiler_rt/fixunsdfti_test.zig @@ -1,5 +1,5 @@ const __fixunsdfti = @import("fixunsdfti.zig").__fixunsdfti; -const assert = @import("../../index.zig").debug.assert; +const assert = @import("std").debug.assert; fn test__fixunsdfti(a: f64, expected: u128) void { const x = __fixunsdfti(a); diff --git a/std/special/compiler_rt/fixunssfdi_test.zig b/std/special/compiler_rt/fixunssfdi_test.zig index de27323777..e4e6c1736d 100644 --- a/std/special/compiler_rt/fixunssfdi_test.zig +++ b/std/special/compiler_rt/fixunssfdi_test.zig @@ -1,5 +1,5 @@ const __fixunssfdi = @import("fixunssfdi.zig").__fixunssfdi; -const assert = @import("../../index.zig").debug.assert; +const assert = @import("std").debug.assert; fn test__fixunssfdi(a: f32, expected: u64) void { const x = __fixunssfdi(a); diff --git a/std/special/compiler_rt/fixunssfsi_test.zig b/std/special/compiler_rt/fixunssfsi_test.zig index 47ed21d4f4..614c648dfe 100644 --- a/std/special/compiler_rt/fixunssfsi_test.zig +++ b/std/special/compiler_rt/fixunssfsi_test.zig @@ -1,5 +1,5 @@ const __fixunssfsi = @import("fixunssfsi.zig").__fixunssfsi; -const assert = @import("../../index.zig").debug.assert; +const assert = @import("std").debug.assert; fn test__fixunssfsi(a: f32, expected: u32) void { const x = __fixunssfsi(a); diff --git a/std/special/compiler_rt/fixunssfti_test.zig b/std/special/compiler_rt/fixunssfti_test.zig index 3033eb0def..43ad527f53 100644 --- a/std/special/compiler_rt/fixunssfti_test.zig +++ b/std/special/compiler_rt/fixunssfti_test.zig @@ -1,5 +1,5 @@ const __fixunssfti = @import("fixunssfti.zig").__fixunssfti; -const assert = @import("../../index.zig").debug.assert; +const assert = @import("std").debug.assert; fn test__fixunssfti(a: f32, expected: u128) void { const x = __fixunssfti(a); diff --git a/std/special/compiler_rt/fixunstfdi_test.zig b/std/special/compiler_rt/fixunstfdi_test.zig index d1f5f6496a..dd0869195a 100644 --- a/std/special/compiler_rt/fixunstfdi_test.zig +++ b/std/special/compiler_rt/fixunstfdi_test.zig @@ -1,5 +1,5 @@ const __fixunstfdi = @import("fixunstfdi.zig").__fixunstfdi; -const assert = @import("../../index.zig").debug.assert; +const assert = @import("std").debug.assert; fn test__fixunstfdi(a: f128, expected: u64) void { const x = __fixunstfdi(a); diff --git a/std/special/compiler_rt/fixunstfsi_test.zig b/std/special/compiler_rt/fixunstfsi_test.zig index 8bdf36d9d4..f682191994 100644 --- a/std/special/compiler_rt/fixunstfsi_test.zig +++ b/std/special/compiler_rt/fixunstfsi_test.zig @@ -1,5 +1,5 @@ const __fixunstfsi = @import("fixunstfsi.zig").__fixunstfsi; -const assert = @import("../../index.zig").debug.assert; +const assert = @import("std").debug.assert; fn test__fixunstfsi(a: f128, expected: u32) void { const x = __fixunstfsi(a); diff --git a/std/special/compiler_rt/fixunstfti_test.zig b/std/special/compiler_rt/fixunstfti_test.zig index d9eb60e59b..9128ac6c08 100644 --- a/std/special/compiler_rt/fixunstfti_test.zig +++ b/std/special/compiler_rt/fixunstfti_test.zig @@ -1,5 +1,5 @@ const __fixunstfti = @import("fixunstfti.zig").__fixunstfti; -const assert = @import("../../index.zig").debug.assert; +const assert = @import("std").debug.assert; fn test__fixunstfti(a: f128, expected: u128) void { const x = __fixunstfti(a); diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index 6ef43c4fed..9da9c3f083 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -71,7 +71,8 @@ comptime { } } -const assert = @import("../../index.zig").debug.assert; +const std = @import("std"); +const assert = std.debug.assert; const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; @@ -80,7 +81,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn { @setCold(true); if (is_test) { - @import("std").debug.panic("{}", msg); + std.debug.panic("{}", msg); } else { unreachable; } diff --git a/std/special/compiler_rt/udivmod.zig b/std/special/compiler_rt/udivmod.zig index 07eaef583c..7820c7beb0 100644 --- a/std/special/compiler_rt/udivmod.zig +++ b/std/special/compiler_rt/udivmod.zig @@ -9,7 +9,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: const SingleInt = @IntType(false, @divExact(DoubleInt.bit_count, 2)); const SignedDoubleInt = @IntType(true, DoubleInt.bit_count); - const Log2SingleInt = @import("../../math/index.zig").Log2Int(SingleInt); + const Log2SingleInt = @import("std").math.Log2Int(SingleInt); const n = *@ptrCast(&const [2]SingleInt, &a); // TODO issue #421 const d = *@ptrCast(&const [2]SingleInt, &b); // TODO issue #421 diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 4688939485..beb3a4e0c3 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,30 @@ +// TODO +//if (sr > n_uword_bits - 1) // d > r +// return 0; + +// TODO switch with no body +// format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; + + +//TODO +//test "zig fmt: same-line comptime" { +// try testCanonical( +// \\test "" { +// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt +// \\} +// \\ +// ); +//} + + +//TODO +//test "zig fmt: number literals" { +// try testCanonical( +// \\pub const f64_true_min = 4.94065645841246544177e-324; +// \\ +// ); +//} + test "zig fmt: line comments in struct initializer" { try testCanonical( \\fn foo() void { @@ -20,25 +47,6 @@ test "zig fmt: line comments in struct initializer" { ); } -//TODO -//test "zig fmt: same-line comptime" { -// try testCanonical( -// \\test "" { -// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt -// \\} -// \\ -// ); -//} - - -//TODO -//test "zig fmt: number literals" { -// try testCanonical( -// \\pub const f64_true_min = 4.94065645841246544177e-324; -// \\ -// ); -//} - test "zig fmt: doc comments before struct field" { try testCanonical( \\pub const Allocator = struct { -- cgit v1.2.3 From 131c133bb74eab2012158c8ecfaa78944db197c7 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 3 May 2018 04:43:07 +0300 Subject: Fixed inlining determination test (#972) When deciding wether we should inline a scope, look up the parents until we get to a function definition scope --- src/ir.cpp | 2 ++ test/behavior.zig | 1 + test/cases/fn_in_struct_in_comptime.zig | 17 +++++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 test/cases/fn_in_struct_in_comptime.zig diff --git a/src/ir.cpp b/src/ir.cpp index 469900bf07..47f188fdf8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -145,6 +145,8 @@ static bool ir_should_inline(IrExecutable *exec, Scope *scope) { while (scope != nullptr) { if (scope->id == ScopeIdCompTime) return true; + if (scope->id == ScopeIdFnDef) + break; scope = scope->parent; } return false; diff --git a/test/behavior.zig b/test/behavior.zig index 2c10c6d71b..3e540e0cf4 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -52,4 +52,5 @@ comptime { _ = @import("cases/var_args.zig"); _ = @import("cases/void.zig"); _ = @import("cases/while.zig"); + _ = @import("cases/fn_in_struct_in_comptime.zig"); } diff --git a/test/cases/fn_in_struct_in_comptime.zig b/test/cases/fn_in_struct_in_comptime.zig new file mode 100644 index 0000000000..4f181d7ffb --- /dev/null +++ b/test/cases/fn_in_struct_in_comptime.zig @@ -0,0 +1,17 @@ +const assert = @import("std").debug.assert; + +fn get_foo() fn(&u8)usize { + comptime { + return struct { + fn func(ptr: &u8) usize { + var u = @ptrToInt(ptr); + return u; + } + }.func; + } +} + +test "define a function in an anonymous struct in comptime" { + const foo = get_foo(); + assert(foo(@intToPtr(&u8, 12345)) == 12345); +} -- cgit v1.2.3 From 6fd14f23b560fba86c687082c8cd2b160fbe9a87 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 May 2018 22:41:02 -0400 Subject: add ReleaseSmall mode in zig tests closes #969 --- test/tests.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/tests.zig b/test/tests.zig index c3c7bf9d4b..5fbb56b736 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -152,7 +152,7 @@ pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []cons const step = b.step(b.fmt("test-{}", name), desc); for (test_targets) |test_target| { const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch); - for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| { + for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast, Mode.ReleaseSmall}) |mode| { for ([]bool{false, true}) |link_libc| { if (link_libc and !is_native) { // don't assume we have a cross-compiling libc set up @@ -451,7 +451,7 @@ pub const CompareOutputContext = struct { self.step.dependOn(&run_and_cmp_output.step); }, Special.None => { - for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| { + for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast, Mode.ReleaseSmall}) |mode| { const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", "compare-output", case.name, @tagName(mode)) catch unreachable; if (self.test_filter) |filter| { @@ -705,7 +705,7 @@ pub const CompileErrorContext = struct { pub fn addCase(self: &CompileErrorContext, case: &const TestCase) void { const b = self.b; - for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| { + for ([]Mode{Mode.Debug, Mode.ReleaseFast}) |mode| { const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {} ({})", case.name, @tagName(mode)) catch unreachable; if (self.test_filter) |filter| { @@ -773,7 +773,7 @@ pub const BuildExamplesContext = struct { pub fn addAllArgs(self: &BuildExamplesContext, root_src: []const u8, link_libc: bool) void { const b = self.b; - for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| { + for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast, Mode.ReleaseSmall}) |mode| { const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", root_src, @tagName(mode)) catch unreachable; if (self.test_filter) |filter| { -- cgit v1.2.3 From adbb691f46083504dd0134543d979b035d7e00d6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 2 May 2018 22:48:04 -0400 Subject: fix behavior tests for ReleaseSmall --- test/cases/coroutines.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 46055d7469..3aa2912429 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -219,8 +219,9 @@ async fn printTrace(p: promise->error!void) void { std.debug.assert(e == error.Fail); if (@errorReturnTrace()) |trace| { assert(trace.index == 1); - } else if (builtin.mode != builtin.Mode.ReleaseFast) { - @panic("expected return trace"); + } else switch (builtin.mode) { + builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"), + builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {}, } }; } -- cgit v1.2.3 From e907c5cab971428607f85b6df4b4f7dc555775d3 Mon Sep 17 00:00:00 2001 From: Braedon Date: Thu, 3 May 2018 23:54:33 +1000 Subject: Unified API --- std/array_list.zig | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++- std/buf_map.zig | 4 ++-- std/buf_set.zig | 3 +-- std/hash_map.zig | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 110 insertions(+), 6 deletions(-) diff --git a/std/array_list.zig b/std/array_list.zig index 2a44b66518..bd7e8ea7ed 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -44,6 +44,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ return l.toSliceConst()[n]; } + pub fn count(self: &const Self) usize { + return self.len; + } + /// ArrayList takes ownership of the passed in slice. The slice must have been /// allocated with `allocator`. /// Deinitialize with `deinit` or use `toOwnedSlice`. @@ -128,6 +132,27 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ return null; return self.pop(); } + + pub const Iterator = struct { + list: &const Self, + // how many items have we returned + count: usize, + + pub fn next(it: &Iterator) ?T { + if (it.count >= it.list.len) return null; + const val = it.list.at(it.count); + it.count += 1; + return val; + } + + pub fn reset(it: &Iterator) void { + it.count = 0; + } + }; + + pub fn iterator(self: &Self) Iterator { + return Iterator { .list = self, .count = 0 }; + } }; } @@ -157,6 +182,35 @@ test "basic ArrayList test" { assert(list.len == 9); } +test "iterator ArrayList test" { + var list = ArrayList(i32).init(debug.global_allocator); + defer list.deinit(); + + try list.append(1); + try list.append(2); + try list.append(3); + + var count : i32 = 0; + var it = list.iterator(); + while (it.next()) |next| { + assert(next == count + 1); + count += 1; + } + + assert(count == 3); + assert(it.next() == null); + it.reset(); + count = 0; + while (it.next()) |next| { + assert(next == count + 1); + count += 1; + if (count == 2) break; + } + + it.reset(); + assert(?? it.next() == 1); +} + test "insert ArrayList test" { var list = ArrayList(i32).init(debug.global_allocator); defer list.deinit(); @@ -174,4 +228,4 @@ test "insert ArrayList test" { const items = []const i32 { 1 }; try list.insertSlice(0, items[0..0]); assert(list.items[0] == 5); -} +} \ No newline at end of file diff --git a/std/buf_map.zig b/std/buf_map.zig index 3e12d9a7d9..3b88b7a753 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -50,7 +50,7 @@ pub const BufMap = struct { } pub fn count(self: &const BufMap) usize { - return self.hash_map.size; + return self.hash_map.count(); } pub fn iterator(self: &const BufMap) BufMapHashMap.Iterator { @@ -87,4 +87,4 @@ test "BufMap" { bufmap.delete("x"); assert(0 == bufmap.count()); -} +} \ No newline at end of file diff --git a/std/buf_set.zig b/std/buf_set.zig index 618b985c41..4b89d495da 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -38,7 +38,7 @@ pub const BufSet = struct { } pub fn count(self: &const BufSet) usize { - return self.hash_map.size; + return self.hash_map.count(); } pub fn iterator(self: &const BufSet) BufSetHashMap.Iterator { @@ -59,4 +59,3 @@ pub const BufSet = struct { return result; } }; - diff --git a/std/hash_map.zig b/std/hash_map.zig index 29dd233753..99b0c58f40 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -54,6 +54,14 @@ pub fn HashMap(comptime K: type, comptime V: type, } unreachable; // no next item } + + // Reset the iterator to the initial index + pub fn reset(it: &Iterator) void { + it.count = 0; + it.index = 0; + // Resetting the modification count too + it.initial_modification_count = it.hm.modification_count; + } }; pub fn init(allocator: &Allocator) Self { @@ -79,6 +87,10 @@ pub fn HashMap(comptime K: type, comptime V: type, hm.incrementModificationCount(); } + pub fn count(hm: &const Self) usize { + return hm.size; + } + /// Returns the value that was already there. pub fn put(hm: &Self, key: K, value: &const V) !?V { if (hm.entries.len == 0) { @@ -258,10 +270,49 @@ test "basic hash map usage" { assert(map.get(2) == null); } +test "iterator hash map" { + var direct_allocator = std.heap.DirectAllocator.init(); + defer direct_allocator.deinit(); + + var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator); + defer reset_map.deinit(); + + assert((reset_map.put(1, 11) catch unreachable) == null); + assert((reset_map.put(2, 22) catch unreachable) == null); + assert((reset_map.put(3, 33) catch unreachable) == null); + + var keys = []i32 { 1, 2, 3 }; + var values = []i32 { 11, 22, 33 }; + + var it = reset_map.iterator(); + var count : usize = 0; + while (it.next()) |next| { + assert(next.key == keys[count]); + assert(next.value == values[count]); + count += 1; + } + + assert(count == 3); + assert(it.next() == null); + it.reset(); + count = 0; + while (it.next()) |next| { + assert(next.key == keys[count]); + assert(next.value == values[count]); + count += 1; + if (count == 2) break; + } + + it.reset(); + var entry = ?? it.next(); + assert(entry.key == keys[0]); + assert(entry.value == values[0]); +} + fn hash_i32(x: i32) u32 { return @bitCast(u32, x); } fn eql_i32(a: i32, b: i32) bool { return a == b; -} +} \ No newline at end of file -- cgit v1.2.3 From aa2586de182e5587c924740e80468c4c4d509500 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Fri, 4 May 2018 04:27:04 +0200 Subject: Fixed extern enums having the wrong size (#970) Fixed extern enums having the wrong size See #977 --- src/analyze.cpp | 8 +++++++- test/cases/enum.zig | 9 +++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 1ecfe32f4c..0f2fdf15de 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2325,8 +2325,14 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { HashMap occupied_tag_values = {}; occupied_tag_values.init(field_count); - TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); + TypeTableEntry *tag_int_type; + if (enum_type->data.enumeration.layout == ContainerLayoutExtern) { + tag_int_type = get_c_int_type(g, CIntTypeInt); + } else { + tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); + } + // TODO: Are extern enums allowed to have an init_arg_expr? if (decl_node->data.container_decl.init_arg_expr != nullptr) { TypeTableEntry *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); if (type_is_invalid(wanted_tag_int_type)) { diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 644c989b04..0a2658eaf7 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -392,3 +392,12 @@ test "enum with 1 field but explicit tag type should still have the tag type" { const Enum = enum(u8) { B = 2 }; comptime @import("std").debug.assert(@sizeOf(Enum) == @sizeOf(u8)); } + +test "empty extern enum with members" { + const E = extern enum { + A, + B, + C, + }; + assert(@sizeOf(E) == @sizeOf(c_int)); +} -- cgit v1.2.3 From 0afc6a9886ea3701836e5a981877e5444e5bd691 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Wed, 2 May 2018 18:04:40 +1200 Subject: Add json decoder - streaming json decoder - dynamic tree/value decoder --- CMakeLists.txt | 1 + std/index.zig | 2 + std/json.zig | 1308 ++++++++++++++++++++++++++++++++++++ std/json_test.zig | 1942 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 3253 insertions(+) create mode 100644 std/json.zig create mode 100644 std/json_test.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 721690e9dc..36f62725da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -454,6 +454,7 @@ set(ZIG_STD_FILES "heap.zig" "index.zig" "io.zig" + "json.zig" "linked_list.zig" "macho.zig" "math/acos.zig" diff --git a/std/index.zig b/std/index.zig index d6a1e3c94d..272f2bbc6a 100644 --- a/std/index.zig +++ b/std/index.zig @@ -23,6 +23,7 @@ pub const fmt = @import("fmt/index.zig"); pub const hash = @import("hash/index.zig"); pub const heap = @import("heap.zig"); pub const io = @import("io.zig"); +pub const json = @import("json.zig"); pub const macho = @import("macho.zig"); pub const math = @import("math/index.zig"); pub const mem = @import("mem.zig"); @@ -56,6 +57,7 @@ test "std" { _ = @import("fmt/index.zig"); _ = @import("hash/index.zig"); _ = @import("io.zig"); + _ = @import("json.zig"); _ = @import("macho.zig"); _ = @import("math/index.zig"); _ = @import("mem.zig"); diff --git a/std/json.zig b/std/json.zig new file mode 100644 index 0000000000..395adc4879 --- /dev/null +++ b/std/json.zig @@ -0,0 +1,1308 @@ +// JSON parser conforming to RFC8259. +// +// https://tools.ietf.org/html/rfc8259 + +const std = @import("index.zig"); +const mem = std.mem; + +const u1 = @IntType(false, 1); +const u256 = @IntType(false, 256); + +pub const TokenId = enum(u5) { + ObjectBegin, + ObjectEnd, + ArrayBegin, + ArrayEnd, + String, + Number, + True, + False, + Null, +}; + +// A single token slice into the parent string. +// +// Use `token.slice()` on the inptu at the current position to get the current slice. +pub const Token = struct { + id: TokenId, + // How many bytes do we skip before counting + offset: u1, + // Whether string contains a \uXXXX sequence and cannot be zero-copied + string_has_unicode_escape: bool, + // Whether number is simple and can be represented by an integer (i.e. no `.` or `e`) + number_is_integer: bool, + // How many bytes from the current position behind the start of this token is. + count: usize, + + pub fn init(id: TokenId, count: usize, offset: u1) Token { + return Token { + .id = id, + .offset = offset, + .string_has_unicode_escape = false, + .number_is_integer = true, + .count = count, + }; + } + + pub fn initString(count: usize, has_unicode_escape: bool) Token { + return Token { + .id = TokenId.String, + .offset = 0, + .string_has_unicode_escape = has_unicode_escape, + .number_is_integer = true, + .count = count, + }; + } + + pub fn initNumber(count: usize, number_is_integer: bool) Token { + return Token { + .id = TokenId.Number, + .offset = 0, + .string_has_unicode_escape = false, + .number_is_integer = number_is_integer, + .count = count, + }; + } + + // A marker token is a zero-length + pub fn initMarker(id: TokenId) Token { + return Token { + .id = id, + .offset = 0, + .string_has_unicode_escape = false, + .number_is_integer = true, + .count = 0, + }; + } + + // Slice into the underlying input string. + pub fn slice(self: &const Token, input: []const u8, i: usize) []const u8 { + return input[i + self.offset - self.count .. i + self.offset]; + } +}; + +// A small streaming JSON parser. This accepts input one byte at a time and returns tokens as +// they are encountered. No copies or allocations are performed during parsing and the entire +// parsing state requires ~40-50 bytes of stack space. +// +// Conforms strictly to RFC8529. +const StreamingJsonParser = struct { + // Current state + state: State, + // How many bytes we have counted for the current token + count: usize, + // What state to follow after parsing a string (either property or value string) + after_string_state: State, + // What state to follow after parsing a value (either top-level or value end) + after_value_state: State, + // If we stopped now, would the complete parsed string to now be a valid json string + complete: bool, + // Current token flags to pass through to the next generated, see Token. + string_has_unicode_escape: bool, + number_is_integer: bool, + + // Bit-stack for nested object/map literals (max 255 nestings). + stack: u256, + stack_used: u8, + + const object_bit = 0; + const array_bit = 1; + const max_stack_size = @maxValue(u8); + + pub fn init() StreamingJsonParser { + var p: StreamingJsonParser = undefined; + p.reset(); + return p; + } + + pub fn reset(p: &StreamingJsonParser) void { + p.state = State.TopLevelBegin; + p.count = 0; + // Set before ever read in main transition function + p.after_string_state = undefined; + p.after_value_state = State.ValueEnd; // handle end of values normally + p.stack = 0; + p.stack_used = 0; + p.complete = false; + p.string_has_unicode_escape = false; + p.number_is_integer = true; + } + + pub const State = enum { + // NOTE: @tagName determines the name based on index, not value. + + // These must be first with these explicit values as we rely on them for indexing the + // bit-stack directly and avoiding a branch. + ObjectSeparator = 0, + ValueEnd = 1, + + TopLevelBegin, + TopLevelEnd, + + ValueBegin, + ValueBeginNoClosing, + + String, + StringUtf8Byte3, + StringUtf8Byte2, + StringUtf8Byte1, + StringEscapeCharacter, + StringEscapeHexUnicode4, + StringEscapeHexUnicode3, + StringEscapeHexUnicode2, + StringEscapeHexUnicode1, + + Number, + NumberMaybeDotOrExponent, + NumberMaybeDigitOrDotOrExponent, + NumberFractionalRequired, + NumberFractional, + NumberMaybeExponent, + NumberExponent, + NumberExponentDigitsRequired, + NumberExponentDigits, + + TrueLiteral1, + TrueLiteral2, + TrueLiteral3, + + FalseLiteral1, + FalseLiteral2, + FalseLiteral3, + FalseLiteral4, + + NullLiteral1, + NullLiteral2, + NullLiteral3, + + // Only call this function to generate array/object final state. + pub fn fromInt(x: var) State { + std.debug.assert(x == 0 or x == 1); + return State(u6(x)); + } + }; + + pub const Error = error { + InvalidTopLevel, + TooManyNestedItems, + TooManyClosingItems, + InvalidValueBegin, + InvalidValueEnd, + UnbalancedBrackets, + UnbalancedBraces, + UnexpectedClosingBracket, + UnexpectedClosingBrace, + InvalidNumber, + InvalidSeparator, + InvalidLiteral, + InvalidEscapeCharacter, + InvalidUnicodeHexSymbol, + InvalidUtf8Byte, + InvalidTopLevelTrailing, + InvalidControlCharacter, + }; + + // Give another byte to the parser and obtain any new tokens. This may (rarely) return two + // tokens. token2 is always null if token1 is null. + // + // There is currently no error recovery on a bad stream. + pub fn feed(p: &StreamingJsonParser, c: u8, token1: &?Token, token2: &?Token) Error!void { + *token1 = null; + *token2 = null; + p.count += 1; + + // unlikely + if (try p.transition(c, token1)) { + _ = try p.transition(c, token2); + } + } + + // Perform a single transition on the state machine and return any possible token. + fn transition(p: &StreamingJsonParser, c: u8, token: &?Token) Error!bool { + switch (p.state) { + State.TopLevelBegin => switch (c) { + '{' => { + p.stack <<= 1; + p.stack |= object_bit; + p.stack_used += 1; + + p.state = State.ValueBegin; + p.after_string_state = State.ObjectSeparator; + + *token = Token.initMarker(TokenId.ObjectBegin); + }, + '[' => { + p.stack <<= 1; + p.stack |= array_bit; + p.stack_used += 1; + + p.state = State.ValueBegin; + p.after_string_state = State.ValueEnd; + + *token = Token.initMarker(TokenId.ArrayBegin); + }, + '-' => { + p.number_is_integer = true; + p.state = State.Number; + p.after_value_state = State.TopLevelEnd; + p.count = 0; + }, + '0' => { + p.number_is_integer = true; + p.state = State.NumberMaybeDotOrExponent; + p.after_value_state = State.TopLevelEnd; + p.count = 0; + }, + '1' ... '9' => { + p.number_is_integer = true; + p.state = State.NumberMaybeDigitOrDotOrExponent; + p.after_value_state = State.TopLevelEnd; + p.count = 0; + }, + '"' => { + p.state = State.String; + p.after_value_state = State.TopLevelEnd; + // We don't actually need the following since after_value_state should override. + p.after_string_state = State.ValueEnd; + p.string_has_unicode_escape = false; + p.count = 0; + }, + 't' => { + p.state = State.TrueLiteral1; + p.after_value_state = State.TopLevelEnd; + p.count = 0; + }, + 'f' => { + p.state = State.FalseLiteral1; + p.after_value_state = State.TopLevelEnd; + p.count = 0; + }, + 'n' => { + p.state = State.NullLiteral1; + p.after_value_state = State.TopLevelEnd; + p.count = 0; + }, + 0x09, 0x0A, 0x0D, 0x20 => { + // whitespace + }, + else => { + return error.InvalidTopLevel; + }, + }, + + State.TopLevelEnd => switch (c) { + 0x09, 0x0A, 0x0D, 0x20 => { + // whitespace + }, + else => { + return error.InvalidTopLevelTrailing; + }, + }, + + State.ValueBegin => switch (c) { + // NOTE: These are shared in ValueEnd as well, think we can reorder states to + // be a bit clearer and avoid this duplication. + '}' => { + // unlikely + if (p.stack & 1 != object_bit) { + return error.UnexpectedClosingBracket; + } + if (p.stack_used == 0) { + return error.TooManyClosingItems; + } + + p.state = State.ValueBegin; + p.after_string_state = State.fromInt(p.stack & 1); + + p.stack >>= 1; + p.stack_used -= 1; + + switch (p.stack_used) { + 0 => { + p.complete = true; + p.state = State.TopLevelEnd; + }, + else => {}, + } + + *token = Token.initMarker(TokenId.ObjectEnd); + }, + ']' => { + if (p.stack & 1 != array_bit) { + return error.UnexpectedClosingBrace; + } + if (p.stack_used == 0) { + return error.TooManyClosingItems; + } + + p.state = State.ValueBegin; + p.after_string_state = State.fromInt(p.stack & 1); + + p.stack >>= 1; + p.stack_used -= 1; + + switch (p.stack_used) { + 0 => { + p.complete = true; + p.state = State.TopLevelEnd; + }, + else => {}, + } + + *token = Token.initMarker(TokenId.ArrayEnd); + }, + '{' => { + if (p.stack_used == max_stack_size) { + return error.TooManyNestedItems; + } + + p.stack <<= 1; + p.stack |= object_bit; + p.stack_used += 1; + + p.state = State.ValueBegin; + p.after_string_state = State.ObjectSeparator; + + *token = Token.initMarker(TokenId.ObjectBegin); + }, + '[' => { + if (p.stack_used == max_stack_size) { + return error.TooManyNestedItems; + } + + p.stack <<= 1; + p.stack |= array_bit; + p.stack_used += 1; + + p.state = State.ValueBegin; + p.after_string_state = State.ValueEnd; + + *token = Token.initMarker(TokenId.ArrayBegin); + }, + '-' => { + p.state = State.Number; + p.count = 0; + }, + '0' => { + p.state = State.NumberMaybeDotOrExponent; + p.count = 0; + }, + '1' ... '9' => { + p.state = State.NumberMaybeDigitOrDotOrExponent; + p.count = 0; + }, + '"' => { + p.state = State.String; + p.count = 0; + }, + 't' => { + p.state = State.TrueLiteral1; + p.count = 0; + }, + 'f' => { + p.state = State.FalseLiteral1; + p.count = 0; + }, + 'n' => { + p.state = State.NullLiteral1; + p.count = 0; + }, + 0x09, 0x0A, 0x0D, 0x20 => { + // whitespace + }, + else => { + return error.InvalidValueBegin; + }, + }, + + // TODO: A bit of duplication here and in the following state, redo. + State.ValueBeginNoClosing => switch (c) { + '{' => { + if (p.stack_used == max_stack_size) { + return error.TooManyNestedItems; + } + + p.stack <<= 1; + p.stack |= object_bit; + p.stack_used += 1; + + p.state = State.ValueBegin; + p.after_string_state = State.ObjectSeparator; + + *token = Token.initMarker(TokenId.ObjectBegin); + }, + '[' => { + if (p.stack_used == max_stack_size) { + return error.TooManyNestedItems; + } + + p.stack <<= 1; + p.stack |= array_bit; + p.stack_used += 1; + + p.state = State.ValueBegin; + p.after_string_state = State.ValueEnd; + + *token = Token.initMarker(TokenId.ArrayBegin); + }, + '-' => { + p.state = State.Number; + p.count = 0; + }, + '0' => { + p.state = State.NumberMaybeDotOrExponent; + p.count = 0; + }, + '1' ... '9' => { + p.state = State.NumberMaybeDigitOrDotOrExponent; + p.count = 0; + }, + '"' => { + p.state = State.String; + p.count = 0; + }, + 't' => { + p.state = State.TrueLiteral1; + p.count = 0; + }, + 'f' => { + p.state = State.FalseLiteral1; + p.count = 0; + }, + 'n' => { + p.state = State.NullLiteral1; + p.count = 0; + }, + 0x09, 0x0A, 0x0D, 0x20 => { + // whitespace + }, + else => { + return error.InvalidValueBegin; + }, + }, + + State.ValueEnd => switch (c) { + ',' => { + p.after_string_state = State.fromInt(p.stack & 1); + p.state = State.ValueBeginNoClosing; + }, + ']' => { + if (p.stack_used == 0) { + return error.UnbalancedBrackets; + } + + p.state = State.ValueEnd; + p.after_string_state = State.fromInt(p.stack & 1); + + p.stack >>= 1; + p.stack_used -= 1; + + if (p.stack_used == 0) { + p.complete = true; + p.state = State.TopLevelEnd; + } + + *token = Token.initMarker(TokenId.ArrayEnd); + }, + '}' => { + if (p.stack_used == 0) { + return error.UnbalancedBraces; + } + + p.state = State.ValueEnd; + p.after_string_state = State.fromInt(p.stack & 1); + + p.stack >>= 1; + p.stack_used -= 1; + + if (p.stack_used == 0) { + p.complete = true; + p.state = State.TopLevelEnd; + } + + *token = Token.initMarker(TokenId.ObjectEnd); + }, + 0x09, 0x0A, 0x0D, 0x20 => { + // whitespace + }, + else => { + return error.InvalidValueEnd; + }, + }, + + State.ObjectSeparator => switch (c) { + ':' => { + p.state = State.ValueBegin; + p.after_string_state = State.ValueEnd; + }, + 0x09, 0x0A, 0x0D, 0x20 => { + // whitespace + }, + else => { + return error.InvalidSeparator; + }, + }, + + State.String => switch (c) { + 0x00 ... 0x1F => { + return error.InvalidControlCharacter; + }, + '"' => { + p.state = p.after_string_state; + if (p.after_value_state == State.TopLevelEnd) { + p.state = State.TopLevelEnd; + p.complete = true; + } + + *token = Token.initString(p.count - 1, p.string_has_unicode_escape); + }, + '\\' => { + p.state = State.StringEscapeCharacter; + }, + 0x20, 0x21, 0x23 ... 0x5B, 0x5D ... 0x7F => { + // non-control ascii + }, + 0xC0 ... 0xDF => { + p.state = State.StringUtf8Byte1; + }, + 0xE0 ... 0xEF => { + p.state = State.StringUtf8Byte2; + }, + 0xF0 ... 0xFF => { + p.state = State.StringUtf8Byte3; + }, + else => { + return error.InvalidUtf8Byte; + }, + }, + + State.StringUtf8Byte3 => switch (c >> 6) { + 0b10 => p.state = State.StringUtf8Byte2, + else => return error.InvalidUtf8Byte, + }, + + State.StringUtf8Byte2 => switch (c >> 6) { + 0b10 => p.state = State.StringUtf8Byte1, + else => return error.InvalidUtf8Byte, + }, + + State.StringUtf8Byte1 => switch (c >> 6) { + 0b10 => p.state = State.String, + else => return error.InvalidUtf8Byte, + }, + + State.StringEscapeCharacter => switch (c) { + // NOTE: '/' is allowed as an escaped character but it also is allowed + // as unescaped according to the RFC. There is a reported errata which suggests + // removing the non-escaped variant but it makes more sense to simply disallow + // it as an escape code here. + // + // The current JSONTestSuite tests rely on both of this behaviour being present + // however, so we default to the status quo where both are accepted until this + // is further clarified. + '"', '\\', '/', 'b', 'f', 'n', 'r', 't' => { + p.state = State.String; + }, + 'u' => { + p.string_has_unicode_escape = true; + p.state = State.StringEscapeHexUnicode4; + }, + else => { + return error.InvalidEscapeCharacter; + }, + }, + + State.StringEscapeHexUnicode4 => switch (c) { + '0' ... '9', 'A' ... 'F', 'a' ... 'f' => { + p.state = State.StringEscapeHexUnicode3; + }, + else => return error.InvalidUnicodeHexSymbol, + }, + + State.StringEscapeHexUnicode3 => switch (c) { + '0' ... '9', 'A' ... 'F', 'a' ... 'f' => { + p.state = State.StringEscapeHexUnicode2; + }, + else => return error.InvalidUnicodeHexSymbol, + }, + + State.StringEscapeHexUnicode2 => switch (c) { + '0' ... '9', 'A' ... 'F', 'a' ... 'f' => { + p.state = State.StringEscapeHexUnicode1; + }, + else => return error.InvalidUnicodeHexSymbol, + }, + + State.StringEscapeHexUnicode1 => switch (c) { + '0' ... '9', 'A' ... 'F', 'a' ... 'f' => { + p.state = State.String; + }, + else => return error.InvalidUnicodeHexSymbol, + }, + + State.Number => { + p.complete = p.after_value_state == State.TopLevelEnd; + switch (c) { + '0' => { + p.state = State.NumberMaybeDotOrExponent; + }, + '1' ... '9' => { + p.state = State.NumberMaybeDigitOrDotOrExponent; + }, + else => { + return error.InvalidNumber; + }, + } + }, + + State.NumberMaybeDotOrExponent => { + p.complete = p.after_value_state == State.TopLevelEnd; + switch (c) { + '.' => { + p.number_is_integer = false; + p.state = State.NumberFractionalRequired; + }, + 'e', 'E' => { + p.number_is_integer = false; + p.state = State.NumberExponent; + }, + else => { + p.state = p.after_value_state; + *token = Token.initNumber(p.count, p.number_is_integer); + return true; + }, + } + }, + + State.NumberMaybeDigitOrDotOrExponent => { + p.complete = p.after_value_state == State.TopLevelEnd; + switch (c) { + '.' => { + p.number_is_integer = false; + p.state = State.NumberFractionalRequired; + }, + 'e', 'E' => { + p.number_is_integer = false; + p.state = State.NumberExponent; + }, + '0' ... '9' => { + // another digit + }, + else => { + p.state = p.after_value_state; + *token = Token.initNumber(p.count, p.number_is_integer); + return true; + }, + } + }, + + State.NumberFractionalRequired => { + p.complete = p.after_value_state == State.TopLevelEnd; + switch (c) { + '0' ... '9' => { + p.state = State.NumberFractional; + }, + else => { + return error.InvalidNumber; + }, + } + }, + + State.NumberFractional => { + p.complete = p.after_value_state == State.TopLevelEnd; + switch (c) { + '0' ... '9' => { + // another digit + }, + 'e', 'E' => { + p.number_is_integer = false; + p.state = State.NumberExponent; + }, + else => { + p.state = p.after_value_state; + *token = Token.initNumber(p.count, p.number_is_integer); + return true; + }, + } + }, + + State.NumberMaybeExponent => { + p.complete = p.after_value_state == State.TopLevelEnd; + switch (c) { + 'e', 'E' => { + p.number_is_integer = false; + p.state = State.NumberExponent; + }, + else => { + p.state = p.after_value_state; + *token = Token.initNumber(p.count, p.number_is_integer); + return true; + }, + } + }, + + State.NumberExponent => switch (c) { + '-', '+', => { + p.complete = false; + p.state = State.NumberExponentDigitsRequired; + }, + '0' ... '9' => { + p.complete = p.after_value_state == State.TopLevelEnd; + p.state = State.NumberExponentDigits; + }, + else => { + return error.InvalidNumber; + }, + }, + + State.NumberExponentDigitsRequired => switch (c) { + '0' ... '9' => { + p.complete = p.after_value_state == State.TopLevelEnd; + p.state = State.NumberExponentDigits; + }, + else => { + return error.InvalidNumber; + }, + }, + + State.NumberExponentDigits => { + p.complete = p.after_value_state == State.TopLevelEnd; + switch (c) { + '0' ... '9' => { + // another digit + }, + else => { + p.state = p.after_value_state; + *token = Token.initNumber(p.count, p.number_is_integer); + return true; + }, + } + }, + + State.TrueLiteral1 => switch (c) { + 'r' => p.state = State.TrueLiteral2, + else => return error.InvalidLiteral, + }, + + State.TrueLiteral2 => switch (c) { + 'u' => p.state = State.TrueLiteral3, + else => return error.InvalidLiteral, + }, + + State.TrueLiteral3 => switch (c) { + 'e' => { + p.state = p.after_value_state; + p.complete = p.state == State.TopLevelEnd; + *token = Token.init(TokenId.True, p.count + 1, 1); + }, + else => { + return error.InvalidLiteral; + }, + }, + + State.FalseLiteral1 => switch (c) { + 'a' => p.state = State.FalseLiteral2, + else => return error.InvalidLiteral, + }, + + State.FalseLiteral2 => switch (c) { + 'l' => p.state = State.FalseLiteral3, + else => return error.InvalidLiteral, + }, + + State.FalseLiteral3 => switch (c) { + 's' => p.state = State.FalseLiteral4, + else => return error.InvalidLiteral, + }, + + State.FalseLiteral4 => switch (c) { + 'e' => { + p.state = p.after_value_state; + p.complete = p.state == State.TopLevelEnd; + *token = Token.init(TokenId.False, p.count + 1, 1); + }, + else => { + return error.InvalidLiteral; + }, + }, + + State.NullLiteral1 => switch (c) { + 'u' => p.state = State.NullLiteral2, + else => return error.InvalidLiteral, + }, + + State.NullLiteral2 => switch (c) { + 'l' => p.state = State.NullLiteral3, + else => return error.InvalidLiteral, + }, + + State.NullLiteral3 => switch (c) { + 'l' => { + p.state = p.after_value_state; + p.complete = p.state == State.TopLevelEnd; + *token = Token.init(TokenId.Null, p.count + 1, 1); + }, + else => { + return error.InvalidLiteral; + }, + }, + } + + return false; + } +}; + +// Validate a JSON string. This does not limit number precision so a decoder may not necessarily +// be able to decode the string even if this returns true. +pub fn validate(s: []const u8) bool { + var p = StreamingJsonParser.init(); + + for (s) |c, i| { + var token1: ?Token = undefined; + var token2: ?Token = undefined; + + p.feed(c, &token1, &token2) catch |err| { + return false; + }; + } + + return p.complete; +} + +const Allocator = std.mem.Allocator; +const ArenaAllocator = std.heap.ArenaAllocator; +const ArrayList = std.ArrayList; +const HashMap = std.HashMap; + +pub const ValueTree = struct { + arena: ArenaAllocator, + root: Value, + + pub fn deinit(self: &ValueTree) void { + self.arena.deinit(); + } +}; + +pub const ObjectMap = HashMap([]const u8, Value, mem.hash_slice_u8, mem.eql_slice_u8); + +pub const Value = union(enum) { + Null, + Bool: bool, + Integer: i64, + Float: f64, + String: []const u8, + Array: ArrayList(Value), + Object: ObjectMap, + + pub fn toString(self: &const Value) void { + switch (*self) { + Value.Null => { + std.debug.warn("null"); + }, + Value.Bool => |inner| { + std.debug.warn("{}", inner); + }, + Value.Integer => |inner| { + std.debug.warn("{}", inner); + }, + Value.Float => |inner| { + std.debug.warn("{.5}", inner); + }, + Value.String => |inner| { + std.debug.warn("\"{}\"", inner); + }, + Value.Array => |inner| { + var not_first = false; + std.debug.warn("["); + for (inner.toSliceConst()) |value| { + if (not_first) { + std.debug.warn(","); + } + not_first = true; + value.toString(); + } + std.debug.warn("]"); + }, + Value.Object => |inner| { + var not_first = false; + std.debug.warn("{{"); + var it = inner.iterator(); + + while (it.next()) |entry| { + if (not_first) { + std.debug.warn(","); + } + not_first = true; + std.debug.warn("\"{}\":", entry.key); + entry.value.toString(); + } + std.debug.warn("}}"); + }, + } + } + + pub fn toStringIndent(self: &const Value, indent: usize) void { + if (indent == 0) { + self.toString(); + } else { + self.toStringIndentLevel(indent, 0); + } + } + + fn toStringIndentLevel(self: &const Value, indent: usize, level: usize) void { + switch (*self) { + Value.Null => { + std.debug.warn("null"); + }, + Value.Bool => |inner| { + std.debug.warn("{}", inner); + }, + Value.Integer => |inner| { + std.debug.warn("{}", inner); + }, + Value.Float => |inner| { + std.debug.warn("{.5}", inner); + }, + Value.String => |inner| { + std.debug.warn("\"{}\"", inner); + }, + Value.Array => |inner| { + var not_first = false; + std.debug.warn("[\n"); + + for (inner.toSliceConst()) |value| { + if (not_first) { + std.debug.warn(",\n"); + } + not_first = true; + padSpace(level + indent); + value.toStringIndentLevel(indent, level + indent); + } + std.debug.warn("\n"); + padSpace(level); + std.debug.warn("]"); + }, + Value.Object => |inner| { + var not_first = false; + std.debug.warn("{{\n"); + var it = inner.iterator(); + + while (it.next()) |entry| { + if (not_first) { + std.debug.warn(",\n"); + } + not_first = true; + padSpace(level + indent); + std.debug.warn("\"{}\": ", entry.key); + entry.value.toStringIndentLevel(indent, level + indent); + } + std.debug.warn("\n"); + padSpace(level); + std.debug.warn("}}"); + }, + } + } + + fn padSpace(indent: usize) void { + var i: usize = 0; + while (i < indent) : (i += 1) { + std.debug.warn(" "); + } + } +}; + +// A non-stream JSON parser which constructs a tree of Value's. +const JsonParser = struct { + allocator: &Allocator, + state: State, + copy_strings: bool, + // Stores parent nodes and un-combined Values. + // Worst case scenario we have nested key, values and so need two times the stack size. + stack: [2 * StreamingJsonParser.max_stack_size]Value, + stack_used: u16, + + const State = enum { + ObjectKey, + ObjectValue, + ArrayValue, + Simple, + }; + + pub fn init(allocator: &Allocator, copy_strings: bool) JsonParser { + return JsonParser { + .allocator = allocator, + .state = State.Simple, + .copy_strings = copy_strings, + .stack = undefined, + .stack_used = 0, + }; + } + + pub fn reset(p: &JsonParser) void { + p.state = State.Simple; + p.stack_used = 0; + } + + pub fn parse(p: &JsonParser, input: []const u8) !ValueTree { + var mp = StreamingJsonParser.init(); + + var arena = ArenaAllocator.init(p.allocator); + errdefer arena.deinit(); + + for (input) |c, i| { + var mt1: ?Token = undefined; + var mt2: ?Token = undefined; + + try mp.feed(c, &mt1, &mt2); + if (mt1) |t1| { + try p.transition(&arena.allocator, input, i, t1); + + if (mt2) |t2| { + try p.transition(&arena.allocator, input, i, t2); + } + } + } + + // Handle top-level lonely number values. + { + const i = input.len; + var mt1: ?Token = undefined; + var mt2: ?Token = undefined; + + try mp.feed(' ', &mt1, &mt2); + if (mt1) |t1| { + try p.transition(&arena.allocator, input, i, t1); + } + } + + if (!mp.complete) { + return error.IncompleteJsonInput; + } + + std.debug.assert(p.stack_used == 1); + + return ValueTree { + .arena = arena, + .root = p.stack[0], + }; + } + + // Even though p.allocator exists, we take an explicit allocator so that allocation state + // can be cleaned up on error correctly during a `parse` on call. + fn transition(p: &JsonParser, allocator: &Allocator, input: []const u8, i: usize, token: &const Token) !void { + switch (p.state) { + State.ObjectKey => switch (token.id) { + TokenId.ObjectEnd => { + if (p.stack_used == 1) { + return; + } + + var value = p.stack[p.stack_used - 1]; + p.stack_used -= 1; + try p.pushToParent(value); + }, + TokenId.String => { + p.pushStack(try p.parseString(allocator, token, input, i)); + p.state = State.ObjectValue; + }, + else => { + unreachable; + }, + }, + State.ObjectValue => { + var object = &p.stack[p.stack_used - 2].Object; + var key = p.stack[p.stack_used - 1].String; + + switch (token.id) { + TokenId.ObjectBegin => { + p.pushStack(Value { .Object = ObjectMap.init(allocator) }); + p.state = State.ObjectKey; + }, + TokenId.ArrayBegin => { + p.pushStack(Value { .Array = ArrayList(Value).init(allocator) }); + p.state = State.ArrayValue; + }, + TokenId.String => { + _ = try object.put(key, try p.parseString(allocator, token, input, i)); + p.stack_used -= 1; + p.state = State.ObjectKey; + }, + TokenId.Number => { + _ = try object.put(key, try p.parseNumber(token, input, i)); + p.stack_used -= 1; + p.state = State.ObjectKey; + }, + TokenId.True => { + _ = try object.put(key, Value { .Bool = true }); + p.stack_used -= 1; + p.state = State.ObjectKey; + }, + TokenId.False => { + _ = try object.put(key, Value { .Bool = false }); + p.stack_used -= 1; + p.state = State.ObjectKey; + }, + TokenId.Null => { + _ = try object.put(key, Value.Null); + p.stack_used -= 1; + p.state = State.ObjectKey; + }, + else => { + unreachable; + }, + } + }, + State.ArrayValue => { + var array = &p.stack[p.stack_used - 1].Array; + + switch (token.id) { + TokenId.ArrayEnd => { + if (p.stack_used == 1) { + return; + } + + var value = p.stack[p.stack_used - 1]; + p.stack_used -= 1; + try p.pushToParent(value); + }, + TokenId.ObjectBegin => { + p.pushStack(Value { .Object = ObjectMap.init(allocator) }); + p.state = State.ObjectKey; + }, + TokenId.ArrayBegin => { + p.pushStack(Value { .Array = ArrayList(Value).init(allocator) }); + p.state = State.ArrayValue; + }, + TokenId.String => { + try array.append(try p.parseString(allocator, token, input, i)); + }, + TokenId.Number => { + try array.append(try p.parseNumber(token, input, i)); + }, + TokenId.True => { + try array.append(Value { .Bool = true }); + }, + TokenId.False => { + try array.append(Value { .Bool = false }); + }, + TokenId.Null => { + try array.append(Value.Null); + }, + else => { + unreachable; + }, + } + }, + State.Simple => switch (token.id) { + TokenId.ObjectBegin => { + p.pushStack(Value { .Object = ObjectMap.init(allocator) }); + p.state = State.ObjectKey; + }, + TokenId.ArrayBegin => { + p.pushStack(Value { .Array = ArrayList(Value).init(allocator) }); + p.state = State.ArrayValue; + }, + TokenId.String => { + p.pushStack(try p.parseString(allocator, token, input, i)); + }, + TokenId.Number => { + p.pushStack(try p.parseNumber(token, input, i)); + }, + TokenId.True => { + p.pushStack(Value { .Bool = true }); + }, + TokenId.False => { + p.pushStack(Value { .Bool = false }); + }, + TokenId.Null => { + p.pushStack(Value.Null); + }, + TokenId.ObjectEnd, TokenId.ArrayEnd => { + unreachable; + }, + }, + } + } + + fn pushToParent(p: &JsonParser, value: &const Value) !void { + switch (p.stack[p.stack_used - 1]) { + // Object Parent -> [ ..., object, , value ] + Value.String => |key| { + p.stack_used -= 1; + + var object = &p.stack[p.stack_used - 1].Object; + _ = try object.put(key, value); + p.state = State.ObjectKey; + }, + // Array Parent -> [ ..., , value ] + Value.Array => |*array| { + try array.append(value); + p.state = State.ArrayValue; + }, + else => { + unreachable; + }, + } + } + + fn pushStack(p: &JsonParser, value: &const Value) void { + p.stack[p.stack_used] = *value; + p.stack_used += 1; + } + + fn parseString(p: &JsonParser, allocator: &Allocator, token: &const Token, input: []const u8, i: usize) !Value { + // TODO: We don't strictly have to copy values which do not contain any escape + // characters if flagged with the option. + const slice = token.slice(input, i); + return Value { .String = try mem.dupe(p.allocator, u8, slice) }; + } + + fn parseNumber(p: &JsonParser, token: &const Token, input: []const u8, i: usize) !Value { + return if (token.number_is_integer) + Value { .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } + else + @panic("TODO: fmt.parseFloat not yet implemented") + ; + } +}; + +const debug = std.debug; + +test "json parser dynamic" { + var p = JsonParser.init(std.debug.global_allocator, false); + + const s = + \\{ + \\ "Image": { + \\ "Width": 800, + \\ "Height": 600, + \\ "Title": "View from 15th Floor", + \\ "Thumbnail": { + \\ "Url": "http://www.example.com/image/481989943", + \\ "Height": 125, + \\ "Width": 100 + \\ }, + \\ "Animated" : false, + \\ "IDs": [116, 943, 234, 38793] + \\ } + \\} + ; + + var tree = try p.parse(s); + tree.deinit(); + var root = tree.root; + + var image = (??root.Object.get("Image")).value; + + const width = (??image.Object.get("Width")).value; + debug.assert(width.Integer == 800); + + const height = (??image.Object.get("Height")).value; + debug.assert(height.Integer == 600); + + const title = (??image.Object.get("Title")).value; + debug.assert(mem.eql(u8, title.String, "View from 15th Floor")); + + const animated = (??image.Object.get("Animated")).value; + debug.assert(animated.Bool == false); +} diff --git a/std/json_test.zig b/std/json_test.zig new file mode 100644 index 0000000000..acbdeb3487 --- /dev/null +++ b/std/json_test.zig @@ -0,0 +1,1942 @@ +// RFC 8529 conformance tests. +// +// Tests are taken from https://github.com/nst/JSONTestSuite +// Read also http://seriot.ch/parsing_json.php for a good overview. + +const std = @import("index.zig"); + +fn ok(comptime s: []const u8) void { + std.debug.assert(std.json.validate(s)); +} + +fn err(comptime s: []const u8) void { + std.debug.assert(!std.json.validate(s)); +} + +fn any(comptime s: []const u8) void { + std.debug.assert(true); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +test "y_array_arraysWithSpaces" { + ok( + \\[[] ] + ); +} + +test "y_array_empty" { + ok( + \\[] + ); +} + +test "y_array_empty-string" { + ok( + \\[""] + ); +} + +test "y_array_ending_with_newline" { + ok( + \\["a"] + ); +} + +test "y_array_false" { + ok( + \\[false] + ); +} + +test "y_array_heterogeneous" { + ok( + \\[null, 1, "1", {}] + ); +} + +test "y_array_null" { + ok( + \\[null] + ); +} + +test "y_array_with_1_and_newline" { + ok( + \\[1 + \\] + ); +} + +test "y_array_with_leading_space" { + ok( + \\ [1] + ); +} + +test "y_array_with_several_null" { + ok( + \\[1,null,null,null,2] + ); +} + +test "y_array_with_trailing_space" { + ok( + "[2] " + ); +} + +test "y_number_0e+1" { + ok( + \\[0e+1] + ); +} + +test "y_number_0e1" { + ok( + \\[0e1] + ); +} + +test "y_number_after_space" { + ok( + \\[ 4] + ); +} + +test "y_number_double_close_to_zero" { + ok( + \\[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] + ); +} + +test "y_number_int_with_exp" { + ok( + \\[20e1] + ); +} + +test "y_number" { + ok( + \\[123e65] + ); +} + +test "y_number_minus_zero" { + ok( + \\[-0] + ); +} + +test "y_number_negative_int" { + ok( + \\[-123] + ); +} + +test "y_number_negative_one" { + ok( + \\[-1] + ); +} + +test "y_number_negative_zero" { + ok( + \\[-0] + ); +} + +test "y_number_real_capital_e" { + ok( + \\[1E22] + ); +} + +test "y_number_real_capital_e_neg_exp" { + ok( + \\[1E-2] + ); +} + +test "y_number_real_capital_e_pos_exp" { + ok( + \\[1E+2] + ); +} + +test "y_number_real_exponent" { + ok( + \\[123e45] + ); +} + +test "y_number_real_fraction_exponent" { + ok( + \\[123.456e78] + ); +} + +test "y_number_real_neg_exp" { + ok( + \\[1e-2] + ); +} + +test "y_number_real_pos_exponent" { + ok( + \\[1e+2] + ); +} + +test "y_number_simple_int" { + ok( + \\[123] + ); +} + +test "y_number_simple_real" { + ok( + \\[123.456789] + ); +} + +test "y_object_basic" { + ok( + \\{"asd":"sdf"} + ); +} + +test "y_object_duplicated_key_and_value" { + ok( + \\{"a":"b","a":"b"} + ); +} + +test "y_object_duplicated_key" { + ok( + \\{"a":"b","a":"c"} + ); +} + +test "y_object_empty" { + ok( + \\{} + ); +} + +test "y_object_empty_key" { + ok( + \\{"":0} + ); +} + +test "y_object_escaped_null_in_key" { + ok( + \\{"foo\u0000bar": 42} + ); +} + +test "y_object_extreme_numbers" { + ok( + \\{ "min": -1.0e+28, "max": 1.0e+28 } + ); +} + +test "y_object" { + ok( + \\{"asd":"sdf", "dfg":"fgh"} + ); +} + +test "y_object_long_strings" { + ok( + \\{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} + ); +} + +test "y_object_simple" { + ok( + \\{"a":[]} + ); +} + +test "y_object_string_unicode" { + ok( + \\{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" } + ); +} + +test "y_object_with_newlines" { + ok( + \\{ + \\"a": "b" + \\} + ); +} + +test "y_string_1_2_3_bytes_UTF-8_sequences" { + ok( + \\["\u0060\u012a\u12AB"] + ); +} + +test "y_string_accepted_surrogate_pair" { + ok( + \\["\uD801\udc37"] + ); +} + +test "y_string_accepted_surrogate_pairs" { + ok( + \\["\ud83d\ude39\ud83d\udc8d"] + ); +} + +test "y_string_allowed_escapes" { + ok( + \\["\"\\\/\b\f\n\r\t"] + ); +} + +test "y_string_backslash_and_u_escaped_zero" { + ok( + \\["\\u0000"] + ); +} + +test "y_string_backslash_doublequotes" { + ok( + \\["\""] + ); +} + +test "y_string_comments" { + ok( + \\["a/*b*/c/*d//e"] + ); +} + +test "y_string_double_escape_a" { + ok( + \\["\\a"] + ); +} + +test "y_string_double_escape_n" { + ok( + \\["\\n"] + ); +} + +test "y_string_escaped_control_character" { + ok( + \\["\u0012"] + ); +} + +test "y_string_escaped_noncharacter" { + ok( + \\["\uFFFF"] + ); +} + +test "y_string_in_array" { + ok( + \\["asd"] + ); +} + +test "y_string_in_array_with_leading_space" { + ok( + \\[ "asd"] + ); +} + +test "y_string_last_surrogates_1_and_2" { + ok( + \\["\uDBFF\uDFFF"] + ); +} + +test "y_string_nbsp_uescaped" { + ok( + \\["new\u00A0line"] + ); +} + +test "y_string_nonCharacterInUTF-8_U+10FFFF" { + ok( + \\["􏿿"] + ); +} + +test "y_string_nonCharacterInUTF-8_U+FFFF" { + ok( + \\["￿"] + ); +} + +test "y_string_null_escape" { + ok( + \\["\u0000"] + ); +} + +test "y_string_one-byte-utf-8" { + ok( + \\["\u002c"] + ); +} + +test "y_string_pi" { + ok( + \\["π"] + ); +} + +test "y_string_reservedCharacterInUTF-8_U+1BFFF" { + ok( + \\["𛿿"] + ); +} + +test "y_string_simple_ascii" { + ok( + \\["asd "] + ); +} + +test "y_string_space" { + ok( + \\" " + ); +} + +test "y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF" { + ok( + \\["\uD834\uDd1e"] + ); +} + +test "y_string_three-byte-utf-8" { + ok( + \\["\u0821"] + ); +} + +test "y_string_two-byte-utf-8" { + ok( + \\["\u0123"] + ); +} + +test "y_string_u+2028_line_sep" { + ok( + \\["
"] + ); +} + +test "y_string_u+2029_par_sep" { + ok( + \\["
"] + ); +} + +test "y_string_uescaped_newline" { + ok( + \\["new\u000Aline"] + ); +} + +test "y_string_uEscape" { + ok( + \\["\u0061\u30af\u30EA\u30b9"] + ); +} + +test "y_string_unescaped_char_delete" { + ok( + \\[""] + ); +} + +test "y_string_unicode_2" { + ok( + \\["⍂㈴⍂"] + ); +} + +test "y_string_unicodeEscapedBackslash" { + ok( + \\["\u005C"] + ); +} + +test "y_string_unicode_escaped_double_quote" { + ok( + \\["\u0022"] + ); +} + +test "y_string_unicode" { + ok( + \\["\uA66D"] + ); +} + +test "y_string_unicode_U+10FFFE_nonchar" { + ok( + \\["\uDBFF\uDFFE"] + ); +} + +test "y_string_unicode_U+1FFFE_nonchar" { + ok( + \\["\uD83F\uDFFE"] + ); +} + +test "y_string_unicode_U+200B_ZERO_WIDTH_SPACE" { + ok( + \\["\u200B"] + ); +} + +test "y_string_unicode_U+2064_invisible_plus" { + ok( + \\["\u2064"] + ); +} + +test "y_string_unicode_U+FDD0_nonchar" { + ok( + \\["\uFDD0"] + ); +} + +test "y_string_unicode_U+FFFE_nonchar" { + ok( + \\["\uFFFE"] + ); +} + +test "y_string_utf8" { + ok( + \\["€𝄞"] + ); +} + +test "y_string_with_del_character" { + ok( + \\["aa"] + ); +} + +test "y_structure_lonely_false" { + ok( + \\false + ); +} + +test "y_structure_lonely_int" { + ok( + \\42 + ); +} + +test "y_structure_lonely_negative_real" { + ok( + \\-0.1 + ); +} + +test "y_structure_lonely_null" { + ok( + \\null + ); +} + +test "y_structure_lonely_string" { + ok( + \\"asd" + ); +} + +test "y_structure_lonely_true" { + ok( + \\true + ); +} + +test "y_structure_string_empty" { + ok( + \\"" + ); +} + +test "y_structure_trailing_newline" { + ok( + \\["a"] + ); +} + +test "y_structure_true_in_array" { + ok( + \\[true] + ); +} + +test "y_structure_whitespace_array" { + ok( + " [] " + ); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +test "n_array_1_true_without_comma" { + err( + \\[1 true] + ); +} + +test "n_array_a_invalid_utf8" { + err( + \\[aå] + ); +} + +test "n_array_colon_instead_of_comma" { + err( + \\["": 1] + ); +} + +test "n_array_comma_after_close" { + //err( + // \\[""], + //); +} + +test "n_array_comma_and_number" { + err( + \\[,1] + ); +} + +test "n_array_double_comma" { + err( + \\[1,,2] + ); +} + +test "n_array_double_extra_comma" { + err( + \\["x",,] + ); +} + +test "n_array_extra_close" { + err( + \\["x"]] + ); +} + +test "n_array_extra_comma" { + //err( + // \\["",] + //); +} + +test "n_array_incomplete_invalid_value" { + err( + \\[x + ); +} + +test "n_array_incomplete" { + err( + \\["x" + ); +} + +test "n_array_inner_array_no_comma" { + err( + \\[3[4]] + ); +} + +test "n_array_invalid_utf8" { + err( + \\[ÿ] + ); +} + +test "n_array_items_separated_by_semicolon" { + err( + \\[1:2] + ); +} + +test "n_array_just_comma" { + err( + \\[,] + ); +} + +test "n_array_just_minus" { + err( + \\[-] + ); +} + +test "n_array_missing_value" { + err( + \\[ , ""] + ); +} + +test "n_array_newlines_unclosed" { + err( + \\["a", + \\4 + \\,1, + ); +} + + +test "n_array_number_and_comma" { + err( + \\[1,] + ); +} + +test "n_array_number_and_several_commas" { + err( + \\[1,,] + ); +} + +test "n_array_spaces_vertical_tab_formfeed" { + err( + \\[" a"\f] + ); +} + +test "n_array_star_inside" { + err( + \\[*] + ); +} + +test "n_array_unclosed" { + err( + \\["" + ); +} + +test "n_array_unclosed_trailing_comma" { + err( + \\[1, + ); +} + +test "n_array_unclosed_with_new_lines" { + err( + \\[1, + \\1 + \\,1 + ); +} + +test "n_array_unclosed_with_object_inside" { + err( + \\[{} + ); +} + +test "n_incomplete_false" { + err( + \\[fals] + ); +} + +test "n_incomplete_null" { + err( + \\[nul] + ); +} + +test "n_incomplete_true" { + err( + \\[tru] + ); +} + +test "n_multidigit_number_then_00" { + err( + \\123 + ); +} + +test "n_number_0.1.2" { + err( + \\[0.1.2] + ); +} + +test "n_number_-01" { + err( + \\[-01] + ); +} + +test "n_number_0.3e" { + err( + \\[0.3e] + ); +} + +test "n_number_0.3e+" { + err( + \\[0.3e+] + ); +} + +test "n_number_0_capital_E" { + err( + \\[0E] + ); +} + +test "n_number_0_capital_E+" { + err( + \\[0E+] + ); +} + +test "n_number_0.e1" { + err( + \\[0.e1] + ); +} + +test "n_number_0e" { + err( + \\[0e] + ); +} + +test "n_number_0e+" { + err( + \\[0e+] + ); +} + +test "n_number_1_000" { + err( + \\[1 000.0] + ); +} + +test "n_number_1.0e-" { + err( + \\[1.0e-] + ); +} + +test "n_number_1.0e" { + err( + \\[1.0e] + ); +} + +test "n_number_1.0e+" { + err( + \\[1.0e+] + ); +} + +test "n_number_-1.0." { + err( + \\[-1.0.] + ); +} + +test "n_number_1eE2" { + err( + \\[1eE2] + ); +} + +test "n_number_.-1" { + err( + \\[.-1] + ); +} + +test "n_number_+1" { + err( + \\[+1] + ); +} + +test "n_number_.2e-3" { + err( + \\[.2e-3] + ); +} + +test "n_number_2.e-3" { + err( + \\[2.e-3] + ); +} + +test "n_number_2.e+3" { + err( + \\[2.e+3] + ); +} + +test "n_number_2.e3" { + err( + \\[2.e3] + ); +} + +test "n_number_-2." { + err( + \\[-2.] + ); +} + +test "n_number_9.e+" { + err( + \\[9.e+] + ); +} + +test "n_number_expression" { + err( + \\[1+2] + ); +} + +test "n_number_hex_1_digit" { + err( + \\[0x1] + ); +} + +test "n_number_hex_2_digits" { + err( + \\[0x42] + ); +} + +test "n_number_infinity" { + err( + \\[Infinity] + ); +} + +test "n_number_+Inf" { + err( + \\[+Inf] + ); +} + +test "n_number_Inf" { + err( + \\[Inf] + ); +} + +test "n_number_invalid+-" { + err( + \\[0e+-1] + ); +} + +test "n_number_invalid-negative-real" { + err( + \\[-123.123foo] + ); +} + +test "n_number_invalid-utf-8-in-bigger-int" { + err( + \\[123å] + ); +} + +test "n_number_invalid-utf-8-in-exponent" { + err( + \\[1e1å] + ); +} + +test "n_number_invalid-utf-8-in-int" { + err( + \\[0å] + ); +} + + +test "n_number_++" { + err( + \\[++1234] + ); +} + +test "n_number_minus_infinity" { + err( + \\[-Infinity] + ); +} + +test "n_number_minus_sign_with_trailing_garbage" { + err( + \\[-foo] + ); +} + +test "n_number_minus_space_1" { + err( + \\[- 1] + ); +} + +test "n_number_-NaN" { + err( + \\[-NaN] + ); +} + +test "n_number_NaN" { + err( + \\[NaN] + ); +} + +test "n_number_neg_int_starting_with_zero" { + err( + \\[-012] + ); +} + +test "n_number_neg_real_without_int_part" { + err( + \\[-.123] + ); +} + +test "n_number_neg_with_garbage_at_end" { + err( + \\[-1x] + ); +} + +test "n_number_real_garbage_after_e" { + err( + \\[1ea] + ); +} + +test "n_number_real_with_invalid_utf8_after_e" { + err( + \\[1eå] + ); +} + +test "n_number_real_without_fractional_part" { + err( + \\[1.] + ); +} + +test "n_number_starting_with_dot" { + err( + \\[.123] + ); +} + +test "n_number_U+FF11_fullwidth_digit_one" { + err( + \\[1] + ); +} + +test "n_number_with_alpha_char" { + err( + \\[1.8011670033376514H-308] + ); +} + +test "n_number_with_alpha" { + err( + \\[1.2a-3] + ); +} + +test "n_number_with_leading_zero" { + err( + \\[012] + ); +} + +test "n_object_bad_value" { + err( + \\["x", truth] + ); +} + +test "n_object_bracket_key" { + err( + \\{[: "x"} + ); +} + +test "n_object_comma_instead_of_colon" { + err( + \\{"x", null} + ); +} + +test "n_object_double_colon" { + err( + \\{"x"::"b"} + ); +} + +test "n_object_emoji" { + err( + \\{🇨🇭} + ); +} + +test "n_object_garbage_at_end" { + err( + \\{"a":"a" 123} + ); +} + +test "n_object_key_with_single_quotes" { + err( + \\{key: 'value'} + ); +} + +test "n_object_lone_continuation_byte_in_key_and_trailing_comma" { + err( + \\{"¹":"0",} + ); +} + +test "n_object_missing_colon" { + err( + \\{"a" b} + ); +} + +test "n_object_missing_key" { + err( + \\{:"b"} + ); +} + +test "n_object_missing_semicolon" { + err( + \\{"a" "b"} + ); +} + +test "n_object_missing_value" { + err( + \\{"a": + ); +} + +test "n_object_no-colon" { + err( + \\{"a" + ); +} + +test "n_object_non_string_key_but_huge_number_instead" { + err( + \\{9999E9999:1} + ); +} + +test "n_object_non_string_key" { + err( + \\{1:1} + ); +} + +test "n_object_repeated_null_null" { + err( + \\{null:null,null:null} + ); +} + +test "n_object_several_trailing_commas" { + err( + \\{"id":0,,,,,} + ); +} + +test "n_object_single_quote" { + err( + \\{'a':0} + ); +} + +test "n_object_trailing_comma" { + err( + \\{"id":0,} + ); +} + +test "n_object_trailing_comment" { + err( + \\{"a":"b"}/**/ + ); +} + +test "n_object_trailing_comment_open" { + err( + \\{"a":"b"}/**// + ); +} + +test "n_object_trailing_comment_slash_open_incomplete" { + err( + \\{"a":"b"}/ + ); +} + +test "n_object_trailing_comment_slash_open" { + err( + \\{"a":"b"}// + ); +} + +test "n_object_two_commas_in_a_row" { + err( + \\{"a":"b",,"c":"d"} + ); +} + +test "n_object_unquoted_key" { + err( + \\{a: "b"} + ); +} + +test "n_object_unterminated-value" { + err( + \\{"a":"a + ); + } + +test "n_object_with_single_string" { + err( + \\{ "foo" : "bar", "a" } + ); +} + +test "n_object_with_trailing_garbage" { + err( + \\{"a":"b"}# + ); +} + +test "n_single_space" { + err( + " " + ); +} + +test "n_string_1_surrogate_then_escape" { + err( + \\["\uD800\"] + ); +} + +test "n_string_1_surrogate_then_escape_u1" { + err( + \\["\uD800\u1"] + ); +} + +test "n_string_1_surrogate_then_escape_u1x" { + err( + \\["\uD800\u1x"] + ); +} + +test "n_string_1_surrogate_then_escape_u" { + err( + \\["\uD800\u"] + ); +} + +test "n_string_accentuated_char_no_quotes" { + err( + \\[é] + ); +} + +test "n_string_backslash_00" { + err( + \\["\"] + ); +} + +test "n_string_escaped_backslash_bad" { + err( + \\["\\\"] + ); +} + +test "n_string_escaped_ctrl_char_tab" { + err( + \\["\ "] + ); +} + +test "n_string_escaped_emoji" { + err( + \\["\🌀"] + ); +} + +test "n_string_escape_x" { + err( + \\["\x00"] + ); +} + +test "n_string_incomplete_escaped_character" { + err( + \\["\u00A"] + ); +} + +test "n_string_incomplete_escape" { + err( + \\["\"] + ); +} + +test "n_string_incomplete_surrogate_escape_invalid" { + err( + \\["\uD800\uD800\x"] + ); +} + +test "n_string_incomplete_surrogate" { + err( + \\["\uD834\uDd"] + ); +} + +test "n_string_invalid_backslash_esc" { + err( + \\["\a"] + ); +} + +test "n_string_invalid_unicode_escape" { + err( + \\["\uqqqq"] + ); +} + +test "n_string_invalid_utf8_after_escape" { + err( + \\["\å"] + ); +} + +test "n_string_invalid-utf-8-in-escape" { + err( + \\["\uå"] + ); +} + +test "n_string_leading_uescaped_thinspace" { + err( + \\[\u0020"asd"] + ); +} + +test "n_string_no_quotes_with_bad_escape" { + err( + \\[\n] + ); +} + +test "n_string_single_doublequote" { + err( + \\" + ); +} + +test "n_string_single_quote" { + err( + \\['single quote'] + ); +} + +test "n_string_single_string_no_double_quotes" { + err( + \\abc + ); +} + +test "n_string_start_escape_unclosed" { + err( + \\["\ + ); +} + +test "n_string_unescaped_crtl_char" { + err( + \\["aa"] + ); +} + +test "n_string_unescaped_newline" { + err( + \\["new + \\line"] + ); +} + +test "n_string_unescaped_tab" { + err( + \\[" "] + ); +} + +test "n_string_unicode_CapitalU" { + err( + \\"\UA66D" + ); +} + +test "n_string_with_trailing_garbage" { + err( + \\""x + ); +} + +test "n_structure_100000_opening_arrays" { + err( + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + ); +} + +test "n_structure_angle_bracket_." { + err( + \\<.> + ); +} + +test "n_structure_angle_bracket_null" { + err( + \\[] + ); +} + +test "n_structure_array_trailing_garbage" { + err( + \\[1]x + ); +} + +test "n_structure_array_with_extra_array_close" { + err( + \\[1]] + ); +} + +test "n_structure_array_with_unclosed_string" { + err( + \\["asd] + ); +} + +test "n_structure_ascii-unicode-identifier" { + err( + \\aÃ¥ + ); +} + +test "n_structure_capitalized_True" { + err( + \\[True] + ); +} + +test "n_structure_close_unopened_array" { + err( + \\1] + ); +} + +test "n_structure_comma_instead_of_closing_brace" { + err( + \\{"x": true, + ); +} + +test "n_structure_double_array" { + err( + \\[][] + ); +} + +test "n_structure_end_array" { + err( + \\] + ); +} + +test "n_structure_incomplete_UTF8_BOM" { + err( + \\ï»{} + ); +} + +test "n_structure_lone-invalid-utf-8" { + err( + \\å + ); +} + +test "n_structure_lone-open-bracket" { + err( + \\[ + ); +} + +test "n_structure_no_data" { + err( + \\ + ); +} + +test "n_structure_null-byte-outside-string" { + err( + \\[] + ); +} + +test "n_structure_number_with_trailing_garbage" { + err( + \\2@ + ); +} + +test "n_structure_object_followed_by_closing_object" { + err( + \\{}} + ); +} + +test "n_structure_object_unclosed_no_value" { + err( + \\{"": + ); +} + +test "n_structure_object_with_comment" { + err( + \\{"a":/*comment*/"b"} + ); +} + +test "n_structure_object_with_trailing_garbage" { + err( + \\{"a": true} "x" + ); +} + +test "n_structure_open_array_apostrophe" { + err( + \\[' + ); +} + +test "n_structure_open_array_comma" { + err( + \\[, + ); +} + +test "n_structure_open_array_object" { + err( + \\[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"": + ); +} + +test "n_structure_open_array_open_object" { + err( + \\[{ + ); +} + +test "n_structure_open_array_open_string" { + err( + \\["a + ); +} + +test "n_structure_open_array_string" { + err( + \\["a" + ); +} + +test "n_structure_open_object_close_array" { + err( + \\{] + ); +} + +test "n_structure_open_object_comma" { + err( + \\{, + ); +} + +test "n_structure_open_object" { + err( + \\{ + ); +} + +test "n_structure_open_object_open_array" { + err( + \\{[ + ); +} + +test "n_structure_open_object_open_string" { + err( + \\{"a + ); +} + +test "n_structure_open_object_string_with_apostrophes" { + err( + \\{'a' + ); +} + +test "n_structure_open_open" { + err( + \\["\{["\{["\{["\{ + ); +} + +test "n_structure_single_eacute" { + err( + \\é + ); +} + +test "n_structure_single_star" { + err( + \\* + ); +} + +test "n_structure_trailing_#" { + err( + \\{"a":"b"}#{} + ); +} + +test "n_structure_U+2060_word_joined" { + err( + \\[⁠] + ); +} + +test "n_structure_uescaped_LF_before_string" { + err( + \\[\u000A""] + ); +} + +test "n_structure_unclosed_array" { + err( + \\[1 + ); +} + +test "n_structure_unclosed_array_partial_null" { + err( + \\[ false, nul + ); +} + +test "n_structure_unclosed_array_unfinished_false" { + err( + \\[ true, fals + ); +} + +test "n_structure_unclosed_array_unfinished_true" { + err( + \\[ false, tru + ); +} + +test "n_structure_unclosed_object" { + err( + \\{"asd":"asd" + ); +} + +test "n_structure_unicode-identifier" { + err( + \\Ã¥ + ); +} + +test "n_structure_UTF8_BOM_no_data" { + err( + \\ + ); +} + +test "n_structure_whitespace_formfeed" { + err( + \\[ ] + ); +} + +test "n_structure_whitespace_U+2060_word_joiner" { + err( + \\[⁠] + ); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +test "i_number_double_huge_neg_exp" { + any( + \\[123.456e-789] + ); +} + +test "i_number_huge_exp" { + any( + \\[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] + ); +} + +test "i_number_neg_int_huge_exp" { + any( + \\[-1e+9999] + ); +} + +test "i_number_pos_double_huge_exp" { + any( + \\[1.5e+9999] + ); +} + +test "i_number_real_neg_overflow" { + any( + \\[-123123e100000] + ); +} + +test "i_number_real_pos_overflow" { + any( + \\[123123e100000] + ); +} + +test "i_number_real_underflow" { + any( + \\[123e-10000000] + ); +} + +test "i_number_too_big_neg_int" { + any( + \\[-123123123123123123123123123123] + ); +} + +test "i_number_too_big_pos_int" { + any( + \\[100000000000000000000] + ); +} + +test "i_number_very_big_negative_int" { + any( + \\[-237462374673276894279832749832423479823246327846] + ); +} + +test "i_object_key_lone_2nd_surrogate" { + any( + \\{"\uDFAA":0} + ); +} + +test "i_string_1st_surrogate_but_2nd_missing" { + any( + \\["\uDADA"] + ); +} + +test "i_string_1st_valid_surrogate_2nd_invalid" { + any( + \\["\uD888\u1234"] + ); +} + +test "i_string_incomplete_surrogate_and_escape_valid" { + any( + \\["\uD800\n"] + ); +} + +test "i_string_incomplete_surrogate_pair" { + any( + \\["\uDd1ea"] + ); +} + +test "i_string_incomplete_surrogates_escape_valid" { + any( + \\["\uD800\uD800\n"] + ); +} + +test "i_string_invalid_lonely_surrogate" { + any( + \\["\ud800"] + ); +} + +test "i_string_invalid_surrogate" { + any( + \\["\ud800abc"] + ); +} + +test "i_string_invalid_utf-8" { + any( + \\["ÿ"] + ); +} + +test "i_string_inverted_surrogates_U+1D11E" { + any( + \\["\uDd1e\uD834"] + ); +} + +test "i_string_iso_latin_1" { + any( + \\["é"] + ); +} + +test "i_string_lone_second_surrogate" { + any( + \\["\uDFAA"] + ); +} + +test "i_string_lone_utf8_continuation_byte" { + any( + \\[""] + ); +} + +test "i_string_not_in_unicode_range" { + any( + \\["ô¿¿¿"] + ); +} + +test "i_string_overlong_sequence_2_bytes" { + any( + \\["À¯"] + ); +} + +test "i_string_overlong_sequence_6_bytes" { + any( + \\["üƒ¿¿¿¿"] + ); +} + +test "i_string_overlong_sequence_6_bytes_null" { + any( + \\["ü€€€€€"] + ); +} + +test "i_string_truncated-utf-8" { + any( + \\["àÿ"] + ); +} + +test "i_string_utf16BE_no_BOM" { + any( + \\["é"] + ); +} + +test "i_string_utf16LE_no_BOM" { + any( + \\["é"] + ); +} + +test "i_string_UTF-16LE_with_BOM" { + any( + \\ÿþ["é"] + ); +} + +test "i_string_UTF-8_invalid_sequence" { + any( + \\["日шú"] + ); +} + +test "i_string_UTF8_surrogate_U+D800" { + any( + \\["í €"] + ); +} + +test "i_structure_500_nested_arrays" { + any( + \\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + ); +} + +test "i_structure_UTF-8_BOM_empty_object" { + any( + \\{} + ); +} -- cgit v1.2.3 From f17472635ee8bb2fbce8a1105059cd919e1b6d5b Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 3 May 2018 19:16:06 +1200 Subject: Fix review comments for json decoder --- std/json.zig | 154 +++++++++++++++++++++++++++--------------------------- std/json_test.zig | 6 +-- 2 files changed, 80 insertions(+), 80 deletions(-) diff --git a/std/json.zig b/std/json.zig index 395adc4879..5a25433140 100644 --- a/std/json.zig +++ b/std/json.zig @@ -8,37 +8,37 @@ const mem = std.mem; const u1 = @IntType(false, 1); const u256 = @IntType(false, 256); -pub const TokenId = enum(u5) { - ObjectBegin, - ObjectEnd, - ArrayBegin, - ArrayEnd, - String, - Number, - True, - False, - Null, -}; - // A single token slice into the parent string. // // Use `token.slice()` on the inptu at the current position to get the current slice. pub const Token = struct { - id: TokenId, + id: Id, // How many bytes do we skip before counting offset: u1, // Whether string contains a \uXXXX sequence and cannot be zero-copied - string_has_unicode_escape: bool, + string_has_escape: bool, // Whether number is simple and can be represented by an integer (i.e. no `.` or `e`) number_is_integer: bool, // How many bytes from the current position behind the start of this token is. count: usize, - pub fn init(id: TokenId, count: usize, offset: u1) Token { + pub const Id = enum { + ObjectBegin, + ObjectEnd, + ArrayBegin, + ArrayEnd, + String, + Number, + True, + False, + Null, + }; + + pub fn init(id: Id, count: usize, offset: u1) Token { return Token { .id = id, .offset = offset, - .string_has_unicode_escape = false, + .string_has_escape = false, .number_is_integer = true, .count = count, }; @@ -46,9 +46,9 @@ pub const Token = struct { pub fn initString(count: usize, has_unicode_escape: bool) Token { return Token { - .id = TokenId.String, + .id = Id.String, .offset = 0, - .string_has_unicode_escape = has_unicode_escape, + .string_has_escape = has_unicode_escape, .number_is_integer = true, .count = count, }; @@ -56,20 +56,20 @@ pub const Token = struct { pub fn initNumber(count: usize, number_is_integer: bool) Token { return Token { - .id = TokenId.Number, + .id = Id.Number, .offset = 0, - .string_has_unicode_escape = false, + .string_has_escape = false, .number_is_integer = number_is_integer, .count = count, }; } // A marker token is a zero-length - pub fn initMarker(id: TokenId) Token { + pub fn initMarker(id: Id) Token { return Token { .id = id, .offset = 0, - .string_has_unicode_escape = false, + .string_has_escape = false, .number_is_integer = true, .count = 0, }; @@ -98,7 +98,7 @@ const StreamingJsonParser = struct { // If we stopped now, would the complete parsed string to now be a valid json string complete: bool, // Current token flags to pass through to the next generated, see Token. - string_has_unicode_escape: bool, + string_has_escape: bool, number_is_integer: bool, // Bit-stack for nested object/map literals (max 255 nestings). @@ -124,13 +124,11 @@ const StreamingJsonParser = struct { p.stack = 0; p.stack_used = 0; p.complete = false; - p.string_has_unicode_escape = false; + p.string_has_escape = false; p.number_is_integer = true; } pub const State = enum { - // NOTE: @tagName determines the name based on index, not value. - // These must be first with these explicit values as we rely on them for indexing the // bit-stack directly and avoiding a branch. ObjectSeparator = 0, @@ -178,7 +176,8 @@ const StreamingJsonParser = struct { // Only call this function to generate array/object final state. pub fn fromInt(x: var) State { std.debug.assert(x == 0 or x == 1); - return State(u6(x)); + const T = @TagType(State); + return State(T(x)); } }; @@ -229,7 +228,7 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ObjectSeparator; - *token = Token.initMarker(TokenId.ObjectBegin); + *token = Token.initMarker(Token.Id.ObjectBegin); }, '[' => { p.stack <<= 1; @@ -239,7 +238,7 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ValueEnd; - *token = Token.initMarker(TokenId.ArrayBegin); + *token = Token.initMarker(Token.Id.ArrayBegin); }, '-' => { p.number_is_integer = true; @@ -264,7 +263,7 @@ const StreamingJsonParser = struct { p.after_value_state = State.TopLevelEnd; // We don't actually need the following since after_value_state should override. p.after_string_state = State.ValueEnd; - p.string_has_unicode_escape = false; + p.string_has_escape = false; p.count = 0; }, 't' => { @@ -325,7 +324,7 @@ const StreamingJsonParser = struct { else => {}, } - *token = Token.initMarker(TokenId.ObjectEnd); + *token = Token.initMarker(Token.Id.ObjectEnd); }, ']' => { if (p.stack & 1 != array_bit) { @@ -349,7 +348,7 @@ const StreamingJsonParser = struct { else => {}, } - *token = Token.initMarker(TokenId.ArrayEnd); + *token = Token.initMarker(Token.Id.ArrayEnd); }, '{' => { if (p.stack_used == max_stack_size) { @@ -363,7 +362,7 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ObjectSeparator; - *token = Token.initMarker(TokenId.ObjectBegin); + *token = Token.initMarker(Token.Id.ObjectBegin); }, '[' => { if (p.stack_used == max_stack_size) { @@ -377,7 +376,7 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ValueEnd; - *token = Token.initMarker(TokenId.ArrayBegin); + *token = Token.initMarker(Token.Id.ArrayBegin); }, '-' => { p.state = State.Number; @@ -429,7 +428,7 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ObjectSeparator; - *token = Token.initMarker(TokenId.ObjectBegin); + *token = Token.initMarker(Token.Id.ObjectBegin); }, '[' => { if (p.stack_used == max_stack_size) { @@ -443,7 +442,7 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ValueEnd; - *token = Token.initMarker(TokenId.ArrayBegin); + *token = Token.initMarker(Token.Id.ArrayBegin); }, '-' => { p.state = State.Number; @@ -502,7 +501,7 @@ const StreamingJsonParser = struct { p.state = State.TopLevelEnd; } - *token = Token.initMarker(TokenId.ArrayEnd); + *token = Token.initMarker(Token.Id.ArrayEnd); }, '}' => { if (p.stack_used == 0) { @@ -520,7 +519,7 @@ const StreamingJsonParser = struct { p.state = State.TopLevelEnd; } - *token = Token.initMarker(TokenId.ObjectEnd); + *token = Token.initMarker(Token.Id.ObjectEnd); }, 0x09, 0x0A, 0x0D, 0x20 => { // whitespace @@ -554,7 +553,7 @@ const StreamingJsonParser = struct { p.complete = true; } - *token = Token.initString(p.count - 1, p.string_has_unicode_escape); + *token = Token.initString(p.count - 1, p.string_has_escape); }, '\\' => { p.state = State.StringEscapeCharacter; @@ -601,10 +600,11 @@ const StreamingJsonParser = struct { // however, so we default to the status quo where both are accepted until this // is further clarified. '"', '\\', '/', 'b', 'f', 'n', 'r', 't' => { + p.string_has_escape = true; p.state = State.String; }, 'u' => { - p.string_has_unicode_escape = true; + p.string_has_escape = true; p.state = State.StringEscapeHexUnicode4; }, else => { @@ -793,7 +793,7 @@ const StreamingJsonParser = struct { 'e' => { p.state = p.after_value_state; p.complete = p.state == State.TopLevelEnd; - *token = Token.init(TokenId.True, p.count + 1, 1); + *token = Token.init(Token.Id.True, p.count + 1, 1); }, else => { return error.InvalidLiteral; @@ -819,7 +819,7 @@ const StreamingJsonParser = struct { 'e' => { p.state = p.after_value_state; p.complete = p.state == State.TopLevelEnd; - *token = Token.init(TokenId.False, p.count + 1, 1); + *token = Token.init(Token.Id.False, p.count + 1, 1); }, else => { return error.InvalidLiteral; @@ -840,7 +840,7 @@ const StreamingJsonParser = struct { 'l' => { p.state = p.after_value_state; p.complete = p.state == State.TopLevelEnd; - *token = Token.init(TokenId.Null, p.count + 1, 1); + *token = Token.init(Token.Id.Null, p.count + 1, 1); }, else => { return error.InvalidLiteral; @@ -894,7 +894,7 @@ pub const Value = union(enum) { Array: ArrayList(Value), Object: ObjectMap, - pub fn toString(self: &const Value) void { + pub fn dump(self: &const Value) void { switch (*self) { Value.Null => { std.debug.warn("null"); @@ -919,7 +919,7 @@ pub const Value = union(enum) { std.debug.warn(","); } not_first = true; - value.toString(); + value.dump(); } std.debug.warn("]"); }, @@ -934,22 +934,22 @@ pub const Value = union(enum) { } not_first = true; std.debug.warn("\"{}\":", entry.key); - entry.value.toString(); + entry.value.dump(); } std.debug.warn("}}"); }, } } - pub fn toStringIndent(self: &const Value, indent: usize) void { + pub fn dumpIndent(self: &const Value, indent: usize) void { if (indent == 0) { - self.toString(); + self.dump(); } else { - self.toStringIndentLevel(indent, 0); + self.dumpIndentLevel(indent, 0); } } - fn toStringIndentLevel(self: &const Value, indent: usize, level: usize) void { + fn dumpIndentLevel(self: &const Value, indent: usize, level: usize) void { switch (*self) { Value.Null => { std.debug.warn("null"); @@ -976,7 +976,7 @@ pub const Value = union(enum) { } not_first = true; padSpace(level + indent); - value.toStringIndentLevel(indent, level + indent); + value.dumpIndentLevel(indent, level + indent); } std.debug.warn("\n"); padSpace(level); @@ -994,7 +994,7 @@ pub const Value = union(enum) { not_first = true; padSpace(level + indent); std.debug.warn("\"{}\": ", entry.key); - entry.value.toStringIndentLevel(indent, level + indent); + entry.value.dumpIndentLevel(indent, level + indent); } std.debug.warn("\n"); padSpace(level); @@ -1092,7 +1092,7 @@ const JsonParser = struct { fn transition(p: &JsonParser, allocator: &Allocator, input: []const u8, i: usize, token: &const Token) !void { switch (p.state) { State.ObjectKey => switch (token.id) { - TokenId.ObjectEnd => { + Token.Id.ObjectEnd => { if (p.stack_used == 1) { return; } @@ -1101,7 +1101,7 @@ const JsonParser = struct { p.stack_used -= 1; try p.pushToParent(value); }, - TokenId.String => { + Token.Id.String => { p.pushStack(try p.parseString(allocator, token, input, i)); p.state = State.ObjectValue; }, @@ -1114,35 +1114,35 @@ const JsonParser = struct { var key = p.stack[p.stack_used - 1].String; switch (token.id) { - TokenId.ObjectBegin => { + Token.Id.ObjectBegin => { p.pushStack(Value { .Object = ObjectMap.init(allocator) }); p.state = State.ObjectKey; }, - TokenId.ArrayBegin => { + Token.Id.ArrayBegin => { p.pushStack(Value { .Array = ArrayList(Value).init(allocator) }); p.state = State.ArrayValue; }, - TokenId.String => { + Token.Id.String => { _ = try object.put(key, try p.parseString(allocator, token, input, i)); p.stack_used -= 1; p.state = State.ObjectKey; }, - TokenId.Number => { + Token.Id.Number => { _ = try object.put(key, try p.parseNumber(token, input, i)); p.stack_used -= 1; p.state = State.ObjectKey; }, - TokenId.True => { + Token.Id.True => { _ = try object.put(key, Value { .Bool = true }); p.stack_used -= 1; p.state = State.ObjectKey; }, - TokenId.False => { + Token.Id.False => { _ = try object.put(key, Value { .Bool = false }); p.stack_used -= 1; p.state = State.ObjectKey; }, - TokenId.Null => { + Token.Id.Null => { _ = try object.put(key, Value.Null); p.stack_used -= 1; p.state = State.ObjectKey; @@ -1156,7 +1156,7 @@ const JsonParser = struct { var array = &p.stack[p.stack_used - 1].Array; switch (token.id) { - TokenId.ArrayEnd => { + Token.Id.ArrayEnd => { if (p.stack_used == 1) { return; } @@ -1165,27 +1165,27 @@ const JsonParser = struct { p.stack_used -= 1; try p.pushToParent(value); }, - TokenId.ObjectBegin => { + Token.Id.ObjectBegin => { p.pushStack(Value { .Object = ObjectMap.init(allocator) }); p.state = State.ObjectKey; }, - TokenId.ArrayBegin => { + Token.Id.ArrayBegin => { p.pushStack(Value { .Array = ArrayList(Value).init(allocator) }); p.state = State.ArrayValue; }, - TokenId.String => { + Token.Id.String => { try array.append(try p.parseString(allocator, token, input, i)); }, - TokenId.Number => { + Token.Id.Number => { try array.append(try p.parseNumber(token, input, i)); }, - TokenId.True => { + Token.Id.True => { try array.append(Value { .Bool = true }); }, - TokenId.False => { + Token.Id.False => { try array.append(Value { .Bool = false }); }, - TokenId.Null => { + Token.Id.Null => { try array.append(Value.Null); }, else => { @@ -1194,30 +1194,30 @@ const JsonParser = struct { } }, State.Simple => switch (token.id) { - TokenId.ObjectBegin => { + Token.Id.ObjectBegin => { p.pushStack(Value { .Object = ObjectMap.init(allocator) }); p.state = State.ObjectKey; }, - TokenId.ArrayBegin => { + Token.Id.ArrayBegin => { p.pushStack(Value { .Array = ArrayList(Value).init(allocator) }); p.state = State.ArrayValue; }, - TokenId.String => { + Token.Id.String => { p.pushStack(try p.parseString(allocator, token, input, i)); }, - TokenId.Number => { + Token.Id.Number => { p.pushStack(try p.parseNumber(token, input, i)); }, - TokenId.True => { + Token.Id.True => { p.pushStack(Value { .Bool = true }); }, - TokenId.False => { + Token.Id.False => { p.pushStack(Value { .Bool = false }); }, - TokenId.Null => { + Token.Id.Null => { p.pushStack(Value.Null); }, - TokenId.ObjectEnd, TokenId.ArrayEnd => { + Token.Id.ObjectEnd, Token.Id.ArrayEnd => { unreachable; }, }, diff --git a/std/json_test.zig b/std/json_test.zig index acbdeb3487..90a2ddbd50 100644 --- a/std/json_test.zig +++ b/std/json_test.zig @@ -1437,7 +1437,7 @@ test "n_string_with_trailing_garbage" { test "n_structure_100000_opening_arrays" { err( - "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "[" ** 100000 ); } @@ -1581,7 +1581,7 @@ test "n_structure_open_array_comma" { test "n_structure_open_array_object" { err( - \\[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"": + "[{\"\":" ** 50000 ); } @@ -1931,7 +1931,7 @@ test "i_string_UTF8_surrogate_U+D800" { test "i_structure_500_nested_arrays" { any( - \\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + ("[" ** 500) ++ ("]" ** 500) ); } -- cgit v1.2.3 From ef3111be236fc389a696562d31bccd3a9b6d1c56 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 3 May 2018 19:26:24 +1200 Subject: Use allocator backed array for json value decoder --- std/json.zig | 82 +++++++++++++++++++++++++++++------------------------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/std/json.zig b/std/json.zig index 5a25433140..6f853501ed 100644 --- a/std/json.zig +++ b/std/json.zig @@ -1017,9 +1017,7 @@ const JsonParser = struct { state: State, copy_strings: bool, // Stores parent nodes and un-combined Values. - // Worst case scenario we have nested key, values and so need two times the stack size. - stack: [2 * StreamingJsonParser.max_stack_size]Value, - stack_used: u16, + stack: ArrayList(Value), const State = enum { ObjectKey, @@ -1033,14 +1031,17 @@ const JsonParser = struct { .allocator = allocator, .state = State.Simple, .copy_strings = copy_strings, - .stack = undefined, - .stack_used = 0, + .stack = ArrayList(Value).init(allocator), }; } + pub fn deinit(p: &JsonParser) void { + p.stack.deinit(); + } + pub fn reset(p: &JsonParser) void { p.state = State.Simple; - p.stack_used = 0; + p.stack.shrink(0); } pub fn parse(p: &JsonParser, input: []const u8) !ValueTree { @@ -1079,11 +1080,11 @@ const JsonParser = struct { return error.IncompleteJsonInput; } - std.debug.assert(p.stack_used == 1); + std.debug.assert(p.stack.len == 1); return ValueTree { .arena = arena, - .root = p.stack[0], + .root = p.stack.at(0), }; } @@ -1093,16 +1094,15 @@ const JsonParser = struct { switch (p.state) { State.ObjectKey => switch (token.id) { Token.Id.ObjectEnd => { - if (p.stack_used == 1) { + if (p.stack.len == 1) { return; } - var value = p.stack[p.stack_used - 1]; - p.stack_used -= 1; + var value = p.stack.pop(); try p.pushToParent(value); }, Token.Id.String => { - p.pushStack(try p.parseString(allocator, token, input, i)); + try p.stack.append(try p.parseString(allocator, token, input, i)); p.state = State.ObjectValue; }, else => { @@ -1110,41 +1110,41 @@ const JsonParser = struct { }, }, State.ObjectValue => { - var object = &p.stack[p.stack_used - 2].Object; - var key = p.stack[p.stack_used - 1].String; + var object = &p.stack.items[p.stack.len - 2].Object; + var key = p.stack.items[p.stack.len - 1].String; switch (token.id) { Token.Id.ObjectBegin => { - p.pushStack(Value { .Object = ObjectMap.init(allocator) }); + try p.stack.append(Value { .Object = ObjectMap.init(allocator) }); p.state = State.ObjectKey; }, Token.Id.ArrayBegin => { - p.pushStack(Value { .Array = ArrayList(Value).init(allocator) }); + try p.stack.append(Value { .Array = ArrayList(Value).init(allocator) }); p.state = State.ArrayValue; }, Token.Id.String => { _ = try object.put(key, try p.parseString(allocator, token, input, i)); - p.stack_used -= 1; + _ = p.stack.pop(); p.state = State.ObjectKey; }, Token.Id.Number => { _ = try object.put(key, try p.parseNumber(token, input, i)); - p.stack_used -= 1; + _ = p.stack.pop(); p.state = State.ObjectKey; }, Token.Id.True => { _ = try object.put(key, Value { .Bool = true }); - p.stack_used -= 1; + _ = p.stack.pop(); p.state = State.ObjectKey; }, Token.Id.False => { _ = try object.put(key, Value { .Bool = false }); - p.stack_used -= 1; + _ = p.stack.pop(); p.state = State.ObjectKey; }, Token.Id.Null => { _ = try object.put(key, Value.Null); - p.stack_used -= 1; + _ = p.stack.pop(); p.state = State.ObjectKey; }, else => { @@ -1153,24 +1153,23 @@ const JsonParser = struct { } }, State.ArrayValue => { - var array = &p.stack[p.stack_used - 1].Array; + var array = &p.stack.items[p.stack.len - 1].Array; switch (token.id) { Token.Id.ArrayEnd => { - if (p.stack_used == 1) { + if (p.stack.len == 1) { return; } - var value = p.stack[p.stack_used - 1]; - p.stack_used -= 1; + var value = p.stack.pop(); try p.pushToParent(value); }, Token.Id.ObjectBegin => { - p.pushStack(Value { .Object = ObjectMap.init(allocator) }); + try p.stack.append(Value { .Object = ObjectMap.init(allocator) }); p.state = State.ObjectKey; }, Token.Id.ArrayBegin => { - p.pushStack(Value { .Array = ArrayList(Value).init(allocator) }); + try p.stack.append(Value { .Array = ArrayList(Value).init(allocator) }); p.state = State.ArrayValue; }, Token.Id.String => { @@ -1195,27 +1194,27 @@ const JsonParser = struct { }, State.Simple => switch (token.id) { Token.Id.ObjectBegin => { - p.pushStack(Value { .Object = ObjectMap.init(allocator) }); + try p.stack.append(Value { .Object = ObjectMap.init(allocator) }); p.state = State.ObjectKey; }, Token.Id.ArrayBegin => { - p.pushStack(Value { .Array = ArrayList(Value).init(allocator) }); + try p.stack.append(Value { .Array = ArrayList(Value).init(allocator) }); p.state = State.ArrayValue; }, Token.Id.String => { - p.pushStack(try p.parseString(allocator, token, input, i)); + try p.stack.append(try p.parseString(allocator, token, input, i)); }, Token.Id.Number => { - p.pushStack(try p.parseNumber(token, input, i)); + try p.stack.append(try p.parseNumber(token, input, i)); }, Token.Id.True => { - p.pushStack(Value { .Bool = true }); + try p.stack.append(Value { .Bool = true }); }, Token.Id.False => { - p.pushStack(Value { .Bool = false }); + try p.stack.append(Value { .Bool = false }); }, Token.Id.Null => { - p.pushStack(Value.Null); + try p.stack.append(Value.Null); }, Token.Id.ObjectEnd, Token.Id.ArrayEnd => { unreachable; @@ -1225,12 +1224,12 @@ const JsonParser = struct { } fn pushToParent(p: &JsonParser, value: &const Value) !void { - switch (p.stack[p.stack_used - 1]) { + switch (p.stack.at(p.stack.len - 1)) { // Object Parent -> [ ..., object, , value ] Value.String => |key| { - p.stack_used -= 1; + _ = p.stack.pop(); - var object = &p.stack[p.stack_used - 1].Object; + var object = &p.stack.items[p.stack.len - 1].Object; _ = try object.put(key, value); p.state = State.ObjectKey; }, @@ -1245,11 +1244,6 @@ const JsonParser = struct { } } - fn pushStack(p: &JsonParser, value: &const Value) void { - p.stack[p.stack_used] = *value; - p.stack_used += 1; - } - fn parseString(p: &JsonParser, allocator: &Allocator, token: &const Token, input: []const u8, i: usize) !Value { // TODO: We don't strictly have to copy values which do not contain any escape // characters if flagged with the option. @@ -1270,6 +1264,7 @@ const debug = std.debug; test "json parser dynamic" { var p = JsonParser.init(std.debug.global_allocator, false); + defer p.deinit(); const s = \\{ @@ -1289,7 +1284,8 @@ test "json parser dynamic" { ; var tree = try p.parse(s); - tree.deinit(); + defer tree.deinit(); + var root = tree.root; var image = (??root.Object.get("Image")).value; -- cgit v1.2.3 From 8721eb68fc566ec5b71b3c8f90726fd815a3d45a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 4 May 2018 14:34:32 -0400 Subject: zig fmt: fix tokenization of float literal with exponent --- std/zig/parser_test.zig | 13 +++--- std/zig/tokenizer.zig | 113 ++++++++++++++++++++++++++---------------------- 2 files changed, 67 insertions(+), 59 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index beb3a4e0c3..1b66a7f21c 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -17,13 +17,12 @@ //} -//TODO -//test "zig fmt: number literals" { -// try testCanonical( -// \\pub const f64_true_min = 4.94065645841246544177e-324; -// \\ -// ); -//} +test "zig fmt: float literal with exponent" { + try testCanonical( + \\pub const f64_true_min = 4.94065645841246544177e-324; + \\ + ); +} test "zig fmt: line comments in struct initializer" { try testCanonical( diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 6fc26bc5fd..31dc06b695 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -6,60 +6,60 @@ pub const Token = struct { start: usize, end: usize, - const KeywordId = struct { + const Keyword = struct { bytes: []const u8, id: Id, }; - const keywords = []KeywordId { - KeywordId{.bytes="align", .id = Id.Keyword_align}, - KeywordId{.bytes="and", .id = Id.Keyword_and}, - KeywordId{.bytes="asm", .id = Id.Keyword_asm}, - KeywordId{.bytes="async", .id = Id.Keyword_async}, - KeywordId{.bytes="await", .id = Id.Keyword_await}, - KeywordId{.bytes="break", .id = Id.Keyword_break}, - KeywordId{.bytes="catch", .id = Id.Keyword_catch}, - KeywordId{.bytes="cancel", .id = Id.Keyword_cancel}, - KeywordId{.bytes="comptime", .id = Id.Keyword_comptime}, - KeywordId{.bytes="const", .id = Id.Keyword_const}, - KeywordId{.bytes="continue", .id = Id.Keyword_continue}, - KeywordId{.bytes="defer", .id = Id.Keyword_defer}, - KeywordId{.bytes="else", .id = Id.Keyword_else}, - KeywordId{.bytes="enum", .id = Id.Keyword_enum}, - KeywordId{.bytes="errdefer", .id = Id.Keyword_errdefer}, - KeywordId{.bytes="error", .id = Id.Keyword_error}, - KeywordId{.bytes="export", .id = Id.Keyword_export}, - KeywordId{.bytes="extern", .id = Id.Keyword_extern}, - KeywordId{.bytes="false", .id = Id.Keyword_false}, - KeywordId{.bytes="fn", .id = Id.Keyword_fn}, - KeywordId{.bytes="for", .id = Id.Keyword_for}, - KeywordId{.bytes="if", .id = Id.Keyword_if}, - KeywordId{.bytes="inline", .id = Id.Keyword_inline}, - KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc}, - KeywordId{.bytes="noalias", .id = Id.Keyword_noalias}, - KeywordId{.bytes="null", .id = Id.Keyword_null}, - KeywordId{.bytes="or", .id = Id.Keyword_or}, - KeywordId{.bytes="packed", .id = Id.Keyword_packed}, - KeywordId{.bytes="promise", .id = Id.Keyword_promise}, - KeywordId{.bytes="pub", .id = Id.Keyword_pub}, - KeywordId{.bytes="resume", .id = Id.Keyword_resume}, - KeywordId{.bytes="return", .id = Id.Keyword_return}, - KeywordId{.bytes="section", .id = Id.Keyword_section}, - KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc}, - KeywordId{.bytes="struct", .id = Id.Keyword_struct}, - KeywordId{.bytes="suspend", .id = Id.Keyword_suspend}, - KeywordId{.bytes="switch", .id = Id.Keyword_switch}, - KeywordId{.bytes="test", .id = Id.Keyword_test}, - KeywordId{.bytes="this", .id = Id.Keyword_this}, - KeywordId{.bytes="true", .id = Id.Keyword_true}, - KeywordId{.bytes="try", .id = Id.Keyword_try}, - KeywordId{.bytes="undefined", .id = Id.Keyword_undefined}, - KeywordId{.bytes="union", .id = Id.Keyword_union}, - KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable}, - KeywordId{.bytes="use", .id = Id.Keyword_use}, - KeywordId{.bytes="var", .id = Id.Keyword_var}, - KeywordId{.bytes="volatile", .id = Id.Keyword_volatile}, - KeywordId{.bytes="while", .id = Id.Keyword_while}, + const keywords = []Keyword { + Keyword{.bytes="align", .id = Id.Keyword_align}, + Keyword{.bytes="and", .id = Id.Keyword_and}, + Keyword{.bytes="asm", .id = Id.Keyword_asm}, + Keyword{.bytes="async", .id = Id.Keyword_async}, + Keyword{.bytes="await", .id = Id.Keyword_await}, + Keyword{.bytes="break", .id = Id.Keyword_break}, + Keyword{.bytes="catch", .id = Id.Keyword_catch}, + Keyword{.bytes="cancel", .id = Id.Keyword_cancel}, + Keyword{.bytes="comptime", .id = Id.Keyword_comptime}, + Keyword{.bytes="const", .id = Id.Keyword_const}, + Keyword{.bytes="continue", .id = Id.Keyword_continue}, + Keyword{.bytes="defer", .id = Id.Keyword_defer}, + Keyword{.bytes="else", .id = Id.Keyword_else}, + Keyword{.bytes="enum", .id = Id.Keyword_enum}, + Keyword{.bytes="errdefer", .id = Id.Keyword_errdefer}, + Keyword{.bytes="error", .id = Id.Keyword_error}, + Keyword{.bytes="export", .id = Id.Keyword_export}, + Keyword{.bytes="extern", .id = Id.Keyword_extern}, + Keyword{.bytes="false", .id = Id.Keyword_false}, + Keyword{.bytes="fn", .id = Id.Keyword_fn}, + Keyword{.bytes="for", .id = Id.Keyword_for}, + Keyword{.bytes="if", .id = Id.Keyword_if}, + Keyword{.bytes="inline", .id = Id.Keyword_inline}, + Keyword{.bytes="nakedcc", .id = Id.Keyword_nakedcc}, + Keyword{.bytes="noalias", .id = Id.Keyword_noalias}, + Keyword{.bytes="null", .id = Id.Keyword_null}, + Keyword{.bytes="or", .id = Id.Keyword_or}, + Keyword{.bytes="packed", .id = Id.Keyword_packed}, + Keyword{.bytes="promise", .id = Id.Keyword_promise}, + Keyword{.bytes="pub", .id = Id.Keyword_pub}, + Keyword{.bytes="resume", .id = Id.Keyword_resume}, + Keyword{.bytes="return", .id = Id.Keyword_return}, + Keyword{.bytes="section", .id = Id.Keyword_section}, + Keyword{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc}, + Keyword{.bytes="struct", .id = Id.Keyword_struct}, + Keyword{.bytes="suspend", .id = Id.Keyword_suspend}, + Keyword{.bytes="switch", .id = Id.Keyword_switch}, + Keyword{.bytes="test", .id = Id.Keyword_test}, + Keyword{.bytes="this", .id = Id.Keyword_this}, + Keyword{.bytes="true", .id = Id.Keyword_true}, + Keyword{.bytes="try", .id = Id.Keyword_try}, + Keyword{.bytes="undefined", .id = Id.Keyword_undefined}, + Keyword{.bytes="union", .id = Id.Keyword_union}, + Keyword{.bytes="unreachable", .id = Id.Keyword_unreachable}, + Keyword{.bytes="use", .id = Id.Keyword_use}, + Keyword{.bytes="var", .id = Id.Keyword_var}, + Keyword{.bytes="volatile", .id = Id.Keyword_volatile}, + Keyword{.bytes="while", .id = Id.Keyword_while}, }; fn getKeyword(bytes: []const u8) ?Id { @@ -912,10 +912,10 @@ pub const Tokenizer = struct { }, }, State.FloatFraction => switch (c) { - 'p', 'P' => { + 'p', 'P', 'e', 'E' => { state = State.FloatExponentUnsigned; }, - '0'...'9', 'a'...'f', 'A'...'F' => {}, + '0'...'9' => {}, else => break, }, State.FloatExponentUnsigned => switch (c) { @@ -1108,6 +1108,15 @@ test "tokenizer" { }); } +test "tokenizer - float literal" { + testTokenize("a = 4.94065645841246544177e-324;\n", []Token.Id { + Token.Id.Identifier, + Token.Id.Equal, + Token.Id.FloatLiteral, + Token.Id.Semicolon, + }); +} + test "tokenizer - chars" { testTokenize("'c'", []Token.Id {Token.Id.CharLiteral}); } -- cgit v1.2.3 From eef21df94fed2b77d8ca6a459686169bb6aca57b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 4 May 2018 16:46:35 -0400 Subject: zig fmt: same-line comment on comptime expression --- std/zig/parser.zig | 7 ++++--- std/zig/parser_test.zig | 18 ++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index fa42807907..31c49b27f5 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -1382,9 +1382,10 @@ pub const Parser = struct { else => { self.putBackToken(token); self.putBackToken(ctx.comptime_token); - const statememt = try ctx.block.statements.addOne(); - stack.append(State { .Semicolon = statememt }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = statememt } }); + const statement = try ctx.block.statements.addOne(); + stack.append(State { .LookForSameLineComment = statement }) catch unreachable; + try stack.append(State { .Semicolon = statement }); + try stack.append(State { .Expression = OptionalCtx { .Required = statement } }); continue; } } diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 1b66a7f21c..af32f23158 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -6,16 +6,14 @@ // format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; -//TODO -//test "zig fmt: same-line comptime" { -// try testCanonical( -// \\test "" { -// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt -// \\} -// \\ -// ); -//} - +test "zig fmt: same-line comment on comptime expression" { + try testCanonical( + \\test "" { + \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt + \\} + \\ + ); +} test "zig fmt: float literal with exponent" { try testCanonical( -- cgit v1.2.3 From 0fc8885a8df6a44455535ba50c160bca90e8d998 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 4 May 2018 16:49:51 -0400 Subject: zig fmt: switch with empty body --- std/zig/parser.zig | 9 ++++++++- std/zig/parser_test.zig | 11 ++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 31c49b27f5..f303bccd82 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -4298,14 +4298,21 @@ pub const Parser = struct { ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes ast.Node.Id.Switch => { const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); + const cases = switch_node.cases.toSliceConst(); + try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token)); + if (cases.len == 0) { + try stack.append(RenderState { .Text = ") {}"}); + try stack.append(RenderState { .Expression = switch_node.expr }); + continue; + } + try stack.append(RenderState { .Text = "}"}); try stack.append(RenderState.PrintIndent); try stack.append(RenderState { .Indent = indent }); try stack.append(RenderState { .Text = "\n"}); - const cases = switch_node.cases.toSliceConst(); var i = cases.len; while (i != 0) { i -= 1; diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index af32f23158..3971f45af5 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -2,9 +2,14 @@ //if (sr > n_uword_bits - 1) // d > r // return 0; -// TODO switch with no body -// format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; - +test "zig fmt: switch with empty body" { + try testCanonical( + \\test "" { + \\ foo() catch |err| switch (err) {}; + \\} + \\ + ); +} test "zig fmt: same-line comment on comptime expression" { try testCanonical( -- cgit v1.2.3 From 87c0060e813be6ee9b449058b4288d148706f8a4 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Fri, 4 May 2018 23:48:14 +0200 Subject: Made container methods that can be const, const --- std/array_list.zig | 16 ++++++++++++---- std/buf_map.zig | 12 ++++++------ std/buf_set.zig | 27 +++++++++++++++++++++++---- std/buffer.zig | 4 ++-- std/hash_map.zig | 15 ++++++++------- 5 files changed, 51 insertions(+), 23 deletions(-) diff --git a/std/array_list.zig b/std/array_list.zig index bd7e8ea7ed..f1881cd7f3 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -28,11 +28,11 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ }; } - pub fn deinit(l: &Self) void { + pub fn deinit(l: &const Self) void { l.allocator.free(l.items); } - pub fn toSlice(l: &Self) []align(A) T { + pub fn toSlice(l: &const Self) []align(A) T { return l.items[0..l.len]; } @@ -150,7 +150,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ } }; - pub fn iterator(self: &Self) Iterator { + pub fn iterator(self: &const Self) Iterator { return Iterator { .list = self, .count = 0 }; } }; @@ -168,6 +168,14 @@ test "basic ArrayList test" { assert(list.items[i] == i32(i + 1)); }} + for (list.toSlice()) |v, i| { + assert(v == i32(i + 1)); + } + + for (list.toSliceConst()) |v, i| { + assert(v == i32(i + 1)); + } + assert(list.pop() == 10); assert(list.len == 9); @@ -228,4 +236,4 @@ test "insert ArrayList test" { const items = []const i32 { 1 }; try list.insertSlice(0, items[0..0]); assert(list.items[0] == 5); -} \ No newline at end of file +} diff --git a/std/buf_map.zig b/std/buf_map.zig index 3b88b7a753..57c5830bbe 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -18,10 +18,10 @@ pub const BufMap = struct { return self; } - pub fn deinit(self: &BufMap) void { + pub fn deinit(self: &const BufMap) void { var it = self.hash_map.iterator(); while (true) { - const entry = it.next() ?? break; + const entry = it.next() ?? break; self.free(entry.key); self.free(entry.value); } @@ -38,7 +38,7 @@ pub const BufMap = struct { _ = try self.hash_map.put(key_copy, value_copy); } - pub fn get(self: &BufMap, key: []const u8) ?[]const u8 { + pub fn get(self: &const BufMap, key: []const u8) ?[]const u8 { const entry = self.hash_map.get(key) ?? return null; return entry.value; } @@ -57,11 +57,11 @@ pub const BufMap = struct { return self.hash_map.iterator(); } - fn free(self: &BufMap, value: []const u8) void { + fn free(self: &const BufMap, value: []const u8) void { self.hash_map.allocator.free(value); } - fn copy(self: &BufMap, value: []const u8) ![]const u8 { + fn copy(self: &const BufMap, value: []const u8) ![]const u8 { return mem.dupe(self.hash_map.allocator, u8, value); } }; @@ -87,4 +87,4 @@ test "BufMap" { bufmap.delete("x"); assert(0 == bufmap.count()); -} \ No newline at end of file +} diff --git a/std/buf_set.zig b/std/buf_set.zig index 4b89d495da..1badb5bf18 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -1,6 +1,8 @@ +const std = @import("index.zig"); const HashMap = @import("hash_map.zig").HashMap; const mem = @import("mem.zig"); const Allocator = mem.Allocator; +const assert = std.debug.assert; pub const BufSet = struct { hash_map: BufSetHashMap, @@ -14,10 +16,10 @@ pub const BufSet = struct { return self; } - pub fn deinit(self: &BufSet) void { + pub fn deinit(self: &const BufSet) void { var it = self.hash_map.iterator(); while (true) { - const entry = it.next() ?? break; + const entry = it.next() ?? break; self.free(entry.key); } @@ -49,13 +51,30 @@ pub const BufSet = struct { return self.hash_map.allocator; } - fn free(self: &BufSet, value: []const u8) void { + fn free(self: &const BufSet, value: []const u8) void { self.hash_map.allocator.free(value); } - fn copy(self: &BufSet, value: []const u8) ![]const u8 { + fn copy(self: &const BufSet, value: []const u8) ![]const u8 { const result = try self.hash_map.allocator.alloc(u8, value.len); mem.copy(u8, result, value); return result; } }; + +test "BufSet" { + var direct_allocator = std.heap.DirectAllocator.init(); + defer direct_allocator.deinit(); + + var bufset = BufSet.init(&direct_allocator.allocator); + defer bufset.deinit(); + + try bufset.put("x"); + assert(bufset.count() == 1); + bufset.delete("x"); + assert(bufset.count() == 0); + + try bufset.put("x"); + try bufset.put("y"); + try bufset.put("z"); +} diff --git a/std/buffer.zig b/std/buffer.zig index e0892d5933..041d891dec 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -66,7 +66,7 @@ pub const Buffer = struct { self.list.deinit(); } - pub fn toSlice(self: &Buffer) []u8 { + pub fn toSlice(self: &const Buffer) []u8 { return self.list.toSlice()[0..self.len()]; } @@ -166,5 +166,5 @@ test "simple Buffer" { assert(buf.endsWith("orld")); try buf2.resize(4); - assert(buf.startsWith(buf2.toSliceConst())); + assert(buf.startsWith(buf2.toSlice())); } diff --git a/std/hash_map.zig b/std/hash_map.zig index 99b0c58f40..2a178d9d44 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -74,7 +74,7 @@ pub fn HashMap(comptime K: type, comptime V: type, }; } - pub fn deinit(hm: &Self) void { + pub fn deinit(hm: &const Self) void { hm.allocator.free(hm.entries); } @@ -114,14 +114,14 @@ pub fn HashMap(comptime K: type, comptime V: type, return hm.internalPut(key, value); } - pub fn get(hm: &Self, key: K) ?&Entry { + pub fn get(hm: &const Self, key: K) ?&Entry { if (hm.entries.len == 0) { return null; } return hm.internalGet(key); } - pub fn contains(hm: &Self, key: K) bool { + pub fn contains(hm: &const Self, key: K) bool { return hm.get(key) != null; } @@ -230,7 +230,7 @@ pub fn HashMap(comptime K: type, comptime V: type, unreachable; // put into a full map } - fn internalGet(hm: &Self, key: K) ?&Entry { + fn internalGet(hm: &const Self, key: K) ?&Entry { const start_index = hm.keyToIndex(key); {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) { const index = (start_index + roll_over) % hm.entries.len; @@ -242,7 +242,7 @@ pub fn HashMap(comptime K: type, comptime V: type, return null; } - fn keyToIndex(hm: &Self, key: K) usize { + fn keyToIndex(hm: &const Self, key: K) usize { return usize(hash(key)) % hm.entries.len; } }; @@ -264,6 +264,7 @@ test "basic hash map usage" { assert(??(map.put(5, 66) catch unreachable) == 55); assert(??(map.put(5, 55) catch unreachable) == 66); + assert(map.contains(2)); assert((??map.get(2)).value == 22); _ = map.remove(2); assert(map.remove(2) == null); @@ -273,7 +274,7 @@ test "basic hash map usage" { test "iterator hash map" { var direct_allocator = std.heap.DirectAllocator.init(); defer direct_allocator.deinit(); - + var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator); defer reset_map.deinit(); @@ -315,4 +316,4 @@ fn hash_i32(x: i32) u32 { fn eql_i32(a: i32, b: i32) bool { return a == b; -} \ No newline at end of file +} -- cgit v1.2.3 From 4d6d2f1cd2a46e95b998b6bbc1effc72b2f4923c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 4 May 2018 18:35:43 -0400 Subject: zig fmt: same-line comment after non-block if expression --- std/zig/parser.zig | 23 ++++++++++++++++++++--- std/zig/parser_test.zig | 24 ++++++++++++++++++------ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index f303bccd82..74271f1aaf 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -1891,12 +1891,13 @@ pub const Parser = struct { } ); - stack.append(State { + stack.append(State {.LookForSameLineCommentDirect = &node.base }) catch unreachable; + try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Pipe, .ptr = &node.rpipe, } - }) catch unreachable; + }); try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); try stack.append(State { .OptionalTokenSave = OptionalTokenSave { @@ -3122,6 +3123,7 @@ pub const Parser = struct { stack.append(State { .Else = &node.@"else" }) catch unreachable; try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.append(State { .LookForSameLineComment = &node.condition }); try stack.append(State { .ExpectToken = Token.Id.RParen }); try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } }); try stack.append(State { .ExpectToken = Token.Id.LParen }); @@ -3460,6 +3462,7 @@ pub const Parser = struct { PrintIndent, Indent: usize, PrintSameLineComment: ?&Token, + PrintLineComment: &Token, }; pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { @@ -4517,7 +4520,18 @@ pub const Parser = struct { } } - try stack.append(RenderState { .Expression = if_node.body }); + if (if_node.condition.same_line_comment) |comment| { + try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Expression = if_node.body }); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent + indent_delta }); + try stack.append(RenderState { .Text = "\n" }); + try stack.append(RenderState { .PrintLineComment = comment }); + } else { + try stack.append(RenderState { .Expression = if_node.body }); + } + + try stack.append(RenderState { .Text = " " }); if (if_node.payload) |payload| { @@ -4678,6 +4692,9 @@ pub const Parser = struct { const comment_token = maybe_comment ?? break :blk; try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token)); }, + RenderState.PrintLineComment => |comment_token| { + try stream.write(self.tokenizer.getTokenSlice(comment_token)); + }, } } } diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 3971f45af5..e1d75d8380 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,6 +1,14 @@ -// TODO -//if (sr > n_uword_bits - 1) // d > r -// return 0; +test "zig fmt: same-line comment after non-block if expression" { + try testCanonical( + \\comptime { + \\ if (sr > n_uword_bits - 1) { + \\ // d > r + \\ return 0; + \\ } + \\} + \\ + ); +} test "zig fmt: switch with empty body" { try testCanonical( @@ -1108,15 +1116,15 @@ fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { return buffer.toOwnedSlice(); } -fn testCanonical(source: []const u8) !void { +fn testTransform(source: []const u8, expected_source: []const u8) !void { const needed_alloc_count = x: { // Try it once with unlimited memory, make sure it works var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize)); const result_source = try testParse(source, &failing_allocator.allocator); - if (!mem.eql(u8, result_source, source)) { + if (!mem.eql(u8, result_source, expected_source)) { warn("\n====== expected this output: =========\n"); - warn("{}", source); + warn("{}", expected_source); warn("\n======== instead found this: =========\n"); warn("{}", result_source); warn("\n======================================\n"); @@ -1147,3 +1155,7 @@ fn testCanonical(source: []const u8) !void { } } +fn testCanonical(source: []const u8) !void { + return testTransform(source, source); +} + -- cgit v1.2.3 From d7b029995c8ac678598de39aa106076dca232902 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 5 May 2018 22:40:29 +1200 Subject: Fix bigint multi-limb shift and masks --- src/bigint.cpp | 10 ++++------ test/cases/math.zig | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/bigint.cpp b/src/bigint.cpp index 2a688debd5..64bc59e5cf 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1259,12 +1259,11 @@ void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) { bigint_normalize(dest); return; } - // TODO this code path is untested - uint64_t first_digit = dest->data.digit; + dest->digit_count = max(op1->digit_count, op2->digit_count); dest->data.digits = allocate_nonzero(dest->digit_count); - dest->data.digits[0] = first_digit; - size_t i = 1; + + size_t i = 0; for (; i < op1->digit_count && i < op2->digit_count; i += 1) { dest->data.digits[i] = op1_digits[i] & op2_digits[i]; } @@ -1412,7 +1411,6 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { return; } - // TODO this code path is untested size_t digit_shift_count = shift_amt / 64; size_t leftover_shift_count = shift_amt % 64; @@ -1427,7 +1425,7 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { uint64_t digit = op1_digits[op_digit_index]; size_t dest_digit_index = op_digit_index - digit_shift_count; dest->data.digits[dest_digit_index] = carry | (digit >> leftover_shift_count); - carry = (0xffffffffffffffffULL << leftover_shift_count) & digit; + carry = digit << leftover_shift_count; if (dest_digit_index == 0) { break; } op_digit_index -= 1; diff --git a/test/cases/math.zig b/test/cases/math.zig index 47d001a590..3c33b14fbf 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -349,6 +349,23 @@ test "big number shifting" { } } +test "big number multi-limb shift and mask" { + comptime { + var a = 0xefffffffa0000001eeeeeeefaaaaaaab; + + assert(u32(a & 0xffffffff) == 0xaaaaaaab); + a >>= 32; + assert(u32(a & 0xffffffff) == 0xeeeeeeef); + a >>= 32; + assert(u32(a & 0xffffffff) == 0xa0000001); + a >>= 32; + assert(u32(a & 0xffffffff) == 0xefffffff); + a >>= 32; + + assert(a == 0); + } +} + test "xor" { test_xor(); comptime test_xor(); -- cgit v1.2.3 From 41e1cd185b82a518c58c92544c45f0348c03ef74 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 May 2018 01:04:43 -0400 Subject: std.SegmentedList implementation --- CMakeLists.txt | 57 ++++++----- std/index.zig | 2 + std/math/index.zig | 26 +++++ std/math/log2.zig | 7 +- std/segmented_list.zig | 272 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 330 insertions(+), 34 deletions(-) create mode 100644 std/segmented_list.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 36f62725da..d435092723 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -416,8 +416,8 @@ set(ZIG_CPP_SOURCES set(ZIG_STD_FILES "array_list.zig" "atomic/index.zig" - "atomic/stack.zig" "atomic/queue.zig" + "atomic/stack.zig" "base64.zig" "buf_map.zig" "buf_set.zig" @@ -427,13 +427,13 @@ set(ZIG_STD_FILES "c/index.zig" "c/linux.zig" "c/windows.zig" + "crypto/blake2.zig" + "crypto/hmac.zig" "crypto/index.zig" "crypto/md5.zig" "crypto/sha1.zig" "crypto/sha2.zig" "crypto/sha3.zig" - "crypto/blake2.zig" - "crypto/hmac.zig" "cstr.zig" "debug/failing_allocator.zig" "debug/index.zig" @@ -445,12 +445,12 @@ set(ZIG_STD_FILES "fmt/errol/index.zig" "fmt/errol/lookup.zig" "fmt/index.zig" - "hash_map.zig" - "hash/index.zig" "hash/adler.zig" "hash/crc.zig" "hash/fnv.zig" + "hash/index.zig" "hash/siphash.zig" + "hash_map.zig" "heap.zig" "index.zig" "io.zig" @@ -466,6 +466,28 @@ set(ZIG_STD_FILES "math/atanh.zig" "math/cbrt.zig" "math/ceil.zig" + "math/complex/abs.zig" + "math/complex/acos.zig" + "math/complex/acosh.zig" + "math/complex/arg.zig" + "math/complex/asin.zig" + "math/complex/asinh.zig" + "math/complex/atan.zig" + "math/complex/atanh.zig" + "math/complex/conj.zig" + "math/complex/cos.zig" + "math/complex/cosh.zig" + "math/complex/exp.zig" + "math/complex/index.zig" + "math/complex/ldexp.zig" + "math/complex/log.zig" + "math/complex/pow.zig" + "math/complex/proj.zig" + "math/complex/sin.zig" + "math/complex/sinh.zig" + "math/complex/sqrt.zig" + "math/complex/tan.zig" + "math/complex/tanh.zig" "math/copysign.zig" "math/cos.zig" "math/cosh.zig" @@ -502,33 +524,12 @@ set(ZIG_STD_FILES "math/tan.zig" "math/tanh.zig" "math/trunc.zig" - "math/complex/abs.zig" - "math/complex/acosh.zig" - "math/complex/acos.zig" - "math/complex/arg.zig" - "math/complex/asinh.zig" - "math/complex/asin.zig" - "math/complex/atanh.zig" - "math/complex/atan.zig" - "math/complex/conj.zig" - "math/complex/cosh.zig" - "math/complex/cos.zig" - "math/complex/exp.zig" - "math/complex/index.zig" - "math/complex/ldexp.zig" - "math/complex/log.zig" - "math/complex/pow.zig" - "math/complex/proj.zig" - "math/complex/sinh.zig" - "math/complex/sin.zig" - "math/complex/sqrt.zig" - "math/complex/tanh.zig" - "math/complex/tan.zig" "mem.zig" "net.zig" "os/child_process.zig" "os/darwin.zig" "os/darwin_errno.zig" + "os/epoch.zig" "os/file.zig" "os/get_user_id.zig" "os/index.zig" @@ -538,13 +539,13 @@ set(ZIG_STD_FILES "os/linux/x86_64.zig" "os/path.zig" "os/time.zig" - "os/epoch.zig" "os/windows/error.zig" "os/windows/index.zig" "os/windows/util.zig" "os/zen.zig" "rand/index.zig" "rand/ziggurat.zig" + "segmented_list.zig" "sort.zig" "special/bootstrap.zig" "special/bootstrap_lib.zig" diff --git a/std/index.zig b/std/index.zig index 272f2bbc6a..8abfa3db88 100644 --- a/std/index.zig +++ b/std/index.zig @@ -7,6 +7,7 @@ pub const BufferOutStream = @import("buffer.zig").BufferOutStream; pub const HashMap = @import("hash_map.zig").HashMap; pub const LinkedList = @import("linked_list.zig").LinkedList; pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList; +pub const SegmentedList = @import("segmented_list.zig").SegmentedList; pub const atomic = @import("atomic/index.zig"); pub const base64 = @import("base64.zig"); @@ -43,6 +44,7 @@ test "std" { _ = @import("buffer.zig"); _ = @import("hash_map.zig"); _ = @import("linked_list.zig"); + _ = @import("segmented_list.zig"); _ = @import("base64.zig"); _ = @import("build.zig"); diff --git a/std/math/index.zig b/std/math/index.zig index 83ba055329..a549a6bb61 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -558,6 +558,32 @@ test "math.floorPowerOfTwo" { comptime testFloorPowerOfTwo(); } +pub fn log2_int(comptime T: type, x: T) Log2Int(T) { + assert(x != 0); + return Log2Int(T)(T.bit_count - 1 - @clz(x)); +} + +pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) { + assert(x != 0); + const log2_val = log2_int(T, x); + if (T(1) << log2_val == x) + return log2_val; + return log2_val + 1; +} + +test "std.math.log2_int_ceil" { + assert(log2_int_ceil(u32, 1) == 0); + assert(log2_int_ceil(u32, 2) == 1); + assert(log2_int_ceil(u32, 3) == 2); + assert(log2_int_ceil(u32, 4) == 2); + assert(log2_int_ceil(u32, 5) == 3); + assert(log2_int_ceil(u32, 6) == 3); + assert(log2_int_ceil(u32, 7) == 3); + assert(log2_int_ceil(u32, 8) == 3); + assert(log2_int_ceil(u32, 9) == 4); + assert(log2_int_ceil(u32, 10) == 4); +} + fn testFloorPowerOfTwo() void { assert(floorPowerOfTwo(u32, 63) == 32); assert(floorPowerOfTwo(u32, 64) == 64); diff --git a/std/math/log2.zig b/std/math/log2.zig index 998d6d6c5e..d5bbe385c2 100644 --- a/std/math/log2.zig +++ b/std/math/log2.zig @@ -31,17 +31,12 @@ pub fn log2(x: var) @typeOf(x) { return result; }, TypeId.Int => { - return log2_int(T, x); + return math.log2_int(T, x); }, else => @compileError("log2 not implemented for " ++ @typeName(T)), } } -pub fn log2_int(comptime T: type, x: T) T { - assert(x != 0); - return T.bit_count - 1 - T(@clz(x)); -} - pub fn log2_32(x_: f32) f32 { const ivln2hi: f32 = 1.4428710938e+00; const ivln2lo: f32 = -1.7605285393e-04; diff --git a/std/segmented_list.zig b/std/segmented_list.zig new file mode 100644 index 0000000000..c9acd53464 --- /dev/null +++ b/std/segmented_list.zig @@ -0,0 +1,272 @@ +const std = @import("index.zig"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; + +// Imagine that `fn at(self: &Self, index: usize) &T` is a customer asking for a box +// from a warehouse, based on a flat array, boxes ordered from 0 to N - 1. +// But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes. +// So when the customer requests a box index, we have to translate it to shelf index +// and box index within that shelf. Illustration: +// +// customer indexes: +// shelf 0: 0 +// shelf 1: 1 2 +// shelf 2: 3 4 5 6 +// shelf 3: 7 8 9 10 11 12 13 14 +// shelf 4: 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 +// shelf 5: 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 +// ... +// +// warehouse indexes: +// shelf 0: 0 +// shelf 1: 0 1 +// shelf 2: 0 1 2 3 +// shelf 3: 0 1 2 3 4 5 6 7 +// shelf 4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// shelf 5: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 +// ... +// +// With this arrangement, here are the equations to get the shelf index and +// box index based on customer box index: +// +// shelf_index = floor(log2(customer_index + 1)) +// shelf_count = ceil(log2(box_count + 1)) +// box_index = customer_index + 1 - 2 ** shelf +// shelf_size = 2 ** shelf_index +// +// Now we complicate it a little bit further by adding a preallocated shelf, which must be +// a power of 2: +// prealloc=4 +// +// customer indexes: +// prealloc: 0 1 2 3 +// shelf 0: 4 5 6 7 8 9 10 11 +// shelf 1: 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 +// shelf 2: 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 +// ... +// +// warehouse indexes: +// prealloc: 0 1 2 3 +// shelf 0: 0 1 2 3 4 5 6 7 +// shelf 1: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// shelf 2: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 +// ... +// +// Now the equations are: +// +// shelf_index = floor(log2(customer_index + prealloc)) - log2(prealloc) - 1 +// shelf_count = ceil(log2(box_count + prealloc)) - log2(prealloc) - 1 +// box_index = customer_index + prealloc - 2 ** (log2(prealloc) + 1 + shelf) +// shelf_size = prealloc * 2 ** (shelf_index + 1) + +/// This is a stack data structure where pointers to indexes have the same lifetime as the data structure +/// itself, unlike ArrayList where push() invalidates all existing element pointers. +/// The tradeoff is that elements are not guaranteed to be contiguous. For that, use ArrayList. +/// Note however that most elements are contiguous, making this data structure cache-friendly. +/// +/// Because it never has to copy elements from an old location to a new location, it does not require +/// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator. +/// +/// This data structure has O(1) push and O(1) pop. +/// +/// It supports preallocated elements, making it especially well suited when the expected maximum +/// size is small. `prealloc_item_count` must be 0, or a power of 2. +pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type { + return struct { + const Self = this; + const prealloc_base = blk: { + assert(prealloc_item_count != 0); + const value = std.math.log2_int(usize, prealloc_item_count); + assert((1 << value) == prealloc_item_count); // prealloc_item_count must be a power of 2 + break :blk @typeOf(1)(value); + }; + const ShelfIndex = std.math.Log2Int(usize); + + allocator: &Allocator, + len: usize, + prealloc_segment: [prealloc_item_count]T, + dynamic_segments: []&T, + + /// Deinitialize with `deinit` + pub fn init(allocator: &Allocator) Self { + return Self { + .allocator = allocator, + .len = 0, + .prealloc_segment = undefined, + .dynamic_segments = []&T{}, + }; + } + + pub fn deinit(self: &Self) void { + self.freeShelves(ShelfIndex(self.dynamic_segments.len), 0); + self.allocator.free(self.dynamic_segments); + *self = undefined; + } + + pub fn at(self: &Self, i: usize) &T { + assert(i < self.len); + return self.uncheckedAt(i); + } + + pub fn count(self: &const Self) usize { + return self.len; + } + + pub fn push(self: &Self, item: &const T) !void { + const new_item_ptr = try self.addOne(); + *new_item_ptr = *item; + } + + pub fn pushMany(self: &Self, items: []const T) !void { + for (items) |item| { + try self.push(item); + } + } + + pub fn pop(self: &Self) ?T { + if (self.len == 0) + return null; + + const index = self.len - 1; + const result = *self.uncheckedAt(index); + self.len = index; + return result; + } + + pub fn addOne(self: &Self) !&T { + const new_length = self.len + 1; + try self.setCapacity(new_length); + const result = self.uncheckedAt(self.len); + self.len = new_length; + return result; + } + + pub fn setCapacity(self: &Self, new_capacity: usize) !void { + if (new_capacity <= prealloc_item_count) { + const len = ShelfIndex(self.dynamic_segments.len); + if (len == 0) return; + self.freeShelves(len, 0); + self.allocator.free(self.dynamic_segments); + self.dynamic_segments = []&T{}; + return; + } + + const new_cap_shelf_count = shelfCount(new_capacity); + const old_shelf_count = ShelfIndex(self.dynamic_segments.len); + if (new_cap_shelf_count > old_shelf_count) { + self.dynamic_segments = try self.allocator.realloc(&T, self.dynamic_segments, new_cap_shelf_count); + var i = old_shelf_count; + errdefer { + self.freeShelves(i, old_shelf_count); + self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, old_shelf_count); + } + while (i < new_cap_shelf_count) : (i += 1) { + self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; + } + return; + } + if (new_cap_shelf_count == old_shelf_count) { + return; + } + self.freeShelves(old_shelf_count, new_cap_shelf_count); + self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, new_cap_shelf_count); + } + + pub fn shrinkCapacity(self: &Self, new_capacity: usize) void { + assert(new_capacity <= prealloc_item_count or shelfCount(new_capacity) <= self.dynamic_segments.len); + self.setCapacity(new_capacity) catch unreachable; + } + + pub fn uncheckedAt(self: &Self, index: usize) &T { + if (index < prealloc_item_count) { + return &self.prealloc_segment[index]; + } + const shelf_index = shelfIndex(index); + const box_index = boxIndex(index, shelf_index); + return &self.dynamic_segments[shelf_index][box_index]; + } + + fn shelfCount(box_count: usize) ShelfIndex { + if (prealloc_item_count == 0) { + return std.math.log2_int_ceil(usize, box_count + 1); + } + return std.math.log2_int_ceil(usize, box_count + prealloc_item_count) - prealloc_base - 1; + } + + fn shelfSize(shelf_index: ShelfIndex) usize { + if (prealloc_item_count == 0) { + return usize(1) << shelf_index; + } + return usize(1) << (shelf_index + (prealloc_base + 1)); + } + + fn shelfIndex(list_index: usize) ShelfIndex { + if (prealloc_item_count == 0) { + return std.math.log2_int(usize, list_index + 1); + } + return std.math.log2_int(usize, list_index + prealloc_item_count) - prealloc_base - 1; + } + + fn boxIndex(list_index: usize, shelf_index: ShelfIndex) usize { + if (prealloc_item_count == 0) { + return (list_index + 1) - (usize(1) << shelf_index); + } + return list_index + prealloc_item_count - (usize(1) << ((prealloc_base + 1) + shelf_index)); + } + + fn freeShelves(self: &Self, from_count: ShelfIndex, to_count: ShelfIndex) void { + var i = from_count; + while (i != to_count) { + i -= 1; + self.allocator.free(self.dynamic_segments[i][0..shelfSize(i)]); + } + } + + }; +} + +test "std.SegmentedList" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + var a = &da.allocator; + + try testSegmentedList(0, a); + try testSegmentedList(1, a); + try testSegmentedList(2, a); + try testSegmentedList(4, a); + try testSegmentedList(8, a); + try testSegmentedList(16, a); +} + +fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void { + var list = SegmentedList(i32, prealloc).init(allocator); + defer list.deinit(); + + {var i: usize = 0; while (i < 100) : (i += 1) { + try list.push(i32(i + 1)); + assert(list.len == i + 1); + }} + + {var i: usize = 0; while (i < 100) : (i += 1) { + assert(*list.at(i) == i32(i + 1)); + }} + + assert(??list.pop() == 100); + assert(list.len == 99); + + try list.pushMany([]i32 { 1, 2, 3 }); + assert(list.len == 102); + assert(??list.pop() == 3); + assert(??list.pop() == 2); + assert(??list.pop() == 1); + assert(list.len == 99); + + try list.pushMany([]const i32 {}); + assert(list.len == 99); + + var i: i32 = 99; + while (list.pop()) |item| : (i -= 1) { + assert(item == i); + list.shrinkCapacity(list.len); + } +} -- cgit v1.2.3 From 7fdbaeca728312d22d877a711b46e157d3b01fe7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 4 May 2018 18:35:43 -0400 Subject: zig fmt: same-line comment after non-block if expression --- std/zig/parser.zig | 23 ++++++++++++++++++++--- std/zig/parser_test.zig | 24 ++++++++++++++++++------ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/std/zig/parser.zig b/std/zig/parser.zig index f303bccd82..74271f1aaf 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -1891,12 +1891,13 @@ pub const Parser = struct { } ); - stack.append(State { + stack.append(State {.LookForSameLineCommentDirect = &node.base }) catch unreachable; + try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Pipe, .ptr = &node.rpipe, } - }) catch unreachable; + }); try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); try stack.append(State { .OptionalTokenSave = OptionalTokenSave { @@ -3122,6 +3123,7 @@ pub const Parser = struct { stack.append(State { .Else = &node.@"else" }) catch unreachable; try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.append(State { .LookForSameLineComment = &node.condition }); try stack.append(State { .ExpectToken = Token.Id.RParen }); try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } }); try stack.append(State { .ExpectToken = Token.Id.LParen }); @@ -3460,6 +3462,7 @@ pub const Parser = struct { PrintIndent, Indent: usize, PrintSameLineComment: ?&Token, + PrintLineComment: &Token, }; pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { @@ -4517,7 +4520,18 @@ pub const Parser = struct { } } - try stack.append(RenderState { .Expression = if_node.body }); + if (if_node.condition.same_line_comment) |comment| { + try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Expression = if_node.body }); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent + indent_delta }); + try stack.append(RenderState { .Text = "\n" }); + try stack.append(RenderState { .PrintLineComment = comment }); + } else { + try stack.append(RenderState { .Expression = if_node.body }); + } + + try stack.append(RenderState { .Text = " " }); if (if_node.payload) |payload| { @@ -4678,6 +4692,9 @@ pub const Parser = struct { const comment_token = maybe_comment ?? break :blk; try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token)); }, + RenderState.PrintLineComment => |comment_token| { + try stream.write(self.tokenizer.getTokenSlice(comment_token)); + }, } } } diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 3971f45af5..e1d75d8380 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,6 +1,14 @@ -// TODO -//if (sr > n_uword_bits - 1) // d > r -// return 0; +test "zig fmt: same-line comment after non-block if expression" { + try testCanonical( + \\comptime { + \\ if (sr > n_uword_bits - 1) { + \\ // d > r + \\ return 0; + \\ } + \\} + \\ + ); +} test "zig fmt: switch with empty body" { try testCanonical( @@ -1108,15 +1116,15 @@ fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { return buffer.toOwnedSlice(); } -fn testCanonical(source: []const u8) !void { +fn testTransform(source: []const u8, expected_source: []const u8) !void { const needed_alloc_count = x: { // Try it once with unlimited memory, make sure it works var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize)); const result_source = try testParse(source, &failing_allocator.allocator); - if (!mem.eql(u8, result_source, source)) { + if (!mem.eql(u8, result_source, expected_source)) { warn("\n====== expected this output: =========\n"); - warn("{}", source); + warn("{}", expected_source); warn("\n======== instead found this: =========\n"); warn("{}", result_source); warn("\n======================================\n"); @@ -1147,3 +1155,7 @@ fn testCanonical(source: []const u8) !void { } } +fn testCanonical(source: []const u8) !void { + return testTransform(source, source); +} + -- cgit v1.2.3 From 81007d0a4beed18314e948d3afbcdc7a8cfd73a9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 May 2018 09:53:52 -0400 Subject: SegmentedList: fixups from review comments --- std/segmented_list.zig | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/std/segmented_list.zig b/std/segmented_list.zig index c9acd53464..5baba3093a 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -18,12 +18,12 @@ const Allocator = std.mem.Allocator; // ... // // warehouse indexes: -// shelf 0: 0 -// shelf 1: 0 1 -// shelf 2: 0 1 2 3 -// shelf 3: 0 1 2 3 4 5 6 7 -// shelf 4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 -// shelf 5: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 +// shelf 0: 0 +// shelf 1: 0 1 +// shelf 2: 0 1 2 3 +// shelf 3: 0 1 2 3 4 5 6 7 +// shelf 4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// shelf 5: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 // ... // // With this arrangement, here are the equations to get the shelf index and @@ -66,6 +66,8 @@ const Allocator = std.mem.Allocator; /// /// Because it never has to copy elements from an old location to a new location, it does not require /// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator. +/// Note that the push() and pop() convenience methods perform a copy, but you can instead use +/// addOne(), at(), setCapacity(), and shrinkCapacity() to avoid copying items. /// /// This data structure has O(1) push and O(1) pop. /// @@ -74,8 +76,10 @@ const Allocator = std.mem.Allocator; pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type { return struct { const Self = this; - const prealloc_base = blk: { + const prealloc_exp = blk: { + // we don't use the prealloc_exp constant when prealloc_item_count is 0. assert(prealloc_item_count != 0); + const value = std.math.log2_int(usize, prealloc_item_count); assert((1 << value) == prealloc_item_count); // prealloc_item_count must be a power of 2 break :blk @typeOf(1)(value); @@ -190,28 +194,28 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type if (prealloc_item_count == 0) { return std.math.log2_int_ceil(usize, box_count + 1); } - return std.math.log2_int_ceil(usize, box_count + prealloc_item_count) - prealloc_base - 1; + return std.math.log2_int_ceil(usize, box_count + prealloc_item_count) - prealloc_exp - 1; } fn shelfSize(shelf_index: ShelfIndex) usize { if (prealloc_item_count == 0) { return usize(1) << shelf_index; } - return usize(1) << (shelf_index + (prealloc_base + 1)); + return usize(1) << (shelf_index + (prealloc_exp + 1)); } fn shelfIndex(list_index: usize) ShelfIndex { if (prealloc_item_count == 0) { return std.math.log2_int(usize, list_index + 1); } - return std.math.log2_int(usize, list_index + prealloc_item_count) - prealloc_base - 1; + return std.math.log2_int(usize, list_index + prealloc_item_count) - prealloc_exp - 1; } fn boxIndex(list_index: usize, shelf_index: ShelfIndex) usize { if (prealloc_item_count == 0) { return (list_index + 1) - (usize(1) << shelf_index); } - return list_index + prealloc_item_count - (usize(1) << ((prealloc_base + 1) + shelf_index)); + return list_index + prealloc_item_count - (usize(1) << ((prealloc_exp + 1) + shelf_index)); } fn freeShelves(self: &Self, from_count: ShelfIndex, to_count: ShelfIndex) void { -- cgit v1.2.3 From 2f633452bb337a3f173c4fd82c2b4a0880f981f5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 May 2018 10:34:38 -0400 Subject: std.SegmentedList: cleaner separation of capacity functions --- std/segmented_list.zig | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/std/segmented_list.zig b/std/segmented_list.zig index 5baba3093a..ec339668c3 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -139,22 +139,23 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type pub fn addOne(self: &Self) !&T { const new_length = self.len + 1; - try self.setCapacity(new_length); + try self.growCapacity(new_length); const result = self.uncheckedAt(self.len); self.len = new_length; return result; } + /// Grows or shrinks capacity to match usage. pub fn setCapacity(self: &Self, new_capacity: usize) !void { - if (new_capacity <= prealloc_item_count) { - const len = ShelfIndex(self.dynamic_segments.len); - if (len == 0) return; - self.freeShelves(len, 0); - self.allocator.free(self.dynamic_segments); - self.dynamic_segments = []&T{}; - return; + if (new_capacity <= usize(1) << (prealloc_exp + self.dynamic_segments.len)) { + return self.shrinkCapacity(new_capacity); + } else { + return self.growCapacity(new_capacity); } + } + /// Only grows capacity, or retains current capacity + pub fn growCapacity(self: &Self, new_capacity: usize) !void { const new_cap_shelf_count = shelfCount(new_capacity); const old_shelf_count = ShelfIndex(self.dynamic_segments.len); if (new_cap_shelf_count > old_shelf_count) { @@ -167,20 +168,30 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type while (i < new_cap_shelf_count) : (i += 1) { self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; } + } + } + + /// Only shrinks capacity or retains current capacity + pub fn shrinkCapacity(self: &Self, new_capacity: usize) void { + if (new_capacity <= prealloc_item_count) { + const len = ShelfIndex(self.dynamic_segments.len); + self.freeShelves(len, 0); + self.allocator.free(self.dynamic_segments); + self.dynamic_segments = []&T{}; return; } + + const new_cap_shelf_count = shelfCount(new_capacity); + const old_shelf_count = ShelfIndex(self.dynamic_segments.len); + assert(new_cap_shelf_count <= old_shelf_count); if (new_cap_shelf_count == old_shelf_count) { return; } + self.freeShelves(old_shelf_count, new_cap_shelf_count); self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, new_cap_shelf_count); } - pub fn shrinkCapacity(self: &Self, new_capacity: usize) void { - assert(new_capacity <= prealloc_item_count or shelfCount(new_capacity) <= self.dynamic_segments.len); - self.setCapacity(new_capacity) catch unreachable; - } - pub fn uncheckedAt(self: &Self, index: usize) &T { if (index < prealloc_item_count) { return &self.prealloc_segment[index]; -- cgit v1.2.3 From 77a1a216d2b3ceb956869ba1716fbb6c0c7eabe8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 May 2018 16:43:20 -0400 Subject: tagged union field access prioritizes members over enum tags closes #959 --- src/ir.cpp | 19 ++++++++++--------- test/cases/union.zig | 12 ++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 5339931590..cdf56f7fee 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13736,7 +13736,16 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru create_const_enum(child_type, &field->value), child_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } - } else if (child_type->id == TypeTableEntryIdUnion && + } + ScopeDecls *container_scope = get_container_scope(child_type); + if (container_scope != nullptr) { + auto entry = container_scope->decl_table.maybe_get(field_name); + Tld *tld = entry ? entry->value : nullptr; + if (tld) { + return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld); + } + } + if (child_type->id == TypeTableEntryIdUnion && (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr || child_type->data.unionation.decl_node->data.container_decl.auto_enum)) { @@ -13753,14 +13762,6 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } } - ScopeDecls *container_scope = get_container_scope(child_type); - if (container_scope != nullptr) { - auto entry = container_scope->decl_table.maybe_get(field_name); - Tld *tld = entry ? entry->value : nullptr; - if (tld) { - return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld); - } - } ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("container '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); diff --git a/test/cases/union.zig b/test/cases/union.zig index e7d9c23d77..f1fef46657 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -272,3 +272,15 @@ const PartialInstWithPayload = union(enum) { Compiled: i32, }; + +test "access a member of tagged union with conflicting enum tag name" { + const Bar = union(enum) { + A: A, + B: B, + + const A = u8; + const B = void; + }; + + comptime assert(Bar.A == u8); +} -- cgit v1.2.3 From 3b7aa808920dc96c9219a57a9d0f3ac6f85a18de Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 May 2018 12:01:20 -0400 Subject: add std.SegmentedList.Iterator --- std/segmented_list.zig | 60 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/std/segmented_list.zig b/std/segmented_list.zig index ec339668c3..f90b51ace4 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -86,10 +86,10 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type }; const ShelfIndex = std.math.Log2Int(usize); - allocator: &Allocator, - len: usize, prealloc_segment: [prealloc_item_count]T, dynamic_segments: []&T, + allocator: &Allocator, + len: usize, /// Deinitialize with `deinit` pub fn init(allocator: &Allocator) Self { @@ -237,6 +237,54 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } } + pub const Iterator = struct { + list: &Self, + index: usize, + box_index: usize, + shelf_index: ShelfIndex, + shelf_size: usize, + + pub fn next(it: &Iterator) ?&T { + if (it.index >= it.list.len) + return null; + if (it.index < prealloc_item_count) { + const ptr = &it.list.prealloc_segment[it.index]; + it.index += 1; + if (it.index == prealloc_item_count) { + it.box_index = 0; + it.shelf_index = 0; + it.shelf_size = prealloc_item_count * 2; + } + return ptr; + } + + const ptr = &it.list.dynamic_segments[it.shelf_index][it.box_index]; + it.index += 1; + it.box_index += 1; + if (it.box_index == it.shelf_size) { + it.shelf_index += 1; + it.box_index = 0; + it.shelf_size *= 2; + } + return ptr; + } + }; + + pub fn iterator(self: &Self, start_index: usize) Iterator { + var it = Iterator { + .list = self, + .index = start_index, + .shelf_index = undefined, + .box_index = undefined, + .shelf_size = undefined, + }; + if (start_index >= prealloc_item_count) { + it.shelf_index = shelfIndex(start_index); + it.box_index = boxIndex(start_index, it.shelf_index); + it.shelf_size = shelfSize(it.shelf_index); + } + return it; + } }; } @@ -266,6 +314,14 @@ fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void { assert(*list.at(i) == i32(i + 1)); }} + { + var it = list.iterator(0); + var x: i32 = 1; + while (it.next()) |item| : (x += 1) { + assert(*item == x); + } + } + assert(??list.pop() == 100); assert(list.len == 99); -- cgit v1.2.3 From dc23350847f6c6f11dbdbb85c312aad2d4c89ec2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 May 2018 12:36:54 -0400 Subject: add std.SegmentedList.Iterator.prev --- std/segmented_list.zig | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/std/segmented_list.zig b/std/segmented_list.zig index f90b51ace4..6c7c879919 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -268,6 +268,25 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } return ptr; } + + pub fn prev(it: &Iterator) ?&T { + if (it.index == 0) + return null; + + it.index -= 1; + if (it.index < prealloc_item_count) + return &it.list.prealloc_segment[it.index]; + + if (it.box_index == 0) { + it.shelf_index -= 1; + it.shelf_size /= 2; + it.box_index = it.shelf_size - 1; + } else { + it.box_index -= 1; + } + + return &it.list.dynamic_segments[it.shelf_index][it.box_index]; + } }; pub fn iterator(self: &Self, start_index: usize) Iterator { @@ -316,10 +335,16 @@ fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void { { var it = list.iterator(0); - var x: i32 = 1; - while (it.next()) |item| : (x += 1) { + var x: i32 = 0; + while (it.next()) |item| { + x += 1; + assert(*item == x); + } + assert(x == 100); + while (it.prev()) |item| : (x -= 1) { assert(*item == x); } + assert(x == 0); } assert(??list.pop() == 100); -- cgit v1.2.3 From 69ef6ae0f9c2a99119bb4a39ef2112b2250a98c5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 May 2018 21:57:44 -0400 Subject: rework std.zig.parser --- src/ir.cpp | 2 +- std/segmented_list.zig | 11 + std/zig/ast.zig | 730 ++-- std/zig/index.zig | 3 +- std/zig/parser.zig | 8480 ++++++++++++++++++++++++----------------------- std/zig/parser_test.zig | 158 +- std/zig/tokenizer.zig | 35 - 7 files changed, 4833 insertions(+), 4586 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index cdf56f7fee..095caa65ed 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14709,7 +14709,7 @@ static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source } if (value->value.type->id != TypeTableEntryIdUnion) { - ir_add_error(ira, source_instr, + ir_add_error(ira, value, buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name))); return ira->codegen->invalid_instruction; } diff --git a/std/segmented_list.zig b/std/segmented_list.zig index 6c7c879919..a89d332556 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -91,6 +91,8 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type allocator: &Allocator, len: usize, + pub const prealloc_count = prealloc_item_count; + /// Deinitialize with `deinit` pub fn init(allocator: &Allocator) Self { return Self { @@ -287,6 +289,15 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return &it.list.dynamic_segments[it.shelf_index][it.box_index]; } + + pub fn peek(it: &Iterator) ?&T { + if (it.index >= it.list.len) + return null; + if (it.index < prealloc_item_count) + return &it.list.prealloc_segment[it.index]; + + return &it.list.dynamic_segments[it.shelf_index][it.box_index]; + } }; pub fn iterator(self: &Self, start_index: usize) Iterator { diff --git a/std/zig/ast.zig b/std/zig/ast.zig index d1d7fe7914..664ab25a28 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1,12 +1,221 @@ const std = @import("../index.zig"); const assert = std.debug.assert; -const ArrayList = std.ArrayList; -const Token = std.zig.Token; +const SegmentedList = std.SegmentedList; const mem = std.mem; +const Token = std.zig.Token; + +pub const TokenIndex = usize; + +pub const Tree = struct { + source: []const u8, + tokens: TokenList, + root_node: &Node.Root, + arena_allocator: std.heap.ArenaAllocator, + errors: ErrorList, + + pub const TokenList = SegmentedList(Token, 64); + pub const ErrorList = SegmentedList(Error, 0); + + pub fn deinit(self: &Tree) void { + self.arena_allocator.deinit(); + } + + pub fn renderError(self: &Tree, parse_error: &Error, stream: var) !void { + return parse_error.render(&self.tokens, stream); + } + + pub fn tokenSlice(self: &Tree, token_index: TokenIndex) []const u8 { + const token = self.tokens.at(token_index); + return self.source[token.start..token.end]; + } + + pub const Location = struct { + line: usize, + column: usize, + line_start: usize, + line_end: usize, + }; + + pub fn tokenLocation(self: &Tree, start_index: usize, token_index: TokenIndex) Location { + var loc = Location { + .line = 0, + .column = 0, + .line_start = start_index, + .line_end = self.source.len, + }; + const token_start = self.tokens.at(token_index).start; + for (self.source[start_index..]) |c, i| { + if (i + start_index == token_start) { + loc.line_end = i + start_index; + while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') : (loc.line_end += 1) {} + return loc; + } + if (c == '\n') { + loc.line += 1; + loc.column = 0; + loc.line_start = i + 1; + } else { + loc.column += 1; + } + } + return loc; + } + +}; + +pub const Error = union(enum) { + InvalidToken: InvalidToken, + ExpectedVarDeclOrFn: ExpectedVarDeclOrFn, + ExpectedAggregateKw: ExpectedAggregateKw, + UnattachedDocComment: UnattachedDocComment, + ExpectedEqOrSemi: ExpectedEqOrSemi, + ExpectedSemiOrLBrace: ExpectedSemiOrLBrace, + ExpectedLabelable: ExpectedLabelable, + ExpectedInlinable: ExpectedInlinable, + ExpectedAsmOutputReturnOrType: ExpectedAsmOutputReturnOrType, + ExpectedCall: ExpectedCall, + ExpectedCallOrFnProto: ExpectedCallOrFnProto, + ExpectedSliceOrRBracket: ExpectedSliceOrRBracket, + ExtraAlignQualifier: ExtraAlignQualifier, + ExtraConstQualifier: ExtraConstQualifier, + ExtraVolatileQualifier: ExtraVolatileQualifier, + ExpectedPrimaryExpr: ExpectedPrimaryExpr, + ExpectedToken: ExpectedToken, + ExpectedCommaOrEnd: ExpectedCommaOrEnd, + + pub fn render(self: &Error, tokens: &Tree.TokenList, stream: var) !void { + switch (*self) { + // TODO https://github.com/zig-lang/zig/issues/683 + @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedAggregateKw => |*x| return x.render(tokens, stream), + @TagType(Error).UnattachedDocComment => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedEqOrSemi => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedLabelable => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedInlinable => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedCall => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedCallOrFnProto => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream), + @TagType(Error).ExtraAlignQualifier => |*x| return x.render(tokens, stream), + @TagType(Error).ExtraConstQualifier => |*x| return x.render(tokens, stream), + @TagType(Error).ExtraVolatileQualifier => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedPrimaryExpr => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedToken => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedCommaOrEnd => |*x| return x.render(tokens, stream), + } + } + + pub fn loc(self: &Error) TokenIndex { + switch (*self) { + // TODO https://github.com/zig-lang/zig/issues/683 + @TagType(Error).InvalidToken => |x| return x.token, + @TagType(Error).ExpectedVarDeclOrFn => |x| return x.token, + @TagType(Error).ExpectedAggregateKw => |x| return x.token, + @TagType(Error).UnattachedDocComment => |x| return x.token, + @TagType(Error).ExpectedEqOrSemi => |x| return x.token, + @TagType(Error).ExpectedSemiOrLBrace => |x| return x.token, + @TagType(Error).ExpectedLabelable => |x| return x.token, + @TagType(Error).ExpectedInlinable => |x| return x.token, + @TagType(Error).ExpectedAsmOutputReturnOrType => |x| return x.token, + @TagType(Error).ExpectedCall => |x| return x.node.firstToken(), + @TagType(Error).ExpectedCallOrFnProto => |x| return x.node.firstToken(), + @TagType(Error).ExpectedSliceOrRBracket => |x| return x.token, + @TagType(Error).ExtraAlignQualifier => |x| return x.token, + @TagType(Error).ExtraConstQualifier => |x| return x.token, + @TagType(Error).ExtraVolatileQualifier => |x| return x.token, + @TagType(Error).ExpectedPrimaryExpr => |x| return x.token, + @TagType(Error).ExpectedToken => |x| return x.token, + @TagType(Error).ExpectedCommaOrEnd => |x| return x.token, + } + } + + pub const InvalidToken = SingleTokenError("Invalid token {}"); + pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found {}"); + pub const ExpectedAggregateKw = SingleTokenError("Expected " ++ + @tagName(Token.Id.Keyword_struct) ++ ", " ++ @tagName(Token.Id.Keyword_union) ++ ", or " ++ + @tagName(Token.Id.Keyword_enum) ++ ", found {}"); + pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found {}"); + pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found {}"); + pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found {}"); + pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found {}"); + pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or " ++ + @tagName(Token.Id.Identifier) ++ ", found {}"); + pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found {}"); + pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found {}"); + + pub const UnattachedDocComment = SimpleError("Unattached documentation comment"); + pub const ExtraAlignQualifier = SimpleError("Extra align qualifier"); + pub const ExtraConstQualifier = SimpleError("Extra const qualifier"); + pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier"); + + pub const ExpectedCall = struct { + node: &Node, + + pub fn render(self: &ExpectedCall, tokens: &Tree.TokenList, stream: var) !void { + return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", + @tagName(self.node.id)); + } + }; + + pub const ExpectedCallOrFnProto = struct { + node: &Node, + + pub fn render(self: &ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void { + return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ + @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id)); + } + }; + + pub const ExpectedToken = struct { + token: TokenIndex, + expected_id: @TagType(Token.Id), + + pub fn render(self: &ExpectedToken, tokens: &Tree.TokenList, stream: var) !void { + const token_name = @tagName(tokens.at(self.token).id); + return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name); + } + }; + + pub const ExpectedCommaOrEnd = struct { + token: TokenIndex, + end_id: @TagType(Token.Id), + + pub fn render(self: &ExpectedCommaOrEnd, tokens: &Tree.TokenList, stream: var) !void { + const token_name = @tagName(tokens.at(self.token).id); + return stream.print("expected ',' or {}, found {}", @tagName(self.end_id), token_name); + } + }; + + fn SingleTokenError(comptime msg: []const u8) type { + return struct { + const ThisError = this; + + token: TokenIndex, + + pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void { + const token_name = @tagName(tokens.at(self.token).id); + return stream.print(msg, token_name); + } + }; + } + + fn SimpleError(comptime msg: []const u8) type { + return struct { + const ThisError = this; + + token: TokenIndex, + + pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void { + return stream.write(msg); + } + }; + } +}; pub const Node = struct { id: Id, - same_line_comment: ?&Token, pub const Id = enum { // Top level @@ -95,7 +304,7 @@ pub const Node = struct { unreachable; } - pub fn firstToken(base: &Node) Token { + pub fn firstToken(base: &Node) TokenIndex { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { @@ -106,7 +315,7 @@ pub const Node = struct { unreachable; } - pub fn lastToken(base: &Node) Token { + pub fn lastToken(base: &Node) TokenIndex { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { @@ -130,8 +339,10 @@ pub const Node = struct { pub const Root = struct { base: Node, doc_comments: ?&DocComment, - decls: ArrayList(&Node), - eof_token: Token, + decls: DeclList, + eof_token: TokenIndex, + + pub const DeclList = SegmentedList(&Node, 4); pub fn iterate(self: &Root, index: usize) ?&Node { if (index < self.decls.len) { @@ -140,29 +351,29 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Root) Token { - return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken(); + pub fn firstToken(self: &Root) TokenIndex { + return if (self.decls.len == 0) self.eof_token else (*self.decls.at(0)).firstToken(); } - pub fn lastToken(self: &Root) Token { - return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken(); + pub fn lastToken(self: &Root) TokenIndex { + return if (self.decls.len == 0) self.eof_token else (*self.decls.at(self.decls.len - 1)).lastToken(); } }; pub const VarDecl = struct { base: Node, doc_comments: ?&DocComment, - visib_token: ?Token, - name_token: Token, - eq_token: Token, - mut_token: Token, - comptime_token: ?Token, - extern_export_token: ?Token, + visib_token: ?TokenIndex, + name_token: TokenIndex, + eq_token: TokenIndex, + mut_token: TokenIndex, + comptime_token: ?TokenIndex, + extern_export_token: ?TokenIndex, lib_name: ?&Node, type_node: ?&Node, align_node: ?&Node, init_node: ?&Node, - semicolon_token: Token, + semicolon_token: TokenIndex, pub fn iterate(self: &VarDecl, index: usize) ?&Node { var i = index; @@ -185,7 +396,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &VarDecl) Token { + pub fn firstToken(self: &VarDecl) TokenIndex { if (self.visib_token) |visib_token| return visib_token; if (self.comptime_token) |comptime_token| return comptime_token; if (self.extern_export_token) |extern_export_token| return extern_export_token; @@ -193,7 +404,7 @@ pub const Node = struct { return self.mut_token; } - pub fn lastToken(self: &VarDecl) Token { + pub fn lastToken(self: &VarDecl) TokenIndex { return self.semicolon_token; } }; @@ -201,9 +412,9 @@ pub const Node = struct { pub const Use = struct { base: Node, doc_comments: ?&DocComment, - visib_token: ?Token, + visib_token: ?TokenIndex, expr: &Node, - semicolon_token: Token, + semicolon_token: TokenIndex, pub fn iterate(self: &Use, index: usize) ?&Node { var i = index; @@ -214,48 +425,52 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Use) Token { + pub fn firstToken(self: &Use) TokenIndex { if (self.visib_token) |visib_token| return visib_token; return self.expr.firstToken(); } - pub fn lastToken(self: &Use) Token { + pub fn lastToken(self: &Use) TokenIndex { return self.semicolon_token; } }; pub const ErrorSetDecl = struct { base: Node, - error_token: Token, - decls: ArrayList(&Node), - rbrace_token: Token, + error_token: TokenIndex, + decls: DeclList, + rbrace_token: TokenIndex, + + pub const DeclList = SegmentedList(&Node, 2); pub fn iterate(self: &ErrorSetDecl, index: usize) ?&Node { var i = index; - if (i < self.decls.len) return self.decls.at(i); + if (i < self.decls.len) return *self.decls.at(i); i -= self.decls.len; return null; } - pub fn firstToken(self: &ErrorSetDecl) Token { + pub fn firstToken(self: &ErrorSetDecl) TokenIndex { return self.error_token; } - pub fn lastToken(self: &ErrorSetDecl) Token { + pub fn lastToken(self: &ErrorSetDecl) TokenIndex { return self.rbrace_token; } }; pub const ContainerDecl = struct { base: Node, - ltoken: Token, + ltoken: TokenIndex, layout: Layout, kind: Kind, init_arg_expr: InitArg, - fields_and_decls: ArrayList(&Node), - rbrace_token: Token, + fields_and_decls: DeclList, + rbrace_token: TokenIndex, + + pub const DeclList = Root.DeclList; const Layout = enum { Auto, @@ -287,17 +502,17 @@ pub const Node = struct { InitArg.Enum => { } } - if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i); + if (i < self.fields_and_decls.len) return *self.fields_and_decls.at(i); i -= self.fields_and_decls.len; return null; } - pub fn firstToken(self: &ContainerDecl) Token { + pub fn firstToken(self: &ContainerDecl) TokenIndex { return self.ltoken; } - pub fn lastToken(self: &ContainerDecl) Token { + pub fn lastToken(self: &ContainerDecl) TokenIndex { return self.rbrace_token; } }; @@ -305,8 +520,8 @@ pub const Node = struct { pub const StructField = struct { base: Node, doc_comments: ?&DocComment, - visib_token: ?Token, - name_token: Token, + visib_token: ?TokenIndex, + name_token: TokenIndex, type_expr: &Node, pub fn iterate(self: &StructField, index: usize) ?&Node { @@ -318,12 +533,12 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &StructField) Token { + pub fn firstToken(self: &StructField) TokenIndex { if (self.visib_token) |visib_token| return visib_token; return self.name_token; } - pub fn lastToken(self: &StructField) Token { + pub fn lastToken(self: &StructField) TokenIndex { return self.type_expr.lastToken(); } }; @@ -331,7 +546,7 @@ pub const Node = struct { pub const UnionTag = struct { base: Node, doc_comments: ?&DocComment, - name_token: Token, + name_token: TokenIndex, type_expr: ?&Node, value_expr: ?&Node, @@ -351,11 +566,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &UnionTag) Token { + pub fn firstToken(self: &UnionTag) TokenIndex { return self.name_token; } - pub fn lastToken(self: &UnionTag) Token { + pub fn lastToken(self: &UnionTag) TokenIndex { if (self.value_expr) |value_expr| { return value_expr.lastToken(); } @@ -370,7 +585,7 @@ pub const Node = struct { pub const EnumTag = struct { base: Node, doc_comments: ?&DocComment, - name_token: Token, + name_token: TokenIndex, value: ?&Node, pub fn iterate(self: &EnumTag, index: usize) ?&Node { @@ -384,11 +599,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &EnumTag) Token { + pub fn firstToken(self: &EnumTag) TokenIndex { return self.name_token; } - pub fn lastToken(self: &EnumTag) Token { + pub fn lastToken(self: &EnumTag) TokenIndex { if (self.value) |value| { return value.lastToken(); } @@ -400,7 +615,7 @@ pub const Node = struct { pub const ErrorTag = struct { base: Node, doc_comments: ?&DocComment, - name_token: Token, + name_token: TokenIndex, pub fn iterate(self: &ErrorTag, index: usize) ?&Node { var i = index; @@ -413,37 +628,37 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ErrorTag) Token { + pub fn firstToken(self: &ErrorTag) TokenIndex { return self.name_token; } - pub fn lastToken(self: &ErrorTag) Token { + pub fn lastToken(self: &ErrorTag) TokenIndex { return self.name_token; } }; pub const Identifier = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &Identifier, index: usize) ?&Node { return null; } - pub fn firstToken(self: &Identifier) Token { + pub fn firstToken(self: &Identifier) TokenIndex { return self.token; } - pub fn lastToken(self: &Identifier) Token { + pub fn lastToken(self: &Identifier) TokenIndex { return self.token; } }; pub const AsyncAttribute = struct { base: Node, - async_token: Token, + async_token: TokenIndex, allocator_type: ?&Node, - rangle_bracket: ?Token, + rangle_bracket: ?TokenIndex, pub fn iterate(self: &AsyncAttribute, index: usize) ?&Node { var i = index; @@ -456,11 +671,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &AsyncAttribute) Token { + pub fn firstToken(self: &AsyncAttribute) TokenIndex { return self.async_token; } - pub fn lastToken(self: &AsyncAttribute) Token { + pub fn lastToken(self: &AsyncAttribute) TokenIndex { if (self.rangle_bracket) |rangle_bracket| { return rangle_bracket; } @@ -472,19 +687,21 @@ pub const Node = struct { pub const FnProto = struct { base: Node, doc_comments: ?&DocComment, - visib_token: ?Token, - fn_token: Token, - name_token: ?Token, - params: ArrayList(&Node), + visib_token: ?TokenIndex, + fn_token: TokenIndex, + name_token: ?TokenIndex, + params: ParamList, return_type: ReturnType, - var_args_token: ?Token, - extern_export_inline_token: ?Token, - cc_token: ?Token, + var_args_token: ?TokenIndex, + extern_export_inline_token: ?TokenIndex, + cc_token: ?TokenIndex, async_attr: ?&AsyncAttribute, body_node: ?&Node, lib_name: ?&Node, // populated if this is an extern declaration align_expr: ?&Node, // populated if align(A) is present + pub const ParamList = SegmentedList(&Node, 2); + pub const ReturnType = union(enum) { Explicit: &Node, InferErrorSet: &Node, @@ -526,7 +743,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &FnProto) Token { + pub fn firstToken(self: &FnProto) TokenIndex { if (self.visib_token) |visib_token| return visib_token; if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token; assert(self.lib_name == null); @@ -534,7 +751,7 @@ pub const Node = struct { return self.fn_token; } - pub fn lastToken(self: &FnProto) Token { + pub fn lastToken(self: &FnProto) TokenIndex { if (self.body_node) |body_node| return body_node.lastToken(); switch (self.return_type) { // TODO allow this and next prong to share bodies since the types are the same @@ -546,11 +763,11 @@ pub const Node = struct { pub const PromiseType = struct { base: Node, - promise_token: Token, + promise_token: TokenIndex, result: ?Result, pub const Result = struct { - arrow_token: Token, + arrow_token: TokenIndex, return_type: &Node, }; @@ -565,11 +782,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PromiseType) Token { + pub fn firstToken(self: &PromiseType) TokenIndex { return self.promise_token; } - pub fn lastToken(self: &PromiseType) Token { + pub fn lastToken(self: &PromiseType) TokenIndex { if (self.result) |result| return result.return_type.lastToken(); return self.promise_token; } @@ -577,11 +794,11 @@ pub const Node = struct { pub const ParamDecl = struct { base: Node, - comptime_token: ?Token, - noalias_token: ?Token, - name_token: ?Token, + comptime_token: ?TokenIndex, + noalias_token: ?TokenIndex, + name_token: ?TokenIndex, type_node: &Node, - var_args_token: ?Token, + var_args_token: ?TokenIndex, pub fn iterate(self: &ParamDecl, index: usize) ?&Node { var i = index; @@ -592,14 +809,14 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ParamDecl) Token { + pub fn firstToken(self: &ParamDecl) TokenIndex { if (self.comptime_token) |comptime_token| return comptime_token; if (self.noalias_token) |noalias_token| return noalias_token; if (self.name_token) |name_token| return name_token; return self.type_node.firstToken(); } - pub fn lastToken(self: &ParamDecl) Token { + pub fn lastToken(self: &ParamDecl) TokenIndex { if (self.var_args_token) |var_args_token| return var_args_token; return self.type_node.lastToken(); } @@ -607,10 +824,12 @@ pub const Node = struct { pub const Block = struct { base: Node, - label: ?Token, - lbrace: Token, - statements: ArrayList(&Node), - rbrace: Token, + label: ?TokenIndex, + lbrace: TokenIndex, + statements: StatementList, + rbrace: TokenIndex, + + pub const StatementList = Root.DeclList; pub fn iterate(self: &Block, index: usize) ?&Node { var i = index; @@ -621,7 +840,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Block) Token { + pub fn firstToken(self: &Block) TokenIndex { if (self.label) |label| { return label; } @@ -629,14 +848,14 @@ pub const Node = struct { return self.lbrace; } - pub fn lastToken(self: &Block) Token { + pub fn lastToken(self: &Block) TokenIndex { return self.rbrace; } }; pub const Defer = struct { base: Node, - defer_token: Token, + defer_token: TokenIndex, kind: Kind, expr: &Node, @@ -654,11 +873,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Defer) Token { + pub fn firstToken(self: &Defer) TokenIndex { return self.defer_token; } - pub fn lastToken(self: &Defer) Token { + pub fn lastToken(self: &Defer) TokenIndex { return self.expr.lastToken(); } }; @@ -666,7 +885,7 @@ pub const Node = struct { pub const Comptime = struct { base: Node, doc_comments: ?&DocComment, - comptime_token: Token, + comptime_token: TokenIndex, expr: &Node, pub fn iterate(self: &Comptime, index: usize) ?&Node { @@ -678,20 +897,20 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Comptime) Token { + pub fn firstToken(self: &Comptime) TokenIndex { return self.comptime_token; } - pub fn lastToken(self: &Comptime) Token { + pub fn lastToken(self: &Comptime) TokenIndex { return self.expr.lastToken(); } }; pub const Payload = struct { base: Node, - lpipe: Token, + lpipe: TokenIndex, error_symbol: &Node, - rpipe: Token, + rpipe: TokenIndex, pub fn iterate(self: &Payload, index: usize) ?&Node { var i = index; @@ -702,21 +921,21 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Payload) Token { + pub fn firstToken(self: &Payload) TokenIndex { return self.lpipe; } - pub fn lastToken(self: &Payload) Token { + pub fn lastToken(self: &Payload) TokenIndex { return self.rpipe; } }; pub const PointerPayload = struct { base: Node, - lpipe: Token, - ptr_token: ?Token, + lpipe: TokenIndex, + ptr_token: ?TokenIndex, value_symbol: &Node, - rpipe: Token, + rpipe: TokenIndex, pub fn iterate(self: &PointerPayload, index: usize) ?&Node { var i = index; @@ -727,22 +946,22 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PointerPayload) Token { + pub fn firstToken(self: &PointerPayload) TokenIndex { return self.lpipe; } - pub fn lastToken(self: &PointerPayload) Token { + pub fn lastToken(self: &PointerPayload) TokenIndex { return self.rpipe; } }; pub const PointerIndexPayload = struct { base: Node, - lpipe: Token, - ptr_token: ?Token, + lpipe: TokenIndex, + ptr_token: ?TokenIndex, value_symbol: &Node, index_symbol: ?&Node, - rpipe: Token, + rpipe: TokenIndex, pub fn iterate(self: &PointerIndexPayload, index: usize) ?&Node { var i = index; @@ -758,18 +977,18 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PointerIndexPayload) Token { + pub fn firstToken(self: &PointerIndexPayload) TokenIndex { return self.lpipe; } - pub fn lastToken(self: &PointerIndexPayload) Token { + pub fn lastToken(self: &PointerIndexPayload) TokenIndex { return self.rpipe; } }; pub const Else = struct { base: Node, - else_token: Token, + else_token: TokenIndex, payload: ?&Node, body: &Node, @@ -787,22 +1006,24 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Else) Token { + pub fn firstToken(self: &Else) TokenIndex { return self.else_token; } - pub fn lastToken(self: &Else) Token { + pub fn lastToken(self: &Else) TokenIndex { return self.body.lastToken(); } }; pub const Switch = struct { base: Node, - switch_token: Token, + switch_token: TokenIndex, expr: &Node, /// these can be SwitchCase nodes or LineComment nodes - cases: ArrayList(&Node), - rbrace: Token, + cases: CaseList, + rbrace: TokenIndex, + + pub const CaseList = SegmentedList(&Node, 2); pub fn iterate(self: &Switch, index: usize) ?&Node { var i = index; @@ -810,31 +1031,33 @@ pub const Node = struct { if (i < 1) return self.expr; i -= 1; - if (i < self.cases.len) return self.cases.at(i); + if (i < self.cases.len) return *self.cases.at(i); i -= self.cases.len; return null; } - pub fn firstToken(self: &Switch) Token { + pub fn firstToken(self: &Switch) TokenIndex { return self.switch_token; } - pub fn lastToken(self: &Switch) Token { + pub fn lastToken(self: &Switch) TokenIndex { return self.rbrace; } }; pub const SwitchCase = struct { base: Node, - items: ArrayList(&Node), + items: ItemList, payload: ?&Node, expr: &Node, + pub const ItemList = SegmentedList(&Node, 1); + pub fn iterate(self: &SwitchCase, index: usize) ?&Node { var i = index; - if (i < self.items.len) return self.items.at(i); + if (i < self.items.len) return *self.items.at(i); i -= self.items.len; if (self.payload) |payload| { @@ -848,37 +1071,37 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &SwitchCase) Token { - return self.items.at(0).firstToken(); + pub fn firstToken(self: &SwitchCase) TokenIndex { + return (*self.items.at(0)).firstToken(); } - pub fn lastToken(self: &SwitchCase) Token { + pub fn lastToken(self: &SwitchCase) TokenIndex { return self.expr.lastToken(); } }; pub const SwitchElse = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &SwitchElse, index: usize) ?&Node { return null; } - pub fn firstToken(self: &SwitchElse) Token { + pub fn firstToken(self: &SwitchElse) TokenIndex { return self.token; } - pub fn lastToken(self: &SwitchElse) Token { + pub fn lastToken(self: &SwitchElse) TokenIndex { return self.token; } }; pub const While = struct { base: Node, - label: ?Token, - inline_token: ?Token, - while_token: Token, + label: ?TokenIndex, + inline_token: ?TokenIndex, + while_token: TokenIndex, condition: &Node, payload: ?&Node, continue_expr: ?&Node, @@ -912,7 +1135,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &While) Token { + pub fn firstToken(self: &While) TokenIndex { if (self.label) |label| { return label; } @@ -924,7 +1147,7 @@ pub const Node = struct { return self.while_token; } - pub fn lastToken(self: &While) Token { + pub fn lastToken(self: &While) TokenIndex { if (self.@"else") |@"else"| { return @"else".body.lastToken(); } @@ -935,9 +1158,9 @@ pub const Node = struct { pub const For = struct { base: Node, - label: ?Token, - inline_token: ?Token, - for_token: Token, + label: ?TokenIndex, + inline_token: ?TokenIndex, + for_token: TokenIndex, array_expr: &Node, payload: ?&Node, body: &Node, @@ -965,7 +1188,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &For) Token { + pub fn firstToken(self: &For) TokenIndex { if (self.label) |label| { return label; } @@ -977,7 +1200,7 @@ pub const Node = struct { return self.for_token; } - pub fn lastToken(self: &For) Token { + pub fn lastToken(self: &For) TokenIndex { if (self.@"else") |@"else"| { return @"else".body.lastToken(); } @@ -988,7 +1211,7 @@ pub const Node = struct { pub const If = struct { base: Node, - if_token: Token, + if_token: TokenIndex, condition: &Node, payload: ?&Node, body: &Node, @@ -1016,11 +1239,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &If) Token { + pub fn firstToken(self: &If) TokenIndex { return self.if_token; } - pub fn lastToken(self: &If) Token { + pub fn lastToken(self: &If) TokenIndex { if (self.@"else") |@"else"| { return @"else".body.lastToken(); } @@ -1031,7 +1254,7 @@ pub const Node = struct { pub const InfixOp = struct { base: Node, - op_token: Token, + op_token: TokenIndex, lhs: &Node, op: Op, rhs: &Node, @@ -1146,18 +1369,18 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &InfixOp) Token { + pub fn firstToken(self: &InfixOp) TokenIndex { return self.lhs.firstToken(); } - pub fn lastToken(self: &InfixOp) Token { + pub fn lastToken(self: &InfixOp) TokenIndex { return self.rhs.lastToken(); } }; pub const PrefixOp = struct { base: Node, - op_token: Token, + op_token: TokenIndex, op: Op, rhs: &Node, @@ -1180,10 +1403,10 @@ pub const Node = struct { const AddrOfInfo = struct { align_expr: ?&Node, - bit_offset_start_token: ?Token, - bit_offset_end_token: ?Token, - const_token: ?Token, - volatile_token: ?Token, + bit_offset_start_token: ?TokenIndex, + bit_offset_end_token: ?TokenIndex, + const_token: ?TokenIndex, + volatile_token: ?TokenIndex, }; pub fn iterate(self: &PrefixOp, index: usize) ?&Node { @@ -1225,19 +1448,19 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PrefixOp) Token { + pub fn firstToken(self: &PrefixOp) TokenIndex { return self.op_token; } - pub fn lastToken(self: &PrefixOp) Token { + pub fn lastToken(self: &PrefixOp) TokenIndex { return self.rhs.lastToken(); } }; pub const FieldInitializer = struct { base: Node, - period_token: Token, - name_token: Token, + period_token: TokenIndex, + name_token: TokenIndex, expr: &Node, pub fn iterate(self: &FieldInitializer, index: usize) ?&Node { @@ -1249,11 +1472,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &FieldInitializer) Token { + pub fn firstToken(self: &FieldInitializer) TokenIndex { return self.period_token; } - pub fn lastToken(self: &FieldInitializer) Token { + pub fn lastToken(self: &FieldInitializer) TokenIndex { return self.expr.lastToken(); } }; @@ -1262,24 +1485,28 @@ pub const Node = struct { base: Node, lhs: &Node, op: Op, - rtoken: Token, + rtoken: TokenIndex, - const Op = union(enum) { - Call: CallInfo, + pub const Op = union(enum) { + Call: Call, ArrayAccess: &Node, - Slice: SliceRange, - ArrayInitializer: ArrayList(&Node), - StructInitializer: ArrayList(&Node), - }; + Slice: Slice, + ArrayInitializer: InitList, + StructInitializer: InitList, - const CallInfo = struct { - params: ArrayList(&Node), - async_attr: ?&AsyncAttribute, - }; + pub const InitList = SegmentedList(&Node, 2); + + pub const Call = struct { + params: ParamList, + async_attr: ?&AsyncAttribute, - const SliceRange = struct { - start: &Node, - end: ?&Node, + pub const ParamList = SegmentedList(&Node, 2); + }; + + pub const Slice = struct { + start: &Node, + end: ?&Node, + }; }; pub fn iterate(self: &SuffixOp, index: usize) ?&Node { @@ -1290,7 +1517,7 @@ pub const Node = struct { switch (self.op) { Op.Call => |call_info| { - if (i < call_info.params.len) return call_info.params.at(i); + if (i < call_info.params.len) return *call_info.params.at(i); i -= call_info.params.len; }, Op.ArrayAccess => |index_expr| { @@ -1307,11 +1534,11 @@ pub const Node = struct { } }, Op.ArrayInitializer => |exprs| { - if (i < exprs.len) return exprs.at(i); + if (i < exprs.len) return *exprs.at(i); i -= exprs.len; }, Op.StructInitializer => |fields| { - if (i < fields.len) return fields.at(i); + if (i < fields.len) return *fields.at(i); i -= fields.len; }, } @@ -1319,20 +1546,20 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &SuffixOp) Token { + pub fn firstToken(self: &SuffixOp) TokenIndex { return self.lhs.firstToken(); } - pub fn lastToken(self: &SuffixOp) Token { + pub fn lastToken(self: &SuffixOp) TokenIndex { return self.rtoken; } }; pub const GroupedExpression = struct { base: Node, - lparen: Token, + lparen: TokenIndex, expr: &Node, - rparen: Token, + rparen: TokenIndex, pub fn iterate(self: &GroupedExpression, index: usize) ?&Node { var i = index; @@ -1343,18 +1570,18 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &GroupedExpression) Token { + pub fn firstToken(self: &GroupedExpression) TokenIndex { return self.lparen; } - pub fn lastToken(self: &GroupedExpression) Token { + pub fn lastToken(self: &GroupedExpression) TokenIndex { return self.rparen; } }; pub const ControlFlowExpression = struct { base: Node, - ltoken: Token, + ltoken: TokenIndex, kind: Kind, rhs: ?&Node, @@ -1391,11 +1618,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ControlFlowExpression) Token { + pub fn firstToken(self: &ControlFlowExpression) TokenIndex { return self.ltoken; } - pub fn lastToken(self: &ControlFlowExpression) Token { + pub fn lastToken(self: &ControlFlowExpression) TokenIndex { if (self.rhs) |rhs| { return rhs.lastToken(); } @@ -1420,8 +1647,8 @@ pub const Node = struct { pub const Suspend = struct { base: Node, - label: ?Token, - suspend_token: Token, + label: ?TokenIndex, + suspend_token: TokenIndex, payload: ?&Node, body: ?&Node, @@ -1441,12 +1668,12 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Suspend) Token { + pub fn firstToken(self: &Suspend) TokenIndex { if (self.label) |label| return label; return self.suspend_token; } - pub fn lastToken(self: &Suspend) Token { + pub fn lastToken(self: &Suspend) TokenIndex { if (self.body) |body| { return body.lastToken(); } @@ -1461,177 +1688,181 @@ pub const Node = struct { pub const IntegerLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &IntegerLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &IntegerLiteral) Token { + pub fn firstToken(self: &IntegerLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &IntegerLiteral) Token { + pub fn lastToken(self: &IntegerLiteral) TokenIndex { return self.token; } }; pub const FloatLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &FloatLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &FloatLiteral) Token { + pub fn firstToken(self: &FloatLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &FloatLiteral) Token { + pub fn lastToken(self: &FloatLiteral) TokenIndex { return self.token; } }; pub const BuiltinCall = struct { base: Node, - builtin_token: Token, - params: ArrayList(&Node), - rparen_token: Token, + builtin_token: TokenIndex, + params: ParamList, + rparen_token: TokenIndex, + + pub const ParamList = SegmentedList(&Node, 2); pub fn iterate(self: &BuiltinCall, index: usize) ?&Node { var i = index; - if (i < self.params.len) return self.params.at(i); + if (i < self.params.len) return *self.params.at(i); i -= self.params.len; return null; } - pub fn firstToken(self: &BuiltinCall) Token { + pub fn firstToken(self: &BuiltinCall) TokenIndex { return self.builtin_token; } - pub fn lastToken(self: &BuiltinCall) Token { + pub fn lastToken(self: &BuiltinCall) TokenIndex { return self.rparen_token; } }; pub const StringLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &StringLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &StringLiteral) Token { + pub fn firstToken(self: &StringLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &StringLiteral) Token { + pub fn lastToken(self: &StringLiteral) TokenIndex { return self.token; } }; pub const MultilineStringLiteral = struct { base: Node, - tokens: ArrayList(Token), + lines: LineList, + + pub const LineList = SegmentedList(TokenIndex, 4); pub fn iterate(self: &MultilineStringLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &MultilineStringLiteral) Token { - return self.tokens.at(0); + pub fn firstToken(self: &MultilineStringLiteral) TokenIndex { + return *self.lines.at(0); } - pub fn lastToken(self: &MultilineStringLiteral) Token { - return self.tokens.at(self.tokens.len - 1); + pub fn lastToken(self: &MultilineStringLiteral) TokenIndex { + return *self.lines.at(self.lines.len - 1); } }; pub const CharLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &CharLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &CharLiteral) Token { + pub fn firstToken(self: &CharLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &CharLiteral) Token { + pub fn lastToken(self: &CharLiteral) TokenIndex { return self.token; } }; pub const BoolLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &BoolLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &BoolLiteral) Token { + pub fn firstToken(self: &BoolLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &BoolLiteral) Token { + pub fn lastToken(self: &BoolLiteral) TokenIndex { return self.token; } }; pub const NullLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &NullLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &NullLiteral) Token { + pub fn firstToken(self: &NullLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &NullLiteral) Token { + pub fn lastToken(self: &NullLiteral) TokenIndex { return self.token; } }; pub const UndefinedLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &UndefinedLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &UndefinedLiteral) Token { + pub fn firstToken(self: &UndefinedLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &UndefinedLiteral) Token { + pub fn lastToken(self: &UndefinedLiteral) TokenIndex { return self.token; } }; pub const ThisLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &ThisLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &ThisLiteral) Token { + pub fn firstToken(self: &ThisLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &ThisLiteral) Token { + pub fn lastToken(self: &ThisLiteral) TokenIndex { return self.token; } }; @@ -1670,11 +1901,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &AsmOutput) Token { + pub fn firstToken(self: &AsmOutput) TokenIndex { return self.symbolic_name.firstToken(); } - pub fn lastToken(self: &AsmOutput) Token { + pub fn lastToken(self: &AsmOutput) TokenIndex { return switch (self.kind) { Kind.Variable => |variable_name| variable_name.lastToken(), Kind.Return => |return_type| return_type.lastToken(), @@ -1703,139 +1934,144 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &AsmInput) Token { + pub fn firstToken(self: &AsmInput) TokenIndex { return self.symbolic_name.firstToken(); } - pub fn lastToken(self: &AsmInput) Token { + pub fn lastToken(self: &AsmInput) TokenIndex { return self.expr.lastToken(); } }; pub const Asm = struct { base: Node, - asm_token: Token, - volatile_token: ?Token, + asm_token: TokenIndex, + volatile_token: ?TokenIndex, template: &Node, - //tokens: ArrayList(AsmToken), - outputs: ArrayList(&AsmOutput), - inputs: ArrayList(&AsmInput), - cloppers: ArrayList(&Node), - rparen: Token, + outputs: OutputList, + inputs: InputList, + clobbers: ClobberList, + rparen: TokenIndex, + + const OutputList = SegmentedList(&AsmOutput, 2); + const InputList = SegmentedList(&AsmInput, 2); + const ClobberList = SegmentedList(&Node, 2); pub fn iterate(self: &Asm, index: usize) ?&Node { var i = index; - if (i < self.outputs.len) return &self.outputs.at(index).base; + if (i < self.outputs.len) return &(*self.outputs.at(index)).base; i -= self.outputs.len; - if (i < self.inputs.len) return &self.inputs.at(index).base; + if (i < self.inputs.len) return &(*self.inputs.at(index)).base; i -= self.inputs.len; - if (i < self.cloppers.len) return self.cloppers.at(index); - i -= self.cloppers.len; + if (i < self.clobbers.len) return *self.clobbers.at(index); + i -= self.clobbers.len; return null; } - pub fn firstToken(self: &Asm) Token { + pub fn firstToken(self: &Asm) TokenIndex { return self.asm_token; } - pub fn lastToken(self: &Asm) Token { + pub fn lastToken(self: &Asm) TokenIndex { return self.rparen; } }; pub const Unreachable = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &Unreachable, index: usize) ?&Node { return null; } - pub fn firstToken(self: &Unreachable) Token { + pub fn firstToken(self: &Unreachable) TokenIndex { return self.token; } - pub fn lastToken(self: &Unreachable) Token { + pub fn lastToken(self: &Unreachable) TokenIndex { return self.token; } }; pub const ErrorType = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &ErrorType, index: usize) ?&Node { return null; } - pub fn firstToken(self: &ErrorType) Token { + pub fn firstToken(self: &ErrorType) TokenIndex { return self.token; } - pub fn lastToken(self: &ErrorType) Token { + pub fn lastToken(self: &ErrorType) TokenIndex { return self.token; } }; pub const VarType = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &VarType, index: usize) ?&Node { return null; } - pub fn firstToken(self: &VarType) Token { + pub fn firstToken(self: &VarType) TokenIndex { return self.token; } - pub fn lastToken(self: &VarType) Token { + pub fn lastToken(self: &VarType) TokenIndex { return self.token; } }; pub const LineComment = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &LineComment, index: usize) ?&Node { return null; } - pub fn firstToken(self: &LineComment) Token { + pub fn firstToken(self: &LineComment) TokenIndex { return self.token; } - pub fn lastToken(self: &LineComment) Token { + pub fn lastToken(self: &LineComment) TokenIndex { return self.token; } }; pub const DocComment = struct { base: Node, - lines: ArrayList(Token), + lines: LineList, + + pub const LineList = SegmentedList(TokenIndex, 4); pub fn iterate(self: &DocComment, index: usize) ?&Node { return null; } - pub fn firstToken(self: &DocComment) Token { - return self.lines.at(0); + pub fn firstToken(self: &DocComment) TokenIndex { + return *self.lines.at(0); } - pub fn lastToken(self: &DocComment) Token { - return self.lines.at(self.lines.len - 1); + pub fn lastToken(self: &DocComment) TokenIndex { + return *self.lines.at(self.lines.len - 1); } }; pub const TestDecl = struct { base: Node, doc_comments: ?&DocComment, - test_token: Token, + test_token: TokenIndex, name: &Node, body_node: &Node, @@ -1848,11 +2084,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &TestDecl) Token { + pub fn firstToken(self: &TestDecl) TokenIndex { return self.test_token; } - pub fn lastToken(self: &TestDecl) Token { + pub fn lastToken(self: &TestDecl) TokenIndex { return self.body_node.lastToken(); } }; diff --git a/std/zig/index.zig b/std/zig/index.zig index 32699935d9..42965f3710 100644 --- a/std/zig/index.zig +++ b/std/zig/index.zig @@ -1,7 +1,8 @@ const tokenizer = @import("tokenizer.zig"); pub const Token = tokenizer.Token; pub const Tokenizer = tokenizer.Tokenizer; -pub const Parser = @import("parser.zig").Parser; +pub const parse = @import("parser.zig").parse; +pub const render = @import("parser.zig").renderSource; pub const ast = @import("ast.zig"); test "std.zig tests" { diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 74271f1aaf..306d460cff 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -1,4188 +1,4159 @@ const std = @import("../index.zig"); const assert = std.debug.assert; -const ArrayList = std.ArrayList; +const SegmentedList = std.SegmentedList; const mem = std.mem; const ast = std.zig.ast; const Tokenizer = std.zig.Tokenizer; const Token = std.zig.Token; +const TokenIndex = ast.TokenIndex; +const Error = ast.Error; const builtin = @import("builtin"); const io = std.io; -// TODO when we make parse errors into error types instead of printing directly, -// get rid of this -const warn = std.debug.warn; - -pub const Parser = struct { - util_allocator: &mem.Allocator, - tokenizer: &Tokenizer, - put_back_tokens: [2]Token, - put_back_count: usize, - source_file_name: []const u8, - - pub const Tree = struct { - root_node: &ast.Node.Root, - arena_allocator: std.heap.ArenaAllocator, - - pub fn deinit(self: &Tree) void { - self.arena_allocator.deinit(); +/// Returns an AST tree, allocated with the parser's allocator. +/// Result should be freed with tree.deinit() when there are +/// no more references to any AST nodes of the tree. +pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { + var tree_arena = std.heap.ArenaAllocator.init(allocator); + errdefer tree_arena.deinit(); + + var stack = SegmentedList(State, 32).init(allocator); + defer stack.deinit(); + + const arena = &tree_arena.allocator; + const root_node = try createNode(arena, ast.Node.Root, + ast.Node.Root { + .base = undefined, + .decls = ast.Node.Root.DeclList.init(arena), + .doc_comments = null, + // initialized when we get the eof token + .eof_token = undefined, } + ); + + var tree = ast.Tree { + .source = source, + .root_node = root_node, + .arena_allocator = tree_arena, + .tokens = ast.Tree.TokenList.init(arena), + .errors = ast.Tree.ErrorList.init(arena), }; - // This memory contents are used only during a function call. It's used to repurpose memory; - // we reuse the same bytes for the stack data structure used by parsing, tree rendering, and - // source rendering. - const utility_bytes_align = @alignOf( union { a: RenderAstFrame, b: State, c: RenderState } ); - utility_bytes: []align(utility_bytes_align) u8, - - /// allocator must outlive the returned Parser and all the parse trees you create with it. - pub fn init(tokenizer: &Tokenizer, allocator: &mem.Allocator, source_file_name: []const u8) Parser { - return Parser { - .util_allocator = allocator, - .tokenizer = tokenizer, - .put_back_tokens = undefined, - .put_back_count = 0, - .source_file_name = source_file_name, - .utility_bytes = []align(utility_bytes_align) u8{}, - }; - } - - pub fn deinit(self: &Parser) void { - self.util_allocator.free(self.utility_bytes); - } - - const TopLevelDeclCtx = struct { - decls: &ArrayList(&ast.Node), - visib_token: ?Token, - extern_export_inline_token: ?Token, - lib_name: ?&ast.Node, - comments: ?&ast.Node.DocComment, - }; - - const VarDeclCtx = struct { - mut_token: Token, - visib_token: ?Token, - comptime_token: ?Token, - extern_export_token: ?Token, - lib_name: ?&ast.Node, - list: &ArrayList(&ast.Node), - comments: ?&ast.Node.DocComment, - }; - - const TopLevelExternOrFieldCtx = struct { - visib_token: Token, - container_decl: &ast.Node.ContainerDecl, - comments: ?&ast.Node.DocComment, - }; - - const ExternTypeCtx = struct { - opt_ctx: OptionalCtx, - extern_token: Token, - comments: ?&ast.Node.DocComment, - }; - - const ContainerKindCtx = struct { - opt_ctx: OptionalCtx, - ltoken: Token, - layout: ast.Node.ContainerDecl.Layout, - }; - - const ExpectTokenSave = struct { - id: Token.Id, - ptr: &Token, - }; - - const OptionalTokenSave = struct { - id: Token.Id, - ptr: &?Token, - }; - - const ExprListCtx = struct { - list: &ArrayList(&ast.Node), - end: Token.Id, - ptr: &Token, - }; - - fn ListSave(comptime T: type) type { - return struct { - list: &ArrayList(T), - ptr: &Token, - }; + var tokenizer = Tokenizer.init(tree.source); + while (true) { + const token_ptr = try tree.tokens.addOne(); + *token_ptr = tokenizer.next(); + if (token_ptr.id == Token.Id.Eof) + break; } + var tok_it = tree.tokens.iterator(0); - const MaybeLabeledExpressionCtx = struct { - label: Token, - opt_ctx: OptionalCtx, - }; - - const LabelCtx = struct { - label: ?Token, - opt_ctx: OptionalCtx, - }; - - const InlineCtx = struct { - label: ?Token, - inline_token: ?Token, - opt_ctx: OptionalCtx, - }; - - const LoopCtx = struct { - label: ?Token, - inline_token: ?Token, - loop_token: Token, - opt_ctx: OptionalCtx, - }; - - const AsyncEndCtx = struct { - ctx: OptionalCtx, - attribute: &ast.Node.AsyncAttribute, - }; - - const ErrorTypeOrSetDeclCtx = struct { - opt_ctx: OptionalCtx, - error_token: Token, - }; - - const ParamDeclEndCtx = struct { - fn_proto: &ast.Node.FnProto, - param_decl: &ast.Node.ParamDecl, - }; - - const ComptimeStatementCtx = struct { - comptime_token: Token, - block: &ast.Node.Block, - }; - - const OptionalCtx = union(enum) { - Optional: &?&ast.Node, - RequiredNull: &?&ast.Node, - Required: &&ast.Node, - - pub fn store(self: &const OptionalCtx, value: &ast.Node) void { - switch (*self) { - OptionalCtx.Optional => |ptr| *ptr = value, - OptionalCtx.RequiredNull => |ptr| *ptr = value, - OptionalCtx.Required => |ptr| *ptr = value, - } - } - - pub fn get(self: &const OptionalCtx) ?&ast.Node { - switch (*self) { - OptionalCtx.Optional => |ptr| return *ptr, - OptionalCtx.RequiredNull => |ptr| return ??*ptr, - OptionalCtx.Required => |ptr| return *ptr, - } - } + try stack.push(State.TopLevel); - pub fn toRequired(self: &const OptionalCtx) OptionalCtx { - switch (*self) { - OptionalCtx.Optional => |ptr| { - return OptionalCtx { .RequiredNull = ptr }; - }, - OptionalCtx.RequiredNull => |ptr| return *self, - OptionalCtx.Required => |ptr| return *self, - } - } - }; + while (true) { + // This gives us 1 free push that can't fail + const state = ??stack.pop(); - const AddCommentsCtx = struct { - node_ptr: &&ast.Node, - comments: ?&ast.Node.DocComment, - }; - - const State = union(enum) { - TopLevel, - TopLevelExtern: TopLevelDeclCtx, - TopLevelLibname: TopLevelDeclCtx, - TopLevelDecl: TopLevelDeclCtx, - TopLevelExternOrField: TopLevelExternOrFieldCtx, - - ContainerKind: ContainerKindCtx, - ContainerInitArgStart: &ast.Node.ContainerDecl, - ContainerInitArg: &ast.Node.ContainerDecl, - ContainerDecl: &ast.Node.ContainerDecl, - - VarDecl: VarDeclCtx, - VarDeclAlign: &ast.Node.VarDecl, - VarDeclEq: &ast.Node.VarDecl, - - FnDef: &ast.Node.FnProto, - FnProto: &ast.Node.FnProto, - FnProtoAlign: &ast.Node.FnProto, - FnProtoReturnType: &ast.Node.FnProto, - - ParamDecl: &ast.Node.FnProto, - ParamDeclAliasOrComptime: &ast.Node.ParamDecl, - ParamDeclName: &ast.Node.ParamDecl, - ParamDeclEnd: ParamDeclEndCtx, - ParamDeclComma: &ast.Node.FnProto, - - MaybeLabeledExpression: MaybeLabeledExpressionCtx, - LabeledExpression: LabelCtx, - Inline: InlineCtx, - While: LoopCtx, - WhileContinueExpr: &?&ast.Node, - For: LoopCtx, - Else: &?&ast.Node.Else, - - Block: &ast.Node.Block, - Statement: &ast.Node.Block, - ComptimeStatement: ComptimeStatementCtx, - Semicolon: &&ast.Node, - LookForSameLineComment: &&ast.Node, - LookForSameLineCommentDirect: &ast.Node, - - AsmOutputItems: &ArrayList(&ast.Node.AsmOutput), - AsmOutputReturnOrType: &ast.Node.AsmOutput, - AsmInputItems: &ArrayList(&ast.Node.AsmInput), - AsmClopperItems: &ArrayList(&ast.Node), - - ExprListItemOrEnd: ExprListCtx, - ExprListCommaOrEnd: ExprListCtx, - FieldInitListItemOrEnd: ListSave(&ast.Node), - FieldInitListCommaOrEnd: ListSave(&ast.Node), - FieldListCommaOrEnd: &ast.Node.ContainerDecl, - FieldInitValue: OptionalCtx, - ErrorTagListItemOrEnd: ListSave(&ast.Node), - ErrorTagListCommaOrEnd: ListSave(&ast.Node), - SwitchCaseOrEnd: ListSave(&ast.Node), - SwitchCaseCommaOrEnd: ListSave(&ast.Node), - SwitchCaseFirstItem: &ArrayList(&ast.Node), - SwitchCaseItem: &ArrayList(&ast.Node), - SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node), - - SuspendBody: &ast.Node.Suspend, - AsyncAllocator: &ast.Node.AsyncAttribute, - AsyncEnd: AsyncEndCtx, - - ExternType: ExternTypeCtx, - SliceOrArrayAccess: &ast.Node.SuffixOp, - SliceOrArrayType: &ast.Node.PrefixOp, - AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo, - - Payload: OptionalCtx, - PointerPayload: OptionalCtx, - PointerIndexPayload: OptionalCtx, - - Expression: OptionalCtx, - RangeExpressionBegin: OptionalCtx, - RangeExpressionEnd: OptionalCtx, - AssignmentExpressionBegin: OptionalCtx, - AssignmentExpressionEnd: OptionalCtx, - UnwrapExpressionBegin: OptionalCtx, - UnwrapExpressionEnd: OptionalCtx, - BoolOrExpressionBegin: OptionalCtx, - BoolOrExpressionEnd: OptionalCtx, - BoolAndExpressionBegin: OptionalCtx, - BoolAndExpressionEnd: OptionalCtx, - ComparisonExpressionBegin: OptionalCtx, - ComparisonExpressionEnd: OptionalCtx, - BinaryOrExpressionBegin: OptionalCtx, - BinaryOrExpressionEnd: OptionalCtx, - BinaryXorExpressionBegin: OptionalCtx, - BinaryXorExpressionEnd: OptionalCtx, - BinaryAndExpressionBegin: OptionalCtx, - BinaryAndExpressionEnd: OptionalCtx, - BitShiftExpressionBegin: OptionalCtx, - BitShiftExpressionEnd: OptionalCtx, - AdditionExpressionBegin: OptionalCtx, - AdditionExpressionEnd: OptionalCtx, - MultiplyExpressionBegin: OptionalCtx, - MultiplyExpressionEnd: OptionalCtx, - CurlySuffixExpressionBegin: OptionalCtx, - CurlySuffixExpressionEnd: OptionalCtx, - TypeExprBegin: OptionalCtx, - TypeExprEnd: OptionalCtx, - PrefixOpExpression: OptionalCtx, - SuffixOpExpressionBegin: OptionalCtx, - SuffixOpExpressionEnd: OptionalCtx, - PrimaryExpression: OptionalCtx, - - ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx, - StringLiteral: OptionalCtx, - Identifier: OptionalCtx, - ErrorTag: &&ast.Node, - - - IfToken: @TagType(Token.Id), - IfTokenSave: ExpectTokenSave, - ExpectToken: @TagType(Token.Id), - ExpectTokenSave: ExpectTokenSave, - OptionalTokenSave: OptionalTokenSave, - }; + switch (state) { + State.TopLevel => { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try root_node.decls.push(&line_comment.base); + } - /// Returns an AST tree, allocated with the parser's allocator. - /// Result should be freed with tree.deinit() when there are - /// no more references to any AST nodes of the tree. - pub fn parse(self: &Parser) !Tree { - var stack = self.initUtilityArrayList(State); - defer self.deinitUtilityArrayList(stack); - - var arena_allocator = std.heap.ArenaAllocator.init(self.util_allocator); - errdefer arena_allocator.deinit(); - - const arena = &arena_allocator.allocator; - const root_node = try self.createNode(arena, ast.Node.Root, - ast.Node.Root { - .base = undefined, - .decls = ArrayList(&ast.Node).init(arena), - .doc_comments = null, - // initialized when we get the eof token - .eof_token = undefined, - } - ); - - try stack.append(State.TopLevel); - - while (true) { - //{ - // const token = self.getNextToken(); - // warn("{} ", @tagName(token.id)); - // self.putBackToken(token); - // var i: usize = stack.len; - // while (i != 0) { - // i -= 1; - // warn("{} ", @tagName(stack.items[i])); - // } - // warn("\n"); - //} - - // This gives us 1 free append that can't fail - const state = stack.pop(); - - switch (state) { - State.TopLevel => { - while (try self.eatLineComment(arena)) |line_comment| { - try root_node.decls.append(&line_comment.base); - } + const comments = try eatDocComments(arena, &tok_it); - const comments = try self.eatDocComments(arena); - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_test => { - stack.append(State.TopLevel) catch unreachable; + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_test => { + stack.push(State.TopLevel) catch unreachable; - const block = try arena.construct(ast.Node.Block { - .base = ast.Node { - .id = ast.Node.Id.Block, - .same_line_comment = null, - }, + const block = try arena.construct(ast.Node.Block { + .base = ast.Node { + .id = ast.Node.Id.Block, + }, + .label = null, + .lbrace = undefined, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + const test_node = try arena.construct(ast.Node.TestDecl { + .base = ast.Node { + .id = ast.Node.Id.TestDecl, + }, + .doc_comments = comments, + .test_token = token_index, + .name = undefined, + .body_node = &block.base, + }); + try root_node.decls.push(&test_node.base); + try stack.push(State { .Block = block }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.LBrace, + .ptr = &block.rbrace, + } + }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } }); + continue; + }, + Token.Id.Eof => { + root_node.eof_token = token_index; + root_node.doc_comments = comments; + return tree; + }, + Token.Id.Keyword_pub => { + stack.push(State.TopLevel) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &root_node.decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; + }, + Token.Id.Keyword_comptime => { + const block = try createNode(arena, ast.Node.Block, + ast.Node.Block { + .base = undefined, .label = null, .lbrace = undefined, - .statements = ArrayList(&ast.Node).init(arena), + .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); - const test_node = try arena.construct(ast.Node.TestDecl { - .base = ast.Node { - .id = ast.Node.Id.TestDecl, - .same_line_comment = null, - }, - .doc_comments = comments, - .test_token = token, - .name = undefined, - .body_node = &block.base, - }); - try root_node.decls.append(&test_node.base); - try stack.append(State { .Block = block }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.LBrace, - .ptr = &block.rbrace, - } - }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } }); - continue; - }, - Token.Id.Eof => { - root_node.eof_token = token; - root_node.doc_comments = comments; - return Tree { - .root_node = root_node, - .arena_allocator = arena_allocator, - }; - }, - Token.Id.Keyword_pub => { - stack.append(State.TopLevel) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &root_node.decls, - .visib_token = token, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - }, - Token.Id.Keyword_comptime => { - const block = try self.createNode(arena, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = null, - .lbrace = undefined, - .statements = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, - } - ); - const node = try self.createAttachNode(arena, &root_node.decls, ast.Node.Comptime, - ast.Node.Comptime { - .base = undefined, - .comptime_token = token, - .expr = &block.base, - .doc_comments = comments, - } - ); - stack.append(State.TopLevel) catch unreachable; - try stack.append(State { .Block = block }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.LBrace, - .ptr = &block.rbrace, - } - }); - continue; - }, - else => { - self.putBackToken(token); - stack.append(State.TopLevel) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &root_node.decls, - .visib_token = null, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - }, - } - }, - State.TopLevelExtern => |ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_export, Token.Id.Keyword_inline => { - stack.append(State { - .TopLevelDecl = TopLevelDeclCtx { - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = token, - .lib_name = null, - .comments = ctx.comments, + } + ); + const node = try arena.construct(ast.Node.Comptime { + .base = ast.Node { + .id = ast.Node.Id.Comptime, + }, + .comptime_token = token_index, + .expr = &block.base, + .doc_comments = comments, + }); + try root_node.decls.push(&node.base); + + stack.push(State.TopLevel) catch unreachable; + try stack.push(State { .Block = block }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.LBrace, + .ptr = &block.rbrace, + } + }); + continue; + }, + else => { + _ = tok_it.prev(); + stack.push(State.TopLevel) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &root_node.decls, + .visib_token = null, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; + }, + } + }, + State.TopLevelExtern => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_export, Token.Id.Keyword_inline => { + stack.push(State { + .TopLevelDecl = TopLevelDeclCtx { + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = AnnotatedToken { + .index = token_index, + .ptr = token_ptr, }, - }) catch unreachable; - continue; - }, - Token.Id.Keyword_extern => { - stack.append(State { - .TopLevelLibname = TopLevelDeclCtx { - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = token, - .lib_name = null, - .comments = ctx.comments, + .lib_name = null, + .comments = ctx.comments, + }, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_extern => { + stack.push(State { + .TopLevelLibname = TopLevelDeclCtx { + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = AnnotatedToken { + .index = token_index, + .ptr = token_ptr, }, - }) catch unreachable; - continue; - }, - else => { - self.putBackToken(token); - stack.append(State { .TopLevelDecl = ctx }) catch unreachable; - continue; - } + .lib_name = null, + .comments = ctx.comments, + }, + }) catch unreachable; + continue; + }, + else => { + _ = tok_it.prev(); + stack.push(State { .TopLevelDecl = ctx }) catch unreachable; + continue; } - }, - State.TopLevelLibname => |ctx| { - const lib_name = blk: { - const lib_name_token = self.getNextToken(); - break :blk (try self.parseStringLiteral(arena, lib_name_token)) ?? { - self.putBackToken(lib_name_token); - break :blk null; - }; + } + }, + State.TopLevelLibname => |ctx| { + const lib_name = blk: { + const lib_name_token_index = tok_it.index; + const lib_name_token_ptr = ??tok_it.next(); + break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index)) ?? { + _ = tok_it.prev(); + break :blk null; }; + }; + + stack.push(State { + .TopLevelDecl = TopLevelDeclCtx { + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = ctx.extern_export_inline_token, + .lib_name = lib_name, + .comments = ctx.comments, + }, + }) catch unreachable; + continue; + }, + State.TopLevelDecl => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_use => { + if (ctx.extern_export_inline_token) |annotated_token| { + *(try tree.errors.addOne()) = Error { + .InvalidToken = Error.InvalidToken { .token = annotated_token.index }, + }; + return tree; + } - stack.append(State { - .TopLevelDecl = TopLevelDeclCtx { - .decls = ctx.decls, + const node = try arena.construct(ast.Node.Use { + .base = ast.Node {.id = ast.Node.Id.Use }, .visib_token = ctx.visib_token, - .extern_export_inline_token = ctx.extern_export_inline_token, - .lib_name = lib_name, - .comments = ctx.comments, - }, - }) catch unreachable; - continue; - }, - State.TopLevelDecl => |ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_use => { - if (ctx.extern_export_inline_token != null) { - return self.parseError(token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id)); - } + .expr = undefined, + .semicolon_token = undefined, + .doc_comments = ctx.comments, + }); + try ctx.decls.push(&node.base); - const node = try self.createAttachNode(arena, ctx.decls, ast.Node.Use, - ast.Node.Use { - .base = undefined, - .visib_token = ctx.visib_token, - .expr = undefined, - .semicolon_token = undefined, - .doc_comments = ctx.comments, - } - ); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Semicolon, - .ptr = &node.semicolon_token, - } - }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); - continue; - }, - Token.Id.Keyword_var, Token.Id.Keyword_const => { - if (ctx.extern_export_inline_token) |extern_export_inline_token| { - if (extern_export_inline_token.id == Token.Id.Keyword_inline) { - return self.parseError(token, "Invalid token {}", @tagName(extern_export_inline_token.id)); - } + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Semicolon, + .ptr = &node.semicolon_token, + } + }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + continue; + }, + Token.Id.Keyword_var, Token.Id.Keyword_const => { + if (ctx.extern_export_inline_token) |annotated_token| { + if (annotated_token.ptr.id == Token.Id.Keyword_inline) { + *(try tree.errors.addOne()) = Error { + .InvalidToken = Error.InvalidToken { .token = annotated_token.index }, + }; + return tree; } + } - try stack.append(State { - .VarDecl = VarDeclCtx { - .comments = ctx.comments, - .visib_token = ctx.visib_token, - .lib_name = ctx.lib_name, - .comptime_token = null, - .extern_export_token = ctx.extern_export_inline_token, - .mut_token = token, - .list = ctx.decls - } - }); - continue; - }, - Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, - Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - .same_line_comment = null, - }, - .doc_comments = ctx.comments, + try stack.push(State { + .VarDecl = VarDeclCtx { + .comments = ctx.comments, .visib_token = ctx.visib_token, - .name_token = null, - .fn_token = undefined, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = ctx.extern_export_inline_token, - .cc_token = null, - .async_attr = null, - .body_node = null, .lib_name = ctx.lib_name, - .align_expr = null, - }); - try ctx.decls.append(&fn_proto.base); - stack.append(State { .FnDef = fn_proto }) catch unreachable; - try stack.append(State { .FnProto = fn_proto }); - - switch (token.id) { - Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - fn_proto.cc_token = token; - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } - }); - continue; - }, - Token.Id.Keyword_async => { - const async_node = try self.createNode(arena, ast.Node.AsyncAttribute, - ast.Node.AsyncAttribute { - .base = undefined, - .async_token = token, - .allocator_type = null, - .rangle_bracket = null, - } - ); - fn_proto.async_attr = async_node; - - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } - }); - try stack.append(State { .AsyncAllocator = async_node }); - continue; - }, - Token.Id.Keyword_fn => { - fn_proto.fn_token = token; - continue; - }, - else => unreachable, + .comptime_token = null, + .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null, + .mut_token = token_index, + .list = ctx.decls } - }, - else => { - return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id)); - }, - } - }, - State.TopLevelExternOrField => |ctx| { - if (self.eatToken(Token.Id.Identifier)) |identifier| { - std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct); - const node = try arena.construct(ast.Node.StructField { + }); + continue; + }, + Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, + Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { + const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { - .id = ast.Node.Id.StructField, - .same_line_comment = null, + .id = ast.Node.Id.FnProto, }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, - .name_token = identifier, - .type_expr = undefined, + .name_token = null, + .fn_token = undefined, + .params = ast.Node.FnProto.ParamList.init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = if (ctx.extern_export_inline_token) |at| at.index else null, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = ctx.lib_name, + .align_expr = null, }); - const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); - *node_ptr = &node.base; - - stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } }); - try stack.append(State { .ExpectToken = Token.Id.Colon }); - continue; - } + try ctx.decls.push(&fn_proto.base); + stack.push(State { .FnDef = fn_proto }) catch unreachable; + try stack.push(State { .FnProto = fn_proto }); + + switch (token_ptr.id) { + Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { + fn_proto.cc_token = token_index; + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + } + }); + continue; + }, + Token.Id.Keyword_async => { + const async_node = try createNode(arena, ast.Node.AsyncAttribute, + ast.Node.AsyncAttribute { + .base = undefined, + .async_token = token_index, + .allocator_type = null, + .rangle_bracket = null, + } + ); + fn_proto.async_attr = async_node; - stack.append(State{ .ContainerDecl = ctx.container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &ctx.container_decl.fields_and_decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = null, - .lib_name = null, - .comments = ctx.comments, + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + } + }); + try stack.push(State { .AsyncAllocator = async_node }); + continue; + }, + Token.Id.Keyword_fn => { + fn_proto.fn_token = token_index; + continue; + }, + else => unreachable, } + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedVarDeclOrFn = Error.ExpectedVarDeclOrFn { .token = token_index }, + }; + return tree; + }, + } + }, + State.TopLevelExternOrField => |ctx| { + if (eatToken(&tok_it, Token.Id.Identifier)) |identifier| { + std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct); + const node = try arena.construct(ast.Node.StructField { + .base = ast.Node { + .id = ast.Node.Id.StructField, + }, + .doc_comments = ctx.comments, + .visib_token = ctx.visib_token, + .name_token = identifier, + .type_expr = undefined, }); + const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); + *node_ptr = &node.base; + + stack.push(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.type_expr } }); + try stack.push(State { .ExpectToken = Token.Id.Colon }); continue; - }, + } - State.FieldInitValue => |ctx| { - const eq_tok = self.getNextToken(); - if (eq_tok.id != Token.Id.Equal) { - self.putBackToken(eq_tok); - continue; + stack.push(State{ .ContainerDecl = ctx.container_decl }) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &ctx.container_decl.fields_and_decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = null, + .lib_name = null, + .comments = ctx.comments, } - stack.append(State { .Expression = ctx }) catch unreachable; + }); + continue; + }, + + State.FieldInitValue => |ctx| { + const eq_tok_index = tok_it.index; + const eq_tok_ptr = ??tok_it.next(); + if (eq_tok_ptr.id != Token.Id.Equal) { + _ = tok_it.prev(); continue; - }, + } + stack.push(State { .Expression = ctx }) catch unreachable; + continue; + }, - State.ContainerKind => |ctx| { - const token = self.getNextToken(); - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl, - ast.Node.ContainerDecl { - .base = undefined, - .ltoken = ctx.ltoken, - .layout = ctx.layout, - .kind = switch (token.id) { - Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct, - Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union, - Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum, - else => { - return self.parseError(token, "expected {}, {} or {}, found {}", - @tagName(Token.Id.Keyword_struct), - @tagName(Token.Id.Keyword_union), - @tagName(Token.Id.Keyword_enum), - @tagName(token.id)); - }, + State.ContainerKind => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl, + ast.Node.ContainerDecl { + .base = undefined, + .ltoken = ctx.ltoken, + .layout = ctx.layout, + .kind = switch (token_ptr.id) { + Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct, + Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union, + Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedAggregateKw = Error.ExpectedAggregateKw { .token = token_index }, + }; + return tree; }, - .init_arg_expr = ast.Node.ContainerDecl.InitArg.None, - .fields_and_decls = ArrayList(&ast.Node).init(arena), - .rbrace_token = undefined, - } - ); + }, + .init_arg_expr = ast.Node.ContainerDecl.InitArg.None, + .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(arena), + .rbrace_token = undefined, + } + ); - stack.append(State { .ContainerDecl = node }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.LBrace }); - try stack.append(State { .ContainerInitArgStart = node }); + stack.push(State { .ContainerDecl = node }) catch unreachable; + try stack.push(State { .ExpectToken = Token.Id.LBrace }); + try stack.push(State { .ContainerInitArgStart = node }); + continue; + }, + + State.ContainerInitArgStart => |container_decl| { + if (eatToken(&tok_it, Token.Id.LParen) == null) { continue; - }, + } - State.ContainerInitArgStart => |container_decl| { - if (self.eatToken(Token.Id.LParen) == null) { - continue; - } + stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + try stack.push(State { .ContainerInitArg = container_decl }); + continue; + }, - stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - try stack.append(State { .ContainerInitArg = container_decl }); - continue; - }, + State.ContainerInitArg => |container_decl| { + const init_arg_token_index = tok_it.index; + const init_arg_token_ptr = ??tok_it.next(); + switch (init_arg_token_ptr.id) { + Token.Id.Keyword_enum => { + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null}; + const lparen_tok_index = tok_it.index; + const lparen_tok_ptr = ??tok_it.next(); + if (lparen_tok_ptr.id == Token.Id.LParen) { + try stack.push(State { .ExpectToken = Token.Id.RParen } ); + try stack.push(State { .Expression = OptionalCtx { + .RequiredNull = &container_decl.init_arg_expr.Enum, + } }); + } else { + _ = tok_it.prev(); + } + }, + else => { + _ = tok_it.prev(); + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined }; + stack.push(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; + }, + } + continue; + }, - State.ContainerInitArg => |container_decl| { - const init_arg_token = self.getNextToken(); - switch (init_arg_token.id) { - Token.Id.Keyword_enum => { - container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null}; - const lparen_tok = self.getNextToken(); - if (lparen_tok.id == Token.Id.LParen) { - try stack.append(State { .ExpectToken = Token.Id.RParen } ); - try stack.append(State { .Expression = OptionalCtx { - .RequiredNull = &container_decl.init_arg_expr.Enum, - } }); - } else { - self.putBackToken(lparen_tok); - } - }, - else => { - self.putBackToken(init_arg_token); - container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined }; - stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; - }, - } - continue; - }, + State.ContainerDecl => |container_decl| { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try container_decl.fields_and_decls.push(&line_comment.base); + } - State.ContainerDecl => |container_decl| { - while (try self.eatLineComment(arena)) |line_comment| { - try container_decl.fields_and_decls.append(&line_comment.base); - } + const comments = try eatDocComments(arena, &tok_it); + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Identifier => { + switch (container_decl.kind) { + ast.Node.ContainerDecl.Kind.Struct => { + const node = try arena.construct(ast.Node.StructField { + .base = ast.Node { + .id = ast.Node.Id.StructField, + }, + .doc_comments = comments, + .visib_token = null, + .name_token = token_index, + .type_expr = undefined, + }); + const node_ptr = try container_decl.fields_and_decls.addOne(); + *node_ptr = &node.base; - const comments = try self.eatDocComments(arena); - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Identifier => { - switch (container_decl.kind) { - ast.Node.ContainerDecl.Kind.Struct => { - const node = try arena.construct(ast.Node.StructField { - .base = ast.Node { - .id = ast.Node.Id.StructField, - .same_line_comment = null, - }, - .doc_comments = comments, - .visib_token = null, - .name_token = token, - .type_expr = undefined, - }); - const node_ptr = try container_decl.fields_and_decls.addOne(); - *node_ptr = &node.base; - - try stack.append(State { .FieldListCommaOrEnd = container_decl }); - try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } }); - try stack.append(State { .ExpectToken = Token.Id.Colon }); - continue; - }, - ast.Node.ContainerDecl.Kind.Union => { - const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.UnionTag, - ast.Node.UnionTag { - .base = undefined, - .name_token = token, - .type_expr = null, - .value_expr = null, - .doc_comments = comments, - } - ); + try stack.push(State { .FieldListCommaOrEnd = container_decl }); + try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } }); + try stack.push(State { .ExpectToken = Token.Id.Colon }); + continue; + }, + ast.Node.ContainerDecl.Kind.Union => { + const node = try arena.construct(ast.Node.UnionTag { + .base = ast.Node {.id = ast.Node.Id.UnionTag }, + .name_token = token_index, + .type_expr = null, + .value_expr = null, + .doc_comments = comments, + }); + try container_decl.fields_and_decls.push(&node.base); - stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - try stack.append(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } }); - try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } }); - try stack.append(State { .IfToken = Token.Id.Colon }); - continue; - }, - ast.Node.ContainerDecl.Kind.Enum => { - const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.EnumTag, - ast.Node.EnumTag { - .base = undefined, - .name_token = token, - .value = null, - .doc_comments = comments, - } - ); + stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; + try stack.push(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } }); + try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } }); + try stack.push(State { .IfToken = Token.Id.Colon }); + continue; + }, + ast.Node.ContainerDecl.Kind.Enum => { + const node = try arena.construct(ast.Node.EnumTag { + .base = ast.Node { .id = ast.Node.Id.EnumTag }, + .name_token = token_index, + .value = null, + .doc_comments = comments, + }); + try container_decl.fields_and_decls.push(&node.base); - stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &node.value } }); - try stack.append(State { .IfToken = Token.Id.Equal }); - continue; - }, - } - }, - Token.Id.Keyword_pub => { - switch (container_decl.kind) { - ast.Node.ContainerDecl.Kind.Struct => { - try stack.append(State { - .TopLevelExternOrField = TopLevelExternOrFieldCtx { - .visib_token = token, - .container_decl = container_decl, - .comments = comments, - } - }); - continue; - }, - else => { - stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = token, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - } + stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &node.value } }); + try stack.push(State { .IfToken = Token.Id.Equal }); + continue; + }, + } + }, + Token.Id.Keyword_pub => { + switch (container_decl.kind) { + ast.Node.ContainerDecl.Kind.Struct => { + try stack.push(State { + .TopLevelExternOrField = TopLevelExternOrFieldCtx { + .visib_token = token_index, + .container_decl = container_decl, + .comments = comments, + } + }); + continue; + }, + else => { + stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &container_decl.fields_and_decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; } - }, - Token.Id.Keyword_export => { - stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = token, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - }, - Token.Id.RBrace => { - if (comments != null) { - return self.parseError(token, "doc comments must be attached to a node"); + } + }, + Token.Id.Keyword_export => { + stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &container_decl.fields_and_decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, } - container_decl.rbrace_token = token; - continue; - }, - else => { - self.putBackToken(token); - stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = null, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; + }); + continue; + }, + Token.Id.RBrace => { + if (comments != null) { + *(try tree.errors.addOne()) = Error { + .UnattachedDocComment = Error.UnattachedDocComment { .token = token_index }, + }; + return tree; } + container_decl.rbrace_token = token_index; + continue; + }, + else => { + _ = tok_it.prev(); + stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &container_decl.fields_and_decls, + .visib_token = null, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; } - }, + } + }, - State.VarDecl => |ctx| { - const var_decl = try arena.construct(ast.Node.VarDecl { - .base = ast.Node { - .id = ast.Node.Id.VarDecl, - .same_line_comment = null, - }, - .doc_comments = ctx.comments, - .visib_token = ctx.visib_token, - .mut_token = ctx.mut_token, - .comptime_token = ctx.comptime_token, - .extern_export_token = ctx.extern_export_token, - .type_node = null, - .align_node = null, - .init_node = null, - .lib_name = ctx.lib_name, - // initialized later - .name_token = undefined, - .eq_token = undefined, - .semicolon_token = undefined, - }); - try ctx.list.append(&var_decl.base); - - try stack.append(State { .LookForSameLineCommentDirect = &var_decl.base }); - try stack.append(State { .VarDeclAlign = var_decl }); - try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Identifier, - .ptr = &var_decl.name_token, - } - }); - continue; - }, - State.VarDeclAlign => |var_decl| { - try stack.append(State { .VarDeclEq = var_decl }); - - const next_token = self.getNextToken(); - if (next_token.id == Token.Id.Keyword_align) { - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - continue; + State.VarDecl => |ctx| { + const var_decl = try arena.construct(ast.Node.VarDecl { + .base = ast.Node { + .id = ast.Node.Id.VarDecl, + }, + .doc_comments = ctx.comments, + .visib_token = ctx.visib_token, + .mut_token = ctx.mut_token, + .comptime_token = ctx.comptime_token, + .extern_export_token = ctx.extern_export_token, + .type_node = null, + .align_node = null, + .init_node = null, + .lib_name = ctx.lib_name, + // initialized later + .name_token = undefined, + .eq_token = undefined, + .semicolon_token = undefined, + }); + try ctx.list.push(&var_decl.base); + + try stack.push(State { .VarDeclAlign = var_decl }); + try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Identifier, + .ptr = &var_decl.name_token, } - - self.putBackToken(next_token); + }); + continue; + }, + State.VarDeclAlign => |var_decl| { + try stack.push(State { .VarDeclEq = var_decl }); + + const next_token_index = tok_it.index; + const next_token_ptr = ??tok_it.next(); + if (next_token_ptr.id == Token.Id.Keyword_align) { + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); continue; - }, - State.VarDeclEq => |var_decl| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Equal => { - var_decl.eq_token = token; - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Semicolon, - .ptr = &var_decl.semicolon_token, - }, - }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } }); - continue; - }, - Token.Id.Semicolon => { - var_decl.semicolon_token = token; - continue; - }, - else => { - return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id)); - } - } - }, - + } - State.FnDef => |fn_proto| { - const token = self.getNextToken(); - switch(token.id) { - Token.Id.LBrace => { - const block = try self.createNode(arena, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = null, - .lbrace = token, - .statements = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, - } - ); - fn_proto.body_node = &block.base; - stack.append(State { .Block = block }) catch unreachable; - continue; - }, - Token.Id.Semicolon => continue, - else => { - return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id)); - }, + _ = tok_it.prev(); + continue; + }, + State.VarDeclEq => |var_decl| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Equal => { + var_decl.eq_token = token_index; + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Semicolon, + .ptr = &var_decl.semicolon_token, + }, + }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } }); + continue; + }, + Token.Id.Semicolon => { + var_decl.semicolon_token = token_index; + continue; + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedEqOrSemi = Error.ExpectedEqOrSemi { .token = token_index }, + }; + return tree; } - }, - State.FnProto => |fn_proto| { - stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable; - try stack.append(State { .ParamDecl = fn_proto }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); + } + }, - if (self.eatToken(Token.Id.Identifier)) |name_token| { - fn_proto.name_token = name_token; - } - continue; - }, - State.FnProtoAlign => |fn_proto| { - stack.append(State { .FnProtoReturnType = fn_proto }) catch unreachable; - if (self.eatToken(Token.Id.Keyword_align)) |align_token| { - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - } - continue; - }, - State.FnProtoReturnType => |fn_proto| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Bang => { - fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined }; - stack.append(State { - .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet }, - }) catch unreachable; - continue; - }, - else => { - // TODO: this is a special case. Remove this when #760 is fixed - if (token.id == Token.Id.Keyword_error) { - if (self.isPeekToken(Token.Id.LBrace)) { - fn_proto.return_type = ast.Node.FnProto.ReturnType { - .Explicit = &(try self.createLiteral(arena, ast.Node.ErrorType, token)).base - }; - continue; - } - } + State.FnDef => |fn_proto| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch(token_ptr.id) { + Token.Id.LBrace => { + const block = try arena.construct(ast.Node.Block { + .base = ast.Node { .id = ast.Node.Id.Block }, + .label = null, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + fn_proto.body_node = &block.base; + stack.push(State { .Block = block }) catch unreachable; + continue; + }, + Token.Id.Semicolon => continue, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedSemiOrLBrace = Error.ExpectedSemiOrLBrace { .token = token_index }, + }; + return tree; + }, + } + }, + State.FnProto => |fn_proto| { + stack.push(State { .FnProtoAlign = fn_proto }) catch unreachable; + try stack.push(State { .ParamDecl = fn_proto }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); - self.putBackToken(token); - fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined }; - stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; - continue; - }, - } - }, + if (eatToken(&tok_it, Token.Id.Identifier)) |name_token| { + fn_proto.name_token = name_token; + } + continue; + }, + State.FnProtoAlign => |fn_proto| { + stack.push(State { .FnProtoReturnType = fn_proto }) catch unreachable; + if (eatToken(&tok_it, Token.Id.Keyword_align)) |align_token| { + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + } + continue; + }, + State.FnProtoReturnType => |fn_proto| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Bang => { + fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined }; + stack.push(State { + .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet }, + }) catch unreachable; + continue; + }, + else => { + // TODO: this is a special case. Remove this when #760 is fixed + if (token_ptr.id == Token.Id.Keyword_error) { + if ((??tok_it.peek()).id == Token.Id.LBrace) { + const error_type_node = try arena.construct(ast.Node.ErrorType { + .base = ast.Node { .id = ast.Node.Id.ErrorType }, + .token = token_index, + }); + fn_proto.return_type = ast.Node.FnProto.ReturnType { + .Explicit = &error_type_node.base, + }; + continue; + } + } - State.ParamDecl => |fn_proto| { - if (self.eatToken(Token.Id.RParen)) |_| { + _ = tok_it.prev(); + fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined }; + stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; continue; - } - const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.Node.ParamDecl, - ast.Node.ParamDecl { - .base = undefined, - .comptime_token = null, - .noalias_token = null, - .name_token = null, - .type_node = undefined, - .var_args_token = null, - }, - ); + }, + } + }, - stack.append(State { - .ParamDeclEnd = ParamDeclEndCtx { - .param_decl = param_decl, - .fn_proto = fn_proto, - } - }) catch unreachable; - try stack.append(State { .ParamDeclName = param_decl }); - try stack.append(State { .ParamDeclAliasOrComptime = param_decl }); + + State.ParamDecl => |fn_proto| { + if (eatToken(&tok_it, Token.Id.RParen)) |_| { continue; - }, - State.ParamDeclAliasOrComptime => |param_decl| { - if (self.eatToken(Token.Id.Keyword_comptime)) |comptime_token| { - param_decl.comptime_token = comptime_token; - } else if (self.eatToken(Token.Id.Keyword_noalias)) |noalias_token| { - param_decl.noalias_token = noalias_token; + } + const param_decl = try arena.construct(ast.Node.ParamDecl { + .base = ast.Node {.id = ast.Node.Id.ParamDecl }, + .comptime_token = null, + .noalias_token = null, + .name_token = null, + .type_node = undefined, + .var_args_token = null, + }); + try fn_proto.params.push(¶m_decl.base); + + stack.push(State { + .ParamDeclEnd = ParamDeclEndCtx { + .param_decl = param_decl, + .fn_proto = fn_proto, } - continue; - }, - State.ParamDeclName => |param_decl| { - // TODO: Here, we eat two tokens in one state. This means that we can't have - // comments between these two tokens. - if (self.eatToken(Token.Id.Identifier)) |ident_token| { - if (self.eatToken(Token.Id.Colon)) |_| { - param_decl.name_token = ident_token; - } else { - self.putBackToken(ident_token); - } + }) catch unreachable; + try stack.push(State { .ParamDeclName = param_decl }); + try stack.push(State { .ParamDeclAliasOrComptime = param_decl }); + continue; + }, + State.ParamDeclAliasOrComptime => |param_decl| { + if (eatToken(&tok_it, Token.Id.Keyword_comptime)) |comptime_token| { + param_decl.comptime_token = comptime_token; + } else if (eatToken(&tok_it, Token.Id.Keyword_noalias)) |noalias_token| { + param_decl.noalias_token = noalias_token; + } + continue; + }, + State.ParamDeclName => |param_decl| { + // TODO: Here, we eat two tokens in one state. This means that we can't have + // comments between these two tokens. + if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| { + if (eatToken(&tok_it, Token.Id.Colon)) |_| { + param_decl.name_token = ident_token; + } else { + _ = tok_it.prev(); } + } + continue; + }, + State.ParamDeclEnd => |ctx| { + if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| { + ctx.param_decl.var_args_token = ellipsis3; + stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; continue; - }, - State.ParamDeclEnd => |ctx| { - if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| { - ctx.param_decl.var_args_token = ellipsis3; - stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + } + + try stack.push(State { .ParamDeclComma = ctx.fn_proto }); + try stack.push(State { + .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node } + }); + continue; + }, + State.ParamDeclComma => |fn_proto| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) { + ExpectCommaOrEndResult.end_token => |t| { + if (t == null) { + stack.push(State { .ParamDecl = fn_proto }) catch unreachable; + } continue; - } + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, - try stack.append(State { .ParamDeclComma = ctx.fn_proto }); - try stack.append(State { - .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node } - }); - continue; - }, - State.ParamDeclComma => |fn_proto| { - if ((try self.expectCommaOrEnd(Token.Id.RParen)) == null) { - stack.append(State { .ParamDecl = fn_proto }) catch unreachable; - } + State.MaybeLabeledExpression => |ctx| { + if (eatToken(&tok_it, Token.Id.Colon)) |_| { + stack.push(State { + .LabeledExpression = LabelCtx { + .label = ctx.label, + .opt_ctx = ctx.opt_ctx, + } + }) catch unreachable; continue; - }, + } - State.MaybeLabeledExpression => |ctx| { - if (self.eatToken(Token.Id.Colon)) |_| { - stack.append(State { - .LabeledExpression = LabelCtx { + _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label); + continue; + }, + State.LabeledExpression => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.LBrace => { + const block = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block, + ast.Node.Block { + .base = undefined, .label = ctx.label, - .opt_ctx = ctx.opt_ctx, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + } + ); + stack.push(State { .Block = block }) catch unreachable; + continue; + }, + Token.Id.Keyword_while => { + stack.push(State { + .While = LoopCtx { + .label = ctx.label, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), } }) catch unreachable; continue; - } - - _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label); - continue; - }, - State.LabeledExpression => |ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.LBrace => { - const block = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = ctx.label, - .lbrace = token, - .statements = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, - } - ); - stack.append(State { .Block = block }) catch unreachable; - continue; - }, - Token.Id.Keyword_while => { - stack.append(State { - .While = LoopCtx { - .label = ctx.label, - .inline_token = null, - .loop_token = token, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_for => { - stack.append(State { - .For = LoopCtx { - .label = ctx.label, - .inline_token = null, - .loop_token = token, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_suspend => { - const node = try arena.construct(ast.Node.Suspend { - .base = ast.Node { - .id = ast.Node.Id.Suspend, - .same_line_comment = null, - }, + }, + Token.Id.Keyword_for => { + stack.push(State { + .For = LoopCtx { .label = ctx.label, - .suspend_token = token, - .payload = null, - .body = null, - }); - ctx.opt_ctx.store(&node.base); - stack.append(State { .SuspendBody = node }) catch unreachable; - try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); - continue; - }, - Token.Id.Keyword_inline => { - stack.append(State { - .Inline = InlineCtx { - .label = ctx.label, - .inline_token = token, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - else => { - if (ctx.opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected 'while', 'for', 'inline' or '{{', found {}", @tagName(token.id)); - } - - self.putBackToken(token); - continue; - }, - } - }, - State.Inline => |ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_while => { - stack.append(State { - .While = LoopCtx { - .inline_token = ctx.inline_token, - .label = ctx.label, - .loop_token = token, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_for => { - stack.append(State { - .For = LoopCtx { - .inline_token = ctx.inline_token, - .label = ctx.label, - .loop_token = token, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - else => { - if (ctx.opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected 'while' or 'for', found {}", @tagName(token.id)); + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), } - - self.putBackToken(token); - continue; - }, - } - }, - State.While => |ctx| { - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.While, - ast.Node.While { - .base = undefined, - .label = ctx.label, - .inline_token = ctx.inline_token, - .while_token = ctx.loop_token, - .condition = undefined, - .payload = null, - .continue_expr = null, - .body = undefined, - .@"else" = null, - } - ); - stack.append(State { .Else = &node.@"else" }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.append(State { .WhileContinueExpr = &node.continue_expr }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - State.WhileContinueExpr => |dest| { - stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - State.For => |ctx| { - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.For, - ast.Node.For { - .base = undefined, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_suspend => { + const node = try arena.construct(ast.Node.Suspend { + .base = ast.Node { + .id = ast.Node.Id.Suspend, + }, .label = ctx.label, - .inline_token = ctx.inline_token, - .for_token = ctx.loop_token, - .array_expr = undefined, + .suspend_token = token_index, .payload = null, - .body = undefined, - .@"else" = null, - } - ); - stack.append(State { .Else = &node.@"else" }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.append(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.array_expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - State.Else => |dest| { - if (self.eatToken(Token.Id.Keyword_else)) |else_token| { - const node = try self.createNode(arena, ast.Node.Else, - ast.Node.Else { - .base = undefined, - .else_token = else_token, - .payload = null, - .body = undefined, + .body = null, + }); + ctx.opt_ctx.store(&node.base); + stack.push(State { .SuspendBody = node }) catch unreachable; + try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + continue; + }, + Token.Id.Keyword_inline => { + stack.push(State { + .Inline = InlineCtx { + .label = ctx.label, + .inline_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), } - ); - *dest = node; + }) catch unreachable; + continue; + }, + else => { + if (ctx.opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedLabelable = Error.ExpectedLabelable { .token = token_index }, + }; + return tree; + } - stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable; - try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + _ = tok_it.prev(); continue; - } else { + }, + } + }, + State.Inline => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_while => { + stack.push(State { + .While = LoopCtx { + .inline_token = ctx.inline_token, + .label = ctx.label, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + } + }) catch unreachable; continue; - } - }, - - - State.Block => |block| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.RBrace => { - block.rbrace = token; - continue; - }, - else => { - self.putBackToken(token); - stack.append(State { .Block = block }) catch unreachable; - - var any_comments = false; - while (try self.eatLineComment(arena)) |line_comment| { - try block.statements.append(&line_comment.base); - any_comments = true; + }, + Token.Id.Keyword_for => { + stack.push(State { + .For = LoopCtx { + .inline_token = ctx.inline_token, + .label = ctx.label, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), } - if (any_comments) continue; - - try stack.append(State { .Statement = block }); - continue; - }, - } - }, - State.Statement => |block| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_comptime => { - stack.append(State { - .ComptimeStatement = ComptimeStatementCtx { - .comptime_token = token, - .block = block, - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_var, Token.Id.Keyword_const => { - stack.append(State { - .VarDecl = VarDeclCtx { - .comments = null, - .visib_token = null, - .comptime_token = null, - .extern_export_token = null, - .lib_name = null, - .mut_token = token, - .list = &block.statements, - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { - const node = try arena.construct(ast.Node.Defer { - .base = ast.Node { - .id = ast.Node.Id.Defer, - .same_line_comment = null, - }, - .defer_token = token, - .kind = switch (token.id) { - Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, - Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, - else => unreachable, - }, - .expr = undefined, - }); - const node_ptr = try block.statements.addOne(); - *node_ptr = &node.base; - - stack.append(State { .Semicolon = node_ptr }) catch unreachable; - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); - continue; - }, - Token.Id.LBrace => { - const inner_block = try self.createAttachNode(arena, &block.statements, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = null, - .lbrace = token, - .statements = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, - } - ); - stack.append(State { .Block = inner_block }) catch unreachable; - continue; - }, - else => { - self.putBackToken(token); - const statement = try block.statements.addOne(); - stack.append(State { .LookForSameLineComment = statement }) catch unreachable; - try stack.append(State { .Semicolon = statement }); - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); - continue; - } - } - }, - State.ComptimeStatement => |ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_var, Token.Id.Keyword_const => { - stack.append(State { - .VarDecl = VarDeclCtx { - .comments = null, - .visib_token = null, - .comptime_token = ctx.comptime_token, - .extern_export_token = null, - .lib_name = null, - .mut_token = token, - .list = &ctx.block.statements, - } - }) catch unreachable; - continue; - }, - else => { - self.putBackToken(token); - self.putBackToken(ctx.comptime_token); - const statement = try ctx.block.statements.addOne(); - stack.append(State { .LookForSameLineComment = statement }) catch unreachable; - try stack.append(State { .Semicolon = statement }); - try stack.append(State { .Expression = OptionalCtx { .Required = statement } }); - continue; - } - } - }, - State.Semicolon => |node_ptr| { - const node = *node_ptr; - if (requireSemiColon(node)) { - stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; + }) catch unreachable; continue; - } - continue; - }, - - State.LookForSameLineComment => |node_ptr| { - try self.lookForSameLineComment(arena, *node_ptr); - continue; - }, - - State.LookForSameLineCommentDirect => |node| { - try self.lookForSameLineComment(arena, node); - continue; - }, - + }, + else => { + if (ctx.opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedInlinable = Error.ExpectedInlinable { .token = token_index }, + }; + return tree; + } - State.AsmOutputItems => |items| { - const lbracket = self.getNextToken(); - if (lbracket.id != Token.Id.LBracket) { - self.putBackToken(lbracket); + _ = tok_it.prev(); continue; + }, + } + }, + State.While => |ctx| { + const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.While, + ast.Node.While { + .base = undefined, + .label = ctx.label, + .inline_token = ctx.inline_token, + .while_token = ctx.loop_token, + .condition = undefined, + .payload = null, + .continue_expr = null, + .body = undefined, + .@"else" = null, } - - const node = try self.createNode(arena, ast.Node.AsmOutput, - ast.Node.AsmOutput { - .base = undefined, - .symbolic_name = undefined, - .constraint = undefined, - .kind = undefined, - } - ); - try items.append(node); - - stack.append(State { .AsmOutputItems = items }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.Comma }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .AsmOutputReturnOrType = node }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); - try stack.append(State { .ExpectToken = Token.Id.RBracket }); - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); - continue; - }, - State.AsmOutputReturnOrType => |node| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Identifier => { - node.kind = ast.Node.AsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.Node.Identifier, token) }; - continue; - }, - Token.Id.Arrow => { - node.kind = ast.Node.AsmOutput.Kind { .Return = undefined }; - try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } }); - continue; - }, - else => { - return self.parseError(token, "expected '->' or {}, found {}", - @tagName(Token.Id.Identifier), - @tagName(token.id)); - }, - } - }, - State.AsmInputItems => |items| { - const lbracket = self.getNextToken(); - if (lbracket.id != Token.Id.LBracket) { - self.putBackToken(lbracket); - continue; + ); + stack.push(State { .Else = &node.@"else" }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.push(State { .WhileContinueExpr = &node.continue_expr }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + State.WhileContinueExpr => |dest| { + stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + State.For => |ctx| { + const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.For, + ast.Node.For { + .base = undefined, + .label = ctx.label, + .inline_token = ctx.inline_token, + .for_token = ctx.loop_token, + .array_expr = undefined, + .payload = null, + .body = undefined, + .@"else" = null, } - - const node = try self.createNode(arena, ast.Node.AsmInput, - ast.Node.AsmInput { + ); + stack.push(State { .Else = &node.@"else" }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.push(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.array_expr } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + State.Else => |dest| { + if (eatToken(&tok_it, Token.Id.Keyword_else)) |else_token| { + const node = try createNode(arena, ast.Node.Else, + ast.Node.Else { .base = undefined, - .symbolic_name = undefined, - .constraint = undefined, - .expr = undefined, + .else_token = else_token, + .payload = null, + .body = undefined, } ); - try items.append(node); - - stack.append(State { .AsmInputItems = items }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.Comma }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); - try stack.append(State { .ExpectToken = Token.Id.RBracket }); - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + *dest = node; + + stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable; + try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); continue; - }, - State.AsmClopperItems => |items| { - stack.append(State { .AsmClopperItems = items }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.Comma }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } }); + } else { continue; - }, + } + }, - State.ExprListItemOrEnd => |list_state| { - if (self.eatToken(list_state.end)) |token| { - *list_state.ptr = token; + State.Block => |block| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.RBrace => { + block.rbrace = token_index; continue; - } + }, + else => { + _ = tok_it.prev(); + stack.push(State { .Block = block }) catch unreachable; + + var any_comments = false; + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try block.statements.push(&line_comment.base); + any_comments = true; + } + if (any_comments) continue; - stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } }); - continue; - }, - State.ExprListCommaOrEnd => |list_state| { - if (try self.expectCommaOrEnd(list_state.end)) |end| { - *list_state.ptr = end; + try stack.push(State { .Statement = block }); continue; - } else { - stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable; + }, + } + }, + State.Statement => |block| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_comptime => { + stack.push(State { + .ComptimeStatement = ComptimeStatementCtx { + .comptime_token = token_index, + .block = block, + } + }) catch unreachable; continue; - } - }, - State.FieldInitListItemOrEnd => |list_state| { - while (try self.eatLineComment(arena)) |line_comment| { - try list_state.list.append(&line_comment.base); - } + }, + Token.Id.Keyword_var, Token.Id.Keyword_const => { + stack.push(State { + .VarDecl = VarDeclCtx { + .comments = null, + .visib_token = null, + .comptime_token = null, + .extern_export_token = null, + .lib_name = null, + .mut_token = token_index, + .list = &block.statements, + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { + const node = try arena.construct(ast.Node.Defer { + .base = ast.Node { + .id = ast.Node.Id.Defer, + }, + .defer_token = token_index, + .kind = switch (token_ptr.id) { + Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, + Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, + else => unreachable, + }, + .expr = undefined, + }); + const node_ptr = try block.statements.addOne(); + *node_ptr = &node.base; - if (self.eatToken(Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; + stack.push(State { .Semicolon = node_ptr }) catch unreachable; + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); continue; - } + }, + Token.Id.LBrace => { + const inner_block = try arena.construct(ast.Node.Block { + .base = ast.Node { .id = ast.Node.Id.Block }, + .label = null, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + try block.statements.push(&inner_block.base); - const node = try arena.construct(ast.Node.FieldInitializer { - .base = ast.Node { - .id = ast.Node.Id.FieldInitializer, - .same_line_comment = null, - }, - .period_token = undefined, - .name_token = undefined, - .expr = undefined, - }); - try list_state.list.append(&node.base); - - stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } }); - try stack.append(State { .ExpectToken = Token.Id.Equal }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Identifier, - .ptr = &node.name_token, - } - }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Period, - .ptr = &node.period_token, - } - }); - continue; - }, - State.FieldInitListCommaOrEnd => |list_state| { - if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { - *list_state.ptr = end; + stack.push(State { .Block = inner_block }) catch unreachable; continue; - } else { - stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable; + }, + else => { + _ = tok_it.prev(); + const statement = try block.statements.addOne(); + try stack.push(State { .Semicolon = statement }); + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); continue; } - }, - State.FieldListCommaOrEnd => |container_decl| { - if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { - container_decl.rbrace_token = end; + } + }, + State.ComptimeStatement => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_var, Token.Id.Keyword_const => { + stack.push(State { + .VarDecl = VarDeclCtx { + .comments = null, + .visib_token = null, + .comptime_token = ctx.comptime_token, + .extern_export_token = null, + .lib_name = null, + .mut_token = token_index, + .list = &ctx.block.statements, + } + }) catch unreachable; + continue; + }, + else => { + _ = tok_it.prev(); + _ = tok_it.prev(); + const statement = try ctx.block.statements.addOne(); + try stack.push(State { .Semicolon = statement }); + try stack.push(State { .Expression = OptionalCtx { .Required = statement } }); continue; } + } + }, + State.Semicolon => |node_ptr| { + const node = *node_ptr; + if (requireSemiColon(node)) { + stack.push(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; + continue; + } + continue; + }, - try self.lookForSameLineComment(arena, container_decl.fields_and_decls.toSlice()[container_decl.fields_and_decls.len - 1]); - try stack.append(State { .ContainerDecl = container_decl }); + State.AsmOutputItems => |items| { + const lbracket_index = tok_it.index; + const lbracket_ptr = ??tok_it.next(); + if (lbracket_ptr.id != Token.Id.LBracket) { + _ = tok_it.prev(); continue; - }, - State.ErrorTagListItemOrEnd => |list_state| { - while (try self.eatLineComment(arena)) |line_comment| { - try list_state.list.append(&line_comment.base); - } + } - if (self.eatToken(Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; - continue; + const node = try createNode(arena, ast.Node.AsmOutput, + ast.Node.AsmOutput { + .base = undefined, + .symbolic_name = undefined, + .constraint = undefined, + .kind = undefined, } - - const node_ptr = try list_state.list.addOne(); - - try stack.append(State { .ErrorTagListCommaOrEnd = list_state }); - try stack.append(State { .ErrorTag = node_ptr }); - continue; - }, - State.ErrorTagListCommaOrEnd => |list_state| { - if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { - *list_state.ptr = end; + ); + try items.push(node); + + stack.push(State { .AsmOutputItems = items }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.Comma }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .AsmOutputReturnOrType = node }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); + try stack.push(State { .ExpectToken = Token.Id.RBracket }); + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + continue; + }, + State.AsmOutputReturnOrType => |node| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Identifier => { + node.kind = ast.Node.AsmOutput.Kind { .Variable = try createLiteral(arena, ast.Node.Identifier, token_index) }; continue; - } else { - stack.append(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable; + }, + Token.Id.Arrow => { + node.kind = ast.Node.AsmOutput.Kind { .Return = undefined }; + try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } }); continue; - } - }, - State.SwitchCaseOrEnd => |list_state| { - while (try self.eatLineComment(arena)) |line_comment| { - try list_state.list.append(&line_comment.base); - } + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedAsmOutputReturnOrType = Error.ExpectedAsmOutputReturnOrType { + .token = token_index, + }, + }; + return tree; + }, + } + }, + State.AsmInputItems => |items| { + const lbracket_index = tok_it.index; + const lbracket_ptr = ??tok_it.next(); + if (lbracket_ptr.id != Token.Id.LBracket) { + _ = tok_it.prev(); + continue; + } - if (self.eatToken(Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; - continue; + const node = try createNode(arena, ast.Node.AsmInput, + ast.Node.AsmInput { + .base = undefined, + .symbolic_name = undefined, + .constraint = undefined, + .expr = undefined, } + ); + try items.push(node); + + stack.push(State { .AsmInputItems = items }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.Comma }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); + try stack.push(State { .ExpectToken = Token.Id.RBracket }); + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + continue; + }, + State.AsmClobberItems => |items| { + stack.push(State { .AsmClobberItems = items }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.Comma }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } }); + continue; + }, - const comments = try self.eatDocComments(arena); - const node = try arena.construct(ast.Node.SwitchCase { - .base = ast.Node { - .id = ast.Node.Id.SwitchCase, - .same_line_comment = null, - }, - .items = ArrayList(&ast.Node).init(arena), - .payload = null, - .expr = undefined, - }); - try list_state.list.append(&node.base); - try stack.append(State { .SwitchCaseCommaOrEnd = list_state }); - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); - try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.append(State { .SwitchCaseFirstItem = &node.items }); + State.ExprListItemOrEnd => |list_state| { + if (eatToken(&tok_it, list_state.end)) |token_index| { + *list_state.ptr = token_index; continue; - }, + } - State.SwitchCaseCommaOrEnd => |list_state| { - if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { + stack.push(State { .ExprListCommaOrEnd = list_state }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } }); + continue; + }, + State.ExprListCommaOrEnd => |list_state| { + switch (expectCommaOrEnd(&tok_it, list_state.end)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { *list_state.ptr = end; continue; - } + } else { + stack.push(State { .ExprListItemOrEnd = list_state }) catch unreachable; + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + State.FieldInitListItemOrEnd => |list_state| { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try list_state.list.push(&line_comment.base); + } - const node = list_state.list.toSlice()[list_state.list.len - 1]; - try self.lookForSameLineComment(arena, node); - try stack.append(State { .SwitchCaseOrEnd = list_state }); + if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { + *list_state.ptr = rbrace; continue; - }, + } - State.SwitchCaseFirstItem => |case_items| { - const token = self.getNextToken(); - if (token.id == Token.Id.Keyword_else) { - const else_node = try self.createAttachNode(arena, case_items, ast.Node.SwitchElse, - ast.Node.SwitchElse { - .base = undefined, - .token = token, - } - ); - try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); + const node = try arena.construct(ast.Node.FieldInitializer { + .base = ast.Node { + .id = ast.Node.Id.FieldInitializer, + }, + .period_token = undefined, + .name_token = undefined, + .expr = undefined, + }); + try list_state.list.push(&node.base); + + stack.push(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx{ .Required = &node.expr } }); + try stack.push(State { .ExpectToken = Token.Id.Equal }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Identifier, + .ptr = &node.name_token, + } + }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Period, + .ptr = &node.period_token, + } + }); + continue; + }, + State.FieldInitListCommaOrEnd => |list_state| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + *list_state.ptr = end; continue; } else { - self.putBackToken(token); - try stack.append(State { .SwitchCaseItem = case_items }); + stack.push(State { .FieldInitListItemOrEnd = list_state }) catch unreachable; continue; - } - }, - State.SwitchCaseItem => |case_items| { - stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; - try stack.append(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } }); - }, - State.SwitchCaseItemCommaOrEnd => |case_items| { - if ((try self.expectCommaOrEnd(Token.Id.EqualAngleBracketRight)) == null) { - stack.append(State { .SwitchCaseItem = case_items }) catch unreachable; - } + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + State.FieldListCommaOrEnd => |container_decl| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + container_decl.rbrace_token = end; + continue; + } else { + try stack.push(State { .ContainerDecl = container_decl }); + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + State.ErrorTagListItemOrEnd => |list_state| { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try list_state.list.push(&line_comment.base); + } + + if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { + *list_state.ptr = rbrace; continue; - }, + } + const node_ptr = try list_state.list.addOne(); - State.SuspendBody => |suspend_node| { - if (suspend_node.payload != null) { - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } }); - } - continue; - }, - State.AsyncAllocator => |async_node| { - if (self.eatToken(Token.Id.AngleBracketLeft) == null) { + try stack.push(State { .ErrorTagListCommaOrEnd = list_state }); + try stack.push(State { .ErrorTag = node_ptr }); + continue; + }, + State.ErrorTagListCommaOrEnd => |list_state| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + *list_state.ptr = end; continue; - } + } else { + stack.push(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable; + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + State.SwitchCaseOrEnd => |list_state| { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try list_state.list.push(&line_comment.base); + } - async_node.rangle_bracket = Token(undefined); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.AngleBracketRight, - .ptr = &??async_node.rangle_bracket, - } - }); - try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } }); + if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { + *list_state.ptr = rbrace; continue; - }, - State.AsyncEnd => |ctx| { - const node = ctx.ctx.get() ?? continue; - - switch (node.id) { - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node); - fn_proto.async_attr = ctx.attribute; - continue; - }, - ast.Node.Id.SuffixOp => { - const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node); - if (suffix_op.op == ast.Node.SuffixOp.Op.Call) { - suffix_op.op.Call.async_attr = ctx.attribute; - continue; - } + } - return self.parseError(node.firstToken(), "expected {}, found {}.", - @tagName(ast.Node.SuffixOp.Op.Call), - @tagName(suffix_op.op)); - }, - else => { - return self.parseError(node.firstToken(), "expected {} or {}, found {}.", - @tagName(ast.Node.SuffixOp.Op.Call), - @tagName(ast.Node.Id.FnProto), - @tagName(node.id)); - } - } - }, + const comments = try eatDocComments(arena, &tok_it); + const node = try arena.construct(ast.Node.SwitchCase { + .base = ast.Node { + .id = ast.Node.Id.SwitchCase, + }, + .items = ast.Node.SwitchCase.ItemList.init(arena), + .payload = null, + .expr = undefined, + }); + try list_state.list.push(&node.base); + try stack.push(State { .SwitchCaseCommaOrEnd = list_state }); + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); + try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.push(State { .SwitchCaseFirstItem = &node.items }); + continue; + }, - State.ExternType => |ctx| { - if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - .same_line_comment = null, - }, - .doc_comments = ctx.comments, - .visib_token = null, - .name_token = null, - .fn_token = fn_token, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = ctx.extern_token, - .cc_token = null, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - }); - ctx.opt_ctx.store(&fn_proto.base); - stack.append(State { .FnProto = fn_proto }) catch unreachable; + State.SwitchCaseCommaOrEnd => |list_state| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + *list_state.ptr = end; continue; - } + } else { + try stack.push(State { .SwitchCaseOrEnd = list_state }); + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, - stack.append(State { - .ContainerKind = ContainerKindCtx { - .opt_ctx = ctx.opt_ctx, - .ltoken = ctx.extern_token, - .layout = ast.Node.ContainerDecl.Layout.Extern, - }, - }) catch unreachable; - continue; - }, - State.SliceOrArrayAccess => |node| { - var token = self.getNextToken(); - switch (token.id) { - Token.Id.Ellipsis2 => { - const start = node.op.ArrayAccess; - node.op = ast.Node.SuffixOp.Op { - .Slice = ast.Node.SuffixOp.SliceRange { - .start = start, - .end = null, - } - }; + State.SwitchCaseFirstItem => |case_items| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id == Token.Id.Keyword_else) { + const else_node = try arena.construct(ast.Node.SwitchElse { + .base = ast.Node{ .id = ast.Node.Id.SwitchElse}, + .token = token_index, + }); + try case_items.push(&else_node.base); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RBracket, - .ptr = &node.rtoken, - } - }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } }); - continue; - }, - Token.Id.RBracket => { - node.rtoken = token; - continue; - }, - else => { - return self.parseError(token, "expected ']' or '..', found {}", @tagName(token.id)); + try stack.push(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); + continue; + } else { + _ = tok_it.prev(); + try stack.push(State { .SwitchCaseItem = case_items }); + continue; + } + }, + State.SwitchCaseItem => |case_items| { + stack.push(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; + try stack.push(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } }); + }, + State.SwitchCaseItemCommaOrEnd => |case_items| { + switch (expectCommaOrEnd(&tok_it, Token.Id.EqualAngleBracketRight)) { + ExpectCommaOrEndResult.end_token => |t| { + if (t == null) { + stack.push(State { .SwitchCaseItem = case_items }) catch unreachable; } - } - }, - State.SliceOrArrayType => |node| { - if (self.eatToken(Token.Id.RBracket)) |_| { - node.op = ast.Node.PrefixOp.Op { - .SliceType = ast.Node.PrefixOp.AddrOfInfo { - .align_expr = null, - .bit_offset_start_token = null, - .bit_offset_end_token = null, - .const_token = null, - .volatile_token = null, - } - }; - stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - try stack.append(State { .AddrOfModifiers = &node.op.SliceType }); continue; - } + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + continue; + }, + - node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined }; - stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.RBracket }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } }); + State.SuspendBody => |suspend_node| { + if (suspend_node.payload != null) { + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } }); + } + continue; + }, + State.AsyncAllocator => |async_node| { + if (eatToken(&tok_it, Token.Id.AngleBracketLeft) == null) { continue; - }, - State.AddrOfModifiers => |addr_of_info| { - var token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_align => { - stack.append(state) catch unreachable; - if (addr_of_info.align_expr != null) { - return self.parseError(token, "multiple align qualifiers"); - } - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - Token.Id.Keyword_const => { - stack.append(state) catch unreachable; - if (addr_of_info.const_token != null) { - return self.parseError(token, "duplicate qualifier: const"); - } - addr_of_info.const_token = token; - continue; - }, - Token.Id.Keyword_volatile => { - stack.append(state) catch unreachable; - if (addr_of_info.volatile_token != null) { - return self.parseError(token, "duplicate qualifier: volatile"); - } - addr_of_info.volatile_token = token; - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, + } + async_node.rangle_bracket = TokenIndex(0); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.AngleBracketRight, + .ptr = &??async_node.rangle_bracket, + } + }); + try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } }); + continue; + }, + State.AsyncEnd => |ctx| { + const node = ctx.ctx.get() ?? continue; - State.Payload => |opt_ctx| { - const token = self.getNextToken(); - if (token.id != Token.Id.Pipe) { - if (opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected {}, found {}.", - @tagName(Token.Id.Pipe), - @tagName(token.id)); + switch (node.id) { + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node); + fn_proto.async_attr = ctx.attribute; + continue; + }, + ast.Node.Id.SuffixOp => { + const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node); + if (suffix_op.op == @TagType(ast.Node.SuffixOp.Op).Call) { + suffix_op.op.Call.async_attr = ctx.attribute; + continue; } - self.putBackToken(token); - continue; + *(try tree.errors.addOne()) = Error { + .ExpectedCall = Error.ExpectedCall { .node = node }, + }; + return tree; + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedCallOrFnProto = Error.ExpectedCallOrFnProto { .node = node }, + }; + return tree; } + } + }, - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Payload, - ast.Node.Payload { - .base = undefined, - .lpipe = token, - .error_symbol = undefined, - .rpipe = undefined - } - ); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } - }) catch unreachable; - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } }); + State.ExternType => |ctx| { + if (eatToken(&tok_it, Token.Id.Keyword_fn)) |fn_token| { + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, + }, + .doc_comments = ctx.comments, + .visib_token = null, + .name_token = null, + .fn_token = fn_token, + .params = ast.Node.FnProto.ParamList.init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = ctx.extern_token, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + }); + ctx.opt_ctx.store(&fn_proto.base); + stack.push(State { .FnProto = fn_proto }) catch unreachable; continue; - }, - State.PointerPayload => |opt_ctx| { - const token = self.getNextToken(); - if (token.id != Token.Id.Pipe) { - if (opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected {}, found {}.", - @tagName(Token.Id.Pipe), - @tagName(token.id)); - } + } + + stack.push(State { + .ContainerKind = ContainerKindCtx { + .opt_ctx = ctx.opt_ctx, + .ltoken = ctx.extern_token, + .layout = ast.Node.ContainerDecl.Layout.Extern, + }, + }) catch unreachable; + continue; + }, + State.SliceOrArrayAccess => |node| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Ellipsis2 => { + const start = node.op.ArrayAccess; + node.op = ast.Node.SuffixOp.Op { + .Slice = ast.Node.SuffixOp.Op.Slice { + .start = start, + .end = null, + } + }; - self.putBackToken(token); + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.RBracket, + .ptr = &node.rtoken, + } + }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } }); + continue; + }, + Token.Id.RBracket => { + node.rtoken = token_index; continue; + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedSliceOrRBracket = Error.ExpectedSliceOrRBracket { .token = token_index }, + }; + return tree; } - - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload, - ast.Node.PointerPayload { - .base = undefined, - .lpipe = token, - .ptr_token = null, - .value_symbol = undefined, - .rpipe = undefined + } + }, + State.SliceOrArrayType => |node| { + if (eatToken(&tok_it, Token.Id.RBracket)) |_| { + node.op = ast.Node.PrefixOp.Op { + .SliceType = ast.Node.PrefixOp.AddrOfInfo { + .align_expr = null, + .bit_offset_start_token = null, + .bit_offset_end_token = null, + .const_token = null, + .volatile_token = null, } - ); + }; + stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + try stack.push(State { .AddrOfModifiers = &node.op.SliceType }); + continue; + } - stack.append(State {.LookForSameLineCommentDirect = &node.base }) catch unreachable; - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Pipe, - .ptr = &node.rpipe, + node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined }; + stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + try stack.push(State { .ExpectToken = Token.Id.RBracket }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } }); + continue; + }, + State.AddrOfModifiers => |addr_of_info| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_align => { + stack.push(state) catch unreachable; + if (addr_of_info.align_expr != null) { + *(try tree.errors.addOne()) = Error { + .ExtraAlignQualifier = Error.ExtraAlignQualifier { .token = token_index }, + }; + return tree; } - }); - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); - try stack.append(State { - .OptionalTokenSave = OptionalTokenSave { - .id = Token.Id.Asterisk, - .ptr = &node.ptr_token, + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + Token.Id.Keyword_const => { + stack.push(state) catch unreachable; + if (addr_of_info.const_token != null) { + *(try tree.errors.addOne()) = Error { + .ExtraConstQualifier = Error.ExtraConstQualifier { .token = token_index }, + }; + return tree; } - }); - continue; - }, - State.PointerIndexPayload => |opt_ctx| { - const token = self.getNextToken(); - if (token.id != Token.Id.Pipe) { - if (opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected {}, found {}.", - @tagName(Token.Id.Pipe), - @tagName(token.id)); + addr_of_info.const_token = token_index; + continue; + }, + Token.Id.Keyword_volatile => { + stack.push(state) catch unreachable; + if (addr_of_info.volatile_token != null) { + *(try tree.errors.addOne()) = Error { + .ExtraVolatileQualifier = Error.ExtraVolatileQualifier { .token = token_index }, + }; + return tree; } - - self.putBackToken(token); + addr_of_info.volatile_token = token_index; continue; - } + }, + else => { + _ = tok_it.prev(); + continue; + }, + } + }, - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload, - ast.Node.PointerIndexPayload { - .base = undefined, - .lpipe = token, - .ptr_token = null, - .value_symbol = undefined, - .index_symbol = null, - .rpipe = undefined - } - ); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } - }) catch unreachable; - try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } }); - try stack.append(State { .IfToken = Token.Id.Comma }); - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); - try stack.append(State { - .OptionalTokenSave = OptionalTokenSave { - .id = Token.Id.Asterisk, - .ptr = &node.ptr_token, - } - }); - continue; - }, + State.Payload => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != Token.Id.Pipe) { + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = Token.Id.Pipe, + }, + }; + return tree; + } + _ = tok_it.prev(); + continue; + } - State.Expression => |opt_ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression, - ast.Node.ControlFlowExpression { - .base = undefined, - .ltoken = token, - .kind = undefined, - .rhs = null, - } - ); - - stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable; - - switch (token.id) { - Token.Id.Keyword_break => { - node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null }; - try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } }); - try stack.append(State { .IfToken = Token.Id.Colon }); - }, - Token.Id.Keyword_continue => { - node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null }; - try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } }); - try stack.append(State { .IfToken = Token.Id.Colon }); - }, - Token.Id.Keyword_return => { - node.kind = ast.Node.ControlFlowExpression.Kind.Return; - }, - else => unreachable, - } - continue; - }, - Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token, - .op = switch (token.id) { - Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} }, - Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} }, - Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} }, - else => unreachable, - }, - .rhs = undefined, - } - ); - - stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - continue; - }, - else => { - if (!try self.parseBlockExpr(&stack, arena, opt_ctx, token)) { - self.putBackToken(token); - stack.append(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable; - } - continue; - } + const node = try createToCtxNode(arena, opt_ctx, ast.Node.Payload, + ast.Node.Payload { + .base = undefined, + .lpipe = token_index, + .error_symbol = undefined, + .rpipe = undefined } - }, - State.RangeExpressionBegin => |opt_ctx| { - stack.append(State { .RangeExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .Expression = opt_ctx }); - continue; - }, - State.RangeExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + ); - if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = ellipsis3, - .op = ast.Node.InfixOp.Op.Range, - .rhs = undefined, - } - ); - stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - continue; + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Pipe, + .ptr = &node.rpipe, } - }, - State.AssignmentExpressionBegin => |opt_ctx| { - stack.append(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .Expression = opt_ctx }); - continue; - }, - - State.AssignmentExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token = self.getNextToken(); - if (tokenIdToAssignment(token.id)) |ass_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token, - .op = ass_id, - .rhs = undefined, - } - ); - stack.append(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; + }) catch unreachable; + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } }); + continue; + }, + State.PointerPayload => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != Token.Id.Pipe) { + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = Token.Id.Pipe, + }, + }; + return tree; } - }, - State.UnwrapExpressionBegin => |opt_ctx| { - stack.append(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BoolOrExpressionBegin = opt_ctx }); + _ = tok_it.prev(); continue; - }, - - State.UnwrapExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token = self.getNextToken(); - if (tokenIdToUnwrapExpr(token.id)) |unwrap_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token, - .op = unwrap_id, - .rhs = undefined, - } - ); + } - stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); + const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload, + ast.Node.PointerPayload { + .base = undefined, + .lpipe = token_index, + .ptr_token = null, + .value_symbol = undefined, + .rpipe = undefined + } + ); - if (node.op == ast.Node.InfixOp.Op.Catch) { - try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } }); - } - continue; - } else { - self.putBackToken(token); - continue; + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } + }); + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); + try stack.push(State { + .OptionalTokenSave = OptionalTokenSave { + .id = Token.Id.Asterisk, + .ptr = &node.ptr_token, + } + }); + continue; + }, + State.PointerIndexPayload => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != Token.Id.Pipe) { + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = Token.Id.Pipe, + }, + }; + return tree; } - }, - State.BoolOrExpressionBegin => |opt_ctx| { - stack.append(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BoolAndExpressionBegin = opt_ctx }); + _ = tok_it.prev(); continue; - }, - - State.BoolOrExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + } - if (self.eatToken(Token.Id.Keyword_or)) |or_token| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = or_token, - .op = ast.Node.InfixOp.Op.BoolOr, - .rhs = undefined, - } - ); - stack.append(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; + const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload, + ast.Node.PointerIndexPayload { + .base = undefined, + .lpipe = token_index, + .ptr_token = null, + .value_symbol = undefined, + .index_symbol = null, + .rpipe = undefined } - }, + ); - State.BoolAndExpressionBegin => |opt_ctx| { - stack.append(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .ComparisonExpressionBegin = opt_ctx }); - continue; - }, + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } + }) catch unreachable; + try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } }); + try stack.push(State { .IfToken = Token.Id.Comma }); + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); + try stack.push(State { + .OptionalTokenSave = OptionalTokenSave { + .id = Token.Id.Asterisk, + .ptr = &node.ptr_token, + } + }); + continue; + }, - State.BoolAndExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - if (self.eatToken(Token.Id.Keyword_and)) |and_token| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { + State.Expression => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression, + ast.Node.ControlFlowExpression { .base = undefined, - .lhs = lhs, - .op_token = and_token, - .op = ast.Node.InfixOp.Op.BoolAnd, - .rhs = undefined, + .ltoken = token_index, + .kind = undefined, + .rhs = null, } ); - stack.append(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, - - State.ComparisonExpressionBegin => |opt_ctx| { - stack.append(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BinaryOrExpressionBegin = opt_ctx }); - continue; - }, - State.ComparisonExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + stack.push(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable; - const token = self.getNextToken(); - if (tokenIdToComparison(token.id)) |comp_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { + switch (token_ptr.id) { + Token.Id.Keyword_break => { + node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null }; + try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } }); + try stack.push(State { .IfToken = Token.Id.Colon }); + }, + Token.Id.Keyword_continue => { + node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null }; + try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } }); + try stack.push(State { .IfToken = Token.Id.Colon }); + }, + Token.Id.Keyword_return => { + node.kind = ast.Node.ControlFlowExpression.Kind.Return; + }, + else => unreachable, + } + continue; + }, + Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { .base = undefined, - .lhs = lhs, - .op_token = token, - .op = comp_id, + .op_token = token_index, + .op = switch (token_ptr.id) { + Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} }, + Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} }, + Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} }, + else => unreachable, + }, .rhs = undefined, } ); - stack.append(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + + stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; continue; - } else { - self.putBackToken(token); + }, + else => { + if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) { + _ = tok_it.prev(); + stack.push(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable; + } continue; } - }, + } + }, + State.RangeExpressionBegin => |opt_ctx| { + stack.push(State { .RangeExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .Expression = opt_ctx }); + continue; + }, + State.RangeExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - State.BinaryOrExpressionBegin => |opt_ctx| { - stack.append(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BinaryXorExpressionBegin = opt_ctx }); + if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = ellipsis3, + .op = ast.Node.InfixOp.Op.Range, + .rhs = undefined, + } + ); + stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; continue; - }, - - State.BinaryOrExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + } + }, + State.AssignmentExpressionBegin => |opt_ctx| { + stack.push(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .Expression = opt_ctx }); + continue; + }, - if (self.eatToken(Token.Id.Pipe)) |pipe| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = pipe, - .op = ast.Node.InfixOp.Op.BitOr, - .rhs = undefined, - } - ); - stack.append(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, + State.AssignmentExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - State.BinaryXorExpressionBegin => |opt_ctx| { - stack.append(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BinaryAndExpressionBegin = opt_ctx }); + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToAssignment(token_ptr.id)) |ass_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = ass_id, + .rhs = undefined, + } + ); + stack.push(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }); continue; - }, - - State.BinaryXorExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - if (self.eatToken(Token.Id.Caret)) |caret| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = caret, - .op = ast.Node.InfixOp.Op.BitXor, - .rhs = undefined, - } - ); - stack.append(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, - - State.BinaryAndExpressionBegin => |opt_ctx| { - stack.append(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BitShiftExpressionBegin = opt_ctx }); + } else { + _ = tok_it.prev(); continue; - }, + } + }, - State.BinaryAndExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + State.UnwrapExpressionBegin => |opt_ctx| { + stack.push(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BoolOrExpressionBegin = opt_ctx }); + continue; + }, - if (self.eatToken(Token.Id.Ampersand)) |ampersand| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = ampersand, - .op = ast.Node.InfixOp.Op.BitAnd, - .rhs = undefined, - } - ); - stack.append(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, + State.UnwrapExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - State.BitShiftExpressionBegin => |opt_ctx| { - stack.append(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .AdditionExpressionBegin = opt_ctx }); - continue; - }, + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = unwrap_id, + .rhs = undefined, + } + ); - State.BitShiftExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + stack.push(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }); - const token = self.getNextToken(); - if (tokenIdToBitShift(token.id)) |bitshift_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token, - .op = bitshift_id, - .rhs = undefined, - } - ); - stack.append(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; + if (node.op == ast.Node.InfixOp.Op.Catch) { + try stack.push(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } }); } - }, - - State.AdditionExpressionBegin => |opt_ctx| { - stack.append(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .MultiplyExpressionBegin = opt_ctx }); continue; - }, + } else { + _ = tok_it.prev(); + continue; + } + }, - State.AdditionExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + State.BoolOrExpressionBegin => |opt_ctx| { + stack.push(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BoolAndExpressionBegin = opt_ctx }); + continue; + }, - const token = self.getNextToken(); - if (tokenIdToAddition(token.id)) |add_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token, - .op = add_id, - .rhs = undefined, - } - ); - stack.append(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; - } - }, + State.BoolOrExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - State.MultiplyExpressionBegin => |opt_ctx| { - stack.append(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .CurlySuffixExpressionBegin = opt_ctx }); + if (eatToken(&tok_it, Token.Id.Keyword_or)) |or_token| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = or_token, + .op = ast.Node.InfixOp.Op.BoolOr, + .rhs = undefined, + } + ); + stack.push(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; - }, + } + }, - State.MultiplyExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + State.BoolAndExpressionBegin => |opt_ctx| { + stack.push(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .ComparisonExpressionBegin = opt_ctx }); + continue; + }, - const token = self.getNextToken(); - if (tokenIdToMultiply(token.id)) |mult_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token, - .op = mult_id, - .rhs = undefined, - } - ); - stack.append(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; - } - }, + State.BoolAndExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - State.CurlySuffixExpressionBegin => |opt_ctx| { - stack.append(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.LBrace }); - try stack.append(State { .TypeExprBegin = opt_ctx }); + if (eatToken(&tok_it, Token.Id.Keyword_and)) |and_token| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = and_token, + .op = ast.Node.InfixOp.Op.BoolAnd, + .rhs = undefined, + } + ); + stack.push(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; - }, + } + }, - State.CurlySuffixExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + State.ComparisonExpressionBegin => |opt_ctx| { + stack.push(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BinaryOrExpressionBegin = opt_ctx }); + continue; + }, - if (self.isPeekToken(Token.Id.Period)) { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { - .base = undefined, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .StructInitializer = ArrayList(&ast.Node).init(arena), - }, - .rtoken = undefined, - } - ); - stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.LBrace }); - try stack.append(State { - .FieldInitListItemOrEnd = ListSave(&ast.Node) { - .list = &node.op.StructInitializer, - .ptr = &node.rtoken, - } - }); - continue; - } + State.ComparisonExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToComparison(token_ptr.id)) |comp_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .ArrayInitializer = ArrayList(&ast.Node).init(arena), - }, - .rtoken = undefined, + .op_token = token_index, + .op = comp_id, + .rhs = undefined, } ); - stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.LBrace }); - try stack.append(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.op.ArrayInitializer, - .end = Token.Id.RBrace, - .ptr = &node.rtoken, - } - }); + stack.push(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; - }, - - State.TypeExprBegin => |opt_ctx| { - stack.append(State { .TypeExprEnd = opt_ctx }) catch unreachable; - try stack.append(State { .PrefixOpExpression = opt_ctx }); + } else { + _ = tok_it.prev(); continue; - }, - - State.TypeExprEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + } + }, - if (self.eatToken(Token.Id.Bang)) |bang| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = bang, - .op = ast.Node.InfixOp.Op.ErrorUnion, - .rhs = undefined, - } - ); - stack.append(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, + State.BinaryOrExpressionBegin => |opt_ctx| { + stack.push(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BinaryXorExpressionBegin = opt_ctx }); + continue; + }, - State.PrefixOpExpression => |opt_ctx| { - const token = self.getNextToken(); - if (tokenIdToPrefixOp(token.id)) |prefix_id| { - var node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token, - .op = prefix_id, - .rhs = undefined, - } - ); + State.BinaryOrExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - // Treat '**' token as two derefs - if (token.id == Token.Id.AsteriskAsterisk) { - const child = try self.createNode(arena, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token, - .op = prefix_id, - .rhs = undefined, - } - ); - node.rhs = &child.base; - node = child; + if (eatToken(&tok_it, Token.Id.Pipe)) |pipe| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = pipe, + .op = ast.Node.InfixOp.Op.BitOr, + .rhs = undefined, } + ); + stack.push(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, - stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - if (node.op == ast.Node.PrefixOp.Op.AddrOf) { - try stack.append(State { .AddrOfModifiers = &node.op.AddrOf }); - } - continue; - } else { - self.putBackToken(token); - stack.append(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable; - continue; - } - }, + State.BinaryXorExpressionBegin => |opt_ctx| { + stack.push(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BinaryAndExpressionBegin = opt_ctx }); + continue; + }, - State.SuffixOpExpressionBegin => |opt_ctx| { - if (self.eatToken(Token.Id.Keyword_async)) |async_token| { - const async_node = try self.createNode(arena, ast.Node.AsyncAttribute, - ast.Node.AsyncAttribute { - .base = undefined, - .async_token = async_token, - .allocator_type = null, - .rangle_bracket = null, - } - ); - stack.append(State { - .AsyncEnd = AsyncEndCtx { - .ctx = opt_ctx, - .attribute = async_node, - } - }) catch unreachable; - try stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }); - try stack.append(State { .PrimaryExpression = opt_ctx.toRequired() }); - try stack.append(State { .AsyncAllocator = async_node }); - continue; - } + State.BinaryXorExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - stack.append(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .PrimaryExpression = opt_ctx }); + if (eatToken(&tok_it, Token.Id.Caret)) |caret| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = caret, + .op = ast.Node.InfixOp.Op.BitXor, + .rhs = undefined, + } + ); + stack.push(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; - }, + } + }, - State.SuffixOpExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token = self.getNextToken(); - switch (token.id) { - Token.Id.LParen => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { - .base = undefined, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .Call = ast.Node.SuffixOp.CallInfo { - .params = ArrayList(&ast.Node).init(arena), - .async_attr = null, - } - }, - .rtoken = undefined, - } - ); - stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.op.Call.params, - .end = Token.Id.RParen, - .ptr = &node.rtoken, - } - }); - continue; - }, - Token.Id.LBracket => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { - .base = undefined, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .ArrayAccess = undefined, - }, - .rtoken = undefined - } - ); - stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .SliceOrArrayAccess = node }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }}); - continue; - }, - Token.Id.Period => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token, - .op = ast.Node.InfixOp.Op.Period, - .rhs = undefined, - } - ); - stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.rhs } }); - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, + State.BinaryAndExpressionBegin => |opt_ctx| { + stack.push(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BitShiftExpressionBegin = opt_ctx }); + continue; + }, - State.PrimaryExpression => |opt_ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.IntegerLiteral => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token); - continue; - }, - Token.Id.FloatLiteral => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token); - continue; - }, - Token.Id.CharLiteral => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token); - continue; - }, - Token.Id.Keyword_undefined => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token); - continue; - }, - Token.Id.Keyword_true, Token.Id.Keyword_false => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token); - continue; - }, - Token.Id.Keyword_null => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token); - continue; - }, - Token.Id.Keyword_this => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token); - continue; - }, - Token.Id.Keyword_var => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token); - continue; - }, - Token.Id.Keyword_unreachable => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token); - continue; - }, - Token.Id.Keyword_promise => { - const node = try arena.construct(ast.Node.PromiseType { - .base = ast.Node { - .id = ast.Node.Id.PromiseType, - .same_line_comment = null, - }, - .promise_token = token, - .result = null, - }); - opt_ctx.store(&node.base); - const next_token = self.getNextToken(); - if (next_token.id != Token.Id.Arrow) { - self.putBackToken(next_token); - continue; - } - node.result = ast.Node.PromiseType.Result { - .arrow_token = next_token, - .return_type = undefined, - }; - const return_type_ptr = &((??node.result).return_type); - try stack.append(State { .Expression = OptionalCtx { .Required = return_type_ptr, } }); - continue; - }, - Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { - opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable); - continue; - }, - Token.Id.LParen => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression, - ast.Node.GroupedExpression { - .base = undefined, - .lparen = token, - .expr = undefined, - .rparen = undefined, - } - ); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RParen, - .ptr = &node.rparen, - } - }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); - continue; - }, - Token.Id.Builtin => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall, - ast.Node.BuiltinCall { - .base = undefined, - .builtin_token = token, - .params = ArrayList(&ast.Node).init(arena), - .rparen_token = undefined, - } - ); - stack.append(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.params, - .end = Token.Id.RParen, - .ptr = &node.rparen_token, - } - }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.LParen, }); - continue; - }, - Token.Id.LBracket => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token, - .op = undefined, - .rhs = undefined, - } - ); - stack.append(State { .SliceOrArrayType = node }) catch unreachable; - continue; - }, - Token.Id.Keyword_error => { - stack.append(State { - .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx { - .error_token = token, - .opt_ctx = opt_ctx - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_packed => { - stack.append(State { - .ContainerKind = ContainerKindCtx { - .opt_ctx = opt_ctx, - .ltoken = token, - .layout = ast.Node.ContainerDecl.Layout.Packed, - }, - }) catch unreachable; - continue; - }, - Token.Id.Keyword_extern => { - stack.append(State { - .ExternType = ExternTypeCtx { - .opt_ctx = opt_ctx, - .extern_token = token, - .comments = null, - }, - }) catch unreachable; - continue; - }, - Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => { - self.putBackToken(token); - stack.append(State { - .ContainerKind = ContainerKindCtx { - .opt_ctx = opt_ctx, - .ltoken = token, - .layout = ast.Node.ContainerDecl.Layout.Auto, - }, - }) catch unreachable; - continue; - }, - Token.Id.Identifier => { - stack.append(State { - .MaybeLabeledExpression = MaybeLabeledExpressionCtx { - .label = token, - .opt_ctx = opt_ctx - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_fn => { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - .same_line_comment = null, - }, - .doc_comments = null, - .visib_token = null, - .name_token = null, - .fn_token = token, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = null, - .cc_token = null, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - }); - opt_ctx.store(&fn_proto.base); - stack.append(State { .FnProto = fn_proto }) catch unreachable; - continue; - }, - Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - .same_line_comment = null, - }, - .doc_comments = null, - .visib_token = null, - .name_token = null, - .fn_token = undefined, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = null, - .cc_token = token, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - }); - opt_ctx.store(&fn_proto.base); - stack.append(State { .FnProto = fn_proto }) catch unreachable; - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token - } - }); - continue; - }, - Token.Id.Keyword_asm => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Asm, - ast.Node.Asm { - .base = undefined, - .asm_token = token, - .volatile_token = null, - .template = undefined, - //.tokens = ArrayList(ast.Node.Asm.AsmToken).init(arena), - .outputs = ArrayList(&ast.Node.AsmOutput).init(arena), - .inputs = ArrayList(&ast.Node.AsmInput).init(arena), - .cloppers = ArrayList(&ast.Node).init(arena), - .rparen = undefined, - } - ); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RParen, - .ptr = &node.rparen, - } - }) catch unreachable; - try stack.append(State { .AsmClopperItems = &node.cloppers }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .AsmInputItems = &node.inputs }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .AsmOutputItems = &node.outputs }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.template } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - try stack.append(State { - .OptionalTokenSave = OptionalTokenSave { - .id = Token.Id.Keyword_volatile, - .ptr = &node.volatile_token, - } - }); - }, - Token.Id.Keyword_inline => { - stack.append(State { - .Inline = InlineCtx { - .label = null, - .inline_token = token, - .opt_ctx = opt_ctx, - } - }) catch unreachable; - continue; - }, - else => { - if (!try self.parseBlockExpr(&stack, arena, opt_ctx, token)) { - self.putBackToken(token); - if (opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)); - } - } - continue; - } - } - }, + State.BinaryAndExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + if (eatToken(&tok_it, Token.Id.Ampersand)) |ampersand| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = ampersand, + .op = ast.Node.InfixOp.Op.BitAnd, + .rhs = undefined, + } + ); + stack.push(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, - State.ErrorTypeOrSetDecl => |ctx| { - if (self.eatToken(Token.Id.LBrace) == null) { - _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token); - continue; - } + State.BitShiftExpressionBegin => |opt_ctx| { + stack.push(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .AdditionExpressionBegin = opt_ctx }); + continue; + }, - const node = try arena.construct(ast.Node.ErrorSetDecl { - .base = ast.Node { - .id = ast.Node.Id.ErrorSetDecl, - .same_line_comment = null, - }, - .error_token = ctx.error_token, - .decls = ArrayList(&ast.Node).init(arena), - .rbrace_token = undefined, - }); - ctx.opt_ctx.store(&node.base); + State.BitShiftExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - stack.append(State { - .ErrorTagListItemOrEnd = ListSave(&ast.Node) { - .list = &node.decls, - .ptr = &node.rbrace_token, + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = bitshift_id, + .rhs = undefined, } - }) catch unreachable; + ); + stack.push(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; - }, - State.StringLiteral => |opt_ctx| { - const token = self.getNextToken(); - opt_ctx.store( - (try self.parseStringLiteral(arena, token)) ?? { - self.putBackToken(token); - if (opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)); - } + } else { + _ = tok_it.prev(); + continue; + } + }, - continue; + State.AdditionExpressionBegin => |opt_ctx| { + stack.push(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .MultiplyExpressionBegin = opt_ctx }); + continue; + }, + + State.AdditionExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToAddition(token_ptr.id)) |add_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = add_id, + .rhs = undefined, } ); - }, + stack.push(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + _ = tok_it.prev(); + continue; + } + }, - State.Identifier => |opt_ctx| { - if (self.eatToken(Token.Id.Identifier)) |ident_token| { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token); - continue; - } + State.MultiplyExpressionBegin => |opt_ctx| { + stack.push(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .CurlySuffixExpressionBegin = opt_ctx }); + continue; + }, - if (opt_ctx != OptionalCtx.Optional) { - const token = self.getNextToken(); - return self.parseError(token, "expected identifier, found {}", @tagName(token.id)); - } - }, + State.MultiplyExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - State.ErrorTag => |node_ptr| { - const comments = try self.eatDocComments(arena); - const ident_token = self.getNextToken(); - if (ident_token.id != Token.Id.Identifier) { - return self.parseError(ident_token, "expected {}, found {}", - @tagName(Token.Id.Identifier), @tagName(ident_token.id)); - } + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToMultiply(token_ptr.id)) |mult_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = mult_id, + .rhs = undefined, + } + ); + stack.push(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + _ = tok_it.prev(); + continue; + } + }, - const node = try arena.construct(ast.Node.ErrorTag { - .base = ast.Node { - .id = ast.Node.Id.ErrorTag, - .same_line_comment = null, + State.CurlySuffixExpressionBegin => |opt_ctx| { + stack.push(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.LBrace }); + try stack.push(State { .TypeExprBegin = opt_ctx }); + continue; + }, + + State.CurlySuffixExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if ((??tok_it.peek()).id == Token.Id.Period) { + const node = try arena.construct(ast.Node.SuffixOp { + .base = ast.Node { .id = ast.Node.Id.SuffixOp }, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena), }, - .doc_comments = comments, - .name_token = ident_token, + .rtoken = undefined, }); - *node_ptr = &node.base; - continue; - }, + opt_ctx.store(&node.base); - State.ExpectToken => |token_id| { - _ = try self.expectToken(token_id); - continue; - }, - State.ExpectTokenSave => |expect_token_save| { - *expect_token_save.ptr = try self.expectToken(expect_token_save.id); + stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.LBrace }); + try stack.push(State { + .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)) { + .list = &node.op.StructInitializer, + .ptr = &node.rtoken, + } + }); continue; - }, - State.IfToken => |token_id| { - if (self.eatToken(token_id)) |_| { - continue; - } + } - _ = stack.pop(); - continue; - }, - State.IfTokenSave => |if_token_save| { - if (self.eatToken(if_token_save.id)) |token| { - *if_token_save.ptr = token; - continue; + const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena), + }, + .rtoken = undefined, } - - _ = stack.pop(); - continue; - }, - State.OptionalTokenSave => |optional_token_save| { - if (self.eatToken(optional_token_save.id)) |token| { - *optional_token_save.ptr = token; - continue; + ); + stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.LBrace }); + try stack.push(State { + .ExprListItemOrEnd = ExprListCtx { + .list = &node.op.ArrayInitializer, + .end = Token.Id.RBrace, + .ptr = &node.rtoken, } + }); + continue; + }, - continue; - }, - } - } - } - - fn eatDocComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.DocComment { - var result: ?&ast.Node.DocComment = null; - while (true) { - if (self.eatToken(Token.Id.DocComment)) |line_comment| { - const node = blk: { - if (result) |comment_node| { - break :blk comment_node; - } else { - const comment_node = try arena.construct(ast.Node.DocComment { - .base = ast.Node { - .id = ast.Node.Id.DocComment, - .same_line_comment = null, - }, - .lines = ArrayList(Token).init(arena), - }); - result = comment_node; - break :blk comment_node; - } - }; - try node.lines.append(line_comment); + State.TypeExprBegin => |opt_ctx| { + stack.push(State { .TypeExprEnd = opt_ctx }) catch unreachable; + try stack.push(State { .PrefixOpExpression = opt_ctx }); continue; - } - break; - } - return result; - } + }, + + State.TypeExprEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - fn eatLineComment(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment { - const token = self.eatToken(Token.Id.LineComment) ?? return null; - return try arena.construct(ast.Node.LineComment { - .base = ast.Node { - .id = ast.Node.Id.LineComment, - .same_line_comment = null, + if (eatToken(&tok_it, Token.Id.Bang)) |bang| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = bang, + .op = ast.Node.InfixOp.Op.ErrorUnion, + .rhs = undefined, + } + ); + stack.push(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } }); + continue; + } }, - .token = token, - }); - } - fn requireSemiColon(node: &const ast.Node) bool { - var n = node; - while (true) { - switch (n.id) { - ast.Node.Id.Root, - ast.Node.Id.StructField, - ast.Node.Id.UnionTag, - ast.Node.Id.EnumTag, - ast.Node.Id.ParamDecl, - ast.Node.Id.Block, - ast.Node.Id.Payload, - ast.Node.Id.PointerPayload, - ast.Node.Id.PointerIndexPayload, - ast.Node.Id.Switch, - ast.Node.Id.SwitchCase, - ast.Node.Id.SwitchElse, - ast.Node.Id.FieldInitializer, - ast.Node.Id.DocComment, - ast.Node.Id.LineComment, - ast.Node.Id.TestDecl => return false, - ast.Node.Id.While => { - const while_node = @fieldParentPtr(ast.Node.While, "base", n); - if (while_node.@"else") |@"else"| { - n = @"else".base; - continue; - } + State.PrefixOpExpression => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| { + var node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { + .base = undefined, + .op_token = token_index, + .op = prefix_id, + .rhs = undefined, + } + ); - return while_node.body.id != ast.Node.Id.Block; - }, - ast.Node.Id.For => { - const for_node = @fieldParentPtr(ast.Node.For, "base", n); - if (for_node.@"else") |@"else"| { - n = @"else".base; - continue; + // Treat '**' token as two derefs + if (token_ptr.id == Token.Id.AsteriskAsterisk) { + const child = try createNode(arena, ast.Node.PrefixOp, + ast.Node.PrefixOp { + .base = undefined, + .op_token = token_index, + .op = prefix_id, + .rhs = undefined, + } + ); + node.rhs = &child.base; + node = child; } - return for_node.body.id != ast.Node.Id.Block; - }, - ast.Node.Id.If => { - const if_node = @fieldParentPtr(ast.Node.If, "base", n); - if (if_node.@"else") |@"else"| { - n = @"else".base; - continue; + stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + if (node.op == ast.Node.PrefixOp.Op.AddrOf) { + try stack.push(State { .AddrOfModifiers = &node.op.AddrOf }); } + continue; + } else { + _ = tok_it.prev(); + stack.push(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable; + continue; + } + }, - return if_node.body.id != ast.Node.Id.Block; - }, - ast.Node.Id.Else => { - const else_node = @fieldParentPtr(ast.Node.Else, "base", n); - n = else_node.body; + State.SuffixOpExpressionBegin => |opt_ctx| { + if (eatToken(&tok_it, Token.Id.Keyword_async)) |async_token| { + const async_node = try createNode(arena, ast.Node.AsyncAttribute, + ast.Node.AsyncAttribute { + .base = undefined, + .async_token = async_token, + .allocator_type = null, + .rangle_bracket = null, + } + ); + stack.push(State { + .AsyncEnd = AsyncEndCtx { + .ctx = opt_ctx, + .attribute = async_node, + } + }) catch unreachable; + try stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }); + try stack.push(State { .PrimaryExpression = opt_ctx.toRequired() }); + try stack.push(State { .AsyncAllocator = async_node }); continue; - }, - ast.Node.Id.Defer => { - const defer_node = @fieldParentPtr(ast.Node.Defer, "base", n); - return defer_node.expr.id != ast.Node.Id.Block; - }, - ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", n); - return comptime_node.expr.id != ast.Node.Id.Block; - }, - ast.Node.Id.Suspend => { - const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", n); - if (suspend_node.body) |body| { - return body.id != ast.Node.Id.Block; - } + } - return true; - }, - else => return true, - } - } - } + stack.push(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .PrimaryExpression = opt_ctx }); + continue; + }, + + State.SuffixOpExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - fn lookForSameLineComment(self: &Parser, arena: &mem.Allocator, node: &ast.Node) !void { - const node_last_token = node.lastToken(); + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.LParen => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .Call = ast.Node.SuffixOp.Op.Call { + .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena), + .async_attr = null, + } + }, + .rtoken = undefined, + } + ); + stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { + .ExprListItemOrEnd = ExprListCtx { + .list = &node.op.Call.params, + .end = Token.Id.RParen, + .ptr = &node.rtoken, + } + }); + continue; + }, + Token.Id.LBracket => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .ArrayAccess = undefined, + }, + .rtoken = undefined + } + ); + stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .SliceOrArrayAccess = node }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }}); + continue; + }, + Token.Id.Period => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = ast.Node.InfixOp.Op.Period, + .rhs = undefined, + } + ); + stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.rhs } }); + continue; + }, + else => { + _ = tok_it.prev(); + continue; + }, + } + }, + + State.PrimaryExpression => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.IntegerLiteral => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token_index); + continue; + }, + Token.Id.FloatLiteral => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token_index); + continue; + }, + Token.Id.CharLiteral => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token_index); + continue; + }, + Token.Id.Keyword_undefined => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token_index); + continue; + }, + Token.Id.Keyword_true, Token.Id.Keyword_false => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token_index); + continue; + }, + Token.Id.Keyword_null => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token_index); + continue; + }, + Token.Id.Keyword_this => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token_index); + continue; + }, + Token.Id.Keyword_var => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token_index); + continue; + }, + Token.Id.Keyword_unreachable => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token_index); + continue; + }, + Token.Id.Keyword_promise => { + const node = try arena.construct(ast.Node.PromiseType { + .base = ast.Node { + .id = ast.Node.Id.PromiseType, + }, + .promise_token = token_index, + .result = null, + }); + opt_ctx.store(&node.base); + const next_token_index = tok_it.index; + const next_token_ptr = ??tok_it.next(); + if (next_token_ptr.id != Token.Id.Arrow) { + _ = tok_it.prev(); + continue; + } + node.result = ast.Node.PromiseType.Result { + .arrow_token = next_token_index, + .return_type = undefined, + }; + const return_type_ptr = &((??node.result).return_type); + try stack.push(State { .Expression = OptionalCtx { .Required = return_type_ptr, } }); + continue; + }, + Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { + opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? unreachable); + continue; + }, + Token.Id.LParen => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression, + ast.Node.GroupedExpression { + .base = undefined, + .lparen = token_index, + .expr = undefined, + .rparen = undefined, + } + ); + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.RParen, + .ptr = &node.rparen, + } + }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + continue; + }, + Token.Id.Builtin => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall, + ast.Node.BuiltinCall { + .base = undefined, + .builtin_token = token_index, + .params = ast.Node.BuiltinCall.ParamList.init(arena), + .rparen_token = undefined, + } + ); + stack.push(State { + .ExprListItemOrEnd = ExprListCtx { + .list = &node.params, + .end = Token.Id.RParen, + .ptr = &node.rparen_token, + } + }) catch unreachable; + try stack.push(State { .ExpectToken = Token.Id.LParen, }); + continue; + }, + Token.Id.LBracket => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { + .base = undefined, + .op_token = token_index, + .op = undefined, + .rhs = undefined, + } + ); + stack.push(State { .SliceOrArrayType = node }) catch unreachable; + continue; + }, + Token.Id.Keyword_error => { + stack.push(State { + .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx { + .error_token = token_index, + .opt_ctx = opt_ctx + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_packed => { + stack.push(State { + .ContainerKind = ContainerKindCtx { + .opt_ctx = opt_ctx, + .ltoken = token_index, + .layout = ast.Node.ContainerDecl.Layout.Packed, + }, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_extern => { + stack.push(State { + .ExternType = ExternTypeCtx { + .opt_ctx = opt_ctx, + .extern_token = token_index, + .comments = null, + }, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => { + _ = tok_it.prev(); + stack.push(State { + .ContainerKind = ContainerKindCtx { + .opt_ctx = opt_ctx, + .ltoken = token_index, + .layout = ast.Node.ContainerDecl.Layout.Auto, + }, + }) catch unreachable; + continue; + }, + Token.Id.Identifier => { + stack.push(State { + .MaybeLabeledExpression = MaybeLabeledExpressionCtx { + .label = token_index, + .opt_ctx = opt_ctx + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_fn => { + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, + }, + .doc_comments = null, + .visib_token = null, + .name_token = null, + .fn_token = token_index, + .params = ast.Node.FnProto.ParamList.init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = null, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + }); + opt_ctx.store(&fn_proto.base); + stack.push(State { .FnProto = fn_proto }) catch unreachable; + continue; + }, + Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, + }, + .doc_comments = null, + .visib_token = null, + .name_token = null, + .fn_token = undefined, + .params = ast.Node.FnProto.ParamList.init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = null, + .cc_token = token_index, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + }); + opt_ctx.store(&fn_proto.base); + stack.push(State { .FnProto = fn_proto }) catch unreachable; + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token + } + }); + continue; + }, + Token.Id.Keyword_asm => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.Asm, + ast.Node.Asm { + .base = undefined, + .asm_token = token_index, + .volatile_token = null, + .template = undefined, + .outputs = ast.Node.Asm.OutputList.init(arena), + .inputs = ast.Node.Asm.InputList.init(arena), + .clobbers = ast.Node.Asm.ClobberList.init(arena), + .rparen = undefined, + } + ); + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.RParen, + .ptr = &node.rparen, + } + }) catch unreachable; + try stack.push(State { .AsmClobberItems = &node.clobbers }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { .AsmInputItems = &node.inputs }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { .AsmOutputItems = &node.outputs }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.template } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.push(State { + .OptionalTokenSave = OptionalTokenSave { + .id = Token.Id.Keyword_volatile, + .ptr = &node.volatile_token, + } + }); + }, + Token.Id.Keyword_inline => { + stack.push(State { + .Inline = InlineCtx { + .label = null, + .inline_token = token_index, + .opt_ctx = opt_ctx, + } + }) catch unreachable; + continue; + }, + else => { + if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) { + _ = tok_it.prev(); + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index }, + }; + return tree; + } + } + continue; + } + } + }, - const line_comment_token = self.getNextToken(); - if (line_comment_token.id != Token.Id.DocComment and line_comment_token.id != Token.Id.LineComment) { - self.putBackToken(line_comment_token); - return; - } - const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token); - const different_line = offset_loc.line != 0; - if (different_line) { - self.putBackToken(line_comment_token); - return; - } + State.ErrorTypeOrSetDecl => |ctx| { + if (eatToken(&tok_it, Token.Id.LBrace) == null) { + _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token); + continue; + } - node.same_line_comment = try arena.construct(line_comment_token); - } + const node = try arena.construct(ast.Node.ErrorSetDecl { + .base = ast.Node { + .id = ast.Node.Id.ErrorSetDecl, + }, + .error_token = ctx.error_token, + .decls = ast.Node.ErrorSetDecl.DeclList.init(arena), + .rbrace_token = undefined, + }); + ctx.opt_ctx.store(&node.base); - fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node { - switch (token.id) { - Token.Id.StringLiteral => { - return &(try self.createLiteral(arena, ast.Node.StringLiteral, token)).base; + stack.push(State { + .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)) { + .list = &node.decls, + .ptr = &node.rbrace_token, + } + }) catch unreachable; + continue; }, - Token.Id.MultilineStringLiteralLine => { - const node = try self.createNode(arena, ast.Node.MultilineStringLiteral, - ast.Node.MultilineStringLiteral { - .base = undefined, - .tokens = ArrayList(Token).init(arena), + State.StringLiteral => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + opt_ctx.store( + (try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? { + _ = tok_it.prev(); + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index }, + }; + return tree; + } + + continue; } ); - try node.tokens.append(token); - while (true) { - const multiline_str = self.getNextToken(); - if (multiline_str.id != Token.Id.MultilineStringLiteralLine) { - self.putBackToken(multiline_str); - break; - } + }, - try node.tokens.append(multiline_str); + State.Identifier => |opt_ctx| { + if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token); + continue; } - return &node.base; + if (opt_ctx != OptionalCtx.Optional) { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = Token.Id.Identifier, + }, + }; + return tree; + } }, - // TODO: We shouldn't need a cast, but: - // zig: /home/jc/Documents/zig/src/ir.cpp:7962: TypeTableEntry* ir_resolve_peer_types(IrAnalyze*, AstNode*, IrInstruction**, size_t): Assertion `err_set_type != nullptr' failed. - else => return (?&ast.Node)(null), - } - } - fn parseBlockExpr(self: &Parser, stack: &ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx, token: &const Token) !bool { - switch (token.id) { - Token.Id.Keyword_suspend => { - const node = try self.createToCtxNode(arena, ctx, ast.Node.Suspend, - ast.Node.Suspend { - .base = undefined, - .label = null, - .suspend_token = *token, - .payload = null, - .body = null, - } - ); - - stack.append(State { .SuspendBody = node }) catch unreachable; - try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); - return true; - }, - Token.Id.Keyword_if => { - const node = try self.createToCtxNode(arena, ctx, ast.Node.If, - ast.Node.If { - .base = undefined, - .if_token = *token, - .condition = undefined, - .payload = null, - .body = undefined, - .@"else" = null, - } - ); + State.ErrorTag => |node_ptr| { + const comments = try eatDocComments(arena, &tok_it); + const ident_token_index = tok_it.index; + const ident_token_ptr = ??tok_it.next(); + if (ident_token_ptr.id != Token.Id.Identifier) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = ident_token_index, + .expected_id = Token.Id.Identifier, + }, + }; + return tree; + } - stack.append(State { .Else = &node.@"else" }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.append(State { .LookForSameLineComment = &node.condition }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - return true; - }, - Token.Id.Keyword_while => { - stack.append(State { - .While = LoopCtx { - .label = null, - .inline_token = null, - .loop_token = *token, - .opt_ctx = *ctx, - } - }) catch unreachable; - return true; - }, - Token.Id.Keyword_for => { - stack.append(State { - .For = LoopCtx { - .label = null, - .inline_token = null, - .loop_token = *token, - .opt_ctx = *ctx, - } - }) catch unreachable; - return true; - }, - Token.Id.Keyword_switch => { - const node = try arena.construct(ast.Node.Switch { + const node = try arena.construct(ast.Node.ErrorTag { .base = ast.Node { - .id = ast.Node.Id.Switch, - .same_line_comment = null, + .id = ast.Node.Id.ErrorTag, }, - .switch_token = *token, - .expr = undefined, - .cases = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, + .doc_comments = comments, + .name_token = ident_token_index, }); - ctx.store(&node.base); + *node_ptr = &node.base; + continue; + }, - stack.append(State { - .SwitchCaseOrEnd = ListSave(&ast.Node) { - .list = &node.cases, - .ptr = &node.rbrace, - }, - }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.LBrace }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - return true; + State.ExpectToken => |token_id| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != token_id) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = token_id, + }, + }; + return tree; + } + continue; }, - Token.Id.Keyword_comptime => { - const node = try self.createToCtxNode(arena, ctx, ast.Node.Comptime, - ast.Node.Comptime { - .base = undefined, - .comptime_token = *token, - .expr = undefined, - .doc_comments = null, - } - ); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); - return true; + State.ExpectTokenSave => |expect_token_save| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != expect_token_save.id) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = expect_token_save.id, + }, + }; + return tree; + } + *expect_token_save.ptr = token_index; + continue; }, - Token.Id.LBrace => { - const block = try self.createToCtxNode(arena, ctx, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = null, - .lbrace = *token, - .statements = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, - } - ); - stack.append(State { .Block = block }) catch unreachable; - return true; + State.IfToken => |token_id| { + if (eatToken(&tok_it, token_id)) |_| { + continue; + } + + _ = stack.pop(); + continue; }, - else => { - return false; - } - } - } + State.IfTokenSave => |if_token_save| { + if (eatToken(&tok_it, if_token_save.id)) |token_index| { + *if_token_save.ptr = token_index; + continue; + } - fn expectCommaOrEnd(self: &Parser, end: @TagType(Token.Id)) !?Token { - var token = self.getNextToken(); - switch (token.id) { - Token.Id.Comma => return null, - else => { - if (end == token.id) { - return token; + _ = stack.pop(); + continue; + }, + State.OptionalTokenSave => |optional_token_save| { + if (eatToken(&tok_it, optional_token_save.id)) |token_index| { + *optional_token_save.ptr = token_index; + continue; } - return self.parseError(token, "expected ',' or {}, found {}", @tagName(end), @tagName(token.id)); + continue; }, } } +} - fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op { - // TODO: We have to cast all cases because of this: - // error: expected type '?InfixOp', found '?@TagType(InfixOp)' - return switch (*id) { - Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = void{} }, - Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = void{} }, - Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = void{} }, - Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = void{} }, - Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = void{} }, - Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = void{} }, - Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = void{} }, - Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = void{} }, - Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = void{} }, - Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = void{} }, - Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = void{} }, - Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = void{} }, - Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = void{} }, - Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = void{} }, - else => null, - }; - } +const AnnotatedToken = struct { + ptr: &Token, + index: TokenIndex, +}; - fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null }, - Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} }, - else => null, - }; - } +const TopLevelDeclCtx = struct { + decls: &ast.Node.Root.DeclList, + visib_token: ?TokenIndex, + extern_export_inline_token: ?AnnotatedToken, + lib_name: ?&ast.Node, + comments: ?&ast.Node.DocComment, +}; - fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} }, - Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} }, - Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} }, - Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} }, - Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} }, - Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} }, - else => null, - }; - } +const VarDeclCtx = struct { + mut_token: TokenIndex, + visib_token: ?TokenIndex, + comptime_token: ?TokenIndex, + extern_export_token: ?TokenIndex, + lib_name: ?&ast.Node, + list: &ast.Node.Root.DeclList, + comments: ?&ast.Node.DocComment, +}; - fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} }, - Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} }, - else => null, - }; - } +const TopLevelExternOrFieldCtx = struct { + visib_token: TokenIndex, + container_decl: &ast.Node.ContainerDecl, + comments: ?&ast.Node.DocComment, +}; - fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} }, - Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} }, - Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} }, - Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} }, - Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} }, - else => null, - }; - } +const ExternTypeCtx = struct { + opt_ctx: OptionalCtx, + extern_token: TokenIndex, + comments: ?&ast.Node.DocComment, +}; - fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} }, - Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} }, - Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} }, - Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} }, - Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} }, - Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} }, - else => null, - }; - } +const ContainerKindCtx = struct { + opt_ctx: OptionalCtx, + ltoken: TokenIndex, + layout: ast.Node.ContainerDecl.Layout, +}; - fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { - return switch (id) { - Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} }, - Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} }, - Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} }, - Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} }, - Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} }, - Token.Id.Ampersand => ast.Node.PrefixOp.Op { - .AddrOf = ast.Node.PrefixOp.AddrOfInfo { - .align_expr = null, - .bit_offset_start_token = null, - .bit_offset_end_token = null, - .const_token = null, - .volatile_token = null, - }, - }, - Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} }, - Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} }, - Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} }, - Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } }, - else => null, - }; - } +const ExpectTokenSave = struct { + id: @TagType(Token.Id), + ptr: &TokenIndex, +}; - fn createNode(self: &Parser, arena: &mem.Allocator, comptime T: type, init_to: &const T) !&T { - const node = try arena.create(T); - *node = *init_to; - node.base = blk: { - const id = ast.Node.typeToId(T); - break :blk ast.Node { - .id = id, - .same_line_comment = null, - }; - }; +const OptionalTokenSave = struct { + id: @TagType(Token.Id), + ptr: &?TokenIndex, +}; - return node; - } +const ExprListCtx = struct { + list: &ast.Node.SuffixOp.Op.InitList, + end: Token.Id, + ptr: &TokenIndex, +}; - fn createAttachNode(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node), comptime T: type, init_to: &const T) !&T { - const node = try self.createNode(arena, T, init_to); - try list.append(&node.base); +fn ListSave(comptime List: type) type { + return struct { + list: &List, + ptr: &TokenIndex, + }; +} - return node; - } +const MaybeLabeledExpressionCtx = struct { + label: TokenIndex, + opt_ctx: OptionalCtx, +}; - fn createToCtxNode(self: &Parser, arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T { - const node = try self.createNode(arena, T, init_to); - opt_ctx.store(&node.base); +const LabelCtx = struct { + label: ?TokenIndex, + opt_ctx: OptionalCtx, +}; - return node; - } +const InlineCtx = struct { + label: ?TokenIndex, + inline_token: ?TokenIndex, + opt_ctx: OptionalCtx, +}; - fn createLiteral(self: &Parser, arena: &mem.Allocator, comptime T: type, token: &const Token) !&T { - return self.createNode(arena, T, - T { - .base = undefined, - .token = *token, - } - ); - } +const LoopCtx = struct { + label: ?TokenIndex, + inline_token: ?TokenIndex, + loop_token: TokenIndex, + opt_ctx: OptionalCtx, +}; + +const AsyncEndCtx = struct { + ctx: OptionalCtx, + attribute: &ast.Node.AsyncAttribute, +}; - fn createToCtxLiteral(self: &Parser, arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token: &const Token) !&T { - const node = try self.createLiteral(arena, T, token); - opt_ctx.store(&node.base); +const ErrorTypeOrSetDeclCtx = struct { + opt_ctx: OptionalCtx, + error_token: TokenIndex, +}; + +const ParamDeclEndCtx = struct { + fn_proto: &ast.Node.FnProto, + param_decl: &ast.Node.ParamDecl, +}; + +const ComptimeStatementCtx = struct { + comptime_token: TokenIndex, + block: &ast.Node.Block, +}; + +const OptionalCtx = union(enum) { + Optional: &?&ast.Node, + RequiredNull: &?&ast.Node, + Required: &&ast.Node, - return node; + pub fn store(self: &const OptionalCtx, value: &ast.Node) void { + switch (*self) { + OptionalCtx.Optional => |ptr| *ptr = value, + OptionalCtx.RequiredNull => |ptr| *ptr = value, + OptionalCtx.Required => |ptr| *ptr = value, + } } - fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) { - const loc = self.tokenizer.getTokenLocation(0, token); - warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args); - warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]); - { - var i: usize = 0; - while (i < loc.column) : (i += 1) { - warn(" "); - } + pub fn get(self: &const OptionalCtx) ?&ast.Node { + switch (*self) { + OptionalCtx.Optional => |ptr| return *ptr, + OptionalCtx.RequiredNull => |ptr| return ??*ptr, + OptionalCtx.Required => |ptr| return *ptr, } - { - const caret_count = token.end - token.start; - var i: usize = 0; - while (i < caret_count) : (i += 1) { - warn("~"); - } + } + + pub fn toRequired(self: &const OptionalCtx) OptionalCtx { + switch (*self) { + OptionalCtx.Optional => |ptr| { + return OptionalCtx { .RequiredNull = ptr }; + }, + OptionalCtx.RequiredNull => |ptr| return *self, + OptionalCtx.Required => |ptr| return *self, } - warn("\n"); - return error.ParseError; } +}; + +const AddCommentsCtx = struct { + node_ptr: &&ast.Node, + comments: ?&ast.Node.DocComment, +}; + +const State = union(enum) { + TopLevel, + TopLevelExtern: TopLevelDeclCtx, + TopLevelLibname: TopLevelDeclCtx, + TopLevelDecl: TopLevelDeclCtx, + TopLevelExternOrField: TopLevelExternOrFieldCtx, + + ContainerKind: ContainerKindCtx, + ContainerInitArgStart: &ast.Node.ContainerDecl, + ContainerInitArg: &ast.Node.ContainerDecl, + ContainerDecl: &ast.Node.ContainerDecl, + + VarDecl: VarDeclCtx, + VarDeclAlign: &ast.Node.VarDecl, + VarDeclEq: &ast.Node.VarDecl, + + FnDef: &ast.Node.FnProto, + FnProto: &ast.Node.FnProto, + FnProtoAlign: &ast.Node.FnProto, + FnProtoReturnType: &ast.Node.FnProto, + + ParamDecl: &ast.Node.FnProto, + ParamDeclAliasOrComptime: &ast.Node.ParamDecl, + ParamDeclName: &ast.Node.ParamDecl, + ParamDeclEnd: ParamDeclEndCtx, + ParamDeclComma: &ast.Node.FnProto, + + MaybeLabeledExpression: MaybeLabeledExpressionCtx, + LabeledExpression: LabelCtx, + Inline: InlineCtx, + While: LoopCtx, + WhileContinueExpr: &?&ast.Node, + For: LoopCtx, + Else: &?&ast.Node.Else, + + Block: &ast.Node.Block, + Statement: &ast.Node.Block, + ComptimeStatement: ComptimeStatementCtx, + Semicolon: &&ast.Node, + + AsmOutputItems: &ast.Node.Asm.OutputList, + AsmOutputReturnOrType: &ast.Node.AsmOutput, + AsmInputItems: &ast.Node.Asm.InputList, + AsmClobberItems: &ast.Node.Asm.ClobberList, + + ExprListItemOrEnd: ExprListCtx, + ExprListCommaOrEnd: ExprListCtx, + FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), + FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), + FieldListCommaOrEnd: &ast.Node.ContainerDecl, + FieldInitValue: OptionalCtx, + ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), + ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), + SwitchCaseOrEnd: ListSave(ast.Node.Switch.CaseList), + SwitchCaseCommaOrEnd: ListSave(ast.Node.Switch.CaseList), + SwitchCaseFirstItem: &ast.Node.SwitchCase.ItemList, + SwitchCaseItem: &ast.Node.SwitchCase.ItemList, + SwitchCaseItemCommaOrEnd: &ast.Node.SwitchCase.ItemList, + + SuspendBody: &ast.Node.Suspend, + AsyncAllocator: &ast.Node.AsyncAttribute, + AsyncEnd: AsyncEndCtx, + + ExternType: ExternTypeCtx, + SliceOrArrayAccess: &ast.Node.SuffixOp, + SliceOrArrayType: &ast.Node.PrefixOp, + AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo, + + Payload: OptionalCtx, + PointerPayload: OptionalCtx, + PointerIndexPayload: OptionalCtx, + + Expression: OptionalCtx, + RangeExpressionBegin: OptionalCtx, + RangeExpressionEnd: OptionalCtx, + AssignmentExpressionBegin: OptionalCtx, + AssignmentExpressionEnd: OptionalCtx, + UnwrapExpressionBegin: OptionalCtx, + UnwrapExpressionEnd: OptionalCtx, + BoolOrExpressionBegin: OptionalCtx, + BoolOrExpressionEnd: OptionalCtx, + BoolAndExpressionBegin: OptionalCtx, + BoolAndExpressionEnd: OptionalCtx, + ComparisonExpressionBegin: OptionalCtx, + ComparisonExpressionEnd: OptionalCtx, + BinaryOrExpressionBegin: OptionalCtx, + BinaryOrExpressionEnd: OptionalCtx, + BinaryXorExpressionBegin: OptionalCtx, + BinaryXorExpressionEnd: OptionalCtx, + BinaryAndExpressionBegin: OptionalCtx, + BinaryAndExpressionEnd: OptionalCtx, + BitShiftExpressionBegin: OptionalCtx, + BitShiftExpressionEnd: OptionalCtx, + AdditionExpressionBegin: OptionalCtx, + AdditionExpressionEnd: OptionalCtx, + MultiplyExpressionBegin: OptionalCtx, + MultiplyExpressionEnd: OptionalCtx, + CurlySuffixExpressionBegin: OptionalCtx, + CurlySuffixExpressionEnd: OptionalCtx, + TypeExprBegin: OptionalCtx, + TypeExprEnd: OptionalCtx, + PrefixOpExpression: OptionalCtx, + SuffixOpExpressionBegin: OptionalCtx, + SuffixOpExpressionEnd: OptionalCtx, + PrimaryExpression: OptionalCtx, + + ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx, + StringLiteral: OptionalCtx, + Identifier: OptionalCtx, + ErrorTag: &&ast.Node, + + + IfToken: @TagType(Token.Id), + IfTokenSave: ExpectTokenSave, + ExpectToken: @TagType(Token.Id), + ExpectTokenSave: ExpectTokenSave, + OptionalTokenSave: OptionalTokenSave, +}; - fn expectToken(self: &Parser, id: @TagType(Token.Id)) !Token { - const token = self.getNextToken(); - if (token.id != id) { - return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id)); +fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.DocComment { + var result: ?&ast.Node.DocComment = null; + while (true) { + if (eatToken(tok_it, Token.Id.DocComment)) |line_comment| { + const node = blk: { + if (result) |comment_node| { + break :blk comment_node; + } else { + const comment_node = try arena.construct(ast.Node.DocComment { + .base = ast.Node { + .id = ast.Node.Id.DocComment, + }, + .lines = ast.Node.DocComment.LineList.init(arena), + }); + result = comment_node; + break :blk comment_node; + } + }; + try node.lines.push(line_comment); + continue; } - return token; + break; } + return result; +} + +fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.LineComment { + const token = eatToken(tok_it, Token.Id.LineComment) ?? return null; + return try arena.construct(ast.Node.LineComment { + .base = ast.Node { + .id = ast.Node.Id.LineComment, + }, + .token = token, + }); +} + +fn requireSemiColon(node: &const ast.Node) bool { + var n = node; + while (true) { + switch (n.id) { + ast.Node.Id.Root, + ast.Node.Id.StructField, + ast.Node.Id.UnionTag, + ast.Node.Id.EnumTag, + ast.Node.Id.ParamDecl, + ast.Node.Id.Block, + ast.Node.Id.Payload, + ast.Node.Id.PointerPayload, + ast.Node.Id.PointerIndexPayload, + ast.Node.Id.Switch, + ast.Node.Id.SwitchCase, + ast.Node.Id.SwitchElse, + ast.Node.Id.FieldInitializer, + ast.Node.Id.DocComment, + ast.Node.Id.LineComment, + ast.Node.Id.TestDecl => return false, + ast.Node.Id.While => { + const while_node = @fieldParentPtr(ast.Node.While, "base", n); + if (while_node.@"else") |@"else"| { + n = @"else".base; + continue; + } + + return while_node.body.id != ast.Node.Id.Block; + }, + ast.Node.Id.For => { + const for_node = @fieldParentPtr(ast.Node.For, "base", n); + if (for_node.@"else") |@"else"| { + n = @"else".base; + continue; + } + + return for_node.body.id != ast.Node.Id.Block; + }, + ast.Node.Id.If => { + const if_node = @fieldParentPtr(ast.Node.If, "base", n); + if (if_node.@"else") |@"else"| { + n = @"else".base; + continue; + } - fn eatToken(self: &Parser, id: @TagType(Token.Id)) ?Token { - if (self.isPeekToken(id)) { - return self.getNextToken(); + return if_node.body.id != ast.Node.Id.Block; + }, + ast.Node.Id.Else => { + const else_node = @fieldParentPtr(ast.Node.Else, "base", n); + n = else_node.body; + continue; + }, + ast.Node.Id.Defer => { + const defer_node = @fieldParentPtr(ast.Node.Defer, "base", n); + return defer_node.expr.id != ast.Node.Id.Block; + }, + ast.Node.Id.Comptime => { + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", n); + return comptime_node.expr.id != ast.Node.Id.Block; + }, + ast.Node.Id.Suspend => { + const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", n); + if (suspend_node.body) |body| { + return body.id != ast.Node.Id.Block; + } + + return true; + }, + else => return true, } - return null; } +} + +fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, + token_ptr: &const Token, token_index: TokenIndex) !?&ast.Node +{ + switch (token_ptr.id) { + Token.Id.StringLiteral => { + return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base; + }, + Token.Id.MultilineStringLiteralLine => { + const node = try arena.construct(ast.Node.MultilineStringLiteral { + .base = ast.Node { .id = ast.Node.Id.MultilineStringLiteral }, + .lines = ast.Node.MultilineStringLiteral.LineList.init(arena), + }); + try node.lines.push(token_index); + while (true) { + const multiline_str_index = tok_it.index; + const multiline_str_ptr = ??tok_it.next(); + if (multiline_str_ptr.id != Token.Id.MultilineStringLiteralLine) { + _ = tok_it.prev(); + break; + } - fn putBackToken(self: &Parser, token: &const Token) void { - self.put_back_tokens[self.put_back_count] = *token; - self.put_back_count += 1; + try node.lines.push(multiline_str_index); + } + + return &node.base; + }, + // TODO: We shouldn't need a cast, but: + // zig: /home/jc/Documents/zig/src/ir.cpp:7962: TypeTableEntry* ir_resolve_peer_types(IrAnalyze*, AstNode*, IrInstruction**, size_t): Assertion `err_set_type != nullptr' failed. + else => return (?&ast.Node)(null), } +} - fn getNextToken(self: &Parser) Token { - if (self.put_back_count != 0) { - const put_back_index = self.put_back_count - 1; - const put_back_token = self.put_back_tokens[put_back_index]; - self.put_back_count = put_back_index; - return put_back_token; - } else { - return self.tokenizer.next(); +fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: &const OptionalCtx, + token_ptr: &const Token, token_index: TokenIndex) !bool { + switch (token_ptr.id) { + Token.Id.Keyword_suspend => { + const node = try createToCtxNode(arena, ctx, ast.Node.Suspend, + ast.Node.Suspend { + .base = undefined, + .label = null, + .suspend_token = token_index, + .payload = null, + .body = null, + } + ); + + stack.push(State { .SuspendBody = node }) catch unreachable; + try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + return true; + }, + Token.Id.Keyword_if => { + const node = try createToCtxNode(arena, ctx, ast.Node.If, + ast.Node.If { + .base = undefined, + .if_token = token_index, + .condition = undefined, + .payload = null, + .body = undefined, + .@"else" = null, + } + ); + + stack.push(State { .Else = &node.@"else" }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + return true; + }, + Token.Id.Keyword_while => { + stack.push(State { + .While = LoopCtx { + .label = null, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = *ctx, + } + }) catch unreachable; + return true; + }, + Token.Id.Keyword_for => { + stack.push(State { + .For = LoopCtx { + .label = null, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = *ctx, + } + }) catch unreachable; + return true; + }, + Token.Id.Keyword_switch => { + const node = try arena.construct(ast.Node.Switch { + .base = ast.Node { + .id = ast.Node.Id.Switch, + }, + .switch_token = token_index, + .expr = undefined, + .cases = ast.Node.Switch.CaseList.init(arena), + .rbrace = undefined, + }); + ctx.store(&node.base); + + stack.push(State { + .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)) { + .list = &node.cases, + .ptr = &node.rbrace, + }, + }) catch unreachable; + try stack.push(State { .ExpectToken = Token.Id.LBrace }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + return true; + }, + Token.Id.Keyword_comptime => { + const node = try createToCtxNode(arena, ctx, ast.Node.Comptime, + ast.Node.Comptime { + .base = undefined, + .comptime_token = token_index, + .expr = undefined, + .doc_comments = null, + } + ); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + return true; + }, + Token.Id.LBrace => { + const block = try arena.construct(ast.Node.Block { + .base = ast.Node {.id = ast.Node.Id.Block }, + .label = null, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + ctx.store(&block.base); + stack.push(State { .Block = block }) catch unreachable; + return true; + }, + else => { + return false; } } +} - fn isPeekToken(self: &Parser, id: @TagType(Token.Id)) bool { - const token = self.getNextToken(); - defer self.putBackToken(token); - return id == token.id; +const ExpectCommaOrEndResult = union(enum) { + end_token: ?TokenIndex, + parse_error: Error, +}; + +fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, end: @TagType(Token.Id)) ExpectCommaOrEndResult { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Comma => return ExpectCommaOrEndResult { .end_token = null}, + else => { + if (end == token_ptr.id) { + return ExpectCommaOrEndResult { .end_token = token_index }; + } + + return ExpectCommaOrEndResult { + .parse_error = Error { + .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd { + .token = token_index, + .end_id = end, + }, + }, + }; + }, } +} - const RenderAstFrame = struct { - node: &ast.Node, - indent: usize, +fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op { + // TODO: We have to cast all cases because of this: + // error: expected type '?InfixOp', found '?@TagType(InfixOp)' + return switch (*id) { + Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = {} }, + Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = {} }, + Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = {} }, + Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = {} }, + Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = {} }, + Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = {} }, + Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = {} }, + Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = {} }, + Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = {} }, + Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = {} }, + Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = {} }, + Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = {} }, + Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = {} }, + Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = {} }, + else => null, }; +} - pub fn renderAst(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { - var stack = self.initUtilityArrayList(RenderAstFrame); - defer self.deinitUtilityArrayList(stack); +fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null }, + Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} }, + else => null, + }; +} - try stack.append(RenderAstFrame { - .node = &root_node.base, - .indent = 0, - }); +fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} }, + Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} }, + Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} }, + Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} }, + Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} }, + Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} }, + else => null, + }; +} - while (stack.popOrNull()) |frame| { - { - var i: usize = 0; - while (i < frame.indent) : (i += 1) { - try stream.print(" "); - } - } - try stream.print("{}\n", @tagName(frame.node.id)); - var child_i: usize = 0; - while (frame.node.iterate(child_i)) |child| : (child_i += 1) { - try stack.append(RenderAstFrame { - .node = child, - .indent = frame.indent + 2, - }); - } - } - } +fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} }, + Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} }, + else => null, + }; +} + +fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} }, + Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} }, + Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} }, + Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} }, + Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} }, + else => null, + }; +} - const RenderState = union(enum) { - TopLevelDecl: &ast.Node, - ParamDecl: &ast.Node, - Text: []const u8, - Expression: &ast.Node, - VarDecl: &ast.Node.VarDecl, - Statement: &ast.Node, - PrintIndent, - Indent: usize, - PrintSameLineComment: ?&Token, - PrintLineComment: &Token, +fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} }, + Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} }, + Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} }, + Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} }, + Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} }, + Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} }, + else => null, }; +} - pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { - var stack = self.initUtilityArrayList(RenderState); - defer self.deinitUtilityArrayList(stack); +fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { + return switch (id) { + Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} }, + Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} }, + Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} }, + Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} }, + Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} }, + Token.Id.Ampersand => ast.Node.PrefixOp.Op { + .AddrOf = ast.Node.PrefixOp.AddrOfInfo { + .align_expr = null, + .bit_offset_start_token = null, + .bit_offset_end_token = null, + .const_token = null, + .volatile_token = null, + }, + }, + Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} }, + Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} }, + Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} }, + Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } }, + else => null, + }; +} - { - try stack.append(RenderState { .Text = "\n"}); - - var i = root_node.decls.len; - while (i != 0) { - i -= 1; - const decl = root_node.decls.items[i]; - try stack.append(RenderState {.TopLevelDecl = decl}); - if (i != 0) { - try stack.append(RenderState { - .Text = blk: { - const prev_node = root_node.decls.at(i - 1); - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, decl.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }, - }); - } - } +fn createNode(arena: &mem.Allocator, comptime T: type, init_to: &const T) !&T { + const node = try arena.create(T); + *node = *init_to; + node.base = blk: { + const id = ast.Node.typeToId(T); + break :blk ast.Node { + .id = id, + }; + }; + + return node; +} + +fn createToCtxNode(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T { + const node = try createNode(arena, T, init_to); + opt_ctx.store(&node.base); + + return node; +} + +fn createLiteral(arena: &mem.Allocator, comptime T: type, token_index: TokenIndex) !&T { + return createNode(arena, T, + T { + .base = undefined, + .token = token_index, } + ); +} - const indent_delta = 4; - var indent: usize = 0; - while (stack.popOrNull()) |state| { - switch (state) { - RenderState.TopLevelDecl => |decl| { - try stack.append(RenderState { .PrintSameLineComment = decl.same_line_comment } ); - switch (decl.id) { - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); - try self.renderComments(stream, fn_proto, indent); - - if (fn_proto.body_node) |body_node| { - stack.append(RenderState { .Expression = body_node}) catch unreachable; - try stack.append(RenderState { .Text = " "}); - } else { - stack.append(RenderState { .Text = ";" }) catch unreachable; - } +fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token_index: TokenIndex) !&T { + const node = try createLiteral(arena, T, token_index); + opt_ctx.store(&node.base); - try stack.append(RenderState { .Expression = decl }); - }, - ast.Node.Id.Use => { - const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); - if (use_decl.visib_token) |visib_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token)); - } - try stream.print("use "); - try stack.append(RenderState { .Text = ";" }); - try stack.append(RenderState { .Expression = use_decl.expr }); - }, - ast.Node.Id.VarDecl => { - const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); - try self.renderComments(stream, var_decl, indent); - try stack.append(RenderState { .VarDecl = var_decl}); - }, - ast.Node.Id.TestDecl => { - const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); - try self.renderComments(stream, test_decl, indent); - try stream.print("test "); - try stack.append(RenderState { .Expression = test_decl.body_node }); - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = test_decl.name }); - }, - ast.Node.Id.StructField => { - const field = @fieldParentPtr(ast.Node.StructField, "base", decl); - try self.renderComments(stream, field, indent); - if (field.visib_token) |visib_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token)); - } - try stream.print("{}: ", self.tokenizer.getTokenSlice(field.name_token)); - try stack.append(RenderState { .Text = "," }); - try stack.append(RenderState { .Expression = field.type_expr}); - }, - ast.Node.Id.UnionTag => { - const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); - try self.renderComments(stream, tag, indent); - try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); + return node; +} + +fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, id: @TagType(Token.Id)) ?TokenIndex { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id == id) + return token_index; + + _ = tok_it.prev(); + return null; +} - try stack.append(RenderState { .Text = "," }); +const RenderAstFrame = struct { + node: &ast.Node, + indent: usize, +}; - if (tag.value_expr) |value_expr| { - try stack.append(RenderState { .Expression = value_expr }); - try stack.append(RenderState { .Text = " = " }); - } +pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) !void { + var stack = SegmentedList(State, 32).init(allocator); + defer stack.deinit(); - if (tag.type_expr) |type_expr| { - try stream.print(": "); - try stack.append(RenderState { .Expression = type_expr}); - } - }, - ast.Node.Id.EnumTag => { - const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); - try self.renderComments(stream, tag, indent); - try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); - - try stack.append(RenderState { .Text = "," }); - if (tag.value) |value| { - try stream.print(" = "); - try stack.append(RenderState { .Expression = value}); - } - }, - ast.Node.Id.ErrorTag => { - const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl); - try self.renderComments(stream, tag, indent); - try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); - }, - ast.Node.Id.Comptime => { - if (requireSemiColon(decl)) { - try stack.append(RenderState { .Text = ";" }); - } - try stack.append(RenderState { .Expression = decl }); - }, - ast.Node.Id.LineComment => { - const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl); - try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token)); - }, - else => unreachable, - } - }, + try stack.push(RenderAstFrame { + .node = &root_node.base, + .indent = 0, + }); - RenderState.VarDecl => |var_decl| { - try stack.append(RenderState { .Text = ";" }); - if (var_decl.init_node) |init_node| { - try stack.append(RenderState { .Expression = init_node }); - const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = "; - try stack.append(RenderState { .Text = text }); - } - if (var_decl.align_node) |align_node| { - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = align_node }); - try stack.append(RenderState { .Text = " align(" }); - } - if (var_decl.type_node) |type_node| { - try stack.append(RenderState { .Expression = type_node }); - try stack.append(RenderState { .Text = ": " }); - } - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(var_decl.name_token) }); - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(var_decl.mut_token) }); + while (stack.popOrNull()) |frame| { + { + var i: usize = 0; + while (i < frame.indent) : (i += 1) { + try stream.print(" "); + } + } + try stream.print("{}\n", @tagName(frame.node.id)); + var child_i: usize = 0; + while (frame.node.iterate(child_i)) |child| : (child_i += 1) { + try stack.push(RenderAstFrame { + .node = child, + .indent = frame.indent + 2, + }); + } + } +} - if (var_decl.comptime_token) |comptime_token| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(comptime_token) }); - } +const RenderState = union(enum) { + TopLevelDecl: &ast.Node, + ParamDecl: &ast.Node, + Text: []const u8, + Expression: &ast.Node, + VarDecl: &ast.Node.VarDecl, + Statement: &ast.Node, + PrintIndent, + Indent: usize, +}; - if (var_decl.extern_export_token) |extern_export_token| { - if (var_decl.lib_name != null) { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = ??var_decl.lib_name }); +pub fn renderSource(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { + var stack = SegmentedList(RenderState, 32).init(allocator); + defer stack.deinit(); + + { + try stack.push(RenderState { .Text = "\n"}); + + var i = tree.root_node.decls.len; + while (i != 0) { + i -= 1; + const decl = *tree.root_node.decls.at(i); + try stack.push(RenderState {.TopLevelDecl = decl}); + if (i != 0) { + try stack.push(RenderState { + .Text = blk: { + const prev_node = *tree.root_node.decls.at(i - 1); + const prev_node_last_token = tree.tokens.at(prev_node.lastToken()); + const loc = tree.tokenLocation(prev_node_last_token.end, decl.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; } - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_export_token) }); - } + break :blk "\n"; + }, + }); + } + } + } - if (var_decl.visib_token) |visib_token| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) }); - } - }, + const indent_delta = 4; + var indent: usize = 0; + while (stack.pop()) |state| { + switch (state) { + RenderState.TopLevelDecl => |decl| { + switch (decl.id) { + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); + try renderComments(tree, stream, fn_proto, indent); - RenderState.ParamDecl => |base| { - const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); - if (param_decl.comptime_token) |comptime_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token)); - } - if (param_decl.noalias_token) |noalias_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(noalias_token)); - } - if (param_decl.name_token) |name_token| { - try stream.print("{}: ", self.tokenizer.getTokenSlice(name_token)); - } - if (param_decl.var_args_token) |var_args_token| { - try stream.print("{}", self.tokenizer.getTokenSlice(var_args_token)); - } else { - try stack.append(RenderState { .Expression = param_decl.type_node}); - } - }, - RenderState.Text => |bytes| { - try stream.write(bytes); - }, - RenderState.Expression => |base| switch (base.id) { - ast.Node.Id.Identifier => { - const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(identifier.token)); - }, - ast.Node.Id.Block => { - const block = @fieldParentPtr(ast.Node.Block, "base", base); - if (block.label) |label| { - try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); + if (fn_proto.body_node) |body_node| { + stack.push(RenderState { .Expression = body_node}) catch unreachable; + try stack.push(RenderState { .Text = " "}); + } else { + stack.push(RenderState { .Text = ";" }) catch unreachable; } - if (block.statements.len == 0) { - try stream.write("{}"); - } else { - try stream.write("{"); - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent}); - try stack.append(RenderState { .Text = "\n"}); - var i = block.statements.len; - while (i != 0) { - i -= 1; - const statement_node = block.statements.items[i]; - try stack.append(RenderState { .Statement = statement_node}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = block.statements.items[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, statement_node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - } - break :blk "\n"; - }, - }); - } + try stack.push(RenderState { .Expression = decl }); + }, + ast.Node.Id.Use => { + const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); + if (use_decl.visib_token) |visib_token| { + try stream.print("{} ", tree.tokenSlice(visib_token)); } + try stream.print("use "); + try stack.push(RenderState { .Text = ";" }); + try stack.push(RenderState { .Expression = use_decl.expr }); }, - ast.Node.Id.Defer => { - const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); - try stream.print("{} ", self.tokenizer.getTokenSlice(defer_node.defer_token)); - try stack.append(RenderState { .Expression = defer_node.expr }); + ast.Node.Id.VarDecl => { + const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); + try renderComments(tree, stream, var_decl, indent); + try stack.push(RenderState { .VarDecl = var_decl}); }, - ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); - try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_node.comptime_token)); - try stack.append(RenderState { .Expression = comptime_node.expr }); + ast.Node.Id.TestDecl => { + const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); + try renderComments(tree, stream, test_decl, indent); + try stream.print("test "); + try stack.push(RenderState { .Expression = test_decl.body_node }); + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = test_decl.name }); }, - ast.Node.Id.AsyncAttribute => { - const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(async_attr.async_token)); - - if (async_attr.allocator_type) |allocator_type| { - try stack.append(RenderState { .Text = ">" }); - try stack.append(RenderState { .Expression = allocator_type }); - try stack.append(RenderState { .Text = "<" }); + ast.Node.Id.StructField => { + const field = @fieldParentPtr(ast.Node.StructField, "base", decl); + try renderComments(tree, stream, field, indent); + if (field.visib_token) |visib_token| { + try stream.print("{} ", tree.tokenSlice(visib_token)); } + try stream.print("{}: ", tree.tokenSlice(field.name_token)); + try stack.push(RenderState { .Text = "," }); + try stack.push(RenderState { .Expression = field.type_expr}); }, - ast.Node.Id.Suspend => { - const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); - if (suspend_node.label) |label| { - try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); - } - try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token)); + ast.Node.Id.UnionTag => { + const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); + try renderComments(tree, stream, tag, indent); + try stream.print("{}", tree.tokenSlice(tag.name_token)); - if (suspend_node.body) |body| { - try stack.append(RenderState { .Expression = body }); - try stack.append(RenderState { .Text = " " }); - } + try stack.push(RenderState { .Text = "," }); - if (suspend_node.payload) |payload| { - try stack.append(RenderState { .Expression = payload }); - try stack.append(RenderState { .Text = " " }); + if (tag.value_expr) |value_expr| { + try stack.push(RenderState { .Expression = value_expr }); + try stack.push(RenderState { .Text = " = " }); } - }, - ast.Node.Id.InfixOp => { - const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); - try stack.append(RenderState { .Expression = prefix_op_node.rhs }); - - if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) { - if (prefix_op_node.op.Catch) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); - } - try stack.append(RenderState { .Text = " catch " }); - } else { - const text = switch (prefix_op_node.op) { - ast.Node.InfixOp.Op.Add => " + ", - ast.Node.InfixOp.Op.AddWrap => " +% ", - ast.Node.InfixOp.Op.ArrayCat => " ++ ", - ast.Node.InfixOp.Op.ArrayMult => " ** ", - ast.Node.InfixOp.Op.Assign => " = ", - ast.Node.InfixOp.Op.AssignBitAnd => " &= ", - ast.Node.InfixOp.Op.AssignBitOr => " |= ", - ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ", - ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ", - ast.Node.InfixOp.Op.AssignBitXor => " ^= ", - ast.Node.InfixOp.Op.AssignDiv => " /= ", - ast.Node.InfixOp.Op.AssignMinus => " -= ", - ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ", - ast.Node.InfixOp.Op.AssignMod => " %= ", - ast.Node.InfixOp.Op.AssignPlus => " += ", - ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ", - ast.Node.InfixOp.Op.AssignTimes => " *= ", - ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ", - ast.Node.InfixOp.Op.BangEqual => " != ", - ast.Node.InfixOp.Op.BitAnd => " & ", - ast.Node.InfixOp.Op.BitOr => " | ", - ast.Node.InfixOp.Op.BitShiftLeft => " << ", - ast.Node.InfixOp.Op.BitShiftRight => " >> ", - ast.Node.InfixOp.Op.BitXor => " ^ ", - ast.Node.InfixOp.Op.BoolAnd => " and ", - ast.Node.InfixOp.Op.BoolOr => " or ", - ast.Node.InfixOp.Op.Div => " / ", - ast.Node.InfixOp.Op.EqualEqual => " == ", - ast.Node.InfixOp.Op.ErrorUnion => "!", - ast.Node.InfixOp.Op.GreaterOrEqual => " >= ", - ast.Node.InfixOp.Op.GreaterThan => " > ", - ast.Node.InfixOp.Op.LessOrEqual => " <= ", - ast.Node.InfixOp.Op.LessThan => " < ", - ast.Node.InfixOp.Op.MergeErrorSets => " || ", - ast.Node.InfixOp.Op.Mod => " % ", - ast.Node.InfixOp.Op.Mult => " * ", - ast.Node.InfixOp.Op.MultWrap => " *% ", - ast.Node.InfixOp.Op.Period => ".", - ast.Node.InfixOp.Op.Sub => " - ", - ast.Node.InfixOp.Op.SubWrap => " -% ", - ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ", - ast.Node.InfixOp.Op.Range => " ... ", - ast.Node.InfixOp.Op.Catch => unreachable, - }; - try stack.append(RenderState { .Text = text }); + if (tag.type_expr) |type_expr| { + try stream.print(": "); + try stack.push(RenderState { .Expression = type_expr}); } - try stack.append(RenderState { .Expression = prefix_op_node.lhs }); }, - ast.Node.Id.PrefixOp => { - const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); - try stack.append(RenderState { .Expression = prefix_op_node.rhs }); - switch (prefix_op_node.op) { - ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { - try stream.write("&"); - if (addr_of_info.volatile_token != null) { - try stack.append(RenderState { .Text = "volatile "}); - } - if (addr_of_info.const_token != null) { - try stack.append(RenderState { .Text = "const "}); - } - if (addr_of_info.align_expr) |align_expr| { - try stream.print("align("); - try stack.append(RenderState { .Text = ") "}); - try stack.append(RenderState { .Expression = align_expr}); - } - }, - ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { - try stream.write("[]"); - if (addr_of_info.volatile_token != null) { - try stack.append(RenderState { .Text = "volatile "}); - } - if (addr_of_info.const_token != null) { - try stack.append(RenderState { .Text = "const "}); - } - if (addr_of_info.align_expr) |align_expr| { - try stream.print("align("); - try stack.append(RenderState { .Text = ") "}); - try stack.append(RenderState { .Expression = align_expr}); - } - }, - ast.Node.PrefixOp.Op.ArrayType => |array_index| { - try stack.append(RenderState { .Text = "]"}); - try stack.append(RenderState { .Expression = array_index}); - try stack.append(RenderState { .Text = "["}); - }, - ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), - ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), - ast.Node.PrefixOp.Op.Deref => try stream.write("*"), - ast.Node.PrefixOp.Op.Negation => try stream.write("-"), - ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"), - ast.Node.PrefixOp.Op.Try => try stream.write("try "), - ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"), - ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"), - ast.Node.PrefixOp.Op.Await => try stream.write("await "), - ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "), - ast.Node.PrefixOp.Op.Resume => try stream.write("resume "), + ast.Node.Id.EnumTag => { + const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); + try renderComments(tree, stream, tag, indent); + try stream.print("{}", tree.tokenSlice(tag.name_token)); + + try stack.push(RenderState { .Text = "," }); + if (tag.value) |value| { + try stream.print(" = "); + try stack.push(RenderState { .Expression = value}); } }, - ast.Node.Id.SuffixOp => { - const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base); - - switch (suffix_op.op) { - ast.Node.SuffixOp.Op.Call => |call_info| { - try stack.append(RenderState { .Text = ")"}); - var i = call_info.params.len; - while (i != 0) { - i -= 1; - const param_node = call_info.params.at(i); - try stack.append(RenderState { .Expression = param_node}); - if (i != 0) { - try stack.append(RenderState { .Text = ", " }); - } - } - try stack.append(RenderState { .Text = "("}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - - if (call_info.async_attr) |async_attr| { - try stack.append(RenderState { .Text = " "}); - try stack.append(RenderState { .Expression = &async_attr.base }); - } - }, - ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { - try stack.append(RenderState { .Text = "]"}); - try stack.append(RenderState { .Expression = index_expr}); - try stack.append(RenderState { .Text = "["}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - }, - ast.Node.SuffixOp.Op.Slice => |range| { - try stack.append(RenderState { .Text = "]"}); - if (range.end) |end| { - try stack.append(RenderState { .Expression = end}); - } - try stack.append(RenderState { .Text = ".."}); - try stack.append(RenderState { .Expression = range.start}); - try stack.append(RenderState { .Text = "["}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - }, - ast.Node.SuffixOp.Op.StructInitializer => |field_inits| { - if (field_inits.len == 0) { - try stack.append(RenderState { .Text = "{}" }); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - continue; - } - if (field_inits.len == 1) { - const field_init = field_inits.at(0); - - try stack.append(RenderState { .Text = " }" }); - try stack.append(RenderState { .Expression = field_init }); - try stack.append(RenderState { .Text = "{ " }); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - continue; - } - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n" }); - var i = field_inits.len; - while (i != 0) { - i -= 1; - const field_init = field_inits.at(i); - if (field_init.id != ast.Node.Id.LineComment) { - try stack.append(RenderState { .Text = "," }); - } - try stack.append(RenderState { .Expression = field_init }); - try stack.append(RenderState.PrintIndent); - if (i != 0) { - try stack.append(RenderState { .Text = blk: { - const prev_node = field_inits.at(i - 1); - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, field_init.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }}); - } - } - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "{\n"}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - }, - ast.Node.SuffixOp.Op.ArrayInitializer => |exprs| { - if (exprs.len == 0) { - try stack.append(RenderState { .Text = "{}" }); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - continue; - } - if (exprs.len == 1) { - const expr = exprs.at(0); - - try stack.append(RenderState { .Text = "}" }); - try stack.append(RenderState { .Expression = expr }); - try stack.append(RenderState { .Text = "{" }); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - continue; - } - - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - var i = exprs.len; - while (i != 0) { - i -= 1; - const expr = exprs.at(i); - try stack.append(RenderState { .Text = ",\n" }); - try stack.append(RenderState { .Expression = expr }); - try stack.append(RenderState.PrintIndent); - } - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "{\n"}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - }, - } + ast.Node.Id.ErrorTag => { + const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl); + try renderComments(tree, stream, tag, indent); + try stream.print("{}", tree.tokenSlice(tag.name_token)); }, - ast.Node.Id.ControlFlowExpression => { - const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); - - if (flow_expr.rhs) |rhs| { - try stack.append(RenderState { .Expression = rhs }); - try stack.append(RenderState { .Text = " " }); - } - - switch (flow_expr.kind) { - ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { - try stream.print("break"); - if (maybe_label) |label| { - try stream.print(" :"); - try stack.append(RenderState { .Expression = label }); - } - }, - ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { - try stream.print("continue"); - if (maybe_label) |label| { - try stream.print(" :"); - try stack.append(RenderState { .Expression = label }); - } - }, - ast.Node.ControlFlowExpression.Kind.Return => { - try stream.print("return"); - }, - + ast.Node.Id.Comptime => { + if (requireSemiColon(decl)) { + try stack.push(RenderState { .Text = ";" }); } + try stack.push(RenderState { .Expression = decl }); }, - ast.Node.Id.Payload => { - const payload = @fieldParentPtr(ast.Node.Payload, "base", base); - try stack.append(RenderState { .Text = "|"}); - try stack.append(RenderState { .Expression = payload.error_symbol }); - try stack.append(RenderState { .Text = "|"}); + ast.Node.Id.LineComment => { + const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl); + try stream.write(tree.tokenSlice(line_comment_node.token)); }, - ast.Node.Id.PointerPayload => { - const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); - try stack.append(RenderState { .Text = "|"}); - try stack.append(RenderState { .Expression = payload.value_symbol }); + else => unreachable, + } + }, - if (payload.ptr_token) |ptr_token| { - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(ptr_token) }); - } + RenderState.VarDecl => |var_decl| { + try stack.push(RenderState { .Text = ";" }); + if (var_decl.init_node) |init_node| { + try stack.push(RenderState { .Expression = init_node }); + const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = "; + try stack.push(RenderState { .Text = text }); + } + if (var_decl.align_node) |align_node| { + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = align_node }); + try stack.push(RenderState { .Text = " align(" }); + } + if (var_decl.type_node) |type_node| { + try stack.push(RenderState { .Expression = type_node }); + try stack.push(RenderState { .Text = ": " }); + } + try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.name_token) }); + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) }); - try stack.append(RenderState { .Text = "|"}); - }, - ast.Node.Id.PointerIndexPayload => { - const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); - try stack.append(RenderState { .Text = "|"}); + if (var_decl.comptime_token) |comptime_token| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(comptime_token) }); + } - if (payload.index_symbol) |index_symbol| { - try stack.append(RenderState { .Expression = index_symbol }); - try stack.append(RenderState { .Text = ", "}); - } + if (var_decl.extern_export_token) |extern_export_token| { + if (var_decl.lib_name != null) { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = ??var_decl.lib_name }); + } + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_token) }); + } + + if (var_decl.visib_token) |visib_token| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(visib_token) }); + } + }, - try stack.append(RenderState { .Expression = payload.value_symbol }); + RenderState.ParamDecl => |base| { + const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); + if (param_decl.comptime_token) |comptime_token| { + try stream.print("{} ", tree.tokenSlice(comptime_token)); + } + if (param_decl.noalias_token) |noalias_token| { + try stream.print("{} ", tree.tokenSlice(noalias_token)); + } + if (param_decl.name_token) |name_token| { + try stream.print("{}: ", tree.tokenSlice(name_token)); + } + if (param_decl.var_args_token) |var_args_token| { + try stream.print("{}", tree.tokenSlice(var_args_token)); + } else { + try stack.push(RenderState { .Expression = param_decl.type_node}); + } + }, + RenderState.Text => |bytes| { + try stream.write(bytes); + }, + RenderState.Expression => |base| switch (base.id) { + ast.Node.Id.Identifier => { + const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); + try stream.print("{}", tree.tokenSlice(identifier.token)); + }, + ast.Node.Id.Block => { + const block = @fieldParentPtr(ast.Node.Block, "base", base); + if (block.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } - if (payload.ptr_token) |ptr_token| { - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(ptr_token) }); + if (block.statements.len == 0) { + try stream.write("{}"); + } else { + try stream.write("{"); + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent}); + try stack.push(RenderState { .Text = "\n"}); + var i = block.statements.len; + while (i != 0) { + i -= 1; + const statement_node = *block.statements.at(i); + try stack.push(RenderState { .Statement = statement_node}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { + .Text = blk: { + if (i != 0) { + const prev_node = *block.statements.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, statement_node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + } + break :blk "\n"; + }, + }); } + } + }, + ast.Node.Id.Defer => { + const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); + try stream.print("{} ", tree.tokenSlice(defer_node.defer_token)); + try stack.push(RenderState { .Expression = defer_node.expr }); + }, + ast.Node.Id.Comptime => { + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); + try stream.print("{} ", tree.tokenSlice(comptime_node.comptime_token)); + try stack.push(RenderState { .Expression = comptime_node.expr }); + }, + ast.Node.Id.AsyncAttribute => { + const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); + try stream.print("{}", tree.tokenSlice(async_attr.async_token)); + + if (async_attr.allocator_type) |allocator_type| { + try stack.push(RenderState { .Text = ">" }); + try stack.push(RenderState { .Expression = allocator_type }); + try stack.push(RenderState { .Text = "<" }); + } + }, + ast.Node.Id.Suspend => { + const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); + if (suspend_node.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } + try stream.print("{}", tree.tokenSlice(suspend_node.suspend_token)); - try stack.append(RenderState { .Text = "|"}); - }, - ast.Node.Id.GroupedExpression => { - const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); - try stack.append(RenderState { .Text = ")"}); - try stack.append(RenderState { .Expression = grouped_expr.expr }); - try stack.append(RenderState { .Text = "("}); - }, - ast.Node.Id.FieldInitializer => { - const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); - try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token)); - try stack.append(RenderState { .Expression = field_init.expr }); - }, - ast.Node.Id.IntegerLiteral => { - const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token)); - }, - ast.Node.Id.FloatLiteral => { - const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token)); - }, - ast.Node.Id.StringLiteral => { - const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token)); - }, - ast.Node.Id.CharLiteral => { - const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(char_literal.token)); - }, - ast.Node.Id.BoolLiteral => { - const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(bool_literal.token)); - }, - ast.Node.Id.NullLiteral => { - const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(null_literal.token)); - }, - ast.Node.Id.ThisLiteral => { - const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(this_literal.token)); - }, - ast.Node.Id.Unreachable => { - const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(unreachable_node.token)); - }, - ast.Node.Id.ErrorType => { - const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(error_type.token)); - }, - ast.Node.Id.VarType => { - const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(var_type.token)); - }, - ast.Node.Id.ContainerDecl => { - const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); + if (suspend_node.body) |body| { + try stack.push(RenderState { .Expression = body }); + try stack.push(RenderState { .Text = " " }); + } - switch (container_decl.layout) { - ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "), - ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "), - ast.Node.ContainerDecl.Layout.Auto => { }, + if (suspend_node.payload) |payload| { + try stack.push(RenderState { .Expression = payload }); + try stack.push(RenderState { .Text = " " }); + } + }, + ast.Node.Id.InfixOp => { + const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); + try stack.push(RenderState { .Expression = prefix_op_node.rhs }); + + if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) { + if (prefix_op_node.op.Catch) |payload| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); } + try stack.push(RenderState { .Text = " catch " }); + } else { + const text = switch (prefix_op_node.op) { + ast.Node.InfixOp.Op.Add => " + ", + ast.Node.InfixOp.Op.AddWrap => " +% ", + ast.Node.InfixOp.Op.ArrayCat => " ++ ", + ast.Node.InfixOp.Op.ArrayMult => " ** ", + ast.Node.InfixOp.Op.Assign => " = ", + ast.Node.InfixOp.Op.AssignBitAnd => " &= ", + ast.Node.InfixOp.Op.AssignBitOr => " |= ", + ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ", + ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ", + ast.Node.InfixOp.Op.AssignBitXor => " ^= ", + ast.Node.InfixOp.Op.AssignDiv => " /= ", + ast.Node.InfixOp.Op.AssignMinus => " -= ", + ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ", + ast.Node.InfixOp.Op.AssignMod => " %= ", + ast.Node.InfixOp.Op.AssignPlus => " += ", + ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ", + ast.Node.InfixOp.Op.AssignTimes => " *= ", + ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ", + ast.Node.InfixOp.Op.BangEqual => " != ", + ast.Node.InfixOp.Op.BitAnd => " & ", + ast.Node.InfixOp.Op.BitOr => " | ", + ast.Node.InfixOp.Op.BitShiftLeft => " << ", + ast.Node.InfixOp.Op.BitShiftRight => " >> ", + ast.Node.InfixOp.Op.BitXor => " ^ ", + ast.Node.InfixOp.Op.BoolAnd => " and ", + ast.Node.InfixOp.Op.BoolOr => " or ", + ast.Node.InfixOp.Op.Div => " / ", + ast.Node.InfixOp.Op.EqualEqual => " == ", + ast.Node.InfixOp.Op.ErrorUnion => "!", + ast.Node.InfixOp.Op.GreaterOrEqual => " >= ", + ast.Node.InfixOp.Op.GreaterThan => " > ", + ast.Node.InfixOp.Op.LessOrEqual => " <= ", + ast.Node.InfixOp.Op.LessThan => " < ", + ast.Node.InfixOp.Op.MergeErrorSets => " || ", + ast.Node.InfixOp.Op.Mod => " % ", + ast.Node.InfixOp.Op.Mult => " * ", + ast.Node.InfixOp.Op.MultWrap => " *% ", + ast.Node.InfixOp.Op.Period => ".", + ast.Node.InfixOp.Op.Sub => " - ", + ast.Node.InfixOp.Op.SubWrap => " -% ", + ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ", + ast.Node.InfixOp.Op.Range => " ... ", + ast.Node.InfixOp.Op.Catch => unreachable, + }; - switch (container_decl.kind) { - ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"), - ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"), - ast.Node.ContainerDecl.Kind.Union => try stream.print("union"), - } + try stack.push(RenderState { .Text = text }); + } + try stack.push(RenderState { .Expression = prefix_op_node.lhs }); + }, + ast.Node.Id.PrefixOp => { + const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); + try stack.push(RenderState { .Expression = prefix_op_node.rhs }); + switch (prefix_op_node.op) { + ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { + try stream.write("&"); + if (addr_of_info.volatile_token != null) { + try stack.push(RenderState { .Text = "volatile "}); + } + if (addr_of_info.const_token != null) { + try stack.push(RenderState { .Text = "const "}); + } + if (addr_of_info.align_expr) |align_expr| { + try stream.print("align("); + try stack.push(RenderState { .Text = ") "}); + try stack.push(RenderState { .Expression = align_expr}); + } + }, + ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { + try stream.write("[]"); + if (addr_of_info.volatile_token != null) { + try stack.push(RenderState { .Text = "volatile "}); + } + if (addr_of_info.const_token != null) { + try stack.push(RenderState { .Text = "const "}); + } + if (addr_of_info.align_expr) |align_expr| { + try stream.print("align("); + try stack.push(RenderState { .Text = ") "}); + try stack.push(RenderState { .Expression = align_expr}); + } + }, + ast.Node.PrefixOp.Op.ArrayType => |array_index| { + try stack.push(RenderState { .Text = "]"}); + try stack.push(RenderState { .Expression = array_index}); + try stack.push(RenderState { .Text = "["}); + }, + ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), + ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), + ast.Node.PrefixOp.Op.Deref => try stream.write("*"), + ast.Node.PrefixOp.Op.Negation => try stream.write("-"), + ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"), + ast.Node.PrefixOp.Op.Try => try stream.write("try "), + ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"), + ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"), + ast.Node.PrefixOp.Op.Await => try stream.write("await "), + ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "), + ast.Node.PrefixOp.Op.Resume => try stream.write("resume "), + } + }, + ast.Node.Id.SuffixOp => { + const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base); - const fields_and_decls = container_decl.fields_and_decls.toSliceConst(); - if (fields_and_decls.len == 0) { - try stack.append(RenderState { .Text = "{}"}); - } else { - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n"}); + switch (suffix_op.op) { + @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| { + try stack.push(RenderState { .Text = ")"}); + var i = call_info.params.len; + while (i != 0) { + i -= 1; + const param_node = *call_info.params.at(i); + try stack.push(RenderState { .Expression = param_node}); + if (i != 0) { + try stack.push(RenderState { .Text = ", " }); + } + } + try stack.push(RenderState { .Text = "("}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + + if (call_info.async_attr) |async_attr| { + try stack.push(RenderState { .Text = " "}); + try stack.push(RenderState { .Expression = &async_attr.base }); + } + }, + ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { + try stack.push(RenderState { .Text = "]"}); + try stack.push(RenderState { .Expression = index_expr}); + try stack.push(RenderState { .Text = "["}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + }, + @TagType(ast.Node.SuffixOp.Op).Slice => |range| { + try stack.push(RenderState { .Text = "]"}); + if (range.end) |end| { + try stack.push(RenderState { .Expression = end}); + } + try stack.push(RenderState { .Text = ".."}); + try stack.push(RenderState { .Expression = range.start}); + try stack.push(RenderState { .Text = "["}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + }, + ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| { + if (field_inits.len == 0) { + try stack.push(RenderState { .Text = "{}" }); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + continue; + } + if (field_inits.len == 1) { + const field_init = *field_inits.at(0); - var i = fields_and_decls.len; + try stack.push(RenderState { .Text = " }" }); + try stack.push(RenderState { .Expression = field_init }); + try stack.push(RenderState { .Text = "{ " }); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + continue; + } + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = "\n" }); + var i = field_inits.len; while (i != 0) { i -= 1; - const node = fields_and_decls[i]; - try stack.append(RenderState { .TopLevelDecl = node}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = fields_and_decls[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } + const field_init = *field_inits.at(i); + if (field_init.id != ast.Node.Id.LineComment) { + try stack.push(RenderState { .Text = "," }); + } + try stack.push(RenderState { .Expression = field_init }); + try stack.push(RenderState.PrintIndent); + if (i != 0) { + try stack.push(RenderState { .Text = blk: { + const prev_node = *field_inits.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, field_init.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; } break :blk "\n"; - }, - }); + }}); + } } - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = "{"}); - } + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "{\n"}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + }, + ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| { + if (exprs.len == 0) { + try stack.push(RenderState { .Text = "{}" }); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + continue; + } + if (exprs.len == 1) { + const expr = *exprs.at(0); - switch (container_decl.init_arg_expr) { - ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}), - ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { - if (enum_tag_type) |expr| { - try stack.append(RenderState { .Text = ")) "}); - try stack.append(RenderState { .Expression = expr}); - try stack.append(RenderState { .Text = "(enum("}); - } else { - try stack.append(RenderState { .Text = "(enum) "}); - } - }, - ast.Node.ContainerDecl.InitArg.Type => |type_expr| { - try stack.append(RenderState { .Text = ") "}); - try stack.append(RenderState { .Expression = type_expr}); - try stack.append(RenderState { .Text = "("}); - }, - } - }, - ast.Node.Id.ErrorSetDecl => { - const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); + try stack.push(RenderState { .Text = "}" }); + try stack.push(RenderState { .Expression = expr }); + try stack.push(RenderState { .Text = "{" }); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + continue; + } - const decls = err_set_decl.decls.toSliceConst(); - if (decls.len == 0) { - try stream.write("error{}"); - continue; - } + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + var i = exprs.len; + while (i != 0) { + i -= 1; + const expr = *exprs.at(i); + try stack.push(RenderState { .Text = ",\n" }); + try stack.push(RenderState { .Expression = expr }); + try stack.push(RenderState.PrintIndent); + } + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "{\n"}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + }, + } + }, + ast.Node.Id.ControlFlowExpression => { + const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); - if (decls.len == 1) blk: { - const node = decls[0]; + if (flow_expr.rhs) |rhs| { + try stack.push(RenderState { .Expression = rhs }); + try stack.push(RenderState { .Text = " " }); + } - // if there are any doc comments or same line comments - // don't try to put it all on one line - if (node.same_line_comment != null) break :blk; - if (node.cast(ast.Node.ErrorTag)) |tag| { - if (tag.doc_comments != null) break :blk; - } else { - break :blk; + switch (flow_expr.kind) { + ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { + try stream.print("break"); + if (maybe_label) |label| { + try stream.print(" :"); + try stack.push(RenderState { .Expression = label }); + } + }, + ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { + try stream.print("continue"); + if (maybe_label) |label| { + try stream.print(" :"); + try stack.push(RenderState { .Expression = label }); } + }, + ast.Node.ControlFlowExpression.Kind.Return => { + try stream.print("return"); + }, + + } + }, + ast.Node.Id.Payload => { + const payload = @fieldParentPtr(ast.Node.Payload, "base", base); + try stack.push(RenderState { .Text = "|"}); + try stack.push(RenderState { .Expression = payload.error_symbol }); + try stack.push(RenderState { .Text = "|"}); + }, + ast.Node.Id.PointerPayload => { + const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); + try stack.push(RenderState { .Text = "|"}); + try stack.push(RenderState { .Expression = payload.value_symbol }); + + if (payload.ptr_token) |ptr_token| { + try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) }); + } + try stack.push(RenderState { .Text = "|"}); + }, + ast.Node.Id.PointerIndexPayload => { + const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); + try stack.push(RenderState { .Text = "|"}); + + if (payload.index_symbol) |index_symbol| { + try stack.push(RenderState { .Expression = index_symbol }); + try stack.push(RenderState { .Text = ", "}); + } + + try stack.push(RenderState { .Expression = payload.value_symbol }); + + if (payload.ptr_token) |ptr_token| { + try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) }); + } + + try stack.push(RenderState { .Text = "|"}); + }, + ast.Node.Id.GroupedExpression => { + const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); + try stack.push(RenderState { .Text = ")"}); + try stack.push(RenderState { .Expression = grouped_expr.expr }); + try stack.push(RenderState { .Text = "("}); + }, + ast.Node.Id.FieldInitializer => { + const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); + try stream.print(".{} = ", tree.tokenSlice(field_init.name_token)); + try stack.push(RenderState { .Expression = field_init.expr }); + }, + ast.Node.Id.IntegerLiteral => { + const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(integer_literal.token)); + }, + ast.Node.Id.FloatLiteral => { + const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(float_literal.token)); + }, + ast.Node.Id.StringLiteral => { + const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(string_literal.token)); + }, + ast.Node.Id.CharLiteral => { + const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(char_literal.token)); + }, + ast.Node.Id.BoolLiteral => { + const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(bool_literal.token)); + }, + ast.Node.Id.NullLiteral => { + const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(null_literal.token)); + }, + ast.Node.Id.ThisLiteral => { + const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(this_literal.token)); + }, + ast.Node.Id.Unreachable => { + const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); + try stream.print("{}", tree.tokenSlice(unreachable_node.token)); + }, + ast.Node.Id.ErrorType => { + const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); + try stream.print("{}", tree.tokenSlice(error_type.token)); + }, + ast.Node.Id.VarType => { + const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); + try stream.print("{}", tree.tokenSlice(var_type.token)); + }, + ast.Node.Id.ContainerDecl => { + const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); - try stream.write("error{"); - try stack.append(RenderState { .Text = "}" }); - try stack.append(RenderState { .TopLevelDecl = node }); - continue; - } + switch (container_decl.layout) { + ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "), + ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "), + ast.Node.ContainerDecl.Layout.Auto => { }, + } - try stream.write("error{"); + switch (container_decl.kind) { + ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"), + ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"), + ast.Node.ContainerDecl.Kind.Union => try stream.print("union"), + } - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n"}); + if (container_decl.fields_and_decls.len == 0) { + try stack.push(RenderState { .Text = "{}"}); + } else { + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = "\n"}); - var i = decls.len; + var i = container_decl.fields_and_decls.len; while (i != 0) { i -= 1; - const node = decls[i]; - if (node.id != ast.Node.Id.LineComment) { - try stack.append(RenderState { .Text = "," }); - } - try stack.append(RenderState { .TopLevelDecl = node }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { + const node = *container_decl.fields_and_decls.at(i); + try stack.push(RenderState { .TopLevelDecl = node}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = blk: { if (i != 0) { - const prev_node = decls[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); + const prev_node = *container_decl.fields_and_decls.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); if (loc.line >= 2) { break :blk "\n\n"; } @@ -4191,538 +4162,579 @@ pub const Parser = struct { }, }); } - try stack.append(RenderState { .Indent = indent + indent_delta}); - }, - ast.Node.Id.MultilineStringLiteral => { - const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); - try stream.print("\n"); - - var i : usize = 0; - while (i < multiline_str_literal.tokens.len) : (i += 1) { - const t = multiline_str_literal.tokens.at(i); - try stream.writeByteNTimes(' ', indent + indent_delta); - try stream.print("{}", self.tokenizer.getTokenSlice(t)); - } - try stream.writeByteNTimes(' ', indent); - }, - ast.Node.Id.UndefinedLiteral => { - const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(undefined_literal.token)); - }, - ast.Node.Id.BuiltinCall => { - const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); - try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token)); - try stack.append(RenderState { .Text = ")"}); - var i = builtin_call.params.len; - while (i != 0) { - i -= 1; - const param_node = builtin_call.params.at(i); - try stack.append(RenderState { .Expression = param_node}); - if (i != 0) { - try stack.append(RenderState { .Text = ", " }); + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { .Text = "{"}); + } + + switch (container_decl.init_arg_expr) { + ast.Node.ContainerDecl.InitArg.None => try stack.push(RenderState { .Text = " "}), + ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { + if (enum_tag_type) |expr| { + try stack.push(RenderState { .Text = ")) "}); + try stack.push(RenderState { .Expression = expr}); + try stack.push(RenderState { .Text = "(enum("}); + } else { + try stack.push(RenderState { .Text = "(enum) "}); } - } - }, - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); + }, + ast.Node.ContainerDecl.InitArg.Type => |type_expr| { + try stack.push(RenderState { .Text = ") "}); + try stack.push(RenderState { .Expression = type_expr}); + try stack.push(RenderState { .Text = "("}); + }, + } + }, + ast.Node.Id.ErrorSetDecl => { + const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); - switch (fn_proto.return_type) { - ast.Node.FnProto.ReturnType.Explicit => |node| { - try stack.append(RenderState { .Expression = node}); - }, - ast.Node.FnProto.ReturnType.InferErrorSet => |node| { - try stack.append(RenderState { .Expression = node}); - try stack.append(RenderState { .Text = "!"}); - }, - } + if (err_set_decl.decls.len == 0) { + try stream.write("error{}"); + continue; + } - if (fn_proto.align_expr) |align_expr| { - try stack.append(RenderState { .Text = ") " }); - try stack.append(RenderState { .Expression = align_expr}); - try stack.append(RenderState { .Text = "align(" }); - } + if (err_set_decl.decls.len == 1) blk: { + const node = *err_set_decl.decls.at(0); - try stack.append(RenderState { .Text = ") " }); - var i = fn_proto.params.len; - while (i != 0) { - i -= 1; - const param_decl_node = fn_proto.params.items[i]; - try stack.append(RenderState { .ParamDecl = param_decl_node}); - if (i != 0) { - try stack.append(RenderState { .Text = ", " }); - } + // if there are any doc comments or same line comments + // don't try to put it all on one line + if (node.cast(ast.Node.ErrorTag)) |tag| { + if (tag.doc_comments != null) break :blk; + } else { + break :blk; } - try stack.append(RenderState { .Text = "(" }); - if (fn_proto.name_token) |name_token| { - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(name_token) }); - try stack.append(RenderState { .Text = " " }); - } - try stack.append(RenderState { .Text = "fn" }); + try stream.write("error{"); + try stack.push(RenderState { .Text = "}" }); + try stack.push(RenderState { .TopLevelDecl = node }); + continue; + } - if (fn_proto.async_attr) |async_attr| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = &async_attr.base }); - } + try stream.write("error{"); - if (fn_proto.cc_token) |cc_token| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(cc_token) }); - } + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = "\n"}); - if (fn_proto.lib_name) |lib_name| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = lib_name }); + var i = err_set_decl.decls.len; + while (i != 0) { + i -= 1; + const node = *err_set_decl.decls.at(i); + if (node.id != ast.Node.Id.LineComment) { + try stack.push(RenderState { .Text = "," }); } - if (fn_proto.extern_export_inline_token) |extern_export_inline_token| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_export_inline_token) }); + try stack.push(RenderState { .TopLevelDecl = node }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + if (i != 0) { + const prev_node = *err_set_decl.decls.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + } + break :blk "\n"; + }, + }); + } + try stack.push(RenderState { .Indent = indent + indent_delta}); + }, + ast.Node.Id.MultilineStringLiteral => { + const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); + try stream.print("\n"); + + var i : usize = 0; + while (i < multiline_str_literal.lines.len) : (i += 1) { + const t = *multiline_str_literal.lines.at(i); + try stream.writeByteNTimes(' ', indent + indent_delta); + try stream.print("{}", tree.tokenSlice(t)); + } + try stream.writeByteNTimes(' ', indent); + }, + ast.Node.Id.UndefinedLiteral => { + const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(undefined_literal.token)); + }, + ast.Node.Id.BuiltinCall => { + const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); + try stream.print("{}(", tree.tokenSlice(builtin_call.builtin_token)); + try stack.push(RenderState { .Text = ")"}); + var i = builtin_call.params.len; + while (i != 0) { + i -= 1; + const param_node = *builtin_call.params.at(i); + try stack.push(RenderState { .Expression = param_node}); + if (i != 0) { + try stack.push(RenderState { .Text = ", " }); } + } + }, + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); - if (fn_proto.visib_token) |visib_token| { - assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export); - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) }); - } - }, - ast.Node.Id.PromiseType => { - const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base); - try stream.write(self.tokenizer.getTokenSlice(promise_type.promise_token)); - if (promise_type.result) |result| { - try stream.write(self.tokenizer.getTokenSlice(result.arrow_token)); - try stack.append(RenderState { .Expression = result.return_type}); - } - }, - ast.Node.Id.LineComment => { - const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base); - try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token)); - }, - ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes - ast.Node.Id.Switch => { - const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); - const cases = switch_node.cases.toSliceConst(); + switch (fn_proto.return_type) { + ast.Node.FnProto.ReturnType.Explicit => |node| { + try stack.push(RenderState { .Expression = node}); + }, + ast.Node.FnProto.ReturnType.InferErrorSet => |node| { + try stack.push(RenderState { .Expression = node}); + try stack.push(RenderState { .Text = "!"}); + }, + } - try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token)); + if (fn_proto.align_expr) |align_expr| { + try stack.push(RenderState { .Text = ") " }); + try stack.push(RenderState { .Expression = align_expr}); + try stack.push(RenderState { .Text = "align(" }); + } - if (cases.len == 0) { - try stack.append(RenderState { .Text = ") {}"}); - try stack.append(RenderState { .Expression = switch_node.expr }); - continue; + try stack.push(RenderState { .Text = ") " }); + var i = fn_proto.params.len; + while (i != 0) { + i -= 1; + const param_decl_node = *fn_proto.params.at(i); + try stack.push(RenderState { .ParamDecl = param_decl_node}); + if (i != 0) { + try stack.push(RenderState { .Text = ", " }); } + } - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n"}); + try stack.push(RenderState { .Text = "(" }); + if (fn_proto.name_token) |name_token| { + try stack.push(RenderState { .Text = tree.tokenSlice(name_token) }); + try stack.push(RenderState { .Text = " " }); + } - var i = cases.len; - while (i != 0) { - i -= 1; - const node = cases[i]; - try stack.append(RenderState { .Expression = node}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = cases[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - } - break :blk "\n"; - }, - }); - } - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = ") {"}); - try stack.append(RenderState { .Expression = switch_node.expr }); - }, - ast.Node.Id.SwitchCase => { - const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); - - try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment }); - try stack.append(RenderState { .Text = "," }); - try stack.append(RenderState { .Expression = switch_case.expr }); - if (switch_case.payload) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); - } - try stack.append(RenderState { .Text = " => "}); + try stack.push(RenderState { .Text = "fn" }); - const items = switch_case.items.toSliceConst(); - var i = items.len; - while (i != 0) { - i -= 1; - try stack.append(RenderState { .Expression = items[i] }); + if (fn_proto.async_attr) |async_attr| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = &async_attr.base }); + } - if (i != 0) { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Text = ",\n" }); - } - } - }, - ast.Node.Id.SwitchElse => { - const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token)); - }, - ast.Node.Id.Else => { - const else_node = @fieldParentPtr(ast.Node.Else, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(else_node.else_token)); - - switch (else_node.body.id) { - ast.Node.Id.Block, ast.Node.Id.If, - ast.Node.Id.For, ast.Node.Id.While, - ast.Node.Id.Switch => { - try stream.print(" "); - try stack.append(RenderState { .Expression = else_node.body }); + if (fn_proto.cc_token) |cc_token| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(cc_token) }); + } + + if (fn_proto.lib_name) |lib_name| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = lib_name }); + } + if (fn_proto.extern_export_inline_token) |extern_export_inline_token| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) }); + } + + if (fn_proto.visib_token) |visib_token_index| { + const visib_token = tree.tokens.at(visib_token_index); + assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export); + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(visib_token_index) }); + } + }, + ast.Node.Id.PromiseType => { + const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base); + try stream.write(tree.tokenSlice(promise_type.promise_token)); + if (promise_type.result) |result| { + try stream.write(tree.tokenSlice(result.arrow_token)); + try stack.push(RenderState { .Expression = result.return_type}); + } + }, + ast.Node.Id.LineComment => { + const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base); + try stream.write(tree.tokenSlice(line_comment_node.token)); + }, + ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes + ast.Node.Id.Switch => { + const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); + + try stream.print("{} (", tree.tokenSlice(switch_node.switch_token)); + + if (switch_node.cases.len == 0) { + try stack.push(RenderState { .Text = ") {}"}); + try stack.push(RenderState { .Expression = switch_node.expr }); + continue; + } + + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = "\n"}); + + var i = switch_node.cases.len; + while (i != 0) { + i -= 1; + const node = *switch_node.cases.at(i); + try stack.push(RenderState { .Expression = node}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + if (i != 0) { + const prev_node = *switch_node.cases.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + } + break :blk "\n"; }, - else => { - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Expression = else_node.body }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); - } - } + }); + } + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { .Text = ") {"}); + try stack.push(RenderState { .Expression = switch_node.expr }); + }, + ast.Node.Id.SwitchCase => { + const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); + + try stack.push(RenderState { .Text = "," }); + try stack.push(RenderState { .Expression = switch_case.expr }); + if (switch_case.payload) |payload| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); + } + try stack.push(RenderState { .Text = " => "}); + + var i = switch_case.items.len; + while (i != 0) { + i -= 1; + try stack.push(RenderState { .Expression = *switch_case.items.at(i) }); - if (else_node.payload) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); + if (i != 0) { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = ",\n" }); } - }, - ast.Node.Id.While => { - const while_node = @fieldParentPtr(ast.Node.While, "base", base); - if (while_node.label) |label| { - try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); + } + }, + ast.Node.Id.SwitchElse => { + const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); + try stream.print("{}", tree.tokenSlice(switch_else.token)); + }, + ast.Node.Id.Else => { + const else_node = @fieldParentPtr(ast.Node.Else, "base", base); + try stream.print("{}", tree.tokenSlice(else_node.else_token)); + + switch (else_node.body.id) { + ast.Node.Id.Block, ast.Node.Id.If, + ast.Node.Id.For, ast.Node.Id.While, + ast.Node.Id.Switch => { + try stream.print(" "); + try stack.push(RenderState { .Expression = else_node.body }); + }, + else => { + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Expression = else_node.body }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "\n" }); } + } - if (while_node.inline_token) |inline_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(inline_token)); - } + if (else_node.payload) |payload| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); + } + }, + ast.Node.Id.While => { + const while_node = @fieldParentPtr(ast.Node.While, "base", base); + if (while_node.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } - try stream.print("{} ", self.tokenizer.getTokenSlice(while_node.while_token)); + if (while_node.inline_token) |inline_token| { + try stream.print("{} ", tree.tokenSlice(inline_token)); + } - if (while_node.@"else") |@"else"| { - try stack.append(RenderState { .Expression = &@"else".base }); + try stream.print("{} ", tree.tokenSlice(while_node.while_token)); - if (while_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Text = " " }); - } else { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Text = "\n" }); - } - } + if (while_node.@"else") |@"else"| { + try stack.push(RenderState { .Expression = &@"else".base }); if (while_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Expression = while_node.body }); - try stack.append(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = " " }); } else { - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Expression = while_node.body }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = "\n" }); } + } - if (while_node.continue_expr) |continue_expr| { - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = continue_expr }); - try stack.append(RenderState { .Text = ": (" }); - try stack.append(RenderState { .Text = " " }); - } + if (while_node.body.id == ast.Node.Id.Block) { + try stack.push(RenderState { .Expression = while_node.body }); + try stack.push(RenderState { .Text = " " }); + } else { + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Expression = while_node.body }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "\n" }); + } - if (while_node.payload) |payload| { - try stack.append(RenderState { .Expression = payload }); - try stack.append(RenderState { .Text = " " }); - } + if (while_node.continue_expr) |continue_expr| { + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = continue_expr }); + try stack.push(RenderState { .Text = ": (" }); + try stack.push(RenderState { .Text = " " }); + } - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = while_node.condition }); - try stack.append(RenderState { .Text = "(" }); - }, - ast.Node.Id.For => { - const for_node = @fieldParentPtr(ast.Node.For, "base", base); - if (for_node.label) |label| { - try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); - } + if (while_node.payload) |payload| { + try stack.push(RenderState { .Expression = payload }); + try stack.push(RenderState { .Text = " " }); + } - if (for_node.inline_token) |inline_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(inline_token)); - } + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = while_node.condition }); + try stack.push(RenderState { .Text = "(" }); + }, + ast.Node.Id.For => { + const for_node = @fieldParentPtr(ast.Node.For, "base", base); + if (for_node.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } - try stream.print("{} ", self.tokenizer.getTokenSlice(for_node.for_token)); + if (for_node.inline_token) |inline_token| { + try stream.print("{} ", tree.tokenSlice(inline_token)); + } - if (for_node.@"else") |@"else"| { - try stack.append(RenderState { .Expression = &@"else".base }); + try stream.print("{} ", tree.tokenSlice(for_node.for_token)); - if (for_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Text = " " }); - } else { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Text = "\n" }); - } - } + if (for_node.@"else") |@"else"| { + try stack.push(RenderState { .Expression = &@"else".base }); if (for_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Expression = for_node.body }); - try stack.append(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = " " }); } else { - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Expression = for_node.body }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); - } - - if (for_node.payload) |payload| { - try stack.append(RenderState { .Expression = payload }); - try stack.append(RenderState { .Text = " " }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = "\n" }); } + } - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = for_node.array_expr }); - try stack.append(RenderState { .Text = "(" }); - }, - ast.Node.Id.If => { - const if_node = @fieldParentPtr(ast.Node.If, "base", base); - try stream.print("{} ", self.tokenizer.getTokenSlice(if_node.if_token)); - - switch (if_node.body.id) { - ast.Node.Id.Block, ast.Node.Id.If, - ast.Node.Id.For, ast.Node.Id.While, - ast.Node.Id.Switch => { - if (if_node.@"else") |@"else"| { - try stack.append(RenderState { .Expression = &@"else".base }); - - if (if_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Text = " " }); - } else { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Text = "\n" }); - } - } - }, - else => { - if (if_node.@"else") |@"else"| { - try stack.append(RenderState { .Expression = @"else".body }); + if (for_node.body.id == ast.Node.Id.Block) { + try stack.push(RenderState { .Expression = for_node.body }); + try stack.push(RenderState { .Text = " " }); + } else { + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Expression = for_node.body }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "\n" }); + } - if (@"else".payload) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); - } + if (for_node.payload) |payload| { + try stack.push(RenderState { .Expression = payload }); + try stack.push(RenderState { .Text = " " }); + } - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(@"else".else_token) }); - try stack.append(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = for_node.array_expr }); + try stack.push(RenderState { .Text = "(" }); + }, + ast.Node.Id.If => { + const if_node = @fieldParentPtr(ast.Node.If, "base", base); + try stream.print("{} ", tree.tokenSlice(if_node.if_token)); + + switch (if_node.body.id) { + ast.Node.Id.Block, ast.Node.Id.If, + ast.Node.Id.For, ast.Node.Id.While, + ast.Node.Id.Switch => { + if (if_node.@"else") |@"else"| { + try stack.push(RenderState { .Expression = &@"else".base }); + + if (if_node.body.id == ast.Node.Id.Block) { + try stack.push(RenderState { .Text = " " }); + } else { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = "\n" }); } } - } + }, + else => { + if (if_node.@"else") |@"else"| { + try stack.push(RenderState { .Expression = @"else".body }); - if (if_node.condition.same_line_comment) |comment| { - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Expression = if_node.body }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); - try stack.append(RenderState { .PrintLineComment = comment }); - } else { - try stack.append(RenderState { .Expression = if_node.body }); - } + if (@"else".payload) |payload| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); + } + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(@"else".else_token) }); + try stack.push(RenderState { .Text = " " }); + } + } + } - try stack.append(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = if_node.body }); + try stack.push(RenderState { .Text = " " }); - if (if_node.payload) |payload| { - try stack.append(RenderState { .Expression = payload }); - try stack.append(RenderState { .Text = " " }); - } + if (if_node.payload) |payload| { + try stack.push(RenderState { .Expression = payload }); + try stack.push(RenderState { .Text = " " }); + } - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = if_node.condition }); - try stack.append(RenderState { .Text = "(" }); - }, - ast.Node.Id.Asm => { - const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); - try stream.print("{} ", self.tokenizer.getTokenSlice(asm_node.asm_token)); + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = if_node.condition }); + try stack.push(RenderState { .Text = "(" }); + }, + ast.Node.Id.Asm => { + const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); + try stream.print("{} ", tree.tokenSlice(asm_node.asm_token)); - if (asm_node.volatile_token) |volatile_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(volatile_token)); - } + if (asm_node.volatile_token) |volatile_token| { + try stream.print("{} ", tree.tokenSlice(volatile_token)); + } - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = ")" }); - { - const cloppers = asm_node.cloppers.toSliceConst(); - var i = cloppers.len; - while (i != 0) { - i -= 1; - try stack.append(RenderState { .Expression = cloppers[i] }); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = ")" }); + { + var i = asm_node.clobbers.len; + while (i != 0) { + i -= 1; + try stack.push(RenderState { .Expression = *asm_node.clobbers.at(i) }); - if (i != 0) { - try stack.append(RenderState { .Text = ", " }); - } + if (i != 0) { + try stack.push(RenderState { .Text = ", " }); } } - try stack.append(RenderState { .Text = ": " }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); - { - const inputs = asm_node.inputs.toSliceConst(); - var i = inputs.len; - while (i != 0) { - i -= 1; - const node = inputs[i]; - try stack.append(RenderState { .Expression = &node.base}); + } + try stack.push(RenderState { .Text = ": " }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "\n" }); + { + var i = asm_node.inputs.len; + while (i != 0) { + i -= 1; + const node = *asm_node.inputs.at(i); + try stack.push(RenderState { .Expression = &node.base}); - if (i != 0) { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - const prev_node = inputs[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }, - }); - try stack.append(RenderState { .Text = "," }); - } + if (i != 0) { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + const prev_node = *asm_node.inputs.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + break :blk "\n"; + }, + }); + try stack.push(RenderState { .Text = "," }); } } - try stack.append(RenderState { .Indent = indent + indent_delta + 2}); - try stack.append(RenderState { .Text = ": "}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = "\n" }); - { - const outputs = asm_node.outputs.toSliceConst(); - var i = outputs.len; - while (i != 0) { - i -= 1; - const node = outputs[i]; - try stack.append(RenderState { .Expression = &node.base}); + } + try stack.push(RenderState { .Indent = indent + indent_delta + 2}); + try stack.push(RenderState { .Text = ": "}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { .Text = "\n" }); + { + var i = asm_node.outputs.len; + while (i != 0) { + i -= 1; + const node = *asm_node.outputs.at(i); + try stack.push(RenderState { .Expression = &node.base}); - if (i != 0) { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - const prev_node = outputs[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }, - }); - try stack.append(RenderState { .Text = "," }); - } + if (i != 0) { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + const prev_node = *asm_node.outputs.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + break :blk "\n"; + }, + }); + try stack.push(RenderState { .Text = "," }); } } - try stack.append(RenderState { .Indent = indent + indent_delta + 2}); - try stack.append(RenderState { .Text = ": "}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = "\n" }); - try stack.append(RenderState { .Expression = asm_node.template }); - try stack.append(RenderState { .Text = "(" }); - }, - ast.Node.Id.AsmInput => { - const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); - - try stack.append(RenderState { .Text = ")"}); - try stack.append(RenderState { .Expression = asm_input.expr}); - try stack.append(RenderState { .Text = " ("}); - try stack.append(RenderState { .Expression = asm_input.constraint }); - try stack.append(RenderState { .Text = "] "}); - try stack.append(RenderState { .Expression = asm_input.symbolic_name }); - try stack.append(RenderState { .Text = "["}); - }, - ast.Node.Id.AsmOutput => { - const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); - - try stack.append(RenderState { .Text = ")"}); - switch (asm_output.kind) { - ast.Node.AsmOutput.Kind.Variable => |variable_name| { - try stack.append(RenderState { .Expression = &variable_name.base}); - }, - ast.Node.AsmOutput.Kind.Return => |return_type| { - try stack.append(RenderState { .Expression = return_type}); - try stack.append(RenderState { .Text = "-> "}); - }, - } - try stack.append(RenderState { .Text = " ("}); - try stack.append(RenderState { .Expression = asm_output.constraint }); - try stack.append(RenderState { .Text = "] "}); - try stack.append(RenderState { .Expression = asm_output.symbolic_name }); - try stack.append(RenderState { .Text = "["}); - }, - - ast.Node.Id.StructField, - ast.Node.Id.UnionTag, - ast.Node.Id.EnumTag, - ast.Node.Id.ErrorTag, - ast.Node.Id.Root, - ast.Node.Id.VarDecl, - ast.Node.Id.Use, - ast.Node.Id.TestDecl, - ast.Node.Id.ParamDecl => unreachable, + } + try stack.push(RenderState { .Indent = indent + indent_delta + 2}); + try stack.push(RenderState { .Text = ": "}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { .Text = "\n" }); + try stack.push(RenderState { .Expression = asm_node.template }); + try stack.push(RenderState { .Text = "(" }); }, - RenderState.Statement => |base| { - try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment } ); - switch (base.id) { - ast.Node.Id.VarDecl => { - const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); - try stack.append(RenderState { .VarDecl = var_decl}); + ast.Node.Id.AsmInput => { + const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); + + try stack.push(RenderState { .Text = ")"}); + try stack.push(RenderState { .Expression = asm_input.expr}); + try stack.push(RenderState { .Text = " ("}); + try stack.push(RenderState { .Expression = asm_input.constraint }); + try stack.push(RenderState { .Text = "] "}); + try stack.push(RenderState { .Expression = asm_input.symbolic_name }); + try stack.push(RenderState { .Text = "["}); + }, + ast.Node.Id.AsmOutput => { + const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); + + try stack.push(RenderState { .Text = ")"}); + switch (asm_output.kind) { + ast.Node.AsmOutput.Kind.Variable => |variable_name| { + try stack.push(RenderState { .Expression = &variable_name.base}); }, - else => { - if (requireSemiColon(base)) { - try stack.append(RenderState { .Text = ";" }); - } - try stack.append(RenderState { .Expression = base }); + ast.Node.AsmOutput.Kind.Return => |return_type| { + try stack.push(RenderState { .Expression = return_type}); + try stack.push(RenderState { .Text = "-> "}); }, } + try stack.push(RenderState { .Text = " ("}); + try stack.push(RenderState { .Expression = asm_output.constraint }); + try stack.push(RenderState { .Text = "] "}); + try stack.push(RenderState { .Expression = asm_output.symbolic_name }); + try stack.push(RenderState { .Text = "["}); }, - RenderState.Indent => |new_indent| indent = new_indent, - RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent), - RenderState.PrintSameLineComment => |maybe_comment| blk: { - const comment_token = maybe_comment ?? break :blk; - try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token)); - }, - RenderState.PrintLineComment => |comment_token| { - try stream.write(self.tokenizer.getTokenSlice(comment_token)); - }, - } - } - } - fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void { - const comment = node.doc_comments ?? return; - for (comment.lines.toSliceConst()) |line_token| { - try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token)); - try stream.writeByteNTimes(' ', indent); + ast.Node.Id.StructField, + ast.Node.Id.UnionTag, + ast.Node.Id.EnumTag, + ast.Node.Id.ErrorTag, + ast.Node.Id.Root, + ast.Node.Id.VarDecl, + ast.Node.Id.Use, + ast.Node.Id.TestDecl, + ast.Node.Id.ParamDecl => unreachable, + }, + RenderState.Statement => |base| { + switch (base.id) { + ast.Node.Id.VarDecl => { + const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); + try stack.push(RenderState { .VarDecl = var_decl}); + }, + else => { + if (requireSemiColon(base)) { + try stack.push(RenderState { .Text = ";" }); + } + try stack.push(RenderState { .Expression = base }); + }, + } + }, + RenderState.Indent => |new_indent| indent = new_indent, + RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent), } } +} - fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) { - const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T); - self.utility_bytes = self.util_allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count); - const typed_slice = ([]T)(self.utility_bytes); - return ArrayList(T) { - .allocator = self.util_allocator, - .items = typed_slice, - .len = 0, - }; - } - - fn deinitUtilityArrayList(self: &Parser, list: var) void { - self.utility_bytes = ([]align(utility_bytes_align) u8)(list.items); +fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) !void { + const comment = node.doc_comments ?? return; + var it = comment.lines.iterator(0); + while (it.next()) |line_token_index| { + try stream.print("{}\n", tree.tokenSlice(*line_token_index)); + try stream.writeByteNTimes(' ', indent); } - -}; +} test "std.zig.parser" { _ = @import("parser_test.zig"); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index e1d75d8380..dd20a6dd8e 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,14 +1,12 @@ -test "zig fmt: same-line comment after non-block if expression" { - try testCanonical( - \\comptime { - \\ if (sr > n_uword_bits - 1) { - \\ // d > r - \\ return 0; - \\ } - \\} - \\ - ); -} +//test "zig fmt: same-line comment after non-block if expression" { +// try testCanonical( +// \\comptime { +// \\ if (sr > n_uword_bits - 1) // d > r +// \\ return 0; +// \\} +// \\ +// ); +//} test "zig fmt: switch with empty body" { try testCanonical( @@ -19,14 +17,14 @@ test "zig fmt: switch with empty body" { ); } -test "zig fmt: same-line comment on comptime expression" { - try testCanonical( - \\test "" { - \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt - \\} - \\ - ); -} +//test "zig fmt: same-line comment on comptime expression" { +// try testCanonical( +// \\test "" { +// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt +// \\} +// \\ +// ); +//} test "zig fmt: float literal with exponent" { try testCanonical( @@ -154,17 +152,17 @@ test "zig fmt: comments before switch prong" { ); } -test "zig fmt: same-line comment after switch prong" { - try testCanonical( - \\test "" { - \\ switch (err) { - \\ error.PathAlreadyExists => {}, // comment 2 - \\ else => return err, // comment 1 - \\ } - \\} - \\ - ); -} +//test "zig fmt: same-line comment after switch prong" { +// try testCanonical( +// \\test "" { +// \\ switch (err) { +// \\ error.PathAlreadyExists => {}, // comment 2 +// \\ else => return err, // comment 1 +// \\ } +// \\} +// \\ +// ); +//} test "zig fmt: comments before var decl in struct" { try testCanonical( @@ -191,27 +189,27 @@ test "zig fmt: comments before var decl in struct" { ); } -test "zig fmt: same-line comment after var decl in struct" { - try testCanonical( - \\pub const vfs_cap_data = extern struct { - \\ const Data = struct {}; // when on disk. - \\}; - \\ - ); -} - -test "zig fmt: same-line comment after field decl" { - try testCanonical( - \\pub const dirent = extern struct { - \\ d_name: u8, - \\ d_name: u8, // comment 1 - \\ d_name: u8, - \\ d_name: u8, // comment 2 - \\ d_name: u8, - \\}; - \\ - ); -} +//test "zig fmt: same-line comment after var decl in struct" { +// try testCanonical( +// \\pub const vfs_cap_data = extern struct { +// \\ const Data = struct {}; // when on disk. +// \\}; +// \\ +// ); +//} +// +//test "zig fmt: same-line comment after field decl" { +// try testCanonical( +// \\pub const dirent = extern struct { +// \\ d_name: u8, +// \\ d_name: u8, // comment 1 +// \\ d_name: u8, +// \\ d_name: u8, // comment 2 +// \\ d_name: u8, +// \\}; +// \\ +// ); +//} test "zig fmt: array literal with 1 item on 1 line" { try testCanonical( @@ -220,16 +218,16 @@ test "zig fmt: array literal with 1 item on 1 line" { ); } -test "zig fmt: same-line comment after a statement" { - try testCanonical( - \\test "" { - \\ a = b; - \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption - \\ a = b; - \\} - \\ - ); -} +//test "zig fmt: same-line comment after a statement" { +// try testCanonical( +// \\test "" { +// \\ a = b; +// \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption +// \\ a = b; +// \\} +// \\ +// ); +//} test "zig fmt: comments before global variables" { try testCanonical( @@ -1094,25 +1092,48 @@ test "zig fmt: error return" { const std = @import("std"); const mem = std.mem; const warn = std.debug.warn; -const Tokenizer = std.zig.Tokenizer; -const Parser = std.zig.Parser; const io = std.io; var fixed_buffer_mem: [100 * 1024]u8 = undefined; fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { - var tokenizer = Tokenizer.init(source); - var parser = Parser.init(&tokenizer, allocator, "(memory buffer)"); - defer parser.deinit(); + var stderr_file = try io.getStdErr(); + var stderr = &io.FileOutStream.init(&stderr_file).stream; - var tree = try parser.parse(); + var tree = try std.zig.parse(allocator, source); defer tree.deinit(); + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + const token = tree.tokens.at(parse_error.loc()); + const loc = tree.tokenLocation(0, parse_error.loc()); + try stderr.print("(memory buffer):{}:{}: error: ", loc.line + 1, loc.column + 1); + try tree.renderError(parse_error, stderr); + try stderr.print("\n{}\n", source[loc.line_start..loc.line_end]); + { + var i: usize = 0; + while (i < loc.column) : (i += 1) { + try stderr.write(" "); + } + } + { + const caret_count = token.end - token.start; + var i: usize = 0; + while (i < caret_count) : (i += 1) { + try stderr.write("~"); + } + } + try stderr.write("\n"); + } + if (tree.errors.len != 0) { + return error.ParseError; + } + var buffer = try std.Buffer.initSize(allocator, 0); errdefer buffer.deinit(); var buffer_out_stream = io.BufferOutStream.init(&buffer); - try parser.renderSource(&buffer_out_stream.stream, tree.root_node); + try std.zig.render(allocator, &buffer_out_stream.stream, &tree); return buffer.toOwnedSlice(); } @@ -1151,6 +1172,7 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { } }, error.ParseError => @panic("test failed"), + else => @panic("test failed"), } } } diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 31dc06b695..b0e5014a1a 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -195,37 +195,6 @@ pub const Tokenizer = struct { index: usize, pending_invalid_token: ?Token, - pub const Location = struct { - line: usize, - column: usize, - line_start: usize, - line_end: usize, - }; - - pub fn getTokenLocation(self: &Tokenizer, start_index: usize, token: &const Token) Location { - var loc = Location { - .line = 0, - .column = 0, - .line_start = start_index, - .line_end = self.buffer.len, - }; - for (self.buffer[start_index..]) |c, i| { - if (i + start_index == token.start) { - loc.line_end = i + start_index; - while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {} - return loc; - } - if (c == '\n') { - loc.line += 1; - loc.column = 0; - loc.line_start = i + 1; - } else { - loc.column += 1; - } - } - return loc; - } - /// For debugging purposes pub fn dump(self: &Tokenizer, token: &const Token) void { std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]); @@ -1047,10 +1016,6 @@ pub const Tokenizer = struct { return result; } - pub fn getTokenSlice(self: &const Tokenizer, token: &const Token) []const u8 { - return self.buffer[token.start..token.end]; - } - fn checkLiteralCharacter(self: &Tokenizer) void { if (self.pending_invalid_token != null) return; const invalid_length = self.getInvalidCharacterLength(); -- cgit v1.2.3 From 0cb65b266aa20015f068e0460c74eb75a0b7f65c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 May 2018 22:07:50 -0400 Subject: separate std.zig.parse and std.zig.render --- CMakeLists.txt | 3 +- std/zig/ast.zig | 74 + std/zig/index.zig | 9 +- std/zig/parse.zig | 3432 +++++++++++++++++++++++++++++++++++++ std/zig/parser.zig | 4741 ---------------------------------------------------- std/zig/render.zig | 1241 ++++++++++++++ 6 files changed, 4754 insertions(+), 4746 deletions(-) create mode 100644 std/zig/parse.zig delete mode 100644 std/zig/parser.zig create mode 100644 std/zig/render.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index d435092723..0aad51c7bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -576,7 +576,8 @@ set(ZIG_STD_FILES "unicode.zig" "zig/ast.zig" "zig/index.zig" - "zig/parser.zig" + "zig/parse.zig" + "zig/render.zig" "zig/tokenizer.zig" ) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 664ab25a28..618b9155c2 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -336,6 +336,80 @@ pub const Node = struct { unreachable; } + pub fn requireSemiColon(base: &const Node) bool { + var n = base; + while (true) { + switch (n.id) { + Id.Root, + Id.StructField, + Id.UnionTag, + Id.EnumTag, + Id.ParamDecl, + Id.Block, + Id.Payload, + Id.PointerPayload, + Id.PointerIndexPayload, + Id.Switch, + Id.SwitchCase, + Id.SwitchElse, + Id.FieldInitializer, + Id.DocComment, + Id.LineComment, + Id.TestDecl => return false, + Id.While => { + const while_node = @fieldParentPtr(While, "base", n); + if (while_node.@"else") |@"else"| { + n = @"else".base; + continue; + } + + return while_node.body.id != Id.Block; + }, + Id.For => { + const for_node = @fieldParentPtr(For, "base", n); + if (for_node.@"else") |@"else"| { + n = @"else".base; + continue; + } + + return for_node.body.id != Id.Block; + }, + Id.If => { + const if_node = @fieldParentPtr(If, "base", n); + if (if_node.@"else") |@"else"| { + n = @"else".base; + continue; + } + + return if_node.body.id != Id.Block; + }, + Id.Else => { + const else_node = @fieldParentPtr(Else, "base", n); + n = else_node.body; + continue; + }, + Id.Defer => { + const defer_node = @fieldParentPtr(Defer, "base", n); + return defer_node.expr.id != Id.Block; + }, + Id.Comptime => { + const comptime_node = @fieldParentPtr(Comptime, "base", n); + return comptime_node.expr.id != Id.Block; + }, + Id.Suspend => { + const suspend_node = @fieldParentPtr(Suspend, "base", n); + if (suspend_node.body) |body| { + return body.id != Id.Block; + } + + return true; + }, + else => return true, + } + } + } + + pub const Root = struct { base: Node, doc_comments: ?&DocComment, diff --git a/std/zig/index.zig b/std/zig/index.zig index 42965f3710..4dd68fa8b3 100644 --- a/std/zig/index.zig +++ b/std/zig/index.zig @@ -1,12 +1,13 @@ const tokenizer = @import("tokenizer.zig"); pub const Token = tokenizer.Token; pub const Tokenizer = tokenizer.Tokenizer; -pub const parse = @import("parser.zig").parse; -pub const render = @import("parser.zig").renderSource; +pub const parse = @import("parse.zig").parse; +pub const render = @import("render.zig").render; pub const ast = @import("ast.zig"); test "std.zig tests" { - _ = @import("tokenizer.zig"); - _ = @import("parser.zig"); _ = @import("ast.zig"); + _ = @import("parse.zig"); + _ = @import("render.zig"); + _ = @import("tokenizer.zig"); } diff --git a/std/zig/parse.zig b/std/zig/parse.zig new file mode 100644 index 0000000000..f6c56cb7d0 --- /dev/null +++ b/std/zig/parse.zig @@ -0,0 +1,3432 @@ +const std = @import("../index.zig"); +const assert = std.debug.assert; +const SegmentedList = std.SegmentedList; +const mem = std.mem; +const ast = std.zig.ast; +const Tokenizer = std.zig.Tokenizer; +const Token = std.zig.Token; +const TokenIndex = ast.TokenIndex; +const Error = ast.Error; + +/// Returns an AST tree, allocated with the parser's allocator. +/// Result should be freed with tree.deinit() when there are +/// no more references to any AST nodes of the tree. +pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { + var tree_arena = std.heap.ArenaAllocator.init(allocator); + errdefer tree_arena.deinit(); + + var stack = SegmentedList(State, 32).init(allocator); + defer stack.deinit(); + + const arena = &tree_arena.allocator; + const root_node = try createNode(arena, ast.Node.Root, + ast.Node.Root { + .base = undefined, + .decls = ast.Node.Root.DeclList.init(arena), + .doc_comments = null, + // initialized when we get the eof token + .eof_token = undefined, + } + ); + + var tree = ast.Tree { + .source = source, + .root_node = root_node, + .arena_allocator = tree_arena, + .tokens = ast.Tree.TokenList.init(arena), + .errors = ast.Tree.ErrorList.init(arena), + }; + + var tokenizer = Tokenizer.init(tree.source); + while (true) { + const token_ptr = try tree.tokens.addOne(); + *token_ptr = tokenizer.next(); + if (token_ptr.id == Token.Id.Eof) + break; + } + var tok_it = tree.tokens.iterator(0); + + try stack.push(State.TopLevel); + + while (true) { + // This gives us 1 free push that can't fail + const state = ??stack.pop(); + + switch (state) { + State.TopLevel => { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try root_node.decls.push(&line_comment.base); + } + + const comments = try eatDocComments(arena, &tok_it); + + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_test => { + stack.push(State.TopLevel) catch unreachable; + + const block = try arena.construct(ast.Node.Block { + .base = ast.Node { + .id = ast.Node.Id.Block, + }, + .label = null, + .lbrace = undefined, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + const test_node = try arena.construct(ast.Node.TestDecl { + .base = ast.Node { + .id = ast.Node.Id.TestDecl, + }, + .doc_comments = comments, + .test_token = token_index, + .name = undefined, + .body_node = &block.base, + }); + try root_node.decls.push(&test_node.base); + try stack.push(State { .Block = block }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.LBrace, + .ptr = &block.rbrace, + } + }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } }); + continue; + }, + Token.Id.Eof => { + root_node.eof_token = token_index; + root_node.doc_comments = comments; + return tree; + }, + Token.Id.Keyword_pub => { + stack.push(State.TopLevel) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &root_node.decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; + }, + Token.Id.Keyword_comptime => { + const block = try createNode(arena, ast.Node.Block, + ast.Node.Block { + .base = undefined, + .label = null, + .lbrace = undefined, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + } + ); + const node = try arena.construct(ast.Node.Comptime { + .base = ast.Node { + .id = ast.Node.Id.Comptime, + }, + .comptime_token = token_index, + .expr = &block.base, + .doc_comments = comments, + }); + try root_node.decls.push(&node.base); + + stack.push(State.TopLevel) catch unreachable; + try stack.push(State { .Block = block }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.LBrace, + .ptr = &block.rbrace, + } + }); + continue; + }, + else => { + _ = tok_it.prev(); + stack.push(State.TopLevel) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &root_node.decls, + .visib_token = null, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; + }, + } + }, + State.TopLevelExtern => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_export, Token.Id.Keyword_inline => { + stack.push(State { + .TopLevelDecl = TopLevelDeclCtx { + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = AnnotatedToken { + .index = token_index, + .ptr = token_ptr, + }, + .lib_name = null, + .comments = ctx.comments, + }, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_extern => { + stack.push(State { + .TopLevelLibname = TopLevelDeclCtx { + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = AnnotatedToken { + .index = token_index, + .ptr = token_ptr, + }, + .lib_name = null, + .comments = ctx.comments, + }, + }) catch unreachable; + continue; + }, + else => { + _ = tok_it.prev(); + stack.push(State { .TopLevelDecl = ctx }) catch unreachable; + continue; + } + } + }, + State.TopLevelLibname => |ctx| { + const lib_name = blk: { + const lib_name_token_index = tok_it.index; + const lib_name_token_ptr = ??tok_it.next(); + break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index)) ?? { + _ = tok_it.prev(); + break :blk null; + }; + }; + + stack.push(State { + .TopLevelDecl = TopLevelDeclCtx { + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = ctx.extern_export_inline_token, + .lib_name = lib_name, + .comments = ctx.comments, + }, + }) catch unreachable; + continue; + }, + State.TopLevelDecl => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_use => { + if (ctx.extern_export_inline_token) |annotated_token| { + *(try tree.errors.addOne()) = Error { + .InvalidToken = Error.InvalidToken { .token = annotated_token.index }, + }; + return tree; + } + + const node = try arena.construct(ast.Node.Use { + .base = ast.Node {.id = ast.Node.Id.Use }, + .visib_token = ctx.visib_token, + .expr = undefined, + .semicolon_token = undefined, + .doc_comments = ctx.comments, + }); + try ctx.decls.push(&node.base); + + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Semicolon, + .ptr = &node.semicolon_token, + } + }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + continue; + }, + Token.Id.Keyword_var, Token.Id.Keyword_const => { + if (ctx.extern_export_inline_token) |annotated_token| { + if (annotated_token.ptr.id == Token.Id.Keyword_inline) { + *(try tree.errors.addOne()) = Error { + .InvalidToken = Error.InvalidToken { .token = annotated_token.index }, + }; + return tree; + } + } + + try stack.push(State { + .VarDecl = VarDeclCtx { + .comments = ctx.comments, + .visib_token = ctx.visib_token, + .lib_name = ctx.lib_name, + .comptime_token = null, + .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null, + .mut_token = token_index, + .list = ctx.decls + } + }); + continue; + }, + Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, + Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, + }, + .doc_comments = ctx.comments, + .visib_token = ctx.visib_token, + .name_token = null, + .fn_token = undefined, + .params = ast.Node.FnProto.ParamList.init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = if (ctx.extern_export_inline_token) |at| at.index else null, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = ctx.lib_name, + .align_expr = null, + }); + try ctx.decls.push(&fn_proto.base); + stack.push(State { .FnDef = fn_proto }) catch unreachable; + try stack.push(State { .FnProto = fn_proto }); + + switch (token_ptr.id) { + Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { + fn_proto.cc_token = token_index; + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + } + }); + continue; + }, + Token.Id.Keyword_async => { + const async_node = try createNode(arena, ast.Node.AsyncAttribute, + ast.Node.AsyncAttribute { + .base = undefined, + .async_token = token_index, + .allocator_type = null, + .rangle_bracket = null, + } + ); + fn_proto.async_attr = async_node; + + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + } + }); + try stack.push(State { .AsyncAllocator = async_node }); + continue; + }, + Token.Id.Keyword_fn => { + fn_proto.fn_token = token_index; + continue; + }, + else => unreachable, + } + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedVarDeclOrFn = Error.ExpectedVarDeclOrFn { .token = token_index }, + }; + return tree; + }, + } + }, + State.TopLevelExternOrField => |ctx| { + if (eatToken(&tok_it, Token.Id.Identifier)) |identifier| { + std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct); + const node = try arena.construct(ast.Node.StructField { + .base = ast.Node { + .id = ast.Node.Id.StructField, + }, + .doc_comments = ctx.comments, + .visib_token = ctx.visib_token, + .name_token = identifier, + .type_expr = undefined, + }); + const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); + *node_ptr = &node.base; + + stack.push(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.type_expr } }); + try stack.push(State { .ExpectToken = Token.Id.Colon }); + continue; + } + + stack.push(State{ .ContainerDecl = ctx.container_decl }) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &ctx.container_decl.fields_and_decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = null, + .lib_name = null, + .comments = ctx.comments, + } + }); + continue; + }, + + State.FieldInitValue => |ctx| { + const eq_tok_index = tok_it.index; + const eq_tok_ptr = ??tok_it.next(); + if (eq_tok_ptr.id != Token.Id.Equal) { + _ = tok_it.prev(); + continue; + } + stack.push(State { .Expression = ctx }) catch unreachable; + continue; + }, + + State.ContainerKind => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl, + ast.Node.ContainerDecl { + .base = undefined, + .ltoken = ctx.ltoken, + .layout = ctx.layout, + .kind = switch (token_ptr.id) { + Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct, + Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union, + Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedAggregateKw = Error.ExpectedAggregateKw { .token = token_index }, + }; + return tree; + }, + }, + .init_arg_expr = ast.Node.ContainerDecl.InitArg.None, + .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(arena), + .rbrace_token = undefined, + } + ); + + stack.push(State { .ContainerDecl = node }) catch unreachable; + try stack.push(State { .ExpectToken = Token.Id.LBrace }); + try stack.push(State { .ContainerInitArgStart = node }); + continue; + }, + + State.ContainerInitArgStart => |container_decl| { + if (eatToken(&tok_it, Token.Id.LParen) == null) { + continue; + } + + stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + try stack.push(State { .ContainerInitArg = container_decl }); + continue; + }, + + State.ContainerInitArg => |container_decl| { + const init_arg_token_index = tok_it.index; + const init_arg_token_ptr = ??tok_it.next(); + switch (init_arg_token_ptr.id) { + Token.Id.Keyword_enum => { + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null}; + const lparen_tok_index = tok_it.index; + const lparen_tok_ptr = ??tok_it.next(); + if (lparen_tok_ptr.id == Token.Id.LParen) { + try stack.push(State { .ExpectToken = Token.Id.RParen } ); + try stack.push(State { .Expression = OptionalCtx { + .RequiredNull = &container_decl.init_arg_expr.Enum, + } }); + } else { + _ = tok_it.prev(); + } + }, + else => { + _ = tok_it.prev(); + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined }; + stack.push(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; + }, + } + continue; + }, + + State.ContainerDecl => |container_decl| { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try container_decl.fields_and_decls.push(&line_comment.base); + } + + const comments = try eatDocComments(arena, &tok_it); + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Identifier => { + switch (container_decl.kind) { + ast.Node.ContainerDecl.Kind.Struct => { + const node = try arena.construct(ast.Node.StructField { + .base = ast.Node { + .id = ast.Node.Id.StructField, + }, + .doc_comments = comments, + .visib_token = null, + .name_token = token_index, + .type_expr = undefined, + }); + const node_ptr = try container_decl.fields_and_decls.addOne(); + *node_ptr = &node.base; + + try stack.push(State { .FieldListCommaOrEnd = container_decl }); + try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } }); + try stack.push(State { .ExpectToken = Token.Id.Colon }); + continue; + }, + ast.Node.ContainerDecl.Kind.Union => { + const node = try arena.construct(ast.Node.UnionTag { + .base = ast.Node {.id = ast.Node.Id.UnionTag }, + .name_token = token_index, + .type_expr = null, + .value_expr = null, + .doc_comments = comments, + }); + try container_decl.fields_and_decls.push(&node.base); + + stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; + try stack.push(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } }); + try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } }); + try stack.push(State { .IfToken = Token.Id.Colon }); + continue; + }, + ast.Node.ContainerDecl.Kind.Enum => { + const node = try arena.construct(ast.Node.EnumTag { + .base = ast.Node { .id = ast.Node.Id.EnumTag }, + .name_token = token_index, + .value = null, + .doc_comments = comments, + }); + try container_decl.fields_and_decls.push(&node.base); + + stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &node.value } }); + try stack.push(State { .IfToken = Token.Id.Equal }); + continue; + }, + } + }, + Token.Id.Keyword_pub => { + switch (container_decl.kind) { + ast.Node.ContainerDecl.Kind.Struct => { + try stack.push(State { + .TopLevelExternOrField = TopLevelExternOrFieldCtx { + .visib_token = token_index, + .container_decl = container_decl, + .comments = comments, + } + }); + continue; + }, + else => { + stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &container_decl.fields_and_decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; + } + } + }, + Token.Id.Keyword_export => { + stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &container_decl.fields_and_decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; + }, + Token.Id.RBrace => { + if (comments != null) { + *(try tree.errors.addOne()) = Error { + .UnattachedDocComment = Error.UnattachedDocComment { .token = token_index }, + }; + return tree; + } + container_decl.rbrace_token = token_index; + continue; + }, + else => { + _ = tok_it.prev(); + stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &container_decl.fields_and_decls, + .visib_token = null, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; + } + } + }, + + + State.VarDecl => |ctx| { + const var_decl = try arena.construct(ast.Node.VarDecl { + .base = ast.Node { + .id = ast.Node.Id.VarDecl, + }, + .doc_comments = ctx.comments, + .visib_token = ctx.visib_token, + .mut_token = ctx.mut_token, + .comptime_token = ctx.comptime_token, + .extern_export_token = ctx.extern_export_token, + .type_node = null, + .align_node = null, + .init_node = null, + .lib_name = ctx.lib_name, + // initialized later + .name_token = undefined, + .eq_token = undefined, + .semicolon_token = undefined, + }); + try ctx.list.push(&var_decl.base); + + try stack.push(State { .VarDeclAlign = var_decl }); + try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Identifier, + .ptr = &var_decl.name_token, + } + }); + continue; + }, + State.VarDeclAlign => |var_decl| { + try stack.push(State { .VarDeclEq = var_decl }); + + const next_token_index = tok_it.index; + const next_token_ptr = ??tok_it.next(); + if (next_token_ptr.id == Token.Id.Keyword_align) { + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + } + + _ = tok_it.prev(); + continue; + }, + State.VarDeclEq => |var_decl| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Equal => { + var_decl.eq_token = token_index; + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Semicolon, + .ptr = &var_decl.semicolon_token, + }, + }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } }); + continue; + }, + Token.Id.Semicolon => { + var_decl.semicolon_token = token_index; + continue; + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedEqOrSemi = Error.ExpectedEqOrSemi { .token = token_index }, + }; + return tree; + } + } + }, + + + State.FnDef => |fn_proto| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch(token_ptr.id) { + Token.Id.LBrace => { + const block = try arena.construct(ast.Node.Block { + .base = ast.Node { .id = ast.Node.Id.Block }, + .label = null, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + fn_proto.body_node = &block.base; + stack.push(State { .Block = block }) catch unreachable; + continue; + }, + Token.Id.Semicolon => continue, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedSemiOrLBrace = Error.ExpectedSemiOrLBrace { .token = token_index }, + }; + return tree; + }, + } + }, + State.FnProto => |fn_proto| { + stack.push(State { .FnProtoAlign = fn_proto }) catch unreachable; + try stack.push(State { .ParamDecl = fn_proto }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + + if (eatToken(&tok_it, Token.Id.Identifier)) |name_token| { + fn_proto.name_token = name_token; + } + continue; + }, + State.FnProtoAlign => |fn_proto| { + stack.push(State { .FnProtoReturnType = fn_proto }) catch unreachable; + + if (eatToken(&tok_it, Token.Id.Keyword_align)) |align_token| { + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + } + continue; + }, + State.FnProtoReturnType => |fn_proto| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Bang => { + fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined }; + stack.push(State { + .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet }, + }) catch unreachable; + continue; + }, + else => { + // TODO: this is a special case. Remove this when #760 is fixed + if (token_ptr.id == Token.Id.Keyword_error) { + if ((??tok_it.peek()).id == Token.Id.LBrace) { + const error_type_node = try arena.construct(ast.Node.ErrorType { + .base = ast.Node { .id = ast.Node.Id.ErrorType }, + .token = token_index, + }); + fn_proto.return_type = ast.Node.FnProto.ReturnType { + .Explicit = &error_type_node.base, + }; + continue; + } + } + + _ = tok_it.prev(); + fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined }; + stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; + continue; + }, + } + }, + + + State.ParamDecl => |fn_proto| { + if (eatToken(&tok_it, Token.Id.RParen)) |_| { + continue; + } + const param_decl = try arena.construct(ast.Node.ParamDecl { + .base = ast.Node {.id = ast.Node.Id.ParamDecl }, + .comptime_token = null, + .noalias_token = null, + .name_token = null, + .type_node = undefined, + .var_args_token = null, + }); + try fn_proto.params.push(¶m_decl.base); + + stack.push(State { + .ParamDeclEnd = ParamDeclEndCtx { + .param_decl = param_decl, + .fn_proto = fn_proto, + } + }) catch unreachable; + try stack.push(State { .ParamDeclName = param_decl }); + try stack.push(State { .ParamDeclAliasOrComptime = param_decl }); + continue; + }, + State.ParamDeclAliasOrComptime => |param_decl| { + if (eatToken(&tok_it, Token.Id.Keyword_comptime)) |comptime_token| { + param_decl.comptime_token = comptime_token; + } else if (eatToken(&tok_it, Token.Id.Keyword_noalias)) |noalias_token| { + param_decl.noalias_token = noalias_token; + } + continue; + }, + State.ParamDeclName => |param_decl| { + // TODO: Here, we eat two tokens in one state. This means that we can't have + // comments between these two tokens. + if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| { + if (eatToken(&tok_it, Token.Id.Colon)) |_| { + param_decl.name_token = ident_token; + } else { + _ = tok_it.prev(); + } + } + continue; + }, + State.ParamDeclEnd => |ctx| { + if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| { + ctx.param_decl.var_args_token = ellipsis3; + stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + continue; + } + + try stack.push(State { .ParamDeclComma = ctx.fn_proto }); + try stack.push(State { + .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node } + }); + continue; + }, + State.ParamDeclComma => |fn_proto| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) { + ExpectCommaOrEndResult.end_token => |t| { + if (t == null) { + stack.push(State { .ParamDecl = fn_proto }) catch unreachable; + } + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + + State.MaybeLabeledExpression => |ctx| { + if (eatToken(&tok_it, Token.Id.Colon)) |_| { + stack.push(State { + .LabeledExpression = LabelCtx { + .label = ctx.label, + .opt_ctx = ctx.opt_ctx, + } + }) catch unreachable; + continue; + } + + _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label); + continue; + }, + State.LabeledExpression => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.LBrace => { + const block = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block, + ast.Node.Block { + .base = undefined, + .label = ctx.label, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + } + ); + stack.push(State { .Block = block }) catch unreachable; + continue; + }, + Token.Id.Keyword_while => { + stack.push(State { + .While = LoopCtx { + .label = ctx.label, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_for => { + stack.push(State { + .For = LoopCtx { + .label = ctx.label, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_suspend => { + const node = try arena.construct(ast.Node.Suspend { + .base = ast.Node { + .id = ast.Node.Id.Suspend, + }, + .label = ctx.label, + .suspend_token = token_index, + .payload = null, + .body = null, + }); + ctx.opt_ctx.store(&node.base); + stack.push(State { .SuspendBody = node }) catch unreachable; + try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + continue; + }, + Token.Id.Keyword_inline => { + stack.push(State { + .Inline = InlineCtx { + .label = ctx.label, + .inline_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + } + }) catch unreachable; + continue; + }, + else => { + if (ctx.opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedLabelable = Error.ExpectedLabelable { .token = token_index }, + }; + return tree; + } + + _ = tok_it.prev(); + continue; + }, + } + }, + State.Inline => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_while => { + stack.push(State { + .While = LoopCtx { + .inline_token = ctx.inline_token, + .label = ctx.label, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_for => { + stack.push(State { + .For = LoopCtx { + .inline_token = ctx.inline_token, + .label = ctx.label, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + } + }) catch unreachable; + continue; + }, + else => { + if (ctx.opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedInlinable = Error.ExpectedInlinable { .token = token_index }, + }; + return tree; + } + + _ = tok_it.prev(); + continue; + }, + } + }, + State.While => |ctx| { + const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.While, + ast.Node.While { + .base = undefined, + .label = ctx.label, + .inline_token = ctx.inline_token, + .while_token = ctx.loop_token, + .condition = undefined, + .payload = null, + .continue_expr = null, + .body = undefined, + .@"else" = null, + } + ); + stack.push(State { .Else = &node.@"else" }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.push(State { .WhileContinueExpr = &node.continue_expr }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + State.WhileContinueExpr => |dest| { + stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + State.For => |ctx| { + const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.For, + ast.Node.For { + .base = undefined, + .label = ctx.label, + .inline_token = ctx.inline_token, + .for_token = ctx.loop_token, + .array_expr = undefined, + .payload = null, + .body = undefined, + .@"else" = null, + } + ); + stack.push(State { .Else = &node.@"else" }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.push(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.array_expr } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + State.Else => |dest| { + if (eatToken(&tok_it, Token.Id.Keyword_else)) |else_token| { + const node = try createNode(arena, ast.Node.Else, + ast.Node.Else { + .base = undefined, + .else_token = else_token, + .payload = null, + .body = undefined, + } + ); + *dest = node; + + stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable; + try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + continue; + } else { + continue; + } + }, + + + State.Block => |block| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.RBrace => { + block.rbrace = token_index; + continue; + }, + else => { + _ = tok_it.prev(); + stack.push(State { .Block = block }) catch unreachable; + + var any_comments = false; + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try block.statements.push(&line_comment.base); + any_comments = true; + } + if (any_comments) continue; + + try stack.push(State { .Statement = block }); + continue; + }, + } + }, + State.Statement => |block| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_comptime => { + stack.push(State { + .ComptimeStatement = ComptimeStatementCtx { + .comptime_token = token_index, + .block = block, + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_var, Token.Id.Keyword_const => { + stack.push(State { + .VarDecl = VarDeclCtx { + .comments = null, + .visib_token = null, + .comptime_token = null, + .extern_export_token = null, + .lib_name = null, + .mut_token = token_index, + .list = &block.statements, + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { + const node = try arena.construct(ast.Node.Defer { + .base = ast.Node { + .id = ast.Node.Id.Defer, + }, + .defer_token = token_index, + .kind = switch (token_ptr.id) { + Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, + Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, + else => unreachable, + }, + .expr = undefined, + }); + const node_ptr = try block.statements.addOne(); + *node_ptr = &node.base; + + stack.push(State { .Semicolon = node_ptr }) catch unreachable; + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); + continue; + }, + Token.Id.LBrace => { + const inner_block = try arena.construct(ast.Node.Block { + .base = ast.Node { .id = ast.Node.Id.Block }, + .label = null, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + try block.statements.push(&inner_block.base); + + stack.push(State { .Block = inner_block }) catch unreachable; + continue; + }, + else => { + _ = tok_it.prev(); + const statement = try block.statements.addOne(); + try stack.push(State { .Semicolon = statement }); + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); + continue; + } + } + }, + State.ComptimeStatement => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_var, Token.Id.Keyword_const => { + stack.push(State { + .VarDecl = VarDeclCtx { + .comments = null, + .visib_token = null, + .comptime_token = ctx.comptime_token, + .extern_export_token = null, + .lib_name = null, + .mut_token = token_index, + .list = &ctx.block.statements, + } + }) catch unreachable; + continue; + }, + else => { + _ = tok_it.prev(); + _ = tok_it.prev(); + const statement = try ctx.block.statements.addOne(); + try stack.push(State { .Semicolon = statement }); + try stack.push(State { .Expression = OptionalCtx { .Required = statement } }); + continue; + } + } + }, + State.Semicolon => |node_ptr| { + const node = *node_ptr; + if (node.requireSemiColon()) { + stack.push(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; + continue; + } + continue; + }, + + State.AsmOutputItems => |items| { + const lbracket_index = tok_it.index; + const lbracket_ptr = ??tok_it.next(); + if (lbracket_ptr.id != Token.Id.LBracket) { + _ = tok_it.prev(); + continue; + } + + const node = try createNode(arena, ast.Node.AsmOutput, + ast.Node.AsmOutput { + .base = undefined, + .symbolic_name = undefined, + .constraint = undefined, + .kind = undefined, + } + ); + try items.push(node); + + stack.push(State { .AsmOutputItems = items }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.Comma }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .AsmOutputReturnOrType = node }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); + try stack.push(State { .ExpectToken = Token.Id.RBracket }); + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + continue; + }, + State.AsmOutputReturnOrType => |node| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Identifier => { + node.kind = ast.Node.AsmOutput.Kind { .Variable = try createLiteral(arena, ast.Node.Identifier, token_index) }; + continue; + }, + Token.Id.Arrow => { + node.kind = ast.Node.AsmOutput.Kind { .Return = undefined }; + try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } }); + continue; + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedAsmOutputReturnOrType = Error.ExpectedAsmOutputReturnOrType { + .token = token_index, + }, + }; + return tree; + }, + } + }, + State.AsmInputItems => |items| { + const lbracket_index = tok_it.index; + const lbracket_ptr = ??tok_it.next(); + if (lbracket_ptr.id != Token.Id.LBracket) { + _ = tok_it.prev(); + continue; + } + + const node = try createNode(arena, ast.Node.AsmInput, + ast.Node.AsmInput { + .base = undefined, + .symbolic_name = undefined, + .constraint = undefined, + .expr = undefined, + } + ); + try items.push(node); + + stack.push(State { .AsmInputItems = items }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.Comma }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); + try stack.push(State { .ExpectToken = Token.Id.RBracket }); + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + continue; + }, + State.AsmClobberItems => |items| { + stack.push(State { .AsmClobberItems = items }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.Comma }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } }); + continue; + }, + + + State.ExprListItemOrEnd => |list_state| { + if (eatToken(&tok_it, list_state.end)) |token_index| { + *list_state.ptr = token_index; + continue; + } + + stack.push(State { .ExprListCommaOrEnd = list_state }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } }); + continue; + }, + State.ExprListCommaOrEnd => |list_state| { + switch (expectCommaOrEnd(&tok_it, list_state.end)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + *list_state.ptr = end; + continue; + } else { + stack.push(State { .ExprListItemOrEnd = list_state }) catch unreachable; + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + State.FieldInitListItemOrEnd => |list_state| { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try list_state.list.push(&line_comment.base); + } + + if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { + *list_state.ptr = rbrace; + continue; + } + + const node = try arena.construct(ast.Node.FieldInitializer { + .base = ast.Node { + .id = ast.Node.Id.FieldInitializer, + }, + .period_token = undefined, + .name_token = undefined, + .expr = undefined, + }); + try list_state.list.push(&node.base); + + stack.push(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx{ .Required = &node.expr } }); + try stack.push(State { .ExpectToken = Token.Id.Equal }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Identifier, + .ptr = &node.name_token, + } + }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Period, + .ptr = &node.period_token, + } + }); + continue; + }, + State.FieldInitListCommaOrEnd => |list_state| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + *list_state.ptr = end; + continue; + } else { + stack.push(State { .FieldInitListItemOrEnd = list_state }) catch unreachable; + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + State.FieldListCommaOrEnd => |container_decl| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + container_decl.rbrace_token = end; + continue; + } else { + try stack.push(State { .ContainerDecl = container_decl }); + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + State.ErrorTagListItemOrEnd => |list_state| { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try list_state.list.push(&line_comment.base); + } + + if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { + *list_state.ptr = rbrace; + continue; + } + + const node_ptr = try list_state.list.addOne(); + + try stack.push(State { .ErrorTagListCommaOrEnd = list_state }); + try stack.push(State { .ErrorTag = node_ptr }); + continue; + }, + State.ErrorTagListCommaOrEnd => |list_state| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + *list_state.ptr = end; + continue; + } else { + stack.push(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable; + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + State.SwitchCaseOrEnd => |list_state| { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try list_state.list.push(&line_comment.base); + } + + if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { + *list_state.ptr = rbrace; + continue; + } + + const comments = try eatDocComments(arena, &tok_it); + const node = try arena.construct(ast.Node.SwitchCase { + .base = ast.Node { + .id = ast.Node.Id.SwitchCase, + }, + .items = ast.Node.SwitchCase.ItemList.init(arena), + .payload = null, + .expr = undefined, + }); + try list_state.list.push(&node.base); + try stack.push(State { .SwitchCaseCommaOrEnd = list_state }); + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); + try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.push(State { .SwitchCaseFirstItem = &node.items }); + + continue; + }, + + State.SwitchCaseCommaOrEnd => |list_state| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + *list_state.ptr = end; + continue; + } else { + try stack.push(State { .SwitchCaseOrEnd = list_state }); + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + + State.SwitchCaseFirstItem => |case_items| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id == Token.Id.Keyword_else) { + const else_node = try arena.construct(ast.Node.SwitchElse { + .base = ast.Node{ .id = ast.Node.Id.SwitchElse}, + .token = token_index, + }); + try case_items.push(&else_node.base); + + try stack.push(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); + continue; + } else { + _ = tok_it.prev(); + try stack.push(State { .SwitchCaseItem = case_items }); + continue; + } + }, + State.SwitchCaseItem => |case_items| { + stack.push(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; + try stack.push(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } }); + }, + State.SwitchCaseItemCommaOrEnd => |case_items| { + switch (expectCommaOrEnd(&tok_it, Token.Id.EqualAngleBracketRight)) { + ExpectCommaOrEndResult.end_token => |t| { + if (t == null) { + stack.push(State { .SwitchCaseItem = case_items }) catch unreachable; + } + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + continue; + }, + + + State.SuspendBody => |suspend_node| { + if (suspend_node.payload != null) { + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } }); + } + continue; + }, + State.AsyncAllocator => |async_node| { + if (eatToken(&tok_it, Token.Id.AngleBracketLeft) == null) { + continue; + } + + async_node.rangle_bracket = TokenIndex(0); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.AngleBracketRight, + .ptr = &??async_node.rangle_bracket, + } + }); + try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } }); + continue; + }, + State.AsyncEnd => |ctx| { + const node = ctx.ctx.get() ?? continue; + + switch (node.id) { + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node); + fn_proto.async_attr = ctx.attribute; + continue; + }, + ast.Node.Id.SuffixOp => { + const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node); + if (suffix_op.op == @TagType(ast.Node.SuffixOp.Op).Call) { + suffix_op.op.Call.async_attr = ctx.attribute; + continue; + } + + *(try tree.errors.addOne()) = Error { + .ExpectedCall = Error.ExpectedCall { .node = node }, + }; + return tree; + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedCallOrFnProto = Error.ExpectedCallOrFnProto { .node = node }, + }; + return tree; + } + } + }, + + + State.ExternType => |ctx| { + if (eatToken(&tok_it, Token.Id.Keyword_fn)) |fn_token| { + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, + }, + .doc_comments = ctx.comments, + .visib_token = null, + .name_token = null, + .fn_token = fn_token, + .params = ast.Node.FnProto.ParamList.init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = ctx.extern_token, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + }); + ctx.opt_ctx.store(&fn_proto.base); + stack.push(State { .FnProto = fn_proto }) catch unreachable; + continue; + } + + stack.push(State { + .ContainerKind = ContainerKindCtx { + .opt_ctx = ctx.opt_ctx, + .ltoken = ctx.extern_token, + .layout = ast.Node.ContainerDecl.Layout.Extern, + }, + }) catch unreachable; + continue; + }, + State.SliceOrArrayAccess => |node| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Ellipsis2 => { + const start = node.op.ArrayAccess; + node.op = ast.Node.SuffixOp.Op { + .Slice = ast.Node.SuffixOp.Op.Slice { + .start = start, + .end = null, + } + }; + + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.RBracket, + .ptr = &node.rtoken, + } + }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } }); + continue; + }, + Token.Id.RBracket => { + node.rtoken = token_index; + continue; + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedSliceOrRBracket = Error.ExpectedSliceOrRBracket { .token = token_index }, + }; + return tree; + } + } + }, + State.SliceOrArrayType => |node| { + if (eatToken(&tok_it, Token.Id.RBracket)) |_| { + node.op = ast.Node.PrefixOp.Op { + .SliceType = ast.Node.PrefixOp.AddrOfInfo { + .align_expr = null, + .bit_offset_start_token = null, + .bit_offset_end_token = null, + .const_token = null, + .volatile_token = null, + } + }; + stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + try stack.push(State { .AddrOfModifiers = &node.op.SliceType }); + continue; + } + + node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined }; + stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + try stack.push(State { .ExpectToken = Token.Id.RBracket }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } }); + continue; + }, + State.AddrOfModifiers => |addr_of_info| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_align => { + stack.push(state) catch unreachable; + if (addr_of_info.align_expr != null) { + *(try tree.errors.addOne()) = Error { + .ExtraAlignQualifier = Error.ExtraAlignQualifier { .token = token_index }, + }; + return tree; + } + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + Token.Id.Keyword_const => { + stack.push(state) catch unreachable; + if (addr_of_info.const_token != null) { + *(try tree.errors.addOne()) = Error { + .ExtraConstQualifier = Error.ExtraConstQualifier { .token = token_index }, + }; + return tree; + } + addr_of_info.const_token = token_index; + continue; + }, + Token.Id.Keyword_volatile => { + stack.push(state) catch unreachable; + if (addr_of_info.volatile_token != null) { + *(try tree.errors.addOne()) = Error { + .ExtraVolatileQualifier = Error.ExtraVolatileQualifier { .token = token_index }, + }; + return tree; + } + addr_of_info.volatile_token = token_index; + continue; + }, + else => { + _ = tok_it.prev(); + continue; + }, + } + }, + + + State.Payload => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != Token.Id.Pipe) { + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = Token.Id.Pipe, + }, + }; + return tree; + } + + _ = tok_it.prev(); + continue; + } + + const node = try createToCtxNode(arena, opt_ctx, ast.Node.Payload, + ast.Node.Payload { + .base = undefined, + .lpipe = token_index, + .error_symbol = undefined, + .rpipe = undefined + } + ); + + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } + }) catch unreachable; + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } }); + continue; + }, + State.PointerPayload => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != Token.Id.Pipe) { + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = Token.Id.Pipe, + }, + }; + return tree; + } + + _ = tok_it.prev(); + continue; + } + + const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload, + ast.Node.PointerPayload { + .base = undefined, + .lpipe = token_index, + .ptr_token = null, + .value_symbol = undefined, + .rpipe = undefined + } + ); + + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } + }); + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); + try stack.push(State { + .OptionalTokenSave = OptionalTokenSave { + .id = Token.Id.Asterisk, + .ptr = &node.ptr_token, + } + }); + continue; + }, + State.PointerIndexPayload => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != Token.Id.Pipe) { + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = Token.Id.Pipe, + }, + }; + return tree; + } + + _ = tok_it.prev(); + continue; + } + + const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload, + ast.Node.PointerIndexPayload { + .base = undefined, + .lpipe = token_index, + .ptr_token = null, + .value_symbol = undefined, + .index_symbol = null, + .rpipe = undefined + } + ); + + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } + }) catch unreachable; + try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } }); + try stack.push(State { .IfToken = Token.Id.Comma }); + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); + try stack.push(State { + .OptionalTokenSave = OptionalTokenSave { + .id = Token.Id.Asterisk, + .ptr = &node.ptr_token, + } + }); + continue; + }, + + + State.Expression => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression, + ast.Node.ControlFlowExpression { + .base = undefined, + .ltoken = token_index, + .kind = undefined, + .rhs = null, + } + ); + + stack.push(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable; + + switch (token_ptr.id) { + Token.Id.Keyword_break => { + node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null }; + try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } }); + try stack.push(State { .IfToken = Token.Id.Colon }); + }, + Token.Id.Keyword_continue => { + node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null }; + try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } }); + try stack.push(State { .IfToken = Token.Id.Colon }); + }, + Token.Id.Keyword_return => { + node.kind = ast.Node.ControlFlowExpression.Kind.Return; + }, + else => unreachable, + } + continue; + }, + Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { + .base = undefined, + .op_token = token_index, + .op = switch (token_ptr.id) { + Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} }, + Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} }, + Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} }, + else => unreachable, + }, + .rhs = undefined, + } + ); + + stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + continue; + }, + else => { + if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) { + _ = tok_it.prev(); + stack.push(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable; + } + continue; + } + } + }, + State.RangeExpressionBegin => |opt_ctx| { + stack.push(State { .RangeExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .Expression = opt_ctx }); + continue; + }, + State.RangeExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = ellipsis3, + .op = ast.Node.InfixOp.Op.Range, + .rhs = undefined, + } + ); + stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + continue; + } + }, + State.AssignmentExpressionBegin => |opt_ctx| { + stack.push(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .Expression = opt_ctx }); + continue; + }, + + State.AssignmentExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToAssignment(token_ptr.id)) |ass_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = ass_id, + .rhs = undefined, + } + ); + stack.push(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + _ = tok_it.prev(); + continue; + } + }, + + State.UnwrapExpressionBegin => |opt_ctx| { + stack.push(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BoolOrExpressionBegin = opt_ctx }); + continue; + }, + + State.UnwrapExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = unwrap_id, + .rhs = undefined, + } + ); + + stack.push(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }); + + if (node.op == ast.Node.InfixOp.Op.Catch) { + try stack.push(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } }); + } + continue; + } else { + _ = tok_it.prev(); + continue; + } + }, + + State.BoolOrExpressionBegin => |opt_ctx| { + stack.push(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BoolAndExpressionBegin = opt_ctx }); + continue; + }, + + State.BoolOrExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (eatToken(&tok_it, Token.Id.Keyword_or)) |or_token| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = or_token, + .op = ast.Node.InfixOp.Op.BoolOr, + .rhs = undefined, + } + ); + stack.push(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, + + State.BoolAndExpressionBegin => |opt_ctx| { + stack.push(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .ComparisonExpressionBegin = opt_ctx }); + continue; + }, + + State.BoolAndExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (eatToken(&tok_it, Token.Id.Keyword_and)) |and_token| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = and_token, + .op = ast.Node.InfixOp.Op.BoolAnd, + .rhs = undefined, + } + ); + stack.push(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, + + State.ComparisonExpressionBegin => |opt_ctx| { + stack.push(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BinaryOrExpressionBegin = opt_ctx }); + continue; + }, + + State.ComparisonExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToComparison(token_ptr.id)) |comp_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = comp_id, + .rhs = undefined, + } + ); + stack.push(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + _ = tok_it.prev(); + continue; + } + }, + + State.BinaryOrExpressionBegin => |opt_ctx| { + stack.push(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BinaryXorExpressionBegin = opt_ctx }); + continue; + }, + + State.BinaryOrExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (eatToken(&tok_it, Token.Id.Pipe)) |pipe| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = pipe, + .op = ast.Node.InfixOp.Op.BitOr, + .rhs = undefined, + } + ); + stack.push(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, + + State.BinaryXorExpressionBegin => |opt_ctx| { + stack.push(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BinaryAndExpressionBegin = opt_ctx }); + continue; + }, + + State.BinaryXorExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (eatToken(&tok_it, Token.Id.Caret)) |caret| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = caret, + .op = ast.Node.InfixOp.Op.BitXor, + .rhs = undefined, + } + ); + stack.push(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, + + State.BinaryAndExpressionBegin => |opt_ctx| { + stack.push(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BitShiftExpressionBegin = opt_ctx }); + continue; + }, + + State.BinaryAndExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (eatToken(&tok_it, Token.Id.Ampersand)) |ampersand| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = ampersand, + .op = ast.Node.InfixOp.Op.BitAnd, + .rhs = undefined, + } + ); + stack.push(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, + + State.BitShiftExpressionBegin => |opt_ctx| { + stack.push(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .AdditionExpressionBegin = opt_ctx }); + continue; + }, + + State.BitShiftExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = bitshift_id, + .rhs = undefined, + } + ); + stack.push(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + _ = tok_it.prev(); + continue; + } + }, + + State.AdditionExpressionBegin => |opt_ctx| { + stack.push(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .MultiplyExpressionBegin = opt_ctx }); + continue; + }, + + State.AdditionExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToAddition(token_ptr.id)) |add_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = add_id, + .rhs = undefined, + } + ); + stack.push(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + _ = tok_it.prev(); + continue; + } + }, + + State.MultiplyExpressionBegin => |opt_ctx| { + stack.push(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .CurlySuffixExpressionBegin = opt_ctx }); + continue; + }, + + State.MultiplyExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToMultiply(token_ptr.id)) |mult_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = mult_id, + .rhs = undefined, + } + ); + stack.push(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + _ = tok_it.prev(); + continue; + } + }, + + State.CurlySuffixExpressionBegin => |opt_ctx| { + stack.push(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.LBrace }); + try stack.push(State { .TypeExprBegin = opt_ctx }); + continue; + }, + + State.CurlySuffixExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if ((??tok_it.peek()).id == Token.Id.Period) { + const node = try arena.construct(ast.Node.SuffixOp { + .base = ast.Node { .id = ast.Node.Id.SuffixOp }, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena), + }, + .rtoken = undefined, + }); + opt_ctx.store(&node.base); + + stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.LBrace }); + try stack.push(State { + .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)) { + .list = &node.op.StructInitializer, + .ptr = &node.rtoken, + } + }); + continue; + } + + const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena), + }, + .rtoken = undefined, + } + ); + stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.LBrace }); + try stack.push(State { + .ExprListItemOrEnd = ExprListCtx { + .list = &node.op.ArrayInitializer, + .end = Token.Id.RBrace, + .ptr = &node.rtoken, + } + }); + continue; + }, + + State.TypeExprBegin => |opt_ctx| { + stack.push(State { .TypeExprEnd = opt_ctx }) catch unreachable; + try stack.push(State { .PrefixOpExpression = opt_ctx }); + continue; + }, + + State.TypeExprEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if (eatToken(&tok_it, Token.Id.Bang)) |bang| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = bang, + .op = ast.Node.InfixOp.Op.ErrorUnion, + .rhs = undefined, + } + ); + stack.push(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, + + State.PrefixOpExpression => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| { + var node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { + .base = undefined, + .op_token = token_index, + .op = prefix_id, + .rhs = undefined, + } + ); + + // Treat '**' token as two derefs + if (token_ptr.id == Token.Id.AsteriskAsterisk) { + const child = try createNode(arena, ast.Node.PrefixOp, + ast.Node.PrefixOp { + .base = undefined, + .op_token = token_index, + .op = prefix_id, + .rhs = undefined, + } + ); + node.rhs = &child.base; + node = child; + } + + stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + if (node.op == ast.Node.PrefixOp.Op.AddrOf) { + try stack.push(State { .AddrOfModifiers = &node.op.AddrOf }); + } + continue; + } else { + _ = tok_it.prev(); + stack.push(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable; + continue; + } + }, + + State.SuffixOpExpressionBegin => |opt_ctx| { + if (eatToken(&tok_it, Token.Id.Keyword_async)) |async_token| { + const async_node = try createNode(arena, ast.Node.AsyncAttribute, + ast.Node.AsyncAttribute { + .base = undefined, + .async_token = async_token, + .allocator_type = null, + .rangle_bracket = null, + } + ); + stack.push(State { + .AsyncEnd = AsyncEndCtx { + .ctx = opt_ctx, + .attribute = async_node, + } + }) catch unreachable; + try stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }); + try stack.push(State { .PrimaryExpression = opt_ctx.toRequired() }); + try stack.push(State { .AsyncAllocator = async_node }); + continue; + } + + stack.push(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .PrimaryExpression = opt_ctx }); + continue; + }, + + State.SuffixOpExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.LParen => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .Call = ast.Node.SuffixOp.Op.Call { + .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena), + .async_attr = null, + } + }, + .rtoken = undefined, + } + ); + stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { + .ExprListItemOrEnd = ExprListCtx { + .list = &node.op.Call.params, + .end = Token.Id.RParen, + .ptr = &node.rtoken, + } + }); + continue; + }, + Token.Id.LBracket => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .ArrayAccess = undefined, + }, + .rtoken = undefined + } + ); + stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .SliceOrArrayAccess = node }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }}); + continue; + }, + Token.Id.Period => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = ast.Node.InfixOp.Op.Period, + .rhs = undefined, + } + ); + stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.rhs } }); + continue; + }, + else => { + _ = tok_it.prev(); + continue; + }, + } + }, + + State.PrimaryExpression => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.IntegerLiteral => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token_index); + continue; + }, + Token.Id.FloatLiteral => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token_index); + continue; + }, + Token.Id.CharLiteral => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token_index); + continue; + }, + Token.Id.Keyword_undefined => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token_index); + continue; + }, + Token.Id.Keyword_true, Token.Id.Keyword_false => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token_index); + continue; + }, + Token.Id.Keyword_null => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token_index); + continue; + }, + Token.Id.Keyword_this => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token_index); + continue; + }, + Token.Id.Keyword_var => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token_index); + continue; + }, + Token.Id.Keyword_unreachable => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token_index); + continue; + }, + Token.Id.Keyword_promise => { + const node = try arena.construct(ast.Node.PromiseType { + .base = ast.Node { + .id = ast.Node.Id.PromiseType, + }, + .promise_token = token_index, + .result = null, + }); + opt_ctx.store(&node.base); + const next_token_index = tok_it.index; + const next_token_ptr = ??tok_it.next(); + if (next_token_ptr.id != Token.Id.Arrow) { + _ = tok_it.prev(); + continue; + } + node.result = ast.Node.PromiseType.Result { + .arrow_token = next_token_index, + .return_type = undefined, + }; + const return_type_ptr = &((??node.result).return_type); + try stack.push(State { .Expression = OptionalCtx { .Required = return_type_ptr, } }); + continue; + }, + Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { + opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? unreachable); + continue; + }, + Token.Id.LParen => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression, + ast.Node.GroupedExpression { + .base = undefined, + .lparen = token_index, + .expr = undefined, + .rparen = undefined, + } + ); + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.RParen, + .ptr = &node.rparen, + } + }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + continue; + }, + Token.Id.Builtin => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall, + ast.Node.BuiltinCall { + .base = undefined, + .builtin_token = token_index, + .params = ast.Node.BuiltinCall.ParamList.init(arena), + .rparen_token = undefined, + } + ); + stack.push(State { + .ExprListItemOrEnd = ExprListCtx { + .list = &node.params, + .end = Token.Id.RParen, + .ptr = &node.rparen_token, + } + }) catch unreachable; + try stack.push(State { .ExpectToken = Token.Id.LParen, }); + continue; + }, + Token.Id.LBracket => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { + .base = undefined, + .op_token = token_index, + .op = undefined, + .rhs = undefined, + } + ); + stack.push(State { .SliceOrArrayType = node }) catch unreachable; + continue; + }, + Token.Id.Keyword_error => { + stack.push(State { + .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx { + .error_token = token_index, + .opt_ctx = opt_ctx + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_packed => { + stack.push(State { + .ContainerKind = ContainerKindCtx { + .opt_ctx = opt_ctx, + .ltoken = token_index, + .layout = ast.Node.ContainerDecl.Layout.Packed, + }, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_extern => { + stack.push(State { + .ExternType = ExternTypeCtx { + .opt_ctx = opt_ctx, + .extern_token = token_index, + .comments = null, + }, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => { + _ = tok_it.prev(); + stack.push(State { + .ContainerKind = ContainerKindCtx { + .opt_ctx = opt_ctx, + .ltoken = token_index, + .layout = ast.Node.ContainerDecl.Layout.Auto, + }, + }) catch unreachable; + continue; + }, + Token.Id.Identifier => { + stack.push(State { + .MaybeLabeledExpression = MaybeLabeledExpressionCtx { + .label = token_index, + .opt_ctx = opt_ctx + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_fn => { + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, + }, + .doc_comments = null, + .visib_token = null, + .name_token = null, + .fn_token = token_index, + .params = ast.Node.FnProto.ParamList.init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = null, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + }); + opt_ctx.store(&fn_proto.base); + stack.push(State { .FnProto = fn_proto }) catch unreachable; + continue; + }, + Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, + }, + .doc_comments = null, + .visib_token = null, + .name_token = null, + .fn_token = undefined, + .params = ast.Node.FnProto.ParamList.init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = null, + .cc_token = token_index, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + }); + opt_ctx.store(&fn_proto.base); + stack.push(State { .FnProto = fn_proto }) catch unreachable; + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token + } + }); + continue; + }, + Token.Id.Keyword_asm => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.Asm, + ast.Node.Asm { + .base = undefined, + .asm_token = token_index, + .volatile_token = null, + .template = undefined, + .outputs = ast.Node.Asm.OutputList.init(arena), + .inputs = ast.Node.Asm.InputList.init(arena), + .clobbers = ast.Node.Asm.ClobberList.init(arena), + .rparen = undefined, + } + ); + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.RParen, + .ptr = &node.rparen, + } + }) catch unreachable; + try stack.push(State { .AsmClobberItems = &node.clobbers }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { .AsmInputItems = &node.inputs }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { .AsmOutputItems = &node.outputs }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.template } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.push(State { + .OptionalTokenSave = OptionalTokenSave { + .id = Token.Id.Keyword_volatile, + .ptr = &node.volatile_token, + } + }); + }, + Token.Id.Keyword_inline => { + stack.push(State { + .Inline = InlineCtx { + .label = null, + .inline_token = token_index, + .opt_ctx = opt_ctx, + } + }) catch unreachable; + continue; + }, + else => { + if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) { + _ = tok_it.prev(); + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index }, + }; + return tree; + } + } + continue; + } + } + }, + + + State.ErrorTypeOrSetDecl => |ctx| { + if (eatToken(&tok_it, Token.Id.LBrace) == null) { + _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token); + continue; + } + + const node = try arena.construct(ast.Node.ErrorSetDecl { + .base = ast.Node { + .id = ast.Node.Id.ErrorSetDecl, + }, + .error_token = ctx.error_token, + .decls = ast.Node.ErrorSetDecl.DeclList.init(arena), + .rbrace_token = undefined, + }); + ctx.opt_ctx.store(&node.base); + + stack.push(State { + .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)) { + .list = &node.decls, + .ptr = &node.rbrace_token, + } + }) catch unreachable; + continue; + }, + State.StringLiteral => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + opt_ctx.store( + (try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? { + _ = tok_it.prev(); + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index }, + }; + return tree; + } + + continue; + } + ); + }, + + State.Identifier => |opt_ctx| { + if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token); + continue; + } + + if (opt_ctx != OptionalCtx.Optional) { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = Token.Id.Identifier, + }, + }; + return tree; + } + }, + + State.ErrorTag => |node_ptr| { + const comments = try eatDocComments(arena, &tok_it); + const ident_token_index = tok_it.index; + const ident_token_ptr = ??tok_it.next(); + if (ident_token_ptr.id != Token.Id.Identifier) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = ident_token_index, + .expected_id = Token.Id.Identifier, + }, + }; + return tree; + } + + const node = try arena.construct(ast.Node.ErrorTag { + .base = ast.Node { + .id = ast.Node.Id.ErrorTag, + }, + .doc_comments = comments, + .name_token = ident_token_index, + }); + *node_ptr = &node.base; + continue; + }, + + State.ExpectToken => |token_id| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != token_id) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = token_id, + }, + }; + return tree; + } + continue; + }, + State.ExpectTokenSave => |expect_token_save| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != expect_token_save.id) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = expect_token_save.id, + }, + }; + return tree; + } + *expect_token_save.ptr = token_index; + continue; + }, + State.IfToken => |token_id| { + if (eatToken(&tok_it, token_id)) |_| { + continue; + } + + _ = stack.pop(); + continue; + }, + State.IfTokenSave => |if_token_save| { + if (eatToken(&tok_it, if_token_save.id)) |token_index| { + *if_token_save.ptr = token_index; + continue; + } + + _ = stack.pop(); + continue; + }, + State.OptionalTokenSave => |optional_token_save| { + if (eatToken(&tok_it, optional_token_save.id)) |token_index| { + *optional_token_save.ptr = token_index; + continue; + } + + continue; + }, + } + } +} + +const AnnotatedToken = struct { + ptr: &Token, + index: TokenIndex, +}; + +const TopLevelDeclCtx = struct { + decls: &ast.Node.Root.DeclList, + visib_token: ?TokenIndex, + extern_export_inline_token: ?AnnotatedToken, + lib_name: ?&ast.Node, + comments: ?&ast.Node.DocComment, +}; + +const VarDeclCtx = struct { + mut_token: TokenIndex, + visib_token: ?TokenIndex, + comptime_token: ?TokenIndex, + extern_export_token: ?TokenIndex, + lib_name: ?&ast.Node, + list: &ast.Node.Root.DeclList, + comments: ?&ast.Node.DocComment, +}; + +const TopLevelExternOrFieldCtx = struct { + visib_token: TokenIndex, + container_decl: &ast.Node.ContainerDecl, + comments: ?&ast.Node.DocComment, +}; + +const ExternTypeCtx = struct { + opt_ctx: OptionalCtx, + extern_token: TokenIndex, + comments: ?&ast.Node.DocComment, +}; + +const ContainerKindCtx = struct { + opt_ctx: OptionalCtx, + ltoken: TokenIndex, + layout: ast.Node.ContainerDecl.Layout, +}; + +const ExpectTokenSave = struct { + id: @TagType(Token.Id), + ptr: &TokenIndex, +}; + +const OptionalTokenSave = struct { + id: @TagType(Token.Id), + ptr: &?TokenIndex, +}; + +const ExprListCtx = struct { + list: &ast.Node.SuffixOp.Op.InitList, + end: Token.Id, + ptr: &TokenIndex, +}; + +fn ListSave(comptime List: type) type { + return struct { + list: &List, + ptr: &TokenIndex, + }; +} + +const MaybeLabeledExpressionCtx = struct { + label: TokenIndex, + opt_ctx: OptionalCtx, +}; + +const LabelCtx = struct { + label: ?TokenIndex, + opt_ctx: OptionalCtx, +}; + +const InlineCtx = struct { + label: ?TokenIndex, + inline_token: ?TokenIndex, + opt_ctx: OptionalCtx, +}; + +const LoopCtx = struct { + label: ?TokenIndex, + inline_token: ?TokenIndex, + loop_token: TokenIndex, + opt_ctx: OptionalCtx, +}; + +const AsyncEndCtx = struct { + ctx: OptionalCtx, + attribute: &ast.Node.AsyncAttribute, +}; + +const ErrorTypeOrSetDeclCtx = struct { + opt_ctx: OptionalCtx, + error_token: TokenIndex, +}; + +const ParamDeclEndCtx = struct { + fn_proto: &ast.Node.FnProto, + param_decl: &ast.Node.ParamDecl, +}; + +const ComptimeStatementCtx = struct { + comptime_token: TokenIndex, + block: &ast.Node.Block, +}; + +const OptionalCtx = union(enum) { + Optional: &?&ast.Node, + RequiredNull: &?&ast.Node, + Required: &&ast.Node, + + pub fn store(self: &const OptionalCtx, value: &ast.Node) void { + switch (*self) { + OptionalCtx.Optional => |ptr| *ptr = value, + OptionalCtx.RequiredNull => |ptr| *ptr = value, + OptionalCtx.Required => |ptr| *ptr = value, + } + } + + pub fn get(self: &const OptionalCtx) ?&ast.Node { + switch (*self) { + OptionalCtx.Optional => |ptr| return *ptr, + OptionalCtx.RequiredNull => |ptr| return ??*ptr, + OptionalCtx.Required => |ptr| return *ptr, + } + } + + pub fn toRequired(self: &const OptionalCtx) OptionalCtx { + switch (*self) { + OptionalCtx.Optional => |ptr| { + return OptionalCtx { .RequiredNull = ptr }; + }, + OptionalCtx.RequiredNull => |ptr| return *self, + OptionalCtx.Required => |ptr| return *self, + } + } +}; + +const AddCommentsCtx = struct { + node_ptr: &&ast.Node, + comments: ?&ast.Node.DocComment, +}; + +const State = union(enum) { + TopLevel, + TopLevelExtern: TopLevelDeclCtx, + TopLevelLibname: TopLevelDeclCtx, + TopLevelDecl: TopLevelDeclCtx, + TopLevelExternOrField: TopLevelExternOrFieldCtx, + + ContainerKind: ContainerKindCtx, + ContainerInitArgStart: &ast.Node.ContainerDecl, + ContainerInitArg: &ast.Node.ContainerDecl, + ContainerDecl: &ast.Node.ContainerDecl, + + VarDecl: VarDeclCtx, + VarDeclAlign: &ast.Node.VarDecl, + VarDeclEq: &ast.Node.VarDecl, + + FnDef: &ast.Node.FnProto, + FnProto: &ast.Node.FnProto, + FnProtoAlign: &ast.Node.FnProto, + FnProtoReturnType: &ast.Node.FnProto, + + ParamDecl: &ast.Node.FnProto, + ParamDeclAliasOrComptime: &ast.Node.ParamDecl, + ParamDeclName: &ast.Node.ParamDecl, + ParamDeclEnd: ParamDeclEndCtx, + ParamDeclComma: &ast.Node.FnProto, + + MaybeLabeledExpression: MaybeLabeledExpressionCtx, + LabeledExpression: LabelCtx, + Inline: InlineCtx, + While: LoopCtx, + WhileContinueExpr: &?&ast.Node, + For: LoopCtx, + Else: &?&ast.Node.Else, + + Block: &ast.Node.Block, + Statement: &ast.Node.Block, + ComptimeStatement: ComptimeStatementCtx, + Semicolon: &&ast.Node, + + AsmOutputItems: &ast.Node.Asm.OutputList, + AsmOutputReturnOrType: &ast.Node.AsmOutput, + AsmInputItems: &ast.Node.Asm.InputList, + AsmClobberItems: &ast.Node.Asm.ClobberList, + + ExprListItemOrEnd: ExprListCtx, + ExprListCommaOrEnd: ExprListCtx, + FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), + FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), + FieldListCommaOrEnd: &ast.Node.ContainerDecl, + FieldInitValue: OptionalCtx, + ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), + ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), + SwitchCaseOrEnd: ListSave(ast.Node.Switch.CaseList), + SwitchCaseCommaOrEnd: ListSave(ast.Node.Switch.CaseList), + SwitchCaseFirstItem: &ast.Node.SwitchCase.ItemList, + SwitchCaseItem: &ast.Node.SwitchCase.ItemList, + SwitchCaseItemCommaOrEnd: &ast.Node.SwitchCase.ItemList, + + SuspendBody: &ast.Node.Suspend, + AsyncAllocator: &ast.Node.AsyncAttribute, + AsyncEnd: AsyncEndCtx, + + ExternType: ExternTypeCtx, + SliceOrArrayAccess: &ast.Node.SuffixOp, + SliceOrArrayType: &ast.Node.PrefixOp, + AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo, + + Payload: OptionalCtx, + PointerPayload: OptionalCtx, + PointerIndexPayload: OptionalCtx, + + Expression: OptionalCtx, + RangeExpressionBegin: OptionalCtx, + RangeExpressionEnd: OptionalCtx, + AssignmentExpressionBegin: OptionalCtx, + AssignmentExpressionEnd: OptionalCtx, + UnwrapExpressionBegin: OptionalCtx, + UnwrapExpressionEnd: OptionalCtx, + BoolOrExpressionBegin: OptionalCtx, + BoolOrExpressionEnd: OptionalCtx, + BoolAndExpressionBegin: OptionalCtx, + BoolAndExpressionEnd: OptionalCtx, + ComparisonExpressionBegin: OptionalCtx, + ComparisonExpressionEnd: OptionalCtx, + BinaryOrExpressionBegin: OptionalCtx, + BinaryOrExpressionEnd: OptionalCtx, + BinaryXorExpressionBegin: OptionalCtx, + BinaryXorExpressionEnd: OptionalCtx, + BinaryAndExpressionBegin: OptionalCtx, + BinaryAndExpressionEnd: OptionalCtx, + BitShiftExpressionBegin: OptionalCtx, + BitShiftExpressionEnd: OptionalCtx, + AdditionExpressionBegin: OptionalCtx, + AdditionExpressionEnd: OptionalCtx, + MultiplyExpressionBegin: OptionalCtx, + MultiplyExpressionEnd: OptionalCtx, + CurlySuffixExpressionBegin: OptionalCtx, + CurlySuffixExpressionEnd: OptionalCtx, + TypeExprBegin: OptionalCtx, + TypeExprEnd: OptionalCtx, + PrefixOpExpression: OptionalCtx, + SuffixOpExpressionBegin: OptionalCtx, + SuffixOpExpressionEnd: OptionalCtx, + PrimaryExpression: OptionalCtx, + + ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx, + StringLiteral: OptionalCtx, + Identifier: OptionalCtx, + ErrorTag: &&ast.Node, + + + IfToken: @TagType(Token.Id), + IfTokenSave: ExpectTokenSave, + ExpectToken: @TagType(Token.Id), + ExpectTokenSave: ExpectTokenSave, + OptionalTokenSave: OptionalTokenSave, +}; + +fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.DocComment { + var result: ?&ast.Node.DocComment = null; + while (true) { + if (eatToken(tok_it, Token.Id.DocComment)) |line_comment| { + const node = blk: { + if (result) |comment_node| { + break :blk comment_node; + } else { + const comment_node = try arena.construct(ast.Node.DocComment { + .base = ast.Node { + .id = ast.Node.Id.DocComment, + }, + .lines = ast.Node.DocComment.LineList.init(arena), + }); + result = comment_node; + break :blk comment_node; + } + }; + try node.lines.push(line_comment); + continue; + } + break; + } + return result; +} + +fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.LineComment { + const token = eatToken(tok_it, Token.Id.LineComment) ?? return null; + return try arena.construct(ast.Node.LineComment { + .base = ast.Node { + .id = ast.Node.Id.LineComment, + }, + .token = token, + }); +} + +fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, + token_ptr: &const Token, token_index: TokenIndex) !?&ast.Node +{ + switch (token_ptr.id) { + Token.Id.StringLiteral => { + return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base; + }, + Token.Id.MultilineStringLiteralLine => { + const node = try arena.construct(ast.Node.MultilineStringLiteral { + .base = ast.Node { .id = ast.Node.Id.MultilineStringLiteral }, + .lines = ast.Node.MultilineStringLiteral.LineList.init(arena), + }); + try node.lines.push(token_index); + while (true) { + const multiline_str_index = tok_it.index; + const multiline_str_ptr = ??tok_it.next(); + if (multiline_str_ptr.id != Token.Id.MultilineStringLiteralLine) { + _ = tok_it.prev(); + break; + } + + try node.lines.push(multiline_str_index); + } + + return &node.base; + }, + // TODO: We shouldn't need a cast, but: + // zig: /home/jc/Documents/zig/src/ir.cpp:7962: TypeTableEntry* ir_resolve_peer_types(IrAnalyze*, AstNode*, IrInstruction**, size_t): Assertion `err_set_type != nullptr' failed. + else => return (?&ast.Node)(null), + } +} + +fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: &const OptionalCtx, + token_ptr: &const Token, token_index: TokenIndex) !bool { + switch (token_ptr.id) { + Token.Id.Keyword_suspend => { + const node = try createToCtxNode(arena, ctx, ast.Node.Suspend, + ast.Node.Suspend { + .base = undefined, + .label = null, + .suspend_token = token_index, + .payload = null, + .body = null, + } + ); + + stack.push(State { .SuspendBody = node }) catch unreachable; + try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + return true; + }, + Token.Id.Keyword_if => { + const node = try createToCtxNode(arena, ctx, ast.Node.If, + ast.Node.If { + .base = undefined, + .if_token = token_index, + .condition = undefined, + .payload = null, + .body = undefined, + .@"else" = null, + } + ); + + stack.push(State { .Else = &node.@"else" }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + return true; + }, + Token.Id.Keyword_while => { + stack.push(State { + .While = LoopCtx { + .label = null, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = *ctx, + } + }) catch unreachable; + return true; + }, + Token.Id.Keyword_for => { + stack.push(State { + .For = LoopCtx { + .label = null, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = *ctx, + } + }) catch unreachable; + return true; + }, + Token.Id.Keyword_switch => { + const node = try arena.construct(ast.Node.Switch { + .base = ast.Node { + .id = ast.Node.Id.Switch, + }, + .switch_token = token_index, + .expr = undefined, + .cases = ast.Node.Switch.CaseList.init(arena), + .rbrace = undefined, + }); + ctx.store(&node.base); + + stack.push(State { + .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)) { + .list = &node.cases, + .ptr = &node.rbrace, + }, + }) catch unreachable; + try stack.push(State { .ExpectToken = Token.Id.LBrace }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + return true; + }, + Token.Id.Keyword_comptime => { + const node = try createToCtxNode(arena, ctx, ast.Node.Comptime, + ast.Node.Comptime { + .base = undefined, + .comptime_token = token_index, + .expr = undefined, + .doc_comments = null, + } + ); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + return true; + }, + Token.Id.LBrace => { + const block = try arena.construct(ast.Node.Block { + .base = ast.Node {.id = ast.Node.Id.Block }, + .label = null, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + ctx.store(&block.base); + stack.push(State { .Block = block }) catch unreachable; + return true; + }, + else => { + return false; + } + } +} + +const ExpectCommaOrEndResult = union(enum) { + end_token: ?TokenIndex, + parse_error: Error, +}; + +fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, end: @TagType(Token.Id)) ExpectCommaOrEndResult { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Comma => return ExpectCommaOrEndResult { .end_token = null}, + else => { + if (end == token_ptr.id) { + return ExpectCommaOrEndResult { .end_token = token_index }; + } + + return ExpectCommaOrEndResult { + .parse_error = Error { + .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd { + .token = token_index, + .end_id = end, + }, + }, + }; + }, + } +} + +fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op { + // TODO: We have to cast all cases because of this: + // error: expected type '?InfixOp', found '?@TagType(InfixOp)' + return switch (*id) { + Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = {} }, + Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = {} }, + Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = {} }, + Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = {} }, + Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = {} }, + Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = {} }, + Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = {} }, + Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = {} }, + Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = {} }, + Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = {} }, + Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = {} }, + Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = {} }, + Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = {} }, + Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = {} }, + else => null, + }; +} + +fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null }, + Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} }, + else => null, + }; +} + +fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} }, + Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} }, + Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} }, + Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} }, + Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} }, + Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} }, + else => null, + }; +} + +fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} }, + Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} }, + else => null, + }; +} + +fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} }, + Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} }, + Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} }, + Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} }, + Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} }, + else => null, + }; +} + +fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} }, + Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} }, + Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} }, + Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} }, + Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} }, + Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} }, + else => null, + }; +} + +fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { + return switch (id) { + Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} }, + Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} }, + Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} }, + Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} }, + Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} }, + Token.Id.Ampersand => ast.Node.PrefixOp.Op { + .AddrOf = ast.Node.PrefixOp.AddrOfInfo { + .align_expr = null, + .bit_offset_start_token = null, + .bit_offset_end_token = null, + .const_token = null, + .volatile_token = null, + }, + }, + Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} }, + Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} }, + Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} }, + Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } }, + else => null, + }; +} + +fn createNode(arena: &mem.Allocator, comptime T: type, init_to: &const T) !&T { + const node = try arena.create(T); + *node = *init_to; + node.base = blk: { + const id = ast.Node.typeToId(T); + break :blk ast.Node { + .id = id, + }; + }; + + return node; +} + +fn createToCtxNode(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T { + const node = try createNode(arena, T, init_to); + opt_ctx.store(&node.base); + + return node; +} + +fn createLiteral(arena: &mem.Allocator, comptime T: type, token_index: TokenIndex) !&T { + return createNode(arena, T, + T { + .base = undefined, + .token = token_index, + } + ); +} + +fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token_index: TokenIndex) !&T { + const node = try createLiteral(arena, T, token_index); + opt_ctx.store(&node.base); + + return node; +} + +fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, id: @TagType(Token.Id)) ?TokenIndex { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id == id) + return token_index; + + _ = tok_it.prev(); + return null; +} + +const RenderAstFrame = struct { + node: &ast.Node, + indent: usize, +}; + +pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) !void { + var stack = SegmentedList(State, 32).init(allocator); + defer stack.deinit(); + + try stack.push(RenderAstFrame { + .node = &root_node.base, + .indent = 0, + }); + + while (stack.popOrNull()) |frame| { + { + var i: usize = 0; + while (i < frame.indent) : (i += 1) { + try stream.print(" "); + } + } + try stream.print("{}\n", @tagName(frame.node.id)); + var child_i: usize = 0; + while (frame.node.iterate(child_i)) |child| : (child_i += 1) { + try stack.push(RenderAstFrame { + .node = child, + .indent = frame.indent + 2, + }); + } + } +} + +test "std.zig.parser" { + _ = @import("parser_test.zig"); +} diff --git a/std/zig/parser.zig b/std/zig/parser.zig deleted file mode 100644 index 306d460cff..0000000000 --- a/std/zig/parser.zig +++ /dev/null @@ -1,4741 +0,0 @@ -const std = @import("../index.zig"); -const assert = std.debug.assert; -const SegmentedList = std.SegmentedList; -const mem = std.mem; -const ast = std.zig.ast; -const Tokenizer = std.zig.Tokenizer; -const Token = std.zig.Token; -const TokenIndex = ast.TokenIndex; -const Error = ast.Error; -const builtin = @import("builtin"); -const io = std.io; - -/// Returns an AST tree, allocated with the parser's allocator. -/// Result should be freed with tree.deinit() when there are -/// no more references to any AST nodes of the tree. -pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { - var tree_arena = std.heap.ArenaAllocator.init(allocator); - errdefer tree_arena.deinit(); - - var stack = SegmentedList(State, 32).init(allocator); - defer stack.deinit(); - - const arena = &tree_arena.allocator; - const root_node = try createNode(arena, ast.Node.Root, - ast.Node.Root { - .base = undefined, - .decls = ast.Node.Root.DeclList.init(arena), - .doc_comments = null, - // initialized when we get the eof token - .eof_token = undefined, - } - ); - - var tree = ast.Tree { - .source = source, - .root_node = root_node, - .arena_allocator = tree_arena, - .tokens = ast.Tree.TokenList.init(arena), - .errors = ast.Tree.ErrorList.init(arena), - }; - - var tokenizer = Tokenizer.init(tree.source); - while (true) { - const token_ptr = try tree.tokens.addOne(); - *token_ptr = tokenizer.next(); - if (token_ptr.id == Token.Id.Eof) - break; - } - var tok_it = tree.tokens.iterator(0); - - try stack.push(State.TopLevel); - - while (true) { - // This gives us 1 free push that can't fail - const state = ??stack.pop(); - - switch (state) { - State.TopLevel => { - while (try eatLineComment(arena, &tok_it)) |line_comment| { - try root_node.decls.push(&line_comment.base); - } - - const comments = try eatDocComments(arena, &tok_it); - - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Keyword_test => { - stack.push(State.TopLevel) catch unreachable; - - const block = try arena.construct(ast.Node.Block { - .base = ast.Node { - .id = ast.Node.Id.Block, - }, - .label = null, - .lbrace = undefined, - .statements = ast.Node.Block.StatementList.init(arena), - .rbrace = undefined, - }); - const test_node = try arena.construct(ast.Node.TestDecl { - .base = ast.Node { - .id = ast.Node.Id.TestDecl, - }, - .doc_comments = comments, - .test_token = token_index, - .name = undefined, - .body_node = &block.base, - }); - try root_node.decls.push(&test_node.base); - try stack.push(State { .Block = block }); - try stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.LBrace, - .ptr = &block.rbrace, - } - }); - try stack.push(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } }); - continue; - }, - Token.Id.Eof => { - root_node.eof_token = token_index; - root_node.doc_comments = comments; - return tree; - }, - Token.Id.Keyword_pub => { - stack.push(State.TopLevel) catch unreachable; - try stack.push(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &root_node.decls, - .visib_token = token_index, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - }, - Token.Id.Keyword_comptime => { - const block = try createNode(arena, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = null, - .lbrace = undefined, - .statements = ast.Node.Block.StatementList.init(arena), - .rbrace = undefined, - } - ); - const node = try arena.construct(ast.Node.Comptime { - .base = ast.Node { - .id = ast.Node.Id.Comptime, - }, - .comptime_token = token_index, - .expr = &block.base, - .doc_comments = comments, - }); - try root_node.decls.push(&node.base); - - stack.push(State.TopLevel) catch unreachable; - try stack.push(State { .Block = block }); - try stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.LBrace, - .ptr = &block.rbrace, - } - }); - continue; - }, - else => { - _ = tok_it.prev(); - stack.push(State.TopLevel) catch unreachable; - try stack.push(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &root_node.decls, - .visib_token = null, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - }, - } - }, - State.TopLevelExtern => |ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Keyword_export, Token.Id.Keyword_inline => { - stack.push(State { - .TopLevelDecl = TopLevelDeclCtx { - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = AnnotatedToken { - .index = token_index, - .ptr = token_ptr, - }, - .lib_name = null, - .comments = ctx.comments, - }, - }) catch unreachable; - continue; - }, - Token.Id.Keyword_extern => { - stack.push(State { - .TopLevelLibname = TopLevelDeclCtx { - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = AnnotatedToken { - .index = token_index, - .ptr = token_ptr, - }, - .lib_name = null, - .comments = ctx.comments, - }, - }) catch unreachable; - continue; - }, - else => { - _ = tok_it.prev(); - stack.push(State { .TopLevelDecl = ctx }) catch unreachable; - continue; - } - } - }, - State.TopLevelLibname => |ctx| { - const lib_name = blk: { - const lib_name_token_index = tok_it.index; - const lib_name_token_ptr = ??tok_it.next(); - break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index)) ?? { - _ = tok_it.prev(); - break :blk null; - }; - }; - - stack.push(State { - .TopLevelDecl = TopLevelDeclCtx { - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = ctx.extern_export_inline_token, - .lib_name = lib_name, - .comments = ctx.comments, - }, - }) catch unreachable; - continue; - }, - State.TopLevelDecl => |ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Keyword_use => { - if (ctx.extern_export_inline_token) |annotated_token| { - *(try tree.errors.addOne()) = Error { - .InvalidToken = Error.InvalidToken { .token = annotated_token.index }, - }; - return tree; - } - - const node = try arena.construct(ast.Node.Use { - .base = ast.Node {.id = ast.Node.Id.Use }, - .visib_token = ctx.visib_token, - .expr = undefined, - .semicolon_token = undefined, - .doc_comments = ctx.comments, - }); - try ctx.decls.push(&node.base); - - stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Semicolon, - .ptr = &node.semicolon_token, - } - }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); - continue; - }, - Token.Id.Keyword_var, Token.Id.Keyword_const => { - if (ctx.extern_export_inline_token) |annotated_token| { - if (annotated_token.ptr.id == Token.Id.Keyword_inline) { - *(try tree.errors.addOne()) = Error { - .InvalidToken = Error.InvalidToken { .token = annotated_token.index }, - }; - return tree; - } - } - - try stack.push(State { - .VarDecl = VarDeclCtx { - .comments = ctx.comments, - .visib_token = ctx.visib_token, - .lib_name = ctx.lib_name, - .comptime_token = null, - .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null, - .mut_token = token_index, - .list = ctx.decls - } - }); - continue; - }, - Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, - Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - }, - .doc_comments = ctx.comments, - .visib_token = ctx.visib_token, - .name_token = null, - .fn_token = undefined, - .params = ast.Node.FnProto.ParamList.init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = if (ctx.extern_export_inline_token) |at| at.index else null, - .cc_token = null, - .async_attr = null, - .body_node = null, - .lib_name = ctx.lib_name, - .align_expr = null, - }); - try ctx.decls.push(&fn_proto.base); - stack.push(State { .FnDef = fn_proto }) catch unreachable; - try stack.push(State { .FnProto = fn_proto }); - - switch (token_ptr.id) { - Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - fn_proto.cc_token = token_index; - try stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } - }); - continue; - }, - Token.Id.Keyword_async => { - const async_node = try createNode(arena, ast.Node.AsyncAttribute, - ast.Node.AsyncAttribute { - .base = undefined, - .async_token = token_index, - .allocator_type = null, - .rangle_bracket = null, - } - ); - fn_proto.async_attr = async_node; - - try stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } - }); - try stack.push(State { .AsyncAllocator = async_node }); - continue; - }, - Token.Id.Keyword_fn => { - fn_proto.fn_token = token_index; - continue; - }, - else => unreachable, - } - }, - else => { - *(try tree.errors.addOne()) = Error { - .ExpectedVarDeclOrFn = Error.ExpectedVarDeclOrFn { .token = token_index }, - }; - return tree; - }, - } - }, - State.TopLevelExternOrField => |ctx| { - if (eatToken(&tok_it, Token.Id.Identifier)) |identifier| { - std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct); - const node = try arena.construct(ast.Node.StructField { - .base = ast.Node { - .id = ast.Node.Id.StructField, - }, - .doc_comments = ctx.comments, - .visib_token = ctx.visib_token, - .name_token = identifier, - .type_expr = undefined, - }); - const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); - *node_ptr = &node.base; - - stack.push(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.type_expr } }); - try stack.push(State { .ExpectToken = Token.Id.Colon }); - continue; - } - - stack.push(State{ .ContainerDecl = ctx.container_decl }) catch unreachable; - try stack.push(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &ctx.container_decl.fields_and_decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = null, - .lib_name = null, - .comments = ctx.comments, - } - }); - continue; - }, - - State.FieldInitValue => |ctx| { - const eq_tok_index = tok_it.index; - const eq_tok_ptr = ??tok_it.next(); - if (eq_tok_ptr.id != Token.Id.Equal) { - _ = tok_it.prev(); - continue; - } - stack.push(State { .Expression = ctx }) catch unreachable; - continue; - }, - - State.ContainerKind => |ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl, - ast.Node.ContainerDecl { - .base = undefined, - .ltoken = ctx.ltoken, - .layout = ctx.layout, - .kind = switch (token_ptr.id) { - Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct, - Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union, - Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum, - else => { - *(try tree.errors.addOne()) = Error { - .ExpectedAggregateKw = Error.ExpectedAggregateKw { .token = token_index }, - }; - return tree; - }, - }, - .init_arg_expr = ast.Node.ContainerDecl.InitArg.None, - .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(arena), - .rbrace_token = undefined, - } - ); - - stack.push(State { .ContainerDecl = node }) catch unreachable; - try stack.push(State { .ExpectToken = Token.Id.LBrace }); - try stack.push(State { .ContainerInitArgStart = node }); - continue; - }, - - State.ContainerInitArgStart => |container_decl| { - if (eatToken(&tok_it, Token.Id.LParen) == null) { - continue; - } - - stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - try stack.push(State { .ContainerInitArg = container_decl }); - continue; - }, - - State.ContainerInitArg => |container_decl| { - const init_arg_token_index = tok_it.index; - const init_arg_token_ptr = ??tok_it.next(); - switch (init_arg_token_ptr.id) { - Token.Id.Keyword_enum => { - container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null}; - const lparen_tok_index = tok_it.index; - const lparen_tok_ptr = ??tok_it.next(); - if (lparen_tok_ptr.id == Token.Id.LParen) { - try stack.push(State { .ExpectToken = Token.Id.RParen } ); - try stack.push(State { .Expression = OptionalCtx { - .RequiredNull = &container_decl.init_arg_expr.Enum, - } }); - } else { - _ = tok_it.prev(); - } - }, - else => { - _ = tok_it.prev(); - container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined }; - stack.push(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; - }, - } - continue; - }, - - State.ContainerDecl => |container_decl| { - while (try eatLineComment(arena, &tok_it)) |line_comment| { - try container_decl.fields_and_decls.push(&line_comment.base); - } - - const comments = try eatDocComments(arena, &tok_it); - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Identifier => { - switch (container_decl.kind) { - ast.Node.ContainerDecl.Kind.Struct => { - const node = try arena.construct(ast.Node.StructField { - .base = ast.Node { - .id = ast.Node.Id.StructField, - }, - .doc_comments = comments, - .visib_token = null, - .name_token = token_index, - .type_expr = undefined, - }); - const node_ptr = try container_decl.fields_and_decls.addOne(); - *node_ptr = &node.base; - - try stack.push(State { .FieldListCommaOrEnd = container_decl }); - try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } }); - try stack.push(State { .ExpectToken = Token.Id.Colon }); - continue; - }, - ast.Node.ContainerDecl.Kind.Union => { - const node = try arena.construct(ast.Node.UnionTag { - .base = ast.Node {.id = ast.Node.Id.UnionTag }, - .name_token = token_index, - .type_expr = null, - .value_expr = null, - .doc_comments = comments, - }); - try container_decl.fields_and_decls.push(&node.base); - - stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - try stack.push(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } }); - try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } }); - try stack.push(State { .IfToken = Token.Id.Colon }); - continue; - }, - ast.Node.ContainerDecl.Kind.Enum => { - const node = try arena.construct(ast.Node.EnumTag { - .base = ast.Node { .id = ast.Node.Id.EnumTag }, - .name_token = token_index, - .value = null, - .doc_comments = comments, - }); - try container_decl.fields_and_decls.push(&node.base); - - stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &node.value } }); - try stack.push(State { .IfToken = Token.Id.Equal }); - continue; - }, - } - }, - Token.Id.Keyword_pub => { - switch (container_decl.kind) { - ast.Node.ContainerDecl.Kind.Struct => { - try stack.push(State { - .TopLevelExternOrField = TopLevelExternOrFieldCtx { - .visib_token = token_index, - .container_decl = container_decl, - .comments = comments, - } - }); - continue; - }, - else => { - stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.push(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = token_index, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - } - } - }, - Token.Id.Keyword_export => { - stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.push(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = token_index, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - }, - Token.Id.RBrace => { - if (comments != null) { - *(try tree.errors.addOne()) = Error { - .UnattachedDocComment = Error.UnattachedDocComment { .token = token_index }, - }; - return tree; - } - container_decl.rbrace_token = token_index; - continue; - }, - else => { - _ = tok_it.prev(); - stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.push(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = null, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - } - } - }, - - - State.VarDecl => |ctx| { - const var_decl = try arena.construct(ast.Node.VarDecl { - .base = ast.Node { - .id = ast.Node.Id.VarDecl, - }, - .doc_comments = ctx.comments, - .visib_token = ctx.visib_token, - .mut_token = ctx.mut_token, - .comptime_token = ctx.comptime_token, - .extern_export_token = ctx.extern_export_token, - .type_node = null, - .align_node = null, - .init_node = null, - .lib_name = ctx.lib_name, - // initialized later - .name_token = undefined, - .eq_token = undefined, - .semicolon_token = undefined, - }); - try ctx.list.push(&var_decl.base); - - try stack.push(State { .VarDeclAlign = var_decl }); - try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); - try stack.push(State { .IfToken = Token.Id.Colon }); - try stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Identifier, - .ptr = &var_decl.name_token, - } - }); - continue; - }, - State.VarDeclAlign => |var_decl| { - try stack.push(State { .VarDeclEq = var_decl }); - - const next_token_index = tok_it.index; - const next_token_ptr = ??tok_it.next(); - if (next_token_ptr.id == Token.Id.Keyword_align) { - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - continue; - } - - _ = tok_it.prev(); - continue; - }, - State.VarDeclEq => |var_decl| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Equal => { - var_decl.eq_token = token_index; - stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Semicolon, - .ptr = &var_decl.semicolon_token, - }, - }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } }); - continue; - }, - Token.Id.Semicolon => { - var_decl.semicolon_token = token_index; - continue; - }, - else => { - *(try tree.errors.addOne()) = Error { - .ExpectedEqOrSemi = Error.ExpectedEqOrSemi { .token = token_index }, - }; - return tree; - } - } - }, - - - State.FnDef => |fn_proto| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch(token_ptr.id) { - Token.Id.LBrace => { - const block = try arena.construct(ast.Node.Block { - .base = ast.Node { .id = ast.Node.Id.Block }, - .label = null, - .lbrace = token_index, - .statements = ast.Node.Block.StatementList.init(arena), - .rbrace = undefined, - }); - fn_proto.body_node = &block.base; - stack.push(State { .Block = block }) catch unreachable; - continue; - }, - Token.Id.Semicolon => continue, - else => { - *(try tree.errors.addOne()) = Error { - .ExpectedSemiOrLBrace = Error.ExpectedSemiOrLBrace { .token = token_index }, - }; - return tree; - }, - } - }, - State.FnProto => |fn_proto| { - stack.push(State { .FnProtoAlign = fn_proto }) catch unreachable; - try stack.push(State { .ParamDecl = fn_proto }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - - if (eatToken(&tok_it, Token.Id.Identifier)) |name_token| { - fn_proto.name_token = name_token; - } - continue; - }, - State.FnProtoAlign => |fn_proto| { - stack.push(State { .FnProtoReturnType = fn_proto }) catch unreachable; - - if (eatToken(&tok_it, Token.Id.Keyword_align)) |align_token| { - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - } - continue; - }, - State.FnProtoReturnType => |fn_proto| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Bang => { - fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined }; - stack.push(State { - .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet }, - }) catch unreachable; - continue; - }, - else => { - // TODO: this is a special case. Remove this when #760 is fixed - if (token_ptr.id == Token.Id.Keyword_error) { - if ((??tok_it.peek()).id == Token.Id.LBrace) { - const error_type_node = try arena.construct(ast.Node.ErrorType { - .base = ast.Node { .id = ast.Node.Id.ErrorType }, - .token = token_index, - }); - fn_proto.return_type = ast.Node.FnProto.ReturnType { - .Explicit = &error_type_node.base, - }; - continue; - } - } - - _ = tok_it.prev(); - fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined }; - stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; - continue; - }, - } - }, - - - State.ParamDecl => |fn_proto| { - if (eatToken(&tok_it, Token.Id.RParen)) |_| { - continue; - } - const param_decl = try arena.construct(ast.Node.ParamDecl { - .base = ast.Node {.id = ast.Node.Id.ParamDecl }, - .comptime_token = null, - .noalias_token = null, - .name_token = null, - .type_node = undefined, - .var_args_token = null, - }); - try fn_proto.params.push(¶m_decl.base); - - stack.push(State { - .ParamDeclEnd = ParamDeclEndCtx { - .param_decl = param_decl, - .fn_proto = fn_proto, - } - }) catch unreachable; - try stack.push(State { .ParamDeclName = param_decl }); - try stack.push(State { .ParamDeclAliasOrComptime = param_decl }); - continue; - }, - State.ParamDeclAliasOrComptime => |param_decl| { - if (eatToken(&tok_it, Token.Id.Keyword_comptime)) |comptime_token| { - param_decl.comptime_token = comptime_token; - } else if (eatToken(&tok_it, Token.Id.Keyword_noalias)) |noalias_token| { - param_decl.noalias_token = noalias_token; - } - continue; - }, - State.ParamDeclName => |param_decl| { - // TODO: Here, we eat two tokens in one state. This means that we can't have - // comments between these two tokens. - if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| { - if (eatToken(&tok_it, Token.Id.Colon)) |_| { - param_decl.name_token = ident_token; - } else { - _ = tok_it.prev(); - } - } - continue; - }, - State.ParamDeclEnd => |ctx| { - if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| { - ctx.param_decl.var_args_token = ellipsis3; - stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - continue; - } - - try stack.push(State { .ParamDeclComma = ctx.fn_proto }); - try stack.push(State { - .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node } - }); - continue; - }, - State.ParamDeclComma => |fn_proto| { - switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) { - ExpectCommaOrEndResult.end_token => |t| { - if (t == null) { - stack.push(State { .ParamDecl = fn_proto }) catch unreachable; - } - continue; - }, - ExpectCommaOrEndResult.parse_error => |e| { - try tree.errors.push(e); - return tree; - }, - } - }, - - State.MaybeLabeledExpression => |ctx| { - if (eatToken(&tok_it, Token.Id.Colon)) |_| { - stack.push(State { - .LabeledExpression = LabelCtx { - .label = ctx.label, - .opt_ctx = ctx.opt_ctx, - } - }) catch unreachable; - continue; - } - - _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label); - continue; - }, - State.LabeledExpression => |ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.LBrace => { - const block = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = ctx.label, - .lbrace = token_index, - .statements = ast.Node.Block.StatementList.init(arena), - .rbrace = undefined, - } - ); - stack.push(State { .Block = block }) catch unreachable; - continue; - }, - Token.Id.Keyword_while => { - stack.push(State { - .While = LoopCtx { - .label = ctx.label, - .inline_token = null, - .loop_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_for => { - stack.push(State { - .For = LoopCtx { - .label = ctx.label, - .inline_token = null, - .loop_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_suspend => { - const node = try arena.construct(ast.Node.Suspend { - .base = ast.Node { - .id = ast.Node.Id.Suspend, - }, - .label = ctx.label, - .suspend_token = token_index, - .payload = null, - .body = null, - }); - ctx.opt_ctx.store(&node.base); - stack.push(State { .SuspendBody = node }) catch unreachable; - try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); - continue; - }, - Token.Id.Keyword_inline => { - stack.push(State { - .Inline = InlineCtx { - .label = ctx.label, - .inline_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - else => { - if (ctx.opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedLabelable = Error.ExpectedLabelable { .token = token_index }, - }; - return tree; - } - - _ = tok_it.prev(); - continue; - }, - } - }, - State.Inline => |ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Keyword_while => { - stack.push(State { - .While = LoopCtx { - .inline_token = ctx.inline_token, - .label = ctx.label, - .loop_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_for => { - stack.push(State { - .For = LoopCtx { - .inline_token = ctx.inline_token, - .label = ctx.label, - .loop_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - else => { - if (ctx.opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedInlinable = Error.ExpectedInlinable { .token = token_index }, - }; - return tree; - } - - _ = tok_it.prev(); - continue; - }, - } - }, - State.While => |ctx| { - const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.While, - ast.Node.While { - .base = undefined, - .label = ctx.label, - .inline_token = ctx.inline_token, - .while_token = ctx.loop_token, - .condition = undefined, - .payload = null, - .continue_expr = null, - .body = undefined, - .@"else" = null, - } - ); - stack.push(State { .Else = &node.@"else" }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.push(State { .WhileContinueExpr = &node.continue_expr }); - try stack.push(State { .IfToken = Token.Id.Colon }); - try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - State.WhileContinueExpr => |dest| { - stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - State.For => |ctx| { - const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.For, - ast.Node.For { - .base = undefined, - .label = ctx.label, - .inline_token = ctx.inline_token, - .for_token = ctx.loop_token, - .array_expr = undefined, - .payload = null, - .body = undefined, - .@"else" = null, - } - ); - stack.push(State { .Else = &node.@"else" }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.push(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.array_expr } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - State.Else => |dest| { - if (eatToken(&tok_it, Token.Id.Keyword_else)) |else_token| { - const node = try createNode(arena, ast.Node.Else, - ast.Node.Else { - .base = undefined, - .else_token = else_token, - .payload = null, - .body = undefined, - } - ); - *dest = node; - - stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable; - try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); - continue; - } else { - continue; - } - }, - - - State.Block => |block| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.RBrace => { - block.rbrace = token_index; - continue; - }, - else => { - _ = tok_it.prev(); - stack.push(State { .Block = block }) catch unreachable; - - var any_comments = false; - while (try eatLineComment(arena, &tok_it)) |line_comment| { - try block.statements.push(&line_comment.base); - any_comments = true; - } - if (any_comments) continue; - - try stack.push(State { .Statement = block }); - continue; - }, - } - }, - State.Statement => |block| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Keyword_comptime => { - stack.push(State { - .ComptimeStatement = ComptimeStatementCtx { - .comptime_token = token_index, - .block = block, - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_var, Token.Id.Keyword_const => { - stack.push(State { - .VarDecl = VarDeclCtx { - .comments = null, - .visib_token = null, - .comptime_token = null, - .extern_export_token = null, - .lib_name = null, - .mut_token = token_index, - .list = &block.statements, - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { - const node = try arena.construct(ast.Node.Defer { - .base = ast.Node { - .id = ast.Node.Id.Defer, - }, - .defer_token = token_index, - .kind = switch (token_ptr.id) { - Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, - Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, - else => unreachable, - }, - .expr = undefined, - }); - const node_ptr = try block.statements.addOne(); - *node_ptr = &node.base; - - stack.push(State { .Semicolon = node_ptr }) catch unreachable; - try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); - continue; - }, - Token.Id.LBrace => { - const inner_block = try arena.construct(ast.Node.Block { - .base = ast.Node { .id = ast.Node.Id.Block }, - .label = null, - .lbrace = token_index, - .statements = ast.Node.Block.StatementList.init(arena), - .rbrace = undefined, - }); - try block.statements.push(&inner_block.base); - - stack.push(State { .Block = inner_block }) catch unreachable; - continue; - }, - else => { - _ = tok_it.prev(); - const statement = try block.statements.addOne(); - try stack.push(State { .Semicolon = statement }); - try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); - continue; - } - } - }, - State.ComptimeStatement => |ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Keyword_var, Token.Id.Keyword_const => { - stack.push(State { - .VarDecl = VarDeclCtx { - .comments = null, - .visib_token = null, - .comptime_token = ctx.comptime_token, - .extern_export_token = null, - .lib_name = null, - .mut_token = token_index, - .list = &ctx.block.statements, - } - }) catch unreachable; - continue; - }, - else => { - _ = tok_it.prev(); - _ = tok_it.prev(); - const statement = try ctx.block.statements.addOne(); - try stack.push(State { .Semicolon = statement }); - try stack.push(State { .Expression = OptionalCtx { .Required = statement } }); - continue; - } - } - }, - State.Semicolon => |node_ptr| { - const node = *node_ptr; - if (requireSemiColon(node)) { - stack.push(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; - continue; - } - continue; - }, - - State.AsmOutputItems => |items| { - const lbracket_index = tok_it.index; - const lbracket_ptr = ??tok_it.next(); - if (lbracket_ptr.id != Token.Id.LBracket) { - _ = tok_it.prev(); - continue; - } - - const node = try createNode(arena, ast.Node.AsmOutput, - ast.Node.AsmOutput { - .base = undefined, - .symbolic_name = undefined, - .constraint = undefined, - .kind = undefined, - } - ); - try items.push(node); - - stack.push(State { .AsmOutputItems = items }) catch unreachable; - try stack.push(State { .IfToken = Token.Id.Comma }); - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .AsmOutputReturnOrType = node }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); - try stack.push(State { .ExpectToken = Token.Id.RBracket }); - try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); - continue; - }, - State.AsmOutputReturnOrType => |node| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Identifier => { - node.kind = ast.Node.AsmOutput.Kind { .Variable = try createLiteral(arena, ast.Node.Identifier, token_index) }; - continue; - }, - Token.Id.Arrow => { - node.kind = ast.Node.AsmOutput.Kind { .Return = undefined }; - try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } }); - continue; - }, - else => { - *(try tree.errors.addOne()) = Error { - .ExpectedAsmOutputReturnOrType = Error.ExpectedAsmOutputReturnOrType { - .token = token_index, - }, - }; - return tree; - }, - } - }, - State.AsmInputItems => |items| { - const lbracket_index = tok_it.index; - const lbracket_ptr = ??tok_it.next(); - if (lbracket_ptr.id != Token.Id.LBracket) { - _ = tok_it.prev(); - continue; - } - - const node = try createNode(arena, ast.Node.AsmInput, - ast.Node.AsmInput { - .base = undefined, - .symbolic_name = undefined, - .constraint = undefined, - .expr = undefined, - } - ); - try items.push(node); - - stack.push(State { .AsmInputItems = items }) catch unreachable; - try stack.push(State { .IfToken = Token.Id.Comma }); - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); - try stack.push(State { .ExpectToken = Token.Id.RBracket }); - try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); - continue; - }, - State.AsmClobberItems => |items| { - stack.push(State { .AsmClobberItems = items }) catch unreachable; - try stack.push(State { .IfToken = Token.Id.Comma }); - try stack.push(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } }); - continue; - }, - - - State.ExprListItemOrEnd => |list_state| { - if (eatToken(&tok_it, list_state.end)) |token_index| { - *list_state.ptr = token_index; - continue; - } - - stack.push(State { .ExprListCommaOrEnd = list_state }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } }); - continue; - }, - State.ExprListCommaOrEnd => |list_state| { - switch (expectCommaOrEnd(&tok_it, list_state.end)) { - ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { - *list_state.ptr = end; - continue; - } else { - stack.push(State { .ExprListItemOrEnd = list_state }) catch unreachable; - continue; - }, - ExpectCommaOrEndResult.parse_error => |e| { - try tree.errors.push(e); - return tree; - }, - } - }, - State.FieldInitListItemOrEnd => |list_state| { - while (try eatLineComment(arena, &tok_it)) |line_comment| { - try list_state.list.push(&line_comment.base); - } - - if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; - continue; - } - - const node = try arena.construct(ast.Node.FieldInitializer { - .base = ast.Node { - .id = ast.Node.Id.FieldInitializer, - }, - .period_token = undefined, - .name_token = undefined, - .expr = undefined, - }); - try list_state.list.push(&node.base); - - stack.push(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx{ .Required = &node.expr } }); - try stack.push(State { .ExpectToken = Token.Id.Equal }); - try stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Identifier, - .ptr = &node.name_token, - } - }); - try stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Period, - .ptr = &node.period_token, - } - }); - continue; - }, - State.FieldInitListCommaOrEnd => |list_state| { - switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { - ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { - *list_state.ptr = end; - continue; - } else { - stack.push(State { .FieldInitListItemOrEnd = list_state }) catch unreachable; - continue; - }, - ExpectCommaOrEndResult.parse_error => |e| { - try tree.errors.push(e); - return tree; - }, - } - }, - State.FieldListCommaOrEnd => |container_decl| { - switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { - ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { - container_decl.rbrace_token = end; - continue; - } else { - try stack.push(State { .ContainerDecl = container_decl }); - continue; - }, - ExpectCommaOrEndResult.parse_error => |e| { - try tree.errors.push(e); - return tree; - }, - } - }, - State.ErrorTagListItemOrEnd => |list_state| { - while (try eatLineComment(arena, &tok_it)) |line_comment| { - try list_state.list.push(&line_comment.base); - } - - if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; - continue; - } - - const node_ptr = try list_state.list.addOne(); - - try stack.push(State { .ErrorTagListCommaOrEnd = list_state }); - try stack.push(State { .ErrorTag = node_ptr }); - continue; - }, - State.ErrorTagListCommaOrEnd => |list_state| { - switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { - ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { - *list_state.ptr = end; - continue; - } else { - stack.push(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable; - continue; - }, - ExpectCommaOrEndResult.parse_error => |e| { - try tree.errors.push(e); - return tree; - }, - } - }, - State.SwitchCaseOrEnd => |list_state| { - while (try eatLineComment(arena, &tok_it)) |line_comment| { - try list_state.list.push(&line_comment.base); - } - - if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; - continue; - } - - const comments = try eatDocComments(arena, &tok_it); - const node = try arena.construct(ast.Node.SwitchCase { - .base = ast.Node { - .id = ast.Node.Id.SwitchCase, - }, - .items = ast.Node.SwitchCase.ItemList.init(arena), - .payload = null, - .expr = undefined, - }); - try list_state.list.push(&node.base); - try stack.push(State { .SwitchCaseCommaOrEnd = list_state }); - try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); - try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.push(State { .SwitchCaseFirstItem = &node.items }); - - continue; - }, - - State.SwitchCaseCommaOrEnd => |list_state| { - switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) { - ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { - *list_state.ptr = end; - continue; - } else { - try stack.push(State { .SwitchCaseOrEnd = list_state }); - continue; - }, - ExpectCommaOrEndResult.parse_error => |e| { - try tree.errors.push(e); - return tree; - }, - } - }, - - State.SwitchCaseFirstItem => |case_items| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (token_ptr.id == Token.Id.Keyword_else) { - const else_node = try arena.construct(ast.Node.SwitchElse { - .base = ast.Node{ .id = ast.Node.Id.SwitchElse}, - .token = token_index, - }); - try case_items.push(&else_node.base); - - try stack.push(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); - continue; - } else { - _ = tok_it.prev(); - try stack.push(State { .SwitchCaseItem = case_items }); - continue; - } - }, - State.SwitchCaseItem => |case_items| { - stack.push(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; - try stack.push(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } }); - }, - State.SwitchCaseItemCommaOrEnd => |case_items| { - switch (expectCommaOrEnd(&tok_it, Token.Id.EqualAngleBracketRight)) { - ExpectCommaOrEndResult.end_token => |t| { - if (t == null) { - stack.push(State { .SwitchCaseItem = case_items }) catch unreachable; - } - continue; - }, - ExpectCommaOrEndResult.parse_error => |e| { - try tree.errors.push(e); - return tree; - }, - } - continue; - }, - - - State.SuspendBody => |suspend_node| { - if (suspend_node.payload != null) { - try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } }); - } - continue; - }, - State.AsyncAllocator => |async_node| { - if (eatToken(&tok_it, Token.Id.AngleBracketLeft) == null) { - continue; - } - - async_node.rangle_bracket = TokenIndex(0); - try stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.AngleBracketRight, - .ptr = &??async_node.rangle_bracket, - } - }); - try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } }); - continue; - }, - State.AsyncEnd => |ctx| { - const node = ctx.ctx.get() ?? continue; - - switch (node.id) { - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node); - fn_proto.async_attr = ctx.attribute; - continue; - }, - ast.Node.Id.SuffixOp => { - const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node); - if (suffix_op.op == @TagType(ast.Node.SuffixOp.Op).Call) { - suffix_op.op.Call.async_attr = ctx.attribute; - continue; - } - - *(try tree.errors.addOne()) = Error { - .ExpectedCall = Error.ExpectedCall { .node = node }, - }; - return tree; - }, - else => { - *(try tree.errors.addOne()) = Error { - .ExpectedCallOrFnProto = Error.ExpectedCallOrFnProto { .node = node }, - }; - return tree; - } - } - }, - - - State.ExternType => |ctx| { - if (eatToken(&tok_it, Token.Id.Keyword_fn)) |fn_token| { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - }, - .doc_comments = ctx.comments, - .visib_token = null, - .name_token = null, - .fn_token = fn_token, - .params = ast.Node.FnProto.ParamList.init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = ctx.extern_token, - .cc_token = null, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - }); - ctx.opt_ctx.store(&fn_proto.base); - stack.push(State { .FnProto = fn_proto }) catch unreachable; - continue; - } - - stack.push(State { - .ContainerKind = ContainerKindCtx { - .opt_ctx = ctx.opt_ctx, - .ltoken = ctx.extern_token, - .layout = ast.Node.ContainerDecl.Layout.Extern, - }, - }) catch unreachable; - continue; - }, - State.SliceOrArrayAccess => |node| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Ellipsis2 => { - const start = node.op.ArrayAccess; - node.op = ast.Node.SuffixOp.Op { - .Slice = ast.Node.SuffixOp.Op.Slice { - .start = start, - .end = null, - } - }; - - stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RBracket, - .ptr = &node.rtoken, - } - }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } }); - continue; - }, - Token.Id.RBracket => { - node.rtoken = token_index; - continue; - }, - else => { - *(try tree.errors.addOne()) = Error { - .ExpectedSliceOrRBracket = Error.ExpectedSliceOrRBracket { .token = token_index }, - }; - return tree; - } - } - }, - State.SliceOrArrayType => |node| { - if (eatToken(&tok_it, Token.Id.RBracket)) |_| { - node.op = ast.Node.PrefixOp.Op { - .SliceType = ast.Node.PrefixOp.AddrOfInfo { - .align_expr = null, - .bit_offset_start_token = null, - .bit_offset_end_token = null, - .const_token = null, - .volatile_token = null, - } - }; - stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - try stack.push(State { .AddrOfModifiers = &node.op.SliceType }); - continue; - } - - node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined }; - stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - try stack.push(State { .ExpectToken = Token.Id.RBracket }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } }); - continue; - }, - State.AddrOfModifiers => |addr_of_info| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Keyword_align => { - stack.push(state) catch unreachable; - if (addr_of_info.align_expr != null) { - *(try tree.errors.addOne()) = Error { - .ExtraAlignQualifier = Error.ExtraAlignQualifier { .token = token_index }, - }; - return tree; - } - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - Token.Id.Keyword_const => { - stack.push(state) catch unreachable; - if (addr_of_info.const_token != null) { - *(try tree.errors.addOne()) = Error { - .ExtraConstQualifier = Error.ExtraConstQualifier { .token = token_index }, - }; - return tree; - } - addr_of_info.const_token = token_index; - continue; - }, - Token.Id.Keyword_volatile => { - stack.push(state) catch unreachable; - if (addr_of_info.volatile_token != null) { - *(try tree.errors.addOne()) = Error { - .ExtraVolatileQualifier = Error.ExtraVolatileQualifier { .token = token_index }, - }; - return tree; - } - addr_of_info.volatile_token = token_index; - continue; - }, - else => { - _ = tok_it.prev(); - continue; - }, - } - }, - - - State.Payload => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (token_ptr.id != Token.Id.Pipe) { - if (opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = token_index, - .expected_id = Token.Id.Pipe, - }, - }; - return tree; - } - - _ = tok_it.prev(); - continue; - } - - const node = try createToCtxNode(arena, opt_ctx, ast.Node.Payload, - ast.Node.Payload { - .base = undefined, - .lpipe = token_index, - .error_symbol = undefined, - .rpipe = undefined - } - ); - - stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } - }) catch unreachable; - try stack.push(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } }); - continue; - }, - State.PointerPayload => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (token_ptr.id != Token.Id.Pipe) { - if (opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = token_index, - .expected_id = Token.Id.Pipe, - }, - }; - return tree; - } - - _ = tok_it.prev(); - continue; - } - - const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload, - ast.Node.PointerPayload { - .base = undefined, - .lpipe = token_index, - .ptr_token = null, - .value_symbol = undefined, - .rpipe = undefined - } - ); - - try stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } - }); - try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); - try stack.push(State { - .OptionalTokenSave = OptionalTokenSave { - .id = Token.Id.Asterisk, - .ptr = &node.ptr_token, - } - }); - continue; - }, - State.PointerIndexPayload => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (token_ptr.id != Token.Id.Pipe) { - if (opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = token_index, - .expected_id = Token.Id.Pipe, - }, - }; - return tree; - } - - _ = tok_it.prev(); - continue; - } - - const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload, - ast.Node.PointerIndexPayload { - .base = undefined, - .lpipe = token_index, - .ptr_token = null, - .value_symbol = undefined, - .index_symbol = null, - .rpipe = undefined - } - ); - - stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } - }) catch unreachable; - try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } }); - try stack.push(State { .IfToken = Token.Id.Comma }); - try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); - try stack.push(State { - .OptionalTokenSave = OptionalTokenSave { - .id = Token.Id.Asterisk, - .ptr = &node.ptr_token, - } - }); - continue; - }, - - - State.Expression => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression, - ast.Node.ControlFlowExpression { - .base = undefined, - .ltoken = token_index, - .kind = undefined, - .rhs = null, - } - ); - - stack.push(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable; - - switch (token_ptr.id) { - Token.Id.Keyword_break => { - node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null }; - try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } }); - try stack.push(State { .IfToken = Token.Id.Colon }); - }, - Token.Id.Keyword_continue => { - node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null }; - try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } }); - try stack.push(State { .IfToken = Token.Id.Colon }); - }, - Token.Id.Keyword_return => { - node.kind = ast.Node.ControlFlowExpression.Kind.Return; - }, - else => unreachable, - } - continue; - }, - Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token_index, - .op = switch (token_ptr.id) { - Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} }, - Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} }, - Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} }, - else => unreachable, - }, - .rhs = undefined, - } - ); - - stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - continue; - }, - else => { - if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) { - _ = tok_it.prev(); - stack.push(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable; - } - continue; - } - } - }, - State.RangeExpressionBegin => |opt_ctx| { - stack.push(State { .RangeExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .Expression = opt_ctx }); - continue; - }, - State.RangeExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = ellipsis3, - .op = ast.Node.InfixOp.Op.Range, - .rhs = undefined, - } - ); - stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - continue; - } - }, - State.AssignmentExpressionBegin => |opt_ctx| { - stack.push(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .Expression = opt_ctx }); - continue; - }, - - State.AssignmentExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (tokenIdToAssignment(token_ptr.id)) |ass_id| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = ass_id, - .rhs = undefined, - } - ); - stack.push(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - _ = tok_it.prev(); - continue; - } - }, - - State.UnwrapExpressionBegin => |opt_ctx| { - stack.push(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .BoolOrExpressionBegin = opt_ctx }); - continue; - }, - - State.UnwrapExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = unwrap_id, - .rhs = undefined, - } - ); - - stack.push(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }); - - if (node.op == ast.Node.InfixOp.Op.Catch) { - try stack.push(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } }); - } - continue; - } else { - _ = tok_it.prev(); - continue; - } - }, - - State.BoolOrExpressionBegin => |opt_ctx| { - stack.push(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .BoolAndExpressionBegin = opt_ctx }); - continue; - }, - - State.BoolOrExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - if (eatToken(&tok_it, Token.Id.Keyword_or)) |or_token| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = or_token, - .op = ast.Node.InfixOp.Op.BoolOr, - .rhs = undefined, - } - ); - stack.push(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, - - State.BoolAndExpressionBegin => |opt_ctx| { - stack.push(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .ComparisonExpressionBegin = opt_ctx }); - continue; - }, - - State.BoolAndExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - if (eatToken(&tok_it, Token.Id.Keyword_and)) |and_token| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = and_token, - .op = ast.Node.InfixOp.Op.BoolAnd, - .rhs = undefined, - } - ); - stack.push(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, - - State.ComparisonExpressionBegin => |opt_ctx| { - stack.push(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .BinaryOrExpressionBegin = opt_ctx }); - continue; - }, - - State.ComparisonExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (tokenIdToComparison(token_ptr.id)) |comp_id| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = comp_id, - .rhs = undefined, - } - ); - stack.push(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - _ = tok_it.prev(); - continue; - } - }, - - State.BinaryOrExpressionBegin => |opt_ctx| { - stack.push(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .BinaryXorExpressionBegin = opt_ctx }); - continue; - }, - - State.BinaryOrExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - if (eatToken(&tok_it, Token.Id.Pipe)) |pipe| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = pipe, - .op = ast.Node.InfixOp.Op.BitOr, - .rhs = undefined, - } - ); - stack.push(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, - - State.BinaryXorExpressionBegin => |opt_ctx| { - stack.push(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .BinaryAndExpressionBegin = opt_ctx }); - continue; - }, - - State.BinaryXorExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - if (eatToken(&tok_it, Token.Id.Caret)) |caret| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = caret, - .op = ast.Node.InfixOp.Op.BitXor, - .rhs = undefined, - } - ); - stack.push(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, - - State.BinaryAndExpressionBegin => |opt_ctx| { - stack.push(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .BitShiftExpressionBegin = opt_ctx }); - continue; - }, - - State.BinaryAndExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - if (eatToken(&tok_it, Token.Id.Ampersand)) |ampersand| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = ampersand, - .op = ast.Node.InfixOp.Op.BitAnd, - .rhs = undefined, - } - ); - stack.push(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, - - State.BitShiftExpressionBegin => |opt_ctx| { - stack.push(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .AdditionExpressionBegin = opt_ctx }); - continue; - }, - - State.BitShiftExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = bitshift_id, - .rhs = undefined, - } - ); - stack.push(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - _ = tok_it.prev(); - continue; - } - }, - - State.AdditionExpressionBegin => |opt_ctx| { - stack.push(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .MultiplyExpressionBegin = opt_ctx }); - continue; - }, - - State.AdditionExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (tokenIdToAddition(token_ptr.id)) |add_id| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = add_id, - .rhs = undefined, - } - ); - stack.push(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - _ = tok_it.prev(); - continue; - } - }, - - State.MultiplyExpressionBegin => |opt_ctx| { - stack.push(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .CurlySuffixExpressionBegin = opt_ctx }); - continue; - }, - - State.MultiplyExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (tokenIdToMultiply(token_ptr.id)) |mult_id| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = mult_id, - .rhs = undefined, - } - ); - stack.push(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - _ = tok_it.prev(); - continue; - } - }, - - State.CurlySuffixExpressionBegin => |opt_ctx| { - stack.push(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .IfToken = Token.Id.LBrace }); - try stack.push(State { .TypeExprBegin = opt_ctx }); - continue; - }, - - State.CurlySuffixExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - if ((??tok_it.peek()).id == Token.Id.Period) { - const node = try arena.construct(ast.Node.SuffixOp { - .base = ast.Node { .id = ast.Node.Id.SuffixOp }, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena), - }, - .rtoken = undefined, - }); - opt_ctx.store(&node.base); - - stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .IfToken = Token.Id.LBrace }); - try stack.push(State { - .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)) { - .list = &node.op.StructInitializer, - .ptr = &node.rtoken, - } - }); - continue; - } - - const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { - .base = undefined, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena), - }, - .rtoken = undefined, - } - ); - stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .IfToken = Token.Id.LBrace }); - try stack.push(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.op.ArrayInitializer, - .end = Token.Id.RBrace, - .ptr = &node.rtoken, - } - }); - continue; - }, - - State.TypeExprBegin => |opt_ctx| { - stack.push(State { .TypeExprEnd = opt_ctx }) catch unreachable; - try stack.push(State { .PrefixOpExpression = opt_ctx }); - continue; - }, - - State.TypeExprEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - if (eatToken(&tok_it, Token.Id.Bang)) |bang| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = bang, - .op = ast.Node.InfixOp.Op.ErrorUnion, - .rhs = undefined, - } - ); - stack.push(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, - - State.PrefixOpExpression => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| { - var node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token_index, - .op = prefix_id, - .rhs = undefined, - } - ); - - // Treat '**' token as two derefs - if (token_ptr.id == Token.Id.AsteriskAsterisk) { - const child = try createNode(arena, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token_index, - .op = prefix_id, - .rhs = undefined, - } - ); - node.rhs = &child.base; - node = child; - } - - stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - if (node.op == ast.Node.PrefixOp.Op.AddrOf) { - try stack.push(State { .AddrOfModifiers = &node.op.AddrOf }); - } - continue; - } else { - _ = tok_it.prev(); - stack.push(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable; - continue; - } - }, - - State.SuffixOpExpressionBegin => |opt_ctx| { - if (eatToken(&tok_it, Token.Id.Keyword_async)) |async_token| { - const async_node = try createNode(arena, ast.Node.AsyncAttribute, - ast.Node.AsyncAttribute { - .base = undefined, - .async_token = async_token, - .allocator_type = null, - .rangle_bracket = null, - } - ); - stack.push(State { - .AsyncEnd = AsyncEndCtx { - .ctx = opt_ctx, - .attribute = async_node, - } - }) catch unreachable; - try stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }); - try stack.push(State { .PrimaryExpression = opt_ctx.toRequired() }); - try stack.push(State { .AsyncAllocator = async_node }); - continue; - } - - stack.push(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .PrimaryExpression = opt_ctx }); - continue; - }, - - State.SuffixOpExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.LParen => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { - .base = undefined, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .Call = ast.Node.SuffixOp.Op.Call { - .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena), - .async_attr = null, - } - }, - .rtoken = undefined, - } - ); - stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.op.Call.params, - .end = Token.Id.RParen, - .ptr = &node.rtoken, - } - }); - continue; - }, - Token.Id.LBracket => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { - .base = undefined, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .ArrayAccess = undefined, - }, - .rtoken = undefined - } - ); - stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .SliceOrArrayAccess = node }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }}); - continue; - }, - Token.Id.Period => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = ast.Node.InfixOp.Op.Period, - .rhs = undefined, - } - ); - stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .Identifier = OptionalCtx { .Required = &node.rhs } }); - continue; - }, - else => { - _ = tok_it.prev(); - continue; - }, - } - }, - - State.PrimaryExpression => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.IntegerLiteral => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token_index); - continue; - }, - Token.Id.FloatLiteral => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token_index); - continue; - }, - Token.Id.CharLiteral => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token_index); - continue; - }, - Token.Id.Keyword_undefined => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token_index); - continue; - }, - Token.Id.Keyword_true, Token.Id.Keyword_false => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token_index); - continue; - }, - Token.Id.Keyword_null => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token_index); - continue; - }, - Token.Id.Keyword_this => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token_index); - continue; - }, - Token.Id.Keyword_var => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token_index); - continue; - }, - Token.Id.Keyword_unreachable => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token_index); - continue; - }, - Token.Id.Keyword_promise => { - const node = try arena.construct(ast.Node.PromiseType { - .base = ast.Node { - .id = ast.Node.Id.PromiseType, - }, - .promise_token = token_index, - .result = null, - }); - opt_ctx.store(&node.base); - const next_token_index = tok_it.index; - const next_token_ptr = ??tok_it.next(); - if (next_token_ptr.id != Token.Id.Arrow) { - _ = tok_it.prev(); - continue; - } - node.result = ast.Node.PromiseType.Result { - .arrow_token = next_token_index, - .return_type = undefined, - }; - const return_type_ptr = &((??node.result).return_type); - try stack.push(State { .Expression = OptionalCtx { .Required = return_type_ptr, } }); - continue; - }, - Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { - opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? unreachable); - continue; - }, - Token.Id.LParen => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression, - ast.Node.GroupedExpression { - .base = undefined, - .lparen = token_index, - .expr = undefined, - .rparen = undefined, - } - ); - stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RParen, - .ptr = &node.rparen, - } - }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); - continue; - }, - Token.Id.Builtin => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall, - ast.Node.BuiltinCall { - .base = undefined, - .builtin_token = token_index, - .params = ast.Node.BuiltinCall.ParamList.init(arena), - .rparen_token = undefined, - } - ); - stack.push(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.params, - .end = Token.Id.RParen, - .ptr = &node.rparen_token, - } - }) catch unreachable; - try stack.push(State { .ExpectToken = Token.Id.LParen, }); - continue; - }, - Token.Id.LBracket => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token_index, - .op = undefined, - .rhs = undefined, - } - ); - stack.push(State { .SliceOrArrayType = node }) catch unreachable; - continue; - }, - Token.Id.Keyword_error => { - stack.push(State { - .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx { - .error_token = token_index, - .opt_ctx = opt_ctx - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_packed => { - stack.push(State { - .ContainerKind = ContainerKindCtx { - .opt_ctx = opt_ctx, - .ltoken = token_index, - .layout = ast.Node.ContainerDecl.Layout.Packed, - }, - }) catch unreachable; - continue; - }, - Token.Id.Keyword_extern => { - stack.push(State { - .ExternType = ExternTypeCtx { - .opt_ctx = opt_ctx, - .extern_token = token_index, - .comments = null, - }, - }) catch unreachable; - continue; - }, - Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => { - _ = tok_it.prev(); - stack.push(State { - .ContainerKind = ContainerKindCtx { - .opt_ctx = opt_ctx, - .ltoken = token_index, - .layout = ast.Node.ContainerDecl.Layout.Auto, - }, - }) catch unreachable; - continue; - }, - Token.Id.Identifier => { - stack.push(State { - .MaybeLabeledExpression = MaybeLabeledExpressionCtx { - .label = token_index, - .opt_ctx = opt_ctx - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_fn => { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - }, - .doc_comments = null, - .visib_token = null, - .name_token = null, - .fn_token = token_index, - .params = ast.Node.FnProto.ParamList.init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = null, - .cc_token = null, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - }); - opt_ctx.store(&fn_proto.base); - stack.push(State { .FnProto = fn_proto }) catch unreachable; - continue; - }, - Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - }, - .doc_comments = null, - .visib_token = null, - .name_token = null, - .fn_token = undefined, - .params = ast.Node.FnProto.ParamList.init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = null, - .cc_token = token_index, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - }); - opt_ctx.store(&fn_proto.base); - stack.push(State { .FnProto = fn_proto }) catch unreachable; - try stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token - } - }); - continue; - }, - Token.Id.Keyword_asm => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.Asm, - ast.Node.Asm { - .base = undefined, - .asm_token = token_index, - .volatile_token = null, - .template = undefined, - .outputs = ast.Node.Asm.OutputList.init(arena), - .inputs = ast.Node.Asm.InputList.init(arena), - .clobbers = ast.Node.Asm.ClobberList.init(arena), - .rparen = undefined, - } - ); - stack.push(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RParen, - .ptr = &node.rparen, - } - }) catch unreachable; - try stack.push(State { .AsmClobberItems = &node.clobbers }); - try stack.push(State { .IfToken = Token.Id.Colon }); - try stack.push(State { .AsmInputItems = &node.inputs }); - try stack.push(State { .IfToken = Token.Id.Colon }); - try stack.push(State { .AsmOutputItems = &node.outputs }); - try stack.push(State { .IfToken = Token.Id.Colon }); - try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.template } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - try stack.push(State { - .OptionalTokenSave = OptionalTokenSave { - .id = Token.Id.Keyword_volatile, - .ptr = &node.volatile_token, - } - }); - }, - Token.Id.Keyword_inline => { - stack.push(State { - .Inline = InlineCtx { - .label = null, - .inline_token = token_index, - .opt_ctx = opt_ctx, - } - }) catch unreachable; - continue; - }, - else => { - if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) { - _ = tok_it.prev(); - if (opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index }, - }; - return tree; - } - } - continue; - } - } - }, - - - State.ErrorTypeOrSetDecl => |ctx| { - if (eatToken(&tok_it, Token.Id.LBrace) == null) { - _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token); - continue; - } - - const node = try arena.construct(ast.Node.ErrorSetDecl { - .base = ast.Node { - .id = ast.Node.Id.ErrorSetDecl, - }, - .error_token = ctx.error_token, - .decls = ast.Node.ErrorSetDecl.DeclList.init(arena), - .rbrace_token = undefined, - }); - ctx.opt_ctx.store(&node.base); - - stack.push(State { - .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)) { - .list = &node.decls, - .ptr = &node.rbrace_token, - } - }) catch unreachable; - continue; - }, - State.StringLiteral => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - opt_ctx.store( - (try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? { - _ = tok_it.prev(); - if (opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index }, - }; - return tree; - } - - continue; - } - ); - }, - - State.Identifier => |opt_ctx| { - if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token); - continue; - } - - if (opt_ctx != OptionalCtx.Optional) { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = token_index, - .expected_id = Token.Id.Identifier, - }, - }; - return tree; - } - }, - - State.ErrorTag => |node_ptr| { - const comments = try eatDocComments(arena, &tok_it); - const ident_token_index = tok_it.index; - const ident_token_ptr = ??tok_it.next(); - if (ident_token_ptr.id != Token.Id.Identifier) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = ident_token_index, - .expected_id = Token.Id.Identifier, - }, - }; - return tree; - } - - const node = try arena.construct(ast.Node.ErrorTag { - .base = ast.Node { - .id = ast.Node.Id.ErrorTag, - }, - .doc_comments = comments, - .name_token = ident_token_index, - }); - *node_ptr = &node.base; - continue; - }, - - State.ExpectToken => |token_id| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (token_ptr.id != token_id) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = token_index, - .expected_id = token_id, - }, - }; - return tree; - } - continue; - }, - State.ExpectTokenSave => |expect_token_save| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (token_ptr.id != expect_token_save.id) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = token_index, - .expected_id = expect_token_save.id, - }, - }; - return tree; - } - *expect_token_save.ptr = token_index; - continue; - }, - State.IfToken => |token_id| { - if (eatToken(&tok_it, token_id)) |_| { - continue; - } - - _ = stack.pop(); - continue; - }, - State.IfTokenSave => |if_token_save| { - if (eatToken(&tok_it, if_token_save.id)) |token_index| { - *if_token_save.ptr = token_index; - continue; - } - - _ = stack.pop(); - continue; - }, - State.OptionalTokenSave => |optional_token_save| { - if (eatToken(&tok_it, optional_token_save.id)) |token_index| { - *optional_token_save.ptr = token_index; - continue; - } - - continue; - }, - } - } -} - -const AnnotatedToken = struct { - ptr: &Token, - index: TokenIndex, -}; - -const TopLevelDeclCtx = struct { - decls: &ast.Node.Root.DeclList, - visib_token: ?TokenIndex, - extern_export_inline_token: ?AnnotatedToken, - lib_name: ?&ast.Node, - comments: ?&ast.Node.DocComment, -}; - -const VarDeclCtx = struct { - mut_token: TokenIndex, - visib_token: ?TokenIndex, - comptime_token: ?TokenIndex, - extern_export_token: ?TokenIndex, - lib_name: ?&ast.Node, - list: &ast.Node.Root.DeclList, - comments: ?&ast.Node.DocComment, -}; - -const TopLevelExternOrFieldCtx = struct { - visib_token: TokenIndex, - container_decl: &ast.Node.ContainerDecl, - comments: ?&ast.Node.DocComment, -}; - -const ExternTypeCtx = struct { - opt_ctx: OptionalCtx, - extern_token: TokenIndex, - comments: ?&ast.Node.DocComment, -}; - -const ContainerKindCtx = struct { - opt_ctx: OptionalCtx, - ltoken: TokenIndex, - layout: ast.Node.ContainerDecl.Layout, -}; - -const ExpectTokenSave = struct { - id: @TagType(Token.Id), - ptr: &TokenIndex, -}; - -const OptionalTokenSave = struct { - id: @TagType(Token.Id), - ptr: &?TokenIndex, -}; - -const ExprListCtx = struct { - list: &ast.Node.SuffixOp.Op.InitList, - end: Token.Id, - ptr: &TokenIndex, -}; - -fn ListSave(comptime List: type) type { - return struct { - list: &List, - ptr: &TokenIndex, - }; -} - -const MaybeLabeledExpressionCtx = struct { - label: TokenIndex, - opt_ctx: OptionalCtx, -}; - -const LabelCtx = struct { - label: ?TokenIndex, - opt_ctx: OptionalCtx, -}; - -const InlineCtx = struct { - label: ?TokenIndex, - inline_token: ?TokenIndex, - opt_ctx: OptionalCtx, -}; - -const LoopCtx = struct { - label: ?TokenIndex, - inline_token: ?TokenIndex, - loop_token: TokenIndex, - opt_ctx: OptionalCtx, -}; - -const AsyncEndCtx = struct { - ctx: OptionalCtx, - attribute: &ast.Node.AsyncAttribute, -}; - -const ErrorTypeOrSetDeclCtx = struct { - opt_ctx: OptionalCtx, - error_token: TokenIndex, -}; - -const ParamDeclEndCtx = struct { - fn_proto: &ast.Node.FnProto, - param_decl: &ast.Node.ParamDecl, -}; - -const ComptimeStatementCtx = struct { - comptime_token: TokenIndex, - block: &ast.Node.Block, -}; - -const OptionalCtx = union(enum) { - Optional: &?&ast.Node, - RequiredNull: &?&ast.Node, - Required: &&ast.Node, - - pub fn store(self: &const OptionalCtx, value: &ast.Node) void { - switch (*self) { - OptionalCtx.Optional => |ptr| *ptr = value, - OptionalCtx.RequiredNull => |ptr| *ptr = value, - OptionalCtx.Required => |ptr| *ptr = value, - } - } - - pub fn get(self: &const OptionalCtx) ?&ast.Node { - switch (*self) { - OptionalCtx.Optional => |ptr| return *ptr, - OptionalCtx.RequiredNull => |ptr| return ??*ptr, - OptionalCtx.Required => |ptr| return *ptr, - } - } - - pub fn toRequired(self: &const OptionalCtx) OptionalCtx { - switch (*self) { - OptionalCtx.Optional => |ptr| { - return OptionalCtx { .RequiredNull = ptr }; - }, - OptionalCtx.RequiredNull => |ptr| return *self, - OptionalCtx.Required => |ptr| return *self, - } - } -}; - -const AddCommentsCtx = struct { - node_ptr: &&ast.Node, - comments: ?&ast.Node.DocComment, -}; - -const State = union(enum) { - TopLevel, - TopLevelExtern: TopLevelDeclCtx, - TopLevelLibname: TopLevelDeclCtx, - TopLevelDecl: TopLevelDeclCtx, - TopLevelExternOrField: TopLevelExternOrFieldCtx, - - ContainerKind: ContainerKindCtx, - ContainerInitArgStart: &ast.Node.ContainerDecl, - ContainerInitArg: &ast.Node.ContainerDecl, - ContainerDecl: &ast.Node.ContainerDecl, - - VarDecl: VarDeclCtx, - VarDeclAlign: &ast.Node.VarDecl, - VarDeclEq: &ast.Node.VarDecl, - - FnDef: &ast.Node.FnProto, - FnProto: &ast.Node.FnProto, - FnProtoAlign: &ast.Node.FnProto, - FnProtoReturnType: &ast.Node.FnProto, - - ParamDecl: &ast.Node.FnProto, - ParamDeclAliasOrComptime: &ast.Node.ParamDecl, - ParamDeclName: &ast.Node.ParamDecl, - ParamDeclEnd: ParamDeclEndCtx, - ParamDeclComma: &ast.Node.FnProto, - - MaybeLabeledExpression: MaybeLabeledExpressionCtx, - LabeledExpression: LabelCtx, - Inline: InlineCtx, - While: LoopCtx, - WhileContinueExpr: &?&ast.Node, - For: LoopCtx, - Else: &?&ast.Node.Else, - - Block: &ast.Node.Block, - Statement: &ast.Node.Block, - ComptimeStatement: ComptimeStatementCtx, - Semicolon: &&ast.Node, - - AsmOutputItems: &ast.Node.Asm.OutputList, - AsmOutputReturnOrType: &ast.Node.AsmOutput, - AsmInputItems: &ast.Node.Asm.InputList, - AsmClobberItems: &ast.Node.Asm.ClobberList, - - ExprListItemOrEnd: ExprListCtx, - ExprListCommaOrEnd: ExprListCtx, - FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), - FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), - FieldListCommaOrEnd: &ast.Node.ContainerDecl, - FieldInitValue: OptionalCtx, - ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), - ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), - SwitchCaseOrEnd: ListSave(ast.Node.Switch.CaseList), - SwitchCaseCommaOrEnd: ListSave(ast.Node.Switch.CaseList), - SwitchCaseFirstItem: &ast.Node.SwitchCase.ItemList, - SwitchCaseItem: &ast.Node.SwitchCase.ItemList, - SwitchCaseItemCommaOrEnd: &ast.Node.SwitchCase.ItemList, - - SuspendBody: &ast.Node.Suspend, - AsyncAllocator: &ast.Node.AsyncAttribute, - AsyncEnd: AsyncEndCtx, - - ExternType: ExternTypeCtx, - SliceOrArrayAccess: &ast.Node.SuffixOp, - SliceOrArrayType: &ast.Node.PrefixOp, - AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo, - - Payload: OptionalCtx, - PointerPayload: OptionalCtx, - PointerIndexPayload: OptionalCtx, - - Expression: OptionalCtx, - RangeExpressionBegin: OptionalCtx, - RangeExpressionEnd: OptionalCtx, - AssignmentExpressionBegin: OptionalCtx, - AssignmentExpressionEnd: OptionalCtx, - UnwrapExpressionBegin: OptionalCtx, - UnwrapExpressionEnd: OptionalCtx, - BoolOrExpressionBegin: OptionalCtx, - BoolOrExpressionEnd: OptionalCtx, - BoolAndExpressionBegin: OptionalCtx, - BoolAndExpressionEnd: OptionalCtx, - ComparisonExpressionBegin: OptionalCtx, - ComparisonExpressionEnd: OptionalCtx, - BinaryOrExpressionBegin: OptionalCtx, - BinaryOrExpressionEnd: OptionalCtx, - BinaryXorExpressionBegin: OptionalCtx, - BinaryXorExpressionEnd: OptionalCtx, - BinaryAndExpressionBegin: OptionalCtx, - BinaryAndExpressionEnd: OptionalCtx, - BitShiftExpressionBegin: OptionalCtx, - BitShiftExpressionEnd: OptionalCtx, - AdditionExpressionBegin: OptionalCtx, - AdditionExpressionEnd: OptionalCtx, - MultiplyExpressionBegin: OptionalCtx, - MultiplyExpressionEnd: OptionalCtx, - CurlySuffixExpressionBegin: OptionalCtx, - CurlySuffixExpressionEnd: OptionalCtx, - TypeExprBegin: OptionalCtx, - TypeExprEnd: OptionalCtx, - PrefixOpExpression: OptionalCtx, - SuffixOpExpressionBegin: OptionalCtx, - SuffixOpExpressionEnd: OptionalCtx, - PrimaryExpression: OptionalCtx, - - ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx, - StringLiteral: OptionalCtx, - Identifier: OptionalCtx, - ErrorTag: &&ast.Node, - - - IfToken: @TagType(Token.Id), - IfTokenSave: ExpectTokenSave, - ExpectToken: @TagType(Token.Id), - ExpectTokenSave: ExpectTokenSave, - OptionalTokenSave: OptionalTokenSave, -}; - -fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.DocComment { - var result: ?&ast.Node.DocComment = null; - while (true) { - if (eatToken(tok_it, Token.Id.DocComment)) |line_comment| { - const node = blk: { - if (result) |comment_node| { - break :blk comment_node; - } else { - const comment_node = try arena.construct(ast.Node.DocComment { - .base = ast.Node { - .id = ast.Node.Id.DocComment, - }, - .lines = ast.Node.DocComment.LineList.init(arena), - }); - result = comment_node; - break :blk comment_node; - } - }; - try node.lines.push(line_comment); - continue; - } - break; - } - return result; -} - -fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.LineComment { - const token = eatToken(tok_it, Token.Id.LineComment) ?? return null; - return try arena.construct(ast.Node.LineComment { - .base = ast.Node { - .id = ast.Node.Id.LineComment, - }, - .token = token, - }); -} - -fn requireSemiColon(node: &const ast.Node) bool { - var n = node; - while (true) { - switch (n.id) { - ast.Node.Id.Root, - ast.Node.Id.StructField, - ast.Node.Id.UnionTag, - ast.Node.Id.EnumTag, - ast.Node.Id.ParamDecl, - ast.Node.Id.Block, - ast.Node.Id.Payload, - ast.Node.Id.PointerPayload, - ast.Node.Id.PointerIndexPayload, - ast.Node.Id.Switch, - ast.Node.Id.SwitchCase, - ast.Node.Id.SwitchElse, - ast.Node.Id.FieldInitializer, - ast.Node.Id.DocComment, - ast.Node.Id.LineComment, - ast.Node.Id.TestDecl => return false, - ast.Node.Id.While => { - const while_node = @fieldParentPtr(ast.Node.While, "base", n); - if (while_node.@"else") |@"else"| { - n = @"else".base; - continue; - } - - return while_node.body.id != ast.Node.Id.Block; - }, - ast.Node.Id.For => { - const for_node = @fieldParentPtr(ast.Node.For, "base", n); - if (for_node.@"else") |@"else"| { - n = @"else".base; - continue; - } - - return for_node.body.id != ast.Node.Id.Block; - }, - ast.Node.Id.If => { - const if_node = @fieldParentPtr(ast.Node.If, "base", n); - if (if_node.@"else") |@"else"| { - n = @"else".base; - continue; - } - - return if_node.body.id != ast.Node.Id.Block; - }, - ast.Node.Id.Else => { - const else_node = @fieldParentPtr(ast.Node.Else, "base", n); - n = else_node.body; - continue; - }, - ast.Node.Id.Defer => { - const defer_node = @fieldParentPtr(ast.Node.Defer, "base", n); - return defer_node.expr.id != ast.Node.Id.Block; - }, - ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", n); - return comptime_node.expr.id != ast.Node.Id.Block; - }, - ast.Node.Id.Suspend => { - const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", n); - if (suspend_node.body) |body| { - return body.id != ast.Node.Id.Block; - } - - return true; - }, - else => return true, - } - } -} - -fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, - token_ptr: &const Token, token_index: TokenIndex) !?&ast.Node -{ - switch (token_ptr.id) { - Token.Id.StringLiteral => { - return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base; - }, - Token.Id.MultilineStringLiteralLine => { - const node = try arena.construct(ast.Node.MultilineStringLiteral { - .base = ast.Node { .id = ast.Node.Id.MultilineStringLiteral }, - .lines = ast.Node.MultilineStringLiteral.LineList.init(arena), - }); - try node.lines.push(token_index); - while (true) { - const multiline_str_index = tok_it.index; - const multiline_str_ptr = ??tok_it.next(); - if (multiline_str_ptr.id != Token.Id.MultilineStringLiteralLine) { - _ = tok_it.prev(); - break; - } - - try node.lines.push(multiline_str_index); - } - - return &node.base; - }, - // TODO: We shouldn't need a cast, but: - // zig: /home/jc/Documents/zig/src/ir.cpp:7962: TypeTableEntry* ir_resolve_peer_types(IrAnalyze*, AstNode*, IrInstruction**, size_t): Assertion `err_set_type != nullptr' failed. - else => return (?&ast.Node)(null), - } -} - -fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: &const OptionalCtx, - token_ptr: &const Token, token_index: TokenIndex) !bool { - switch (token_ptr.id) { - Token.Id.Keyword_suspend => { - const node = try createToCtxNode(arena, ctx, ast.Node.Suspend, - ast.Node.Suspend { - .base = undefined, - .label = null, - .suspend_token = token_index, - .payload = null, - .body = null, - } - ); - - stack.push(State { .SuspendBody = node }) catch unreachable; - try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); - return true; - }, - Token.Id.Keyword_if => { - const node = try createToCtxNode(arena, ctx, ast.Node.If, - ast.Node.If { - .base = undefined, - .if_token = token_index, - .condition = undefined, - .payload = null, - .body = undefined, - .@"else" = null, - } - ); - - stack.push(State { .Else = &node.@"else" }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - return true; - }, - Token.Id.Keyword_while => { - stack.push(State { - .While = LoopCtx { - .label = null, - .inline_token = null, - .loop_token = token_index, - .opt_ctx = *ctx, - } - }) catch unreachable; - return true; - }, - Token.Id.Keyword_for => { - stack.push(State { - .For = LoopCtx { - .label = null, - .inline_token = null, - .loop_token = token_index, - .opt_ctx = *ctx, - } - }) catch unreachable; - return true; - }, - Token.Id.Keyword_switch => { - const node = try arena.construct(ast.Node.Switch { - .base = ast.Node { - .id = ast.Node.Id.Switch, - }, - .switch_token = token_index, - .expr = undefined, - .cases = ast.Node.Switch.CaseList.init(arena), - .rbrace = undefined, - }); - ctx.store(&node.base); - - stack.push(State { - .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)) { - .list = &node.cases, - .ptr = &node.rbrace, - }, - }) catch unreachable; - try stack.push(State { .ExpectToken = Token.Id.LBrace }); - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - return true; - }, - Token.Id.Keyword_comptime => { - const node = try createToCtxNode(arena, ctx, ast.Node.Comptime, - ast.Node.Comptime { - .base = undefined, - .comptime_token = token_index, - .expr = undefined, - .doc_comments = null, - } - ); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); - return true; - }, - Token.Id.LBrace => { - const block = try arena.construct(ast.Node.Block { - .base = ast.Node {.id = ast.Node.Id.Block }, - .label = null, - .lbrace = token_index, - .statements = ast.Node.Block.StatementList.init(arena), - .rbrace = undefined, - }); - ctx.store(&block.base); - stack.push(State { .Block = block }) catch unreachable; - return true; - }, - else => { - return false; - } - } -} - -const ExpectCommaOrEndResult = union(enum) { - end_token: ?TokenIndex, - parse_error: Error, -}; - -fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, end: @TagType(Token.Id)) ExpectCommaOrEndResult { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { - Token.Id.Comma => return ExpectCommaOrEndResult { .end_token = null}, - else => { - if (end == token_ptr.id) { - return ExpectCommaOrEndResult { .end_token = token_index }; - } - - return ExpectCommaOrEndResult { - .parse_error = Error { - .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd { - .token = token_index, - .end_id = end, - }, - }, - }; - }, - } -} - -fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op { - // TODO: We have to cast all cases because of this: - // error: expected type '?InfixOp', found '?@TagType(InfixOp)' - return switch (*id) { - Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = {} }, - Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = {} }, - Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = {} }, - Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = {} }, - Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = {} }, - Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = {} }, - Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = {} }, - Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = {} }, - Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = {} }, - Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = {} }, - Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = {} }, - Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = {} }, - Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = {} }, - Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = {} }, - else => null, - }; -} - -fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null }, - Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} }, - else => null, - }; -} - -fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} }, - Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} }, - Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} }, - Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} }, - Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} }, - Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} }, - else => null, - }; -} - -fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} }, - Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} }, - else => null, - }; -} - -fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} }, - Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} }, - Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} }, - Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} }, - Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} }, - else => null, - }; -} - -fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} }, - Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} }, - Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} }, - Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} }, - Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} }, - Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} }, - else => null, - }; -} - -fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { - return switch (id) { - Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} }, - Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} }, - Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} }, - Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} }, - Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} }, - Token.Id.Ampersand => ast.Node.PrefixOp.Op { - .AddrOf = ast.Node.PrefixOp.AddrOfInfo { - .align_expr = null, - .bit_offset_start_token = null, - .bit_offset_end_token = null, - .const_token = null, - .volatile_token = null, - }, - }, - Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} }, - Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} }, - Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} }, - Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } }, - else => null, - }; -} - -fn createNode(arena: &mem.Allocator, comptime T: type, init_to: &const T) !&T { - const node = try arena.create(T); - *node = *init_to; - node.base = blk: { - const id = ast.Node.typeToId(T); - break :blk ast.Node { - .id = id, - }; - }; - - return node; -} - -fn createToCtxNode(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T { - const node = try createNode(arena, T, init_to); - opt_ctx.store(&node.base); - - return node; -} - -fn createLiteral(arena: &mem.Allocator, comptime T: type, token_index: TokenIndex) !&T { - return createNode(arena, T, - T { - .base = undefined, - .token = token_index, - } - ); -} - -fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token_index: TokenIndex) !&T { - const node = try createLiteral(arena, T, token_index); - opt_ctx.store(&node.base); - - return node; -} - -fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, id: @TagType(Token.Id)) ?TokenIndex { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (token_ptr.id == id) - return token_index; - - _ = tok_it.prev(); - return null; -} - -const RenderAstFrame = struct { - node: &ast.Node, - indent: usize, -}; - -pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) !void { - var stack = SegmentedList(State, 32).init(allocator); - defer stack.deinit(); - - try stack.push(RenderAstFrame { - .node = &root_node.base, - .indent = 0, - }); - - while (stack.popOrNull()) |frame| { - { - var i: usize = 0; - while (i < frame.indent) : (i += 1) { - try stream.print(" "); - } - } - try stream.print("{}\n", @tagName(frame.node.id)); - var child_i: usize = 0; - while (frame.node.iterate(child_i)) |child| : (child_i += 1) { - try stack.push(RenderAstFrame { - .node = child, - .indent = frame.indent + 2, - }); - } - } -} - -const RenderState = union(enum) { - TopLevelDecl: &ast.Node, - ParamDecl: &ast.Node, - Text: []const u8, - Expression: &ast.Node, - VarDecl: &ast.Node.VarDecl, - Statement: &ast.Node, - PrintIndent, - Indent: usize, -}; - -pub fn renderSource(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { - var stack = SegmentedList(RenderState, 32).init(allocator); - defer stack.deinit(); - - { - try stack.push(RenderState { .Text = "\n"}); - - var i = tree.root_node.decls.len; - while (i != 0) { - i -= 1; - const decl = *tree.root_node.decls.at(i); - try stack.push(RenderState {.TopLevelDecl = decl}); - if (i != 0) { - try stack.push(RenderState { - .Text = blk: { - const prev_node = *tree.root_node.decls.at(i - 1); - const prev_node_last_token = tree.tokens.at(prev_node.lastToken()); - const loc = tree.tokenLocation(prev_node_last_token.end, decl.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }, - }); - } - } - } - - const indent_delta = 4; - var indent: usize = 0; - while (stack.pop()) |state| { - switch (state) { - RenderState.TopLevelDecl => |decl| { - switch (decl.id) { - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); - try renderComments(tree, stream, fn_proto, indent); - - if (fn_proto.body_node) |body_node| { - stack.push(RenderState { .Expression = body_node}) catch unreachable; - try stack.push(RenderState { .Text = " "}); - } else { - stack.push(RenderState { .Text = ";" }) catch unreachable; - } - - try stack.push(RenderState { .Expression = decl }); - }, - ast.Node.Id.Use => { - const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); - if (use_decl.visib_token) |visib_token| { - try stream.print("{} ", tree.tokenSlice(visib_token)); - } - try stream.print("use "); - try stack.push(RenderState { .Text = ";" }); - try stack.push(RenderState { .Expression = use_decl.expr }); - }, - ast.Node.Id.VarDecl => { - const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); - try renderComments(tree, stream, var_decl, indent); - try stack.push(RenderState { .VarDecl = var_decl}); - }, - ast.Node.Id.TestDecl => { - const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); - try renderComments(tree, stream, test_decl, indent); - try stream.print("test "); - try stack.push(RenderState { .Expression = test_decl.body_node }); - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = test_decl.name }); - }, - ast.Node.Id.StructField => { - const field = @fieldParentPtr(ast.Node.StructField, "base", decl); - try renderComments(tree, stream, field, indent); - if (field.visib_token) |visib_token| { - try stream.print("{} ", tree.tokenSlice(visib_token)); - } - try stream.print("{}: ", tree.tokenSlice(field.name_token)); - try stack.push(RenderState { .Text = "," }); - try stack.push(RenderState { .Expression = field.type_expr}); - }, - ast.Node.Id.UnionTag => { - const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); - try renderComments(tree, stream, tag, indent); - try stream.print("{}", tree.tokenSlice(tag.name_token)); - - try stack.push(RenderState { .Text = "," }); - - if (tag.value_expr) |value_expr| { - try stack.push(RenderState { .Expression = value_expr }); - try stack.push(RenderState { .Text = " = " }); - } - - if (tag.type_expr) |type_expr| { - try stream.print(": "); - try stack.push(RenderState { .Expression = type_expr}); - } - }, - ast.Node.Id.EnumTag => { - const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); - try renderComments(tree, stream, tag, indent); - try stream.print("{}", tree.tokenSlice(tag.name_token)); - - try stack.push(RenderState { .Text = "," }); - if (tag.value) |value| { - try stream.print(" = "); - try stack.push(RenderState { .Expression = value}); - } - }, - ast.Node.Id.ErrorTag => { - const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl); - try renderComments(tree, stream, tag, indent); - try stream.print("{}", tree.tokenSlice(tag.name_token)); - }, - ast.Node.Id.Comptime => { - if (requireSemiColon(decl)) { - try stack.push(RenderState { .Text = ";" }); - } - try stack.push(RenderState { .Expression = decl }); - }, - ast.Node.Id.LineComment => { - const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl); - try stream.write(tree.tokenSlice(line_comment_node.token)); - }, - else => unreachable, - } - }, - - RenderState.VarDecl => |var_decl| { - try stack.push(RenderState { .Text = ";" }); - if (var_decl.init_node) |init_node| { - try stack.push(RenderState { .Expression = init_node }); - const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = "; - try stack.push(RenderState { .Text = text }); - } - if (var_decl.align_node) |align_node| { - try stack.push(RenderState { .Text = ")" }); - try stack.push(RenderState { .Expression = align_node }); - try stack.push(RenderState { .Text = " align(" }); - } - if (var_decl.type_node) |type_node| { - try stack.push(RenderState { .Expression = type_node }); - try stack.push(RenderState { .Text = ": " }); - } - try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.name_token) }); - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) }); - - if (var_decl.comptime_token) |comptime_token| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(comptime_token) }); - } - - if (var_decl.extern_export_token) |extern_export_token| { - if (var_decl.lib_name != null) { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = ??var_decl.lib_name }); - } - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_token) }); - } - - if (var_decl.visib_token) |visib_token| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(visib_token) }); - } - }, - - RenderState.ParamDecl => |base| { - const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); - if (param_decl.comptime_token) |comptime_token| { - try stream.print("{} ", tree.tokenSlice(comptime_token)); - } - if (param_decl.noalias_token) |noalias_token| { - try stream.print("{} ", tree.tokenSlice(noalias_token)); - } - if (param_decl.name_token) |name_token| { - try stream.print("{}: ", tree.tokenSlice(name_token)); - } - if (param_decl.var_args_token) |var_args_token| { - try stream.print("{}", tree.tokenSlice(var_args_token)); - } else { - try stack.push(RenderState { .Expression = param_decl.type_node}); - } - }, - RenderState.Text => |bytes| { - try stream.write(bytes); - }, - RenderState.Expression => |base| switch (base.id) { - ast.Node.Id.Identifier => { - const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); - try stream.print("{}", tree.tokenSlice(identifier.token)); - }, - ast.Node.Id.Block => { - const block = @fieldParentPtr(ast.Node.Block, "base", base); - if (block.label) |label| { - try stream.print("{}: ", tree.tokenSlice(label)); - } - - if (block.statements.len == 0) { - try stream.write("{}"); - } else { - try stream.write("{"); - try stack.push(RenderState { .Text = "}"}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent}); - try stack.push(RenderState { .Text = "\n"}); - var i = block.statements.len; - while (i != 0) { - i -= 1; - const statement_node = *block.statements.at(i); - try stack.push(RenderState { .Statement = statement_node}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta}); - try stack.push(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = *block.statements.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, statement_node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - } - break :blk "\n"; - }, - }); - } - } - }, - ast.Node.Id.Defer => { - const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); - try stream.print("{} ", tree.tokenSlice(defer_node.defer_token)); - try stack.push(RenderState { .Expression = defer_node.expr }); - }, - ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); - try stream.print("{} ", tree.tokenSlice(comptime_node.comptime_token)); - try stack.push(RenderState { .Expression = comptime_node.expr }); - }, - ast.Node.Id.AsyncAttribute => { - const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); - try stream.print("{}", tree.tokenSlice(async_attr.async_token)); - - if (async_attr.allocator_type) |allocator_type| { - try stack.push(RenderState { .Text = ">" }); - try stack.push(RenderState { .Expression = allocator_type }); - try stack.push(RenderState { .Text = "<" }); - } - }, - ast.Node.Id.Suspend => { - const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); - if (suspend_node.label) |label| { - try stream.print("{}: ", tree.tokenSlice(label)); - } - try stream.print("{}", tree.tokenSlice(suspend_node.suspend_token)); - - if (suspend_node.body) |body| { - try stack.push(RenderState { .Expression = body }); - try stack.push(RenderState { .Text = " " }); - } - - if (suspend_node.payload) |payload| { - try stack.push(RenderState { .Expression = payload }); - try stack.push(RenderState { .Text = " " }); - } - }, - ast.Node.Id.InfixOp => { - const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); - try stack.push(RenderState { .Expression = prefix_op_node.rhs }); - - if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) { - if (prefix_op_node.op.Catch) |payload| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = payload }); - } - try stack.push(RenderState { .Text = " catch " }); - } else { - const text = switch (prefix_op_node.op) { - ast.Node.InfixOp.Op.Add => " + ", - ast.Node.InfixOp.Op.AddWrap => " +% ", - ast.Node.InfixOp.Op.ArrayCat => " ++ ", - ast.Node.InfixOp.Op.ArrayMult => " ** ", - ast.Node.InfixOp.Op.Assign => " = ", - ast.Node.InfixOp.Op.AssignBitAnd => " &= ", - ast.Node.InfixOp.Op.AssignBitOr => " |= ", - ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ", - ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ", - ast.Node.InfixOp.Op.AssignBitXor => " ^= ", - ast.Node.InfixOp.Op.AssignDiv => " /= ", - ast.Node.InfixOp.Op.AssignMinus => " -= ", - ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ", - ast.Node.InfixOp.Op.AssignMod => " %= ", - ast.Node.InfixOp.Op.AssignPlus => " += ", - ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ", - ast.Node.InfixOp.Op.AssignTimes => " *= ", - ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ", - ast.Node.InfixOp.Op.BangEqual => " != ", - ast.Node.InfixOp.Op.BitAnd => " & ", - ast.Node.InfixOp.Op.BitOr => " | ", - ast.Node.InfixOp.Op.BitShiftLeft => " << ", - ast.Node.InfixOp.Op.BitShiftRight => " >> ", - ast.Node.InfixOp.Op.BitXor => " ^ ", - ast.Node.InfixOp.Op.BoolAnd => " and ", - ast.Node.InfixOp.Op.BoolOr => " or ", - ast.Node.InfixOp.Op.Div => " / ", - ast.Node.InfixOp.Op.EqualEqual => " == ", - ast.Node.InfixOp.Op.ErrorUnion => "!", - ast.Node.InfixOp.Op.GreaterOrEqual => " >= ", - ast.Node.InfixOp.Op.GreaterThan => " > ", - ast.Node.InfixOp.Op.LessOrEqual => " <= ", - ast.Node.InfixOp.Op.LessThan => " < ", - ast.Node.InfixOp.Op.MergeErrorSets => " || ", - ast.Node.InfixOp.Op.Mod => " % ", - ast.Node.InfixOp.Op.Mult => " * ", - ast.Node.InfixOp.Op.MultWrap => " *% ", - ast.Node.InfixOp.Op.Period => ".", - ast.Node.InfixOp.Op.Sub => " - ", - ast.Node.InfixOp.Op.SubWrap => " -% ", - ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ", - ast.Node.InfixOp.Op.Range => " ... ", - ast.Node.InfixOp.Op.Catch => unreachable, - }; - - try stack.push(RenderState { .Text = text }); - } - try stack.push(RenderState { .Expression = prefix_op_node.lhs }); - }, - ast.Node.Id.PrefixOp => { - const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); - try stack.push(RenderState { .Expression = prefix_op_node.rhs }); - switch (prefix_op_node.op) { - ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { - try stream.write("&"); - if (addr_of_info.volatile_token != null) { - try stack.push(RenderState { .Text = "volatile "}); - } - if (addr_of_info.const_token != null) { - try stack.push(RenderState { .Text = "const "}); - } - if (addr_of_info.align_expr) |align_expr| { - try stream.print("align("); - try stack.push(RenderState { .Text = ") "}); - try stack.push(RenderState { .Expression = align_expr}); - } - }, - ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { - try stream.write("[]"); - if (addr_of_info.volatile_token != null) { - try stack.push(RenderState { .Text = "volatile "}); - } - if (addr_of_info.const_token != null) { - try stack.push(RenderState { .Text = "const "}); - } - if (addr_of_info.align_expr) |align_expr| { - try stream.print("align("); - try stack.push(RenderState { .Text = ") "}); - try stack.push(RenderState { .Expression = align_expr}); - } - }, - ast.Node.PrefixOp.Op.ArrayType => |array_index| { - try stack.push(RenderState { .Text = "]"}); - try stack.push(RenderState { .Expression = array_index}); - try stack.push(RenderState { .Text = "["}); - }, - ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), - ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), - ast.Node.PrefixOp.Op.Deref => try stream.write("*"), - ast.Node.PrefixOp.Op.Negation => try stream.write("-"), - ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"), - ast.Node.PrefixOp.Op.Try => try stream.write("try "), - ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"), - ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"), - ast.Node.PrefixOp.Op.Await => try stream.write("await "), - ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "), - ast.Node.PrefixOp.Op.Resume => try stream.write("resume "), - } - }, - ast.Node.Id.SuffixOp => { - const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base); - - switch (suffix_op.op) { - @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| { - try stack.push(RenderState { .Text = ")"}); - var i = call_info.params.len; - while (i != 0) { - i -= 1; - const param_node = *call_info.params.at(i); - try stack.push(RenderState { .Expression = param_node}); - if (i != 0) { - try stack.push(RenderState { .Text = ", " }); - } - } - try stack.push(RenderState { .Text = "("}); - try stack.push(RenderState { .Expression = suffix_op.lhs }); - - if (call_info.async_attr) |async_attr| { - try stack.push(RenderState { .Text = " "}); - try stack.push(RenderState { .Expression = &async_attr.base }); - } - }, - ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { - try stack.push(RenderState { .Text = "]"}); - try stack.push(RenderState { .Expression = index_expr}); - try stack.push(RenderState { .Text = "["}); - try stack.push(RenderState { .Expression = suffix_op.lhs }); - }, - @TagType(ast.Node.SuffixOp.Op).Slice => |range| { - try stack.push(RenderState { .Text = "]"}); - if (range.end) |end| { - try stack.push(RenderState { .Expression = end}); - } - try stack.push(RenderState { .Text = ".."}); - try stack.push(RenderState { .Expression = range.start}); - try stack.push(RenderState { .Text = "["}); - try stack.push(RenderState { .Expression = suffix_op.lhs }); - }, - ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| { - if (field_inits.len == 0) { - try stack.push(RenderState { .Text = "{}" }); - try stack.push(RenderState { .Expression = suffix_op.lhs }); - continue; - } - if (field_inits.len == 1) { - const field_init = *field_inits.at(0); - - try stack.push(RenderState { .Text = " }" }); - try stack.push(RenderState { .Expression = field_init }); - try stack.push(RenderState { .Text = "{ " }); - try stack.push(RenderState { .Expression = suffix_op.lhs }); - continue; - } - try stack.push(RenderState { .Text = "}"}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Text = "\n" }); - var i = field_inits.len; - while (i != 0) { - i -= 1; - const field_init = *field_inits.at(i); - if (field_init.id != ast.Node.Id.LineComment) { - try stack.push(RenderState { .Text = "," }); - } - try stack.push(RenderState { .Expression = field_init }); - try stack.push(RenderState.PrintIndent); - if (i != 0) { - try stack.push(RenderState { .Text = blk: { - const prev_node = *field_inits.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, field_init.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }}); - } - } - try stack.push(RenderState { .Indent = indent + indent_delta }); - try stack.push(RenderState { .Text = "{\n"}); - try stack.push(RenderState { .Expression = suffix_op.lhs }); - }, - ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| { - if (exprs.len == 0) { - try stack.push(RenderState { .Text = "{}" }); - try stack.push(RenderState { .Expression = suffix_op.lhs }); - continue; - } - if (exprs.len == 1) { - const expr = *exprs.at(0); - - try stack.push(RenderState { .Text = "}" }); - try stack.push(RenderState { .Expression = expr }); - try stack.push(RenderState { .Text = "{" }); - try stack.push(RenderState { .Expression = suffix_op.lhs }); - continue; - } - - try stack.push(RenderState { .Text = "}"}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent }); - var i = exprs.len; - while (i != 0) { - i -= 1; - const expr = *exprs.at(i); - try stack.push(RenderState { .Text = ",\n" }); - try stack.push(RenderState { .Expression = expr }); - try stack.push(RenderState.PrintIndent); - } - try stack.push(RenderState { .Indent = indent + indent_delta }); - try stack.push(RenderState { .Text = "{\n"}); - try stack.push(RenderState { .Expression = suffix_op.lhs }); - }, - } - }, - ast.Node.Id.ControlFlowExpression => { - const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); - - if (flow_expr.rhs) |rhs| { - try stack.push(RenderState { .Expression = rhs }); - try stack.push(RenderState { .Text = " " }); - } - - switch (flow_expr.kind) { - ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { - try stream.print("break"); - if (maybe_label) |label| { - try stream.print(" :"); - try stack.push(RenderState { .Expression = label }); - } - }, - ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { - try stream.print("continue"); - if (maybe_label) |label| { - try stream.print(" :"); - try stack.push(RenderState { .Expression = label }); - } - }, - ast.Node.ControlFlowExpression.Kind.Return => { - try stream.print("return"); - }, - - } - }, - ast.Node.Id.Payload => { - const payload = @fieldParentPtr(ast.Node.Payload, "base", base); - try stack.push(RenderState { .Text = "|"}); - try stack.push(RenderState { .Expression = payload.error_symbol }); - try stack.push(RenderState { .Text = "|"}); - }, - ast.Node.Id.PointerPayload => { - const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); - try stack.push(RenderState { .Text = "|"}); - try stack.push(RenderState { .Expression = payload.value_symbol }); - - if (payload.ptr_token) |ptr_token| { - try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) }); - } - - try stack.push(RenderState { .Text = "|"}); - }, - ast.Node.Id.PointerIndexPayload => { - const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); - try stack.push(RenderState { .Text = "|"}); - - if (payload.index_symbol) |index_symbol| { - try stack.push(RenderState { .Expression = index_symbol }); - try stack.push(RenderState { .Text = ", "}); - } - - try stack.push(RenderState { .Expression = payload.value_symbol }); - - if (payload.ptr_token) |ptr_token| { - try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) }); - } - - try stack.push(RenderState { .Text = "|"}); - }, - ast.Node.Id.GroupedExpression => { - const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); - try stack.push(RenderState { .Text = ")"}); - try stack.push(RenderState { .Expression = grouped_expr.expr }); - try stack.push(RenderState { .Text = "("}); - }, - ast.Node.Id.FieldInitializer => { - const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); - try stream.print(".{} = ", tree.tokenSlice(field_init.name_token)); - try stack.push(RenderState { .Expression = field_init.expr }); - }, - ast.Node.Id.IntegerLiteral => { - const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(integer_literal.token)); - }, - ast.Node.Id.FloatLiteral => { - const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(float_literal.token)); - }, - ast.Node.Id.StringLiteral => { - const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(string_literal.token)); - }, - ast.Node.Id.CharLiteral => { - const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(char_literal.token)); - }, - ast.Node.Id.BoolLiteral => { - const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(bool_literal.token)); - }, - ast.Node.Id.NullLiteral => { - const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(null_literal.token)); - }, - ast.Node.Id.ThisLiteral => { - const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(this_literal.token)); - }, - ast.Node.Id.Unreachable => { - const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); - try stream.print("{}", tree.tokenSlice(unreachable_node.token)); - }, - ast.Node.Id.ErrorType => { - const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); - try stream.print("{}", tree.tokenSlice(error_type.token)); - }, - ast.Node.Id.VarType => { - const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); - try stream.print("{}", tree.tokenSlice(var_type.token)); - }, - ast.Node.Id.ContainerDecl => { - const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); - - switch (container_decl.layout) { - ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "), - ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "), - ast.Node.ContainerDecl.Layout.Auto => { }, - } - - switch (container_decl.kind) { - ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"), - ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"), - ast.Node.ContainerDecl.Kind.Union => try stream.print("union"), - } - - if (container_decl.fields_and_decls.len == 0) { - try stack.push(RenderState { .Text = "{}"}); - } else { - try stack.push(RenderState { .Text = "}"}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Text = "\n"}); - - var i = container_decl.fields_and_decls.len; - while (i != 0) { - i -= 1; - const node = *container_decl.fields_and_decls.at(i); - try stack.push(RenderState { .TopLevelDecl = node}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = *container_decl.fields_and_decls.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - } - break :blk "\n"; - }, - }); - } - try stack.push(RenderState { .Indent = indent + indent_delta}); - try stack.push(RenderState { .Text = "{"}); - } - - switch (container_decl.init_arg_expr) { - ast.Node.ContainerDecl.InitArg.None => try stack.push(RenderState { .Text = " "}), - ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { - if (enum_tag_type) |expr| { - try stack.push(RenderState { .Text = ")) "}); - try stack.push(RenderState { .Expression = expr}); - try stack.push(RenderState { .Text = "(enum("}); - } else { - try stack.push(RenderState { .Text = "(enum) "}); - } - }, - ast.Node.ContainerDecl.InitArg.Type => |type_expr| { - try stack.push(RenderState { .Text = ") "}); - try stack.push(RenderState { .Expression = type_expr}); - try stack.push(RenderState { .Text = "("}); - }, - } - }, - ast.Node.Id.ErrorSetDecl => { - const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); - - if (err_set_decl.decls.len == 0) { - try stream.write("error{}"); - continue; - } - - if (err_set_decl.decls.len == 1) blk: { - const node = *err_set_decl.decls.at(0); - - // if there are any doc comments or same line comments - // don't try to put it all on one line - if (node.cast(ast.Node.ErrorTag)) |tag| { - if (tag.doc_comments != null) break :blk; - } else { - break :blk; - } - - - try stream.write("error{"); - try stack.push(RenderState { .Text = "}" }); - try stack.push(RenderState { .TopLevelDecl = node }); - continue; - } - - try stream.write("error{"); - - try stack.push(RenderState { .Text = "}"}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Text = "\n"}); - - var i = err_set_decl.decls.len; - while (i != 0) { - i -= 1; - const node = *err_set_decl.decls.at(i); - if (node.id != ast.Node.Id.LineComment) { - try stack.push(RenderState { .Text = "," }); - } - try stack.push(RenderState { .TopLevelDecl = node }); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = *err_set_decl.decls.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - } - break :blk "\n"; - }, - }); - } - try stack.push(RenderState { .Indent = indent + indent_delta}); - }, - ast.Node.Id.MultilineStringLiteral => { - const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); - try stream.print("\n"); - - var i : usize = 0; - while (i < multiline_str_literal.lines.len) : (i += 1) { - const t = *multiline_str_literal.lines.at(i); - try stream.writeByteNTimes(' ', indent + indent_delta); - try stream.print("{}", tree.tokenSlice(t)); - } - try stream.writeByteNTimes(' ', indent); - }, - ast.Node.Id.UndefinedLiteral => { - const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(undefined_literal.token)); - }, - ast.Node.Id.BuiltinCall => { - const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); - try stream.print("{}(", tree.tokenSlice(builtin_call.builtin_token)); - try stack.push(RenderState { .Text = ")"}); - var i = builtin_call.params.len; - while (i != 0) { - i -= 1; - const param_node = *builtin_call.params.at(i); - try stack.push(RenderState { .Expression = param_node}); - if (i != 0) { - try stack.push(RenderState { .Text = ", " }); - } - } - }, - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); - - switch (fn_proto.return_type) { - ast.Node.FnProto.ReturnType.Explicit => |node| { - try stack.push(RenderState { .Expression = node}); - }, - ast.Node.FnProto.ReturnType.InferErrorSet => |node| { - try stack.push(RenderState { .Expression = node}); - try stack.push(RenderState { .Text = "!"}); - }, - } - - if (fn_proto.align_expr) |align_expr| { - try stack.push(RenderState { .Text = ") " }); - try stack.push(RenderState { .Expression = align_expr}); - try stack.push(RenderState { .Text = "align(" }); - } - - try stack.push(RenderState { .Text = ") " }); - var i = fn_proto.params.len; - while (i != 0) { - i -= 1; - const param_decl_node = *fn_proto.params.at(i); - try stack.push(RenderState { .ParamDecl = param_decl_node}); - if (i != 0) { - try stack.push(RenderState { .Text = ", " }); - } - } - - try stack.push(RenderState { .Text = "(" }); - if (fn_proto.name_token) |name_token| { - try stack.push(RenderState { .Text = tree.tokenSlice(name_token) }); - try stack.push(RenderState { .Text = " " }); - } - - try stack.push(RenderState { .Text = "fn" }); - - if (fn_proto.async_attr) |async_attr| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = &async_attr.base }); - } - - if (fn_proto.cc_token) |cc_token| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(cc_token) }); - } - - if (fn_proto.lib_name) |lib_name| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = lib_name }); - } - if (fn_proto.extern_export_inline_token) |extern_export_inline_token| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) }); - } - - if (fn_proto.visib_token) |visib_token_index| { - const visib_token = tree.tokens.at(visib_token_index); - assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export); - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(visib_token_index) }); - } - }, - ast.Node.Id.PromiseType => { - const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base); - try stream.write(tree.tokenSlice(promise_type.promise_token)); - if (promise_type.result) |result| { - try stream.write(tree.tokenSlice(result.arrow_token)); - try stack.push(RenderState { .Expression = result.return_type}); - } - }, - ast.Node.Id.LineComment => { - const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base); - try stream.write(tree.tokenSlice(line_comment_node.token)); - }, - ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes - ast.Node.Id.Switch => { - const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); - - try stream.print("{} (", tree.tokenSlice(switch_node.switch_token)); - - if (switch_node.cases.len == 0) { - try stack.push(RenderState { .Text = ") {}"}); - try stack.push(RenderState { .Expression = switch_node.expr }); - continue; - } - - try stack.push(RenderState { .Text = "}"}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Text = "\n"}); - - var i = switch_node.cases.len; - while (i != 0) { - i -= 1; - const node = *switch_node.cases.at(i); - try stack.push(RenderState { .Expression = node}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = *switch_node.cases.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - } - break :blk "\n"; - }, - }); - } - try stack.push(RenderState { .Indent = indent + indent_delta}); - try stack.push(RenderState { .Text = ") {"}); - try stack.push(RenderState { .Expression = switch_node.expr }); - }, - ast.Node.Id.SwitchCase => { - const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); - - try stack.push(RenderState { .Text = "," }); - try stack.push(RenderState { .Expression = switch_case.expr }); - if (switch_case.payload) |payload| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = payload }); - } - try stack.push(RenderState { .Text = " => "}); - - var i = switch_case.items.len; - while (i != 0) { - i -= 1; - try stack.push(RenderState { .Expression = *switch_case.items.at(i) }); - - if (i != 0) { - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Text = ",\n" }); - } - } - }, - ast.Node.Id.SwitchElse => { - const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); - try stream.print("{}", tree.tokenSlice(switch_else.token)); - }, - ast.Node.Id.Else => { - const else_node = @fieldParentPtr(ast.Node.Else, "base", base); - try stream.print("{}", tree.tokenSlice(else_node.else_token)); - - switch (else_node.body.id) { - ast.Node.Id.Block, ast.Node.Id.If, - ast.Node.Id.For, ast.Node.Id.While, - ast.Node.Id.Switch => { - try stream.print(" "); - try stack.push(RenderState { .Expression = else_node.body }); - }, - else => { - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Expression = else_node.body }); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta }); - try stack.push(RenderState { .Text = "\n" }); - } - } - - if (else_node.payload) |payload| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = payload }); - } - }, - ast.Node.Id.While => { - const while_node = @fieldParentPtr(ast.Node.While, "base", base); - if (while_node.label) |label| { - try stream.print("{}: ", tree.tokenSlice(label)); - } - - if (while_node.inline_token) |inline_token| { - try stream.print("{} ", tree.tokenSlice(inline_token)); - } - - try stream.print("{} ", tree.tokenSlice(while_node.while_token)); - - if (while_node.@"else") |@"else"| { - try stack.push(RenderState { .Expression = &@"else".base }); - - if (while_node.body.id == ast.Node.Id.Block) { - try stack.push(RenderState { .Text = " " }); - } else { - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Text = "\n" }); - } - } - - if (while_node.body.id == ast.Node.Id.Block) { - try stack.push(RenderState { .Expression = while_node.body }); - try stack.push(RenderState { .Text = " " }); - } else { - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Expression = while_node.body }); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta }); - try stack.push(RenderState { .Text = "\n" }); - } - - if (while_node.continue_expr) |continue_expr| { - try stack.push(RenderState { .Text = ")" }); - try stack.push(RenderState { .Expression = continue_expr }); - try stack.push(RenderState { .Text = ": (" }); - try stack.push(RenderState { .Text = " " }); - } - - if (while_node.payload) |payload| { - try stack.push(RenderState { .Expression = payload }); - try stack.push(RenderState { .Text = " " }); - } - - try stack.push(RenderState { .Text = ")" }); - try stack.push(RenderState { .Expression = while_node.condition }); - try stack.push(RenderState { .Text = "(" }); - }, - ast.Node.Id.For => { - const for_node = @fieldParentPtr(ast.Node.For, "base", base); - if (for_node.label) |label| { - try stream.print("{}: ", tree.tokenSlice(label)); - } - - if (for_node.inline_token) |inline_token| { - try stream.print("{} ", tree.tokenSlice(inline_token)); - } - - try stream.print("{} ", tree.tokenSlice(for_node.for_token)); - - if (for_node.@"else") |@"else"| { - try stack.push(RenderState { .Expression = &@"else".base }); - - if (for_node.body.id == ast.Node.Id.Block) { - try stack.push(RenderState { .Text = " " }); - } else { - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Text = "\n" }); - } - } - - if (for_node.body.id == ast.Node.Id.Block) { - try stack.push(RenderState { .Expression = for_node.body }); - try stack.push(RenderState { .Text = " " }); - } else { - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Expression = for_node.body }); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta }); - try stack.push(RenderState { .Text = "\n" }); - } - - if (for_node.payload) |payload| { - try stack.push(RenderState { .Expression = payload }); - try stack.push(RenderState { .Text = " " }); - } - - try stack.push(RenderState { .Text = ")" }); - try stack.push(RenderState { .Expression = for_node.array_expr }); - try stack.push(RenderState { .Text = "(" }); - }, - ast.Node.Id.If => { - const if_node = @fieldParentPtr(ast.Node.If, "base", base); - try stream.print("{} ", tree.tokenSlice(if_node.if_token)); - - switch (if_node.body.id) { - ast.Node.Id.Block, ast.Node.Id.If, - ast.Node.Id.For, ast.Node.Id.While, - ast.Node.Id.Switch => { - if (if_node.@"else") |@"else"| { - try stack.push(RenderState { .Expression = &@"else".base }); - - if (if_node.body.id == ast.Node.Id.Block) { - try stack.push(RenderState { .Text = " " }); - } else { - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Text = "\n" }); - } - } - }, - else => { - if (if_node.@"else") |@"else"| { - try stack.push(RenderState { .Expression = @"else".body }); - - if (@"else".payload) |payload| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = payload }); - } - - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(@"else".else_token) }); - try stack.push(RenderState { .Text = " " }); - } - } - } - - try stack.push(RenderState { .Expression = if_node.body }); - try stack.push(RenderState { .Text = " " }); - - if (if_node.payload) |payload| { - try stack.push(RenderState { .Expression = payload }); - try stack.push(RenderState { .Text = " " }); - } - - try stack.push(RenderState { .Text = ")" }); - try stack.push(RenderState { .Expression = if_node.condition }); - try stack.push(RenderState { .Text = "(" }); - }, - ast.Node.Id.Asm => { - const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); - try stream.print("{} ", tree.tokenSlice(asm_node.asm_token)); - - if (asm_node.volatile_token) |volatile_token| { - try stream.print("{} ", tree.tokenSlice(volatile_token)); - } - - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Text = ")" }); - { - var i = asm_node.clobbers.len; - while (i != 0) { - i -= 1; - try stack.push(RenderState { .Expression = *asm_node.clobbers.at(i) }); - - if (i != 0) { - try stack.push(RenderState { .Text = ", " }); - } - } - } - try stack.push(RenderState { .Text = ": " }); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta }); - try stack.push(RenderState { .Text = "\n" }); - { - var i = asm_node.inputs.len; - while (i != 0) { - i -= 1; - const node = *asm_node.inputs.at(i); - try stack.push(RenderState { .Expression = &node.base}); - - if (i != 0) { - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { - .Text = blk: { - const prev_node = *asm_node.inputs.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }, - }); - try stack.push(RenderState { .Text = "," }); - } - } - } - try stack.push(RenderState { .Indent = indent + indent_delta + 2}); - try stack.push(RenderState { .Text = ": "}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta}); - try stack.push(RenderState { .Text = "\n" }); - { - var i = asm_node.outputs.len; - while (i != 0) { - i -= 1; - const node = *asm_node.outputs.at(i); - try stack.push(RenderState { .Expression = &node.base}); - - if (i != 0) { - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { - .Text = blk: { - const prev_node = *asm_node.outputs.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }, - }); - try stack.push(RenderState { .Text = "," }); - } - } - } - try stack.push(RenderState { .Indent = indent + indent_delta + 2}); - try stack.push(RenderState { .Text = ": "}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta}); - try stack.push(RenderState { .Text = "\n" }); - try stack.push(RenderState { .Expression = asm_node.template }); - try stack.push(RenderState { .Text = "(" }); - }, - ast.Node.Id.AsmInput => { - const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); - - try stack.push(RenderState { .Text = ")"}); - try stack.push(RenderState { .Expression = asm_input.expr}); - try stack.push(RenderState { .Text = " ("}); - try stack.push(RenderState { .Expression = asm_input.constraint }); - try stack.push(RenderState { .Text = "] "}); - try stack.push(RenderState { .Expression = asm_input.symbolic_name }); - try stack.push(RenderState { .Text = "["}); - }, - ast.Node.Id.AsmOutput => { - const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); - - try stack.push(RenderState { .Text = ")"}); - switch (asm_output.kind) { - ast.Node.AsmOutput.Kind.Variable => |variable_name| { - try stack.push(RenderState { .Expression = &variable_name.base}); - }, - ast.Node.AsmOutput.Kind.Return => |return_type| { - try stack.push(RenderState { .Expression = return_type}); - try stack.push(RenderState { .Text = "-> "}); - }, - } - try stack.push(RenderState { .Text = " ("}); - try stack.push(RenderState { .Expression = asm_output.constraint }); - try stack.push(RenderState { .Text = "] "}); - try stack.push(RenderState { .Expression = asm_output.symbolic_name }); - try stack.push(RenderState { .Text = "["}); - }, - - ast.Node.Id.StructField, - ast.Node.Id.UnionTag, - ast.Node.Id.EnumTag, - ast.Node.Id.ErrorTag, - ast.Node.Id.Root, - ast.Node.Id.VarDecl, - ast.Node.Id.Use, - ast.Node.Id.TestDecl, - ast.Node.Id.ParamDecl => unreachable, - }, - RenderState.Statement => |base| { - switch (base.id) { - ast.Node.Id.VarDecl => { - const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); - try stack.push(RenderState { .VarDecl = var_decl}); - }, - else => { - if (requireSemiColon(base)) { - try stack.push(RenderState { .Text = ";" }); - } - try stack.push(RenderState { .Expression = base }); - }, - } - }, - RenderState.Indent => |new_indent| indent = new_indent, - RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent), - } - } -} - -fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) !void { - const comment = node.doc_comments ?? return; - var it = comment.lines.iterator(0); - while (it.next()) |line_token_index| { - try stream.print("{}\n", tree.tokenSlice(*line_token_index)); - try stream.writeByteNTimes(' ', indent); - } -} - -test "std.zig.parser" { - _ = @import("parser_test.zig"); -} diff --git a/std/zig/render.zig b/std/zig/render.zig new file mode 100644 index 0000000000..3fa7c4c171 --- /dev/null +++ b/std/zig/render.zig @@ -0,0 +1,1241 @@ +const std = @import("../index.zig"); +const assert = std.debug.assert; +const SegmentedList = std.SegmentedList; +const mem = std.mem; +const ast = std.zig.ast; +const Token = std.zig.Token; + +const RenderState = union(enum) { + TopLevelDecl: &ast.Node, + ParamDecl: &ast.Node, + Text: []const u8, + Expression: &ast.Node, + VarDecl: &ast.Node.VarDecl, + Statement: &ast.Node, + PrintIndent, + Indent: usize, +}; + +pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { + var stack = SegmentedList(RenderState, 32).init(allocator); + defer stack.deinit(); + + { + try stack.push(RenderState { .Text = "\n"}); + + var i = tree.root_node.decls.len; + while (i != 0) { + i -= 1; + const decl = *tree.root_node.decls.at(i); + try stack.push(RenderState {.TopLevelDecl = decl}); + if (i != 0) { + try stack.push(RenderState { + .Text = blk: { + const prev_node = *tree.root_node.decls.at(i - 1); + const prev_node_last_token = tree.tokens.at(prev_node.lastToken()); + const loc = tree.tokenLocation(prev_node_last_token.end, decl.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + break :blk "\n"; + }, + }); + } + } + } + + const indent_delta = 4; + var indent: usize = 0; + while (stack.pop()) |state| { + switch (state) { + RenderState.TopLevelDecl => |decl| { + switch (decl.id) { + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); + try renderComments(tree, stream, fn_proto, indent); + + if (fn_proto.body_node) |body_node| { + stack.push(RenderState { .Expression = body_node}) catch unreachable; + try stack.push(RenderState { .Text = " "}); + } else { + stack.push(RenderState { .Text = ";" }) catch unreachable; + } + + try stack.push(RenderState { .Expression = decl }); + }, + ast.Node.Id.Use => { + const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); + if (use_decl.visib_token) |visib_token| { + try stream.print("{} ", tree.tokenSlice(visib_token)); + } + try stream.print("use "); + try stack.push(RenderState { .Text = ";" }); + try stack.push(RenderState { .Expression = use_decl.expr }); + }, + ast.Node.Id.VarDecl => { + const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); + try renderComments(tree, stream, var_decl, indent); + try stack.push(RenderState { .VarDecl = var_decl}); + }, + ast.Node.Id.TestDecl => { + const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); + try renderComments(tree, stream, test_decl, indent); + try stream.print("test "); + try stack.push(RenderState { .Expression = test_decl.body_node }); + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = test_decl.name }); + }, + ast.Node.Id.StructField => { + const field = @fieldParentPtr(ast.Node.StructField, "base", decl); + try renderComments(tree, stream, field, indent); + if (field.visib_token) |visib_token| { + try stream.print("{} ", tree.tokenSlice(visib_token)); + } + try stream.print("{}: ", tree.tokenSlice(field.name_token)); + try stack.push(RenderState { .Text = "," }); + try stack.push(RenderState { .Expression = field.type_expr}); + }, + ast.Node.Id.UnionTag => { + const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); + try renderComments(tree, stream, tag, indent); + try stream.print("{}", tree.tokenSlice(tag.name_token)); + + try stack.push(RenderState { .Text = "," }); + + if (tag.value_expr) |value_expr| { + try stack.push(RenderState { .Expression = value_expr }); + try stack.push(RenderState { .Text = " = " }); + } + + if (tag.type_expr) |type_expr| { + try stream.print(": "); + try stack.push(RenderState { .Expression = type_expr}); + } + }, + ast.Node.Id.EnumTag => { + const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); + try renderComments(tree, stream, tag, indent); + try stream.print("{}", tree.tokenSlice(tag.name_token)); + + try stack.push(RenderState { .Text = "," }); + if (tag.value) |value| { + try stream.print(" = "); + try stack.push(RenderState { .Expression = value}); + } + }, + ast.Node.Id.ErrorTag => { + const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl); + try renderComments(tree, stream, tag, indent); + try stream.print("{}", tree.tokenSlice(tag.name_token)); + }, + ast.Node.Id.Comptime => { + if (decl.requireSemiColon()) { + try stack.push(RenderState { .Text = ";" }); + } + try stack.push(RenderState { .Expression = decl }); + }, + ast.Node.Id.LineComment => { + const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl); + try stream.write(tree.tokenSlice(line_comment_node.token)); + }, + else => unreachable, + } + }, + + RenderState.VarDecl => |var_decl| { + try stack.push(RenderState { .Text = ";" }); + if (var_decl.init_node) |init_node| { + try stack.push(RenderState { .Expression = init_node }); + const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = "; + try stack.push(RenderState { .Text = text }); + } + if (var_decl.align_node) |align_node| { + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = align_node }); + try stack.push(RenderState { .Text = " align(" }); + } + if (var_decl.type_node) |type_node| { + try stack.push(RenderState { .Expression = type_node }); + try stack.push(RenderState { .Text = ": " }); + } + try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.name_token) }); + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) }); + + if (var_decl.comptime_token) |comptime_token| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(comptime_token) }); + } + + if (var_decl.extern_export_token) |extern_export_token| { + if (var_decl.lib_name != null) { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = ??var_decl.lib_name }); + } + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_token) }); + } + + if (var_decl.visib_token) |visib_token| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(visib_token) }); + } + }, + + RenderState.ParamDecl => |base| { + const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); + if (param_decl.comptime_token) |comptime_token| { + try stream.print("{} ", tree.tokenSlice(comptime_token)); + } + if (param_decl.noalias_token) |noalias_token| { + try stream.print("{} ", tree.tokenSlice(noalias_token)); + } + if (param_decl.name_token) |name_token| { + try stream.print("{}: ", tree.tokenSlice(name_token)); + } + if (param_decl.var_args_token) |var_args_token| { + try stream.print("{}", tree.tokenSlice(var_args_token)); + } else { + try stack.push(RenderState { .Expression = param_decl.type_node}); + } + }, + RenderState.Text => |bytes| { + try stream.write(bytes); + }, + RenderState.Expression => |base| switch (base.id) { + ast.Node.Id.Identifier => { + const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); + try stream.print("{}", tree.tokenSlice(identifier.token)); + }, + ast.Node.Id.Block => { + const block = @fieldParentPtr(ast.Node.Block, "base", base); + if (block.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } + + if (block.statements.len == 0) { + try stream.write("{}"); + } else { + try stream.write("{"); + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent}); + try stack.push(RenderState { .Text = "\n"}); + var i = block.statements.len; + while (i != 0) { + i -= 1; + const statement_node = *block.statements.at(i); + try stack.push(RenderState { .Statement = statement_node}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { + .Text = blk: { + if (i != 0) { + const prev_node = *block.statements.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, statement_node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + } + break :blk "\n"; + }, + }); + } + } + }, + ast.Node.Id.Defer => { + const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); + try stream.print("{} ", tree.tokenSlice(defer_node.defer_token)); + try stack.push(RenderState { .Expression = defer_node.expr }); + }, + ast.Node.Id.Comptime => { + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); + try stream.print("{} ", tree.tokenSlice(comptime_node.comptime_token)); + try stack.push(RenderState { .Expression = comptime_node.expr }); + }, + ast.Node.Id.AsyncAttribute => { + const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); + try stream.print("{}", tree.tokenSlice(async_attr.async_token)); + + if (async_attr.allocator_type) |allocator_type| { + try stack.push(RenderState { .Text = ">" }); + try stack.push(RenderState { .Expression = allocator_type }); + try stack.push(RenderState { .Text = "<" }); + } + }, + ast.Node.Id.Suspend => { + const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); + if (suspend_node.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } + try stream.print("{}", tree.tokenSlice(suspend_node.suspend_token)); + + if (suspend_node.body) |body| { + try stack.push(RenderState { .Expression = body }); + try stack.push(RenderState { .Text = " " }); + } + + if (suspend_node.payload) |payload| { + try stack.push(RenderState { .Expression = payload }); + try stack.push(RenderState { .Text = " " }); + } + }, + ast.Node.Id.InfixOp => { + const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); + try stack.push(RenderState { .Expression = prefix_op_node.rhs }); + + if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) { + if (prefix_op_node.op.Catch) |payload| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); + } + try stack.push(RenderState { .Text = " catch " }); + } else { + const text = switch (prefix_op_node.op) { + ast.Node.InfixOp.Op.Add => " + ", + ast.Node.InfixOp.Op.AddWrap => " +% ", + ast.Node.InfixOp.Op.ArrayCat => " ++ ", + ast.Node.InfixOp.Op.ArrayMult => " ** ", + ast.Node.InfixOp.Op.Assign => " = ", + ast.Node.InfixOp.Op.AssignBitAnd => " &= ", + ast.Node.InfixOp.Op.AssignBitOr => " |= ", + ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ", + ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ", + ast.Node.InfixOp.Op.AssignBitXor => " ^= ", + ast.Node.InfixOp.Op.AssignDiv => " /= ", + ast.Node.InfixOp.Op.AssignMinus => " -= ", + ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ", + ast.Node.InfixOp.Op.AssignMod => " %= ", + ast.Node.InfixOp.Op.AssignPlus => " += ", + ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ", + ast.Node.InfixOp.Op.AssignTimes => " *= ", + ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ", + ast.Node.InfixOp.Op.BangEqual => " != ", + ast.Node.InfixOp.Op.BitAnd => " & ", + ast.Node.InfixOp.Op.BitOr => " | ", + ast.Node.InfixOp.Op.BitShiftLeft => " << ", + ast.Node.InfixOp.Op.BitShiftRight => " >> ", + ast.Node.InfixOp.Op.BitXor => " ^ ", + ast.Node.InfixOp.Op.BoolAnd => " and ", + ast.Node.InfixOp.Op.BoolOr => " or ", + ast.Node.InfixOp.Op.Div => " / ", + ast.Node.InfixOp.Op.EqualEqual => " == ", + ast.Node.InfixOp.Op.ErrorUnion => "!", + ast.Node.InfixOp.Op.GreaterOrEqual => " >= ", + ast.Node.InfixOp.Op.GreaterThan => " > ", + ast.Node.InfixOp.Op.LessOrEqual => " <= ", + ast.Node.InfixOp.Op.LessThan => " < ", + ast.Node.InfixOp.Op.MergeErrorSets => " || ", + ast.Node.InfixOp.Op.Mod => " % ", + ast.Node.InfixOp.Op.Mult => " * ", + ast.Node.InfixOp.Op.MultWrap => " *% ", + ast.Node.InfixOp.Op.Period => ".", + ast.Node.InfixOp.Op.Sub => " - ", + ast.Node.InfixOp.Op.SubWrap => " -% ", + ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ", + ast.Node.InfixOp.Op.Range => " ... ", + ast.Node.InfixOp.Op.Catch => unreachable, + }; + + try stack.push(RenderState { .Text = text }); + } + try stack.push(RenderState { .Expression = prefix_op_node.lhs }); + }, + ast.Node.Id.PrefixOp => { + const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); + try stack.push(RenderState { .Expression = prefix_op_node.rhs }); + switch (prefix_op_node.op) { + ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { + try stream.write("&"); + if (addr_of_info.volatile_token != null) { + try stack.push(RenderState { .Text = "volatile "}); + } + if (addr_of_info.const_token != null) { + try stack.push(RenderState { .Text = "const "}); + } + if (addr_of_info.align_expr) |align_expr| { + try stream.print("align("); + try stack.push(RenderState { .Text = ") "}); + try stack.push(RenderState { .Expression = align_expr}); + } + }, + ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { + try stream.write("[]"); + if (addr_of_info.volatile_token != null) { + try stack.push(RenderState { .Text = "volatile "}); + } + if (addr_of_info.const_token != null) { + try stack.push(RenderState { .Text = "const "}); + } + if (addr_of_info.align_expr) |align_expr| { + try stream.print("align("); + try stack.push(RenderState { .Text = ") "}); + try stack.push(RenderState { .Expression = align_expr}); + } + }, + ast.Node.PrefixOp.Op.ArrayType => |array_index| { + try stack.push(RenderState { .Text = "]"}); + try stack.push(RenderState { .Expression = array_index}); + try stack.push(RenderState { .Text = "["}); + }, + ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), + ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), + ast.Node.PrefixOp.Op.Deref => try stream.write("*"), + ast.Node.PrefixOp.Op.Negation => try stream.write("-"), + ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"), + ast.Node.PrefixOp.Op.Try => try stream.write("try "), + ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"), + ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"), + ast.Node.PrefixOp.Op.Await => try stream.write("await "), + ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "), + ast.Node.PrefixOp.Op.Resume => try stream.write("resume "), + } + }, + ast.Node.Id.SuffixOp => { + const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base); + + switch (suffix_op.op) { + @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| { + try stack.push(RenderState { .Text = ")"}); + var i = call_info.params.len; + while (i != 0) { + i -= 1; + const param_node = *call_info.params.at(i); + try stack.push(RenderState { .Expression = param_node}); + if (i != 0) { + try stack.push(RenderState { .Text = ", " }); + } + } + try stack.push(RenderState { .Text = "("}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + + if (call_info.async_attr) |async_attr| { + try stack.push(RenderState { .Text = " "}); + try stack.push(RenderState { .Expression = &async_attr.base }); + } + }, + ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { + try stack.push(RenderState { .Text = "]"}); + try stack.push(RenderState { .Expression = index_expr}); + try stack.push(RenderState { .Text = "["}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + }, + @TagType(ast.Node.SuffixOp.Op).Slice => |range| { + try stack.push(RenderState { .Text = "]"}); + if (range.end) |end| { + try stack.push(RenderState { .Expression = end}); + } + try stack.push(RenderState { .Text = ".."}); + try stack.push(RenderState { .Expression = range.start}); + try stack.push(RenderState { .Text = "["}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + }, + ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| { + if (field_inits.len == 0) { + try stack.push(RenderState { .Text = "{}" }); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + continue; + } + if (field_inits.len == 1) { + const field_init = *field_inits.at(0); + + try stack.push(RenderState { .Text = " }" }); + try stack.push(RenderState { .Expression = field_init }); + try stack.push(RenderState { .Text = "{ " }); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + continue; + } + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = "\n" }); + var i = field_inits.len; + while (i != 0) { + i -= 1; + const field_init = *field_inits.at(i); + if (field_init.id != ast.Node.Id.LineComment) { + try stack.push(RenderState { .Text = "," }); + } + try stack.push(RenderState { .Expression = field_init }); + try stack.push(RenderState.PrintIndent); + if (i != 0) { + try stack.push(RenderState { .Text = blk: { + const prev_node = *field_inits.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, field_init.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + break :blk "\n"; + }}); + } + } + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "{\n"}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + }, + ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| { + if (exprs.len == 0) { + try stack.push(RenderState { .Text = "{}" }); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + continue; + } + if (exprs.len == 1) { + const expr = *exprs.at(0); + + try stack.push(RenderState { .Text = "}" }); + try stack.push(RenderState { .Expression = expr }); + try stack.push(RenderState { .Text = "{" }); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + continue; + } + + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + var i = exprs.len; + while (i != 0) { + i -= 1; + const expr = *exprs.at(i); + try stack.push(RenderState { .Text = ",\n" }); + try stack.push(RenderState { .Expression = expr }); + try stack.push(RenderState.PrintIndent); + } + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "{\n"}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + }, + } + }, + ast.Node.Id.ControlFlowExpression => { + const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); + + if (flow_expr.rhs) |rhs| { + try stack.push(RenderState { .Expression = rhs }); + try stack.push(RenderState { .Text = " " }); + } + + switch (flow_expr.kind) { + ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { + try stream.print("break"); + if (maybe_label) |label| { + try stream.print(" :"); + try stack.push(RenderState { .Expression = label }); + } + }, + ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { + try stream.print("continue"); + if (maybe_label) |label| { + try stream.print(" :"); + try stack.push(RenderState { .Expression = label }); + } + }, + ast.Node.ControlFlowExpression.Kind.Return => { + try stream.print("return"); + }, + + } + }, + ast.Node.Id.Payload => { + const payload = @fieldParentPtr(ast.Node.Payload, "base", base); + try stack.push(RenderState { .Text = "|"}); + try stack.push(RenderState { .Expression = payload.error_symbol }); + try stack.push(RenderState { .Text = "|"}); + }, + ast.Node.Id.PointerPayload => { + const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); + try stack.push(RenderState { .Text = "|"}); + try stack.push(RenderState { .Expression = payload.value_symbol }); + + if (payload.ptr_token) |ptr_token| { + try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) }); + } + + try stack.push(RenderState { .Text = "|"}); + }, + ast.Node.Id.PointerIndexPayload => { + const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); + try stack.push(RenderState { .Text = "|"}); + + if (payload.index_symbol) |index_symbol| { + try stack.push(RenderState { .Expression = index_symbol }); + try stack.push(RenderState { .Text = ", "}); + } + + try stack.push(RenderState { .Expression = payload.value_symbol }); + + if (payload.ptr_token) |ptr_token| { + try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) }); + } + + try stack.push(RenderState { .Text = "|"}); + }, + ast.Node.Id.GroupedExpression => { + const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); + try stack.push(RenderState { .Text = ")"}); + try stack.push(RenderState { .Expression = grouped_expr.expr }); + try stack.push(RenderState { .Text = "("}); + }, + ast.Node.Id.FieldInitializer => { + const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); + try stream.print(".{} = ", tree.tokenSlice(field_init.name_token)); + try stack.push(RenderState { .Expression = field_init.expr }); + }, + ast.Node.Id.IntegerLiteral => { + const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(integer_literal.token)); + }, + ast.Node.Id.FloatLiteral => { + const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(float_literal.token)); + }, + ast.Node.Id.StringLiteral => { + const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(string_literal.token)); + }, + ast.Node.Id.CharLiteral => { + const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(char_literal.token)); + }, + ast.Node.Id.BoolLiteral => { + const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(bool_literal.token)); + }, + ast.Node.Id.NullLiteral => { + const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(null_literal.token)); + }, + ast.Node.Id.ThisLiteral => { + const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(this_literal.token)); + }, + ast.Node.Id.Unreachable => { + const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); + try stream.print("{}", tree.tokenSlice(unreachable_node.token)); + }, + ast.Node.Id.ErrorType => { + const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); + try stream.print("{}", tree.tokenSlice(error_type.token)); + }, + ast.Node.Id.VarType => { + const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); + try stream.print("{}", tree.tokenSlice(var_type.token)); + }, + ast.Node.Id.ContainerDecl => { + const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); + + switch (container_decl.layout) { + ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "), + ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "), + ast.Node.ContainerDecl.Layout.Auto => { }, + } + + switch (container_decl.kind) { + ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"), + ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"), + ast.Node.ContainerDecl.Kind.Union => try stream.print("union"), + } + + if (container_decl.fields_and_decls.len == 0) { + try stack.push(RenderState { .Text = "{}"}); + } else { + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = "\n"}); + + var i = container_decl.fields_and_decls.len; + while (i != 0) { + i -= 1; + const node = *container_decl.fields_and_decls.at(i); + try stack.push(RenderState { .TopLevelDecl = node}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + if (i != 0) { + const prev_node = *container_decl.fields_and_decls.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + } + break :blk "\n"; + }, + }); + } + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { .Text = "{"}); + } + + switch (container_decl.init_arg_expr) { + ast.Node.ContainerDecl.InitArg.None => try stack.push(RenderState { .Text = " "}), + ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { + if (enum_tag_type) |expr| { + try stack.push(RenderState { .Text = ")) "}); + try stack.push(RenderState { .Expression = expr}); + try stack.push(RenderState { .Text = "(enum("}); + } else { + try stack.push(RenderState { .Text = "(enum) "}); + } + }, + ast.Node.ContainerDecl.InitArg.Type => |type_expr| { + try stack.push(RenderState { .Text = ") "}); + try stack.push(RenderState { .Expression = type_expr}); + try stack.push(RenderState { .Text = "("}); + }, + } + }, + ast.Node.Id.ErrorSetDecl => { + const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); + + if (err_set_decl.decls.len == 0) { + try stream.write("error{}"); + continue; + } + + if (err_set_decl.decls.len == 1) blk: { + const node = *err_set_decl.decls.at(0); + + // if there are any doc comments or same line comments + // don't try to put it all on one line + if (node.cast(ast.Node.ErrorTag)) |tag| { + if (tag.doc_comments != null) break :blk; + } else { + break :blk; + } + + + try stream.write("error{"); + try stack.push(RenderState { .Text = "}" }); + try stack.push(RenderState { .TopLevelDecl = node }); + continue; + } + + try stream.write("error{"); + + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = "\n"}); + + var i = err_set_decl.decls.len; + while (i != 0) { + i -= 1; + const node = *err_set_decl.decls.at(i); + if (node.id != ast.Node.Id.LineComment) { + try stack.push(RenderState { .Text = "," }); + } + try stack.push(RenderState { .TopLevelDecl = node }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + if (i != 0) { + const prev_node = *err_set_decl.decls.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + } + break :blk "\n"; + }, + }); + } + try stack.push(RenderState { .Indent = indent + indent_delta}); + }, + ast.Node.Id.MultilineStringLiteral => { + const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); + try stream.print("\n"); + + var i : usize = 0; + while (i < multiline_str_literal.lines.len) : (i += 1) { + const t = *multiline_str_literal.lines.at(i); + try stream.writeByteNTimes(' ', indent + indent_delta); + try stream.print("{}", tree.tokenSlice(t)); + } + try stream.writeByteNTimes(' ', indent); + }, + ast.Node.Id.UndefinedLiteral => { + const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(undefined_literal.token)); + }, + ast.Node.Id.BuiltinCall => { + const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); + try stream.print("{}(", tree.tokenSlice(builtin_call.builtin_token)); + try stack.push(RenderState { .Text = ")"}); + var i = builtin_call.params.len; + while (i != 0) { + i -= 1; + const param_node = *builtin_call.params.at(i); + try stack.push(RenderState { .Expression = param_node}); + if (i != 0) { + try stack.push(RenderState { .Text = ", " }); + } + } + }, + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); + + switch (fn_proto.return_type) { + ast.Node.FnProto.ReturnType.Explicit => |node| { + try stack.push(RenderState { .Expression = node}); + }, + ast.Node.FnProto.ReturnType.InferErrorSet => |node| { + try stack.push(RenderState { .Expression = node}); + try stack.push(RenderState { .Text = "!"}); + }, + } + + if (fn_proto.align_expr) |align_expr| { + try stack.push(RenderState { .Text = ") " }); + try stack.push(RenderState { .Expression = align_expr}); + try stack.push(RenderState { .Text = "align(" }); + } + + try stack.push(RenderState { .Text = ") " }); + var i = fn_proto.params.len; + while (i != 0) { + i -= 1; + const param_decl_node = *fn_proto.params.at(i); + try stack.push(RenderState { .ParamDecl = param_decl_node}); + if (i != 0) { + try stack.push(RenderState { .Text = ", " }); + } + } + + try stack.push(RenderState { .Text = "(" }); + if (fn_proto.name_token) |name_token| { + try stack.push(RenderState { .Text = tree.tokenSlice(name_token) }); + try stack.push(RenderState { .Text = " " }); + } + + try stack.push(RenderState { .Text = "fn" }); + + if (fn_proto.async_attr) |async_attr| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = &async_attr.base }); + } + + if (fn_proto.cc_token) |cc_token| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(cc_token) }); + } + + if (fn_proto.lib_name) |lib_name| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = lib_name }); + } + if (fn_proto.extern_export_inline_token) |extern_export_inline_token| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) }); + } + + if (fn_proto.visib_token) |visib_token_index| { + const visib_token = tree.tokens.at(visib_token_index); + assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export); + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(visib_token_index) }); + } + }, + ast.Node.Id.PromiseType => { + const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base); + try stream.write(tree.tokenSlice(promise_type.promise_token)); + if (promise_type.result) |result| { + try stream.write(tree.tokenSlice(result.arrow_token)); + try stack.push(RenderState { .Expression = result.return_type}); + } + }, + ast.Node.Id.LineComment => { + const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base); + try stream.write(tree.tokenSlice(line_comment_node.token)); + }, + ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes + ast.Node.Id.Switch => { + const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); + + try stream.print("{} (", tree.tokenSlice(switch_node.switch_token)); + + if (switch_node.cases.len == 0) { + try stack.push(RenderState { .Text = ") {}"}); + try stack.push(RenderState { .Expression = switch_node.expr }); + continue; + } + + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = "\n"}); + + var i = switch_node.cases.len; + while (i != 0) { + i -= 1; + const node = *switch_node.cases.at(i); + try stack.push(RenderState { .Expression = node}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + if (i != 0) { + const prev_node = *switch_node.cases.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + } + break :blk "\n"; + }, + }); + } + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { .Text = ") {"}); + try stack.push(RenderState { .Expression = switch_node.expr }); + }, + ast.Node.Id.SwitchCase => { + const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); + + try stack.push(RenderState { .Text = "," }); + try stack.push(RenderState { .Expression = switch_case.expr }); + if (switch_case.payload) |payload| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); + } + try stack.push(RenderState { .Text = " => "}); + + var i = switch_case.items.len; + while (i != 0) { + i -= 1; + try stack.push(RenderState { .Expression = *switch_case.items.at(i) }); + + if (i != 0) { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = ",\n" }); + } + } + }, + ast.Node.Id.SwitchElse => { + const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); + try stream.print("{}", tree.tokenSlice(switch_else.token)); + }, + ast.Node.Id.Else => { + const else_node = @fieldParentPtr(ast.Node.Else, "base", base); + try stream.print("{}", tree.tokenSlice(else_node.else_token)); + + switch (else_node.body.id) { + ast.Node.Id.Block, ast.Node.Id.If, + ast.Node.Id.For, ast.Node.Id.While, + ast.Node.Id.Switch => { + try stream.print(" "); + try stack.push(RenderState { .Expression = else_node.body }); + }, + else => { + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Expression = else_node.body }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "\n" }); + } + } + + if (else_node.payload) |payload| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); + } + }, + ast.Node.Id.While => { + const while_node = @fieldParentPtr(ast.Node.While, "base", base); + if (while_node.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } + + if (while_node.inline_token) |inline_token| { + try stream.print("{} ", tree.tokenSlice(inline_token)); + } + + try stream.print("{} ", tree.tokenSlice(while_node.while_token)); + + if (while_node.@"else") |@"else"| { + try stack.push(RenderState { .Expression = &@"else".base }); + + if (while_node.body.id == ast.Node.Id.Block) { + try stack.push(RenderState { .Text = " " }); + } else { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = "\n" }); + } + } + + if (while_node.body.id == ast.Node.Id.Block) { + try stack.push(RenderState { .Expression = while_node.body }); + try stack.push(RenderState { .Text = " " }); + } else { + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Expression = while_node.body }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "\n" }); + } + + if (while_node.continue_expr) |continue_expr| { + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = continue_expr }); + try stack.push(RenderState { .Text = ": (" }); + try stack.push(RenderState { .Text = " " }); + } + + if (while_node.payload) |payload| { + try stack.push(RenderState { .Expression = payload }); + try stack.push(RenderState { .Text = " " }); + } + + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = while_node.condition }); + try stack.push(RenderState { .Text = "(" }); + }, + ast.Node.Id.For => { + const for_node = @fieldParentPtr(ast.Node.For, "base", base); + if (for_node.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } + + if (for_node.inline_token) |inline_token| { + try stream.print("{} ", tree.tokenSlice(inline_token)); + } + + try stream.print("{} ", tree.tokenSlice(for_node.for_token)); + + if (for_node.@"else") |@"else"| { + try stack.push(RenderState { .Expression = &@"else".base }); + + if (for_node.body.id == ast.Node.Id.Block) { + try stack.push(RenderState { .Text = " " }); + } else { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = "\n" }); + } + } + + if (for_node.body.id == ast.Node.Id.Block) { + try stack.push(RenderState { .Expression = for_node.body }); + try stack.push(RenderState { .Text = " " }); + } else { + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Expression = for_node.body }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "\n" }); + } + + if (for_node.payload) |payload| { + try stack.push(RenderState { .Expression = payload }); + try stack.push(RenderState { .Text = " " }); + } + + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = for_node.array_expr }); + try stack.push(RenderState { .Text = "(" }); + }, + ast.Node.Id.If => { + const if_node = @fieldParentPtr(ast.Node.If, "base", base); + try stream.print("{} ", tree.tokenSlice(if_node.if_token)); + + switch (if_node.body.id) { + ast.Node.Id.Block, ast.Node.Id.If, + ast.Node.Id.For, ast.Node.Id.While, + ast.Node.Id.Switch => { + if (if_node.@"else") |@"else"| { + try stack.push(RenderState { .Expression = &@"else".base }); + + if (if_node.body.id == ast.Node.Id.Block) { + try stack.push(RenderState { .Text = " " }); + } else { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = "\n" }); + } + } + }, + else => { + if (if_node.@"else") |@"else"| { + try stack.push(RenderState { .Expression = @"else".body }); + + if (@"else".payload) |payload| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); + } + + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(@"else".else_token) }); + try stack.push(RenderState { .Text = " " }); + } + } + } + + try stack.push(RenderState { .Expression = if_node.body }); + try stack.push(RenderState { .Text = " " }); + + if (if_node.payload) |payload| { + try stack.push(RenderState { .Expression = payload }); + try stack.push(RenderState { .Text = " " }); + } + + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = if_node.condition }); + try stack.push(RenderState { .Text = "(" }); + }, + ast.Node.Id.Asm => { + const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); + try stream.print("{} ", tree.tokenSlice(asm_node.asm_token)); + + if (asm_node.volatile_token) |volatile_token| { + try stream.print("{} ", tree.tokenSlice(volatile_token)); + } + + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = ")" }); + { + var i = asm_node.clobbers.len; + while (i != 0) { + i -= 1; + try stack.push(RenderState { .Expression = *asm_node.clobbers.at(i) }); + + if (i != 0) { + try stack.push(RenderState { .Text = ", " }); + } + } + } + try stack.push(RenderState { .Text = ": " }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "\n" }); + { + var i = asm_node.inputs.len; + while (i != 0) { + i -= 1; + const node = *asm_node.inputs.at(i); + try stack.push(RenderState { .Expression = &node.base}); + + if (i != 0) { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + const prev_node = *asm_node.inputs.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + break :blk "\n"; + }, + }); + try stack.push(RenderState { .Text = "," }); + } + } + } + try stack.push(RenderState { .Indent = indent + indent_delta + 2}); + try stack.push(RenderState { .Text = ": "}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { .Text = "\n" }); + { + var i = asm_node.outputs.len; + while (i != 0) { + i -= 1; + const node = *asm_node.outputs.at(i); + try stack.push(RenderState { .Expression = &node.base}); + + if (i != 0) { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + const prev_node = *asm_node.outputs.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + break :blk "\n"; + }, + }); + try stack.push(RenderState { .Text = "," }); + } + } + } + try stack.push(RenderState { .Indent = indent + indent_delta + 2}); + try stack.push(RenderState { .Text = ": "}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { .Text = "\n" }); + try stack.push(RenderState { .Expression = asm_node.template }); + try stack.push(RenderState { .Text = "(" }); + }, + ast.Node.Id.AsmInput => { + const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); + + try stack.push(RenderState { .Text = ")"}); + try stack.push(RenderState { .Expression = asm_input.expr}); + try stack.push(RenderState { .Text = " ("}); + try stack.push(RenderState { .Expression = asm_input.constraint }); + try stack.push(RenderState { .Text = "] "}); + try stack.push(RenderState { .Expression = asm_input.symbolic_name }); + try stack.push(RenderState { .Text = "["}); + }, + ast.Node.Id.AsmOutput => { + const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); + + try stack.push(RenderState { .Text = ")"}); + switch (asm_output.kind) { + ast.Node.AsmOutput.Kind.Variable => |variable_name| { + try stack.push(RenderState { .Expression = &variable_name.base}); + }, + ast.Node.AsmOutput.Kind.Return => |return_type| { + try stack.push(RenderState { .Expression = return_type}); + try stack.push(RenderState { .Text = "-> "}); + }, + } + try stack.push(RenderState { .Text = " ("}); + try stack.push(RenderState { .Expression = asm_output.constraint }); + try stack.push(RenderState { .Text = "] "}); + try stack.push(RenderState { .Expression = asm_output.symbolic_name }); + try stack.push(RenderState { .Text = "["}); + }, + + ast.Node.Id.StructField, + ast.Node.Id.UnionTag, + ast.Node.Id.EnumTag, + ast.Node.Id.ErrorTag, + ast.Node.Id.Root, + ast.Node.Id.VarDecl, + ast.Node.Id.Use, + ast.Node.Id.TestDecl, + ast.Node.Id.ParamDecl => unreachable, + }, + RenderState.Statement => |base| { + switch (base.id) { + ast.Node.Id.VarDecl => { + const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); + try stack.push(RenderState { .VarDecl = var_decl}); + }, + else => { + if (base.requireSemiColon()) { + try stack.push(RenderState { .Text = ";" }); + } + try stack.push(RenderState { .Expression = base }); + }, + } + }, + RenderState.Indent => |new_indent| indent = new_indent, + RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent), + } + } +} + +fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) !void { + const comment = node.doc_comments ?? return; + var it = comment.lines.iterator(0); + while (it.next()) |line_token_index| { + try stream.print("{}\n", tree.tokenSlice(*line_token_index)); + try stream.writeByteNTimes(' ', indent); + } +} + -- cgit v1.2.3 From ca27ce3bee16ebb611621f15830dd6bf74d65f9f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 May 2018 23:54:35 -0400 Subject: std.zig.parser supports same-line comments on any token --- std/zig/ast.zig | 12 +- std/zig/parse.zig | 504 +++++++++++++++++++++++++++--------------------- std/zig/parser_test.zig | 108 +++++------ 3 files changed, 351 insertions(+), 273 deletions(-) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 618b9155c2..a92555731d 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -25,7 +25,10 @@ pub const Tree = struct { } pub fn tokenSlice(self: &Tree, token_index: TokenIndex) []const u8 { - const token = self.tokens.at(token_index); + return self.tokenSlicePtr(self.tokens.at(token_index)); + } + + pub fn tokenSlicePtr(self: &Tree, token: &const Token) []const u8 { return self.source[token.start..token.end]; } @@ -36,14 +39,14 @@ pub const Tree = struct { line_end: usize, }; - pub fn tokenLocation(self: &Tree, start_index: usize, token_index: TokenIndex) Location { + pub fn tokenLocationPtr(self: &Tree, start_index: usize, token: &const Token) Location { var loc = Location { .line = 0, .column = 0, .line_start = start_index, .line_end = self.source.len, }; - const token_start = self.tokens.at(token_index).start; + const token_start = token.start; for (self.source[start_index..]) |c, i| { if (i + start_index == token_start) { loc.line_end = i + start_index; @@ -61,6 +64,9 @@ pub const Tree = struct { return loc; } + pub fn tokenLocation(self: &Tree, start_index: usize, token_index: TokenIndex) Location { + return self.tokenLocationPtr(start_index, self.tokens.at(token_index)); + } }; pub const Error = union(enum) { diff --git a/std/zig/parse.zig b/std/zig/parse.zig index f6c56cb7d0..405c7b995a 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -54,14 +54,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { switch (state) { State.TopLevel => { - while (try eatLineComment(arena, &tok_it)) |line_comment| { + while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| { try root_node.decls.push(&line_comment.base); } - const comments = try eatDocComments(arena, &tok_it); + const comments = try eatDocComments(arena, &tok_it, &tree); - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_test => { stack.push(State.TopLevel) catch unreachable; @@ -144,7 +145,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, else => { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); stack.push(State.TopLevel) catch unreachable; try stack.push(State { .TopLevelExtern = TopLevelDeclCtx { @@ -160,8 +161,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.TopLevelExtern => |ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_export, Token.Id.Keyword_inline => { stack.push(State { @@ -194,7 +196,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, else => { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); stack.push(State { .TopLevelDecl = ctx }) catch unreachable; continue; } @@ -202,10 +204,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.TopLevelLibname => |ctx| { const lib_name = blk: { - const lib_name_token_index = tok_it.index; - const lib_name_token_ptr = ??tok_it.next(); - break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index)) ?? { - _ = tok_it.prev(); + const lib_name_token = nextToken(&tok_it, &tree); + const lib_name_token_index = lib_name_token.index; + const lib_name_token_ptr = lib_name_token.ptr; + break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, &tree)) ?? { + putBackToken(&tok_it, &tree); break :blk null; }; }; @@ -222,8 +225,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.TopLevelDecl => |ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_use => { if (ctx.extern_export_inline_token) |annotated_token| { @@ -345,7 +349,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.TopLevelExternOrField => |ctx| { - if (eatToken(&tok_it, Token.Id.Identifier)) |identifier| { + if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |identifier| { std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct); const node = try arena.construct(ast.Node.StructField { .base = ast.Node { @@ -379,10 +383,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.FieldInitValue => |ctx| { - const eq_tok_index = tok_it.index; - const eq_tok_ptr = ??tok_it.next(); + const eq_tok = nextToken(&tok_it, &tree); + const eq_tok_index = eq_tok.index; + const eq_tok_ptr = eq_tok.ptr; if (eq_tok_ptr.id != Token.Id.Equal) { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } stack.push(State { .Expression = ctx }) catch unreachable; @@ -390,8 +395,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.ContainerKind => |ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl, ast.Node.ContainerDecl { .base = undefined, @@ -421,7 +427,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.ContainerInitArgStart => |container_decl| { - if (eatToken(&tok_it, Token.Id.LParen) == null) { + if (eatToken(&tok_it, &tree, Token.Id.LParen) == null) { continue; } @@ -431,24 +437,26 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.ContainerInitArg => |container_decl| { - const init_arg_token_index = tok_it.index; - const init_arg_token_ptr = ??tok_it.next(); + const init_arg_token = nextToken(&tok_it, &tree); + const init_arg_token_index = init_arg_token.index; + const init_arg_token_ptr = init_arg_token.ptr; switch (init_arg_token_ptr.id) { Token.Id.Keyword_enum => { container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null}; - const lparen_tok_index = tok_it.index; - const lparen_tok_ptr = ??tok_it.next(); + const lparen_tok = nextToken(&tok_it, &tree); + const lparen_tok_index = lparen_tok.index; + const lparen_tok_ptr = lparen_tok.ptr; if (lparen_tok_ptr.id == Token.Id.LParen) { try stack.push(State { .ExpectToken = Token.Id.RParen } ); try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &container_decl.init_arg_expr.Enum, } }); } else { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); } }, else => { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined }; stack.push(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; }, @@ -457,13 +465,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.ContainerDecl => |container_decl| { - while (try eatLineComment(arena, &tok_it)) |line_comment| { + while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| { try container_decl.fields_and_decls.push(&line_comment.base); } - const comments = try eatDocComments(arena, &tok_it); - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const comments = try eatDocComments(arena, &tok_it, &tree); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Identifier => { switch (container_decl.kind) { @@ -568,7 +577,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, else => { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; try stack.push(State { .TopLevelExtern = TopLevelDeclCtx { @@ -620,8 +629,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.VarDeclAlign => |var_decl| { try stack.push(State { .VarDeclEq = var_decl }); - const next_token_index = tok_it.index; - const next_token_ptr = ??tok_it.next(); + const next_token = nextToken(&tok_it, &tree); + const next_token_index = next_token.index; + const next_token_ptr = next_token.ptr; if (next_token_ptr.id == Token.Id.Keyword_align) { try stack.push(State { .ExpectToken = Token.Id.RParen }); try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} }); @@ -629,12 +639,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; }, State.VarDeclEq => |var_decl| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Equal => { var_decl.eq_token = token_index; @@ -662,8 +673,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.FnDef => |fn_proto| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch(token_ptr.id) { Token.Id.LBrace => { const block = try arena.construct(ast.Node.Block { @@ -691,7 +703,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.push(State { .ParamDecl = fn_proto }); try stack.push(State { .ExpectToken = Token.Id.LParen }); - if (eatToken(&tok_it, Token.Id.Identifier)) |name_token| { + if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |name_token| { fn_proto.name_token = name_token; } continue; @@ -699,7 +711,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.FnProtoAlign => |fn_proto| { stack.push(State { .FnProtoReturnType = fn_proto }) catch unreachable; - if (eatToken(&tok_it, Token.Id.Keyword_align)) |align_token| { + if (eatToken(&tok_it, &tree, Token.Id.Keyword_align)) |align_token| { try stack.push(State { .ExpectToken = Token.Id.RParen }); try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } }); try stack.push(State { .ExpectToken = Token.Id.LParen }); @@ -707,8 +719,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.FnProtoReturnType => |fn_proto| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Bang => { fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined }; @@ -732,7 +745,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } } - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined }; stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; continue; @@ -742,7 +755,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.ParamDecl => |fn_proto| { - if (eatToken(&tok_it, Token.Id.RParen)) |_| { + if (eatToken(&tok_it, &tree, Token.Id.RParen)) |_| { continue; } const param_decl = try arena.construct(ast.Node.ParamDecl { @@ -766,9 +779,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.ParamDeclAliasOrComptime => |param_decl| { - if (eatToken(&tok_it, Token.Id.Keyword_comptime)) |comptime_token| { + if (eatToken(&tok_it, &tree, Token.Id.Keyword_comptime)) |comptime_token| { param_decl.comptime_token = comptime_token; - } else if (eatToken(&tok_it, Token.Id.Keyword_noalias)) |noalias_token| { + } else if (eatToken(&tok_it, &tree, Token.Id.Keyword_noalias)) |noalias_token| { param_decl.noalias_token = noalias_token; } continue; @@ -776,17 +789,17 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.ParamDeclName => |param_decl| { // TODO: Here, we eat two tokens in one state. This means that we can't have // comments between these two tokens. - if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| { - if (eatToken(&tok_it, Token.Id.Colon)) |_| { + if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |ident_token| { + if (eatToken(&tok_it, &tree, Token.Id.Colon)) |_| { param_decl.name_token = ident_token; } else { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); } } continue; }, State.ParamDeclEnd => |ctx| { - if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| { + if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| { ctx.param_decl.var_args_token = ellipsis3; stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; continue; @@ -799,7 +812,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.ParamDeclComma => |fn_proto| { - switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) { + switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) { ExpectCommaOrEndResult.end_token => |t| { if (t == null) { stack.push(State { .ParamDecl = fn_proto }) catch unreachable; @@ -814,7 +827,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.MaybeLabeledExpression => |ctx| { - if (eatToken(&tok_it, Token.Id.Colon)) |_| { + if (eatToken(&tok_it, &tree, Token.Id.Colon)) |_| { stack.push(State { .LabeledExpression = LabelCtx { .label = ctx.label, @@ -828,8 +841,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.LabeledExpression => |ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LBrace => { const block = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block, @@ -899,14 +913,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { return tree; } - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; }, } }, State.Inline => |ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_while => { stack.push(State { @@ -938,7 +953,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { return tree; } - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; }, } @@ -995,7 +1010,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.Else => |dest| { - if (eatToken(&tok_it, Token.Id.Keyword_else)) |else_token| { + if (eatToken(&tok_it, &tree, Token.Id.Keyword_else)) |else_token| { const node = try createNode(arena, ast.Node.Else, ast.Node.Else { .base = undefined, @@ -1016,19 +1031,20 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.Block => |block| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.RBrace => { block.rbrace = token_index; continue; }, else => { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); stack.push(State { .Block = block }) catch unreachable; var any_comments = false; - while (try eatLineComment(arena, &tok_it)) |line_comment| { + while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| { try block.statements.push(&line_comment.base); any_comments = true; } @@ -1040,8 +1056,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.Statement => |block| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_comptime => { stack.push(State { @@ -1100,7 +1117,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, else => { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); const statement = try block.statements.addOne(); try stack.push(State { .Semicolon = statement }); try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); @@ -1109,8 +1126,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.ComptimeStatement => |ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_var, Token.Id.Keyword_const => { stack.push(State { @@ -1127,8 +1145,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, else => { - _ = tok_it.prev(); - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); + putBackToken(&tok_it, &tree); const statement = try ctx.block.statements.addOne(); try stack.push(State { .Semicolon = statement }); try stack.push(State { .Expression = OptionalCtx { .Required = statement } }); @@ -1146,10 +1164,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.AsmOutputItems => |items| { - const lbracket_index = tok_it.index; - const lbracket_ptr = ??tok_it.next(); + const lbracket = nextToken(&tok_it, &tree); + const lbracket_index = lbracket.index; + const lbracket_ptr = lbracket.ptr; if (lbracket_ptr.id != Token.Id.LBracket) { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } @@ -1174,8 +1193,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.AsmOutputReturnOrType => |node| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Identifier => { node.kind = ast.Node.AsmOutput.Kind { .Variable = try createLiteral(arena, ast.Node.Identifier, token_index) }; @@ -1197,10 +1217,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.AsmInputItems => |items| { - const lbracket_index = tok_it.index; - const lbracket_ptr = ??tok_it.next(); + const lbracket = nextToken(&tok_it, &tree); + const lbracket_index = lbracket.index; + const lbracket_ptr = lbracket.ptr; if (lbracket_ptr.id != Token.Id.LBracket) { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } @@ -1233,7 +1254,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.ExprListItemOrEnd => |list_state| { - if (eatToken(&tok_it, list_state.end)) |token_index| { + if (eatToken(&tok_it, &tree, list_state.end)) |token_index| { *list_state.ptr = token_index; continue; } @@ -1243,7 +1264,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.ExprListCommaOrEnd => |list_state| { - switch (expectCommaOrEnd(&tok_it, list_state.end)) { + switch (expectCommaOrEnd(&tok_it, &tree, list_state.end)) { ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { *list_state.ptr = end; continue; @@ -1258,11 +1279,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.FieldInitListItemOrEnd => |list_state| { - while (try eatLineComment(arena, &tok_it)) |line_comment| { + while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| { try list_state.list.push(&line_comment.base); } - if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { + if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| { *list_state.ptr = rbrace; continue; } @@ -1295,7 +1316,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.FieldInitListCommaOrEnd => |list_state| { - switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { + switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) { ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { *list_state.ptr = end; continue; @@ -1310,7 +1331,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.FieldListCommaOrEnd => |container_decl| { - switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { + switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) { ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { container_decl.rbrace_token = end; continue; @@ -1325,11 +1346,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.ErrorTagListItemOrEnd => |list_state| { - while (try eatLineComment(arena, &tok_it)) |line_comment| { + while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| { try list_state.list.push(&line_comment.base); } - if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { + if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| { *list_state.ptr = rbrace; continue; } @@ -1341,7 +1362,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.ErrorTagListCommaOrEnd => |list_state| { - switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { + switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) { ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { *list_state.ptr = end; continue; @@ -1356,16 +1377,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.SwitchCaseOrEnd => |list_state| { - while (try eatLineComment(arena, &tok_it)) |line_comment| { + while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| { try list_state.list.push(&line_comment.base); } - if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { + if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| { *list_state.ptr = rbrace; continue; } - const comments = try eatDocComments(arena, &tok_it); + const comments = try eatDocComments(arena, &tok_it, &tree); const node = try arena.construct(ast.Node.SwitchCase { .base = ast.Node { .id = ast.Node.Id.SwitchCase, @@ -1384,7 +1405,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.SwitchCaseCommaOrEnd => |list_state| { - switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) { + switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) { ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { *list_state.ptr = end; continue; @@ -1400,8 +1421,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.SwitchCaseFirstItem => |case_items| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (token_ptr.id == Token.Id.Keyword_else) { const else_node = try arena.construct(ast.Node.SwitchElse { .base = ast.Node{ .id = ast.Node.Id.SwitchElse}, @@ -1412,7 +1434,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.push(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); continue; } else { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); try stack.push(State { .SwitchCaseItem = case_items }); continue; } @@ -1422,7 +1444,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.push(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } }); }, State.SwitchCaseItemCommaOrEnd => |case_items| { - switch (expectCommaOrEnd(&tok_it, Token.Id.EqualAngleBracketRight)) { + switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.EqualAngleBracketRight)) { ExpectCommaOrEndResult.end_token => |t| { if (t == null) { stack.push(State { .SwitchCaseItem = case_items }) catch unreachable; @@ -1445,7 +1467,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.AsyncAllocator => |async_node| { - if (eatToken(&tok_it, Token.Id.AngleBracketLeft) == null) { + if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) { continue; } @@ -1491,7 +1513,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.ExternType => |ctx| { - if (eatToken(&tok_it, Token.Id.Keyword_fn)) |fn_token| { + if (eatToken(&tok_it, &tree, Token.Id.Keyword_fn)) |fn_token| { const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { .id = ast.Node.Id.FnProto, @@ -1525,8 +1547,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.SliceOrArrayAccess => |node| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Ellipsis2 => { const start = node.op.ArrayAccess; @@ -1559,7 +1582,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.SliceOrArrayType => |node| { - if (eatToken(&tok_it, Token.Id.RBracket)) |_| { + if (eatToken(&tok_it, &tree, Token.Id.RBracket)) |_| { node.op = ast.Node.PrefixOp.Op { .SliceType = ast.Node.PrefixOp.AddrOfInfo { .align_expr = null, @@ -1581,8 +1604,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.AddrOfModifiers => |addr_of_info| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_align => { stack.push(state) catch unreachable; @@ -1620,7 +1644,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, else => { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; }, } @@ -1628,8 +1652,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.Payload => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (token_ptr.id != Token.Id.Pipe) { if (opt_ctx != OptionalCtx.Optional) { *(try tree.errors.addOne()) = Error { @@ -1641,7 +1666,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { return tree; } - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } @@ -1664,8 +1689,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.PointerPayload => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (token_ptr.id != Token.Id.Pipe) { if (opt_ctx != OptionalCtx.Optional) { *(try tree.errors.addOne()) = Error { @@ -1677,7 +1703,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { return tree; } - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } @@ -1707,8 +1733,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.PointerIndexPayload => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (token_ptr.id != Token.Id.Pipe) { if (opt_ctx != OptionalCtx.Optional) { *(try tree.errors.addOne()) = Error { @@ -1720,7 +1747,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { return tree; } - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } @@ -1755,8 +1782,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.Expression => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { const node = try createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression, @@ -1808,7 +1836,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, else => { if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); stack.push(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable; } continue; @@ -1823,7 +1851,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.RangeExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| { + if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { .base = undefined, @@ -1846,8 +1874,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.AssignmentExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (tokenIdToAssignment(token_ptr.id)) |ass_id| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { @@ -1862,7 +1891,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }); continue; } else { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } }, @@ -1876,8 +1905,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.UnwrapExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { @@ -1897,7 +1927,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } continue; } else { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } }, @@ -1911,7 +1941,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.BoolOrExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - if (eatToken(&tok_it, Token.Id.Keyword_or)) |or_token| { + if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { .base = undefined, @@ -1936,7 +1966,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.BoolAndExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - if (eatToken(&tok_it, Token.Id.Keyword_and)) |and_token| { + if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { .base = undefined, @@ -1961,8 +1991,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.ComparisonExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (tokenIdToComparison(token_ptr.id)) |comp_id| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { @@ -1977,7 +2008,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.push(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } else { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } }, @@ -1991,7 +2022,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.BinaryOrExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - if (eatToken(&tok_it, Token.Id.Pipe)) |pipe| { + if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { .base = undefined, @@ -2016,7 +2047,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.BinaryXorExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - if (eatToken(&tok_it, Token.Id.Caret)) |caret| { + if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { .base = undefined, @@ -2041,7 +2072,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.BinaryAndExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - if (eatToken(&tok_it, Token.Id.Ampersand)) |ampersand| { + if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { .base = undefined, @@ -2066,8 +2097,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.BitShiftExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { @@ -2082,7 +2114,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.push(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } else { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } }, @@ -2096,8 +2128,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.AdditionExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (tokenIdToAddition(token_ptr.id)) |add_id| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { @@ -2112,7 +2145,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.push(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } else { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } }, @@ -2126,8 +2159,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.MultiplyExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (tokenIdToMultiply(token_ptr.id)) |mult_id| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { @@ -2142,7 +2176,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.push(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } else { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } }, @@ -2210,7 +2244,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.TypeExprEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - if (eatToken(&tok_it, Token.Id.Bang)) |bang| { + if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| { const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, ast.Node.InfixOp { .base = undefined, @@ -2227,8 +2261,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.PrefixOpExpression => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| { var node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, ast.Node.PrefixOp { @@ -2259,14 +2294,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } continue; } else { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); stack.push(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable; continue; } }, State.SuffixOpExpressionBegin => |opt_ctx| { - if (eatToken(&tok_it, Token.Id.Keyword_async)) |async_token| { + if (eatToken(&tok_it, &tree, Token.Id.Keyword_async)) |async_token| { const async_node = try createNode(arena, ast.Node.AsyncAttribute, ast.Node.AsyncAttribute { .base = undefined, @@ -2295,8 +2330,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.SuffixOpExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LParen => { const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, @@ -2353,50 +2389,49 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, else => { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; }, } }, State.PrimaryExpression => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - switch (token_ptr.id) { + const token = nextToken(&tok_it, &tree); + switch (token.ptr.id) { Token.Id.IntegerLiteral => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token_index); + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token.index); continue; }, Token.Id.FloatLiteral => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token_index); + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token.index); continue; }, Token.Id.CharLiteral => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token_index); + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token.index); continue; }, Token.Id.Keyword_undefined => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token_index); + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token.index); continue; }, Token.Id.Keyword_true, Token.Id.Keyword_false => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token_index); + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token.index); continue; }, Token.Id.Keyword_null => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token_index); + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token.index); continue; }, Token.Id.Keyword_this => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token_index); + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token.index); continue; }, Token.Id.Keyword_var => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token_index); + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token.index); continue; }, Token.Id.Keyword_unreachable => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token_index); + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token.index); continue; }, Token.Id.Keyword_promise => { @@ -2404,14 +2439,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .base = ast.Node { .id = ast.Node.Id.PromiseType, }, - .promise_token = token_index, + .promise_token = token.index, .result = null, }); opt_ctx.store(&node.base); - const next_token_index = tok_it.index; - const next_token_ptr = ??tok_it.next(); + const next_token = nextToken(&tok_it, &tree); + const next_token_index = next_token.index; + const next_token_ptr = next_token.ptr; if (next_token_ptr.id != Token.Id.Arrow) { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); continue; } node.result = ast.Node.PromiseType.Result { @@ -2423,14 +2459,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { - opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? unreachable); + opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) ?? unreachable); continue; }, Token.Id.LParen => { const node = try createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression, ast.Node.GroupedExpression { .base = undefined, - .lparen = token_index, + .lparen = token.index, .expr = undefined, .rparen = undefined, } @@ -2448,7 +2484,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const node = try createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall, ast.Node.BuiltinCall { .base = undefined, - .builtin_token = token_index, + .builtin_token = token.index, .params = ast.Node.BuiltinCall.ParamList.init(arena), .rparen_token = undefined, } @@ -2467,7 +2503,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, ast.Node.PrefixOp { .base = undefined, - .op_token = token_index, + .op_token = token.index, .op = undefined, .rhs = undefined, } @@ -2478,7 +2514,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Keyword_error => { stack.push(State { .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx { - .error_token = token_index, + .error_token = token.index, .opt_ctx = opt_ctx } }) catch unreachable; @@ -2488,7 +2524,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { stack.push(State { .ContainerKind = ContainerKindCtx { .opt_ctx = opt_ctx, - .ltoken = token_index, + .ltoken = token.index, .layout = ast.Node.ContainerDecl.Layout.Packed, }, }) catch unreachable; @@ -2498,18 +2534,18 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { stack.push(State { .ExternType = ExternTypeCtx { .opt_ctx = opt_ctx, - .extern_token = token_index, + .extern_token = token.index, .comments = null, }, }) catch unreachable; continue; }, Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => { - _ = tok_it.prev(); + putBackToken(&tok_it, &tree); stack.push(State { .ContainerKind = ContainerKindCtx { .opt_ctx = opt_ctx, - .ltoken = token_index, + .ltoken = token.index, .layout = ast.Node.ContainerDecl.Layout.Auto, }, }) catch unreachable; @@ -2518,7 +2554,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Identifier => { stack.push(State { .MaybeLabeledExpression = MaybeLabeledExpressionCtx { - .label = token_index, + .label = token.index, .opt_ctx = opt_ctx } }) catch unreachable; @@ -2532,7 +2568,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .doc_comments = null, .visib_token = null, .name_token = null, - .fn_token = token_index, + .fn_token = token.index, .params = ast.Node.FnProto.ParamList.init(arena), .return_type = undefined, .var_args_token = null, @@ -2560,7 +2596,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .return_type = undefined, .var_args_token = null, .extern_export_inline_token = null, - .cc_token = token_index, + .cc_token = token.index, .async_attr = null, .body_node = null, .lib_name = null, @@ -2580,7 +2616,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const node = try createToCtxNode(arena, opt_ctx, ast.Node.Asm, ast.Node.Asm { .base = undefined, - .asm_token = token_index, + .asm_token = token.index, .volatile_token = null, .template = undefined, .outputs = ast.Node.Asm.OutputList.init(arena), @@ -2614,18 +2650,18 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { stack.push(State { .Inline = InlineCtx { .label = null, - .inline_token = token_index, + .inline_token = token.index, .opt_ctx = opt_ctx, } }) catch unreachable; continue; }, else => { - if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) { - _ = tok_it.prev(); + if (!try parseBlockExpr(&stack, arena, opt_ctx, token.ptr, token.index)) { + putBackToken(&tok_it, &tree); if (opt_ctx != OptionalCtx.Optional) { *(try tree.errors.addOne()) = Error { - .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index }, + .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token.index }, }; return tree; } @@ -2637,7 +2673,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.ErrorTypeOrSetDecl => |ctx| { - if (eatToken(&tok_it, Token.Id.LBrace) == null) { + if (eatToken(&tok_it, &tree, Token.Id.LBrace) == null) { _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token); continue; } @@ -2661,11 +2697,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.StringLiteral => |opt_ctx| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; opt_ctx.store( - (try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? { - _ = tok_it.prev(); + (try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) ?? { + putBackToken(&tok_it, &tree); if (opt_ctx != OptionalCtx.Optional) { *(try tree.errors.addOne()) = Error { .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index }, @@ -2679,14 +2716,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.Identifier => |opt_ctx| { - if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| { + if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |ident_token| { _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token); continue; } if (opt_ctx != OptionalCtx.Optional) { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; *(try tree.errors.addOne()) = Error { .ExpectedToken = Error.ExpectedToken { .token = token_index, @@ -2698,9 +2736,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.ErrorTag => |node_ptr| { - const comments = try eatDocComments(arena, &tok_it); - const ident_token_index = tok_it.index; - const ident_token_ptr = ??tok_it.next(); + const comments = try eatDocComments(arena, &tok_it, &tree); + const ident_token = nextToken(&tok_it, &tree); + const ident_token_index = ident_token.index; + const ident_token_ptr = ident_token.ptr; if (ident_token_ptr.id != Token.Id.Identifier) { *(try tree.errors.addOne()) = Error { .ExpectedToken = Error.ExpectedToken { @@ -2723,8 +2762,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.ExpectToken => |token_id| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (token_ptr.id != token_id) { *(try tree.errors.addOne()) = Error { .ExpectedToken = Error.ExpectedToken { @@ -2737,8 +2777,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.ExpectTokenSave => |expect_token_save| { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); + const token = nextToken(&tok_it, &tree); + const token_index = token.index; + const token_ptr = token.ptr; if (token_ptr.id != expect_token_save.id) { *(try tree.errors.addOne()) = Error { .ExpectedToken = Error.ExpectedToken { @@ -2752,7 +2793,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.IfToken => |token_id| { - if (eatToken(&tok_it, token_id)) |_| { + if (eatToken(&tok_it, &tree, token_id)) |_| { continue; } @@ -2760,7 +2801,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.IfTokenSave => |if_token_save| { - if (eatToken(&tok_it, if_token_save.id)) |token_index| { + if (eatToken(&tok_it, &tree, if_token_save.id)) |token_index| { *if_token_save.ptr = token_index; continue; } @@ -2769,7 +2810,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.OptionalTokenSave => |optional_token_save| { - if (eatToken(&tok_it, optional_token_save.id)) |token_index| { + if (eatToken(&tok_it, &tree, optional_token_save.id)) |token_index| { *optional_token_save.ptr = token_index; continue; } @@ -3043,10 +3084,10 @@ const State = union(enum) { OptionalTokenSave: OptionalTokenSave, }; -fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.DocComment { +fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) !?&ast.Node.DocComment { var result: ?&ast.Node.DocComment = null; while (true) { - if (eatToken(tok_it, Token.Id.DocComment)) |line_comment| { + if (eatToken(tok_it, tree, Token.Id.DocComment)) |line_comment| { const node = blk: { if (result) |comment_node| { break :blk comment_node; @@ -3069,8 +3110,8 @@ fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) ! return result; } -fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.LineComment { - const token = eatToken(tok_it, Token.Id.LineComment) ?? return null; +fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) !?&ast.Node.LineComment { + const token = eatToken(tok_it, tree, Token.Id.LineComment) ?? return null; return try arena.construct(ast.Node.LineComment { .base = ast.Node { .id = ast.Node.Id.LineComment, @@ -3080,7 +3121,7 @@ fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) ! } fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, - token_ptr: &const Token, token_index: TokenIndex) !?&ast.Node + token_ptr: &const Token, token_index: TokenIndex, tree: &ast.Tree) !?&ast.Node { switch (token_ptr.id) { Token.Id.StringLiteral => { @@ -3093,10 +3134,11 @@ fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterato }); try node.lines.push(token_index); while (true) { - const multiline_str_index = tok_it.index; - const multiline_str_ptr = ??tok_it.next(); + const multiline_str = nextToken(tok_it, tree); + const multiline_str_index = multiline_str.index; + const multiline_str_ptr = multiline_str.ptr; if (multiline_str_ptr.id != Token.Id.MultilineStringLiteralLine) { - _ = tok_it.prev(); + putBackToken(tok_it, tree); break; } @@ -3230,9 +3272,10 @@ const ExpectCommaOrEndResult = union(enum) { parse_error: Error, }; -fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, end: @TagType(Token.Id)) ExpectCommaOrEndResult { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); +fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, end: @TagType(Token.Id)) ExpectCommaOrEndResult { + const token = nextToken(tok_it, tree); + const token_index = token.index; + const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Comma => return ExpectCommaOrEndResult { .end_token = null}, else => { @@ -3385,16 +3428,45 @@ fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, compti return node; } -fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, id: @TagType(Token.Id)) ?TokenIndex { - const token_index = tok_it.index; - const token_ptr = ??tok_it.next(); - if (token_ptr.id == id) - return token_index; +fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, id: @TagType(Token.Id)) ?TokenIndex { + const token = nextToken(tok_it, tree); + + if (token.ptr.id == id) + return token.index; - _ = tok_it.prev(); + putBackToken(tok_it, tree); return null; } +fn nextToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) AnnotatedToken { + const result = AnnotatedToken { + .index = tok_it.index, + .ptr = ??tok_it.next(), + }; + // possibly skip a following same line token + const token = tok_it.next() ?? return result; + if (token.id != Token.Id.LineComment) { + putBackToken(tok_it, tree); + return result; + } + const loc = tree.tokenLocationPtr(result.ptr.end, token); + if (loc.line != 0) { + putBackToken(tok_it, tree); + } + return result; +} + +fn putBackToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) void { + const prev_tok = ??tok_it.prev(); + if (prev_tok.id == Token.Id.LineComment) { + const minus2_tok = tok_it.prev() ?? return; + const loc = tree.tokenLocationPtr(minus2_tok.end, prev_tok); + if (loc.line != 0) { + _ = tok_it.next(); + } + } +} + const RenderAstFrame = struct { node: &ast.Node, indent: usize, diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index dd20a6dd8e..7b5358d238 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,48 @@ +//test "zig fmt: same-line comment after a statement" { +// try testCanonical( +// \\test "" { +// \\ a = b; +// \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption +// \\ a = b; +// \\} +// \\ +// ); +//} +// +//test "zig fmt: same-line comment after var decl in struct" { +// try testCanonical( +// \\pub const vfs_cap_data = extern struct { +// \\ const Data = struct {}; // when on disk. +// \\}; +// \\ +// ); +//} +// +//test "zig fmt: same-line comment after field decl" { +// try testCanonical( +// \\pub const dirent = extern struct { +// \\ d_name: u8, +// \\ d_name: u8, // comment 1 +// \\ d_name: u8, +// \\ d_name: u8, // comment 2 +// \\ d_name: u8, +// \\}; +// \\ +// ); +//} +// +//test "zig fmt: same-line comment after switch prong" { +// try testCanonical( +// \\test "" { +// \\ switch (err) { +// \\ error.PathAlreadyExists => {}, // comment 2 +// \\ else => return err, // comment 1 +// \\ } +// \\} +// \\ +// ); +//} +// //test "zig fmt: same-line comment after non-block if expression" { // try testCanonical( // \\comptime { @@ -7,6 +52,15 @@ // \\ // ); //} +// +//test "zig fmt: same-line comment on comptime expression" { +// try testCanonical( +// \\test "" { +// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt +// \\} +// \\ +// ); +//} test "zig fmt: switch with empty body" { try testCanonical( @@ -17,15 +71,6 @@ test "zig fmt: switch with empty body" { ); } -//test "zig fmt: same-line comment on comptime expression" { -// try testCanonical( -// \\test "" { -// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt -// \\} -// \\ -// ); -//} - test "zig fmt: float literal with exponent" { try testCanonical( \\pub const f64_true_min = 4.94065645841246544177e-324; @@ -152,18 +197,6 @@ test "zig fmt: comments before switch prong" { ); } -//test "zig fmt: same-line comment after switch prong" { -// try testCanonical( -// \\test "" { -// \\ switch (err) { -// \\ error.PathAlreadyExists => {}, // comment 2 -// \\ else => return err, // comment 1 -// \\ } -// \\} -// \\ -// ); -//} - test "zig fmt: comments before var decl in struct" { try testCanonical( \\pub const vfs_cap_data = extern struct { @@ -189,28 +222,6 @@ test "zig fmt: comments before var decl in struct" { ); } -//test "zig fmt: same-line comment after var decl in struct" { -// try testCanonical( -// \\pub const vfs_cap_data = extern struct { -// \\ const Data = struct {}; // when on disk. -// \\}; -// \\ -// ); -//} -// -//test "zig fmt: same-line comment after field decl" { -// try testCanonical( -// \\pub const dirent = extern struct { -// \\ d_name: u8, -// \\ d_name: u8, // comment 1 -// \\ d_name: u8, -// \\ d_name: u8, // comment 2 -// \\ d_name: u8, -// \\}; -// \\ -// ); -//} - test "zig fmt: array literal with 1 item on 1 line" { try testCanonical( \\var s = []const u64{0} ** 25; @@ -218,17 +229,6 @@ test "zig fmt: array literal with 1 item on 1 line" { ); } -//test "zig fmt: same-line comment after a statement" { -// try testCanonical( -// \\test "" { -// \\ a = b; -// \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption -// \\ a = b; -// \\} -// \\ -// ); -//} - test "zig fmt: comments before global variables" { try testCanonical( \\/// Foo copies keys and values before they go into the map, and -- cgit v1.2.3 From 670c9f9b741651f8b9873356a9e24da07c3ed355 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 May 2018 16:23:08 -0400 Subject: add benchmark for measuring parser performance --- std/zig/bench.zig | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 std/zig/bench.zig diff --git a/std/zig/bench.zig b/std/zig/bench.zig new file mode 100644 index 0000000000..c3b6b0d3d3 --- /dev/null +++ b/std/zig/bench.zig @@ -0,0 +1,38 @@ +const std = @import("std"); +const mem = std.mem; +const warn = std.debug.warn; +const Tokenizer = std.zig.Tokenizer; +const Parser = std.zig.Parser; +const io = std.io; + +const source = @embedFile("../os/index.zig"); +var fixed_buffer_mem: [10 * 1024 * 1024]u8 = undefined; + +pub fn main() !void { + var i: usize = 0; + var timer = try std.os.time.Timer.start(); + const start = timer.lap(); + const iterations = 100; + var memory_used: usize = 0; + while (i < iterations) : (i += 1) { + memory_used += testOnce(); + } + const end = timer.read(); + memory_used /= iterations; + const elapsed_s = f64(end - start) / std.os.time.ns_per_s; + const bytes_per_sec = f64(source.len * iterations) / elapsed_s; + const mb_per_sec = bytes_per_sec / (1024 * 1024); + + var stdout_file = try std.io.getStdOut(); + const stdout = &std.io.FileOutStream.init(&stdout_file).stream; + try stdout.print("{.3} MB/s, {} KB used \n", mb_per_sec, memory_used / 1024); +} + +fn testOnce() usize { + var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); + var allocator = &fixed_buf_alloc.allocator; + var tokenizer = Tokenizer.init(source); + var parser = Parser.init(&tokenizer, allocator, "(memory buffer)"); + _ = parser.parse() catch @panic("parse failure"); + return fixed_buf_alloc.end_index; +} -- cgit v1.2.3 From 9b29c872ce1836743b64c37db5272a7d7893f474 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Wed, 9 May 2018 09:34:04 +0200 Subject: Added Slice as it's own type info in userland --- src/analyze.cpp | 6 ++-- src/analyze.hpp | 2 +- src/codegen.cpp | 4 +++ src/ir.cpp | 80 ++++++++++++++++++++++++++++-------------------- test/cases/type_info.zig | 25 +++++++++++++-- 5 files changed, 78 insertions(+), 39 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 0f2fdf15de..590c946f7e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5931,8 +5931,8 @@ size_t type_id_len() { return array_length(all_type_ids); } -size_t type_id_index(TypeTableEntryId id) { - switch (id) { +size_t type_id_index(TypeTableEntry *entry) { + switch (entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdMetaType: @@ -5952,6 +5952,8 @@ size_t type_id_index(TypeTableEntryId id) { case TypeTableEntryIdArray: return 7; case TypeTableEntryIdStruct: + if (entry->data.structure.is_slice) + return 25; return 8; case TypeTableEntryIdNumLitFloat: return 9; diff --git a/src/analyze.hpp b/src/analyze.hpp index aca78f4e25..56ca21a93f 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -174,7 +174,7 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value); const char *type_id_name(TypeTableEntryId id); TypeTableEntryId type_id_at_index(size_t index); size_t type_id_len(); -size_t type_id_index(TypeTableEntryId id); +size_t type_id_index(TypeTableEntry *entry); TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id); bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry); LinkLib *create_link_lib(Buf *name); diff --git a/src/codegen.cpp b/src/codegen.cpp index db69708e9a..4e58f86d4b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6345,6 +6345,7 @@ static void define_builtin_compile_vars(CodeGen *g) { const TypeTableEntryId id = type_id_at_index(i); buf_appendf(contents, " %s,\n", type_id_name(id)); } + buf_appendf(contents, " Slice,\n"); buf_appendf(contents, "};\n\n"); } { @@ -6357,6 +6358,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " Int: Int,\n" " Float: Float,\n" " Pointer: Pointer,\n" + " Slice: Slice,\n" " Array: Array,\n" " Struct: Struct,\n" " FloatLiteral: void,\n" @@ -6392,6 +6394,8 @@ static void define_builtin_compile_vars(CodeGen *g) { " child: type,\n" " };\n" "\n" + " pub const Slice = Pointer;\n" + "\n" " pub const Array = struct {\n" " len: usize,\n" " child: type,\n" diff --git a/src/ir.cpp b/src/ir.cpp index cdf56f7fee..035e27707a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15785,11 +15785,10 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na Buf field_name = BUF_INIT; buf_init_from_str(&field_name, type_name); - auto entry = type_info_scope->decl_table.maybe_get(&field_name); + auto entry = type_info_scope->decl_table.get(&field_name); buf_deinit(&field_name); - assert(entry != nullptr); - TldVar *tld = (TldVar *)entry->value; + TldVar *tld = (TldVar *)entry; assert(tld->base.id == TldIdVar); VariableTableEntry *var = tld->var; @@ -16071,6 +16070,38 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t enum_field_val->data.x_struct.fields = inner_fields; }; + const auto create_ptr_like_type_info = [ira](const char *name, TypeTableEntry *ptr_type_entry) { + ConstExprValue *result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, name); + + ConstExprValue *fields = create_const_vals(4); + result->data.x_struct.fields = fields; + + // is_const: bool + ensure_field_index(result->type, "is_const", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_bool; + fields[0].data.x_bool = ptr_type_entry->data.pointer.is_const; + // is_volatile: bool + ensure_field_index(result->type, "is_volatile", 1); + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_bool; + fields[1].data.x_bool = ptr_type_entry->data.pointer.is_volatile; + // alignment: u32 + ensure_field_index(result->type, "alignment", 2); + fields[2].special = ConstValSpecialStatic; + fields[2].type = ira->codegen->builtin_types.entry_u32; + bigint_init_unsigned(&fields[2].data.x_bigint, ptr_type_entry->data.pointer.alignment); + // child: type + ensure_field_index(result->type, "child", 3); + fields[3].special = ConstValSpecialStatic; + fields[3].type = ira->codegen->builtin_types.entry_type; + fields[3].data.x_type = ptr_type_entry->data.pointer.child_type; + + return result; + }; + ConstExprValue *result = nullptr; switch (type_entry->id) { @@ -16139,34 +16170,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } case TypeTableEntryIdPointer: { - result = create_const_vals(1); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Pointer"); - - ConstExprValue *fields = create_const_vals(4); - result->data.x_struct.fields = fields; - - // is_const: bool - ensure_field_index(result->type, "is_const", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = ira->codegen->builtin_types.entry_bool; - fields[0].data.x_bool = type_entry->data.pointer.is_const; - // is_volatile: bool - ensure_field_index(result->type, "is_volatile", 1); - fields[1].special = ConstValSpecialStatic; - fields[1].type = ira->codegen->builtin_types.entry_bool; - fields[1].data.x_bool = type_entry->data.pointer.is_volatile; - // alignment: u32 - ensure_field_index(result->type, "alignment", 2); - fields[2].special = ConstValSpecialStatic; - fields[2].type = ira->codegen->builtin_types.entry_u32; - bigint_init_unsigned(&fields[2].data.x_bigint, type_entry->data.pointer.alignment); - // child: type - ensure_field_index(result->type, "child", 3); - fields[3].special = ConstValSpecialStatic; - fields[3].type = ira->codegen->builtin_types.entry_type; - fields[3].data.x_type = type_entry->data.pointer.child_type; - + result = create_ptr_like_type_info("Pointer", type_entry); break; } case TypeTableEntryIdArray: @@ -16436,6 +16440,16 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } case TypeTableEntryIdStruct: { + if (type_entry->data.structure.is_slice) { + Buf ptr_field_name = BUF_INIT; + buf_init_from_str(&ptr_field_name, "ptr"); + TypeTableEntry *ptr_type = type_entry->data.structure.fields_by_name.get(&ptr_field_name)->type_entry; + ensure_complete_type(ira->codegen, ptr_type); + + result = create_ptr_like_type_info("Slice", ptr_type); + break; + } + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Struct"); @@ -16622,7 +16636,7 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = result_type; - bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); + bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry)); ConstExprValue *payload = ir_make_type_info_value(ira, type_entry); out_val->data.x_union.payload = payload; @@ -16650,7 +16664,7 @@ static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira, TypeTableEntry *result_type = var_value->data.x_type; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - bigint_init_unsigned(&out_val->data.x_enum_tag, type_id_index(type_entry->id)); + bigint_init_unsigned(&out_val->data.x_enum_tag, type_id_index(type_entry)); return result_type; } diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index c9b15157e8..f10703e3ee 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -25,7 +25,7 @@ test "type info: integer, floating point type info" { } } -test "type info: pointer, array and nullable type info" { +test "type info: pointer type info" { comptime { const u32_ptr_info = @typeInfo(&u32); assert(TypeId(u32_ptr_info) == TypeId.Pointer); @@ -33,12 +33,31 @@ test "type info: pointer, array and nullable type info" { assert(u32_ptr_info.Pointer.is_volatile == false); assert(u32_ptr_info.Pointer.alignment == 4); assert(u32_ptr_info.Pointer.child == u32); + } +} + +test "type info: slice type info" { + comptime { + const u32_slice_info = @typeInfo([]u32); + assert(TypeId(u32_slice_info) == TypeId.Slice); + assert(u32_slice_info.Slice.is_const == false); + assert(u32_slice_info.Slice.is_volatile == false); + assert(u32_slice_info.Slice.alignment == 4); + assert(u32_slice_info.Slice.child == u32); + } +} +test "type info: array type info" { + comptime { const arr_info = @typeInfo([42]bool); assert(TypeId(arr_info) == TypeId.Array); assert(arr_info.Array.len == 42); assert(arr_info.Array.child == bool); + } +} +test "type info: nullable type info" { + comptime { const null_info = @typeInfo(?void); assert(TypeId(null_info) == TypeId.Nullable); assert(null_info.Nullable.child == void); @@ -100,11 +119,11 @@ test "type info: union info" { assert(TypeId(typeinfo_info) == TypeId.Union); assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); assert(typeinfo_info.Union.tag_type == TypeId); - assert(typeinfo_info.Union.fields.len == 25); + assert(typeinfo_info.Union.fields.len == 26); assert(typeinfo_info.Union.fields[4].enum_field != null); assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); - assert(typeinfo_info.Union.defs.len == 20); + assert(typeinfo_info.Union.defs.len == 21); const TestNoTagUnion = union { Foo: void, -- cgit v1.2.3 From 2a74aa206781b56d3aae5c0e8a94c75f0d73ac51 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Wed, 9 May 2018 09:40:57 +0200 Subject: Freeing ptr_field_name after use --- src/ir.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ir.cpp b/src/ir.cpp index 035e27707a..777e219a46 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16445,6 +16445,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t buf_init_from_str(&ptr_field_name, "ptr"); TypeTableEntry *ptr_type = type_entry->data.structure.fields_by_name.get(&ptr_field_name)->type_entry; ensure_complete_type(ira->codegen, ptr_type); + buf_deinit(&ptr_field_name); result = create_ptr_like_type_info("Slice", ptr_type); break; -- cgit v1.2.3 From bf21747a426887ed2ac866c9a9d317f64b22da79 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 May 2018 20:23:36 -0400 Subject: translate-c: fix typedef duplicate definition of variable closes #998 --- src/translate_c.cpp | 8 ++++++-- test/translate_c.zig | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index c0a76b8969..608d717b16 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -3667,6 +3667,7 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_ if (existing_entry) { return existing_entry->value; } + QualType child_qt = typedef_decl->getUnderlyingType(); Buf *type_name = buf_create_from_str(decl_name(typedef_decl)); @@ -3700,16 +3701,19 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_ // use the name of this typedef // TODO + // trans_qual_type here might cause us to look at this typedef again so we put the item in the map first + AstNode *symbol_node = trans_create_node_symbol(c, type_name); + c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node); + AstNode *type_node = trans_qual_type(c, child_qt, typedef_decl->getLocation()); if (type_node == nullptr) { emit_warning(c, typedef_decl->getLocation(), "typedef %s - unresolved child type", buf_ptr(type_name)); c->decl_table.put(typedef_decl, nullptr); + // TODO add global var with type_name equal to @compileError("unable to resolve C type") return nullptr; } add_global_var(c, type_name, type_node); - AstNode *symbol_node = trans_create_node_symbol(c, type_name); - c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node); return symbol_node; } diff --git a/test/translate_c.zig b/test/translate_c.zig index a5b5f3ae2a..2cd59f6f75 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,27 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.TranslateCContext) void { + cases.add("double define struct", + \\typedef struct Bar Bar; + \\typedef struct Foo Foo; + \\ + \\struct Foo { + \\ Foo *a; + \\}; + \\ + \\struct Bar { + \\ Foo *a; + \\}; + , + \\pub const struct_Foo = extern struct { + \\ a: ?&Foo, + \\}; + \\pub const Foo = struct_Foo; + \\pub const struct_Bar = extern struct { + \\ a: ?&Foo, + \\}; + ); + cases.addAllowWarnings("simple data types", \\#include \\int foo(char a, unsigned char b, signed char c); -- cgit v1.2.3 From 403e5239e3668f626ac105fbfbb08456b859963a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 May 2018 21:15:34 -0400 Subject: all tests passing again --- std/zig/parser_test.zig | 126 ++++++++++++++++++++++++------------------------ std/zig/render.zig | 56 ++++++++++++++++----- 2 files changed, 106 insertions(+), 76 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 7b5358d238..29b231a4db 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,66 +1,66 @@ -//test "zig fmt: same-line comment after a statement" { -// try testCanonical( -// \\test "" { -// \\ a = b; -// \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption -// \\ a = b; -// \\} -// \\ -// ); -//} -// -//test "zig fmt: same-line comment after var decl in struct" { -// try testCanonical( -// \\pub const vfs_cap_data = extern struct { -// \\ const Data = struct {}; // when on disk. -// \\}; -// \\ -// ); -//} -// -//test "zig fmt: same-line comment after field decl" { -// try testCanonical( -// \\pub const dirent = extern struct { -// \\ d_name: u8, -// \\ d_name: u8, // comment 1 -// \\ d_name: u8, -// \\ d_name: u8, // comment 2 -// \\ d_name: u8, -// \\}; -// \\ -// ); -//} -// -//test "zig fmt: same-line comment after switch prong" { -// try testCanonical( -// \\test "" { -// \\ switch (err) { -// \\ error.PathAlreadyExists => {}, // comment 2 -// \\ else => return err, // comment 1 -// \\ } -// \\} -// \\ -// ); -//} -// -//test "zig fmt: same-line comment after non-block if expression" { -// try testCanonical( -// \\comptime { -// \\ if (sr > n_uword_bits - 1) // d > r -// \\ return 0; -// \\} -// \\ -// ); -//} -// -//test "zig fmt: same-line comment on comptime expression" { -// try testCanonical( -// \\test "" { -// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt -// \\} -// \\ -// ); -//} +test "zig fmt: same-line comment after a statement" { + try testCanonical( + \\test "" { + \\ a = b; + \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption + \\ a = b; + \\} + \\ + ); +} + +test "zig fmt: same-line comment after var decl in struct" { + try testCanonical( + \\pub const vfs_cap_data = extern struct { + \\ const Data = struct {}; // when on disk. + \\}; + \\ + ); +} + +test "zig fmt: same-line comment after field decl" { + try testCanonical( + \\pub const dirent = extern struct { + \\ d_name: u8, + \\ d_name: u8, // comment 1 + \\ d_name: u8, + \\ d_name: u8, // comment 2 + \\ d_name: u8, + \\}; + \\ + ); +} + +test "zig fmt: same-line comment after switch prong" { + try testCanonical( + \\test "" { + \\ switch (err) { + \\ error.PathAlreadyExists => {}, // comment 2 + \\ else => return err, // comment 1 + \\ } + \\} + \\ + ); +} + +test "zig fmt: same-line comment after non-block if expression" { + try testCanonical( + \\comptime { + \\ if (sr > n_uword_bits - 1) // d > r + \\ return 0; + \\} + \\ + ); +} + +test "zig fmt: same-line comment on comptime expression" { + try testCanonical( + \\test "" { + \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt + \\} + \\ + ); +} test "zig fmt: switch with empty body" { try testCanonical( diff --git a/std/zig/render.zig b/std/zig/render.zig index 3fa7c4c171..00a5613765 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -14,8 +14,13 @@ const RenderState = union(enum) { Statement: &ast.Node, PrintIndent, Indent: usize, + MaybeSemiColon: &ast.Node, + Token: ast.TokenIndex, + NonBreakToken: ast.TokenIndex, }; +const indent_delta = 4; + pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { var stack = SegmentedList(RenderState, 32).init(allocator); defer stack.deinit(); @@ -44,7 +49,6 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { } } - const indent_delta = 4; var indent: usize = 0; while (stack.pop()) |state| { switch (state) { @@ -92,7 +96,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.print("{} ", tree.tokenSlice(visib_token)); } try stream.print("{}: ", tree.tokenSlice(field.name_token)); - try stack.push(RenderState { .Text = "," }); + try stack.push(RenderState { .Token = field.lastToken() + 1 }); try stack.push(RenderState { .Expression = field.type_expr}); }, ast.Node.Id.UnionTag => { @@ -129,9 +133,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.print("{}", tree.tokenSlice(tag.name_token)); }, ast.Node.Id.Comptime => { - if (decl.requireSemiColon()) { - try stack.push(RenderState { .Text = ";" }); - } + try stack.push(RenderState { .MaybeSemiColon = decl }); try stack.push(RenderState { .Expression = decl }); }, ast.Node.Id.LineComment => { @@ -143,7 +145,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { }, RenderState.VarDecl => |var_decl| { - try stack.push(RenderState { .Text = ";" }); + try stack.push(RenderState { .Token = var_decl.semicolon_token }); if (var_decl.init_node) |init_node| { try stack.push(RenderState { .Expression = init_node }); const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = "; @@ -895,7 +897,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { ast.Node.Id.SwitchCase => { const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); - try stack.push(RenderState { .Text = "," }); + try stack.push(RenderState { .Token = switch_case.lastToken() + 1 }); try stack.push(RenderState { .Expression = switch_case.expr }); if (switch_case.payload) |payload| { try stack.push(RenderState { .Text = " " }); @@ -1072,14 +1074,13 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { } try stack.push(RenderState { .Expression = if_node.body }); - try stack.push(RenderState { .Text = " " }); if (if_node.payload) |payload| { - try stack.push(RenderState { .Expression = payload }); try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); } - try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .NonBreakToken = if_node.condition.lastToken() + 1 }); try stack.push(RenderState { .Expression = if_node.condition }); try stack.push(RenderState { .Text = "(" }); }, @@ -1217,17 +1218,46 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stack.push(RenderState { .VarDecl = var_decl}); }, else => { - if (base.requireSemiColon()) { - try stack.push(RenderState { .Text = ";" }); - } + try stack.push(RenderState { .MaybeSemiColon = base }); try stack.push(RenderState { .Expression = base }); }, } }, RenderState.Indent => |new_indent| indent = new_indent, RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent), + RenderState.Token => |token_index| try renderToken(tree, stream, token_index, indent, true), + RenderState.NonBreakToken => |token_index| try renderToken(tree, stream, token_index, indent, false), + RenderState.MaybeSemiColon => |base| { + if (base.requireSemiColon()) { + const semicolon_index = base.lastToken() + 1; + assert(tree.tokens.at(semicolon_index).id == Token.Id.Semicolon); + try renderToken(tree, stream, semicolon_index, indent, true); + } + }, + } + } +} + +fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, line_break: bool) !void { + const token = tree.tokens.at(token_index); + try stream.write(tree.tokenSlicePtr(token)); + + const next_token = tree.tokens.at(token_index + 1); + if (next_token.id == Token.Id.LineComment) { + const loc = tree.tokenLocationPtr(token.end, next_token); + if (loc.line == 0) { + try stream.print(" {}", tree.tokenSlicePtr(next_token)); + if (!line_break) { + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent + indent_delta); + return; + } } } + + if (!line_break) { + try stream.writeByte(' '); + } } fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) !void { -- cgit v1.2.3 From 774b6ffe1e0577a2d1a32b04d71c86525627748a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 May 2018 21:17:05 -0400 Subject: fix parser performance regression --- std/zig/parse.zig | 655 ++++++++++++++++++++++++++-------------------------- std/zig/render.zig | 667 ++++++++++++++++++++++++++--------------------------- 2 files changed, 660 insertions(+), 662 deletions(-) diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 405c7b995a..c96893fd96 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -1,6 +1,5 @@ const std = @import("../index.zig"); const assert = std.debug.assert; -const SegmentedList = std.SegmentedList; const mem = std.mem; const ast = std.zig.ast; const Tokenizer = std.zig.Tokenizer; @@ -15,7 +14,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { var tree_arena = std.heap.ArenaAllocator.init(allocator); errdefer tree_arena.deinit(); - var stack = SegmentedList(State, 32).init(allocator); + var stack = std.ArrayList(State).init(allocator); defer stack.deinit(); const arena = &tree_arena.allocator; @@ -46,11 +45,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } var tok_it = tree.tokens.iterator(0); - try stack.push(State.TopLevel); + try stack.append(State.TopLevel); while (true) { // This gives us 1 free push that can't fail - const state = ??stack.pop(); + const state = stack.pop(); switch (state) { State.TopLevel => { @@ -65,7 +64,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_test => { - stack.push(State.TopLevel) catch unreachable; + stack.append(State.TopLevel) catch unreachable; const block = try arena.construct(ast.Node.Block { .base = ast.Node { @@ -86,14 +85,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .body_node = &block.base, }); try root_node.decls.push(&test_node.base); - try stack.push(State { .Block = block }); - try stack.push(State { + try stack.append(State { .Block = block }); + try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.LBrace, .ptr = &block.rbrace, } }); - try stack.push(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } }); + try stack.append(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } }); continue; }, Token.Id.Eof => { @@ -102,8 +101,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { return tree; }, Token.Id.Keyword_pub => { - stack.push(State.TopLevel) catch unreachable; - try stack.push(State { + stack.append(State.TopLevel) catch unreachable; + try stack.append(State { .TopLevelExtern = TopLevelDeclCtx { .decls = &root_node.decls, .visib_token = token_index, @@ -134,9 +133,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try root_node.decls.push(&node.base); - stack.push(State.TopLevel) catch unreachable; - try stack.push(State { .Block = block }); - try stack.push(State { + stack.append(State.TopLevel) catch unreachable; + try stack.append(State { .Block = block }); + try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.LBrace, .ptr = &block.rbrace, @@ -146,8 +145,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, else => { putBackToken(&tok_it, &tree); - stack.push(State.TopLevel) catch unreachable; - try stack.push(State { + stack.append(State.TopLevel) catch unreachable; + try stack.append(State { .TopLevelExtern = TopLevelDeclCtx { .decls = &root_node.decls, .visib_token = null, @@ -166,7 +165,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_export, Token.Id.Keyword_inline => { - stack.push(State { + stack.append(State { .TopLevelDecl = TopLevelDeclCtx { .decls = ctx.decls, .visib_token = ctx.visib_token, @@ -181,7 +180,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_extern => { - stack.push(State { + stack.append(State { .TopLevelLibname = TopLevelDeclCtx { .decls = ctx.decls, .visib_token = ctx.visib_token, @@ -197,7 +196,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, else => { putBackToken(&tok_it, &tree); - stack.push(State { .TopLevelDecl = ctx }) catch unreachable; + stack.append(State { .TopLevelDecl = ctx }) catch unreachable; continue; } } @@ -213,7 +212,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }; }; - stack.push(State { + stack.append(State { .TopLevelDecl = TopLevelDeclCtx { .decls = ctx.decls, .visib_token = ctx.visib_token, @@ -246,13 +245,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try ctx.decls.push(&node.base); - stack.push(State { + stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Semicolon, .ptr = &node.semicolon_token, } }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); continue; }, Token.Id.Keyword_var, Token.Id.Keyword_const => { @@ -265,7 +264,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } } - try stack.push(State { + try stack.append(State { .VarDecl = VarDeclCtx { .comments = ctx.comments, .visib_token = ctx.visib_token, @@ -299,13 +298,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .align_expr = null, }); try ctx.decls.push(&fn_proto.base); - stack.push(State { .FnDef = fn_proto }) catch unreachable; - try stack.push(State { .FnProto = fn_proto }); + stack.append(State { .FnDef = fn_proto }) catch unreachable; + try stack.append(State { .FnProto = fn_proto }); switch (token_ptr.id) { Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { fn_proto.cc_token = token_index; - try stack.push(State { + try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Keyword_fn, .ptr = &fn_proto.fn_token, @@ -324,13 +323,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { ); fn_proto.async_attr = async_node; - try stack.push(State { + try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Keyword_fn, .ptr = &fn_proto.fn_token, } }); - try stack.push(State { .AsyncAllocator = async_node }); + try stack.append(State { .AsyncAllocator = async_node }); continue; }, Token.Id.Keyword_fn => { @@ -363,14 +362,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); *node_ptr = &node.base; - stack.push(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.type_expr } }); - try stack.push(State { .ExpectToken = Token.Id.Colon }); + stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } }); + try stack.append(State { .ExpectToken = Token.Id.Colon }); continue; } - stack.push(State{ .ContainerDecl = ctx.container_decl }) catch unreachable; - try stack.push(State { + stack.append(State{ .ContainerDecl = ctx.container_decl }) catch unreachable; + try stack.append(State { .TopLevelExtern = TopLevelDeclCtx { .decls = &ctx.container_decl.fields_and_decls, .visib_token = ctx.visib_token, @@ -390,7 +389,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { putBackToken(&tok_it, &tree); continue; } - stack.push(State { .Expression = ctx }) catch unreachable; + stack.append(State { .Expression = ctx }) catch unreachable; continue; }, @@ -420,9 +419,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } ); - stack.push(State { .ContainerDecl = node }) catch unreachable; - try stack.push(State { .ExpectToken = Token.Id.LBrace }); - try stack.push(State { .ContainerInitArgStart = node }); + stack.append(State { .ContainerDecl = node }) catch unreachable; + try stack.append(State { .ExpectToken = Token.Id.LBrace }); + try stack.append(State { .ContainerInitArgStart = node }); continue; }, @@ -431,8 +430,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - try stack.push(State { .ContainerInitArg = container_decl }); + stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + try stack.append(State { .ContainerInitArg = container_decl }); continue; }, @@ -447,8 +446,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lparen_tok_index = lparen_tok.index; const lparen_tok_ptr = lparen_tok.ptr; if (lparen_tok_ptr.id == Token.Id.LParen) { - try stack.push(State { .ExpectToken = Token.Id.RParen } ); - try stack.push(State { .Expression = OptionalCtx { + try stack.append(State { .ExpectToken = Token.Id.RParen } ); + try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &container_decl.init_arg_expr.Enum, } }); } else { @@ -458,7 +457,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { else => { putBackToken(&tok_it, &tree); container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined }; - stack.push(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; + stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; }, } continue; @@ -489,9 +488,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const node_ptr = try container_decl.fields_and_decls.addOne(); *node_ptr = &node.base; - try stack.push(State { .FieldListCommaOrEnd = container_decl }); - try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } }); - try stack.push(State { .ExpectToken = Token.Id.Colon }); + try stack.append(State { .FieldListCommaOrEnd = container_decl }); + try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } }); + try stack.append(State { .ExpectToken = Token.Id.Colon }); continue; }, ast.Node.ContainerDecl.Kind.Union => { @@ -504,10 +503,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try container_decl.fields_and_decls.push(&node.base); - stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - try stack.push(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } }); - try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } }); - try stack.push(State { .IfToken = Token.Id.Colon }); + stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; + try stack.append(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } }); + try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } }); + try stack.append(State { .IfToken = Token.Id.Colon }); continue; }, ast.Node.ContainerDecl.Kind.Enum => { @@ -519,9 +518,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try container_decl.fields_and_decls.push(&node.base); - stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &node.value } }); - try stack.push(State { .IfToken = Token.Id.Equal }); + stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &node.value } }); + try stack.append(State { .IfToken = Token.Id.Equal }); continue; }, } @@ -529,7 +528,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Keyword_pub => { switch (container_decl.kind) { ast.Node.ContainerDecl.Kind.Struct => { - try stack.push(State { + try stack.append(State { .TopLevelExternOrField = TopLevelExternOrFieldCtx { .visib_token = token_index, .container_decl = container_decl, @@ -539,8 +538,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, else => { - stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.push(State { + stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.append(State { .TopLevelExtern = TopLevelDeclCtx { .decls = &container_decl.fields_and_decls, .visib_token = token_index, @@ -554,8 +553,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, Token.Id.Keyword_export => { - stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.push(State { + stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.append(State { .TopLevelExtern = TopLevelDeclCtx { .decls = &container_decl.fields_and_decls, .visib_token = token_index, @@ -578,8 +577,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, else => { putBackToken(&tok_it, &tree); - stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.push(State { + stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.append(State { .TopLevelExtern = TopLevelDeclCtx { .decls = &container_decl.fields_and_decls, .visib_token = null, @@ -615,10 +614,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try ctx.list.push(&var_decl.base); - try stack.push(State { .VarDeclAlign = var_decl }); - try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); - try stack.push(State { .IfToken = Token.Id.Colon }); - try stack.push(State { + try stack.append(State { .VarDeclAlign = var_decl }); + try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); + try stack.append(State { .IfToken = Token.Id.Colon }); + try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Identifier, .ptr = &var_decl.name_token, @@ -627,15 +626,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.VarDeclAlign => |var_decl| { - try stack.push(State { .VarDeclEq = var_decl }); + try stack.append(State { .VarDeclEq = var_decl }); const next_token = nextToken(&tok_it, &tree); const next_token_index = next_token.index; const next_token_ptr = next_token.ptr; if (next_token_ptr.id == Token.Id.Keyword_align) { - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); continue; } @@ -649,13 +648,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { switch (token_ptr.id) { Token.Id.Equal => { var_decl.eq_token = token_index; - stack.push(State { + stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Semicolon, .ptr = &var_decl.semicolon_token, }, }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } }); + try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } }); continue; }, Token.Id.Semicolon => { @@ -686,7 +685,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rbrace = undefined, }); fn_proto.body_node = &block.base; - stack.push(State { .Block = block }) catch unreachable; + stack.append(State { .Block = block }) catch unreachable; continue; }, Token.Id.Semicolon => continue, @@ -699,9 +698,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.FnProto => |fn_proto| { - stack.push(State { .FnProtoAlign = fn_proto }) catch unreachable; - try stack.push(State { .ParamDecl = fn_proto }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); + stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable; + try stack.append(State { .ParamDecl = fn_proto }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |name_token| { fn_proto.name_token = name_token; @@ -709,12 +708,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.FnProtoAlign => |fn_proto| { - stack.push(State { .FnProtoReturnType = fn_proto }) catch unreachable; + stack.append(State { .FnProtoReturnType = fn_proto }) catch unreachable; if (eatToken(&tok_it, &tree, Token.Id.Keyword_align)) |align_token| { - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); } continue; }, @@ -725,7 +724,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { switch (token_ptr.id) { Token.Id.Bang => { fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined }; - stack.push(State { + stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet }, }) catch unreachable; continue; @@ -747,7 +746,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { putBackToken(&tok_it, &tree); fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined }; - stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; + stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; continue; }, } @@ -768,14 +767,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try fn_proto.params.push(¶m_decl.base); - stack.push(State { + stack.append(State { .ParamDeclEnd = ParamDeclEndCtx { .param_decl = param_decl, .fn_proto = fn_proto, } }) catch unreachable; - try stack.push(State { .ParamDeclName = param_decl }); - try stack.push(State { .ParamDeclAliasOrComptime = param_decl }); + try stack.append(State { .ParamDeclName = param_decl }); + try stack.append(State { .ParamDeclAliasOrComptime = param_decl }); continue; }, State.ParamDeclAliasOrComptime => |param_decl| { @@ -801,12 +800,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.ParamDeclEnd => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| { ctx.param_decl.var_args_token = ellipsis3; - stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; continue; } - try stack.push(State { .ParamDeclComma = ctx.fn_proto }); - try stack.push(State { + try stack.append(State { .ParamDeclComma = ctx.fn_proto }); + try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node } }); continue; @@ -815,7 +814,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) { ExpectCommaOrEndResult.end_token => |t| { if (t == null) { - stack.push(State { .ParamDecl = fn_proto }) catch unreachable; + stack.append(State { .ParamDecl = fn_proto }) catch unreachable; } continue; }, @@ -828,7 +827,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.MaybeLabeledExpression => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Colon)) |_| { - stack.push(State { + stack.append(State { .LabeledExpression = LabelCtx { .label = ctx.label, .opt_ctx = ctx.opt_ctx, @@ -855,11 +854,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rbrace = undefined, } ); - stack.push(State { .Block = block }) catch unreachable; + stack.append(State { .Block = block }) catch unreachable; continue; }, Token.Id.Keyword_while => { - stack.push(State { + stack.append(State { .While = LoopCtx { .label = ctx.label, .inline_token = null, @@ -870,7 +869,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_for => { - stack.push(State { + stack.append(State { .For = LoopCtx { .label = ctx.label, .inline_token = null, @@ -891,12 +890,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .body = null, }); ctx.opt_ctx.store(&node.base); - stack.push(State { .SuspendBody = node }) catch unreachable; - try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + stack.append(State { .SuspendBody = node }) catch unreachable; + try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); continue; }, Token.Id.Keyword_inline => { - stack.push(State { + stack.append(State { .Inline = InlineCtx { .label = ctx.label, .inline_token = token_index, @@ -924,7 +923,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_while => { - stack.push(State { + stack.append(State { .While = LoopCtx { .inline_token = ctx.inline_token, .label = ctx.label, @@ -935,7 +934,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_for => { - stack.push(State { + stack.append(State { .For = LoopCtx { .inline_token = ctx.inline_token, .label = ctx.label, @@ -972,20 +971,20 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .@"else" = null, } ); - stack.push(State { .Else = &node.@"else" }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.push(State { .WhileContinueExpr = &node.continue_expr }); - try stack.push(State { .IfToken = Token.Id.Colon }); - try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); + stack.append(State { .Else = &node.@"else" }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.append(State { .WhileContinueExpr = &node.continue_expr }); + try stack.append(State { .IfToken = Token.Id.Colon }); + try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); continue; }, State.WhileContinueExpr => |dest| { - stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); + stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); continue; }, State.For => |ctx| { @@ -1001,12 +1000,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .@"else" = null, } ); - stack.push(State { .Else = &node.@"else" }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.push(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.array_expr } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); + stack.append(State { .Else = &node.@"else" }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.append(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.array_expr } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); continue; }, State.Else => |dest| { @@ -1021,8 +1020,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { ); *dest = node; - stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable; - try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable; + try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); continue; } else { continue; @@ -1041,7 +1040,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, else => { putBackToken(&tok_it, &tree); - stack.push(State { .Block = block }) catch unreachable; + stack.append(State { .Block = block }) catch unreachable; var any_comments = false; while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| { @@ -1050,7 +1049,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } if (any_comments) continue; - try stack.push(State { .Statement = block }); + try stack.append(State { .Statement = block }); continue; }, } @@ -1061,7 +1060,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_comptime => { - stack.push(State { + stack.append(State { .ComptimeStatement = ComptimeStatementCtx { .comptime_token = token_index, .block = block, @@ -1070,7 +1069,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_var, Token.Id.Keyword_const => { - stack.push(State { + stack.append(State { .VarDecl = VarDeclCtx { .comments = null, .visib_token = null, @@ -1099,8 +1098,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const node_ptr = try block.statements.addOne(); *node_ptr = &node.base; - stack.push(State { .Semicolon = node_ptr }) catch unreachable; - try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); + stack.append(State { .Semicolon = node_ptr }) catch unreachable; + try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); continue; }, Token.Id.LBrace => { @@ -1113,14 +1112,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try block.statements.push(&inner_block.base); - stack.push(State { .Block = inner_block }) catch unreachable; + stack.append(State { .Block = inner_block }) catch unreachable; continue; }, else => { putBackToken(&tok_it, &tree); const statement = try block.statements.addOne(); - try stack.push(State { .Semicolon = statement }); - try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); + try stack.append(State { .Semicolon = statement }); + try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); continue; } } @@ -1131,7 +1130,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_var, Token.Id.Keyword_const => { - stack.push(State { + stack.append(State { .VarDecl = VarDeclCtx { .comments = null, .visib_token = null, @@ -1148,8 +1147,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { putBackToken(&tok_it, &tree); putBackToken(&tok_it, &tree); const statement = try ctx.block.statements.addOne(); - try stack.push(State { .Semicolon = statement }); - try stack.push(State { .Expression = OptionalCtx { .Required = statement } }); + try stack.append(State { .Semicolon = statement }); + try stack.append(State { .Expression = OptionalCtx { .Required = statement } }); continue; } } @@ -1157,7 +1156,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.Semicolon => |node_ptr| { const node = *node_ptr; if (node.requireSemiColon()) { - stack.push(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; + stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; continue; } continue; @@ -1182,14 +1181,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { ); try items.push(node); - stack.push(State { .AsmOutputItems = items }) catch unreachable; - try stack.push(State { .IfToken = Token.Id.Comma }); - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .AsmOutputReturnOrType = node }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); - try stack.push(State { .ExpectToken = Token.Id.RBracket }); - try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + stack.append(State { .AsmOutputItems = items }) catch unreachable; + try stack.append(State { .IfToken = Token.Id.Comma }); + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .AsmOutputReturnOrType = node }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); + try stack.append(State { .ExpectToken = Token.Id.RBracket }); + try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); continue; }, State.AsmOutputReturnOrType => |node| { @@ -1203,7 +1202,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, Token.Id.Arrow => { node.kind = ast.Node.AsmOutput.Kind { .Return = undefined }; - try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } }); + try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } }); continue; }, else => { @@ -1235,20 +1234,20 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { ); try items.push(node); - stack.push(State { .AsmInputItems = items }) catch unreachable; - try stack.push(State { .IfToken = Token.Id.Comma }); - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); - try stack.push(State { .ExpectToken = Token.Id.RBracket }); - try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + stack.append(State { .AsmInputItems = items }) catch unreachable; + try stack.append(State { .IfToken = Token.Id.Comma }); + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); + try stack.append(State { .ExpectToken = Token.Id.RBracket }); + try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); continue; }, State.AsmClobberItems => |items| { - stack.push(State { .AsmClobberItems = items }) catch unreachable; - try stack.push(State { .IfToken = Token.Id.Comma }); - try stack.push(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } }); + stack.append(State { .AsmClobberItems = items }) catch unreachable; + try stack.append(State { .IfToken = Token.Id.Comma }); + try stack.append(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } }); continue; }, @@ -1259,8 +1258,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - stack.push(State { .ExprListCommaOrEnd = list_state }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } }); + stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } }); continue; }, State.ExprListCommaOrEnd => |list_state| { @@ -1269,7 +1268,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { *list_state.ptr = end; continue; } else { - stack.push(State { .ExprListItemOrEnd = list_state }) catch unreachable; + stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable; continue; }, ExpectCommaOrEndResult.parse_error => |e| { @@ -1298,16 +1297,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try list_state.list.push(&node.base); - stack.push(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx{ .Required = &node.expr } }); - try stack.push(State { .ExpectToken = Token.Id.Equal }); - try stack.push(State { + stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } }); + try stack.append(State { .ExpectToken = Token.Id.Equal }); + try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Identifier, .ptr = &node.name_token, } }); - try stack.push(State { + try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Period, .ptr = &node.period_token, @@ -1321,7 +1320,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { *list_state.ptr = end; continue; } else { - stack.push(State { .FieldInitListItemOrEnd = list_state }) catch unreachable; + stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable; continue; }, ExpectCommaOrEndResult.parse_error => |e| { @@ -1336,7 +1335,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { container_decl.rbrace_token = end; continue; } else { - try stack.push(State { .ContainerDecl = container_decl }); + try stack.append(State { .ContainerDecl = container_decl }); continue; }, ExpectCommaOrEndResult.parse_error => |e| { @@ -1357,8 +1356,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const node_ptr = try list_state.list.addOne(); - try stack.push(State { .ErrorTagListCommaOrEnd = list_state }); - try stack.push(State { .ErrorTag = node_ptr }); + try stack.append(State { .ErrorTagListCommaOrEnd = list_state }); + try stack.append(State { .ErrorTag = node_ptr }); continue; }, State.ErrorTagListCommaOrEnd => |list_state| { @@ -1367,7 +1366,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { *list_state.ptr = end; continue; } else { - stack.push(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable; + stack.append(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable; continue; }, ExpectCommaOrEndResult.parse_error => |e| { @@ -1396,10 +1395,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .expr = undefined, }); try list_state.list.push(&node.base); - try stack.push(State { .SwitchCaseCommaOrEnd = list_state }); - try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); - try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.push(State { .SwitchCaseFirstItem = &node.items }); + try stack.append(State { .SwitchCaseCommaOrEnd = list_state }); + try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); + try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.append(State { .SwitchCaseFirstItem = &node.items }); continue; }, @@ -1410,7 +1409,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { *list_state.ptr = end; continue; } else { - try stack.push(State { .SwitchCaseOrEnd = list_state }); + try stack.append(State { .SwitchCaseOrEnd = list_state }); continue; }, ExpectCommaOrEndResult.parse_error => |e| { @@ -1431,23 +1430,23 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try case_items.push(&else_node.base); - try stack.push(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); + try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); continue; } else { putBackToken(&tok_it, &tree); - try stack.push(State { .SwitchCaseItem = case_items }); + try stack.append(State { .SwitchCaseItem = case_items }); continue; } }, State.SwitchCaseItem => |case_items| { - stack.push(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; - try stack.push(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } }); + stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; + try stack.append(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } }); }, State.SwitchCaseItemCommaOrEnd => |case_items| { switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.EqualAngleBracketRight)) { ExpectCommaOrEndResult.end_token => |t| { if (t == null) { - stack.push(State { .SwitchCaseItem = case_items }) catch unreachable; + stack.append(State { .SwitchCaseItem = case_items }) catch unreachable; } continue; }, @@ -1462,7 +1461,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.SuspendBody => |suspend_node| { if (suspend_node.payload != null) { - try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } }); + try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } }); } continue; }, @@ -1472,13 +1471,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } async_node.rangle_bracket = TokenIndex(0); - try stack.push(State { + try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.AngleBracketRight, .ptr = &??async_node.rangle_bracket, } }); - try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } }); + try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } }); continue; }, State.AsyncEnd => |ctx| { @@ -1533,11 +1532,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .align_expr = null, }); ctx.opt_ctx.store(&fn_proto.base); - stack.push(State { .FnProto = fn_proto }) catch unreachable; + stack.append(State { .FnProto = fn_proto }) catch unreachable; continue; } - stack.push(State { + stack.append(State { .ContainerKind = ContainerKindCtx { .opt_ctx = ctx.opt_ctx, .ltoken = ctx.extern_token, @@ -1560,13 +1559,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }; - stack.push(State { + stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.RBracket, .ptr = &node.rtoken, } }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } }); + try stack.append(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } }); continue; }, Token.Id.RBracket => { @@ -1592,15 +1591,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .volatile_token = null, } }; - stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - try stack.push(State { .AddrOfModifiers = &node.op.SliceType }); + stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + try stack.append(State { .AddrOfModifiers = &node.op.SliceType }); continue; } node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined }; - stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - try stack.push(State { .ExpectToken = Token.Id.RBracket }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } }); + stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + try stack.append(State { .ExpectToken = Token.Id.RBracket }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } }); continue; }, State.AddrOfModifiers => |addr_of_info| { @@ -1609,20 +1608,20 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_align => { - stack.push(state) catch unreachable; + stack.append(state) catch unreachable; if (addr_of_info.align_expr != null) { *(try tree.errors.addOne()) = Error { .ExtraAlignQualifier = Error.ExtraAlignQualifier { .token = token_index }, }; return tree; } - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); continue; }, Token.Id.Keyword_const => { - stack.push(state) catch unreachable; + stack.append(state) catch unreachable; if (addr_of_info.const_token != null) { *(try tree.errors.addOne()) = Error { .ExtraConstQualifier = Error.ExtraConstQualifier { .token = token_index }, @@ -1633,7 +1632,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_volatile => { - stack.push(state) catch unreachable; + stack.append(state) catch unreachable; if (addr_of_info.volatile_token != null) { *(try tree.errors.addOne()) = Error { .ExtraVolatileQualifier = Error.ExtraVolatileQualifier { .token = token_index }, @@ -1679,13 +1678,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } ); - stack.push(State { + stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Pipe, .ptr = &node.rpipe, } }) catch unreachable; - try stack.push(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } }); + try stack.append(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } }); continue; }, State.PointerPayload => |opt_ctx| { @@ -1717,14 +1716,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } ); - try stack.push(State { + try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Pipe, .ptr = &node.rpipe, } }); - try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); - try stack.push(State { + try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); + try stack.append(State { .OptionalTokenSave = OptionalTokenSave { .id = Token.Id.Asterisk, .ptr = &node.ptr_token, @@ -1762,16 +1761,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } ); - stack.push(State { + stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Pipe, .ptr = &node.rpipe, } }) catch unreachable; - try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } }); - try stack.push(State { .IfToken = Token.Id.Comma }); - try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); - try stack.push(State { + try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } }); + try stack.append(State { .IfToken = Token.Id.Comma }); + try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); + try stack.append(State { .OptionalTokenSave = OptionalTokenSave { .id = Token.Id.Asterisk, .ptr = &node.ptr_token, @@ -1796,18 +1795,18 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } ); - stack.push(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable; + stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable; switch (token_ptr.id) { Token.Id.Keyword_break => { node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null }; - try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } }); - try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } }); + try stack.append(State { .IfToken = Token.Id.Colon }); }, Token.Id.Keyword_continue => { node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null }; - try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } }); - try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } }); + try stack.append(State { .IfToken = Token.Id.Colon }); }, Token.Id.Keyword_return => { node.kind = ast.Node.ControlFlowExpression.Kind.Return; @@ -1831,21 +1830,21 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } ); - stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; continue; }, else => { if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) { putBackToken(&tok_it, &tree); - stack.push(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable; + stack.append(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable; } continue; } } }, State.RangeExpressionBegin => |opt_ctx| { - stack.push(State { .RangeExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .Expression = opt_ctx }); + stack.append(State { .RangeExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .Expression = opt_ctx }); continue; }, State.RangeExpressionEnd => |opt_ctx| { @@ -1861,13 +1860,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; continue; } }, State.AssignmentExpressionBegin => |opt_ctx| { - stack.push(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .Expression = opt_ctx }); + stack.append(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .Expression = opt_ctx }); continue; }, @@ -1887,8 +1886,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); continue; } else { putBackToken(&tok_it, &tree); @@ -1897,8 +1896,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.UnwrapExpressionBegin => |opt_ctx| { - stack.push(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .BoolOrExpressionBegin = opt_ctx }); + stack.append(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .BoolOrExpressionBegin = opt_ctx }); continue; }, @@ -1919,11 +1918,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } ); - stack.push(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); if (node.op == ast.Node.InfixOp.Op.Catch) { - try stack.push(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } }); + try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } }); } continue; } else { @@ -1933,8 +1932,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.BoolOrExpressionBegin => |opt_ctx| { - stack.push(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .BoolAndExpressionBegin = opt_ctx }); + stack.append(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .BoolAndExpressionBegin = opt_ctx }); continue; }, @@ -1951,15 +1950,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } }, State.BoolAndExpressionBegin => |opt_ctx| { - stack.push(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .ComparisonExpressionBegin = opt_ctx }); + stack.append(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .ComparisonExpressionBegin = opt_ctx }); continue; }, @@ -1976,15 +1975,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } }, State.ComparisonExpressionBegin => |opt_ctx| { - stack.push(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .BinaryOrExpressionBegin = opt_ctx }); + stack.append(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .BinaryOrExpressionBegin = opt_ctx }); continue; }, @@ -2004,8 +2003,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } else { putBackToken(&tok_it, &tree); @@ -2014,8 +2013,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.BinaryOrExpressionBegin => |opt_ctx| { - stack.push(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .BinaryXorExpressionBegin = opt_ctx }); + stack.append(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .BinaryXorExpressionBegin = opt_ctx }); continue; }, @@ -2032,15 +2031,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } }, State.BinaryXorExpressionBegin => |opt_ctx| { - stack.push(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .BinaryAndExpressionBegin = opt_ctx }); + stack.append(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .BinaryAndExpressionBegin = opt_ctx }); continue; }, @@ -2057,15 +2056,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } }, State.BinaryAndExpressionBegin => |opt_ctx| { - stack.push(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .BitShiftExpressionBegin = opt_ctx }); + stack.append(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .BitShiftExpressionBegin = opt_ctx }); continue; }, @@ -2082,15 +2081,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } }, State.BitShiftExpressionBegin => |opt_ctx| { - stack.push(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .AdditionExpressionBegin = opt_ctx }); + stack.append(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .AdditionExpressionBegin = opt_ctx }); continue; }, @@ -2110,8 +2109,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } else { putBackToken(&tok_it, &tree); @@ -2120,8 +2119,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.AdditionExpressionBegin => |opt_ctx| { - stack.push(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .MultiplyExpressionBegin = opt_ctx }); + stack.append(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .MultiplyExpressionBegin = opt_ctx }); continue; }, @@ -2141,8 +2140,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } else { putBackToken(&tok_it, &tree); @@ -2151,8 +2150,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.MultiplyExpressionBegin => |opt_ctx| { - stack.push(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .CurlySuffixExpressionBegin = opt_ctx }); + stack.append(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .CurlySuffixExpressionBegin = opt_ctx }); continue; }, @@ -2172,8 +2171,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; } else { putBackToken(&tok_it, &tree); @@ -2182,9 +2181,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.CurlySuffixExpressionBegin => |opt_ctx| { - stack.push(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .IfToken = Token.Id.LBrace }); - try stack.push(State { .TypeExprBegin = opt_ctx }); + stack.append(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .IfToken = Token.Id.LBrace }); + try stack.append(State { .TypeExprBegin = opt_ctx }); continue; }, @@ -2202,9 +2201,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); opt_ctx.store(&node.base); - stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .IfToken = Token.Id.LBrace }); - try stack.push(State { + stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .IfToken = Token.Id.LBrace }); + try stack.append(State { .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)) { .list = &node.op.StructInitializer, .ptr = &node.rtoken, @@ -2223,9 +2222,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rtoken = undefined, } ); - stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .IfToken = Token.Id.LBrace }); - try stack.push(State { + stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .IfToken = Token.Id.LBrace }); + try stack.append(State { .ExprListItemOrEnd = ExprListCtx { .list = &node.op.ArrayInitializer, .end = Token.Id.RBrace, @@ -2236,8 +2235,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.TypeExprBegin => |opt_ctx| { - stack.push(State { .TypeExprEnd = opt_ctx }) catch unreachable; - try stack.push(State { .PrefixOpExpression = opt_ctx }); + stack.append(State { .TypeExprEnd = opt_ctx }) catch unreachable; + try stack.append(State { .PrefixOpExpression = opt_ctx }); continue; }, @@ -2254,8 +2253,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } }); continue; } }, @@ -2288,14 +2287,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { node = child; } - stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; if (node.op == ast.Node.PrefixOp.Op.AddrOf) { - try stack.push(State { .AddrOfModifiers = &node.op.AddrOf }); + try stack.append(State { .AddrOfModifiers = &node.op.AddrOf }); } continue; } else { putBackToken(&tok_it, &tree); - stack.push(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable; + stack.append(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable; continue; } }, @@ -2310,20 +2309,20 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rangle_bracket = null, } ); - stack.push(State { + stack.append(State { .AsyncEnd = AsyncEndCtx { .ctx = opt_ctx, .attribute = async_node, } }) catch unreachable; - try stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }); - try stack.push(State { .PrimaryExpression = opt_ctx.toRequired() }); - try stack.push(State { .AsyncAllocator = async_node }); + try stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }); + try stack.append(State { .PrimaryExpression = opt_ctx.toRequired() }); + try stack.append(State { .AsyncAllocator = async_node }); continue; } - stack.push(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable; - try stack.push(State { .PrimaryExpression = opt_ctx }); + stack.append(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State { .PrimaryExpression = opt_ctx }); continue; }, @@ -2348,8 +2347,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rtoken = undefined, } ); - stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { + stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .ExprListItemOrEnd = ExprListCtx { .list = &node.op.Call.params, .end = Token.Id.RParen, @@ -2369,9 +2368,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rtoken = undefined } ); - stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .SliceOrArrayAccess = node }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }}); + stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .SliceOrArrayAccess = node }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }}); continue; }, Token.Id.Period => { @@ -2384,8 +2383,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.push(State { .Identifier = OptionalCtx { .Required = &node.rhs } }); + stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State { .Identifier = OptionalCtx { .Required = &node.rhs } }); continue; }, else => { @@ -2455,7 +2454,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .return_type = undefined, }; const return_type_ptr = &((??node.result).return_type); - try stack.push(State { .Expression = OptionalCtx { .Required = return_type_ptr, } }); + try stack.append(State { .Expression = OptionalCtx { .Required = return_type_ptr, } }); continue; }, Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { @@ -2471,13 +2470,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rparen = undefined, } ); - stack.push(State { + stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.RParen, .ptr = &node.rparen, } }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); continue; }, Token.Id.Builtin => { @@ -2489,14 +2488,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rparen_token = undefined, } ); - stack.push(State { + stack.append(State { .ExprListItemOrEnd = ExprListCtx { .list = &node.params, .end = Token.Id.RParen, .ptr = &node.rparen_token, } }) catch unreachable; - try stack.push(State { .ExpectToken = Token.Id.LParen, }); + try stack.append(State { .ExpectToken = Token.Id.LParen, }); continue; }, Token.Id.LBracket => { @@ -2508,11 +2507,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rhs = undefined, } ); - stack.push(State { .SliceOrArrayType = node }) catch unreachable; + stack.append(State { .SliceOrArrayType = node }) catch unreachable; continue; }, Token.Id.Keyword_error => { - stack.push(State { + stack.append(State { .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx { .error_token = token.index, .opt_ctx = opt_ctx @@ -2521,7 +2520,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_packed => { - stack.push(State { + stack.append(State { .ContainerKind = ContainerKindCtx { .opt_ctx = opt_ctx, .ltoken = token.index, @@ -2531,7 +2530,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_extern => { - stack.push(State { + stack.append(State { .ExternType = ExternTypeCtx { .opt_ctx = opt_ctx, .extern_token = token.index, @@ -2542,7 +2541,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => { putBackToken(&tok_it, &tree); - stack.push(State { + stack.append(State { .ContainerKind = ContainerKindCtx { .opt_ctx = opt_ctx, .ltoken = token.index, @@ -2552,7 +2551,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Identifier => { - stack.push(State { + stack.append(State { .MaybeLabeledExpression = MaybeLabeledExpressionCtx { .label = token.index, .opt_ctx = opt_ctx @@ -2580,7 +2579,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .align_expr = null, }); opt_ctx.store(&fn_proto.base); - stack.push(State { .FnProto = fn_proto }) catch unreachable; + stack.append(State { .FnProto = fn_proto }) catch unreachable; continue; }, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { @@ -2603,8 +2602,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .align_expr = null, }); opt_ctx.store(&fn_proto.base); - stack.push(State { .FnProto = fn_proto }) catch unreachable; - try stack.push(State { + stack.append(State { .FnProto = fn_proto }) catch unreachable; + try stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.Keyword_fn, .ptr = &fn_proto.fn_token @@ -2625,21 +2624,21 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .rparen = undefined, } ); - stack.push(State { + stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.RParen, .ptr = &node.rparen, } }) catch unreachable; - try stack.push(State { .AsmClobberItems = &node.clobbers }); - try stack.push(State { .IfToken = Token.Id.Colon }); - try stack.push(State { .AsmInputItems = &node.inputs }); - try stack.push(State { .IfToken = Token.Id.Colon }); - try stack.push(State { .AsmOutputItems = &node.outputs }); - try stack.push(State { .IfToken = Token.Id.Colon }); - try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.template } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); - try stack.push(State { + try stack.append(State { .AsmClobberItems = &node.clobbers }); + try stack.append(State { .IfToken = Token.Id.Colon }); + try stack.append(State { .AsmInputItems = &node.inputs }); + try stack.append(State { .IfToken = Token.Id.Colon }); + try stack.append(State { .AsmOutputItems = &node.outputs }); + try stack.append(State { .IfToken = Token.Id.Colon }); + try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.template } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State { .OptionalTokenSave = OptionalTokenSave { .id = Token.Id.Keyword_volatile, .ptr = &node.volatile_token, @@ -2647,7 +2646,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); }, Token.Id.Keyword_inline => { - stack.push(State { + stack.append(State { .Inline = InlineCtx { .label = null, .inline_token = token.index, @@ -2688,7 +2687,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); ctx.opt_ctx.store(&node.base); - stack.push(State { + stack.append(State { .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)) { .list = &node.decls, .ptr = &node.rbrace_token, @@ -3153,7 +3152,7 @@ fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterato } } -fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: &const OptionalCtx, +fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx, token_ptr: &const Token, token_index: TokenIndex) !bool { switch (token_ptr.id) { Token.Id.Keyword_suspend => { @@ -3167,8 +3166,8 @@ fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: } ); - stack.push(State { .SuspendBody = node }) catch unreachable; - try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + stack.append(State { .SuspendBody = node }) catch unreachable; + try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); return true; }, Token.Id.Keyword_if => { @@ -3183,16 +3182,16 @@ fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: } ); - stack.push(State { .Else = &node.@"else" }) catch unreachable; - try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); + stack.append(State { .Else = &node.@"else" }) catch unreachable; + try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); return true; }, Token.Id.Keyword_while => { - stack.push(State { + stack.append(State { .While = LoopCtx { .label = null, .inline_token = null, @@ -3203,7 +3202,7 @@ fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: return true; }, Token.Id.Keyword_for => { - stack.push(State { + stack.append(State { .For = LoopCtx { .label = null, .inline_token = null, @@ -3225,16 +3224,16 @@ fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: }); ctx.store(&node.base); - stack.push(State { + stack.append(State { .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)) { .list = &node.cases, .ptr = &node.rbrace, }, }) catch unreachable; - try stack.push(State { .ExpectToken = Token.Id.LBrace }); - try stack.push(State { .ExpectToken = Token.Id.RParen }); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); - try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State { .ExpectToken = Token.Id.LBrace }); + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); return true; }, Token.Id.Keyword_comptime => { @@ -3246,7 +3245,7 @@ fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: .doc_comments = null, } ); - try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); return true; }, Token.Id.LBrace => { @@ -3258,7 +3257,7 @@ fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: .rbrace = undefined, }); ctx.store(&block.base); - stack.push(State { .Block = block }) catch unreachable; + stack.append(State { .Block = block }) catch unreachable; return true; }, else => { @@ -3473,10 +3472,10 @@ const RenderAstFrame = struct { }; pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) !void { - var stack = SegmentedList(State, 32).init(allocator); + var stack = std.ArrayList(State).init(allocator); defer stack.deinit(); - try stack.push(RenderAstFrame { + try stack.append(RenderAstFrame { .node = &root_node.base, .indent = 0, }); @@ -3491,7 +3490,7 @@ pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) try stream.print("{}\n", @tagName(frame.node.id)); var child_i: usize = 0; while (frame.node.iterate(child_i)) |child| : (child_i += 1) { - try stack.push(RenderAstFrame { + try stack.append(RenderAstFrame { .node = child, .indent = frame.indent + 2, }); diff --git a/std/zig/render.zig b/std/zig/render.zig index 00a5613765..cced30cd60 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -1,6 +1,5 @@ const std = @import("../index.zig"); const assert = std.debug.assert; -const SegmentedList = std.SegmentedList; const mem = std.mem; const ast = std.zig.ast; const Token = std.zig.Token; @@ -22,19 +21,19 @@ const RenderState = union(enum) { const indent_delta = 4; pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { - var stack = SegmentedList(RenderState, 32).init(allocator); + var stack = std.ArrayList(RenderState).init(allocator); defer stack.deinit(); { - try stack.push(RenderState { .Text = "\n"}); + try stack.append(RenderState { .Text = "\n"}); var i = tree.root_node.decls.len; while (i != 0) { i -= 1; const decl = *tree.root_node.decls.at(i); - try stack.push(RenderState {.TopLevelDecl = decl}); + try stack.append(RenderState {.TopLevelDecl = decl}); if (i != 0) { - try stack.push(RenderState { + try stack.append(RenderState { .Text = blk: { const prev_node = *tree.root_node.decls.at(i - 1); const prev_node_last_token = tree.tokens.at(prev_node.lastToken()); @@ -50,7 +49,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { } var indent: usize = 0; - while (stack.pop()) |state| { + while (stack.popOrNull()) |state| { switch (state) { RenderState.TopLevelDecl => |decl| { switch (decl.id) { @@ -59,13 +58,13 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try renderComments(tree, stream, fn_proto, indent); if (fn_proto.body_node) |body_node| { - stack.push(RenderState { .Expression = body_node}) catch unreachable; - try stack.push(RenderState { .Text = " "}); + stack.append(RenderState { .Expression = body_node}) catch unreachable; + try stack.append(RenderState { .Text = " "}); } else { - stack.push(RenderState { .Text = ";" }) catch unreachable; + stack.append(RenderState { .Text = ";" }) catch unreachable; } - try stack.push(RenderState { .Expression = decl }); + try stack.append(RenderState { .Expression = decl }); }, ast.Node.Id.Use => { const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); @@ -73,21 +72,21 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.print("{} ", tree.tokenSlice(visib_token)); } try stream.print("use "); - try stack.push(RenderState { .Text = ";" }); - try stack.push(RenderState { .Expression = use_decl.expr }); + try stack.append(RenderState { .Text = ";" }); + try stack.append(RenderState { .Expression = use_decl.expr }); }, ast.Node.Id.VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); try renderComments(tree, stream, var_decl, indent); - try stack.push(RenderState { .VarDecl = var_decl}); + try stack.append(RenderState { .VarDecl = var_decl}); }, ast.Node.Id.TestDecl => { const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); try renderComments(tree, stream, test_decl, indent); try stream.print("test "); - try stack.push(RenderState { .Expression = test_decl.body_node }); - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = test_decl.name }); + try stack.append(RenderState { .Expression = test_decl.body_node }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = test_decl.name }); }, ast.Node.Id.StructField => { const field = @fieldParentPtr(ast.Node.StructField, "base", decl); @@ -96,24 +95,24 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.print("{} ", tree.tokenSlice(visib_token)); } try stream.print("{}: ", tree.tokenSlice(field.name_token)); - try stack.push(RenderState { .Token = field.lastToken() + 1 }); - try stack.push(RenderState { .Expression = field.type_expr}); + try stack.append(RenderState { .Token = field.lastToken() + 1 }); + try stack.append(RenderState { .Expression = field.type_expr}); }, ast.Node.Id.UnionTag => { const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); try renderComments(tree, stream, tag, indent); try stream.print("{}", tree.tokenSlice(tag.name_token)); - try stack.push(RenderState { .Text = "," }); + try stack.append(RenderState { .Text = "," }); if (tag.value_expr) |value_expr| { - try stack.push(RenderState { .Expression = value_expr }); - try stack.push(RenderState { .Text = " = " }); + try stack.append(RenderState { .Expression = value_expr }); + try stack.append(RenderState { .Text = " = " }); } if (tag.type_expr) |type_expr| { try stream.print(": "); - try stack.push(RenderState { .Expression = type_expr}); + try stack.append(RenderState { .Expression = type_expr}); } }, ast.Node.Id.EnumTag => { @@ -121,10 +120,10 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try renderComments(tree, stream, tag, indent); try stream.print("{}", tree.tokenSlice(tag.name_token)); - try stack.push(RenderState { .Text = "," }); + try stack.append(RenderState { .Text = "," }); if (tag.value) |value| { try stream.print(" = "); - try stack.push(RenderState { .Expression = value}); + try stack.append(RenderState { .Expression = value}); } }, ast.Node.Id.ErrorTag => { @@ -133,8 +132,8 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.print("{}", tree.tokenSlice(tag.name_token)); }, ast.Node.Id.Comptime => { - try stack.push(RenderState { .MaybeSemiColon = decl }); - try stack.push(RenderState { .Expression = decl }); + try stack.append(RenderState { .MaybeSemiColon = decl }); + try stack.append(RenderState { .Expression = decl }); }, ast.Node.Id.LineComment => { const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl); @@ -145,42 +144,42 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { }, RenderState.VarDecl => |var_decl| { - try stack.push(RenderState { .Token = var_decl.semicolon_token }); + try stack.append(RenderState { .Token = var_decl.semicolon_token }); if (var_decl.init_node) |init_node| { - try stack.push(RenderState { .Expression = init_node }); + try stack.append(RenderState { .Expression = init_node }); const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = "; - try stack.push(RenderState { .Text = text }); + try stack.append(RenderState { .Text = text }); } if (var_decl.align_node) |align_node| { - try stack.push(RenderState { .Text = ")" }); - try stack.push(RenderState { .Expression = align_node }); - try stack.push(RenderState { .Text = " align(" }); + try stack.append(RenderState { .Text = ")" }); + try stack.append(RenderState { .Expression = align_node }); + try stack.append(RenderState { .Text = " align(" }); } if (var_decl.type_node) |type_node| { - try stack.push(RenderState { .Expression = type_node }); - try stack.push(RenderState { .Text = ": " }); + try stack.append(RenderState { .Expression = type_node }); + try stack.append(RenderState { .Text = ": " }); } - try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.name_token) }); - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) }); + try stack.append(RenderState { .Text = tree.tokenSlice(var_decl.name_token) }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) }); if (var_decl.comptime_token) |comptime_token| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(comptime_token) }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = tree.tokenSlice(comptime_token) }); } if (var_decl.extern_export_token) |extern_export_token| { if (var_decl.lib_name != null) { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = ??var_decl.lib_name }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = ??var_decl.lib_name }); } - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_token) }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = tree.tokenSlice(extern_export_token) }); } if (var_decl.visib_token) |visib_token| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(visib_token) }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = tree.tokenSlice(visib_token) }); } }, @@ -198,7 +197,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { if (param_decl.var_args_token) |var_args_token| { try stream.print("{}", tree.tokenSlice(var_args_token)); } else { - try stack.push(RenderState { .Expression = param_decl.type_node}); + try stack.append(RenderState { .Expression = param_decl.type_node}); } }, RenderState.Text => |bytes| { @@ -219,18 +218,18 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.write("{}"); } else { try stream.write("{"); - try stack.push(RenderState { .Text = "}"}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent}); - try stack.push(RenderState { .Text = "\n"}); + try stack.append(RenderState { .Text = "}"}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent}); + try stack.append(RenderState { .Text = "\n"}); var i = block.statements.len; while (i != 0) { i -= 1; const statement_node = *block.statements.at(i); - try stack.push(RenderState { .Statement = statement_node}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta}); - try stack.push(RenderState { + try stack.append(RenderState { .Statement = statement_node}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent + indent_delta}); + try stack.append(RenderState { .Text = blk: { if (i != 0) { const prev_node = *block.statements.at(i - 1); @@ -249,21 +248,21 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { ast.Node.Id.Defer => { const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); try stream.print("{} ", tree.tokenSlice(defer_node.defer_token)); - try stack.push(RenderState { .Expression = defer_node.expr }); + try stack.append(RenderState { .Expression = defer_node.expr }); }, ast.Node.Id.Comptime => { const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); try stream.print("{} ", tree.tokenSlice(comptime_node.comptime_token)); - try stack.push(RenderState { .Expression = comptime_node.expr }); + try stack.append(RenderState { .Expression = comptime_node.expr }); }, ast.Node.Id.AsyncAttribute => { const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); try stream.print("{}", tree.tokenSlice(async_attr.async_token)); if (async_attr.allocator_type) |allocator_type| { - try stack.push(RenderState { .Text = ">" }); - try stack.push(RenderState { .Expression = allocator_type }); - try stack.push(RenderState { .Text = "<" }); + try stack.append(RenderState { .Text = ">" }); + try stack.append(RenderState { .Expression = allocator_type }); + try stack.append(RenderState { .Text = "<" }); } }, ast.Node.Id.Suspend => { @@ -274,25 +273,25 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.print("{}", tree.tokenSlice(suspend_node.suspend_token)); if (suspend_node.body) |body| { - try stack.push(RenderState { .Expression = body }); - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = body }); + try stack.append(RenderState { .Text = " " }); } if (suspend_node.payload) |payload| { - try stack.push(RenderState { .Expression = payload }); - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = payload }); + try stack.append(RenderState { .Text = " " }); } }, ast.Node.Id.InfixOp => { const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); - try stack.push(RenderState { .Expression = prefix_op_node.rhs }); + try stack.append(RenderState { .Expression = prefix_op_node.rhs }); if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) { if (prefix_op_node.op.Catch) |payload| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = payload }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = payload }); } - try stack.push(RenderState { .Text = " catch " }); + try stack.append(RenderState { .Text = " catch " }); } else { const text = switch (prefix_op_node.op) { ast.Node.InfixOp.Op.Add => " + ", @@ -340,46 +339,46 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { ast.Node.InfixOp.Op.Catch => unreachable, }; - try stack.push(RenderState { .Text = text }); + try stack.append(RenderState { .Text = text }); } - try stack.push(RenderState { .Expression = prefix_op_node.lhs }); + try stack.append(RenderState { .Expression = prefix_op_node.lhs }); }, ast.Node.Id.PrefixOp => { const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); - try stack.push(RenderState { .Expression = prefix_op_node.rhs }); + try stack.append(RenderState { .Expression = prefix_op_node.rhs }); switch (prefix_op_node.op) { ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { try stream.write("&"); if (addr_of_info.volatile_token != null) { - try stack.push(RenderState { .Text = "volatile "}); + try stack.append(RenderState { .Text = "volatile "}); } if (addr_of_info.const_token != null) { - try stack.push(RenderState { .Text = "const "}); + try stack.append(RenderState { .Text = "const "}); } if (addr_of_info.align_expr) |align_expr| { try stream.print("align("); - try stack.push(RenderState { .Text = ") "}); - try stack.push(RenderState { .Expression = align_expr}); + try stack.append(RenderState { .Text = ") "}); + try stack.append(RenderState { .Expression = align_expr}); } }, ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { try stream.write("[]"); if (addr_of_info.volatile_token != null) { - try stack.push(RenderState { .Text = "volatile "}); + try stack.append(RenderState { .Text = "volatile "}); } if (addr_of_info.const_token != null) { - try stack.push(RenderState { .Text = "const "}); + try stack.append(RenderState { .Text = "const "}); } if (addr_of_info.align_expr) |align_expr| { try stream.print("align("); - try stack.push(RenderState { .Text = ") "}); - try stack.push(RenderState { .Expression = align_expr}); + try stack.append(RenderState { .Text = ") "}); + try stack.append(RenderState { .Expression = align_expr}); } }, ast.Node.PrefixOp.Op.ArrayType => |array_index| { - try stack.push(RenderState { .Text = "]"}); - try stack.push(RenderState { .Expression = array_index}); - try stack.push(RenderState { .Text = "["}); + try stack.append(RenderState { .Text = "]"}); + try stack.append(RenderState { .Expression = array_index}); + try stack.append(RenderState { .Text = "["}); }, ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), @@ -399,70 +398,70 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { switch (suffix_op.op) { @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| { - try stack.push(RenderState { .Text = ")"}); + try stack.append(RenderState { .Text = ")"}); var i = call_info.params.len; while (i != 0) { i -= 1; const param_node = *call_info.params.at(i); - try stack.push(RenderState { .Expression = param_node}); + try stack.append(RenderState { .Expression = param_node}); if (i != 0) { - try stack.push(RenderState { .Text = ", " }); + try stack.append(RenderState { .Text = ", " }); } } - try stack.push(RenderState { .Text = "("}); - try stack.push(RenderState { .Expression = suffix_op.lhs }); + try stack.append(RenderState { .Text = "("}); + try stack.append(RenderState { .Expression = suffix_op.lhs }); if (call_info.async_attr) |async_attr| { - try stack.push(RenderState { .Text = " "}); - try stack.push(RenderState { .Expression = &async_attr.base }); + try stack.append(RenderState { .Text = " "}); + try stack.append(RenderState { .Expression = &async_attr.base }); } }, ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { - try stack.push(RenderState { .Text = "]"}); - try stack.push(RenderState { .Expression = index_expr}); - try stack.push(RenderState { .Text = "["}); - try stack.push(RenderState { .Expression = suffix_op.lhs }); + try stack.append(RenderState { .Text = "]"}); + try stack.append(RenderState { .Expression = index_expr}); + try stack.append(RenderState { .Text = "["}); + try stack.append(RenderState { .Expression = suffix_op.lhs }); }, @TagType(ast.Node.SuffixOp.Op).Slice => |range| { - try stack.push(RenderState { .Text = "]"}); + try stack.append(RenderState { .Text = "]"}); if (range.end) |end| { - try stack.push(RenderState { .Expression = end}); + try stack.append(RenderState { .Expression = end}); } - try stack.push(RenderState { .Text = ".."}); - try stack.push(RenderState { .Expression = range.start}); - try stack.push(RenderState { .Text = "["}); - try stack.push(RenderState { .Expression = suffix_op.lhs }); + try stack.append(RenderState { .Text = ".."}); + try stack.append(RenderState { .Expression = range.start}); + try stack.append(RenderState { .Text = "["}); + try stack.append(RenderState { .Expression = suffix_op.lhs }); }, ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| { if (field_inits.len == 0) { - try stack.push(RenderState { .Text = "{}" }); - try stack.push(RenderState { .Expression = suffix_op.lhs }); + try stack.append(RenderState { .Text = "{}" }); + try stack.append(RenderState { .Expression = suffix_op.lhs }); continue; } if (field_inits.len == 1) { const field_init = *field_inits.at(0); - try stack.push(RenderState { .Text = " }" }); - try stack.push(RenderState { .Expression = field_init }); - try stack.push(RenderState { .Text = "{ " }); - try stack.push(RenderState { .Expression = suffix_op.lhs }); + try stack.append(RenderState { .Text = " }" }); + try stack.append(RenderState { .Expression = field_init }); + try stack.append(RenderState { .Text = "{ " }); + try stack.append(RenderState { .Expression = suffix_op.lhs }); continue; } - try stack.push(RenderState { .Text = "}"}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Text = "\n" }); + try stack.append(RenderState { .Text = "}"}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Text = "\n" }); var i = field_inits.len; while (i != 0) { i -= 1; const field_init = *field_inits.at(i); if (field_init.id != ast.Node.Id.LineComment) { - try stack.push(RenderState { .Text = "," }); + try stack.append(RenderState { .Text = "," }); } - try stack.push(RenderState { .Expression = field_init }); - try stack.push(RenderState.PrintIndent); + try stack.append(RenderState { .Expression = field_init }); + try stack.append(RenderState.PrintIndent); if (i != 0) { - try stack.push(RenderState { .Text = blk: { + try stack.append(RenderState { .Text = blk: { const prev_node = *field_inits.at(i - 1); const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; const loc = tree.tokenLocation(prev_node_last_token_end, field_init.firstToken()); @@ -473,40 +472,40 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { }}); } } - try stack.push(RenderState { .Indent = indent + indent_delta }); - try stack.push(RenderState { .Text = "{\n"}); - try stack.push(RenderState { .Expression = suffix_op.lhs }); + try stack.append(RenderState { .Indent = indent + indent_delta }); + try stack.append(RenderState { .Text = "{\n"}); + try stack.append(RenderState { .Expression = suffix_op.lhs }); }, ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| { if (exprs.len == 0) { - try stack.push(RenderState { .Text = "{}" }); - try stack.push(RenderState { .Expression = suffix_op.lhs }); + try stack.append(RenderState { .Text = "{}" }); + try stack.append(RenderState { .Expression = suffix_op.lhs }); continue; } if (exprs.len == 1) { const expr = *exprs.at(0); - try stack.push(RenderState { .Text = "}" }); - try stack.push(RenderState { .Expression = expr }); - try stack.push(RenderState { .Text = "{" }); - try stack.push(RenderState { .Expression = suffix_op.lhs }); + try stack.append(RenderState { .Text = "}" }); + try stack.append(RenderState { .Expression = expr }); + try stack.append(RenderState { .Text = "{" }); + try stack.append(RenderState { .Expression = suffix_op.lhs }); continue; } - try stack.push(RenderState { .Text = "}"}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent }); + try stack.append(RenderState { .Text = "}"}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent }); var i = exprs.len; while (i != 0) { i -= 1; const expr = *exprs.at(i); - try stack.push(RenderState { .Text = ",\n" }); - try stack.push(RenderState { .Expression = expr }); - try stack.push(RenderState.PrintIndent); + try stack.append(RenderState { .Text = ",\n" }); + try stack.append(RenderState { .Expression = expr }); + try stack.append(RenderState.PrintIndent); } - try stack.push(RenderState { .Indent = indent + indent_delta }); - try stack.push(RenderState { .Text = "{\n"}); - try stack.push(RenderState { .Expression = suffix_op.lhs }); + try stack.append(RenderState { .Indent = indent + indent_delta }); + try stack.append(RenderState { .Text = "{\n"}); + try stack.append(RenderState { .Expression = suffix_op.lhs }); }, } }, @@ -514,8 +513,8 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); if (flow_expr.rhs) |rhs| { - try stack.push(RenderState { .Expression = rhs }); - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = rhs }); + try stack.append(RenderState { .Text = " " }); } switch (flow_expr.kind) { @@ -523,14 +522,14 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.print("break"); if (maybe_label) |label| { try stream.print(" :"); - try stack.push(RenderState { .Expression = label }); + try stack.append(RenderState { .Expression = label }); } }, ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { try stream.print("continue"); if (maybe_label) |label| { try stream.print(" :"); - try stack.push(RenderState { .Expression = label }); + try stack.append(RenderState { .Expression = label }); } }, ast.Node.ControlFlowExpression.Kind.Return => { @@ -541,48 +540,48 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { }, ast.Node.Id.Payload => { const payload = @fieldParentPtr(ast.Node.Payload, "base", base); - try stack.push(RenderState { .Text = "|"}); - try stack.push(RenderState { .Expression = payload.error_symbol }); - try stack.push(RenderState { .Text = "|"}); + try stack.append(RenderState { .Text = "|"}); + try stack.append(RenderState { .Expression = payload.error_symbol }); + try stack.append(RenderState { .Text = "|"}); }, ast.Node.Id.PointerPayload => { const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); - try stack.push(RenderState { .Text = "|"}); - try stack.push(RenderState { .Expression = payload.value_symbol }); + try stack.append(RenderState { .Text = "|"}); + try stack.append(RenderState { .Expression = payload.value_symbol }); if (payload.ptr_token) |ptr_token| { - try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) }); + try stack.append(RenderState { .Text = tree.tokenSlice(ptr_token) }); } - try stack.push(RenderState { .Text = "|"}); + try stack.append(RenderState { .Text = "|"}); }, ast.Node.Id.PointerIndexPayload => { const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); - try stack.push(RenderState { .Text = "|"}); + try stack.append(RenderState { .Text = "|"}); if (payload.index_symbol) |index_symbol| { - try stack.push(RenderState { .Expression = index_symbol }); - try stack.push(RenderState { .Text = ", "}); + try stack.append(RenderState { .Expression = index_symbol }); + try stack.append(RenderState { .Text = ", "}); } - try stack.push(RenderState { .Expression = payload.value_symbol }); + try stack.append(RenderState { .Expression = payload.value_symbol }); if (payload.ptr_token) |ptr_token| { - try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) }); + try stack.append(RenderState { .Text = tree.tokenSlice(ptr_token) }); } - try stack.push(RenderState { .Text = "|"}); + try stack.append(RenderState { .Text = "|"}); }, ast.Node.Id.GroupedExpression => { const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); - try stack.push(RenderState { .Text = ")"}); - try stack.push(RenderState { .Expression = grouped_expr.expr }); - try stack.push(RenderState { .Text = "("}); + try stack.append(RenderState { .Text = ")"}); + try stack.append(RenderState { .Expression = grouped_expr.expr }); + try stack.append(RenderState { .Text = "("}); }, ast.Node.Id.FieldInitializer => { const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); try stream.print(".{} = ", tree.tokenSlice(field_init.name_token)); - try stack.push(RenderState { .Expression = field_init.expr }); + try stack.append(RenderState { .Expression = field_init.expr }); }, ast.Node.Id.IntegerLiteral => { const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); @@ -640,20 +639,20 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { } if (container_decl.fields_and_decls.len == 0) { - try stack.push(RenderState { .Text = "{}"}); + try stack.append(RenderState { .Text = "{}"}); } else { - try stack.push(RenderState { .Text = "}"}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Text = "\n"}); + try stack.append(RenderState { .Text = "}"}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Text = "\n"}); var i = container_decl.fields_and_decls.len; while (i != 0) { i -= 1; const node = *container_decl.fields_and_decls.at(i); - try stack.push(RenderState { .TopLevelDecl = node}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { + try stack.append(RenderState { .TopLevelDecl = node}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Text = blk: { if (i != 0) { const prev_node = *container_decl.fields_and_decls.at(i - 1); @@ -667,25 +666,25 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { }, }); } - try stack.push(RenderState { .Indent = indent + indent_delta}); - try stack.push(RenderState { .Text = "{"}); + try stack.append(RenderState { .Indent = indent + indent_delta}); + try stack.append(RenderState { .Text = "{"}); } switch (container_decl.init_arg_expr) { - ast.Node.ContainerDecl.InitArg.None => try stack.push(RenderState { .Text = " "}), + ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}), ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { if (enum_tag_type) |expr| { - try stack.push(RenderState { .Text = ")) "}); - try stack.push(RenderState { .Expression = expr}); - try stack.push(RenderState { .Text = "(enum("}); + try stack.append(RenderState { .Text = ")) "}); + try stack.append(RenderState { .Expression = expr}); + try stack.append(RenderState { .Text = "(enum("}); } else { - try stack.push(RenderState { .Text = "(enum) "}); + try stack.append(RenderState { .Text = "(enum) "}); } }, ast.Node.ContainerDecl.InitArg.Type => |type_expr| { - try stack.push(RenderState { .Text = ") "}); - try stack.push(RenderState { .Expression = type_expr}); - try stack.push(RenderState { .Text = "("}); + try stack.append(RenderState { .Text = ") "}); + try stack.append(RenderState { .Expression = type_expr}); + try stack.append(RenderState { .Text = "("}); }, } }, @@ -710,28 +709,28 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.write("error{"); - try stack.push(RenderState { .Text = "}" }); - try stack.push(RenderState { .TopLevelDecl = node }); + try stack.append(RenderState { .Text = "}" }); + try stack.append(RenderState { .TopLevelDecl = node }); continue; } try stream.write("error{"); - try stack.push(RenderState { .Text = "}"}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Text = "\n"}); + try stack.append(RenderState { .Text = "}"}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Text = "\n"}); var i = err_set_decl.decls.len; while (i != 0) { i -= 1; const node = *err_set_decl.decls.at(i); if (node.id != ast.Node.Id.LineComment) { - try stack.push(RenderState { .Text = "," }); + try stack.append(RenderState { .Text = "," }); } - try stack.push(RenderState { .TopLevelDecl = node }); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { + try stack.append(RenderState { .TopLevelDecl = node }); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Text = blk: { if (i != 0) { const prev_node = *err_set_decl.decls.at(i - 1); @@ -745,7 +744,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { }, }); } - try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.append(RenderState { .Indent = indent + indent_delta}); }, ast.Node.Id.MultilineStringLiteral => { const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); @@ -766,14 +765,14 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { ast.Node.Id.BuiltinCall => { const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); try stream.print("{}(", tree.tokenSlice(builtin_call.builtin_token)); - try stack.push(RenderState { .Text = ")"}); + try stack.append(RenderState { .Text = ")"}); var i = builtin_call.params.len; while (i != 0) { i -= 1; const param_node = *builtin_call.params.at(i); - try stack.push(RenderState { .Expression = param_node}); + try stack.append(RenderState { .Expression = param_node}); if (i != 0) { - try stack.push(RenderState { .Text = ", " }); + try stack.append(RenderState { .Text = ", " }); } } }, @@ -782,63 +781,63 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { switch (fn_proto.return_type) { ast.Node.FnProto.ReturnType.Explicit => |node| { - try stack.push(RenderState { .Expression = node}); + try stack.append(RenderState { .Expression = node}); }, ast.Node.FnProto.ReturnType.InferErrorSet => |node| { - try stack.push(RenderState { .Expression = node}); - try stack.push(RenderState { .Text = "!"}); + try stack.append(RenderState { .Expression = node}); + try stack.append(RenderState { .Text = "!"}); }, } if (fn_proto.align_expr) |align_expr| { - try stack.push(RenderState { .Text = ") " }); - try stack.push(RenderState { .Expression = align_expr}); - try stack.push(RenderState { .Text = "align(" }); + try stack.append(RenderState { .Text = ") " }); + try stack.append(RenderState { .Expression = align_expr}); + try stack.append(RenderState { .Text = "align(" }); } - try stack.push(RenderState { .Text = ") " }); + try stack.append(RenderState { .Text = ") " }); var i = fn_proto.params.len; while (i != 0) { i -= 1; const param_decl_node = *fn_proto.params.at(i); - try stack.push(RenderState { .ParamDecl = param_decl_node}); + try stack.append(RenderState { .ParamDecl = param_decl_node}); if (i != 0) { - try stack.push(RenderState { .Text = ", " }); + try stack.append(RenderState { .Text = ", " }); } } - try stack.push(RenderState { .Text = "(" }); + try stack.append(RenderState { .Text = "(" }); if (fn_proto.name_token) |name_token| { - try stack.push(RenderState { .Text = tree.tokenSlice(name_token) }); - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = tree.tokenSlice(name_token) }); + try stack.append(RenderState { .Text = " " }); } - try stack.push(RenderState { .Text = "fn" }); + try stack.append(RenderState { .Text = "fn" }); if (fn_proto.async_attr) |async_attr| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = &async_attr.base }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = &async_attr.base }); } if (fn_proto.cc_token) |cc_token| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(cc_token) }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = tree.tokenSlice(cc_token) }); } if (fn_proto.lib_name) |lib_name| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = lib_name }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = lib_name }); } if (fn_proto.extern_export_inline_token) |extern_export_inline_token| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) }); } if (fn_proto.visib_token) |visib_token_index| { const visib_token = tree.tokens.at(visib_token_index); assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export); - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(visib_token_index) }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = tree.tokenSlice(visib_token_index) }); } }, ast.Node.Id.PromiseType => { @@ -846,7 +845,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.write(tree.tokenSlice(promise_type.promise_token)); if (promise_type.result) |result| { try stream.write(tree.tokenSlice(result.arrow_token)); - try stack.push(RenderState { .Expression = result.return_type}); + try stack.append(RenderState { .Expression = result.return_type}); } }, ast.Node.Id.LineComment => { @@ -860,23 +859,23 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.print("{} (", tree.tokenSlice(switch_node.switch_token)); if (switch_node.cases.len == 0) { - try stack.push(RenderState { .Text = ") {}"}); - try stack.push(RenderState { .Expression = switch_node.expr }); + try stack.append(RenderState { .Text = ") {}"}); + try stack.append(RenderState { .Expression = switch_node.expr }); continue; } - try stack.push(RenderState { .Text = "}"}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Text = "\n"}); + try stack.append(RenderState { .Text = "}"}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Text = "\n"}); var i = switch_node.cases.len; while (i != 0) { i -= 1; const node = *switch_node.cases.at(i); - try stack.push(RenderState { .Expression = node}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { + try stack.append(RenderState { .Expression = node}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Text = blk: { if (i != 0) { const prev_node = *switch_node.cases.at(i - 1); @@ -890,29 +889,29 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { }, }); } - try stack.push(RenderState { .Indent = indent + indent_delta}); - try stack.push(RenderState { .Text = ") {"}); - try stack.push(RenderState { .Expression = switch_node.expr }); + try stack.append(RenderState { .Indent = indent + indent_delta}); + try stack.append(RenderState { .Text = ") {"}); + try stack.append(RenderState { .Expression = switch_node.expr }); }, ast.Node.Id.SwitchCase => { const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); - try stack.push(RenderState { .Token = switch_case.lastToken() + 1 }); - try stack.push(RenderState { .Expression = switch_case.expr }); + try stack.append(RenderState { .Token = switch_case.lastToken() + 1 }); + try stack.append(RenderState { .Expression = switch_case.expr }); if (switch_case.payload) |payload| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = payload }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = payload }); } - try stack.push(RenderState { .Text = " => "}); + try stack.append(RenderState { .Text = " => "}); var i = switch_case.items.len; while (i != 0) { i -= 1; - try stack.push(RenderState { .Expression = *switch_case.items.at(i) }); + try stack.append(RenderState { .Expression = *switch_case.items.at(i) }); if (i != 0) { - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Text = ",\n" }); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Text = ",\n" }); } } }, @@ -929,20 +928,20 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { ast.Node.Id.For, ast.Node.Id.While, ast.Node.Id.Switch => { try stream.print(" "); - try stack.push(RenderState { .Expression = else_node.body }); + try stack.append(RenderState { .Expression = else_node.body }); }, else => { - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Expression = else_node.body }); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta }); - try stack.push(RenderState { .Text = "\n" }); + try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Expression = else_node.body }); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent + indent_delta }); + try stack.append(RenderState { .Text = "\n" }); } } if (else_node.payload) |payload| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = payload }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = payload }); } }, ast.Node.Id.While => { @@ -958,42 +957,42 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.print("{} ", tree.tokenSlice(while_node.while_token)); if (while_node.@"else") |@"else"| { - try stack.push(RenderState { .Expression = &@"else".base }); + try stack.append(RenderState { .Expression = &@"else".base }); if (while_node.body.id == ast.Node.Id.Block) { - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = " " }); } else { - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Text = "\n" }); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Text = "\n" }); } } if (while_node.body.id == ast.Node.Id.Block) { - try stack.push(RenderState { .Expression = while_node.body }); - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = while_node.body }); + try stack.append(RenderState { .Text = " " }); } else { - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Expression = while_node.body }); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta }); - try stack.push(RenderState { .Text = "\n" }); + try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Expression = while_node.body }); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent + indent_delta }); + try stack.append(RenderState { .Text = "\n" }); } if (while_node.continue_expr) |continue_expr| { - try stack.push(RenderState { .Text = ")" }); - try stack.push(RenderState { .Expression = continue_expr }); - try stack.push(RenderState { .Text = ": (" }); - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = ")" }); + try stack.append(RenderState { .Expression = continue_expr }); + try stack.append(RenderState { .Text = ": (" }); + try stack.append(RenderState { .Text = " " }); } if (while_node.payload) |payload| { - try stack.push(RenderState { .Expression = payload }); - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = payload }); + try stack.append(RenderState { .Text = " " }); } - try stack.push(RenderState { .Text = ")" }); - try stack.push(RenderState { .Expression = while_node.condition }); - try stack.push(RenderState { .Text = "(" }); + try stack.append(RenderState { .Text = ")" }); + try stack.append(RenderState { .Expression = while_node.condition }); + try stack.append(RenderState { .Text = "(" }); }, ast.Node.Id.For => { const for_node = @fieldParentPtr(ast.Node.For, "base", base); @@ -1008,35 +1007,35 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.print("{} ", tree.tokenSlice(for_node.for_token)); if (for_node.@"else") |@"else"| { - try stack.push(RenderState { .Expression = &@"else".base }); + try stack.append(RenderState { .Expression = &@"else".base }); if (for_node.body.id == ast.Node.Id.Block) { - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = " " }); } else { - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Text = "\n" }); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Text = "\n" }); } } if (for_node.body.id == ast.Node.Id.Block) { - try stack.push(RenderState { .Expression = for_node.body }); - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = for_node.body }); + try stack.append(RenderState { .Text = " " }); } else { - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Expression = for_node.body }); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta }); - try stack.push(RenderState { .Text = "\n" }); + try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Expression = for_node.body }); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent + indent_delta }); + try stack.append(RenderState { .Text = "\n" }); } if (for_node.payload) |payload| { - try stack.push(RenderState { .Expression = payload }); - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = payload }); + try stack.append(RenderState { .Text = " " }); } - try stack.push(RenderState { .Text = ")" }); - try stack.push(RenderState { .Expression = for_node.array_expr }); - try stack.push(RenderState { .Text = "(" }); + try stack.append(RenderState { .Text = ")" }); + try stack.append(RenderState { .Expression = for_node.array_expr }); + try stack.append(RenderState { .Text = "(" }); }, ast.Node.Id.If => { const if_node = @fieldParentPtr(ast.Node.If, "base", base); @@ -1047,42 +1046,42 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { ast.Node.Id.For, ast.Node.Id.While, ast.Node.Id.Switch => { if (if_node.@"else") |@"else"| { - try stack.push(RenderState { .Expression = &@"else".base }); + try stack.append(RenderState { .Expression = &@"else".base }); if (if_node.body.id == ast.Node.Id.Block) { - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = " " }); } else { - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Text = "\n" }); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Text = "\n" }); } } }, else => { if (if_node.@"else") |@"else"| { - try stack.push(RenderState { .Expression = @"else".body }); + try stack.append(RenderState { .Expression = @"else".body }); if (@"else".payload) |payload| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = payload }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = payload }); } - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Text = tree.tokenSlice(@"else".else_token) }); - try stack.push(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Text = tree.tokenSlice(@"else".else_token) }); + try stack.append(RenderState { .Text = " " }); } } } - try stack.push(RenderState { .Expression = if_node.body }); + try stack.append(RenderState { .Expression = if_node.body }); if (if_node.payload) |payload| { - try stack.push(RenderState { .Text = " " }); - try stack.push(RenderState { .Expression = payload }); + try stack.append(RenderState { .Text = " " }); + try stack.append(RenderState { .Expression = payload }); } - try stack.push(RenderState { .NonBreakToken = if_node.condition.lastToken() + 1 }); - try stack.push(RenderState { .Expression = if_node.condition }); - try stack.push(RenderState { .Text = "(" }); + try stack.append(RenderState { .NonBreakToken = if_node.condition.lastToken() + 1 }); + try stack.append(RenderState { .Expression = if_node.condition }); + try stack.append(RenderState { .Text = "(" }); }, ast.Node.Id.Asm => { const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); @@ -1092,33 +1091,33 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { try stream.print("{} ", tree.tokenSlice(volatile_token)); } - try stack.push(RenderState { .Indent = indent }); - try stack.push(RenderState { .Text = ")" }); + try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Text = ")" }); { var i = asm_node.clobbers.len; while (i != 0) { i -= 1; - try stack.push(RenderState { .Expression = *asm_node.clobbers.at(i) }); + try stack.append(RenderState { .Expression = *asm_node.clobbers.at(i) }); if (i != 0) { - try stack.push(RenderState { .Text = ", " }); + try stack.append(RenderState { .Text = ", " }); } } } - try stack.push(RenderState { .Text = ": " }); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta }); - try stack.push(RenderState { .Text = "\n" }); + try stack.append(RenderState { .Text = ": " }); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent + indent_delta }); + try stack.append(RenderState { .Text = "\n" }); { var i = asm_node.inputs.len; while (i != 0) { i -= 1; const node = *asm_node.inputs.at(i); - try stack.push(RenderState { .Expression = &node.base}); + try stack.append(RenderState { .Expression = &node.base}); if (i != 0) { - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Text = blk: { const prev_node = *asm_node.inputs.at(i - 1); const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; @@ -1129,25 +1128,25 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { break :blk "\n"; }, }); - try stack.push(RenderState { .Text = "," }); + try stack.append(RenderState { .Text = "," }); } } } - try stack.push(RenderState { .Indent = indent + indent_delta + 2}); - try stack.push(RenderState { .Text = ": "}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta}); - try stack.push(RenderState { .Text = "\n" }); + try stack.append(RenderState { .Indent = indent + indent_delta + 2}); + try stack.append(RenderState { .Text = ": "}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent + indent_delta}); + try stack.append(RenderState { .Text = "\n" }); { var i = asm_node.outputs.len; while (i != 0) { i -= 1; const node = *asm_node.outputs.at(i); - try stack.push(RenderState { .Expression = &node.base}); + try stack.append(RenderState { .Expression = &node.base}); if (i != 0) { - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Text = blk: { const prev_node = *asm_node.outputs.at(i - 1); const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; @@ -1158,47 +1157,47 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { break :blk "\n"; }, }); - try stack.push(RenderState { .Text = "," }); + try stack.append(RenderState { .Text = "," }); } } } - try stack.push(RenderState { .Indent = indent + indent_delta + 2}); - try stack.push(RenderState { .Text = ": "}); - try stack.push(RenderState.PrintIndent); - try stack.push(RenderState { .Indent = indent + indent_delta}); - try stack.push(RenderState { .Text = "\n" }); - try stack.push(RenderState { .Expression = asm_node.template }); - try stack.push(RenderState { .Text = "(" }); + try stack.append(RenderState { .Indent = indent + indent_delta + 2}); + try stack.append(RenderState { .Text = ": "}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent + indent_delta}); + try stack.append(RenderState { .Text = "\n" }); + try stack.append(RenderState { .Expression = asm_node.template }); + try stack.append(RenderState { .Text = "(" }); }, ast.Node.Id.AsmInput => { const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); - try stack.push(RenderState { .Text = ")"}); - try stack.push(RenderState { .Expression = asm_input.expr}); - try stack.push(RenderState { .Text = " ("}); - try stack.push(RenderState { .Expression = asm_input.constraint }); - try stack.push(RenderState { .Text = "] "}); - try stack.push(RenderState { .Expression = asm_input.symbolic_name }); - try stack.push(RenderState { .Text = "["}); + try stack.append(RenderState { .Text = ")"}); + try stack.append(RenderState { .Expression = asm_input.expr}); + try stack.append(RenderState { .Text = " ("}); + try stack.append(RenderState { .Expression = asm_input.constraint }); + try stack.append(RenderState { .Text = "] "}); + try stack.append(RenderState { .Expression = asm_input.symbolic_name }); + try stack.append(RenderState { .Text = "["}); }, ast.Node.Id.AsmOutput => { const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); - try stack.push(RenderState { .Text = ")"}); + try stack.append(RenderState { .Text = ")"}); switch (asm_output.kind) { ast.Node.AsmOutput.Kind.Variable => |variable_name| { - try stack.push(RenderState { .Expression = &variable_name.base}); + try stack.append(RenderState { .Expression = &variable_name.base}); }, ast.Node.AsmOutput.Kind.Return => |return_type| { - try stack.push(RenderState { .Expression = return_type}); - try stack.push(RenderState { .Text = "-> "}); + try stack.append(RenderState { .Expression = return_type}); + try stack.append(RenderState { .Text = "-> "}); }, } - try stack.push(RenderState { .Text = " ("}); - try stack.push(RenderState { .Expression = asm_output.constraint }); - try stack.push(RenderState { .Text = "] "}); - try stack.push(RenderState { .Expression = asm_output.symbolic_name }); - try stack.push(RenderState { .Text = "["}); + try stack.append(RenderState { .Text = " ("}); + try stack.append(RenderState { .Expression = asm_output.constraint }); + try stack.append(RenderState { .Text = "] "}); + try stack.append(RenderState { .Expression = asm_output.symbolic_name }); + try stack.append(RenderState { .Text = "["}); }, ast.Node.Id.StructField, @@ -1215,11 +1214,11 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { switch (base.id) { ast.Node.Id.VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); - try stack.push(RenderState { .VarDecl = var_decl}); + try stack.append(RenderState { .VarDecl = var_decl}); }, else => { - try stack.push(RenderState { .MaybeSemiColon = base }); - try stack.push(RenderState { .Expression = base }); + try stack.append(RenderState { .MaybeSemiColon = base }); + try stack.append(RenderState { .Expression = base }); }, } }, -- cgit v1.2.3 From bbae6267fe47d6848068938f1a1a83d545f4818f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 May 2018 21:45:29 -0400 Subject: fix self hosted compiler --- src-self-hosted/main.zig | 47 ++++++++++++++++++++++++++++------------------ src-self-hosted/module.zig | 23 ++--------------------- 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index c1a6bbe99a..51cc0014a1 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -671,34 +671,45 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { }; defer allocator.free(source_code); - var tokenizer = std.zig.Tokenizer.init(source_code); - var parser = std.zig.Parser.init(&tokenizer, allocator, file_path); - defer parser.deinit(); - - var tree = parser.parse() catch |err| { + var tree = std.zig.parse(allocator, source_code) catch |err| { try stderr.print("error parsing file '{}': {}\n", file_path, err); continue; }; defer tree.deinit(); - var original_file_backup = try Buffer.init(allocator, file_path); - defer original_file_backup.deinit(); - try original_file_backup.append(".backup"); - try os.rename(allocator, file_path, original_file_backup.toSliceConst()); + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + const token = tree.tokens.at(parse_error.loc()); + const loc = tree.tokenLocation(0, parse_error.loc()); + try stderr.print("{}:{}:{}: error: ", file_path, loc.line + 1, loc.column + 1); + try tree.renderError(parse_error, stderr); + try stderr.print("\n{}\n", source_code[loc.line_start..loc.line_end]); + { + var i: usize = 0; + while (i < loc.column) : (i += 1) { + try stderr.write(" "); + } + } + { + const caret_count = token.end - token.start; + var i: usize = 0; + while (i < caret_count) : (i += 1) { + try stderr.write("~"); + } + } + try stderr.write("\n"); + } + if (tree.errors.len != 0) { + continue; + } try stderr.print("{}\n", file_path); - // TODO: BufferedAtomicFile has some access problems. - var out_file = try os.File.openWrite(allocator, file_path); - defer out_file.close(); + const baf = try io.BufferedAtomicFile.create(allocator, file_path); + defer baf.destroy(); - var out_file_stream = io.FileOutStream.init(&out_file); - try parser.renderSource(out_file_stream.stream, tree.root_node); - - if (!flags.present("keep-backups")) { - try os.deleteFile(allocator, original_file_backup.toSliceConst()); - } + try std.zig.render(allocator, baf.stream(), &tree); } } diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index eec30749e2..ccbd683bdc 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -8,9 +8,7 @@ const c = @import("c.zig"); const builtin = @import("builtin"); const Target = @import("target.zig").Target; const warn = std.debug.warn; -const Tokenizer = std.zig.Tokenizer; const Token = std.zig.Token; -const Parser = std.zig.Parser; const ArrayList = std.ArrayList; pub const Module = struct { @@ -246,34 +244,17 @@ pub const Module = struct { warn("{}", source_code); - warn("====tokenization:====\n"); - { - var tokenizer = Tokenizer.init(source_code); - while (true) { - const token = tokenizer.next(); - tokenizer.dump(token); - if (token.id == Token.Id.Eof) { - break; - } - } - } - warn("====parse:====\n"); - var tokenizer = Tokenizer.init(source_code); - var parser = Parser.init(&tokenizer, self.allocator, root_src_real_path); - defer parser.deinit(); - - var tree = try parser.parse(); + var tree = try std.zig.parse(self.allocator, source_code); defer tree.deinit(); var stderr_file = try std.io.getStdErr(); var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file); const out_stream = &stderr_file_out_stream.stream; - try parser.renderAst(out_stream, tree.root_node); warn("====fmt:====\n"); - try parser.renderSource(out_stream, tree.root_node); + try std.zig.render(self.allocator, out_stream, &tree); warn("====ir:====\n"); warn("TODO\n\n"); -- cgit v1.2.3 From 4787127cf6418f7a819c9d6f07a9046d76e0de65 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 May 2018 00:29:49 -0400 Subject: partial conversion to post-fix pointer deref using zig fmt --- std/atomic/queue.zig | 12 +- std/atomic/stack.zig | 16 +- std/buffer.zig | 11 +- std/build.zig | 207 ++++++-------- std/crypto/blake2.zig | 711 +++++++++++++++++++++++++++++++---------------- std/crypto/hmac.zig | 4 +- std/crypto/sha3.zig | 281 ++++++++++++------- std/event.zig | 53 ++-- std/hash/crc.zig | 32 +-- std/hash_map.zig | 102 ++++--- std/json.zig | 207 ++++++++------ std/math/acos.zig | 14 +- std/math/asin.zig | 18 +- std/math/atan2.zig | 66 ++--- std/math/cbrt.zig | 10 +- std/math/ceil.zig | 4 +- std/math/cos.zig | 12 +- std/math/floor.zig | 4 +- std/math/fma.zig | 7 +- std/math/hypot.zig | 8 +- std/math/ln.zig | 6 +- std/math/log10.zig | 18 +- std/math/log2.zig | 7 +- std/math/round.zig | 8 +- std/math/sin.zig | 12 +- std/math/tan.zig | 6 +- std/net.zig | 40 ++- std/os/child_process.zig | 192 +++++++------ std/segmented_list.zig | 54 ++-- std/sort.zig | 562 ++++++++++++++++++++++++++----------- 30 files changed, 1620 insertions(+), 1064 deletions(-) diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index e25c8e6b17..288a2b3b48 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -70,7 +70,7 @@ test "std.atomic.queue" { var queue: Queue(i32) = undefined; queue.init(); - var context = Context { + var context = Context{ .allocator = a, .queue = &queue, .put_sum = 0, @@ -81,16 +81,18 @@ test "std.atomic.queue" { var putters: [put_thread_count]&std.os.Thread = undefined; for (putters) |*t| { - *t = try std.os.spawnThread(&context, startPuts); + t.* = try std.os.spawnThread(&context, startPuts); } var getters: [put_thread_count]&std.os.Thread = undefined; for (getters) |*t| { - *t = try std.os.spawnThread(&context, startGets); + t.* = try std.os.spawnThread(&context, startGets); } - for (putters) |t| t.wait(); + for (putters) |t| + t.wait(); _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - for (getters) |t| t.wait(); + for (getters) |t| + t.wait(); std.debug.assert(context.put_sum == context.get_sum); std.debug.assert(context.get_count == puts_per_thread * put_thread_count); diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 4a3dbef32b..400a1a3c4f 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -14,9 +14,7 @@ pub fn Stack(comptime T: type) type { }; pub fn init() Self { - return Self { - .root = null, - }; + return Self{ .root = null }; } /// push operation, but only if you are the first item in the stack. if you did not succeed in @@ -75,7 +73,7 @@ test "std.atomic.stack" { var a = &fixed_buffer_allocator.allocator; var stack = Stack(i32).init(); - var context = Context { + var context = Context{ .allocator = a, .stack = &stack, .put_sum = 0, @@ -86,16 +84,18 @@ test "std.atomic.stack" { var putters: [put_thread_count]&std.os.Thread = undefined; for (putters) |*t| { - *t = try std.os.spawnThread(&context, startPuts); + t.* = try std.os.spawnThread(&context, startPuts); } var getters: [put_thread_count]&std.os.Thread = undefined; for (getters) |*t| { - *t = try std.os.spawnThread(&context, startGets); + t.* = try std.os.spawnThread(&context, startGets); } - for (putters) |t| t.wait(); + for (putters) |t| + t.wait(); _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - for (getters) |t| t.wait(); + for (getters) |t| + t.wait(); std.debug.assert(context.put_sum == context.get_sum); std.debug.assert(context.get_count == puts_per_thread * put_thread_count); diff --git a/std/buffer.zig b/std/buffer.zig index 041d891dec..cf530c3c9e 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -31,9 +31,7 @@ pub const Buffer = struct { /// * ::replaceContentsBuffer /// * ::resize pub fn initNull(allocator: &Allocator) Buffer { - return Buffer { - .list = ArrayList(u8).init(allocator), - }; + return Buffer{ .list = ArrayList(u8).init(allocator) }; } /// Must deinitialize with deinit. @@ -45,9 +43,7 @@ pub const Buffer = struct { /// allocated with `allocator`. /// Must deinitialize with deinit. pub fn fromOwnedSlice(allocator: &Allocator, slice: []u8) Buffer { - var self = Buffer { - .list = ArrayList(u8).fromOwnedSlice(allocator, slice), - }; + var self = Buffer{ .list = ArrayList(u8).fromOwnedSlice(allocator, slice) }; self.list.append(0); return self; } @@ -57,11 +53,10 @@ pub const Buffer = struct { pub fn toOwnedSlice(self: &Buffer) []u8 { const allocator = self.list.allocator; const result = allocator.shrink(u8, self.list.items, self.len()); - *self = initNull(allocator); + self.* = initNull(allocator); return result; } - pub fn deinit(self: &Buffer) void { self.list.deinit(); } diff --git a/std/build.zig b/std/build.zig index a312b28a6f..276176c63c 100644 --- a/std/build.zig +++ b/std/build.zig @@ -82,10 +82,8 @@ pub const Builder = struct { description: []const u8, }; - pub fn init(allocator: &Allocator, zig_exe: []const u8, build_root: []const u8, - cache_root: []const u8) Builder - { - var self = Builder { + pub fn init(allocator: &Allocator, zig_exe: []const u8, build_root: []const u8, cache_root: []const u8) Builder { + var self = Builder{ .zig_exe = zig_exe, .build_root = build_root, .cache_root = os.path.relative(allocator, build_root, cache_root) catch unreachable, @@ -112,12 +110,12 @@ pub const Builder = struct { .lib_dir = undefined, .exe_dir = undefined, .installed_files = ArrayList([]const u8).init(allocator), - .uninstall_tls = TopLevelStep { + .uninstall_tls = TopLevelStep{ .step = Step.init("uninstall", allocator, makeUninstall), .description = "Remove build artifacts from prefix path", }, .have_uninstall_step = false, - .install_tls = TopLevelStep { + .install_tls = TopLevelStep{ .step = Step.initNoOp("install", allocator), .description = "Copy build artifacts to prefix path", }, @@ -151,9 +149,7 @@ pub const Builder = struct { return LibExeObjStep.createObject(self, name, root_src); } - pub fn addSharedLibrary(self: &Builder, name: []const u8, root_src: ?[]const u8, - ver: &const Version) &LibExeObjStep - { + pub fn addSharedLibrary(self: &Builder, name: []const u8, root_src: ?[]const u8, ver: &const Version) &LibExeObjStep { return LibExeObjStep.createSharedLibrary(self, name, root_src, ver); } @@ -163,7 +159,7 @@ pub const Builder = struct { pub fn addTest(self: &Builder, root_src: []const u8) &TestStep { const test_step = self.allocator.create(TestStep) catch unreachable; - *test_step = TestStep.init(self, root_src); + test_step.* = TestStep.init(self, root_src); return test_step; } @@ -190,33 +186,31 @@ pub const Builder = struct { } /// ::argv is copied. - pub fn addCommand(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap, - argv: []const []const u8) &CommandStep - { + pub fn addCommand(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap, argv: []const []const u8) &CommandStep { return CommandStep.create(self, cwd, env_map, argv); } pub fn addWriteFile(self: &Builder, file_path: []const u8, data: []const u8) &WriteFileStep { const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; - *write_file_step = WriteFileStep.init(self, file_path, data); + write_file_step.* = WriteFileStep.init(self, file_path, data); return write_file_step; } pub fn addLog(self: &Builder, comptime format: []const u8, args: ...) &LogStep { const data = self.fmt(format, args); const log_step = self.allocator.create(LogStep) catch unreachable; - *log_step = LogStep.init(self, data); + log_step.* = LogStep.init(self, data); return log_step; } pub fn addRemoveDirTree(self: &Builder, dir_path: []const u8) &RemoveDirStep { const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable; - *remove_dir_step = RemoveDirStep.init(self, dir_path); + remove_dir_step.* = RemoveDirStep.init(self, dir_path); return remove_dir_step; } pub fn version(self: &const Builder, major: u32, minor: u32, patch: u32) Version { - return Version { + return Version{ .major = major, .minor = minor, .patch = patch, @@ -254,8 +248,7 @@ pub const Builder = struct { } pub fn getInstallStep(self: &Builder) &Step { - if (self.have_install_step) - return &self.install_tls.step; + if (self.have_install_step) return &self.install_tls.step; self.top_level_steps.append(&self.install_tls) catch unreachable; self.have_install_step = true; @@ -263,8 +256,7 @@ pub const Builder = struct { } pub fn getUninstallStep(self: &Builder) &Step { - if (self.have_uninstall_step) - return &self.uninstall_tls.step; + if (self.have_uninstall_step) return &self.uninstall_tls.step; self.top_level_steps.append(&self.uninstall_tls) catch unreachable; self.have_uninstall_step = true; @@ -360,7 +352,7 @@ pub const Builder = struct { pub fn option(self: &Builder, comptime T: type, name: []const u8, description: []const u8) ?T { const type_id = comptime typeToEnum(T); - const available_option = AvailableOption { + const available_option = AvailableOption{ .name = name, .type_id = type_id, .description = description, @@ -413,7 +405,7 @@ pub const Builder = struct { pub fn step(self: &Builder, name: []const u8, description: []const u8) &Step { const step_info = self.allocator.create(TopLevelStep) catch unreachable; - *step_info = TopLevelStep { + step_info.* = TopLevelStep{ .step = Step.initNoOp(name, self.allocator), .description = description, }; @@ -446,9 +438,9 @@ pub const Builder = struct { } pub fn addUserInputOption(self: &Builder, name: []const u8, value: []const u8) bool { - if (self.user_input_options.put(name, UserInputOption { + if (self.user_input_options.put(name, UserInputOption{ .name = name, - .value = UserValue { .Scalar = value }, + .value = UserValue{ .Scalar = value }, .used = false, }) catch unreachable) |*prev_value| { // option already exists @@ -458,18 +450,18 @@ pub const Builder = struct { var list = ArrayList([]const u8).init(self.allocator); list.append(s) catch unreachable; list.append(value) catch unreachable; - _ = self.user_input_options.put(name, UserInputOption { + _ = self.user_input_options.put(name, UserInputOption{ .name = name, - .value = UserValue { .List = list }, + .value = UserValue{ .List = list }, .used = false, }) catch unreachable; }, UserValue.List => |*list| { // append to the list list.append(value) catch unreachable; - _ = self.user_input_options.put(name, UserInputOption { + _ = self.user_input_options.put(name, UserInputOption{ .name = name, - .value = UserValue { .List = *list }, + .value = UserValue{ .List = list.* }, .used = false, }) catch unreachable; }, @@ -483,9 +475,9 @@ pub const Builder = struct { } pub fn addUserInputFlag(self: &Builder, name: []const u8) bool { - if (self.user_input_options.put(name, UserInputOption { + if (self.user_input_options.put(name, UserInputOption{ .name = name, - .value = UserValue {.Flag = {} }, + .value = UserValue{ .Flag = {} }, .used = false, }) catch unreachable) |*prev_value| { switch (prev_value.value) { @@ -556,9 +548,7 @@ pub const Builder = struct { warn("\n"); } - fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap, - argv: []const []const u8) !void - { + fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap, argv: []const []const u8) !void { if (self.verbose) { printCmd(cwd, argv); } @@ -617,7 +607,7 @@ pub const Builder = struct { self.pushInstalledFile(full_dest_path); const install_step = self.allocator.create(InstallFileStep) catch unreachable; - *install_step = InstallFileStep.init(self, src_path, full_dest_path); + install_step.* = InstallFileStep.init(self, src_path, full_dest_path); return install_step; } @@ -659,25 +649,23 @@ pub const Builder = struct { if (builtin.environ == builtin.Environ.msvc) { return "cl.exe"; } else { - return os.getEnvVarOwned(self.allocator, "CC") catch |err| + return os.getEnvVarOwned(self.allocator, "CC") catch |err| if (err == error.EnvironmentVariableNotFound) ([]const u8)("cc") else - debug.panic("Unable to get environment variable: {}", err) - ; + debug.panic("Unable to get environment variable: {}", err); } } pub fn findProgram(self: &Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { // TODO report error for ambiguous situations - const exe_extension = (Target { .Native = {}}).exeFileExt(); + const exe_extension = (Target{ .Native = {} }).exeFileExt(); for (self.search_prefixes.toSliceConst()) |search_prefix| { for (names) |name| { if (os.path.isAbsolute(name)) { return name; } - const full_path = try os.path.join(self.allocator, search_prefix, "bin", - self.fmt("{}{}", name, exe_extension)); + const full_path = try os.path.join(self.allocator, search_prefix, "bin", self.fmt("{}{}", name, exe_extension)); if (os.path.real(self.allocator, full_path)) |real_path| { return real_path; } else |_| { @@ -761,7 +749,7 @@ pub const Target = union(enum) { Cross: CrossTarget, pub fn oFileExt(self: &const Target) []const u8 { - const environ = switch (*self) { + const environ = switch (self.*) { Target.Native => builtin.environ, Target.Cross => |t| t.environ, }; @@ -786,7 +774,7 @@ pub const Target = union(enum) { } pub fn getOs(self: &const Target) builtin.Os { - return switch (*self) { + return switch (self.*) { Target.Native => builtin.os, Target.Cross => |t| t.os, }; @@ -794,7 +782,8 @@ pub const Target = union(enum) { pub fn isDarwin(self: &const Target) bool { return switch (self.getOs()) { - builtin.Os.ios, builtin.Os.macosx => true, + builtin.Os.ios, + builtin.Os.macosx => true, else => false, }; } @@ -860,61 +849,57 @@ pub const LibExeObjStep = struct { Obj, }; - pub fn createSharedLibrary(builder: &Builder, name: []const u8, root_src: ?[]const u8, - ver: &const Version) &LibExeObjStep - { + pub fn createSharedLibrary(builder: &Builder, name: []const u8, root_src: ?[]const u8, ver: &const Version) &LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - *self = initExtraArgs(builder, name, root_src, Kind.Lib, false, ver); + self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, ver); return self; } pub fn createCSharedLibrary(builder: &Builder, name: []const u8, version: &const Version) &LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - *self = initC(builder, name, Kind.Lib, version, false); + self.* = initC(builder, name, Kind.Lib, version, false); return self; } pub fn createStaticLibrary(builder: &Builder, name: []const u8, root_src: ?[]const u8) &LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - *self = initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0)); + self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0)); return self; } pub fn createCStaticLibrary(builder: &Builder, name: []const u8) &LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - *self = initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true); + self.* = initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true); return self; } pub fn createObject(builder: &Builder, name: []const u8, root_src: []const u8) &LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - *self = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); + self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); return self; } pub fn createCObject(builder: &Builder, name: []const u8, src: []const u8) &LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - *self = initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false); + self.* = initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false); self.object_src = src; return self; } pub fn createExecutable(builder: &Builder, name: []const u8, root_src: ?[]const u8) &LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - *self = initExtraArgs(builder, name, root_src, Kind.Exe, false, builder.version(0, 0, 0)); + self.* = initExtraArgs(builder, name, root_src, Kind.Exe, false, builder.version(0, 0, 0)); return self; } pub fn createCExecutable(builder: &Builder, name: []const u8) &LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - *self = initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false); + self.* = initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false); return self; } - fn initExtraArgs(builder: &Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, - static: bool, ver: &const Version) LibExeObjStep - { - var self = LibExeObjStep { + fn initExtraArgs(builder: &Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: &const Version) LibExeObjStep { + var self = LibExeObjStep{ .strip = false, .builder = builder, .verbose_link = false, @@ -930,7 +915,7 @@ pub const LibExeObjStep = struct { .step = Step.init(name, builder.allocator, make), .output_path = null, .output_h_path = null, - .version = *ver, + .version = ver.*, .out_filename = undefined, .out_h_filename = builder.fmt("{}.h", name), .major_only_filename = undefined, @@ -953,11 +938,11 @@ pub const LibExeObjStep = struct { } fn initC(builder: &Builder, name: []const u8, kind: Kind, version: &const Version, static: bool) LibExeObjStep { - var self = LibExeObjStep { + var self = LibExeObjStep{ .builder = builder, .name = name, .kind = kind, - .version = *version, + .version = version.*, .static = static, .target = Target.Native, .cflags = ArrayList([]const u8).init(builder.allocator), @@ -1005,9 +990,9 @@ pub const LibExeObjStep = struct { self.out_filename = self.builder.fmt("lib{}.a", self.name); } else { switch (self.target.getOs()) { - builtin.Os.ios, builtin.Os.macosx => { - self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", - self.name, self.version.major, self.version.minor, self.version.patch); + builtin.Os.ios, + builtin.Os.macosx => { + self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", self.name, self.version.major, self.version.minor, self.version.patch); self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", self.name, self.version.major); self.name_only_filename = self.builder.fmt("lib{}.dylib", self.name); }, @@ -1015,8 +1000,7 @@ pub const LibExeObjStep = struct { self.out_filename = self.builder.fmt("{}.dll", self.name); }, else => { - self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", - self.name, self.version.major, self.version.minor, self.version.patch); + self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", self.name, self.version.major, self.version.minor, self.version.patch); self.major_only_filename = self.builder.fmt("lib{}.so.{d}", self.name, self.version.major); self.name_only_filename = self.builder.fmt("lib{}.so", self.name); }, @@ -1026,16 +1010,12 @@ pub const LibExeObjStep = struct { } } - pub fn setTarget(self: &LibExeObjStep, target_arch: builtin.Arch, target_os: builtin.Os, - target_environ: builtin.Environ) void - { - self.target = Target { - .Cross = CrossTarget { - .arch = target_arch, - .os = target_os, - .environ = target_environ, - } - }; + pub fn setTarget(self: &LibExeObjStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { + self.target = Target{ .Cross = CrossTarget{ + .arch = target_arch, + .os = target_os, + .environ = target_environ, + } }; self.computeOutFileNames(); } @@ -1159,7 +1139,7 @@ pub const LibExeObjStep = struct { pub fn addPackagePath(self: &LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { assert(self.is_zig); - self.packages.append(Pkg { + self.packages.append(Pkg{ .name = name, .path = pkg_index_path, }) catch unreachable; @@ -1343,8 +1323,7 @@ pub const LibExeObjStep = struct { try builder.spawnChild(zig_args.toSliceConst()); if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) { - try doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename, - self.name_only_filename); + try doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename, self.name_only_filename); } } @@ -1373,7 +1352,8 @@ pub const LibExeObjStep = struct { args.append("ssp-buffer-size=4") catch unreachable; } }, - builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => { + builtin.Mode.ReleaseFast, + builtin.Mode.ReleaseSmall => { args.append("-O2") catch unreachable; args.append("-fno-stack-protector") catch unreachable; }, @@ -1505,8 +1485,7 @@ pub const LibExeObjStep = struct { } if (!is_darwin) { - const rpath_arg = builder.fmt("-Wl,-rpath,{}", - os.path.real(builder.allocator, builder.pathFromRoot(builder.cache_root)) catch unreachable); + const rpath_arg = builder.fmt("-Wl,-rpath,{}", os.path.real(builder.allocator, builder.pathFromRoot(builder.cache_root)) catch unreachable); defer builder.allocator.free(rpath_arg); cc_args.append(rpath_arg) catch unreachable; @@ -1535,8 +1514,7 @@ pub const LibExeObjStep = struct { try builder.spawnChild(cc_args.toSliceConst()); if (self.target.wantSharedLibSymLinks()) { - try doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename, - self.name_only_filename); + try doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename, self.name_only_filename); } } }, @@ -1581,8 +1559,7 @@ pub const LibExeObjStep = struct { cc_args.append("-o") catch unreachable; cc_args.append(output_path) catch unreachable; - const rpath_arg = builder.fmt("-Wl,-rpath,{}", - os.path.real(builder.allocator, builder.pathFromRoot(builder.cache_root)) catch unreachable); + const rpath_arg = builder.fmt("-Wl,-rpath,{}", os.path.real(builder.allocator, builder.pathFromRoot(builder.cache_root)) catch unreachable); defer builder.allocator.free(rpath_arg); cc_args.append(rpath_arg) catch unreachable; @@ -1635,7 +1612,7 @@ pub const TestStep = struct { pub fn init(builder: &Builder, root_src: []const u8) TestStep { const step_name = builder.fmt("test {}", root_src); - return TestStep { + return TestStep{ .step = Step.init(step_name, builder.allocator, make), .builder = builder, .root_src = root_src, @@ -1644,7 +1621,7 @@ pub const TestStep = struct { .name_prefix = "", .filter = null, .link_libs = BufSet.init(builder.allocator), - .target = Target { .Native = {} }, + .target = Target{ .Native = {} }, .exec_cmd_args = null, .include_dirs = ArrayList([]const u8).init(builder.allocator), }; @@ -1674,16 +1651,12 @@ pub const TestStep = struct { self.filter = text; } - pub fn setTarget(self: &TestStep, target_arch: builtin.Arch, target_os: builtin.Os, - target_environ: builtin.Environ) void - { - self.target = Target { - .Cross = CrossTarget { - .arch = target_arch, - .os = target_os, - .environ = target_environ, - } - }; + pub fn setTarget(self: &TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { + self.target = Target{ .Cross = CrossTarget{ + .arch = target_arch, + .os = target_os, + .environ = target_environ, + } }; } pub fn setExecCmd(self: &TestStep, args: []const ?[]const u8) void { @@ -1789,11 +1762,9 @@ pub const CommandStep = struct { env_map: &const BufMap, /// ::argv is copied. - pub fn create(builder: &Builder, cwd: ?[]const u8, env_map: &const BufMap, - argv: []const []const u8) &CommandStep - { + pub fn create(builder: &Builder, cwd: ?[]const u8, env_map: &const BufMap, argv: []const []const u8) &CommandStep { const self = builder.allocator.create(CommandStep) catch unreachable; - *self = CommandStep { + self.* = CommandStep{ .builder = builder, .step = Step.init(argv[0], builder.allocator, make), .argv = builder.allocator.alloc([]u8, argv.len) catch unreachable, @@ -1828,7 +1799,7 @@ const InstallArtifactStep = struct { LibExeObjStep.Kind.Exe => builder.exe_dir, LibExeObjStep.Kind.Lib => builder.lib_dir, }; - *self = Self { + self.* = Self{ .builder = builder, .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make), .artifact = artifact, @@ -1837,10 +1808,8 @@ const InstallArtifactStep = struct { self.step.dependOn(&artifact.step); builder.pushInstalledFile(self.dest_file); if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) { - builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, - artifact.major_only_filename) catch unreachable); - builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, - artifact.name_only_filename) catch unreachable); + builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.major_only_filename) catch unreachable); + builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.name_only_filename) catch unreachable); } return self; } @@ -1859,8 +1828,7 @@ const InstallArtifactStep = struct { }; try builder.copyFileMode(self.artifact.getOutputPath(), self.dest_file, mode); if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) { - try doAtomicSymLinks(builder.allocator, self.dest_file, - self.artifact.major_only_filename, self.artifact.name_only_filename); + try doAtomicSymLinks(builder.allocator, self.dest_file, self.artifact.major_only_filename, self.artifact.name_only_filename); } } }; @@ -1872,7 +1840,7 @@ pub const InstallFileStep = struct { dest_path: []const u8, pub fn init(builder: &Builder, src_path: []const u8, dest_path: []const u8) InstallFileStep { - return InstallFileStep { + return InstallFileStep{ .builder = builder, .step = Step.init(builder.fmt("install {}", src_path), builder.allocator, make), .src_path = src_path, @@ -1893,7 +1861,7 @@ pub const WriteFileStep = struct { data: []const u8, pub fn init(builder: &Builder, file_path: []const u8, data: []const u8) WriteFileStep { - return WriteFileStep { + return WriteFileStep{ .builder = builder, .step = Step.init(builder.fmt("writefile {}", file_path), builder.allocator, make), .file_path = file_path, @@ -1922,7 +1890,7 @@ pub const LogStep = struct { data: []const u8, pub fn init(builder: &Builder, data: []const u8) LogStep { - return LogStep { + return LogStep{ .builder = builder, .step = Step.init(builder.fmt("log {}", data), builder.allocator, make), .data = data, @@ -1941,7 +1909,7 @@ pub const RemoveDirStep = struct { dir_path: []const u8, pub fn init(builder: &Builder, dir_path: []const u8) RemoveDirStep { - return RemoveDirStep { + return RemoveDirStep{ .builder = builder, .step = Step.init(builder.fmt("RemoveDir {}", dir_path), builder.allocator, make), .dir_path = dir_path, @@ -1966,8 +1934,8 @@ pub const Step = struct { loop_flag: bool, done_flag: bool, - pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn (&Step)error!void) Step { - return Step { + pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn(&Step) error!void) Step { + return Step{ .name = name, .makeFn = makeFn, .dependencies = ArrayList(&Step).init(allocator), @@ -1980,8 +1948,7 @@ pub const Step = struct { } pub fn make(self: &Step) !void { - if (self.done_flag) - return; + if (self.done_flag) return; try self.makeFn(self); self.done_flag = true; @@ -1994,9 +1961,7 @@ pub const Step = struct { fn makeNoOp(self: &Step) error!void {} }; -fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_major_only: []const u8, - filename_name_only: []const u8) !void -{ +fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { const out_dir = os.path.dirname(output_path); const out_basename = os.path.basename(output_path); // sym link for libfoo.so.1 to libfoo.so.1.2.3 diff --git a/std/crypto/blake2.zig b/std/crypto/blake2.zig index 99f0e629cd..18025d08eb 100644 --- a/std/crypto/blake2.zig +++ b/std/crypto/blake2.zig @@ -6,11 +6,23 @@ const builtin = @import("builtin"); const htest = @import("test.zig"); const RoundParam = struct { - a: usize, b: usize, c: usize, d: usize, x: usize, y: usize, + a: usize, + b: usize, + c: usize, + d: usize, + x: usize, + y: usize, }; fn Rp(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) RoundParam { - return RoundParam { .a = a, .b = b, .c = c, .d = d, .x = x, .y = y, }; + return RoundParam{ + .a = a, + .b = b, + .c = c, + .d = d, + .x = x, + .y = y, + }; } ///////////////////// @@ -19,145 +31,153 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) RoundParam { pub const Blake2s224 = Blake2s(224); pub const Blake2s256 = Blake2s(256); -fn Blake2s(comptime out_len: usize) type { return struct { - const Self = this; - const block_size = 64; - const digest_size = out_len / 8; +fn Blake2s(comptime out_len: usize) type { + return struct { + const Self = this; + const block_size = 64; + const digest_size = out_len / 8; + + const iv = [8]u32{ + 0x6A09E667, + 0xBB67AE85, + 0x3C6EF372, + 0xA54FF53A, + 0x510E527F, + 0x9B05688C, + 0x1F83D9AB, + 0x5BE0CD19, + }; - const iv = [8]u32 { - 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, - 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19, - }; + const sigma = [10][16]u8{ + []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + []const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + []const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + []const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + []const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + []const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + []const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + []const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + []const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + }; - const sigma = [10][16]u8 { - []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, - []const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, - []const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, - []const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, - []const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, - []const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, - []const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, - []const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, - []const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, - }; + h: [8]u32, + t: u64, + // Streaming cache + buf: [64]u8, + buf_len: u8, - h: [8]u32, - t: u64, - // Streaming cache - buf: [64]u8, - buf_len: u8, - - pub fn init() Self { - debug.assert(8 <= out_len and out_len <= 512); - - var s: Self = undefined; - s.reset(); - return s; - } - - pub fn reset(d: &Self) void { - mem.copy(u32, d.h[0..], iv[0..]); - - // No key plus default parameters - d.h[0] ^= 0x01010000 ^ u32(out_len >> 3); - d.t = 0; - d.buf_len = 0; - } - - pub fn hash(b: []const u8, out: []u8) void { - var d = Self.init(); - d.update(b); - d.final(out); - } - - pub fn update(d: &Self, b: []const u8) void { - var off: usize = 0; - - // Partial buffer exists from previous update. Copy into buffer then hash. - if (d.buf_len != 0 and d.buf_len + b.len > 64) { - off += 64 - d.buf_len; - mem.copy(u8, d.buf[d.buf_len..], b[0..off]); - d.t += 64; - d.round(d.buf[0..], false); - d.buf_len = 0; + pub fn init() Self { + debug.assert(8 <= out_len and out_len <= 512); + + var s: Self = undefined; + s.reset(); + return s; } - // Full middle blocks. - while (off + 64 <= b.len) : (off += 64) { - d.t += 64; - d.round(b[off..off + 64], false); + pub fn reset(d: &Self) void { + mem.copy(u32, d.h[0..], iv[0..]); + + // No key plus default parameters + d.h[0] ^= 0x01010000 ^ u32(out_len >> 3); + d.t = 0; + d.buf_len = 0; } - // Copy any remainder for next pass. - mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); - } + pub fn hash(b: []const u8, out: []u8) void { + var d = Self.init(); + d.update(b); + d.final(out); + } - pub fn final(d: &Self, out: []u8) void { - debug.assert(out.len >= out_len / 8); + pub fn update(d: &Self, b: []const u8) void { + var off: usize = 0; - mem.set(u8, d.buf[d.buf_len..], 0); - d.t += d.buf_len; - d.round(d.buf[0..], true); + // Partial buffer exists from previous update. Copy into buffer then hash. + if (d.buf_len != 0 and d.buf_len + b.len > 64) { + off += 64 - d.buf_len; + mem.copy(u8, d.buf[d.buf_len..], b[0..off]); + d.t += 64; + d.round(d.buf[0..], false); + d.buf_len = 0; + } - const rr = d.h[0 .. out_len / 32]; + // Full middle blocks. + while (off + 64 <= b.len) : (off += 64) { + d.t += 64; + d.round(b[off..off + 64], false); + } - for (rr) |s, j| { - mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Little); + // Copy any remainder for next pass. + mem.copy(u8, d.buf[d.buf_len..], b[off..]); + d.buf_len += u8(b[off..].len); } - } - fn round(d: &Self, b: []const u8, last: bool) void { - debug.assert(b.len == 64); + pub fn final(d: &Self, out: []u8) void { + debug.assert(out.len >= out_len / 8); - var m: [16]u32 = undefined; - var v: [16]u32 = undefined; + mem.set(u8, d.buf[d.buf_len..], 0); + d.t += d.buf_len; + d.round(d.buf[0..], true); - for (m) |*r, i| { - *r = mem.readIntLE(u32, b[4*i .. 4*i + 4]); - } + const rr = d.h[0..out_len / 32]; - var k: usize = 0; - while (k < 8) : (k += 1) { - v[k] = d.h[k]; - v[k+8] = iv[k]; + for (rr) |s, j| { + mem.writeInt(out[4 * j..4 * j + 4], s, builtin.Endian.Little); + } } - v[12] ^= @truncate(u32, d.t); - v[13] ^= u32(d.t >> 32); - if (last) v[14] = ~v[14]; - - const rounds = comptime []RoundParam { - Rp(0, 4, 8, 12, 0, 1), - Rp(1, 5, 9, 13, 2, 3), - Rp(2, 6, 10, 14, 4, 5), - Rp(3, 7, 11, 15, 6, 7), - Rp(0, 5, 10, 15, 8, 9), - Rp(1, 6, 11, 12, 10, 11), - Rp(2, 7, 8, 13, 12, 13), - Rp(3, 4, 9, 14, 14, 15), - }; + fn round(d: &Self, b: []const u8, last: bool) void { + debug.assert(b.len == 64); - comptime var j: usize = 0; - inline while (j < 10) : (j += 1) { - inline for (rounds) |r| { - v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]]; - v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(16)); - v[r.c] = v[r.c] +% v[r.d]; - v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(12)); - v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]]; - v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(8)); - v[r.c] = v[r.c] +% v[r.d]; - v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(7)); + var m: [16]u32 = undefined; + var v: [16]u32 = undefined; + + for (m) |*r, i| { + r.* = mem.readIntLE(u32, b[4 * i..4 * i + 4]); } - } - for (d.h) |*r, i| { - *r ^= v[i] ^ v[i + 8]; + var k: usize = 0; + while (k < 8) : (k += 1) { + v[k] = d.h[k]; + v[k + 8] = iv[k]; + } + + v[12] ^= @truncate(u32, d.t); + v[13] ^= u32(d.t >> 32); + if (last) v[14] = ~v[14]; + + const rounds = comptime []RoundParam{ + Rp(0, 4, 8, 12, 0, 1), + Rp(1, 5, 9, 13, 2, 3), + Rp(2, 6, 10, 14, 4, 5), + Rp(3, 7, 11, 15, 6, 7), + Rp(0, 5, 10, 15, 8, 9), + Rp(1, 6, 11, 12, 10, 11), + Rp(2, 7, 8, 13, 12, 13), + Rp(3, 4, 9, 14, 14, 15), + }; + + comptime var j: usize = 0; + inline while (j < 10) : (j += 1) { + inline for (rounds) |r| { + v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]]; + v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(16)); + v[r.c] = v[r.c] +% v[r.d]; + v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(12)); + v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]]; + v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(8)); + v[r.c] = v[r.c] +% v[r.d]; + v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(7)); + } + } + + for (d.h) |*r, i| { + r.* ^= v[i] ^ v[i + 8]; + } } - } -};} + }; +} test "blake2s224 single" { const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4"; @@ -230,7 +250,7 @@ test "blake2s256 streaming" { } test "blake2s256 aligned final" { - var block = []u8 {0} ** Blake2s256.block_size; + var block = []u8{0} ** Blake2s256.block_size; var out: [Blake2s256.digest_size]u8 = undefined; var h = Blake2s256.init(); @@ -238,154 +258,363 @@ test "blake2s256 aligned final" { h.final(out[0..]); } - ///////////////////// // Blake2b pub const Blake2b384 = Blake2b(384); pub const Blake2b512 = Blake2b(512); -fn Blake2b(comptime out_len: usize) type { return struct { - const Self = this; - const block_size = 128; - const digest_size = out_len / 8; +fn Blake2b(comptime out_len: usize) type { + return struct { + const Self = this; + const block_size = 128; + const digest_size = out_len / 8; + + const iv = [8]u64{ + 0x6a09e667f3bcc908, + 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, + 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, + 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, + 0x5be0cd19137e2179, + }; - const iv = [8]u64 { - 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, - 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, - 0x510e527fade682d1, 0x9b05688c2b3e6c1f, - 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, - }; + const sigma = [12][16]u8{ + []const u8{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + }, + []const u8{ + 14, + 10, + 4, + 8, + 9, + 15, + 13, + 6, + 1, + 12, + 0, + 2, + 11, + 7, + 5, + 3, + }, + []const u8{ + 11, + 8, + 12, + 0, + 5, + 2, + 15, + 13, + 10, + 14, + 3, + 6, + 7, + 1, + 9, + 4, + }, + []const u8{ + 7, + 9, + 3, + 1, + 13, + 12, + 11, + 14, + 2, + 6, + 5, + 10, + 4, + 0, + 15, + 8, + }, + []const u8{ + 9, + 0, + 5, + 7, + 2, + 4, + 10, + 15, + 14, + 1, + 11, + 12, + 6, + 8, + 3, + 13, + }, + []const u8{ + 2, + 12, + 6, + 10, + 0, + 11, + 8, + 3, + 4, + 13, + 7, + 5, + 15, + 14, + 1, + 9, + }, + []const u8{ + 12, + 5, + 1, + 15, + 14, + 13, + 4, + 10, + 0, + 7, + 6, + 3, + 9, + 2, + 8, + 11, + }, + []const u8{ + 13, + 11, + 7, + 14, + 12, + 1, + 3, + 9, + 5, + 0, + 15, + 4, + 8, + 6, + 2, + 10, + }, + []const u8{ + 6, + 15, + 14, + 9, + 11, + 3, + 0, + 8, + 12, + 2, + 13, + 7, + 1, + 4, + 10, + 5, + }, + []const u8{ + 10, + 2, + 8, + 4, + 7, + 6, + 1, + 5, + 15, + 11, + 9, + 14, + 3, + 12, + 13, + 0, + }, + []const u8{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + }, + []const u8{ + 14, + 10, + 4, + 8, + 9, + 15, + 13, + 6, + 1, + 12, + 0, + 2, + 11, + 7, + 5, + 3, + }, + }; - const sigma = [12][16]u8 { - []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, - []const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, - []const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, - []const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, - []const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, - []const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, - []const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, - []const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, - []const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 }, - []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, - }; + h: [8]u64, + t: u128, + // Streaming cache + buf: [128]u8, + buf_len: u8, - h: [8]u64, - t: u128, - // Streaming cache - buf: [128]u8, - buf_len: u8, - - pub fn init() Self { - debug.assert(8 <= out_len and out_len <= 512); - - var s: Self = undefined; - s.reset(); - return s; - } - - pub fn reset(d: &Self) void { - mem.copy(u64, d.h[0..], iv[0..]); - - // No key plus default parameters - d.h[0] ^= 0x01010000 ^ (out_len >> 3); - d.t = 0; - d.buf_len = 0; - } - - pub fn hash(b: []const u8, out: []u8) void { - var d = Self.init(); - d.update(b); - d.final(out); - } - - pub fn update(d: &Self, b: []const u8) void { - var off: usize = 0; - - // Partial buffer exists from previous update. Copy into buffer then hash. - if (d.buf_len != 0 and d.buf_len + b.len > 128) { - off += 128 - d.buf_len; - mem.copy(u8, d.buf[d.buf_len..], b[0..off]); - d.t += 128; - d.round(d.buf[0..], false); + pub fn init() Self { + debug.assert(8 <= out_len and out_len <= 512); + + var s: Self = undefined; + s.reset(); + return s; + } + + pub fn reset(d: &Self) void { + mem.copy(u64, d.h[0..], iv[0..]); + + // No key plus default parameters + d.h[0] ^= 0x01010000 ^ (out_len >> 3); + d.t = 0; d.buf_len = 0; } - // Full middle blocks. - while (off + 128 <= b.len) : (off += 128) { - d.t += 128; - d.round(b[off..off + 128], false); + pub fn hash(b: []const u8, out: []u8) void { + var d = Self.init(); + d.update(b); + d.final(out); } - // Copy any remainder for next pass. - mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); - } + pub fn update(d: &Self, b: []const u8) void { + var off: usize = 0; - pub fn final(d: &Self, out: []u8) void { - mem.set(u8, d.buf[d.buf_len..], 0); - d.t += d.buf_len; - d.round(d.buf[0..], true); + // Partial buffer exists from previous update. Copy into buffer then hash. + if (d.buf_len != 0 and d.buf_len + b.len > 128) { + off += 128 - d.buf_len; + mem.copy(u8, d.buf[d.buf_len..], b[0..off]); + d.t += 128; + d.round(d.buf[0..], false); + d.buf_len = 0; + } - const rr = d.h[0 .. out_len / 64]; + // Full middle blocks. + while (off + 128 <= b.len) : (off += 128) { + d.t += 128; + d.round(b[off..off + 128], false); + } - for (rr) |s, j| { - mem.writeInt(out[8*j .. 8*j + 8], s, builtin.Endian.Little); + // Copy any remainder for next pass. + mem.copy(u8, d.buf[d.buf_len..], b[off..]); + d.buf_len += u8(b[off..].len); } - } - fn round(d: &Self, b: []const u8, last: bool) void { - debug.assert(b.len == 128); + pub fn final(d: &Self, out: []u8) void { + mem.set(u8, d.buf[d.buf_len..], 0); + d.t += d.buf_len; + d.round(d.buf[0..], true); - var m: [16]u64 = undefined; - var v: [16]u64 = undefined; + const rr = d.h[0..out_len / 64]; - for (m) |*r, i| { - *r = mem.readIntLE(u64, b[8*i .. 8*i + 8]); + for (rr) |s, j| { + mem.writeInt(out[8 * j..8 * j + 8], s, builtin.Endian.Little); + } } - var k: usize = 0; - while (k < 8) : (k += 1) { - v[k] = d.h[k]; - v[k+8] = iv[k]; - } + fn round(d: &Self, b: []const u8, last: bool) void { + debug.assert(b.len == 128); - v[12] ^= @truncate(u64, d.t); - v[13] ^= u64(d.t >> 64); - if (last) v[14] = ~v[14]; - - const rounds = comptime []RoundParam { - Rp(0, 4, 8, 12, 0, 1), - Rp(1, 5, 9, 13, 2, 3), - Rp(2, 6, 10, 14, 4, 5), - Rp(3, 7, 11, 15, 6, 7), - Rp(0, 5, 10, 15, 8, 9), - Rp(1, 6, 11, 12, 10, 11), - Rp(2, 7, 8, 13, 12, 13), - Rp(3, 4, 9, 14, 14, 15), - }; + var m: [16]u64 = undefined; + var v: [16]u64 = undefined; - comptime var j: usize = 0; - inline while (j < 12) : (j += 1) { - inline for (rounds) |r| { - v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]]; - v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(32)); - v[r.c] = v[r.c] +% v[r.d]; - v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(24)); - v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]]; - v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(16)); - v[r.c] = v[r.c] +% v[r.d]; - v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(63)); + for (m) |*r, i| { + r.* = mem.readIntLE(u64, b[8 * i..8 * i + 8]); + } + + var k: usize = 0; + while (k < 8) : (k += 1) { + v[k] = d.h[k]; + v[k + 8] = iv[k]; } - } - for (d.h) |*r, i| { - *r ^= v[i] ^ v[i + 8]; + v[12] ^= @truncate(u64, d.t); + v[13] ^= u64(d.t >> 64); + if (last) v[14] = ~v[14]; + + const rounds = comptime []RoundParam{ + Rp(0, 4, 8, 12, 0, 1), + Rp(1, 5, 9, 13, 2, 3), + Rp(2, 6, 10, 14, 4, 5), + Rp(3, 7, 11, 15, 6, 7), + Rp(0, 5, 10, 15, 8, 9), + Rp(1, 6, 11, 12, 10, 11), + Rp(2, 7, 8, 13, 12, 13), + Rp(3, 4, 9, 14, 14, 15), + }; + + comptime var j: usize = 0; + inline while (j < 12) : (j += 1) { + inline for (rounds) |r| { + v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]]; + v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(32)); + v[r.c] = v[r.c] +% v[r.d]; + v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(24)); + v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]]; + v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(16)); + v[r.c] = v[r.c] +% v[r.d]; + v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(63)); + } + } + + for (d.h) |*r, i| { + r.* ^= v[i] ^ v[i + 8]; + } } - } -};} + }; +} test "blake2b384 single" { const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100"; @@ -458,7 +687,7 @@ test "blake2b512 streaming" { } test "blake2b512 aligned final" { - var block = []u8 {0} ** Blake2b512.block_size; + var block = []u8{0} ** Blake2b512.block_size; var out: [Blake2b512.digest_size]u8 = undefined; var h = Blake2b512.init(); diff --git a/std/crypto/hmac.zig b/std/crypto/hmac.zig index 2a36f15b71..1415e88cf4 100644 --- a/std/crypto/hmac.zig +++ b/std/crypto/hmac.zig @@ -29,12 +29,12 @@ pub fn Hmac(comptime H: type) type { var o_key_pad: [H.block_size]u8 = undefined; for (o_key_pad) |*b, i| { - *b = scratch[i] ^ 0x5c; + b.* = scratch[i] ^ 0x5c; } var i_key_pad: [H.block_size]u8 = undefined; for (i_key_pad) |*b, i| { - *b = scratch[i] ^ 0x36; + b.* = scratch[i] ^ 0x36; } // HMAC(k, m) = H(o_key_pad | H(i_key_pad | message)) where | is concatenation diff --git a/std/crypto/sha3.zig b/std/crypto/sha3.zig index f92f56d68f..73b6415e1d 100644 --- a/std/crypto/sha3.zig +++ b/std/crypto/sha3.zig @@ -10,148 +10,228 @@ pub const Sha3_256 = Keccak(256, 0x06); pub const Sha3_384 = Keccak(384, 0x06); pub const Sha3_512 = Keccak(512, 0x06); -fn Keccak(comptime bits: usize, comptime delim: u8) type { return struct { - const Self = this; - const block_size = 200; - const digest_size = bits / 8; - - s: [200]u8, - offset: usize, - rate: usize, - - pub fn init() Self { - var d: Self = undefined; - d.reset(); - return d; - } +fn Keccak(comptime bits: usize, comptime delim: u8) type { + return struct { + const Self = this; + const block_size = 200; + const digest_size = bits / 8; + + s: [200]u8, + offset: usize, + rate: usize, + + pub fn init() Self { + var d: Self = undefined; + d.reset(); + return d; + } - pub fn reset(d: &Self) void { - mem.set(u8, d.s[0..], 0); - d.offset = 0; - d.rate = 200 - (bits / 4); - } + pub fn reset(d: &Self) void { + mem.set(u8, d.s[0..], 0); + d.offset = 0; + d.rate = 200 - (bits / 4); + } - pub fn hash(b: []const u8, out: []u8) void { - var d = Self.init(); - d.update(b); - d.final(out); - } + pub fn hash(b: []const u8, out: []u8) void { + var d = Self.init(); + d.update(b); + d.final(out); + } - pub fn update(d: &Self, b: []const u8) void { - var ip: usize = 0; - var len = b.len; - var rate = d.rate - d.offset; - var offset = d.offset; + pub fn update(d: &Self, b: []const u8) void { + var ip: usize = 0; + var len = b.len; + var rate = d.rate - d.offset; + var offset = d.offset; - // absorb - while (len >= rate) { - for (d.s[offset .. offset + rate]) |*r, i| - *r ^= b[ip..][i]; + // absorb + while (len >= rate) { + for (d.s[offset..offset + rate]) |*r, i| + r.* ^= b[ip..][i]; - keccak_f(1600, d.s[0..]); + keccak_f(1600, d.s[0..]); - ip += rate; - len -= rate; - rate = d.rate; - offset = 0; - } + ip += rate; + len -= rate; + rate = d.rate; + offset = 0; + } - for (d.s[offset .. offset + len]) |*r, i| - *r ^= b[ip..][i]; + for (d.s[offset..offset + len]) |*r, i| + r.* ^= b[ip..][i]; - d.offset = offset + len; - } + d.offset = offset + len; + } - pub fn final(d: &Self, out: []u8) void { - // padding - d.s[d.offset] ^= delim; - d.s[d.rate - 1] ^= 0x80; + pub fn final(d: &Self, out: []u8) void { + // padding + d.s[d.offset] ^= delim; + d.s[d.rate - 1] ^= 0x80; - keccak_f(1600, d.s[0..]); + keccak_f(1600, d.s[0..]); - // squeeze - var op: usize = 0; - var len: usize = bits / 8; + // squeeze + var op: usize = 0; + var len: usize = bits / 8; - while (len >= d.rate) { - mem.copy(u8, out[op..], d.s[0..d.rate]); - keccak_f(1600, d.s[0..]); - op += d.rate; - len -= d.rate; + while (len >= d.rate) { + mem.copy(u8, out[op..], d.s[0..d.rate]); + keccak_f(1600, d.s[0..]); + op += d.rate; + len -= d.rate; + } + + mem.copy(u8, out[op..], d.s[0..len]); } + }; +} - mem.copy(u8, out[op..], d.s[0..len]); - } -};} - -const RC = []const u64 { - 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000, - 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, - 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, - 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003, - 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a, - 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, +const RC = []const u64{ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808a, + 0x8000000080008000, + 0x000000000000808b, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008a, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000a, + 0x000000008000808b, + 0x800000000000008b, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800a, + 0x800000008000000a, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, }; -const ROTC = []const usize { - 1, 3, 6, 10, 15, 21, 28, 36, - 45, 55, 2, 14, 27, 41, 56, 8, - 25, 43, 62, 18, 39, 61, 20, 44 +const ROTC = []const usize{ + 1, + 3, + 6, + 10, + 15, + 21, + 28, + 36, + 45, + 55, + 2, + 14, + 27, + 41, + 56, + 8, + 25, + 43, + 62, + 18, + 39, + 61, + 20, + 44, }; -const PIL = []const usize { - 10, 7, 11, 17, 18, 3, 5, 16, - 8, 21, 24, 4, 15, 23, 19, 13, - 12, 2, 20, 14, 22, 9, 6, 1 +const PIL = []const usize{ + 10, + 7, + 11, + 17, + 18, + 3, + 5, + 16, + 8, + 21, + 24, + 4, + 15, + 23, + 19, + 13, + 12, + 2, + 20, + 14, + 22, + 9, + 6, + 1, }; -const M5 = []const usize { - 0, 1, 2, 3, 4, 0, 1, 2, 3, 4 +const M5 = []const usize{ + 0, + 1, + 2, + 3, + 4, + 0, + 1, + 2, + 3, + 4, }; fn keccak_f(comptime F: usize, d: []u8) void { debug.assert(d.len == F / 8); const B = F / 25; - const no_rounds = comptime x: { break :x 12 + 2 * math.log2(B); }; + const no_rounds = comptime x: { + break :x 12 + 2 * math.log2(B); + }; - var s = []const u64 {0} ** 25; - var t = []const u64 {0} ** 1; - var c = []const u64 {0} ** 5; + var s = []const u64{0} ** 25; + var t = []const u64{0} ** 1; + var c = []const u64{0} ** 5; for (s) |*r, i| { - *r = mem.readIntLE(u64, d[8*i .. 8*i + 8]); + r.* = mem.readIntLE(u64, d[8 * i..8 * i + 8]); } comptime var x: usize = 0; comptime var y: usize = 0; for (RC[0..no_rounds]) |round| { // theta - x = 0; inline while (x < 5) : (x += 1) { - c[x] = s[x] ^ s[x+5] ^ s[x+10] ^ s[x+15] ^ s[x+20]; + x = 0; + inline while (x < 5) : (x += 1) { + c[x] = s[x] ^ s[x + 5] ^ s[x + 10] ^ s[x + 15] ^ s[x + 20]; } - x = 0; inline while (x < 5) : (x += 1) { - t[0] = c[M5[x+4]] ^ math.rotl(u64, c[M5[x+1]], usize(1)); - y = 0; inline while (y < 5) : (y += 1) { - s[x + y*5] ^= t[0]; + x = 0; + inline while (x < 5) : (x += 1) { + t[0] = c[M5[x + 4]] ^ math.rotl(u64, c[M5[x + 1]], usize(1)); + y = 0; + inline while (y < 5) : (y += 1) { + s[x + y * 5] ^= t[0]; } } // rho+pi t[0] = s[1]; - x = 0; inline while (x < 24) : (x += 1) { + x = 0; + inline while (x < 24) : (x += 1) { c[0] = s[PIL[x]]; s[PIL[x]] = math.rotl(u64, t[0], ROTC[x]); t[0] = c[0]; } // chi - y = 0; inline while (y < 5) : (y += 1) { - x = 0; inline while (x < 5) : (x += 1) { - c[x] = s[x + y*5]; + y = 0; + inline while (y < 5) : (y += 1) { + x = 0; + inline while (x < 5) : (x += 1) { + c[x] = s[x + y * 5]; } - x = 0; inline while (x < 5) : (x += 1) { - s[x + y*5] = c[x] ^ (~c[M5[x+1]] & c[M5[x+2]]); + x = 0; + inline while (x < 5) : (x += 1) { + s[x + y * 5] = c[x] ^ (~c[M5[x + 1]] & c[M5[x + 2]]); } } @@ -160,11 +240,10 @@ fn keccak_f(comptime F: usize, d: []u8) void { } for (s) |r, i| { - mem.writeInt(d[8*i .. 8*i + 8], r, builtin.Endian.Little); + mem.writeInt(d[8 * i..8 * i + 8], r, builtin.Endian.Little); } } - test "sha3-224 single" { htest.assertEqualHash(Sha3_224, "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", ""); htest.assertEqualHash(Sha3_224, "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc"); @@ -192,7 +271,7 @@ test "sha3-224 streaming" { } test "sha3-256 single" { - htest.assertEqualHash(Sha3_256, "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a" , ""); + htest.assertEqualHash(Sha3_256, "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", ""); htest.assertEqualHash(Sha3_256, "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", "abc"); htest.assertEqualHash(Sha3_256, "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); } @@ -218,7 +297,7 @@ test "sha3-256 streaming" { } test "sha3-256 aligned final" { - var block = []u8 {0} ** Sha3_256.block_size; + var block = []u8{0} ** Sha3_256.block_size; var out: [Sha3_256.digest_size]u8 = undefined; var h = Sha3_256.init(); @@ -228,7 +307,7 @@ test "sha3-256 aligned final" { test "sha3-384 single" { const h1 = "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004"; - htest.assertEqualHash(Sha3_384, h1 , ""); + htest.assertEqualHash(Sha3_384, h1, ""); const h2 = "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25"; htest.assertEqualHash(Sha3_384, h2, "abc"); const h3 = "79407d3b5916b59c3e30b09822974791c313fb9ecc849e406f23592d04f625dc8c709b98b43b3852b337216179aa7fc7"; @@ -259,7 +338,7 @@ test "sha3-384 streaming" { test "sha3-512 single" { const h1 = "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26"; - htest.assertEqualHash(Sha3_512, h1 , ""); + htest.assertEqualHash(Sha3_512, h1, ""); const h2 = "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0"; htest.assertEqualHash(Sha3_512, h2, "abc"); const h3 = "afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185"; @@ -289,7 +368,7 @@ test "sha3-512 streaming" { } test "sha3-512 aligned final" { - var block = []u8 {0} ** Sha3_512.block_size; + var block = []u8{0} ** Sha3_512.block_size; var out: [Sha3_512.digest_size]u8 = undefined; var h = Sha3_512.init(); diff --git a/std/event.zig b/std/event.zig index bdad7fcc18..b2e7e3ae38 100644 --- a/std/event.zig +++ b/std/event.zig @@ -6,7 +6,7 @@ const mem = std.mem; const posix = std.os.posix; pub const TcpServer = struct { - handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void, + handleRequestFn: async<&mem.Allocator> fn(&TcpServer, &const std.net.Address, &const std.os.File) void, loop: &Loop, sockfd: i32, @@ -18,13 +18,11 @@ pub const TcpServer = struct { const PromiseNode = std.LinkedList(promise).Node; pub fn init(loop: &Loop) !TcpServer { - const sockfd = try std.os.posixSocket(posix.AF_INET, - posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK, - posix.PROTO_tcp); + const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); errdefer std.os.close(sockfd); // TODO can't initialize handler coroutine here because we need well defined copy elision - return TcpServer { + return TcpServer{ .loop = loop, .sockfd = sockfd, .accept_coro = null, @@ -34,9 +32,7 @@ pub const TcpServer = struct { }; } - pub fn listen(self: &TcpServer, address: &const std.net.Address, - handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File)void) !void - { + pub fn listen(self: &TcpServer, address: &const std.net.Address, handleRequestFn: async<&mem.Allocator> fn(&TcpServer, &const std.net.Address, &const std.os.File) void) !void { self.handleRequestFn = handleRequestFn; try std.os.posixBind(self.sockfd, &address.os_addr); @@ -48,7 +44,6 @@ pub const TcpServer = struct { try self.loop.addFd(self.sockfd, ??self.accept_coro); errdefer self.loop.removeFd(self.sockfd); - } pub fn deinit(self: &TcpServer) void { @@ -60,9 +55,7 @@ pub const TcpServer = struct { pub async fn handler(self: &TcpServer) void { while (true) { var accepted_addr: std.net.Address = undefined; - if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr, - posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| - { + if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr, posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| { var socket = std.os.File.openHandle(accepted_fd); _ = async self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) { error.OutOfMemory => { @@ -110,7 +103,7 @@ pub const Loop = struct { fn init(allocator: &mem.Allocator) !Loop { const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC); - return Loop { + return Loop{ .keep_running = true, .allocator = allocator, .epollfd = epollfd, @@ -118,11 +111,9 @@ pub const Loop = struct { } pub fn addFd(self: &Loop, fd: i32, prom: promise) !void { - var ev = std.os.linux.epoll_event { - .events = std.os.linux.EPOLLIN|std.os.linux.EPOLLOUT|std.os.linux.EPOLLET, - .data = std.os.linux.epoll_data { - .ptr = @ptrToInt(prom), - }, + var ev = std.os.linux.epoll_event{ + .events = std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, + .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(prom) }, }; try std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev); } @@ -157,9 +148,9 @@ pub const Loop = struct { }; pub async fn connect(loop: &Loop, _address: &const std.net.Address) !std.os.File { - var address = *_address; // TODO https://github.com/zig-lang/zig/issues/733 + var address = _address.*; // TODO https://github.com/zig-lang/zig/issues/733 - const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK, posix.PROTO_tcp); + const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); errdefer std.os.close(sockfd); try std.os.posixConnectAsync(sockfd, &address.os_addr); @@ -179,11 +170,9 @@ test "listen on a port, send bytes, receive bytes" { const Self = this; - async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address, - _socket: &const std.os.File) void - { + async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address, _socket: &const std.os.File) void { const self = @fieldParentPtr(Self, "tcp_server", tcp_server); - var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733 + var socket = _socket.*; // TODO https://github.com/zig-lang/zig/issues/733 defer socket.close(); const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) { error.OutOfMemory => @panic("unable to handle connection: out of memory"), @@ -191,14 +180,14 @@ test "listen on a port, send bytes, receive bytes" { (await next_handler) catch |err| { std.debug.panic("unable to handle connection: {}\n", err); }; - suspend |p| { cancel p; } + suspend |p| { + cancel p; + } } - async fn errorableHandler(self: &Self, _addr: &const std.net.Address, - _socket: &const std.os.File) !void - { - const addr = *_addr; // TODO https://github.com/zig-lang/zig/issues/733 - var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733 + async fn errorableHandler(self: &Self, _addr: &const std.net.Address, _socket: &const std.os.File) !void { + const addr = _addr.*; // TODO https://github.com/zig-lang/zig/issues/733 + var socket = _socket.*; // TODO https://github.com/zig-lang/zig/issues/733 var adapter = std.io.FileOutStream.init(&socket); var stream = &adapter.stream; @@ -210,9 +199,7 @@ test "listen on a port, send bytes, receive bytes" { const addr = std.net.Address.initIp4(ip4addr, 0); var loop = try Loop.init(std.debug.global_allocator); - var server = MyServer { - .tcp_server = try TcpServer.init(&loop), - }; + var server = MyServer{ .tcp_server = try TcpServer.init(&loop) }; defer server.tcp_server.deinit(); try server.tcp_server.listen(addr, MyServer.handler); diff --git a/std/hash/crc.zig b/std/hash/crc.zig index f88069ce3c..e4c1405a1c 100644 --- a/std/hash/crc.zig +++ b/std/hash/crc.zig @@ -9,9 +9,9 @@ const std = @import("../index.zig"); const debug = std.debug; pub const Polynomial = struct { - const IEEE = 0xedb88320; + const IEEE = 0xedb88320; const Castagnoli = 0x82f63b78; - const Koopman = 0xeb31d82e; + const Koopman = 0xeb31d82e; }; // IEEE is by far the most common CRC and so is aliased by default. @@ -27,20 +27,22 @@ pub fn Crc32WithPoly(comptime poly: u32) type { for (tables[0]) |*e, i| { var crc = u32(i); - var j: usize = 0; while (j < 8) : (j += 1) { + var j: usize = 0; + while (j < 8) : (j += 1) { if (crc & 1 == 1) { crc = (crc >> 1) ^ poly; } else { crc = (crc >> 1); } } - *e = crc; + e.* = crc; } var i: usize = 0; while (i < 256) : (i += 1) { var crc = tables[0][i]; - var j: usize = 1; while (j < 8) : (j += 1) { + var j: usize = 1; + while (j < 8) : (j += 1) { const index = @truncate(u8, crc); crc = tables[0][index] ^ (crc >> 8); tables[j][i] = crc; @@ -53,22 +55,21 @@ pub fn Crc32WithPoly(comptime poly: u32) type { crc: u32, pub fn init() Self { - return Self { - .crc = 0xffffffff, - }; + return Self{ .crc = 0xffffffff }; } pub fn update(self: &Self, input: []const u8) void { var i: usize = 0; while (i + 8 <= input.len) : (i += 8) { - const p = input[i..i+8]; + const p = input[i..i + 8]; // Unrolling this way gives ~50Mb/s increase - self.crc ^= (u32(p[0]) << 0); - self.crc ^= (u32(p[1]) << 8); + self.crc ^= (u32(p[0]) << 0); + self.crc ^= (u32(p[1]) << 8); self.crc ^= (u32(p[2]) << 16); self.crc ^= (u32(p[3]) << 24); + self.crc = lookup_tables[0][p[7]] ^ lookup_tables[1][p[6]] ^ @@ -123,14 +124,15 @@ pub fn Crc32SmallWithPoly(comptime poly: u32) type { for (table) |*e, i| { var crc = u32(i * 16); - var j: usize = 0; while (j < 8) : (j += 1) { + var j: usize = 0; + while (j < 8) : (j += 1) { if (crc & 1 == 1) { crc = (crc >> 1) ^ poly; } else { crc = (crc >> 1); } } - *e = crc; + e.* = crc; } break :block table; @@ -139,9 +141,7 @@ pub fn Crc32SmallWithPoly(comptime poly: u32) type { crc: u32, pub fn init() Self { - return Self { - .crc = 0xffffffff, - }; + return Self{ .crc = 0xffffffff }; } pub fn update(self: &Self, input: []const u8) void { diff --git a/std/hash_map.zig b/std/hash_map.zig index 2a178d9d44..e3b86f8a3b 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -9,10 +9,7 @@ const builtin = @import("builtin"); const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast; const debug_u32 = if (want_modification_safety) u32 else void; -pub fn HashMap(comptime K: type, comptime V: type, - comptime hash: fn(key: K)u32, - comptime eql: fn(a: K, b: K)bool) type -{ +pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn(key: K) u32, comptime eql: fn(a: K, b: K) bool) type { return struct { entries: []Entry, size: usize, @@ -65,7 +62,7 @@ pub fn HashMap(comptime K: type, comptime V: type, }; pub fn init(allocator: &Allocator) Self { - return Self { + return Self{ .entries = []Entry{}, .allocator = allocator, .size = 0, @@ -129,34 +126,36 @@ pub fn HashMap(comptime K: type, comptime V: type, if (hm.entries.len == 0) return null; hm.incrementModificationCount(); const start_index = hm.keyToIndex(key); - {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) { - const index = (start_index + roll_over) % hm.entries.len; - var entry = &hm.entries[index]; - - if (!entry.used) - return null; - - if (!eql(entry.key, key)) continue; - - while (roll_over < hm.entries.len) : (roll_over += 1) { - const next_index = (start_index + roll_over + 1) % hm.entries.len; - const next_entry = &hm.entries[next_index]; - if (!next_entry.used or next_entry.distance_from_start_index == 0) { - entry.used = false; - hm.size -= 1; - return entry; + { + var roll_over: usize = 0; + while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) { + const index = (start_index + roll_over) % hm.entries.len; + var entry = &hm.entries[index]; + + if (!entry.used) return null; + + if (!eql(entry.key, key)) continue; + + while (roll_over < hm.entries.len) : (roll_over += 1) { + const next_index = (start_index + roll_over + 1) % hm.entries.len; + const next_entry = &hm.entries[next_index]; + if (!next_entry.used or next_entry.distance_from_start_index == 0) { + entry.used = false; + hm.size -= 1; + return entry; + } + entry.* = next_entry.*; + entry.distance_from_start_index -= 1; + entry = next_entry; } - *entry = *next_entry; - entry.distance_from_start_index -= 1; - entry = next_entry; + unreachable; // shifting everything in the table } - unreachable; // shifting everything in the table - }} + } return null; } pub fn iterator(hm: &const Self) Iterator { - return Iterator { + return Iterator{ .hm = hm, .count = 0, .index = 0, @@ -182,21 +181,23 @@ pub fn HashMap(comptime K: type, comptime V: type, /// Returns the value that was already there. fn internalPut(hm: &Self, orig_key: K, orig_value: &const V) ?V { var key = orig_key; - var value = *orig_value; + var value = orig_value.*; const start_index = hm.keyToIndex(key); var roll_over: usize = 0; var distance_from_start_index: usize = 0; - while (roll_over < hm.entries.len) : ({roll_over += 1; distance_from_start_index += 1;}) { + while (roll_over < hm.entries.len) : ({ + roll_over += 1; + distance_from_start_index += 1; + }) { const index = (start_index + roll_over) % hm.entries.len; const entry = &hm.entries[index]; if (entry.used and !eql(entry.key, key)) { if (entry.distance_from_start_index < distance_from_start_index) { // robin hood to the rescue - const tmp = *entry; - hm.max_distance_from_start_index = math.max(hm.max_distance_from_start_index, - distance_from_start_index); - *entry = Entry { + const tmp = entry.*; + hm.max_distance_from_start_index = math.max(hm.max_distance_from_start_index, distance_from_start_index); + entry.* = Entry{ .used = true, .distance_from_start_index = distance_from_start_index, .key = key, @@ -219,7 +220,7 @@ pub fn HashMap(comptime K: type, comptime V: type, } hm.max_distance_from_start_index = math.max(distance_from_start_index, hm.max_distance_from_start_index); - *entry = Entry { + entry.* = Entry{ .used = true, .distance_from_start_index = distance_from_start_index, .key = key, @@ -232,13 +233,16 @@ pub fn HashMap(comptime K: type, comptime V: type, fn internalGet(hm: &const Self, key: K) ?&Entry { const start_index = hm.keyToIndex(key); - {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) { - const index = (start_index + roll_over) % hm.entries.len; - const entry = &hm.entries[index]; - - if (!entry.used) return null; - if (eql(entry.key, key)) return entry; - }} + { + var roll_over: usize = 0; + while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) { + const index = (start_index + roll_over) % hm.entries.len; + const entry = &hm.entries[index]; + + if (!entry.used) return null; + if (eql(entry.key, key)) return entry; + } + } return null; } @@ -282,11 +286,19 @@ test "iterator hash map" { assert((reset_map.put(2, 22) catch unreachable) == null); assert((reset_map.put(3, 33) catch unreachable) == null); - var keys = []i32 { 1, 2, 3 }; - var values = []i32 { 11, 22, 33 }; + var keys = []i32{ + 1, + 2, + 3, + }; + var values = []i32{ + 11, + 22, + 33, + }; var it = reset_map.iterator(); - var count : usize = 0; + var count: usize = 0; while (it.next()) |next| { assert(next.key == keys[count]); assert(next.value == values[count]); @@ -305,7 +317,7 @@ test "iterator hash map" { } it.reset(); - var entry = ?? it.next(); + var entry = ??it.next(); assert(entry.key == keys[0]); assert(entry.value == values[0]); } diff --git a/std/json.zig b/std/json.zig index 6f853501ed..2ea9083e2f 100644 --- a/std/json.zig +++ b/std/json.zig @@ -35,7 +35,7 @@ pub const Token = struct { }; pub fn init(id: Id, count: usize, offset: u1) Token { - return Token { + return Token{ .id = id, .offset = offset, .string_has_escape = false, @@ -45,7 +45,7 @@ pub const Token = struct { } pub fn initString(count: usize, has_unicode_escape: bool) Token { - return Token { + return Token{ .id = Id.String, .offset = 0, .string_has_escape = has_unicode_escape, @@ -55,7 +55,7 @@ pub const Token = struct { } pub fn initNumber(count: usize, number_is_integer: bool) Token { - return Token { + return Token{ .id = Id.Number, .offset = 0, .string_has_escape = false, @@ -66,7 +66,7 @@ pub const Token = struct { // A marker token is a zero-length pub fn initMarker(id: Id) Token { - return Token { + return Token{ .id = id, .offset = 0, .string_has_escape = false, @@ -77,7 +77,7 @@ pub const Token = struct { // Slice into the underlying input string. pub fn slice(self: &const Token, input: []const u8, i: usize) []const u8 { - return input[i + self.offset - self.count .. i + self.offset]; + return input[i + self.offset - self.count..i + self.offset]; } }; @@ -105,8 +105,8 @@ const StreamingJsonParser = struct { stack: u256, stack_used: u8, - const object_bit = 0; - const array_bit = 1; + const object_bit = 0; + const array_bit = 1; const max_stack_size = @maxValue(u8); pub fn init() StreamingJsonParser { @@ -120,7 +120,7 @@ const StreamingJsonParser = struct { p.count = 0; // Set before ever read in main transition function p.after_string_state = undefined; - p.after_value_state = State.ValueEnd; // handle end of values normally + p.after_value_state = State.ValueEnd; // handle end of values normally p.stack = 0; p.stack_used = 0; p.complete = false; @@ -181,7 +181,7 @@ const StreamingJsonParser = struct { } }; - pub const Error = error { + pub const Error = error{ InvalidTopLevel, TooManyNestedItems, TooManyClosingItems, @@ -206,8 +206,8 @@ const StreamingJsonParser = struct { // // There is currently no error recovery on a bad stream. pub fn feed(p: &StreamingJsonParser, c: u8, token1: &?Token, token2: &?Token) Error!void { - *token1 = null; - *token2 = null; + token1.* = null; + token2.* = null; p.count += 1; // unlikely @@ -228,7 +228,7 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ObjectSeparator; - *token = Token.initMarker(Token.Id.ObjectBegin); + token.* = Token.initMarker(Token.Id.ObjectBegin); }, '[' => { p.stack <<= 1; @@ -238,7 +238,7 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ValueEnd; - *token = Token.initMarker(Token.Id.ArrayBegin); + token.* = Token.initMarker(Token.Id.ArrayBegin); }, '-' => { p.number_is_integer = true; @@ -281,7 +281,10 @@ const StreamingJsonParser = struct { p.after_value_state = State.TopLevelEnd; p.count = 0; }, - 0x09, 0x0A, 0x0D, 0x20 => { + 0x09, + 0x0A, + 0x0D, + 0x20 => { // whitespace }, else => { @@ -290,7 +293,10 @@ const StreamingJsonParser = struct { }, State.TopLevelEnd => switch (c) { - 0x09, 0x0A, 0x0D, 0x20 => { + 0x09, + 0x0A, + 0x0D, + 0x20 => { // whitespace }, else => { @@ -324,7 +330,7 @@ const StreamingJsonParser = struct { else => {}, } - *token = Token.initMarker(Token.Id.ObjectEnd); + token.* = Token.initMarker(Token.Id.ObjectEnd); }, ']' => { if (p.stack & 1 != array_bit) { @@ -348,7 +354,7 @@ const StreamingJsonParser = struct { else => {}, } - *token = Token.initMarker(Token.Id.ArrayEnd); + token.* = Token.initMarker(Token.Id.ArrayEnd); }, '{' => { if (p.stack_used == max_stack_size) { @@ -362,7 +368,7 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ObjectSeparator; - *token = Token.initMarker(Token.Id.ObjectBegin); + token.* = Token.initMarker(Token.Id.ObjectBegin); }, '[' => { if (p.stack_used == max_stack_size) { @@ -376,7 +382,7 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ValueEnd; - *token = Token.initMarker(Token.Id.ArrayBegin); + token.* = Token.initMarker(Token.Id.ArrayBegin); }, '-' => { p.state = State.Number; @@ -406,7 +412,10 @@ const StreamingJsonParser = struct { p.state = State.NullLiteral1; p.count = 0; }, - 0x09, 0x0A, 0x0D, 0x20 => { + 0x09, + 0x0A, + 0x0D, + 0x20 => { // whitespace }, else => { @@ -428,7 +437,7 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ObjectSeparator; - *token = Token.initMarker(Token.Id.ObjectBegin); + token.* = Token.initMarker(Token.Id.ObjectBegin); }, '[' => { if (p.stack_used == max_stack_size) { @@ -442,7 +451,7 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ValueEnd; - *token = Token.initMarker(Token.Id.ArrayBegin); + token.* = Token.initMarker(Token.Id.ArrayBegin); }, '-' => { p.state = State.Number; @@ -472,7 +481,10 @@ const StreamingJsonParser = struct { p.state = State.NullLiteral1; p.count = 0; }, - 0x09, 0x0A, 0x0D, 0x20 => { + 0x09, + 0x0A, + 0x0D, + 0x20 => { // whitespace }, else => { @@ -501,7 +513,7 @@ const StreamingJsonParser = struct { p.state = State.TopLevelEnd; } - *token = Token.initMarker(Token.Id.ArrayEnd); + token.* = Token.initMarker(Token.Id.ArrayEnd); }, '}' => { if (p.stack_used == 0) { @@ -519,9 +531,12 @@ const StreamingJsonParser = struct { p.state = State.TopLevelEnd; } - *token = Token.initMarker(Token.Id.ObjectEnd); + token.* = Token.initMarker(Token.Id.ObjectEnd); }, - 0x09, 0x0A, 0x0D, 0x20 => { + 0x09, + 0x0A, + 0x0D, + 0x20 => { // whitespace }, else => { @@ -534,7 +549,10 @@ const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ValueEnd; }, - 0x09, 0x0A, 0x0D, 0x20 => { + 0x09, + 0x0A, + 0x0D, + 0x20 => { // whitespace }, else => { @@ -553,12 +571,15 @@ const StreamingJsonParser = struct { p.complete = true; } - *token = Token.initString(p.count - 1, p.string_has_escape); + token.* = Token.initString(p.count - 1, p.string_has_escape); }, '\\' => { p.state = State.StringEscapeCharacter; }, - 0x20, 0x21, 0x23 ... 0x5B, 0x5D ... 0x7F => { + 0x20, + 0x21, + 0x23 ... 0x5B, + 0x5D ... 0x7F => { // non-control ascii }, 0xC0 ... 0xDF => { @@ -599,7 +620,14 @@ const StreamingJsonParser = struct { // The current JSONTestSuite tests rely on both of this behaviour being present // however, so we default to the status quo where both are accepted until this // is further clarified. - '"', '\\', '/', 'b', 'f', 'n', 'r', 't' => { + '"', + '\\', + '/', + 'b', + 'f', + 'n', + 'r', + 't' => { p.string_has_escape = true; p.state = State.String; }, @@ -613,28 +641,36 @@ const StreamingJsonParser = struct { }, State.StringEscapeHexUnicode4 => switch (c) { - '0' ... '9', 'A' ... 'F', 'a' ... 'f' => { + '0' ... '9', + 'A' ... 'F', + 'a' ... 'f' => { p.state = State.StringEscapeHexUnicode3; }, else => return error.InvalidUnicodeHexSymbol, }, State.StringEscapeHexUnicode3 => switch (c) { - '0' ... '9', 'A' ... 'F', 'a' ... 'f' => { + '0' ... '9', + 'A' ... 'F', + 'a' ... 'f' => { p.state = State.StringEscapeHexUnicode2; }, else => return error.InvalidUnicodeHexSymbol, }, State.StringEscapeHexUnicode2 => switch (c) { - '0' ... '9', 'A' ... 'F', 'a' ... 'f' => { + '0' ... '9', + 'A' ... 'F', + 'a' ... 'f' => { p.state = State.StringEscapeHexUnicode1; }, else => return error.InvalidUnicodeHexSymbol, }, State.StringEscapeHexUnicode1 => switch (c) { - '0' ... '9', 'A' ... 'F', 'a' ... 'f' => { + '0' ... '9', + 'A' ... 'F', + 'a' ... 'f' => { p.state = State.String; }, else => return error.InvalidUnicodeHexSymbol, @@ -662,13 +698,14 @@ const StreamingJsonParser = struct { p.number_is_integer = false; p.state = State.NumberFractionalRequired; }, - 'e', 'E' => { + 'e', + 'E' => { p.number_is_integer = false; p.state = State.NumberExponent; }, else => { p.state = p.after_value_state; - *token = Token.initNumber(p.count, p.number_is_integer); + token.* = Token.initNumber(p.count, p.number_is_integer); return true; }, } @@ -681,7 +718,8 @@ const StreamingJsonParser = struct { p.number_is_integer = false; p.state = State.NumberFractionalRequired; }, - 'e', 'E' => { + 'e', + 'E' => { p.number_is_integer = false; p.state = State.NumberExponent; }, @@ -690,7 +728,7 @@ const StreamingJsonParser = struct { }, else => { p.state = p.after_value_state; - *token = Token.initNumber(p.count, p.number_is_integer); + token.* = Token.initNumber(p.count, p.number_is_integer); return true; }, } @@ -714,13 +752,14 @@ const StreamingJsonParser = struct { '0' ... '9' => { // another digit }, - 'e', 'E' => { + 'e', + 'E' => { p.number_is_integer = false; p.state = State.NumberExponent; }, else => { p.state = p.after_value_state; - *token = Token.initNumber(p.count, p.number_is_integer); + token.* = Token.initNumber(p.count, p.number_is_integer); return true; }, } @@ -729,20 +768,22 @@ const StreamingJsonParser = struct { State.NumberMaybeExponent => { p.complete = p.after_value_state == State.TopLevelEnd; switch (c) { - 'e', 'E' => { + 'e', + 'E' => { p.number_is_integer = false; p.state = State.NumberExponent; }, else => { p.state = p.after_value_state; - *token = Token.initNumber(p.count, p.number_is_integer); + token.* = Token.initNumber(p.count, p.number_is_integer); return true; }, } }, State.NumberExponent => switch (c) { - '-', '+', => { + '-', + '+' => { p.complete = false; p.state = State.NumberExponentDigitsRequired; }, @@ -773,7 +814,7 @@ const StreamingJsonParser = struct { }, else => { p.state = p.after_value_state; - *token = Token.initNumber(p.count, p.number_is_integer); + token.* = Token.initNumber(p.count, p.number_is_integer); return true; }, } @@ -793,7 +834,7 @@ const StreamingJsonParser = struct { 'e' => { p.state = p.after_value_state; p.complete = p.state == State.TopLevelEnd; - *token = Token.init(Token.Id.True, p.count + 1, 1); + token.* = Token.init(Token.Id.True, p.count + 1, 1); }, else => { return error.InvalidLiteral; @@ -819,7 +860,7 @@ const StreamingJsonParser = struct { 'e' => { p.state = p.after_value_state; p.complete = p.state == State.TopLevelEnd; - *token = Token.init(Token.Id.False, p.count + 1, 1); + token.* = Token.init(Token.Id.False, p.count + 1, 1); }, else => { return error.InvalidLiteral; @@ -840,7 +881,7 @@ const StreamingJsonParser = struct { 'l' => { p.state = p.after_value_state; p.complete = p.state == State.TopLevelEnd; - *token = Token.init(Token.Id.Null, p.count + 1, 1); + token.* = Token.init(Token.Id.Null, p.count + 1, 1); }, else => { return error.InvalidLiteral; @@ -895,7 +936,7 @@ pub const Value = union(enum) { Object: ObjectMap, pub fn dump(self: &const Value) void { - switch (*self) { + switch (self.*) { Value.Null => { std.debug.warn("null"); }, @@ -950,7 +991,7 @@ pub const Value = union(enum) { } fn dumpIndentLevel(self: &const Value, indent: usize, level: usize) void { - switch (*self) { + switch (self.*) { Value.Null => { std.debug.warn("null"); }, @@ -1027,7 +1068,7 @@ const JsonParser = struct { }; pub fn init(allocator: &Allocator, copy_strings: bool) JsonParser { - return JsonParser { + return JsonParser{ .allocator = allocator, .state = State.Simple, .copy_strings = copy_strings, @@ -1082,7 +1123,7 @@ const JsonParser = struct { std.debug.assert(p.stack.len == 1); - return ValueTree { + return ValueTree{ .arena = arena, .root = p.stack.at(0), }; @@ -1115,11 +1156,11 @@ const JsonParser = struct { switch (token.id) { Token.Id.ObjectBegin => { - try p.stack.append(Value { .Object = ObjectMap.init(allocator) }); + try p.stack.append(Value{ .Object = ObjectMap.init(allocator) }); p.state = State.ObjectKey; }, Token.Id.ArrayBegin => { - try p.stack.append(Value { .Array = ArrayList(Value).init(allocator) }); + try p.stack.append(Value{ .Array = ArrayList(Value).init(allocator) }); p.state = State.ArrayValue; }, Token.Id.String => { @@ -1133,12 +1174,12 @@ const JsonParser = struct { p.state = State.ObjectKey; }, Token.Id.True => { - _ = try object.put(key, Value { .Bool = true }); + _ = try object.put(key, Value{ .Bool = true }); _ = p.stack.pop(); p.state = State.ObjectKey; }, Token.Id.False => { - _ = try object.put(key, Value { .Bool = false }); + _ = try object.put(key, Value{ .Bool = false }); _ = p.stack.pop(); p.state = State.ObjectKey; }, @@ -1165,11 +1206,11 @@ const JsonParser = struct { try p.pushToParent(value); }, Token.Id.ObjectBegin => { - try p.stack.append(Value { .Object = ObjectMap.init(allocator) }); + try p.stack.append(Value{ .Object = ObjectMap.init(allocator) }); p.state = State.ObjectKey; }, Token.Id.ArrayBegin => { - try p.stack.append(Value { .Array = ArrayList(Value).init(allocator) }); + try p.stack.append(Value{ .Array = ArrayList(Value).init(allocator) }); p.state = State.ArrayValue; }, Token.Id.String => { @@ -1179,10 +1220,10 @@ const JsonParser = struct { try array.append(try p.parseNumber(token, input, i)); }, Token.Id.True => { - try array.append(Value { .Bool = true }); + try array.append(Value{ .Bool = true }); }, Token.Id.False => { - try array.append(Value { .Bool = false }); + try array.append(Value{ .Bool = false }); }, Token.Id.Null => { try array.append(Value.Null); @@ -1194,11 +1235,11 @@ const JsonParser = struct { }, State.Simple => switch (token.id) { Token.Id.ObjectBegin => { - try p.stack.append(Value { .Object = ObjectMap.init(allocator) }); + try p.stack.append(Value{ .Object = ObjectMap.init(allocator) }); p.state = State.ObjectKey; }, Token.Id.ArrayBegin => { - try p.stack.append(Value { .Array = ArrayList(Value).init(allocator) }); + try p.stack.append(Value{ .Array = ArrayList(Value).init(allocator) }); p.state = State.ArrayValue; }, Token.Id.String => { @@ -1208,15 +1249,16 @@ const JsonParser = struct { try p.stack.append(try p.parseNumber(token, input, i)); }, Token.Id.True => { - try p.stack.append(Value { .Bool = true }); + try p.stack.append(Value{ .Bool = true }); }, Token.Id.False => { - try p.stack.append(Value { .Bool = false }); + try p.stack.append(Value{ .Bool = false }); }, Token.Id.Null => { try p.stack.append(Value.Null); }, - Token.Id.ObjectEnd, Token.Id.ArrayEnd => { + Token.Id.ObjectEnd, + Token.Id.ArrayEnd => { unreachable; }, }, @@ -1248,15 +1290,14 @@ const JsonParser = struct { // TODO: We don't strictly have to copy values which do not contain any escape // characters if flagged with the option. const slice = token.slice(input, i); - return Value { .String = try mem.dupe(p.allocator, u8, slice) }; + return Value{ .String = try mem.dupe(p.allocator, u8, slice) }; } fn parseNumber(p: &JsonParser, token: &const Token, input: []const u8, i: usize) !Value { return if (token.number_is_integer) - Value { .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } + Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } else - @panic("TODO: fmt.parseFloat not yet implemented") - ; + @panic("TODO: fmt.parseFloat not yet implemented"); } }; @@ -1267,21 +1308,21 @@ test "json parser dynamic" { defer p.deinit(); const s = - \\{ - \\ "Image": { - \\ "Width": 800, - \\ "Height": 600, - \\ "Title": "View from 15th Floor", - \\ "Thumbnail": { - \\ "Url": "http://www.example.com/image/481989943", - \\ "Height": 125, - \\ "Width": 100 - \\ }, - \\ "Animated" : false, - \\ "IDs": [116, 943, 234, 38793] - \\ } - \\} - ; + \\{ + \\ "Image": { + \\ "Width": 800, + \\ "Height": 600, + \\ "Title": "View from 15th Floor", + \\ "Thumbnail": { + \\ "Url": "http://www.example.com/image/481989943", + \\ "Height": 125, + \\ "Width": 100 + \\ }, + \\ "Animated" : false, + \\ "IDs": [116, 943, 234, 38793] + \\ } + \\} + ; var tree = try p.parse(s); defer tree.deinit(); diff --git a/std/math/acos.zig b/std/math/acos.zig index a4f08af306..284f73fc91 100644 --- a/std/math/acos.zig +++ b/std/math/acos.zig @@ -16,7 +16,7 @@ pub fn acos(x: var) @typeOf(x) { } fn r32(z: f32) f32 { - const pS0 = 1.6666586697e-01; + const pS0 = 1.6666586697e-01; const pS1 = -4.2743422091e-02; const pS2 = -8.6563630030e-03; const qS1 = -7.0662963390e-01; @@ -74,16 +74,16 @@ fn acos32(x: f32) f32 { } fn r64(z: f64) f64 { - const pS0: f64 = 1.66666666666666657415e-01; + const pS0: f64 = 1.66666666666666657415e-01; const pS1: f64 = -3.25565818622400915405e-01; - const pS2: f64 = 2.01212532134862925881e-01; + const pS2: f64 = 2.01212532134862925881e-01; const pS3: f64 = -4.00555345006794114027e-02; - const pS4: f64 = 7.91534994289814532176e-04; - const pS5: f64 = 3.47933107596021167570e-05; + const pS4: f64 = 7.91534994289814532176e-04; + const pS5: f64 = 3.47933107596021167570e-05; const qS1: f64 = -2.40339491173441421878e+00; - const qS2: f64 = 2.02094576023350569471e+00; + const qS2: f64 = 2.02094576023350569471e+00; const qS3: f64 = -6.88283971605453293030e-01; - const qS4: f64 = 7.70381505559019352791e-02; + const qS4: f64 = 7.70381505559019352791e-02; const p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5))))); const q = 1.0 + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4))); diff --git a/std/math/asin.zig b/std/math/asin.zig index 9fa5a80ea5..2ee3aa6048 100644 --- a/std/math/asin.zig +++ b/std/math/asin.zig @@ -17,7 +17,7 @@ pub fn asin(x: var) @typeOf(x) { } fn r32(z: f32) f32 { - const pS0 = 1.6666586697e-01; + const pS0 = 1.6666586697e-01; const pS1 = -4.2743422091e-02; const pS2 = -8.6563630030e-03; const qS1 = -7.0662963390e-01; @@ -37,9 +37,9 @@ fn asin32(x: f32) f32 { if (ix >= 0x3F800000) { // |x| >= 1 if (ix == 0x3F800000) { - return x * pio2 + 0x1.0p-120; // asin(+-1) = +-pi/2 with inexact + return x * pio2 + 0x1.0p-120; // asin(+-1) = +-pi/2 with inexact } else { - return math.nan(f32); // asin(|x| > 1) is nan + return math.nan(f32); // asin(|x| > 1) is nan } } @@ -66,16 +66,16 @@ fn asin32(x: f32) f32 { } fn r64(z: f64) f64 { - const pS0: f64 = 1.66666666666666657415e-01; + const pS0: f64 = 1.66666666666666657415e-01; const pS1: f64 = -3.25565818622400915405e-01; - const pS2: f64 = 2.01212532134862925881e-01; + const pS2: f64 = 2.01212532134862925881e-01; const pS3: f64 = -4.00555345006794114027e-02; - const pS4: f64 = 7.91534994289814532176e-04; - const pS5: f64 = 3.47933107596021167570e-05; + const pS4: f64 = 7.91534994289814532176e-04; + const pS5: f64 = 3.47933107596021167570e-05; const qS1: f64 = -2.40339491173441421878e+00; - const qS2: f64 = 2.02094576023350569471e+00; + const qS2: f64 = 2.02094576023350569471e+00; const qS3: f64 = -6.88283971605453293030e-01; - const qS4: f64 = 7.70381505559019352791e-02; + const qS4: f64 = 7.70381505559019352791e-02; const p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5))))); const q = 1.0 + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4))); diff --git a/std/math/atan2.zig b/std/math/atan2.zig index 37c520da46..0892f0b438 100644 --- a/std/math/atan2.zig +++ b/std/math/atan2.zig @@ -31,7 +31,7 @@ pub fn atan2(comptime T: type, x: T, y: T) T { } fn atan2_32(y: f32, x: f32) f32 { - const pi: f32 = 3.1415927410e+00; + const pi: f32 = 3.1415927410e+00; const pi_lo: f32 = -8.7422776573e-08; if (math.isNan(x) or math.isNan(y)) { @@ -53,9 +53,10 @@ fn atan2_32(y: f32, x: f32) f32 { if (iy == 0) { switch (m) { - 0, 1 => return y, // atan(+-0, +...) - 2 => return pi, // atan(+0, -...) - 3 => return -pi, // atan(-0, -...) + 0, + 1 => return y, // atan(+-0, +...) + 2 => return pi, // atan(+0, -...) + 3 => return -pi, // atan(-0, -...) else => unreachable, } } @@ -71,18 +72,18 @@ fn atan2_32(y: f32, x: f32) f32 { if (ix == 0x7F800000) { if (iy == 0x7F800000) { switch (m) { - 0 => return pi / 4, // atan(+inf, +inf) - 1 => return -pi / 4, // atan(-inf, +inf) - 2 => return 3*pi / 4, // atan(+inf, -inf) - 3 => return -3*pi / 4, // atan(-inf, -inf) + 0 => return pi / 4, // atan(+inf, +inf) + 1 => return -pi / 4, // atan(-inf, +inf) + 2 => return 3 * pi / 4, // atan(+inf, -inf) + 3 => return -3 * pi / 4, // atan(-inf, -inf) else => unreachable, } } else { switch (m) { - 0 => return 0.0, // atan(+..., +inf) - 1 => return -0.0, // atan(-..., +inf) - 2 => return pi, // atan(+..., -inf) - 3 => return -pi, // atan(-...f, -inf) + 0 => return 0.0, // atan(+..., +inf) + 1 => return -0.0, // atan(-..., +inf) + 2 => return pi, // atan(+..., -inf) + 3 => return -pi, // atan(-...f, -inf) else => unreachable, } } @@ -107,16 +108,16 @@ fn atan2_32(y: f32, x: f32) f32 { }; switch (m) { - 0 => return z, // atan(+, +) - 1 => return -z, // atan(-, +) - 2 => return pi - (z - pi_lo), // atan(+, -) - 3 => return (z - pi_lo) - pi, // atan(-, -) + 0 => return z, // atan(+, +) + 1 => return -z, // atan(-, +) + 2 => return pi - (z - pi_lo), // atan(+, -) + 3 => return (z - pi_lo) - pi, // atan(-, -) else => unreachable, } } fn atan2_64(y: f64, x: f64) f64 { - const pi: f64 = 3.1415926535897931160E+00; + const pi: f64 = 3.1415926535897931160E+00; const pi_lo: f64 = 1.2246467991473531772E-16; if (math.isNan(x) or math.isNan(y)) { @@ -143,9 +144,10 @@ fn atan2_64(y: f64, x: f64) f64 { if (iy | ly == 0) { switch (m) { - 0, 1 => return y, // atan(+-0, +...) - 2 => return pi, // atan(+0, -...) - 3 => return -pi, // atan(-0, -...) + 0, + 1 => return y, // atan(+-0, +...) + 2 => return pi, // atan(+0, -...) + 3 => return -pi, // atan(-0, -...) else => unreachable, } } @@ -161,18 +163,18 @@ fn atan2_64(y: f64, x: f64) f64 { if (ix == 0x7FF00000) { if (iy == 0x7FF00000) { switch (m) { - 0 => return pi / 4, // atan(+inf, +inf) - 1 => return -pi / 4, // atan(-inf, +inf) - 2 => return 3*pi / 4, // atan(+inf, -inf) - 3 => return -3*pi / 4, // atan(-inf, -inf) + 0 => return pi / 4, // atan(+inf, +inf) + 1 => return -pi / 4, // atan(-inf, +inf) + 2 => return 3 * pi / 4, // atan(+inf, -inf) + 3 => return -3 * pi / 4, // atan(-inf, -inf) else => unreachable, } } else { switch (m) { - 0 => return 0.0, // atan(+..., +inf) - 1 => return -0.0, // atan(-..., +inf) - 2 => return pi, // atan(+..., -inf) - 3 => return -pi, // atan(-...f, -inf) + 0 => return 0.0, // atan(+..., +inf) + 1 => return -0.0, // atan(-..., +inf) + 2 => return pi, // atan(+..., -inf) + 3 => return -pi, // atan(-...f, -inf) else => unreachable, } } @@ -197,10 +199,10 @@ fn atan2_64(y: f64, x: f64) f64 { }; switch (m) { - 0 => return z, // atan(+, +) - 1 => return -z, // atan(-, +) - 2 => return pi - (z - pi_lo), // atan(+, -) - 3 => return (z - pi_lo) - pi, // atan(-, -) + 0 => return z, // atan(+, +) + 1 => return -z, // atan(-, +) + 2 => return pi - (z - pi_lo), // atan(+, -) + 3 => return (z - pi_lo) - pi, // atan(-, -) else => unreachable, } } diff --git a/std/math/cbrt.zig b/std/math/cbrt.zig index a265392ff7..cd3b71ca8a 100644 --- a/std/math/cbrt.zig +++ b/std/math/cbrt.zig @@ -58,15 +58,15 @@ fn cbrt32(x: f32) f32 { } fn cbrt64(x: f64) f64 { - const B1: u32 = 715094163; // (1023 - 1023 / 3 - 0.03306235651 * 2^20 - const B2: u32 = 696219795; // (1023 - 1023 / 3 - 54 / 3 - 0.03306235651 * 2^20 + const B1: u32 = 715094163; // (1023 - 1023 / 3 - 0.03306235651 * 2^20 + const B2: u32 = 696219795; // (1023 - 1023 / 3 - 54 / 3 - 0.03306235651 * 2^20 // |1 / cbrt(x) - p(x)| < 2^(23.5) - const P0: f64 = 1.87595182427177009643; + const P0: f64 = 1.87595182427177009643; const P1: f64 = -1.88497979543377169875; - const P2: f64 = 1.621429720105354466140; + const P2: f64 = 1.621429720105354466140; const P3: f64 = -0.758397934778766047437; - const P4: f64 = 0.145996192886612446982; + const P4: f64 = 0.145996192886612446982; var u = @bitCast(u64, x); var hx = u32(u >> 32) & 0x7FFFFFFF; diff --git a/std/math/ceil.zig b/std/math/ceil.zig index 5bdb84ca00..a189bb66d2 100644 --- a/std/math/ceil.zig +++ b/std/math/ceil.zig @@ -56,7 +56,7 @@ fn ceil64(x: f64) f64 { const e = (u >> 52) & 0x7FF; var y: f64 = undefined; - if (e >= 0x3FF+52 or x == 0) { + if (e >= 0x3FF + 52 or x == 0) { return x; } @@ -68,7 +68,7 @@ fn ceil64(x: f64) f64 { y = x + math.f64_toint - math.f64_toint - x; } - if (e <= 0x3FF-1) { + if (e <= 0x3FF - 1) { math.forceEval(y); if (u >> 63 != 0) { return -0.0; diff --git a/std/math/cos.zig b/std/math/cos.zig index bb405b0d10..5e5ec4f1cb 100644 --- a/std/math/cos.zig +++ b/std/math/cos.zig @@ -18,20 +18,20 @@ pub fn cos(x: var) @typeOf(x) { } // sin polynomial coefficients -const S0 = 1.58962301576546568060E-10; +const S0 = 1.58962301576546568060E-10; const S1 = -2.50507477628578072866E-8; -const S2 = 2.75573136213857245213E-6; +const S2 = 2.75573136213857245213E-6; const S3 = -1.98412698295895385996E-4; -const S4 = 8.33333333332211858878E-3; +const S4 = 8.33333333332211858878E-3; const S5 = -1.66666666666666307295E-1; // cos polynomial coeffiecients const C0 = -1.13585365213876817300E-11; -const C1 = 2.08757008419747316778E-9; +const C1 = 2.08757008419747316778E-9; const C2 = -2.75573141792967388112E-7; -const C3 = 2.48015872888517045348E-5; +const C3 = 2.48015872888517045348E-5; const C4 = -1.38888888888730564116E-3; -const C5 = 4.16666666666665929218E-2; +const C5 = 4.16666666666665929218E-2; // NOTE: This is taken from the go stdlib. The musl implementation is much more complex. // diff --git a/std/math/floor.zig b/std/math/floor.zig index 1b8e2dfeed..7d5364787f 100644 --- a/std/math/floor.zig +++ b/std/math/floor.zig @@ -57,7 +57,7 @@ fn floor64(x: f64) f64 { const e = (u >> 52) & 0x7FF; var y: f64 = undefined; - if (e >= 0x3FF+52 or x == 0) { + if (e >= 0x3FF + 52 or x == 0) { return x; } @@ -69,7 +69,7 @@ fn floor64(x: f64) f64 { y = x + math.f64_toint - math.f64_toint - x; } - if (e <= 0x3FF-1) { + if (e <= 0x3FF - 1) { math.forceEval(y); if (u >> 63 != 0) { return -1.0; diff --git a/std/math/fma.zig b/std/math/fma.zig index e8d146db34..3e9214f35b 100644 --- a/std/math/fma.zig +++ b/std/math/fma.zig @@ -5,7 +5,7 @@ const assert = std.debug.assert; pub fn fma(comptime T: type, x: T, y: T, z: T) T { return switch (T) { f32 => fma32(x, y, z), - f64 => fma64(x, y ,z), + f64 => fma64(x, y, z), else => @compileError("fma not implemented for " ++ @typeName(T)), }; } @@ -71,7 +71,10 @@ fn fma64(x: f64, y: f64, z: f64) f64 { } } -const dd = struct { hi: f64, lo: f64, }; +const dd = struct { + hi: f64, + lo: f64, +}; fn dd_add(a: f64, b: f64) dd { var ret: dd = undefined; diff --git a/std/math/hypot.zig b/std/math/hypot.zig index 06427d0865..fe0de3a1ea 100644 --- a/std/math/hypot.zig +++ b/std/math/hypot.zig @@ -39,11 +39,11 @@ fn hypot32(x: f32, y: f32) f32 { } var z: f32 = 1.0; - if (ux >= (0x7F+60) << 23) { + if (ux >= (0x7F + 60) << 23) { z = 0x1.0p90; xx *= 0x1.0p-90; yy *= 0x1.0p-90; - } else if (uy < (0x7F-60) << 23) { + } else if (uy < (0x7F - 60) << 23) { z = 0x1.0p-90; xx *= 0x1.0p-90; yy *= 0x1.0p-90; @@ -57,8 +57,8 @@ fn sq(hi: &f64, lo: &f64, x: f64) void { const xc = x * split; const xh = x - xc + xc; const xl = x - xh; - *hi = x * x; - *lo = xh * xh - *hi + 2 * xh * xl + xl * xl; + hi.* = x * x; + lo.* = xh * xh - hi.* + 2 * xh * xl + xl * xl; } fn hypot64(x: f64, y: f64) f64 { diff --git a/std/math/ln.zig b/std/math/ln.zig index d09494b998..263e5955cb 100644 --- a/std/math/ln.zig +++ b/std/math/ln.zig @@ -120,11 +120,9 @@ pub fn ln_64(x_: f64) f64 { k -= 54; x *= 0x1.0p54; hx = u32(@bitCast(u64, ix) >> 32); - } - else if (hx >= 0x7FF00000) { + } else if (hx >= 0x7FF00000) { return x; - } - else if (hx == 0x3FF00000 and ix << 32 == 0) { + } else if (hx == 0x3FF00000 and ix << 32 == 0) { return 0; } diff --git a/std/math/log10.zig b/std/math/log10.zig index aa74caa901..d9fa1dcb02 100644 --- a/std/math/log10.zig +++ b/std/math/log10.zig @@ -35,10 +35,10 @@ pub fn log10(x: var) @typeOf(x) { } pub fn log10_32(x_: f32) f32 { - const ivln10hi: f32 = 4.3432617188e-01; - const ivln10lo: f32 = -3.1689971365e-05; - const log10_2hi: f32 = 3.0102920532e-01; - const log10_2lo: f32 = 7.9034151668e-07; + const ivln10hi: f32 = 4.3432617188e-01; + const ivln10lo: f32 = -3.1689971365e-05; + const log10_2hi: f32 = 3.0102920532e-01; + const log10_2lo: f32 = 7.9034151668e-07; const Lg1: f32 = 0xaaaaaa.0p-24; const Lg2: f32 = 0xccce13.0p-25; const Lg3: f32 = 0x91e9ee.0p-25; @@ -95,8 +95,8 @@ pub fn log10_32(x_: f32) f32 { } pub fn log10_64(x_: f64) f64 { - const ivln10hi: f64 = 4.34294481878168880939e-01; - const ivln10lo: f64 = 2.50829467116452752298e-11; + const ivln10hi: f64 = 4.34294481878168880939e-01; + const ivln10lo: f64 = 2.50829467116452752298e-11; const log10_2hi: f64 = 3.01029995663611771306e-01; const log10_2lo: f64 = 3.69423907715893078616e-13; const Lg1: f64 = 6.666666666666735130e-01; @@ -126,11 +126,9 @@ pub fn log10_64(x_: f64) f64 { k -= 54; x *= 0x1.0p54; hx = u32(@bitCast(u64, x) >> 32); - } - else if (hx >= 0x7FF00000) { + } else if (hx >= 0x7FF00000) { return x; - } - else if (hx == 0x3FF00000 and ix << 32 == 0) { + } else if (hx == 0x3FF00000 and ix << 32 == 0) { return 0; } diff --git a/std/math/log2.zig b/std/math/log2.zig index d5bbe385c2..22cc8082b3 100644 --- a/std/math/log2.zig +++ b/std/math/log2.zig @@ -27,7 +27,10 @@ pub fn log2(x: var) @typeOf(x) { TypeId.IntLiteral => comptime { var result = 0; var x_shifted = x; - while (b: {x_shifted >>= 1; break :b x_shifted != 0;}) : (result += 1) {} + while (b: { + x_shifted >>= 1; + break :b x_shifted != 0; + }) : (result += 1) {} return result; }, TypeId.Int => { @@ -38,7 +41,7 @@ pub fn log2(x: var) @typeOf(x) { } pub fn log2_32(x_: f32) f32 { - const ivln2hi: f32 = 1.4428710938e+00; + const ivln2hi: f32 = 1.4428710938e+00; const ivln2lo: f32 = -1.7605285393e-04; const Lg1: f32 = 0xaaaaaa.0p-24; const Lg2: f32 = 0xccce13.0p-25; diff --git a/std/math/round.zig b/std/math/round.zig index c16190da21..c8d9eb4fd4 100644 --- a/std/math/round.zig +++ b/std/math/round.zig @@ -24,13 +24,13 @@ fn round32(x_: f32) f32 { const e = (u >> 23) & 0xFF; var y: f32 = undefined; - if (e >= 0x7F+23) { + if (e >= 0x7F + 23) { return x; } if (u >> 31 != 0) { x = -x; } - if (e < 0x7F-1) { + if (e < 0x7F - 1) { math.forceEval(x + math.f32_toint); return 0 * @bitCast(f32, u); } @@ -61,13 +61,13 @@ fn round64(x_: f64) f64 { const e = (u >> 52) & 0x7FF; var y: f64 = undefined; - if (e >= 0x3FF+52) { + if (e >= 0x3FF + 52) { return x; } if (u >> 63 != 0) { x = -x; } - if (e < 0x3ff-1) { + if (e < 0x3ff - 1) { math.forceEval(x + math.f64_toint); return 0 * @bitCast(f64, u); } diff --git a/std/math/sin.zig b/std/math/sin.zig index 5dd869545b..21c324e444 100644 --- a/std/math/sin.zig +++ b/std/math/sin.zig @@ -19,20 +19,20 @@ pub fn sin(x: var) @typeOf(x) { } // sin polynomial coefficients -const S0 = 1.58962301576546568060E-10; +const S0 = 1.58962301576546568060E-10; const S1 = -2.50507477628578072866E-8; -const S2 = 2.75573136213857245213E-6; +const S2 = 2.75573136213857245213E-6; const S3 = -1.98412698295895385996E-4; -const S4 = 8.33333333332211858878E-3; +const S4 = 8.33333333332211858878E-3; const S5 = -1.66666666666666307295E-1; // cos polynomial coeffiecients const C0 = -1.13585365213876817300E-11; -const C1 = 2.08757008419747316778E-9; +const C1 = 2.08757008419747316778E-9; const C2 = -2.75573141792967388112E-7; -const C3 = 2.48015872888517045348E-5; +const C3 = 2.48015872888517045348E-5; const C4 = -1.38888888888730564116E-3; -const C5 = 4.16666666666665929218E-2; +const C5 = 4.16666666666665929218E-2; // NOTE: This is taken from the go stdlib. The musl implementation is much more complex. // diff --git a/std/math/tan.zig b/std/math/tan.zig index 11428b6e8b..f578cf8156 100644 --- a/std/math/tan.zig +++ b/std/math/tan.zig @@ -19,12 +19,12 @@ pub fn tan(x: var) @typeOf(x) { } const Tp0 = -1.30936939181383777646E4; -const Tp1 = 1.15351664838587416140E6; +const Tp1 = 1.15351664838587416140E6; const Tp2 = -1.79565251976484877988E7; -const Tq1 = 1.36812963470692954678E4; +const Tq1 = 1.36812963470692954678E4; const Tq2 = -1.32089234440210967447E6; -const Tq3 = 2.50083801823357915839E7; +const Tq3 = 2.50083801823357915839E7; const Tq4 = -5.38695755929454629881E7; // NOTE: This is taken from the go stdlib. The musl implementation is much more complex. diff --git a/std/net.zig b/std/net.zig index 8e1b8d97b2..b1e291ab92 100644 --- a/std/net.zig +++ b/std/net.zig @@ -19,37 +19,29 @@ pub const Address = struct { os_addr: OsAddress, pub fn initIp4(ip4: u32, port: u16) Address { - return Address { - .os_addr = posix.sockaddr { - .in = posix.sockaddr_in { - .family = posix.AF_INET, - .port = std.mem.endianSwapIfLe(u16, port), - .addr = ip4, - .zero = []u8{0} ** 8, - }, - }, - }; + return Address{ .os_addr = posix.sockaddr{ .in = posix.sockaddr_in{ + .family = posix.AF_INET, + .port = std.mem.endianSwapIfLe(u16, port), + .addr = ip4, + .zero = []u8{0} ** 8, + } } }; } pub fn initIp6(ip6: &const Ip6Addr, port: u16) Address { - return Address { + return Address{ .family = posix.AF_INET6, - .os_addr = posix.sockaddr { - .in6 = posix.sockaddr_in6 { - .family = posix.AF_INET6, - .port = std.mem.endianSwapIfLe(u16, port), - .flowinfo = 0, - .addr = ip6.addr, - .scope_id = ip6.scope_id, - }, - }, + .os_addr = posix.sockaddr{ .in6 = posix.sockaddr_in6{ + .family = posix.AF_INET6, + .port = std.mem.endianSwapIfLe(u16, port), + .flowinfo = 0, + .addr = ip6.addr, + .scope_id = ip6.scope_id, + } }, }; } pub fn initPosix(addr: &const posix.sockaddr) Address { - return Address { - .os_addr = *addr, - }; + return Address{ .os_addr = addr.* }; } pub fn format(self: &const Address, out_stream: var) !void { @@ -98,7 +90,7 @@ pub fn parseIp4(buf: []const u8) !u32 { } } else { return error.InvalidCharacter; - } + } } if (index == 3 and saw_any_digits) { out_ptr[index] = x; diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 8bb8b2d7e7..6c0c21f64a 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -49,7 +49,7 @@ pub const ChildProcess = struct { err_pipe: if (is_windows) void else [2]i32, llnode: if (is_windows) void else LinkedList(&ChildProcess).Node, - pub const SpawnError = error { + pub const SpawnError = error{ ProcessFdQuotaExceeded, Unexpected, NotDir, @@ -88,7 +88,7 @@ pub const ChildProcess = struct { const child = try allocator.create(ChildProcess); errdefer allocator.destroy(child); - *child = ChildProcess { + child.* = ChildProcess{ .allocator = allocator, .argv = argv, .pid = undefined, @@ -99,8 +99,10 @@ pub const ChildProcess = struct { .term = null, .env_map = null, .cwd = null, - .uid = if (is_windows) {} else null, - .gid = if (is_windows) {} else null, + .uid = if (is_windows) {} else + null, + .gid = if (is_windows) {} else + null, .stdin = null, .stdout = null, .stderr = null, @@ -193,9 +195,7 @@ pub const ChildProcess = struct { /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. /// If it succeeds, the caller owns result.stdout and result.stderr memory. - pub fn exec(allocator: &mem.Allocator, argv: []const []const u8, cwd: ?[]const u8, - env_map: ?&const BufMap, max_output_size: usize) !ExecResult - { + pub fn exec(allocator: &mem.Allocator, argv: []const []const u8, cwd: ?[]const u8, env_map: ?&const BufMap, max_output_size: usize) !ExecResult { const child = try ChildProcess.init(argv, allocator); defer child.deinit(); @@ -218,7 +218,7 @@ pub const ChildProcess = struct { try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size); try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size); - return ExecResult { + return ExecResult{ .term = try child.wait(), .stdout = stdout.toOwnedSlice(), .stderr = stderr.toOwnedSlice(), @@ -255,9 +255,9 @@ pub const ChildProcess = struct { self.term = (SpawnError!Term)(x: { var exit_code: windows.DWORD = undefined; if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) { - break :x Term { .Unknown = 0 }; + break :x Term{ .Unknown = 0 }; } else { - break :x Term { .Exited = @bitCast(i32, exit_code)}; + break :x Term{ .Exited = @bitCast(i32, exit_code) }; } }); @@ -288,9 +288,18 @@ pub const ChildProcess = struct { } fn cleanupStreams(self: &ChildProcess) void { - if (self.stdin) |*stdin| { stdin.close(); self.stdin = null; } - if (self.stdout) |*stdout| { stdout.close(); self.stdout = null; } - if (self.stderr) |*stderr| { stderr.close(); self.stderr = null; } + if (self.stdin) |*stdin| { + stdin.close(); + self.stdin = null; + } + if (self.stdout) |*stdout| { + stdout.close(); + self.stdout = null; + } + if (self.stderr) |*stderr| { + stderr.close(); + self.stderr = null; + } } fn cleanupAfterWait(self: &ChildProcess, status: i32) !Term { @@ -317,25 +326,30 @@ pub const ChildProcess = struct { fn statusToTerm(status: i32) Term { return if (posix.WIFEXITED(status)) - Term { .Exited = posix.WEXITSTATUS(status) } + Term{ .Exited = posix.WEXITSTATUS(status) } else if (posix.WIFSIGNALED(status)) - Term { .Signal = posix.WTERMSIG(status) } + Term{ .Signal = posix.WTERMSIG(status) } else if (posix.WIFSTOPPED(status)) - Term { .Stopped = posix.WSTOPSIG(status) } + Term{ .Stopped = posix.WSTOPSIG(status) } else - Term { .Unknown = status } - ; + Term{ .Unknown = status }; } fn spawnPosix(self: &ChildProcess) !void { const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try makePipe() else undefined; - errdefer if (self.stdin_behavior == StdIo.Pipe) { destroyPipe(stdin_pipe); }; + errdefer if (self.stdin_behavior == StdIo.Pipe) { + destroyPipe(stdin_pipe); + }; const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try makePipe() else undefined; - errdefer if (self.stdout_behavior == StdIo.Pipe) { destroyPipe(stdout_pipe); }; + errdefer if (self.stdout_behavior == StdIo.Pipe) { + destroyPipe(stdout_pipe); + }; const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try makePipe() else undefined; - errdefer if (self.stderr_behavior == StdIo.Pipe) { destroyPipe(stderr_pipe); }; + errdefer if (self.stderr_behavior == StdIo.Pipe) { + destroyPipe(stderr_pipe); + }; const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); const dev_null_fd = if (any_ignore) blk: { @@ -346,7 +360,9 @@ pub const ChildProcess = struct { } else blk: { break :blk undefined; }; - defer { if (any_ignore) os.close(dev_null_fd); } + defer { + if (any_ignore) os.close(dev_null_fd); + } var env_map_owned: BufMap = undefined; var we_own_env_map: bool = undefined; @@ -358,7 +374,9 @@ pub const ChildProcess = struct { env_map_owned = try os.getEnvMap(self.allocator); break :x &env_map_owned; }; - defer { if (we_own_env_map) env_map_owned.deinit(); } + defer { + if (we_own_env_map) env_map_owned.deinit(); + } // This pipe is used to communicate errors between the time of fork // and execve from the child process to the parent process. @@ -369,23 +387,21 @@ pub const ChildProcess = struct { const pid_err = posix.getErrno(pid_result); if (pid_err > 0) { return switch (pid_err) { - posix.EAGAIN, posix.ENOMEM, posix.ENOSYS => error.SystemResources, + posix.EAGAIN, + posix.ENOMEM, + posix.ENOSYS => error.SystemResources, else => os.unexpectedErrorPosix(pid_err), }; } if (pid_result == 0) { // we are the child - setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch - |err| forkChildErrReport(err_pipe[1], err); - setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch - |err| forkChildErrReport(err_pipe[1], err); - setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch - |err| forkChildErrReport(err_pipe[1], err); + setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); + setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); + setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); if (self.cwd) |cwd| { - os.changeCurDir(self.allocator, cwd) catch - |err| forkChildErrReport(err_pipe[1], err); + os.changeCurDir(self.allocator, cwd) catch |err| forkChildErrReport(err_pipe[1], err); } if (self.gid) |gid| { @@ -396,8 +412,7 @@ pub const ChildProcess = struct { os.posix_setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err); } - os.posixExecve(self.argv, env_map, self.allocator) catch - |err| forkChildErrReport(err_pipe[1], err); + os.posixExecve(self.argv, env_map, self.allocator) catch |err| forkChildErrReport(err_pipe[1], err); } // we are the parent @@ -423,37 +438,41 @@ pub const ChildProcess = struct { self.llnode = LinkedList(&ChildProcess).Node.init(self); self.term = null; - if (self.stdin_behavior == StdIo.Pipe) { os.close(stdin_pipe[0]); } - if (self.stdout_behavior == StdIo.Pipe) { os.close(stdout_pipe[1]); } - if (self.stderr_behavior == StdIo.Pipe) { os.close(stderr_pipe[1]); } + if (self.stdin_behavior == StdIo.Pipe) { + os.close(stdin_pipe[0]); + } + if (self.stdout_behavior == StdIo.Pipe) { + os.close(stdout_pipe[1]); + } + if (self.stderr_behavior == StdIo.Pipe) { + os.close(stderr_pipe[1]); + } } fn spawnWindows(self: &ChildProcess) !void { - const saAttr = windows.SECURITY_ATTRIBUTES { + const saAttr = windows.SECURITY_ATTRIBUTES{ .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), .bInheritHandle = windows.TRUE, .lpSecurityDescriptor = null, }; - const any_ignore = (self.stdin_behavior == StdIo.Ignore or - self.stdout_behavior == StdIo.Ignore or - self.stderr_behavior == StdIo.Ignore); + const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); const nul_handle = if (any_ignore) blk: { const nul_file_path = "NUL"; var fixed_buffer_mem: [nul_file_path.len + 1]u8 = undefined; var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - break :blk try os.windowsOpen(&fixed_allocator.allocator, "NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, - windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL); + break :blk try os.windowsOpen(&fixed_allocator.allocator, "NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL); } else blk: { break :blk undefined; }; - defer { if (any_ignore) os.close(nul_handle); } + defer { + if (any_ignore) os.close(nul_handle); + } if (any_ignore) { try windowsSetHandleInfo(nul_handle, windows.HANDLE_FLAG_INHERIT, 0); } - var g_hChildStd_IN_Rd: ?windows.HANDLE = null; var g_hChildStd_IN_Wr: ?windows.HANDLE = null; switch (self.stdin_behavior) { @@ -470,7 +489,9 @@ pub const ChildProcess = struct { g_hChildStd_IN_Rd = null; }, } - errdefer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr); }; + errdefer if (self.stdin_behavior == StdIo.Pipe) { + windowsDestroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr); + }; var g_hChildStd_OUT_Rd: ?windows.HANDLE = null; var g_hChildStd_OUT_Wr: ?windows.HANDLE = null; @@ -488,7 +509,9 @@ pub const ChildProcess = struct { g_hChildStd_OUT_Wr = null; }, } - errdefer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr); }; + errdefer if (self.stdin_behavior == StdIo.Pipe) { + windowsDestroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr); + }; var g_hChildStd_ERR_Rd: ?windows.HANDLE = null; var g_hChildStd_ERR_Wr: ?windows.HANDLE = null; @@ -506,12 +529,14 @@ pub const ChildProcess = struct { g_hChildStd_ERR_Wr = null; }, } - errdefer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); }; + errdefer if (self.stdin_behavior == StdIo.Pipe) { + windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); + }; const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv); defer self.allocator.free(cmd_line); - var siStartInfo = windows.STARTUPINFOA { + var siStartInfo = windows.STARTUPINFOA{ .cb = @sizeOf(windows.STARTUPINFOA), .hStdError = g_hChildStd_ERR_Wr, .hStdOutput = g_hChildStd_OUT_Wr, @@ -534,19 +559,11 @@ pub const ChildProcess = struct { }; var piProcInfo: windows.PROCESS_INFORMATION = undefined; - const cwd_slice = if (self.cwd) |cwd| - try cstr.addNullByte(self.allocator, cwd) - else - null - ; + const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null; defer if (cwd_slice) |cwd| self.allocator.free(cwd); const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null; - const maybe_envp_buf = if (self.env_map) |env_map| - try os.createWindowsEnvBlock(self.allocator, env_map) - else - null - ; + const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null; defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf); const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null; @@ -563,11 +580,8 @@ pub const ChildProcess = struct { }; defer self.allocator.free(app_name); - windowsCreateProcess(app_name.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, - &siStartInfo, &piProcInfo) catch |no_path_err| - { - if (no_path_err != error.FileNotFound) - return no_path_err; + windowsCreateProcess(app_name.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| { + if (no_path_err != error.FileNotFound) return no_path_err; const PATH = try os.getEnvVarOwned(self.allocator, "PATH"); defer self.allocator.free(PATH); @@ -577,9 +591,7 @@ pub const ChildProcess = struct { const joined_path = try os.path.join(self.allocator, search_path, app_name); defer self.allocator.free(joined_path); - if (windowsCreateProcess(joined_path.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, - &siStartInfo, &piProcInfo)) |_| - { + if (windowsCreateProcess(joined_path.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo)) |_| { break; } else |err| if (err == error.FileNotFound) { continue; @@ -609,9 +621,15 @@ pub const ChildProcess = struct { self.thread_handle = piProcInfo.hThread; self.term = null; - if (self.stdin_behavior == StdIo.Pipe) { os.close(??g_hChildStd_IN_Rd); } - if (self.stderr_behavior == StdIo.Pipe) { os.close(??g_hChildStd_ERR_Wr); } - if (self.stdout_behavior == StdIo.Pipe) { os.close(??g_hChildStd_OUT_Wr); } + if (self.stdin_behavior == StdIo.Pipe) { + os.close(??g_hChildStd_IN_Rd); + } + if (self.stderr_behavior == StdIo.Pipe) { + os.close(??g_hChildStd_ERR_Wr); + } + if (self.stdout_behavior == StdIo.Pipe) { + os.close(??g_hChildStd_OUT_Wr); + } } fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void { @@ -622,18 +640,14 @@ pub const ChildProcess = struct { StdIo.Ignore => try os.posixDup2(dev_null_fd, std_fileno), } } - }; -fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?&u8, - lpStartupInfo: &windows.STARTUPINFOA, lpProcessInformation: &windows.PROCESS_INFORMATION) !void -{ - if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, - @ptrCast(?&c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) - { +fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?&u8, lpStartupInfo: &windows.STARTUPINFOA, lpProcessInformation: &windows.PROCESS_INFORMATION) !void { + if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?&c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) { const err = windows.GetLastError(); return switch (err) { - windows.ERROR.FILE_NOT_FOUND, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, + windows.ERROR.FILE_NOT_FOUND, + windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, windows.ERROR.INVALID_PARAMETER => unreachable, windows.ERROR.INVALID_NAME => error.InvalidName, else => os.unexpectedErrorWindows(err), @@ -641,9 +655,6 @@ fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ? } } - - - /// Caller must dealloc. /// Guarantees a null byte at result[result.len]. fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) ![]u8 { @@ -651,8 +662,7 @@ fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) defer buf.deinit(); for (argv) |arg, arg_i| { - if (arg_i != 0) - try buf.appendByte(' '); + if (arg_i != 0) try buf.appendByte(' '); if (mem.indexOfAny(u8, arg, " \t\n\"") == null) { try buf.append(arg); continue; @@ -686,7 +696,6 @@ fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { if (wr) |h| os.close(h); } - // TODO: workaround for bug where the `const` from `&const` is dropped when the type is // a namespace field lookup const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES; @@ -715,8 +724,8 @@ fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const S try windowsMakePipe(&rd_h, &wr_h, sattr); errdefer windowsDestroyPipe(rd_h, wr_h); try windowsSetHandleInfo(wr_h, windows.HANDLE_FLAG_INHERIT, 0); - *rd = rd_h; - *wr = wr_h; + rd.* = rd_h; + wr.* = wr_h; } fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void { @@ -725,8 +734,8 @@ fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const try windowsMakePipe(&rd_h, &wr_h, sattr); errdefer windowsDestroyPipe(rd_h, wr_h); try windowsSetHandleInfo(rd_h, windows.HANDLE_FLAG_INHERIT, 0); - *rd = rd_h; - *wr = wr_h; + rd.* = rd_h; + wr.* = wr_h; } fn makePipe() ![2]i32 { @@ -734,7 +743,8 @@ fn makePipe() ![2]i32 { const err = posix.getErrno(posix.pipe(&fds)); if (err > 0) { return switch (err) { - posix.EMFILE, posix.ENFILE => error.SystemResources, + posix.EMFILE, + posix.ENFILE => error.SystemResources, else => os.unexpectedErrorPosix(err), }; } @@ -742,8 +752,8 @@ fn makePipe() ![2]i32 { } fn destroyPipe(pipe: &const [2]i32) void { - os.close((*pipe)[0]); - os.close((*pipe)[1]); + os.close((pipe.*)[0]); + os.close((pipe.*)[1]); } // Child of fork calls this to report an error to the fork parent. diff --git a/std/segmented_list.zig b/std/segmented_list.zig index 6c7c879919..e9abc7adf9 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -93,7 +93,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type /// Deinitialize with `deinit` pub fn init(allocator: &Allocator) Self { - return Self { + return Self{ .allocator = allocator, .len = 0, .prealloc_segment = undefined, @@ -104,7 +104,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type pub fn deinit(self: &Self) void { self.freeShelves(ShelfIndex(self.dynamic_segments.len), 0); self.allocator.free(self.dynamic_segments); - *self = undefined; + self.* = undefined; } pub fn at(self: &Self, i: usize) &T { @@ -118,7 +118,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type pub fn push(self: &Self, item: &const T) !void { const new_item_ptr = try self.addOne(); - *new_item_ptr = *item; + new_item_ptr.* = item.*; } pub fn pushMany(self: &Self, items: []const T) !void { @@ -128,11 +128,10 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } pub fn pop(self: &Self) ?T { - if (self.len == 0) - return null; + if (self.len == 0) return null; const index = self.len - 1; - const result = *self.uncheckedAt(index); + const result = self.uncheckedAt(index).*; self.len = index; return result; } @@ -245,8 +244,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type shelf_size: usize, pub fn next(it: &Iterator) ?&T { - if (it.index >= it.list.len) - return null; + if (it.index >= it.list.len) return null; if (it.index < prealloc_item_count) { const ptr = &it.list.prealloc_segment[it.index]; it.index += 1; @@ -270,12 +268,10 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } pub fn prev(it: &Iterator) ?&T { - if (it.index == 0) - return null; + if (it.index == 0) return null; it.index -= 1; - if (it.index < prealloc_item_count) - return &it.list.prealloc_segment[it.index]; + if (it.index < prealloc_item_count) return &it.list.prealloc_segment[it.index]; if (it.box_index == 0) { it.shelf_index -= 1; @@ -290,7 +286,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type }; pub fn iterator(self: &Self, start_index: usize) Iterator { - var it = Iterator { + var it = Iterator{ .list = self, .index = start_index, .shelf_index = undefined, @@ -324,25 +320,31 @@ fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void { var list = SegmentedList(i32, prealloc).init(allocator); defer list.deinit(); - {var i: usize = 0; while (i < 100) : (i += 1) { - try list.push(i32(i + 1)); - assert(list.len == i + 1); - }} + { + var i: usize = 0; + while (i < 100) : (i += 1) { + try list.push(i32(i + 1)); + assert(list.len == i + 1); + } + } - {var i: usize = 0; while (i < 100) : (i += 1) { - assert(*list.at(i) == i32(i + 1)); - }} + { + var i: usize = 0; + while (i < 100) : (i += 1) { + assert(list.at(i).* == i32(i + 1)); + } + } { var it = list.iterator(0); var x: i32 = 0; while (it.next()) |item| { x += 1; - assert(*item == x); + assert(item.* == x); } assert(x == 100); while (it.prev()) |item| : (x -= 1) { - assert(*item == x); + assert(item.* == x); } assert(x == 0); } @@ -350,14 +352,18 @@ fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void { assert(??list.pop() == 100); assert(list.len == 99); - try list.pushMany([]i32 { 1, 2, 3 }); + try list.pushMany([]i32{ + 1, + 2, + 3, + }); assert(list.len == 102); assert(??list.pop() == 3); assert(??list.pop() == 2); assert(??list.pop() == 1); assert(list.len == 99); - try list.pushMany([]const i32 {}); + try list.pushMany([]const i32{}); assert(list.len == 99); var i: i32 = 99; diff --git a/std/sort.zig b/std/sort.zig index 0f83df7bb4..4cc7ad503a 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -5,15 +5,18 @@ const math = std.math; const builtin = @import("builtin"); /// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required). -pub fn insertionSort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T)bool) void { - {var i: usize = 1; while (i < items.len) : (i += 1) { - const x = items[i]; - var j: usize = i; - while (j > 0 and lessThan(x, items[j - 1])) : (j -= 1) { - items[j] = items[j - 1]; +pub fn insertionSort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T) bool) void { + { + var i: usize = 1; + while (i < items.len) : (i += 1) { + const x = items[i]; + var j: usize = i; + while (j > 0 and lessThan(x, items[j - 1])) : (j -= 1) { + items[j] = items[j - 1]; + } + items[j] = x; } - items[j] = x; - }} + } } const Range = struct { @@ -21,7 +24,10 @@ const Range = struct { end: usize, fn init(start: usize, end: usize) Range { - return Range { .start = start, .end = end }; + return Range{ + .start = start, + .end = end, + }; } fn length(self: &const Range) usize { @@ -29,7 +35,6 @@ const Range = struct { } }; - const Iterator = struct { size: usize, power_of_two: usize, @@ -42,7 +47,7 @@ const Iterator = struct { fn init(size2: usize, min_level: usize) Iterator { const power_of_two = math.floorPowerOfTwo(usize, size2); const denominator = power_of_two / min_level; - return Iterator { + return Iterator{ .numerator = 0, .decimal = 0, .size = size2, @@ -68,7 +73,10 @@ const Iterator = struct { self.decimal += 1; } - return Range {.start = start, .end = self.decimal}; + return Range{ + .start = start, + .end = self.decimal, + }; } fn finished(self: &Iterator) bool { @@ -100,7 +108,7 @@ const Pull = struct { /// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required). /// Currently implemented as block sort. -pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T)bool) void { +pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T) bool) void { // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c var cache: [512]T = undefined; @@ -123,7 +131,16 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons // http://pages.ripco.net/~jgamble/nw.html var iterator = Iterator.init(items.len, 4); while (!iterator.finished()) { - var order = []u8{0, 1, 2, 3, 4, 5, 6, 7}; + var order = []u8{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + }; const range = iterator.nextRange(); const sliced_items = items[range.start..]; @@ -149,56 +166,56 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons swap(T, sliced_items, lessThan, &order, 3, 5); swap(T, sliced_items, lessThan, &order, 3, 4); }, - 7 => { - swap(T, sliced_items, lessThan, &order, 1, 2); - swap(T, sliced_items, lessThan, &order, 3, 4); - swap(T, sliced_items, lessThan, &order, 5, 6); - swap(T, sliced_items, lessThan, &order, 0, 2); - swap(T, sliced_items, lessThan, &order, 3, 5); - swap(T, sliced_items, lessThan, &order, 4, 6); - swap(T, sliced_items, lessThan, &order, 0, 1); - swap(T, sliced_items, lessThan, &order, 4, 5); - swap(T, sliced_items, lessThan, &order, 2, 6); - swap(T, sliced_items, lessThan, &order, 0, 4); - swap(T, sliced_items, lessThan, &order, 1, 5); - swap(T, sliced_items, lessThan, &order, 0, 3); - swap(T, sliced_items, lessThan, &order, 2, 5); - swap(T, sliced_items, lessThan, &order, 1, 3); - swap(T, sliced_items, lessThan, &order, 2, 4); - swap(T, sliced_items, lessThan, &order, 2, 3); - }, - 6 => { - swap(T, sliced_items, lessThan, &order, 1, 2); - swap(T, sliced_items, lessThan, &order, 4, 5); - swap(T, sliced_items, lessThan, &order, 0, 2); - swap(T, sliced_items, lessThan, &order, 3, 5); - swap(T, sliced_items, lessThan, &order, 0, 1); - swap(T, sliced_items, lessThan, &order, 3, 4); - swap(T, sliced_items, lessThan, &order, 2, 5); - swap(T, sliced_items, lessThan, &order, 0, 3); - swap(T, sliced_items, lessThan, &order, 1, 4); - swap(T, sliced_items, lessThan, &order, 2, 4); - swap(T, sliced_items, lessThan, &order, 1, 3); - swap(T, sliced_items, lessThan, &order, 2, 3); - }, - 5 => { - swap(T, sliced_items, lessThan, &order, 0, 1); - swap(T, sliced_items, lessThan, &order, 3, 4); - swap(T, sliced_items, lessThan, &order, 2, 4); - swap(T, sliced_items, lessThan, &order, 2, 3); - swap(T, sliced_items, lessThan, &order, 1, 4); - swap(T, sliced_items, lessThan, &order, 0, 3); - swap(T, sliced_items, lessThan, &order, 0, 2); - swap(T, sliced_items, lessThan, &order, 1, 3); - swap(T, sliced_items, lessThan, &order, 1, 2); - }, - 4 => { - swap(T, sliced_items, lessThan, &order, 0, 1); - swap(T, sliced_items, lessThan, &order, 2, 3); - swap(T, sliced_items, lessThan, &order, 0, 2); - swap(T, sliced_items, lessThan, &order, 1, 3); - swap(T, sliced_items, lessThan, &order, 1, 2); - }, + 7 => { + swap(T, sliced_items, lessThan, &order, 1, 2); + swap(T, sliced_items, lessThan, &order, 3, 4); + swap(T, sliced_items, lessThan, &order, 5, 6); + swap(T, sliced_items, lessThan, &order, 0, 2); + swap(T, sliced_items, lessThan, &order, 3, 5); + swap(T, sliced_items, lessThan, &order, 4, 6); + swap(T, sliced_items, lessThan, &order, 0, 1); + swap(T, sliced_items, lessThan, &order, 4, 5); + swap(T, sliced_items, lessThan, &order, 2, 6); + swap(T, sliced_items, lessThan, &order, 0, 4); + swap(T, sliced_items, lessThan, &order, 1, 5); + swap(T, sliced_items, lessThan, &order, 0, 3); + swap(T, sliced_items, lessThan, &order, 2, 5); + swap(T, sliced_items, lessThan, &order, 1, 3); + swap(T, sliced_items, lessThan, &order, 2, 4); + swap(T, sliced_items, lessThan, &order, 2, 3); + }, + 6 => { + swap(T, sliced_items, lessThan, &order, 1, 2); + swap(T, sliced_items, lessThan, &order, 4, 5); + swap(T, sliced_items, lessThan, &order, 0, 2); + swap(T, sliced_items, lessThan, &order, 3, 5); + swap(T, sliced_items, lessThan, &order, 0, 1); + swap(T, sliced_items, lessThan, &order, 3, 4); + swap(T, sliced_items, lessThan, &order, 2, 5); + swap(T, sliced_items, lessThan, &order, 0, 3); + swap(T, sliced_items, lessThan, &order, 1, 4); + swap(T, sliced_items, lessThan, &order, 2, 4); + swap(T, sliced_items, lessThan, &order, 1, 3); + swap(T, sliced_items, lessThan, &order, 2, 3); + }, + 5 => { + swap(T, sliced_items, lessThan, &order, 0, 1); + swap(T, sliced_items, lessThan, &order, 3, 4); + swap(T, sliced_items, lessThan, &order, 2, 4); + swap(T, sliced_items, lessThan, &order, 2, 3); + swap(T, sliced_items, lessThan, &order, 1, 4); + swap(T, sliced_items, lessThan, &order, 0, 3); + swap(T, sliced_items, lessThan, &order, 0, 2); + swap(T, sliced_items, lessThan, &order, 1, 3); + swap(T, sliced_items, lessThan, &order, 1, 2); + }, + 4 => { + swap(T, sliced_items, lessThan, &order, 0, 1); + swap(T, sliced_items, lessThan, &order, 2, 3); + swap(T, sliced_items, lessThan, &order, 0, 2); + swap(T, sliced_items, lessThan, &order, 1, 3); + swap(T, sliced_items, lessThan, &order, 1, 2); + }, else => {}, } } @@ -273,7 +290,6 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons // we merged two levels at the same time, so we're done with this level already // (iterator.nextLevel() is called again at the bottom of this outer merge loop) _ = iterator.nextLevel(); - } else { iterator.begin(); while (!iterator.finished()) { @@ -303,7 +319,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons // 8. redistribute the two internal buffers back into the items var block_size: usize = math.sqrt(iterator.length()); - var buffer_size = iterator.length()/block_size + 1; + var buffer_size = iterator.length() / block_size + 1; // as an optimization, we really only need to pull out the internal buffers once for each level of merges // after that we can reuse the same buffers over and over, then redistribute it when we're finished with this level @@ -316,8 +332,18 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons var start: usize = 0; var pull_index: usize = 0; var pull = []Pull{ - Pull {.from = 0, .to = 0, .count = 0, .range = Range.init(0, 0),}, - Pull {.from = 0, .to = 0, .count = 0, .range = Range.init(0, 0),}, + Pull{ + .from = 0, + .to = 0, + .count = 0, + .range = Range.init(0, 0), + }, + Pull{ + .from = 0, + .to = 0, + .count = 0, + .range = Range.init(0, 0), + }, }; var buffer1 = Range.init(0, 0); @@ -355,7 +381,10 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons // these values will be pulled out to the start of A last = A.start; count = 1; - while (count < find) : ({last = index; count += 1;}) { + while (count < find) : ({ + last = index; + count += 1; + }) { index = findLastForward(T, items, items[last], Range.init(last + 1, A.end), lessThan, find - count); if (index == A.end) break; } @@ -363,7 +392,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons if (count >= buffer_size) { // keep track of the range within the items where we'll need to "pull out" these values to create the internal buffer - pull[pull_index] = Pull { + pull[pull_index] = Pull{ .range = Range.init(A.start, B.end), .count = count, .from = index, @@ -398,7 +427,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons } else if (pull_index == 0 and count > buffer1.length()) { // keep track of the largest buffer we were able to find buffer1 = Range.init(A.start, A.start + count); - pull[pull_index] = Pull { + pull[pull_index] = Pull{ .range = Range.init(A.start, B.end), .count = count, .from = index, @@ -410,7 +439,10 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons // these values will be pulled out to the end of B last = B.end - 1; count = 1; - while (count < find) : ({last = index - 1; count += 1;}) { + while (count < find) : ({ + last = index - 1; + count += 1; + }) { index = findFirstBackward(T, items, items[last], Range.init(B.start, last), lessThan, find - count); if (index == B.start) break; } @@ -418,7 +450,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons if (count >= buffer_size) { // keep track of the range within the items where we'll need to "pull out" these values to create the internal buffe - pull[pull_index] = Pull { + pull[pull_index] = Pull{ .range = Range.init(A.start, B.end), .count = count, .from = index, @@ -457,7 +489,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons } else if (pull_index == 0 and count > buffer1.length()) { // keep track of the largest buffer we were able to find buffer1 = Range.init(B.end - count, B.end); - pull[pull_index] = Pull { + pull[pull_index] = Pull{ .range = Range.init(A.start, B.end), .count = count, .from = index, @@ -496,7 +528,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons // adjust block_size and buffer_size based on the values we were able to pull out buffer_size = buffer1.length(); - block_size = iterator.length()/buffer_size + 1; + block_size = iterator.length() / buffer_size + 1; // the first buffer NEEDS to be large enough to tag each of the evenly sized A blocks, // so this was originally here to test the math for adjusting block_size above @@ -547,7 +579,10 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons // swap the first value of each A block with the value in buffer1 var indexA = buffer1.start; index = firstA.end; - while (index < blockA.end) : ({indexA += 1; index += block_size;}) { + while (index < blockA.end) : ({ + indexA += 1; + index += block_size; + }) { mem.swap(T, &items[indexA], &items[index]); } @@ -626,9 +661,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons // if there are no more A blocks remaining, this step is finished! blockA.start += block_size; - if (blockA.length() == 0) - break; - + if (blockA.length() == 0) break; } else if (blockB.length() < block_size) { // move the last B block, which is unevenly sized, to before the remaining A blocks, by using a rotation // the cache is disabled here since it might contain the contents of the previous A block @@ -709,7 +742,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons } // merge operation without a buffer -fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const Range, lessThan: fn(&const T,&const T)bool) void { +fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const Range, lessThan: fn(&const T, &const T) bool) void { if (A_arg.length() == 0 or B_arg.length() == 0) return; // this just repeatedly binary searches into B and rotates A into position. @@ -730,8 +763,8 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const // again, this is NOT a general-purpose solution – it only works well in this case! // kind of like how the O(n^2) insertion sort is used in some places - var A = *A_arg; - var B = *B_arg; + var A = A_arg.*; + var B = B_arg.*; while (true) { // find the first place in B where the first item in A needs to be inserted @@ -751,7 +784,7 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const } // merge operation using an internal buffer -fn mergeInternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn(&const T,&const T)bool, buffer: &const Range) void { +fn mergeInternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn(&const T, &const T) bool, buffer: &const Range) void { // whenever we find a value to add to the final array, swap it with the value that's already in that spot // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order var A_count: usize = 0; @@ -787,9 +820,9 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s // combine a linear search with a binary search to reduce the number of comparisons in situations // where have some idea as to how many unique values there are and where the next value might be -fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool, unique: usize) usize { +fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T, &const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; - const skip = math.max(range.length()/unique, usize(1)); + const skip = math.max(range.length() / unique, usize(1)); var index = range.start + skip; while (lessThan(items[index - 1], value)) : (index += skip) { @@ -801,9 +834,9 @@ fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan); } -fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool, unique: usize) usize { +fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T, &const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; - const skip = math.max(range.length()/unique, usize(1)); + const skip = math.max(range.length() / unique, usize(1)); var index = range.end - skip; while (index > range.start and !lessThan(items[index - 1], value)) : (index -= skip) { @@ -815,9 +848,9 @@ fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &cons return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan); } -fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool, unique: usize) usize { +fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T, &const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; - const skip = math.max(range.length()/unique, usize(1)); + const skip = math.max(range.length() / unique, usize(1)); var index = range.start + skip; while (!lessThan(value, items[index - 1])) : (index += skip) { @@ -829,9 +862,9 @@ fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const return binaryLast(T, items, value, Range.init(index - skip, index), lessThan); } -fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool, unique: usize) usize { +fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T, &const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; - const skip = math.max(range.length()/unique, usize(1)); + const skip = math.max(range.length() / unique, usize(1)); var index = range.end - skip; while (index > range.start and lessThan(value, items[index - 1])) : (index -= skip) { @@ -843,12 +876,12 @@ fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const return binaryLast(T, items, value, Range.init(index, index + skip), lessThan); } -fn binaryFirst(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool) usize { +fn binaryFirst(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T, &const T) bool) usize { var start = range.start; var end = range.end - 1; if (range.start >= range.end) return range.end; while (start < end) { - const mid = start + (end - start)/2; + const mid = start + (end - start) / 2; if (lessThan(items[mid], value)) { start = mid + 1; } else { @@ -861,12 +894,12 @@ fn binaryFirst(comptime T: type, items: []T, value: &const T, range: &const Rang return start; } -fn binaryLast(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool) usize { +fn binaryLast(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T, &const T) bool) usize { var start = range.start; var end = range.end - 1; if (range.start >= range.end) return range.end; while (start < end) { - const mid = start + (end - start)/2; + const mid = start + (end - start) / 2; if (!lessThan(value, items[mid])) { start = mid + 1; } else { @@ -879,7 +912,7 @@ fn binaryLast(comptime T: type, items: []T, value: &const T, range: &const Range return start; } -fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, lessThan: fn(&const T,&const T)bool, into: []T) void { +fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, lessThan: fn(&const T, &const T) bool, into: []T) void { var A_index: usize = A.start; var B_index: usize = B.start; const A_last = A.end; @@ -909,7 +942,7 @@ fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, less } } -fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn(&const T,&const T)bool, cache: []T) void { +fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn(&const T, &const T) bool, cache: []T) void { // A fits into the cache, so use that instead of the internal buffer var A_index: usize = 0; var B_index: usize = B.start; @@ -937,29 +970,27 @@ fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range, mem.copy(T, items[insert_index..], cache[A_index..A_last]); } -fn swap(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T)bool, order: &[8]u8, x: usize, y: usize) void { - if (lessThan(items[y], items[x]) or - ((*order)[x] > (*order)[y] and !lessThan(items[x], items[y]))) - { +fn swap(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T) bool, order: &[8]u8, x: usize, y: usize) void { + if (lessThan(items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(items[x], items[y]))) { mem.swap(T, &items[x], &items[y]); - mem.swap(u8, &(*order)[x], &(*order)[y]); + mem.swap(u8, &(order.*)[x], &(order.*)[y]); } } fn i32asc(lhs: &const i32, rhs: &const i32) bool { - return *lhs < *rhs; + return lhs.* < rhs.*; } fn i32desc(lhs: &const i32, rhs: &const i32) bool { - return *rhs < *lhs; + return rhs.* < lhs.*; } fn u8asc(lhs: &const u8, rhs: &const u8) bool { - return *lhs < *rhs; + return lhs.* < rhs.*; } fn u8desc(lhs: &const u8, rhs: &const u8) bool { - return *rhs < *lhs; + return rhs.* < lhs.*; } test "stable sort" { @@ -967,44 +998,125 @@ test "stable sort" { comptime testStableSort(); } fn testStableSort() void { - var expected = []IdAndValue { - IdAndValue{.id = 0, .value = 0}, - IdAndValue{.id = 1, .value = 0}, - IdAndValue{.id = 2, .value = 0}, - IdAndValue{.id = 0, .value = 1}, - IdAndValue{.id = 1, .value = 1}, - IdAndValue{.id = 2, .value = 1}, - IdAndValue{.id = 0, .value = 2}, - IdAndValue{.id = 1, .value = 2}, - IdAndValue{.id = 2, .value = 2}, + var expected = []IdAndValue{ + IdAndValue{ + .id = 0, + .value = 0, + }, + IdAndValue{ + .id = 1, + .value = 0, + }, + IdAndValue{ + .id = 2, + .value = 0, + }, + IdAndValue{ + .id = 0, + .value = 1, + }, + IdAndValue{ + .id = 1, + .value = 1, + }, + IdAndValue{ + .id = 2, + .value = 1, + }, + IdAndValue{ + .id = 0, + .value = 2, + }, + IdAndValue{ + .id = 1, + .value = 2, + }, + IdAndValue{ + .id = 2, + .value = 2, + }, }; - var cases = [][9]IdAndValue { - []IdAndValue { - IdAndValue{.id = 0, .value = 0}, - IdAndValue{.id = 0, .value = 1}, - IdAndValue{.id = 0, .value = 2}, - IdAndValue{.id = 1, .value = 0}, - IdAndValue{.id = 1, .value = 1}, - IdAndValue{.id = 1, .value = 2}, - IdAndValue{.id = 2, .value = 0}, - IdAndValue{.id = 2, .value = 1}, - IdAndValue{.id = 2, .value = 2}, + var cases = [][9]IdAndValue{ + []IdAndValue{ + IdAndValue{ + .id = 0, + .value = 0, + }, + IdAndValue{ + .id = 0, + .value = 1, + }, + IdAndValue{ + .id = 0, + .value = 2, + }, + IdAndValue{ + .id = 1, + .value = 0, + }, + IdAndValue{ + .id = 1, + .value = 1, + }, + IdAndValue{ + .id = 1, + .value = 2, + }, + IdAndValue{ + .id = 2, + .value = 0, + }, + IdAndValue{ + .id = 2, + .value = 1, + }, + IdAndValue{ + .id = 2, + .value = 2, + }, }, - []IdAndValue { - IdAndValue{.id = 0, .value = 2}, - IdAndValue{.id = 0, .value = 1}, - IdAndValue{.id = 0, .value = 0}, - IdAndValue{.id = 1, .value = 2}, - IdAndValue{.id = 1, .value = 1}, - IdAndValue{.id = 1, .value = 0}, - IdAndValue{.id = 2, .value = 2}, - IdAndValue{.id = 2, .value = 1}, - IdAndValue{.id = 2, .value = 0}, + []IdAndValue{ + IdAndValue{ + .id = 0, + .value = 2, + }, + IdAndValue{ + .id = 0, + .value = 1, + }, + IdAndValue{ + .id = 0, + .value = 0, + }, + IdAndValue{ + .id = 1, + .value = 2, + }, + IdAndValue{ + .id = 1, + .value = 1, + }, + IdAndValue{ + .id = 1, + .value = 0, + }, + IdAndValue{ + .id = 2, + .value = 2, + }, + IdAndValue{ + .id = 2, + .value = 1, + }, + IdAndValue{ + .id = 2, + .value = 0, + }, }, }; for (cases) |*case| { - insertionSort(IdAndValue, (*case)[0..], cmpByValue); - for (*case) |item, i| { + insertionSort(IdAndValue, (case.*)[0..], cmpByValue); + for (case.*) |item, i| { assert(item.id == expected[i].id); assert(item.value == expected[i].value); } @@ -1019,13 +1131,31 @@ fn cmpByValue(a: &const IdAndValue, b: &const IdAndValue) bool { } test "std.sort" { - const u8cases = [][]const []const u8 { - [][]const u8{"", ""}, - [][]const u8{"a", "a"}, - [][]const u8{"az", "az"}, - [][]const u8{"za", "az"}, - [][]const u8{"asdf", "adfs"}, - [][]const u8{"one", "eno"}, + const u8cases = [][]const []const u8{ + [][]const u8{ + "", + "", + }, + [][]const u8{ + "a", + "a", + }, + [][]const u8{ + "az", + "az", + }, + [][]const u8{ + "za", + "az", + }, + [][]const u8{ + "asdf", + "adfs", + }, + [][]const u8{ + "one", + "eno", + }, }; for (u8cases) |case| { @@ -1036,13 +1166,59 @@ test "std.sort" { assert(mem.eql(u8, slice, case[1])); } - const i32cases = [][]const []const i32 { - [][]const i32{[]i32{}, []i32{}}, - [][]const i32{[]i32{1}, []i32{1}}, - [][]const i32{[]i32{0, 1}, []i32{0, 1}}, - [][]const i32{[]i32{1, 0}, []i32{0, 1}}, - [][]const i32{[]i32{1, -1, 0}, []i32{-1, 0, 1}}, - [][]const i32{[]i32{2, 1, 3}, []i32{1, 2, 3}}, + const i32cases = [][]const []const i32{ + [][]const i32{ + []i32{}, + []i32{}, + }, + [][]const i32{ + []i32{1}, + []i32{1}, + }, + [][]const i32{ + []i32{ + 0, + 1, + }, + []i32{ + 0, + 1, + }, + }, + [][]const i32{ + []i32{ + 1, + 0, + }, + []i32{ + 0, + 1, + }, + }, + [][]const i32{ + []i32{ + 1, + -1, + 0, + }, + []i32{ + -1, + 0, + 1, + }, + }, + [][]const i32{ + []i32{ + 2, + 1, + 3, + }, + []i32{ + 1, + 2, + 3, + }, + }, }; for (i32cases) |case| { @@ -1055,13 +1231,59 @@ test "std.sort" { } test "std.sort descending" { - const rev_cases = [][]const []const i32 { - [][]const i32{[]i32{}, []i32{}}, - [][]const i32{[]i32{1}, []i32{1}}, - [][]const i32{[]i32{0, 1}, []i32{1, 0}}, - [][]const i32{[]i32{1, 0}, []i32{1, 0}}, - [][]const i32{[]i32{1, -1, 0}, []i32{1, 0, -1}}, - [][]const i32{[]i32{2, 1, 3}, []i32{3, 2, 1}}, + const rev_cases = [][]const []const i32{ + [][]const i32{ + []i32{}, + []i32{}, + }, + [][]const i32{ + []i32{1}, + []i32{1}, + }, + [][]const i32{ + []i32{ + 0, + 1, + }, + []i32{ + 1, + 0, + }, + }, + [][]const i32{ + []i32{ + 1, + 0, + }, + []i32{ + 1, + 0, + }, + }, + [][]const i32{ + []i32{ + 1, + -1, + 0, + }, + []i32{ + 1, + 0, + -1, + }, + }, + [][]const i32{ + []i32{ + 2, + 1, + 3, + }, + []i32{ + 3, + 2, + 1, + }, + }, }; for (rev_cases) |case| { @@ -1074,10 +1296,22 @@ test "std.sort descending" { } test "another sort case" { - var arr = []i32{ 5, 3, 1, 2, 4 }; + var arr = []i32{ + 5, + 3, + 1, + 2, + 4, + }; sort(i32, arr[0..], i32asc); - assert(mem.eql(i32, arr, []i32{ 1, 2, 3, 4, 5 })); + assert(mem.eql(i32, arr, []i32{ + 1, + 2, + 3, + 4, + 5, + })); } test "sort fuzz testing" { @@ -1112,7 +1346,7 @@ fn fuzzTest(rng: &std.rand.Random) void { } } -pub fn min(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T)bool) T { +pub fn min(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T) bool) T { var i: usize = 0; var smallest = items[0]; for (items[1..]) |item| { @@ -1123,7 +1357,7 @@ pub fn min(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const return smallest; } -pub fn max(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T)bool) T { +pub fn max(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T) bool) T { var i: usize = 0; var biggest = items[0]; for (items[1..]) |item| { -- cgit v1.2.3 From c3ddf5069e0fb0b728ce275410988ccec3ab7ce9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 May 2018 23:48:50 -0400 Subject: zig fmt: fix not writing results --- src-self-hosted/main.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 51cc0014a1..8dc1d8ce3b 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -710,6 +710,7 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { defer baf.destroy(); try std.zig.render(allocator, baf.stream(), &tree); + try baf.finish(); } } -- cgit v1.2.3 From efa39c5343e13a13e65210f55da5df23ee3feb3e Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 10 May 2018 22:26:26 +1200 Subject: Fix bigint shift-right partial shift --- src/bigint.cpp | 2 +- test/cases/math.zig | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/bigint.cpp b/src/bigint.cpp index 64bc59e5cf..367ae79b6c 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1425,7 +1425,7 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { uint64_t digit = op1_digits[op_digit_index]; size_t dest_digit_index = op_digit_index - digit_shift_count; dest->data.digits[dest_digit_index] = carry | (digit >> leftover_shift_count); - carry = digit << leftover_shift_count; + carry = digit << (64 - leftover_shift_count); if (dest_digit_index == 0) { break; } op_digit_index -= 1; diff --git a/test/cases/math.zig b/test/cases/math.zig index 3c33b14fbf..13704ecd4b 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -366,6 +366,14 @@ test "big number multi-limb shift and mask" { } } +test "big number multi-limb partial shift right" { + comptime { + var a = 0x1ffffffffeeeeeeee; + a >>= 16; + assert(a == 0x1ffffffffeeee); + } +} + test "xor" { test_xor(); comptime test_xor(); -- cgit v1.2.3 From 6e821078f625a03eb8b7794c983da0f7793366ab Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 11 May 2018 14:08:16 -0400 Subject: update std.Buffer API * remove Buffer.appendFormat * remove Buffer.appendByte * remove Buffer.appendByteNTimes Added test to demo what to use instead of the above functions --- std/buffer.zig | 24 ++++-------------------- std/io_test.zig | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/std/buffer.zig b/std/buffer.zig index 041d891dec..42fec7f988 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -99,26 +99,10 @@ pub const Buffer = struct { mem.copy(u8, self.list.toSlice()[old_len..], m); } - // TODO: remove, use OutStream for this - pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) !void { - return fmt.format(self, append, format, args); - } - - // TODO: remove, use OutStream for this pub fn appendByte(self: &Buffer, byte: u8) !void { - return self.appendByteNTimes(byte, 1); - } - - // TODO: remove, use OutStream for this - pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) !void { - var prev_size: usize = self.len(); - const new_size = prev_size + count; - try self.resize(new_size); - - var i: usize = prev_size; - while (i < new_size) : (i += 1) { - self.list.items[i] = byte; - } + const old_len = self.len(); + try self.resize(old_len + 1); + self.list.toSlice()[old_len] = byte; } pub fn eql(self: &const Buffer, m: []const u8) bool { @@ -154,7 +138,7 @@ test "simple Buffer" { var buf = try Buffer.init(debug.global_allocator, ""); assert(buf.len() == 0); try buf.append("hello"); - try buf.appendByte(' '); + try buf.append(" "); try buf.append("world"); assert(buf.eql("hello world")); assert(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst())); diff --git a/std/io_test.zig b/std/io_test.zig index 89959b7b54..5f53556785 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -1,6 +1,5 @@ const std = @import("index.zig"); const io = std.io; -const allocator = std.debug.global_allocator; const DefaultPrng = std.rand.DefaultPrng; const assert = std.debug.assert; const mem = std.mem; @@ -8,6 +7,9 @@ const os = std.os; const builtin = @import("builtin"); test "write a file, read it, then delete it" { + var raw_bytes: [200 * 1024]u8 = undefined; + var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator; + var data: [1024]u8 = undefined; var prng = DefaultPrng.init(1234); prng.random.bytes(data[0..]); @@ -44,3 +46,17 @@ test "write a file, read it, then delete it" { } try os.deleteFile(allocator, tmp_file_name); } + +test "BufferOutStream" { + var bytes: [100]u8 = undefined; + var allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + + var buffer = try std.Buffer.initSize(allocator, 0); + var buf_stream = &std.io.BufferOutStream.init(&buffer).stream; + + const x: i32 = 42; + const y: i32 = 1234; + try buf_stream.print("x: {}\ny: {}\n", x, y); + + assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n")); +} -- cgit v1.2.3 From 277b9cf8788f340f387e63029ad9fc12664cafff Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 11 May 2018 22:41:44 -0400 Subject: fix comptime code modification of global const closes #1008 --- src/ir.cpp | 7 ++++++- test/cases/eval.zig | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 1e6a7d7b8b..c251f30320 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8686,6 +8686,10 @@ static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_ *dest = *src; if (!same_global_refs) { dest->global_refs = global_refs; + if (dest->type->id == TypeTableEntryIdStruct) { + dest->data.x_struct.fields = allocate_nonzero(dest->type->data.structure.src_field_count); + memcpy(dest->data.x_struct.fields, src->data.x_struct.fields, sizeof(ConstExprValue) * dest->type->data.structure.src_field_count); + } } } @@ -11670,7 +11674,8 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc if (var->mem_slot_index != SIZE_MAX) { assert(var->mem_slot_index < ira->exec_context.mem_slot_count); ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; - *mem_slot = casted_init_value->value; + copy_const_val(mem_slot, &casted_init_value->value, + !is_comptime_var || var->gen_is_const); if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { ir_build_const_from(ira, &decl_var_instruction->base); diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 364db5e152..1ed30872e0 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -536,3 +536,20 @@ test "runtime 128 bit integer division" { var c = a / b; assert(c == 15231399999); } + +pub const Info = struct { + version: u8, +}; + +pub const diamond_info = Info { + .version = 0, +}; + +test "comptime modification of const struct field" { + comptime { + var res = diamond_info; + res.version = 1; + assert(diamond_info.version == 0); + assert(res.version == 1); + } +} -- cgit v1.2.3 From 4277762b742216d4dd44bfe7490947e69527fbc7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 11 May 2018 23:04:41 -0400 Subject: fix windows build system broken by 6e821078f625a03eb8b7794c983da0f7793366ab --- std/os/child_process.zig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 8bb8b2d7e7..ebc8a38cd1 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -650,6 +650,8 @@ fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) var buf = try Buffer.initSize(allocator, 0); defer buf.deinit(); + var buf_stream = &io.BufferOutStream.init(&buf).stream; + for (argv) |arg, arg_i| { if (arg_i != 0) try buf.appendByte(' '); @@ -663,18 +665,18 @@ fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) switch (byte) { '\\' => backslash_count += 1, '"' => { - try buf.appendByteNTimes('\\', backslash_count * 2 + 1); + try buf_stream.writeByteNTimes('\\', backslash_count * 2 + 1); try buf.appendByte('"'); backslash_count = 0; }, else => { - try buf.appendByteNTimes('\\', backslash_count); + try buf_stream.writeByteNTimes('\\', backslash_count); try buf.appendByte(byte); backslash_count = 0; }, } } - try buf.appendByteNTimes('\\', backslash_count * 2); + try buf_stream.writeByteNTimes('\\', backslash_count * 2); try buf.appendByte('"'); } -- cgit v1.2.3 From a6ae45145f5814963cfdff4e18c1f984729588b9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 May 2018 17:35:15 -0400 Subject: add @newStackCall builtin function See #1006 --- doc/langref.html.in | 47 ++++++++++++++++++-- src/all_types.hpp | 7 +++ src/codegen.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++- src/ir.cpp | 66 +++++++++++++++++++++++++---- src/target.cpp | 62 +++++++++++++++++++++++++++ src/target.hpp | 2 + test/behavior.zig | 5 ++- test/cases/new_stack_call.zig | 26 ++++++++++++ 8 files changed, 298 insertions(+), 16 deletions(-) create mode 100644 test/cases/new_stack_call.zig diff --git a/doc/langref.html.in b/doc/langref.html.in index b867ff0b35..4ae98abbd2 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4485,17 +4485,58 @@ mem.set(u8, dest, c); If no overflow or underflow occurs, returns false.

{#header_close#} + {#header_open|@newStackCall#} +
@newStackCall(new_stack: []u8, function: var, args: ...) -> var
+

+ This calls a function, in the same way that invoking an expression with parentheses does. However, + instead of using the same stack as the caller, the function uses the stack provided in the new_stack + parameter. +

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +var new_stack_bytes: [1024]u8 = undefined; + +test "calling a function with a new stack" { + const arg = 1234; + + const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg); + const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); + _ = targetFunction(arg); + + assert(arg == 1234); + assert(a < b); +} + +fn targetFunction(x: i32) usize { + assert(x == 1234); + + var local_variable: i32 = 42; + const ptr = &local_variable; + *ptr += 1; + + assert(local_variable == 43); + return @ptrToInt(ptr); +} + {#code_end#} + {#header_close#} {#header_open|@noInlineCall#}
@noInlineCall(function: var, args: ...) -> var

This calls a function, in the same way that invoking an expression with parentheses does:

-
const assert = @import("std").debug.assert;
+      {#code_begin|test#}
+const assert = @import("std").debug.assert;
+
 test "noinline function call" {
     assert(@noInlineCall(add, 3, 9) == 12);
 }
 
-fn add(a: i32, b: i32) -> i32 { a + b }
+fn add(a: i32, b: i32) i32 { + return a + b; +} + {#code_end#}

Unlike a normal function call, however, @noInlineCall guarantees that the call will not be inlined. If the call must be inlined, a compile error is emitted. @@ -6451,7 +6492,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo newStackCall", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index c1c6c9a1a5..dc61c8235f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1340,6 +1340,7 @@ enum BuiltinFnId { BuiltinFnIdOffsetOf, BuiltinFnIdInlineCall, BuiltinFnIdNoInlineCall, + BuiltinFnIdNewStackCall, BuiltinFnIdTypeId, BuiltinFnIdShlExact, BuiltinFnIdShrExact, @@ -1656,8 +1657,13 @@ struct CodeGen { LLVMValueRef coro_alloc_helper_fn_val; LLVMValueRef merge_err_ret_traces_fn_val; LLVMValueRef add_error_return_trace_addr_fn_val; + LLVMValueRef stacksave_fn_val; + LLVMValueRef stackrestore_fn_val; + LLVMValueRef write_register_fn_val; bool error_during_imports; + LLVMValueRef sp_md_node; + const char **clang_argv; size_t clang_argv_len; ZigList lib_dirs; @@ -2280,6 +2286,7 @@ struct IrInstructionCall { bool is_async; IrInstruction *async_allocator; + IrInstruction *new_stack; }; struct IrInstructionConst { diff --git a/src/codegen.cpp b/src/codegen.cpp index 4e58f86d4b..f1e102392a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -938,6 +938,53 @@ static LLVMValueRef get_memcpy_fn_val(CodeGen *g) { return g->memcpy_fn_val; } +static LLVMValueRef get_stacksave_fn_val(CodeGen *g) { + if (g->stacksave_fn_val) + return g->stacksave_fn_val; + + // declare i8* @llvm.stacksave() + + LLVMTypeRef fn_type = LLVMFunctionType(LLVMPointerType(LLVMInt8Type(), 0), nullptr, 0, false); + g->stacksave_fn_val = LLVMAddFunction(g->module, "llvm.stacksave", fn_type); + assert(LLVMGetIntrinsicID(g->stacksave_fn_val)); + + return g->stacksave_fn_val; +} + +static LLVMValueRef get_stackrestore_fn_val(CodeGen *g) { + if (g->stackrestore_fn_val) + return g->stackrestore_fn_val; + + // declare void @llvm.stackrestore(i8* %ptr) + + LLVMTypeRef param_type = LLVMPointerType(LLVMInt8Type(), 0); + LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), ¶m_type, 1, false); + g->stackrestore_fn_val = LLVMAddFunction(g->module, "llvm.stackrestore", fn_type); + assert(LLVMGetIntrinsicID(g->stackrestore_fn_val)); + + return g->stackrestore_fn_val; +} + +static LLVMValueRef get_write_register_fn_val(CodeGen *g) { + if (g->write_register_fn_val) + return g->write_register_fn_val; + + // declare void @llvm.write_register.i64(metadata, i64 @value) + // !0 = !{!"sp\00"} + + LLVMTypeRef param_types[] = { + LLVMMetadataTypeInContext(LLVMGetGlobalContext()), + LLVMIntType(g->pointer_size_bytes * 8), + }; + + LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), param_types, 2, false); + Buf *name = buf_sprintf("llvm.write_register.i%d", g->pointer_size_bytes * 8); + g->write_register_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type); + assert(LLVMGetIntrinsicID(g->write_register_fn_val)); + + return g->write_register_fn_val; +} + static LLVMValueRef get_coro_destroy_fn_val(CodeGen *g) { if (g->coro_destroy_fn_val) return g->coro_destroy_fn_val; @@ -2901,6 +2948,38 @@ static size_t get_async_err_code_arg_index(CodeGen *g, FnTypeId *fn_type_id) { return 1 + get_async_allocator_arg_index(g, fn_type_id); } + +static LLVMValueRef get_new_stack_addr(CodeGen *g, LLVMValueRef new_stack) { + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, new_stack, (unsigned)slice_ptr_index, ""); + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, new_stack, (unsigned)slice_len_index, ""); + + LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); + LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, ""); + + LLVMValueRef ptr_addr = LLVMBuildPtrToInt(g->builder, ptr_value, LLVMTypeOf(len_value), ""); + LLVMValueRef end_addr = LLVMBuildNUWAdd(g->builder, ptr_addr, len_value, ""); + LLVMValueRef align_amt = LLVMConstInt(LLVMTypeOf(end_addr), get_abi_alignment(g, g->builtin_types.entry_usize), false); + LLVMValueRef align_adj = LLVMBuildURem(g->builder, end_addr, align_amt, ""); + return LLVMBuildNUWSub(g->builder, end_addr, align_adj, ""); +} + +static void gen_set_stack_pointer(CodeGen *g, LLVMValueRef aligned_end_addr) { + LLVMValueRef write_register_fn_val = get_write_register_fn_val(g); + + if (g->sp_md_node == nullptr) { + Buf *sp_reg_name = buf_create_from_str(arch_stack_pointer_register_name(&g->zig_target.arch)); + LLVMValueRef str_node = LLVMMDString(buf_ptr(sp_reg_name), buf_len(sp_reg_name) + 1); + g->sp_md_node = LLVMMDNode(&str_node, 1); + } + + LLVMValueRef params[] = { + g->sp_md_node, + aligned_end_addr, + }; + + LLVMBuildCall(g->builder, write_register_fn_val, params, 2, ""); +} + static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) { LLVMValueRef fn_val; TypeTableEntry *fn_type; @@ -2967,8 +3046,23 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } LLVMCallConv llvm_cc = get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc); - LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val, - gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, ""); + LLVMValueRef result; + + if (instruction->new_stack == nullptr) { + result = ZigLLVMBuildCall(g->builder, fn_val, + gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, ""); + } else { + LLVMValueRef stacksave_fn_val = get_stacksave_fn_val(g); + LLVMValueRef stackrestore_fn_val = get_stackrestore_fn_val(g); + + LLVMValueRef new_stack_addr = get_new_stack_addr(g, ir_llvm_value(g, instruction->new_stack)); + LLVMValueRef old_stack_ref = LLVMBuildCall(g->builder, stacksave_fn_val, nullptr, 0, ""); + gen_set_stack_pointer(g, new_stack_addr); + result = ZigLLVMBuildCall(g->builder, fn_val, + gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, ""); + LLVMBuildCall(g->builder, stackrestore_fn_val, &old_stack_ref, 1, ""); + } + for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; @@ -6171,6 +6265,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2); create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); + create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1); create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2); create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2); diff --git a/src/ir.cpp b/src/ir.cpp index c251f30320..7bc837d908 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1102,7 +1102,8 @@ static IrInstruction *ir_build_union_field_ptr_from(IrBuilder *irb, IrInstructio static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node, FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, - bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator) + bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator, + IrInstruction *new_stack) { IrInstructionCall *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->fn_entry = fn_entry; @@ -1113,6 +1114,7 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc call_instruction->arg_count = arg_count; call_instruction->is_async = is_async; call_instruction->async_allocator = async_allocator; + call_instruction->new_stack = new_stack; if (fn_ref) ir_ref_instruction(fn_ref, irb->current_basic_block); @@ -1120,16 +1122,19 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc ir_ref_instruction(args[i], irb->current_basic_block); if (async_allocator) ir_ref_instruction(async_allocator, irb->current_basic_block); + if (new_stack != nullptr) + ir_ref_instruction(new_stack, irb->current_basic_block); return &call_instruction->base; } static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_instruction, FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, - bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator) + bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator, + IrInstruction *new_stack) { IrInstruction *new_instruction = ir_build_call(irb, old_instruction->scope, - old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime, fn_inline, is_async, async_allocator); + old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime, fn_inline, is_async, async_allocator, new_stack); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } @@ -4303,7 +4308,37 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; - IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr); + IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr, nullptr); + return ir_lval_wrap(irb, scope, call, lval); + } + case BuiltinFnIdNewStackCall: + { + if (node->data.fn_call_expr.params.length == 0) { + add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0")); + return irb->codegen->invalid_instruction; + } + + AstNode *new_stack_node = node->data.fn_call_expr.params.at(0); + IrInstruction *new_stack = ir_gen_node(irb, new_stack_node, scope); + if (new_stack == irb->codegen->invalid_instruction) + return new_stack; + + AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1); + IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + if (fn_ref == irb->codegen->invalid_instruction) + return fn_ref; + + size_t arg_count = node->data.fn_call_expr.params.length - 2; + + IrInstruction **args = allocate(arg_count); + for (size_t i = 0; i < arg_count; i += 1) { + AstNode *arg_node = node->data.fn_call_expr.params.at(i + 2); + args[i] = ir_gen_node(irb, arg_node, scope); + if (args[i] == irb->codegen->invalid_instruction) + return args[i]; + } + + IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, new_stack); return ir_lval_wrap(irb, scope, call, lval); } case BuiltinFnIdTypeId: @@ -4513,7 +4548,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } } - IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator); + IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr); return ir_lval_wrap(irb, scope, fn_call, lval); } @@ -6825,7 +6860,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction **args = allocate(arg_count); args[0] = implicit_allocator_ptr; // self args[1] = mem_slice; // old_mem - ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, nullptr); + ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume"); ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); @@ -11992,7 +12027,7 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c TypeTableEntry *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type); IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, - fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, async_allocator_inst); + fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, async_allocator_inst, nullptr); result->value.type = async_return_type; return result; } @@ -12362,6 +12397,19 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return ir_finish_anal(ira, return_type); } + IrInstruction *casted_new_stack = nullptr; + if (call_instruction->new_stack != nullptr) { + TypeTableEntry *u8_ptr = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); + TypeTableEntry *u8_slice = get_slice_type(ira->codegen, u8_ptr); + IrInstruction *new_stack = call_instruction->new_stack->other; + if (type_is_invalid(new_stack->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + casted_new_stack = ir_implicit_cast(ira, new_stack, u8_slice); + if (type_is_invalid(casted_new_stack->value.type)) + return ira->codegen->builtin_types.entry_invalid; + } + if (fn_type->data.fn.is_generic) { if (!fn_entry) { ir_add_error(ira, call_instruction->fn_ref, @@ -12588,7 +12636,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal assert(async_allocator_inst == nullptr); IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base, impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline, - call_instruction->is_async, nullptr); + call_instruction->is_async, nullptr, casted_new_stack); ir_add_alloca(ira, new_call_instruction, return_type); @@ -12679,7 +12727,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base, - fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr); + fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, casted_new_stack); ir_add_alloca(ira, new_call_instruction, return_type); return ir_finish_anal(ira, return_type); diff --git a/src/target.cpp b/src/target.cpp index 5008b51a09..57970888fc 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -896,3 +896,65 @@ bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target return false; } + +const char *arch_stack_pointer_register_name(const ArchType *arch) { + switch (arch->arch) { + case ZigLLVM_UnknownArch: + zig_unreachable(); + case ZigLLVM_x86: + return "sp"; + case ZigLLVM_x86_64: + return "rsp"; + + case ZigLLVM_aarch64: + case ZigLLVM_arm: + case ZigLLVM_thumb: + case ZigLLVM_aarch64_be: + case ZigLLVM_amdgcn: + case ZigLLVM_amdil: + case ZigLLVM_amdil64: + case ZigLLVM_armeb: + case ZigLLVM_arc: + case ZigLLVM_avr: + case ZigLLVM_bpfeb: + case ZigLLVM_bpfel: + case ZigLLVM_hexagon: + case ZigLLVM_lanai: + case ZigLLVM_hsail: + case ZigLLVM_hsail64: + case ZigLLVM_kalimba: + case ZigLLVM_le32: + case ZigLLVM_le64: + case ZigLLVM_mips: + case ZigLLVM_mips64: + case ZigLLVM_mips64el: + case ZigLLVM_mipsel: + case ZigLLVM_msp430: + case ZigLLVM_nios2: + case ZigLLVM_nvptx: + case ZigLLVM_nvptx64: + case ZigLLVM_ppc64le: + case ZigLLVM_r600: + case ZigLLVM_renderscript32: + case ZigLLVM_renderscript64: + case ZigLLVM_riscv32: + case ZigLLVM_riscv64: + case ZigLLVM_shave: + case ZigLLVM_sparc: + case ZigLLVM_sparcel: + case ZigLLVM_sparcv9: + case ZigLLVM_spir: + case ZigLLVM_spir64: + case ZigLLVM_systemz: + case ZigLLVM_tce: + case ZigLLVM_tcele: + case ZigLLVM_thumbeb: + case ZigLLVM_wasm32: + case ZigLLVM_wasm64: + case ZigLLVM_xcore: + case ZigLLVM_ppc: + case ZigLLVM_ppc64: + zig_panic("TODO populate this table with stack pointer register name for this CPU architecture"); + } + zig_unreachable(); +} diff --git a/src/target.hpp b/src/target.hpp index 614b0627d5..5a118f6d8d 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -77,6 +77,8 @@ size_t target_arch_count(void); const ArchType *get_target_arch(size_t index); void get_arch_name(char *out_str, const ArchType *arch); +const char *arch_stack_pointer_register_name(const ArchType *arch); + size_t target_vendor_count(void); ZigLLVM_VendorType get_target_vendor(size_t index); diff --git a/test/behavior.zig b/test/behavior.zig index d700faaebc..fbec60f648 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -23,6 +23,7 @@ comptime { _ = @import("cases/eval.zig"); _ = @import("cases/field_parent_ptr.zig"); _ = @import("cases/fn.zig"); + _ = @import("cases/fn_in_struct_in_comptime.zig"); _ = @import("cases/for.zig"); _ = @import("cases/generics.zig"); _ = @import("cases/if.zig"); @@ -32,11 +33,11 @@ comptime { _ = @import("cases/math.zig"); _ = @import("cases/misc.zig"); _ = @import("cases/namespace_depends_on_compile_var/index.zig"); + _ = @import("cases/new_stack_call.zig"); _ = @import("cases/null.zig"); _ = @import("cases/pub_enum/index.zig"); _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("cases/reflection.zig"); - _ = @import("cases/type_info.zig"); _ = @import("cases/sizeof_and_typeof.zig"); _ = @import("cases/slice.zig"); _ = @import("cases/struct.zig"); @@ -48,10 +49,10 @@ comptime { _ = @import("cases/syntax.zig"); _ = @import("cases/this.zig"); _ = @import("cases/try.zig"); + _ = @import("cases/type_info.zig"); _ = @import("cases/undefined.zig"); _ = @import("cases/union.zig"); _ = @import("cases/var_args.zig"); _ = @import("cases/void.zig"); _ = @import("cases/while.zig"); - _ = @import("cases/fn_in_struct_in_comptime.zig"); } diff --git a/test/cases/new_stack_call.zig b/test/cases/new_stack_call.zig new file mode 100644 index 0000000000..ea9f0c914f --- /dev/null +++ b/test/cases/new_stack_call.zig @@ -0,0 +1,26 @@ +const std = @import("std"); +const assert = std.debug.assert; + +var new_stack_bytes: [1024]u8 = undefined; + +test "calling a function with a new stack" { + const arg = 1234; + + const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg); + const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); + _ = targetFunction(arg); + + assert(arg == 1234); + assert(a < b); +} + +fn targetFunction(x: i32) usize { + assert(x == 1234); + + var local_variable: i32 = 42; + const ptr = &local_variable; + *ptr += 1; + + assert(local_variable == 43); + return @ptrToInt(ptr); +} -- cgit v1.2.3 From 911cbf57cd10159176950285feb9ee14fb88a803 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 May 2018 19:03:39 -0400 Subject: recursive render top level decl --- std/zig/render.zig | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/std/zig/render.zig b/std/zig/render.zig index cced30cd60..c7a08a11fd 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -21,32 +21,28 @@ const RenderState = union(enum) { const indent_delta = 4; pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { + var it = tree.root_node.decls.iterator(0); + while (it.next()) |decl| { + try renderTopLevelDecl(allocator, stream, tree, *decl); + if (it.peek()) |next_decl| { + const n = if (nodeLineOffset(tree, *decl, *next_decl) >= 2) u8(2) else u8(1); + try stream.writeByteNTimes('\n', n); + } + } + try stream.write("\n"); +} + +fn nodeLineOffset(tree: &ast.Tree, a: &ast.Node, b: &ast.Node) usize { + const a_last_token = tree.tokens.at(a.lastToken()); + const loc = tree.tokenLocation(a_last_token.end, b.firstToken()); + return loc.line; +} + +fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, decl_ptr: &ast.Node) !void { var stack = std.ArrayList(RenderState).init(allocator); defer stack.deinit(); - { - try stack.append(RenderState { .Text = "\n"}); - - var i = tree.root_node.decls.len; - while (i != 0) { - i -= 1; - const decl = *tree.root_node.decls.at(i); - try stack.append(RenderState {.TopLevelDecl = decl}); - if (i != 0) { - try stack.append(RenderState { - .Text = blk: { - const prev_node = *tree.root_node.decls.at(i - 1); - const prev_node_last_token = tree.tokens.at(prev_node.lastToken()); - const loc = tree.tokenLocation(prev_node_last_token.end, decl.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }, - }); - } - } - } + try stack.append(RenderState {.TopLevelDecl = decl_ptr}); var indent: usize = 0; while (stack.popOrNull()) |state| { -- cgit v1.2.3 From 7cdc9d98c7134be5edd18eb6f94dd8cfc55bb764 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 May 2018 23:06:54 -0400 Subject: refactor std.zig.render to be recursive See #1006 --- std/zig/render.zig | 2157 +++++++++++++++++++++++++--------------------------- 1 file changed, 1057 insertions(+), 1100 deletions(-) diff --git a/std/zig/render.zig b/std/zig/render.zig index c7a08a11fd..13ef4607f4 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -1,29 +1,23 @@ const std = @import("../index.zig"); +const builtin = @import("builtin"); const assert = std.debug.assert; const mem = std.mem; const ast = std.zig.ast; const Token = std.zig.Token; -const RenderState = union(enum) { - TopLevelDecl: &ast.Node, - ParamDecl: &ast.Node, - Text: []const u8, - Expression: &ast.Node, - VarDecl: &ast.Node.VarDecl, - Statement: &ast.Node, - PrintIndent, - Indent: usize, - MaybeSemiColon: &ast.Node, - Token: ast.TokenIndex, - NonBreakToken: ast.TokenIndex, +const indent_delta = 4; + +pub const Error = error { + /// Ran out of memory allocating call stack frames to complete rendering. + OutOfMemory, }; -const indent_delta = 4; +pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf(stream).Child.Error || Error)!void { + comptime assert(@typeId(@typeOf(stream)) == builtin.TypeId.Pointer); -pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { var it = tree.root_node.decls.iterator(0); while (it.next()) |decl| { - try renderTopLevelDecl(allocator, stream, tree, *decl); + try renderTopLevelDecl(allocator, stream, tree, 0, *decl); if (it.peek()) |next_decl| { const n = if (nodeLineOffset(tree, *decl, *next_decl) >= 2) u8(2) else u8(1); try stream.writeByteNTimes('\n', n); @@ -38,1202 +32,1165 @@ fn nodeLineOffset(tree: &ast.Tree, a: &ast.Node, b: &ast.Node) usize { return loc.line; } -fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, decl_ptr: &ast.Node) !void { - var stack = std.ArrayList(RenderState).init(allocator); - defer stack.deinit(); +fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, decl: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { + switch (decl.id) { + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); - try stack.append(RenderState {.TopLevelDecl = decl_ptr}); + try renderComments(tree, stream, fn_proto, indent); + try renderExpression(allocator, stream, tree, indent, decl); - var indent: usize = 0; - while (stack.popOrNull()) |state| { - switch (state) { - RenderState.TopLevelDecl => |decl| { - switch (decl.id) { - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); - try renderComments(tree, stream, fn_proto, indent); + if (fn_proto.body_node) |body_node| { + try stream.write(" "); + try renderExpression(allocator, stream, tree, indent, body_node); + } else { + try stream.write(";"); + } + }, + ast.Node.Id.Use => { + const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); - if (fn_proto.body_node) |body_node| { - stack.append(RenderState { .Expression = body_node}) catch unreachable; - try stack.append(RenderState { .Text = " "}); - } else { - stack.append(RenderState { .Text = ";" }) catch unreachable; - } + if (use_decl.visib_token) |visib_token| { + try stream.print("{} ", tree.tokenSlice(visib_token)); + } + try stream.write("use "); + try renderExpression(allocator, stream, tree, indent, use_decl.expr); + try stream.write(";"); + }, + ast.Node.Id.VarDecl => { + const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); + + try renderComments(tree, stream, var_decl, indent); + try renderVarDecl(allocator, stream, tree, indent, var_decl); + }, + ast.Node.Id.TestDecl => { + const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); + + try renderComments(tree, stream, test_decl, indent); + try stream.write("test "); + try renderExpression(allocator, stream, tree, indent, test_decl.name); + try stream.write(" "); + try renderExpression(allocator, stream, tree, indent, test_decl.body_node); + }, + ast.Node.Id.StructField => { + const field = @fieldParentPtr(ast.Node.StructField, "base", decl); + + try renderComments(tree, stream, field, indent); + if (field.visib_token) |visib_token| { + try stream.print("{} ", tree.tokenSlice(visib_token)); + } + try stream.print("{}: ", tree.tokenSlice(field.name_token)); + try renderExpression(allocator, stream, tree, indent, field.type_expr); + try renderToken(tree, stream, field.lastToken() + 1, indent, true); + }, + ast.Node.Id.UnionTag => { + const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); + + try renderComments(tree, stream, tag, indent); + try stream.print("{}", tree.tokenSlice(tag.name_token)); + + if (tag.type_expr) |type_expr| { + try stream.print(": "); + try renderExpression(allocator, stream, tree, indent, type_expr); + } - try stack.append(RenderState { .Expression = decl }); - }, - ast.Node.Id.Use => { - const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); - if (use_decl.visib_token) |visib_token| { - try stream.print("{} ", tree.tokenSlice(visib_token)); - } - try stream.print("use "); - try stack.append(RenderState { .Text = ";" }); - try stack.append(RenderState { .Expression = use_decl.expr }); - }, - ast.Node.Id.VarDecl => { - const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); - try renderComments(tree, stream, var_decl, indent); - try stack.append(RenderState { .VarDecl = var_decl}); - }, - ast.Node.Id.TestDecl => { - const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); - try renderComments(tree, stream, test_decl, indent); - try stream.print("test "); - try stack.append(RenderState { .Expression = test_decl.body_node }); - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = test_decl.name }); - }, - ast.Node.Id.StructField => { - const field = @fieldParentPtr(ast.Node.StructField, "base", decl); - try renderComments(tree, stream, field, indent); - if (field.visib_token) |visib_token| { - try stream.print("{} ", tree.tokenSlice(visib_token)); - } - try stream.print("{}: ", tree.tokenSlice(field.name_token)); - try stack.append(RenderState { .Token = field.lastToken() + 1 }); - try stack.append(RenderState { .Expression = field.type_expr}); - }, - ast.Node.Id.UnionTag => { - const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); - try renderComments(tree, stream, tag, indent); - try stream.print("{}", tree.tokenSlice(tag.name_token)); - - try stack.append(RenderState { .Text = "," }); - - if (tag.value_expr) |value_expr| { - try stack.append(RenderState { .Expression = value_expr }); - try stack.append(RenderState { .Text = " = " }); - } + if (tag.value_expr) |value_expr| { + try stream.print(" = "); + try renderExpression(allocator, stream, tree, indent, value_expr); + } - if (tag.type_expr) |type_expr| { - try stream.print(": "); - try stack.append(RenderState { .Expression = type_expr}); - } - }, - ast.Node.Id.EnumTag => { - const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); - try renderComments(tree, stream, tag, indent); - try stream.print("{}", tree.tokenSlice(tag.name_token)); - - try stack.append(RenderState { .Text = "," }); - if (tag.value) |value| { - try stream.print(" = "); - try stack.append(RenderState { .Expression = value}); - } - }, - ast.Node.Id.ErrorTag => { - const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl); - try renderComments(tree, stream, tag, indent); - try stream.print("{}", tree.tokenSlice(tag.name_token)); - }, - ast.Node.Id.Comptime => { - try stack.append(RenderState { .MaybeSemiColon = decl }); - try stack.append(RenderState { .Expression = decl }); - }, - ast.Node.Id.LineComment => { - const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl); - try stream.write(tree.tokenSlice(line_comment_node.token)); - }, - else => unreachable, - } - }, - - RenderState.VarDecl => |var_decl| { - try stack.append(RenderState { .Token = var_decl.semicolon_token }); - if (var_decl.init_node) |init_node| { - try stack.append(RenderState { .Expression = init_node }); - const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = "; - try stack.append(RenderState { .Text = text }); - } - if (var_decl.align_node) |align_node| { - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = align_node }); - try stack.append(RenderState { .Text = " align(" }); - } - if (var_decl.type_node) |type_node| { - try stack.append(RenderState { .Expression = type_node }); - try stack.append(RenderState { .Text = ": " }); - } - try stack.append(RenderState { .Text = tree.tokenSlice(var_decl.name_token) }); - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) }); + try stream.write(","); + }, + ast.Node.Id.EnumTag => { + const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); - if (var_decl.comptime_token) |comptime_token| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = tree.tokenSlice(comptime_token) }); - } + try renderComments(tree, stream, tag, indent); + try stream.print("{}", tree.tokenSlice(tag.name_token)); - if (var_decl.extern_export_token) |extern_export_token| { - if (var_decl.lib_name != null) { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = ??var_decl.lib_name }); + if (tag.value) |value| { + try stream.print(" = "); + try renderExpression(allocator, stream, tree, indent, value); + } + + try stream.write(","); + }, + ast.Node.Id.ErrorTag => { + const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl); + + try renderComments(tree, stream, tag, indent); + try stream.print("{}", tree.tokenSlice(tag.name_token)); + }, + ast.Node.Id.Comptime => { + try renderExpression(allocator, stream, tree, indent, decl); + try maybeRenderSemicolon(stream, tree, indent, decl); + }, + ast.Node.Id.LineComment => { + const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl); + + try stream.write(tree.tokenSlice(line_comment_node.token)); + }, + else => unreachable, + } +} + +fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { + switch (base.id) { + ast.Node.Id.Identifier => { + const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); + try stream.print("{}", tree.tokenSlice(identifier.token)); + }, + ast.Node.Id.Block => { + const block = @fieldParentPtr(ast.Node.Block, "base", base); + if (block.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } + + if (block.statements.len == 0) { + try stream.write("{}"); + } else { + try stream.write("{\n"); + const block_indent = indent + indent_delta; + + var it = block.statements.iterator(0); + while (it.next()) |statement| { + try stream.writeByteNTimes(' ', block_indent); + try renderStatement(allocator, stream, tree, block_indent, *statement); + + if (it.peek()) |next_statement| { + const n = if (nodeLineOffset(tree, *statement, *next_statement) >= 2) u8(2) else u8(1); + try stream.writeByteNTimes('\n', n); } - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = tree.tokenSlice(extern_export_token) }); } - if (var_decl.visib_token) |visib_token| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = tree.tokenSlice(visib_token) }); - } - }, + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent); + try stream.write("}"); + } + }, + ast.Node.Id.Defer => { + const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); + try stream.print("{} ", tree.tokenSlice(defer_node.defer_token)); + try renderExpression(allocator, stream, tree, indent, defer_node.expr); + }, + ast.Node.Id.Comptime => { + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); + try stream.print("{} ", tree.tokenSlice(comptime_node.comptime_token)); + try renderExpression(allocator, stream, tree, indent, comptime_node.expr); + }, + ast.Node.Id.AsyncAttribute => { + const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); + try stream.print("{}", tree.tokenSlice(async_attr.async_token)); + + if (async_attr.allocator_type) |allocator_type| { + try stream.write("<"); + try renderExpression(allocator, stream, tree, indent, allocator_type); + try stream.write(">"); + } + }, + ast.Node.Id.Suspend => { + const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); + if (suspend_node.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } + try stream.print("{}", tree.tokenSlice(suspend_node.suspend_token)); - RenderState.ParamDecl => |base| { - const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); - if (param_decl.comptime_token) |comptime_token| { - try stream.print("{} ", tree.tokenSlice(comptime_token)); - } - if (param_decl.noalias_token) |noalias_token| { - try stream.print("{} ", tree.tokenSlice(noalias_token)); - } - if (param_decl.name_token) |name_token| { - try stream.print("{}: ", tree.tokenSlice(name_token)); - } - if (param_decl.var_args_token) |var_args_token| { - try stream.print("{}", tree.tokenSlice(var_args_token)); - } else { - try stack.append(RenderState { .Expression = param_decl.type_node}); - } - }, - RenderState.Text => |bytes| { - try stream.write(bytes); - }, - RenderState.Expression => |base| switch (base.id) { - ast.Node.Id.Identifier => { - const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); - try stream.print("{}", tree.tokenSlice(identifier.token)); - }, - ast.Node.Id.Block => { - const block = @fieldParentPtr(ast.Node.Block, "base", base); - if (block.label) |label| { - try stream.print("{}: ", tree.tokenSlice(label)); - } + if (suspend_node.payload) |payload| { + try stream.write(" "); + try renderExpression(allocator, stream, tree, indent, payload); + } - if (block.statements.len == 0) { - try stream.write("{}"); - } else { - try stream.write("{"); - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent}); - try stack.append(RenderState { .Text = "\n"}); - var i = block.statements.len; - while (i != 0) { - i -= 1; - const statement_node = *block.statements.at(i); - try stack.append(RenderState { .Statement = statement_node}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = *block.statements.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, statement_node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - } - break :blk "\n"; - }, - }); - } - } - }, - ast.Node.Id.Defer => { - const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); - try stream.print("{} ", tree.tokenSlice(defer_node.defer_token)); - try stack.append(RenderState { .Expression = defer_node.expr }); - }, - ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); - try stream.print("{} ", tree.tokenSlice(comptime_node.comptime_token)); - try stack.append(RenderState { .Expression = comptime_node.expr }); - }, - ast.Node.Id.AsyncAttribute => { - const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); - try stream.print("{}", tree.tokenSlice(async_attr.async_token)); - - if (async_attr.allocator_type) |allocator_type| { - try stack.append(RenderState { .Text = ">" }); - try stack.append(RenderState { .Expression = allocator_type }); - try stack.append(RenderState { .Text = "<" }); + if (suspend_node.body) |body| { + try stream.write(" "); + try renderExpression(allocator, stream, tree, indent, body); + } + + }, + + ast.Node.Id.InfixOp => { + const infix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); + + try renderExpression(allocator, stream, tree, indent, infix_op_node.lhs); + + const text = switch (infix_op_node.op) { + ast.Node.InfixOp.Op.Add => " + ", + ast.Node.InfixOp.Op.AddWrap => " +% ", + ast.Node.InfixOp.Op.ArrayCat => " ++ ", + ast.Node.InfixOp.Op.ArrayMult => " ** ", + ast.Node.InfixOp.Op.Assign => " = ", + ast.Node.InfixOp.Op.AssignBitAnd => " &= ", + ast.Node.InfixOp.Op.AssignBitOr => " |= ", + ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ", + ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ", + ast.Node.InfixOp.Op.AssignBitXor => " ^= ", + ast.Node.InfixOp.Op.AssignDiv => " /= ", + ast.Node.InfixOp.Op.AssignMinus => " -= ", + ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ", + ast.Node.InfixOp.Op.AssignMod => " %= ", + ast.Node.InfixOp.Op.AssignPlus => " += ", + ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ", + ast.Node.InfixOp.Op.AssignTimes => " *= ", + ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ", + ast.Node.InfixOp.Op.BangEqual => " != ", + ast.Node.InfixOp.Op.BitAnd => " & ", + ast.Node.InfixOp.Op.BitOr => " | ", + ast.Node.InfixOp.Op.BitShiftLeft => " << ", + ast.Node.InfixOp.Op.BitShiftRight => " >> ", + ast.Node.InfixOp.Op.BitXor => " ^ ", + ast.Node.InfixOp.Op.BoolAnd => " and ", + ast.Node.InfixOp.Op.BoolOr => " or ", + ast.Node.InfixOp.Op.Div => " / ", + ast.Node.InfixOp.Op.EqualEqual => " == ", + ast.Node.InfixOp.Op.ErrorUnion => "!", + ast.Node.InfixOp.Op.GreaterOrEqual => " >= ", + ast.Node.InfixOp.Op.GreaterThan => " > ", + ast.Node.InfixOp.Op.LessOrEqual => " <= ", + ast.Node.InfixOp.Op.LessThan => " < ", + ast.Node.InfixOp.Op.MergeErrorSets => " || ", + ast.Node.InfixOp.Op.Mod => " % ", + ast.Node.InfixOp.Op.Mult => " * ", + ast.Node.InfixOp.Op.MultWrap => " *% ", + ast.Node.InfixOp.Op.Period => ".", + ast.Node.InfixOp.Op.Sub => " - ", + ast.Node.InfixOp.Op.SubWrap => " -% ", + ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ", + ast.Node.InfixOp.Op.Range => " ... ", + ast.Node.InfixOp.Op.Catch => |maybe_payload| blk: { + try stream.write(" catch "); + if (maybe_payload) |payload| { + try renderExpression(allocator, stream, tree, indent, payload); + try stream.write(" "); } + break :blk ""; }, - ast.Node.Id.Suspend => { - const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); - if (suspend_node.label) |label| { - try stream.print("{}: ", tree.tokenSlice(label)); + }; + + try stream.write(text); + try renderExpression(allocator, stream, tree, indent, infix_op_node.rhs); + }, + + ast.Node.Id.PrefixOp => { + const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); + + switch (prefix_op_node.op) { + ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { + try stream.write("&"); + if (addr_of_info.align_expr) |align_expr| { + try stream.write("align("); + try renderExpression(allocator, stream, tree, indent, align_expr); + try stream.write(") "); } - try stream.print("{}", tree.tokenSlice(suspend_node.suspend_token)); - - if (suspend_node.body) |body| { - try stack.append(RenderState { .Expression = body }); - try stack.append(RenderState { .Text = " " }); + if (addr_of_info.const_token != null) { + try stream.write("const "); } - - if (suspend_node.payload) |payload| { - try stack.append(RenderState { .Expression = payload }); - try stack.append(RenderState { .Text = " " }); + if (addr_of_info.volatile_token != null) { + try stream.write("volatile "); } }, - ast.Node.Id.InfixOp => { - const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); - try stack.append(RenderState { .Expression = prefix_op_node.rhs }); - - if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) { - if (prefix_op_node.op.Catch) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); - } - try stack.append(RenderState { .Text = " catch " }); - } else { - const text = switch (prefix_op_node.op) { - ast.Node.InfixOp.Op.Add => " + ", - ast.Node.InfixOp.Op.AddWrap => " +% ", - ast.Node.InfixOp.Op.ArrayCat => " ++ ", - ast.Node.InfixOp.Op.ArrayMult => " ** ", - ast.Node.InfixOp.Op.Assign => " = ", - ast.Node.InfixOp.Op.AssignBitAnd => " &= ", - ast.Node.InfixOp.Op.AssignBitOr => " |= ", - ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ", - ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ", - ast.Node.InfixOp.Op.AssignBitXor => " ^= ", - ast.Node.InfixOp.Op.AssignDiv => " /= ", - ast.Node.InfixOp.Op.AssignMinus => " -= ", - ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ", - ast.Node.InfixOp.Op.AssignMod => " %= ", - ast.Node.InfixOp.Op.AssignPlus => " += ", - ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ", - ast.Node.InfixOp.Op.AssignTimes => " *= ", - ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ", - ast.Node.InfixOp.Op.BangEqual => " != ", - ast.Node.InfixOp.Op.BitAnd => " & ", - ast.Node.InfixOp.Op.BitOr => " | ", - ast.Node.InfixOp.Op.BitShiftLeft => " << ", - ast.Node.InfixOp.Op.BitShiftRight => " >> ", - ast.Node.InfixOp.Op.BitXor => " ^ ", - ast.Node.InfixOp.Op.BoolAnd => " and ", - ast.Node.InfixOp.Op.BoolOr => " or ", - ast.Node.InfixOp.Op.Div => " / ", - ast.Node.InfixOp.Op.EqualEqual => " == ", - ast.Node.InfixOp.Op.ErrorUnion => "!", - ast.Node.InfixOp.Op.GreaterOrEqual => " >= ", - ast.Node.InfixOp.Op.GreaterThan => " > ", - ast.Node.InfixOp.Op.LessOrEqual => " <= ", - ast.Node.InfixOp.Op.LessThan => " < ", - ast.Node.InfixOp.Op.MergeErrorSets => " || ", - ast.Node.InfixOp.Op.Mod => " % ", - ast.Node.InfixOp.Op.Mult => " * ", - ast.Node.InfixOp.Op.MultWrap => " *% ", - ast.Node.InfixOp.Op.Period => ".", - ast.Node.InfixOp.Op.Sub => " - ", - ast.Node.InfixOp.Op.SubWrap => " -% ", - ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ", - ast.Node.InfixOp.Op.Range => " ... ", - ast.Node.InfixOp.Op.Catch => unreachable, - }; - - try stack.append(RenderState { .Text = text }); + ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { + try stream.write("[]"); + if (addr_of_info.align_expr) |align_expr| { + try stream.print("align("); + try renderExpression(allocator, stream, tree, indent, align_expr); + try stream.print(") "); } - try stack.append(RenderState { .Expression = prefix_op_node.lhs }); - }, - ast.Node.Id.PrefixOp => { - const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); - try stack.append(RenderState { .Expression = prefix_op_node.rhs }); - switch (prefix_op_node.op) { - ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { - try stream.write("&"); - if (addr_of_info.volatile_token != null) { - try stack.append(RenderState { .Text = "volatile "}); - } - if (addr_of_info.const_token != null) { - try stack.append(RenderState { .Text = "const "}); - } - if (addr_of_info.align_expr) |align_expr| { - try stream.print("align("); - try stack.append(RenderState { .Text = ") "}); - try stack.append(RenderState { .Expression = align_expr}); - } - }, - ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { - try stream.write("[]"); - if (addr_of_info.volatile_token != null) { - try stack.append(RenderState { .Text = "volatile "}); - } - if (addr_of_info.const_token != null) { - try stack.append(RenderState { .Text = "const "}); - } - if (addr_of_info.align_expr) |align_expr| { - try stream.print("align("); - try stack.append(RenderState { .Text = ") "}); - try stack.append(RenderState { .Expression = align_expr}); - } - }, - ast.Node.PrefixOp.Op.ArrayType => |array_index| { - try stack.append(RenderState { .Text = "]"}); - try stack.append(RenderState { .Expression = array_index}); - try stack.append(RenderState { .Text = "["}); - }, - ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), - ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), - ast.Node.PrefixOp.Op.Deref => try stream.write("*"), - ast.Node.PrefixOp.Op.Negation => try stream.write("-"), - ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"), - ast.Node.PrefixOp.Op.Try => try stream.write("try "), - ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"), - ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"), - ast.Node.PrefixOp.Op.Await => try stream.write("await "), - ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "), - ast.Node.PrefixOp.Op.Resume => try stream.write("resume "), + if (addr_of_info.const_token != null) { + try stream.print("const "); } - }, - ast.Node.Id.SuffixOp => { - const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base); - - switch (suffix_op.op) { - @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| { - try stack.append(RenderState { .Text = ")"}); - var i = call_info.params.len; - while (i != 0) { - i -= 1; - const param_node = *call_info.params.at(i); - try stack.append(RenderState { .Expression = param_node}); - if (i != 0) { - try stack.append(RenderState { .Text = ", " }); - } - } - try stack.append(RenderState { .Text = "("}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - - if (call_info.async_attr) |async_attr| { - try stack.append(RenderState { .Text = " "}); - try stack.append(RenderState { .Expression = &async_attr.base }); - } - }, - ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { - try stack.append(RenderState { .Text = "]"}); - try stack.append(RenderState { .Expression = index_expr}); - try stack.append(RenderState { .Text = "["}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - }, - @TagType(ast.Node.SuffixOp.Op).Slice => |range| { - try stack.append(RenderState { .Text = "]"}); - if (range.end) |end| { - try stack.append(RenderState { .Expression = end}); - } - try stack.append(RenderState { .Text = ".."}); - try stack.append(RenderState { .Expression = range.start}); - try stack.append(RenderState { .Text = "["}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - }, - ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| { - if (field_inits.len == 0) { - try stack.append(RenderState { .Text = "{}" }); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - continue; - } - if (field_inits.len == 1) { - const field_init = *field_inits.at(0); - - try stack.append(RenderState { .Text = " }" }); - try stack.append(RenderState { .Expression = field_init }); - try stack.append(RenderState { .Text = "{ " }); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - continue; - } - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n" }); - var i = field_inits.len; - while (i != 0) { - i -= 1; - const field_init = *field_inits.at(i); - if (field_init.id != ast.Node.Id.LineComment) { - try stack.append(RenderState { .Text = "," }); - } - try stack.append(RenderState { .Expression = field_init }); - try stack.append(RenderState.PrintIndent); - if (i != 0) { - try stack.append(RenderState { .Text = blk: { - const prev_node = *field_inits.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, field_init.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }}); - } - } - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "{\n"}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - }, - ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| { - if (exprs.len == 0) { - try stack.append(RenderState { .Text = "{}" }); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - continue; - } - if (exprs.len == 1) { - const expr = *exprs.at(0); - - try stack.append(RenderState { .Text = "}" }); - try stack.append(RenderState { .Expression = expr }); - try stack.append(RenderState { .Text = "{" }); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - continue; - } - - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - var i = exprs.len; - while (i != 0) { - i -= 1; - const expr = *exprs.at(i); - try stack.append(RenderState { .Text = ",\n" }); - try stack.append(RenderState { .Expression = expr }); - try stack.append(RenderState.PrintIndent); - } - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "{\n"}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - }, + if (addr_of_info.volatile_token != null) { + try stream.print("volatile "); } }, - ast.Node.Id.ControlFlowExpression => { - const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); + ast.Node.PrefixOp.Op.ArrayType => |array_index| { + try stream.print("["); + try renderExpression(allocator, stream, tree, indent, array_index); + try stream.print("]"); + }, + ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), + ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), + ast.Node.PrefixOp.Op.Deref => try stream.write("*"), + ast.Node.PrefixOp.Op.Negation => try stream.write("-"), + ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"), + ast.Node.PrefixOp.Op.Try => try stream.write("try "), + ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"), + ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"), + ast.Node.PrefixOp.Op.Await => try stream.write("await "), + ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "), + ast.Node.PrefixOp.Op.Resume => try stream.write("resume "), + } + + try renderExpression(allocator, stream, tree, indent, prefix_op_node.rhs); + }, - if (flow_expr.rhs) |rhs| { - try stack.append(RenderState { .Expression = rhs }); - try stack.append(RenderState { .Text = " " }); + ast.Node.Id.SuffixOp => { + const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base); + + switch (suffix_op.op) { + @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| { + if (call_info.async_attr) |async_attr| { + try renderExpression(allocator, stream, tree, indent, &async_attr.base); + try stream.write(" "); } - switch (flow_expr.kind) { - ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { - try stream.print("break"); - if (maybe_label) |label| { - try stream.print(" :"); - try stack.append(RenderState { .Expression = label }); - } - }, - ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { - try stream.print("continue"); - if (maybe_label) |label| { - try stream.print(" :"); - try stack.append(RenderState { .Expression = label }); - } - }, - ast.Node.ControlFlowExpression.Kind.Return => { - try stream.print("return"); - }, + try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); + try stream.write("("); + var it = call_info.params.iterator(0); + while (it.next()) |param_node| { + try renderExpression(allocator, stream, tree, indent, *param_node); + if (it.peek() != null) { + try stream.write(", "); + } } + + try stream.write(")"); }, - ast.Node.Id.Payload => { - const payload = @fieldParentPtr(ast.Node.Payload, "base", base); - try stack.append(RenderState { .Text = "|"}); - try stack.append(RenderState { .Expression = payload.error_symbol }); - try stack.append(RenderState { .Text = "|"}); + + ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { + try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); + try stream.write("["); + try renderExpression(allocator, stream, tree, indent, index_expr); + try stream.write("]"); }, - ast.Node.Id.PointerPayload => { - const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); - try stack.append(RenderState { .Text = "|"}); - try stack.append(RenderState { .Expression = payload.value_symbol }); - if (payload.ptr_token) |ptr_token| { - try stack.append(RenderState { .Text = tree.tokenSlice(ptr_token) }); + @TagType(ast.Node.SuffixOp.Op).Slice => |range| { + try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); + try stream.write("["); + try renderExpression(allocator, stream, tree, indent, range.start); + try stream.write(".."); + if (range.end) |end| { + try renderExpression(allocator, stream, tree, indent, end); } - - try stack.append(RenderState { .Text = "|"}); + try stream.write("]"); }, - ast.Node.Id.PointerIndexPayload => { - const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); - try stack.append(RenderState { .Text = "|"}); - if (payload.index_symbol) |index_symbol| { - try stack.append(RenderState { .Expression = index_symbol }); - try stack.append(RenderState { .Text = ", "}); + ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| { + if (field_inits.len == 0) { + try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); + try stream.write("{}"); + return; } - try stack.append(RenderState { .Expression = payload.value_symbol }); + if (field_inits.len == 1) { + const field_init = *field_inits.at(0); - if (payload.ptr_token) |ptr_token| { - try stack.append(RenderState { .Text = tree.tokenSlice(ptr_token) }); + try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); + try stream.write("{ "); + try renderExpression(allocator, stream, tree, indent, field_init); + try stream.write(" }"); + return; } - try stack.append(RenderState { .Text = "|"}); - }, - ast.Node.Id.GroupedExpression => { - const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); - try stack.append(RenderState { .Text = ")"}); - try stack.append(RenderState { .Expression = grouped_expr.expr }); - try stack.append(RenderState { .Text = "("}); - }, - ast.Node.Id.FieldInitializer => { - const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); - try stream.print(".{} = ", tree.tokenSlice(field_init.name_token)); - try stack.append(RenderState { .Expression = field_init.expr }); - }, - ast.Node.Id.IntegerLiteral => { - const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(integer_literal.token)); - }, - ast.Node.Id.FloatLiteral => { - const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(float_literal.token)); - }, - ast.Node.Id.StringLiteral => { - const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(string_literal.token)); - }, - ast.Node.Id.CharLiteral => { - const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(char_literal.token)); - }, - ast.Node.Id.BoolLiteral => { - const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(bool_literal.token)); - }, - ast.Node.Id.NullLiteral => { - const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(null_literal.token)); - }, - ast.Node.Id.ThisLiteral => { - const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(this_literal.token)); - }, - ast.Node.Id.Unreachable => { - const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); - try stream.print("{}", tree.tokenSlice(unreachable_node.token)); - }, - ast.Node.Id.ErrorType => { - const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); - try stream.print("{}", tree.tokenSlice(error_type.token)); - }, - ast.Node.Id.VarType => { - const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); - try stream.print("{}", tree.tokenSlice(var_type.token)); - }, - ast.Node.Id.ContainerDecl => { - const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); + try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); + try stream.write("{\n"); - switch (container_decl.layout) { - ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "), - ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "), - ast.Node.ContainerDecl.Layout.Auto => { }, - } + const new_indent = indent + indent_delta; - switch (container_decl.kind) { - ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"), - ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"), - ast.Node.ContainerDecl.Kind.Union => try stream.print("union"), - } - - if (container_decl.fields_and_decls.len == 0) { - try stack.append(RenderState { .Text = "{}"}); - } else { - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n"}); - - var i = container_decl.fields_and_decls.len; - while (i != 0) { - i -= 1; - const node = *container_decl.fields_and_decls.at(i); - try stack.append(RenderState { .TopLevelDecl = node}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = *container_decl.fields_and_decls.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - } - break :blk "\n"; - }, - }); + var it = field_inits.iterator(0); + while (it.next()) |field_init| { + try stream.writeByteNTimes(' ', new_indent); + try renderExpression(allocator, stream, tree, new_indent, *field_init); + if ((*field_init).id != ast.Node.Id.LineComment) { + try stream.write(","); + } + if (it.peek()) |next_field_init| { + const n = if (nodeLineOffset(tree, *field_init, *next_field_init) >= 2) u8(2) else u8(1); + try stream.writeByteNTimes('\n', n); } - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = "{"}); } - switch (container_decl.init_arg_expr) { - ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}), - ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { - if (enum_tag_type) |expr| { - try stack.append(RenderState { .Text = ")) "}); - try stack.append(RenderState { .Expression = expr}); - try stack.append(RenderState { .Text = "(enum("}); - } else { - try stack.append(RenderState { .Text = "(enum) "}); - } - }, - ast.Node.ContainerDecl.InitArg.Type => |type_expr| { - try stack.append(RenderState { .Text = ") "}); - try stack.append(RenderState { .Expression = type_expr}); - try stack.append(RenderState { .Text = "("}); - }, - } + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent); + try stream.write("}"); }, - ast.Node.Id.ErrorSetDecl => { - const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); - if (err_set_decl.decls.len == 0) { - try stream.write("error{}"); - continue; + ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| { + + if (exprs.len == 0) { + try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); + try stream.write("{}"); + return; + } + if (exprs.len == 1) { + const expr = *exprs.at(0); + + try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); + try stream.write("{"); + try renderExpression(allocator, stream, tree, indent, expr); + try stream.write("}"); + return; } - if (err_set_decl.decls.len == 1) blk: { - const node = *err_set_decl.decls.at(0); + try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); - // if there are any doc comments or same line comments - // don't try to put it all on one line - if (node.cast(ast.Node.ErrorTag)) |tag| { - if (tag.doc_comments != null) break :blk; - } else { - break :blk; - } + const new_indent = indent + indent_delta; + try stream.write("{\n"); + var it = exprs.iterator(0); + while (it.next()) |expr| { + try stream.writeByteNTimes(' ', new_indent); + try renderExpression(allocator, stream, tree, new_indent, *expr); + try stream.write(","); - try stream.write("error{"); - try stack.append(RenderState { .Text = "}" }); - try stack.append(RenderState { .TopLevelDecl = node }); - continue; + if (it.peek()) |next_expr| { + const n = if (nodeLineOffset(tree, *expr, *next_expr) >= 2) u8(2) else u8(1); + try stream.writeByteNTimes('\n', n); + } } - try stream.write("error{"); + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent); + try stream.write("}"); + }, + } + }, - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n"}); + ast.Node.Id.ControlFlowExpression => { + const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); - var i = err_set_decl.decls.len; - while (i != 0) { - i -= 1; - const node = *err_set_decl.decls.at(i); - if (node.id != ast.Node.Id.LineComment) { - try stack.append(RenderState { .Text = "," }); - } - try stack.append(RenderState { .TopLevelDecl = node }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = *err_set_decl.decls.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - } - break :blk "\n"; - }, - }); + switch (flow_expr.kind) { + ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { + try stream.print("break"); + if (maybe_label) |label| { + try stream.print(" :"); + try renderExpression(allocator, stream, tree, indent, label); } - try stack.append(RenderState { .Indent = indent + indent_delta}); }, - ast.Node.Id.MultilineStringLiteral => { - const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); - try stream.print("\n"); - - var i : usize = 0; - while (i < multiline_str_literal.lines.len) : (i += 1) { - const t = *multiline_str_literal.lines.at(i); - try stream.writeByteNTimes(' ', indent + indent_delta); - try stream.print("{}", tree.tokenSlice(t)); + ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { + try stream.print("continue"); + if (maybe_label) |label| { + try stream.print(" :"); + try renderExpression(allocator, stream, tree, indent, label); } - try stream.writeByteNTimes(' ', indent); - }, - ast.Node.Id.UndefinedLiteral => { - const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(undefined_literal.token)); }, - ast.Node.Id.BuiltinCall => { - const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); - try stream.print("{}(", tree.tokenSlice(builtin_call.builtin_token)); - try stack.append(RenderState { .Text = ")"}); - var i = builtin_call.params.len; - while (i != 0) { - i -= 1; - const param_node = *builtin_call.params.at(i); - try stack.append(RenderState { .Expression = param_node}); - if (i != 0) { - try stack.append(RenderState { .Text = ", " }); - } - } + ast.Node.ControlFlowExpression.Kind.Return => { + try stream.print("return"); }, - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); - - switch (fn_proto.return_type) { - ast.Node.FnProto.ReturnType.Explicit => |node| { - try stack.append(RenderState { .Expression = node}); - }, - ast.Node.FnProto.ReturnType.InferErrorSet => |node| { - try stack.append(RenderState { .Expression = node}); - try stack.append(RenderState { .Text = "!"}); - }, - } - if (fn_proto.align_expr) |align_expr| { - try stack.append(RenderState { .Text = ") " }); - try stack.append(RenderState { .Expression = align_expr}); - try stack.append(RenderState { .Text = "align(" }); - } + } - try stack.append(RenderState { .Text = ") " }); - var i = fn_proto.params.len; - while (i != 0) { - i -= 1; - const param_decl_node = *fn_proto.params.at(i); - try stack.append(RenderState { .ParamDecl = param_decl_node}); - if (i != 0) { - try stack.append(RenderState { .Text = ", " }); - } - } + if (flow_expr.rhs) |rhs| { + try stream.write(" "); + try renderExpression(allocator, stream, tree, indent, rhs); + } + }, - try stack.append(RenderState { .Text = "(" }); - if (fn_proto.name_token) |name_token| { - try stack.append(RenderState { .Text = tree.tokenSlice(name_token) }); - try stack.append(RenderState { .Text = " " }); - } + ast.Node.Id.Payload => { + const payload = @fieldParentPtr(ast.Node.Payload, "base", base); - try stack.append(RenderState { .Text = "fn" }); + try stream.write("|"); + try renderExpression(allocator, stream, tree, indent, payload.error_symbol); + try stream.write("|"); + }, - if (fn_proto.async_attr) |async_attr| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = &async_attr.base }); - } + ast.Node.Id.PointerPayload => { + const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); - if (fn_proto.cc_token) |cc_token| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = tree.tokenSlice(cc_token) }); - } + try stream.write("|"); + if (payload.ptr_token) |ptr_token| { + try stream.write(tree.tokenSlice(ptr_token)); + } + try renderExpression(allocator, stream, tree, indent, payload.value_symbol); + try stream.write("|"); + }, - if (fn_proto.lib_name) |lib_name| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = lib_name }); - } - if (fn_proto.extern_export_inline_token) |extern_export_inline_token| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) }); - } + ast.Node.Id.PointerIndexPayload => { + const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); - if (fn_proto.visib_token) |visib_token_index| { - const visib_token = tree.tokens.at(visib_token_index); - assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export); - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = tree.tokenSlice(visib_token_index) }); - } - }, - ast.Node.Id.PromiseType => { - const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base); - try stream.write(tree.tokenSlice(promise_type.promise_token)); - if (promise_type.result) |result| { - try stream.write(tree.tokenSlice(result.arrow_token)); - try stack.append(RenderState { .Expression = result.return_type}); + try stream.write("|"); + if (payload.ptr_token) |ptr_token| { + try stream.write(tree.tokenSlice(ptr_token)); + } + try renderExpression(allocator, stream, tree, indent, payload.value_symbol); + + if (payload.index_symbol) |index_symbol| { + try stream.write(", "); + try renderExpression(allocator, stream, tree, indent, index_symbol); + } + + try stream.write("|"); + }, + + ast.Node.Id.GroupedExpression => { + const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); + + try stream.write("("); + try renderExpression(allocator, stream, tree, indent, grouped_expr.expr); + try stream.write(")"); + }, + + ast.Node.Id.FieldInitializer => { + const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); + + try stream.print(".{} = ", tree.tokenSlice(field_init.name_token)); + try renderExpression(allocator, stream, tree, indent, field_init.expr); + }, + + ast.Node.Id.IntegerLiteral => { + const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(integer_literal.token)); + }, + ast.Node.Id.FloatLiteral => { + const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(float_literal.token)); + }, + ast.Node.Id.StringLiteral => { + const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(string_literal.token)); + }, + ast.Node.Id.CharLiteral => { + const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(char_literal.token)); + }, + ast.Node.Id.BoolLiteral => { + const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(bool_literal.token)); + }, + ast.Node.Id.NullLiteral => { + const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(null_literal.token)); + }, + ast.Node.Id.ThisLiteral => { + const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(this_literal.token)); + }, + ast.Node.Id.Unreachable => { + const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); + try stream.print("{}", tree.tokenSlice(unreachable_node.token)); + }, + ast.Node.Id.ErrorType => { + const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); + try stream.print("{}", tree.tokenSlice(error_type.token)); + }, + ast.Node.Id.VarType => { + const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); + try stream.print("{}", tree.tokenSlice(var_type.token)); + }, + ast.Node.Id.ContainerDecl => { + const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); + + switch (container_decl.layout) { + ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "), + ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "), + ast.Node.ContainerDecl.Layout.Auto => { }, + } + + switch (container_decl.kind) { + ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"), + ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"), + ast.Node.ContainerDecl.Kind.Union => try stream.print("union"), + } + + switch (container_decl.init_arg_expr) { + ast.Node.ContainerDecl.InitArg.None => try stream.write(" "), + ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { + if (enum_tag_type) |expr| { + try stream.write("(enum("); + try renderExpression(allocator, stream, tree, indent, expr); + try stream.write(")) "); + } else { + try stream.write("(enum) "); } }, - ast.Node.Id.LineComment => { - const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base); - try stream.write(tree.tokenSlice(line_comment_node.token)); + ast.Node.ContainerDecl.InitArg.Type => |type_expr| { + try stream.write("("); + try renderExpression(allocator, stream, tree, indent, type_expr); + try stream.write(") "); }, - ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes - ast.Node.Id.Switch => { - const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); + } - try stream.print("{} (", tree.tokenSlice(switch_node.switch_token)); + if (container_decl.fields_and_decls.len == 0) { + try stream.write("{}"); + } else { + try stream.write("{\n"); + const new_indent = indent + indent_delta; - if (switch_node.cases.len == 0) { - try stack.append(RenderState { .Text = ") {}"}); - try stack.append(RenderState { .Expression = switch_node.expr }); - continue; - } + var it = container_decl.fields_and_decls.iterator(0); + while (it.next()) |decl| { + try stream.writeByteNTimes(' ', new_indent); + try renderTopLevelDecl(allocator, stream, tree, new_indent, *decl); - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n"}); - - var i = switch_node.cases.len; - while (i != 0) { - i -= 1; - const node = *switch_node.cases.at(i); - try stack.append(RenderState { .Expression = node}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = *switch_node.cases.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - } - break :blk "\n"; - }, - }); + if (it.peek()) |next_decl| { + const n = if (nodeLineOffset(tree, *decl, *next_decl) >= 2) u8(2) else u8(1); + try stream.writeByteNTimes('\n', n); } - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = ") {"}); - try stack.append(RenderState { .Expression = switch_node.expr }); - }, - ast.Node.Id.SwitchCase => { - const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); - - try stack.append(RenderState { .Token = switch_case.lastToken() + 1 }); - try stack.append(RenderState { .Expression = switch_case.expr }); - if (switch_case.payload) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); - } - try stack.append(RenderState { .Text = " => "}); + } - var i = switch_case.items.len; - while (i != 0) { - i -= 1; - try stack.append(RenderState { .Expression = *switch_case.items.at(i) }); + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent); + try stream.write("}"); + } + }, - if (i != 0) { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Text = ",\n" }); - } - } + ast.Node.Id.ErrorSetDecl => { + const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); + + if (err_set_decl.decls.len == 0) { + try stream.write("error{}"); + return; + } + + if (err_set_decl.decls.len == 1) blk: { + const node = *err_set_decl.decls.at(0); + + // if there are any doc comments or same line comments + // don't try to put it all on one line + if (node.cast(ast.Node.ErrorTag)) |tag| { + if (tag.doc_comments != null) break :blk; + } else { + break :blk; + } + + + try stream.write("error{"); + try renderTopLevelDecl(allocator, stream, tree, indent, node); + try stream.write("}"); + return; + } + + try stream.write("error{\n"); + const new_indent = indent + indent_delta; + + var it = err_set_decl.decls.iterator(0); + while (it.next()) |node| { + try stream.writeByteNTimes(' ', new_indent); + try renderTopLevelDecl(allocator, stream, tree, new_indent, *node); + if ((*node).id != ast.Node.Id.LineComment) { + try stream.write(","); + } + if (it.peek()) |next_node| { + const n = if (nodeLineOffset(tree, *node, *next_node) >= 2) u8(2) else u8(1); + try stream.writeByteNTimes('\n', n); + } + } + + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent); + try stream.write("}"); + }, + + ast.Node.Id.MultilineStringLiteral => { + const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); + try stream.print("\n"); + + var i : usize = 0; + while (i < multiline_str_literal.lines.len) : (i += 1) { + const t = *multiline_str_literal.lines.at(i); + try stream.writeByteNTimes(' ', indent + indent_delta); + try stream.print("{}", tree.tokenSlice(t)); + } + try stream.writeByteNTimes(' ', indent); + }, + ast.Node.Id.UndefinedLiteral => { + const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(undefined_literal.token)); + }, + + ast.Node.Id.BuiltinCall => { + const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); + try stream.print("{}(", tree.tokenSlice(builtin_call.builtin_token)); + + var it = builtin_call.params.iterator(0); + while (it.next()) |param_node| { + try renderExpression(allocator, stream, tree, indent, *param_node); + if (it.peek() != null) { + try stream.write(", "); + } + } + try stream.write(")"); + }, + + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); + + if (fn_proto.visib_token) |visib_token_index| { + const visib_token = tree.tokens.at(visib_token_index); + assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export); + try stream.print("{} ", tree.tokenSlice(visib_token_index)); + } + + if (fn_proto.extern_export_inline_token) |extern_export_inline_token| { + try stream.print("{} ", tree.tokenSlice(extern_export_inline_token)); + } + + if (fn_proto.lib_name) |lib_name| { + try renderExpression(allocator, stream, tree, indent, lib_name); + try stream.write(" "); + } + + if (fn_proto.cc_token) |cc_token| { + try stream.print("{} ", tree.tokenSlice(cc_token)); + } + + if (fn_proto.async_attr) |async_attr| { + try renderExpression(allocator, stream, tree, indent, &async_attr.base); + try stream.write(" "); + } + + try stream.write("fn"); + + if (fn_proto.name_token) |name_token| { + try stream.print(" {}", tree.tokenSlice(name_token)); + } + + try stream.write("("); + + var it = fn_proto.params.iterator(0); + while (it.next()) |param_decl_node| { + try renderParamDecl(allocator, stream, tree, indent, *param_decl_node); + + if (it.peek() != null) { + try stream.write(", "); + } + } + + try stream.write(") "); + + if (fn_proto.align_expr) |align_expr| { + try stream.write("align("); + try renderExpression(allocator, stream, tree, indent, align_expr); + try stream.write(") "); + } + + switch (fn_proto.return_type) { + ast.Node.FnProto.ReturnType.Explicit => |node| { + try renderExpression(allocator, stream, tree, indent, node); }, - ast.Node.Id.SwitchElse => { - const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); - try stream.print("{}", tree.tokenSlice(switch_else.token)); + ast.Node.FnProto.ReturnType.InferErrorSet => |node| { + try stream.write("!"); + try renderExpression(allocator, stream, tree, indent, node); }, - ast.Node.Id.Else => { - const else_node = @fieldParentPtr(ast.Node.Else, "base", base); - try stream.print("{}", tree.tokenSlice(else_node.else_token)); - - switch (else_node.body.id) { - ast.Node.Id.Block, ast.Node.Id.If, - ast.Node.Id.For, ast.Node.Id.While, - ast.Node.Id.Switch => { - try stream.print(" "); - try stack.append(RenderState { .Expression = else_node.body }); - }, - else => { - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Expression = else_node.body }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); - } - } + } - if (else_node.payload) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); - } - }, - ast.Node.Id.While => { - const while_node = @fieldParentPtr(ast.Node.While, "base", base); - if (while_node.label) |label| { - try stream.print("{}: ", tree.tokenSlice(label)); - } + }, - if (while_node.inline_token) |inline_token| { - try stream.print("{} ", tree.tokenSlice(inline_token)); - } + ast.Node.Id.PromiseType => { + const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base); + try stream.write(tree.tokenSlice(promise_type.promise_token)); + if (promise_type.result) |result| { + try stream.write(tree.tokenSlice(result.arrow_token)); + try renderExpression(allocator, stream, tree, indent, result.return_type); + } + }, - try stream.print("{} ", tree.tokenSlice(while_node.while_token)); + ast.Node.Id.LineComment => { + const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base); + try stream.write(tree.tokenSlice(line_comment_node.token)); + }, - if (while_node.@"else") |@"else"| { - try stack.append(RenderState { .Expression = &@"else".base }); + ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes - if (while_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Text = " " }); - } else { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Text = "\n" }); - } - } + ast.Node.Id.Switch => { + const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); - if (while_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Expression = while_node.body }); - try stack.append(RenderState { .Text = " " }); - } else { - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Expression = while_node.body }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); - } + try stream.print("{} (", tree.tokenSlice(switch_node.switch_token)); + if (switch_node.cases.len == 0) { + try renderExpression(allocator, stream, tree, indent, switch_node.expr); + try stream.write(") {}"); + return; + } - if (while_node.continue_expr) |continue_expr| { - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = continue_expr }); - try stack.append(RenderState { .Text = ": (" }); - try stack.append(RenderState { .Text = " " }); - } + try renderExpression(allocator, stream, tree, indent, switch_node.expr); + try stream.write(") {\n"); - if (while_node.payload) |payload| { - try stack.append(RenderState { .Expression = payload }); - try stack.append(RenderState { .Text = " " }); - } + const new_indent = indent + indent_delta; - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = while_node.condition }); - try stack.append(RenderState { .Text = "(" }); - }, - ast.Node.Id.For => { - const for_node = @fieldParentPtr(ast.Node.For, "base", base); - if (for_node.label) |label| { - try stream.print("{}: ", tree.tokenSlice(label)); - } + var it = switch_node.cases.iterator(0); + while (it.next()) |node| { + try stream.writeByteNTimes(' ', new_indent); + try renderExpression(allocator, stream, tree, new_indent, *node); - if (for_node.inline_token) |inline_token| { - try stream.print("{} ", tree.tokenSlice(inline_token)); - } + if (it.peek()) |next_node| { + const n = if (nodeLineOffset(tree, *node, *next_node) >= 2) u8(2) else u8(1); + try stream.writeByteNTimes('\n', n); + } + } - try stream.print("{} ", tree.tokenSlice(for_node.for_token)); + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent); + try stream.write("}"); + }, - if (for_node.@"else") |@"else"| { - try stack.append(RenderState { .Expression = &@"else".base }); + ast.Node.Id.SwitchCase => { + const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); - if (for_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Text = " " }); - } else { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Text = "\n" }); - } - } + var it = switch_case.items.iterator(0); + while (it.next()) |node| { + try renderExpression(allocator, stream, tree, indent, *node); - if (for_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Expression = for_node.body }); - try stack.append(RenderState { .Text = " " }); - } else { - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Expression = for_node.body }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); - } + if (it.peek() != null) { + try stream.write(",\n"); + try stream.writeByteNTimes(' ', indent); + } + } - if (for_node.payload) |payload| { - try stack.append(RenderState { .Expression = payload }); - try stack.append(RenderState { .Text = " " }); - } + try stream.write(" => "); - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = for_node.array_expr }); - try stack.append(RenderState { .Text = "(" }); - }, - ast.Node.Id.If => { - const if_node = @fieldParentPtr(ast.Node.If, "base", base); - try stream.print("{} ", tree.tokenSlice(if_node.if_token)); - - switch (if_node.body.id) { - ast.Node.Id.Block, ast.Node.Id.If, - ast.Node.Id.For, ast.Node.Id.While, - ast.Node.Id.Switch => { - if (if_node.@"else") |@"else"| { - try stack.append(RenderState { .Expression = &@"else".base }); - - if (if_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Text = " " }); - } else { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Text = "\n" }); - } - } - }, - else => { - if (if_node.@"else") |@"else"| { - try stack.append(RenderState { .Expression = @"else".body }); - - if (@"else".payload) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); - } - - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = tree.tokenSlice(@"else".else_token) }); - try stack.append(RenderState { .Text = " " }); - } + if (switch_case.payload) |payload| { + try renderExpression(allocator, stream, tree, indent, payload); + try stream.write(" "); + } + + try renderExpression(allocator, stream, tree, indent, switch_case.expr); + try renderToken(tree, stream, switch_case.lastToken() + 1, indent, true); + }, + ast.Node.Id.SwitchElse => { + const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); + try stream.print("{}", tree.tokenSlice(switch_else.token)); + }, + ast.Node.Id.Else => { + const else_node = @fieldParentPtr(ast.Node.Else, "base", base); + try stream.print("{}", tree.tokenSlice(else_node.else_token)); + + const block_body = switch (else_node.body.id) { + ast.Node.Id.Block, ast.Node.Id.If, + ast.Node.Id.For, ast.Node.Id.While, + ast.Node.Id.Switch => true, + else => false, + }; + + if (block_body) { + try stream.write(" "); + } + + if (else_node.payload) |payload| { + try renderExpression(allocator, stream, tree, indent, payload); + try stream.write(" "); + } + + if (block_body) { + try renderExpression(allocator, stream, tree, indent, else_node.body); + } else { + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent + indent_delta); + try renderExpression(allocator, stream, tree, indent, else_node.body); + } + }, + + ast.Node.Id.While => { + const while_node = @fieldParentPtr(ast.Node.While, "base", base); + + if (while_node.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } + + if (while_node.inline_token) |inline_token| { + try stream.print("{} ", tree.tokenSlice(inline_token)); + } + + try stream.print("{} (", tree.tokenSlice(while_node.while_token)); + try renderExpression(allocator, stream, tree, indent, while_node.condition); + try stream.write(")"); + + if (while_node.payload) |payload| { + try stream.write(" "); + try renderExpression(allocator, stream, tree, indent, payload); + } + + if (while_node.continue_expr) |continue_expr| { + try stream.write(" : ("); + try renderExpression(allocator, stream, tree, indent, continue_expr); + try stream.write(")"); + } + + if (while_node.body.id == ast.Node.Id.Block) { + try stream.write(" "); + try renderExpression(allocator, stream, tree, indent, while_node.body); + } else { + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent + indent_delta); + try renderExpression(allocator, stream, tree, indent, while_node.body); + } + + if (while_node.@"else") |@"else"| { + if (while_node.body.id == ast.Node.Id.Block) { + try stream.write(" "); + } else { + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent); + } + + try renderExpression(allocator, stream, tree, indent, &@"else".base); + } + }, + + ast.Node.Id.For => { + const for_node = @fieldParentPtr(ast.Node.For, "base", base); + if (for_node.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } + + if (for_node.inline_token) |inline_token| { + try stream.print("{} ", tree.tokenSlice(inline_token)); + } + + try stream.print("{} (", tree.tokenSlice(for_node.for_token)); + try renderExpression(allocator, stream, tree, indent, for_node.array_expr); + try stream.write(")"); + + if (for_node.payload) |payload| { + try stream.write(" "); + try renderExpression(allocator, stream, tree, indent, payload); + } + + if (for_node.body.id == ast.Node.Id.Block) { + try stream.write(" "); + try renderExpression(allocator, stream, tree, indent, for_node.body); + } else { + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent + indent_delta); + try renderExpression(allocator, stream, tree, indent, for_node.body); + } + + if (for_node.@"else") |@"else"| { + if (for_node.body.id == ast.Node.Id.Block) { + try stream.write(" "); + } else { + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent); + } + + try renderExpression(allocator, stream, tree, indent, &@"else".base); + } + }, + + ast.Node.Id.If => { + const if_node = @fieldParentPtr(ast.Node.If, "base", base); + try stream.print("{} (", tree.tokenSlice(if_node.if_token)); + + try renderExpression(allocator, stream, tree, indent, if_node.condition); + try renderToken(tree, stream, if_node.condition.lastToken() + 1, indent, false); + + if (if_node.payload) |payload| { + try renderExpression(allocator, stream, tree, indent, payload); + try stream.write(" "); + } + + try renderExpression(allocator, stream, tree, indent, if_node.body); + + switch (if_node.body.id) { + ast.Node.Id.Block, ast.Node.Id.If, ast.Node.Id.For, ast.Node.Id.While, ast.Node.Id.Switch => { + if (if_node.@"else") |@"else"| { + if (if_node.body.id == ast.Node.Id.Block) { + try stream.write(" "); + } else { + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent); } + + try renderExpression(allocator, stream, tree, indent, &@"else".base); } + }, + else => { + if (if_node.@"else") |@"else"| { + try stream.print(" {} ", tree.tokenSlice(@"else".else_token)); - try stack.append(RenderState { .Expression = if_node.body }); + if (@"else".payload) |payload| { + try renderExpression(allocator, stream, tree, indent, payload); + try stream.write(" "); + } - if (if_node.payload) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); + try renderExpression(allocator, stream, tree, indent, @"else".body); } + } + } + }, - try stack.append(RenderState { .NonBreakToken = if_node.condition.lastToken() + 1 }); - try stack.append(RenderState { .Expression = if_node.condition }); - try stack.append(RenderState { .Text = "(" }); - }, - ast.Node.Id.Asm => { - const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); - try stream.print("{} ", tree.tokenSlice(asm_node.asm_token)); + ast.Node.Id.Asm => { + const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); + try stream.print("{} ", tree.tokenSlice(asm_node.asm_token)); - if (asm_node.volatile_token) |volatile_token| { - try stream.print("{} ", tree.tokenSlice(volatile_token)); - } + if (asm_node.volatile_token) |volatile_token| { + try stream.print("{} ", tree.tokenSlice(volatile_token)); + } - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = ")" }); - { - var i = asm_node.clobbers.len; - while (i != 0) { - i -= 1; - try stack.append(RenderState { .Expression = *asm_node.clobbers.at(i) }); - - if (i != 0) { - try stack.append(RenderState { .Text = ", " }); - } - } + try stream.print("("); + try renderExpression(allocator, stream, tree, indent, asm_node.template); + try stream.print("\n"); + const indent_once = indent + indent_delta; + try stream.writeByteNTimes(' ', indent_once); + try stream.print(": "); + const indent_extra = indent_once + 2; + + { + var it = asm_node.outputs.iterator(0); + while (it.next()) |asm_output| { + const node = &(*asm_output).base; + try renderExpression(allocator, stream, tree, indent_extra, node); + + if (it.peek()) |next_asm_output| { + const next_node = &(*next_asm_output).base; + const n = if (nodeLineOffset(tree, node, next_node) >= 2) u8(2) else u8(1); + try stream.writeByte(','); + try stream.writeByteNTimes('\n', n); + try stream.writeByteNTimes(' ', indent_extra); } - try stack.append(RenderState { .Text = ": " }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); - { - var i = asm_node.inputs.len; - while (i != 0) { - i -= 1; - const node = *asm_node.inputs.at(i); - try stack.append(RenderState { .Expression = &node.base}); - - if (i != 0) { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - const prev_node = *asm_node.inputs.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }, - }); - try stack.append(RenderState { .Text = "," }); - } - } + } + } + + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent_once); + try stream.write(": "); + + { + var it = asm_node.inputs.iterator(0); + while (it.next()) |asm_input| { + const node = &(*asm_input).base; + try renderExpression(allocator, stream, tree, indent_extra, node); + + if (it.peek()) |next_asm_input| { + const next_node = &(*next_asm_input).base; + const n = if (nodeLineOffset(tree, node, next_node) >= 2) u8(2) else u8(1); + try stream.writeByte(','); + try stream.writeByteNTimes('\n', n); + try stream.writeByteNTimes(' ', indent_extra); } - try stack.append(RenderState { .Indent = indent + indent_delta + 2}); - try stack.append(RenderState { .Text = ": "}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = "\n" }); - { - var i = asm_node.outputs.len; - while (i != 0) { - i -= 1; - const node = *asm_node.outputs.at(i); - try stack.append(RenderState { .Expression = &node.base}); - - if (i != 0) { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - const prev_node = *asm_node.outputs.at(i - 1); - const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; - const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }, - }); - try stack.append(RenderState { .Text = "," }); - } - } + } + } + + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent_once); + try stream.write(": "); + + { + var it = asm_node.clobbers.iterator(0); + while (it.next()) |node| { + try renderExpression(allocator, stream, tree, indent_once, *node); + + if (it.peek() != null) { + try stream.write(", "); } - try stack.append(RenderState { .Indent = indent + indent_delta + 2}); - try stack.append(RenderState { .Text = ": "}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = "\n" }); - try stack.append(RenderState { .Expression = asm_node.template }); - try stack.append(RenderState { .Text = "(" }); - }, - ast.Node.Id.AsmInput => { - const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); - - try stack.append(RenderState { .Text = ")"}); - try stack.append(RenderState { .Expression = asm_input.expr}); - try stack.append(RenderState { .Text = " ("}); - try stack.append(RenderState { .Expression = asm_input.constraint }); - try stack.append(RenderState { .Text = "] "}); - try stack.append(RenderState { .Expression = asm_input.symbolic_name }); - try stack.append(RenderState { .Text = "["}); + } + } + + try stream.write(")"); + }, + + ast.Node.Id.AsmInput => { + const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); + + try stream.write("["); + try renderExpression(allocator, stream, tree, indent, asm_input.symbolic_name); + try stream.write("] "); + try renderExpression(allocator, stream, tree, indent, asm_input.constraint); + try stream.write(" ("); + try renderExpression(allocator, stream, tree, indent, asm_input.expr); + try stream.write(")"); + }, + + ast.Node.Id.AsmOutput => { + const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); + + try stream.write("["); + try renderExpression(allocator, stream, tree, indent, asm_output.symbolic_name); + try stream.write("] "); + try renderExpression(allocator, stream, tree, indent, asm_output.constraint); + try stream.write(" ("); + + switch (asm_output.kind) { + ast.Node.AsmOutput.Kind.Variable => |variable_name| { + try renderExpression(allocator, stream, tree, indent, &variable_name.base); }, - ast.Node.Id.AsmOutput => { - const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); - - try stack.append(RenderState { .Text = ")"}); - switch (asm_output.kind) { - ast.Node.AsmOutput.Kind.Variable => |variable_name| { - try stack.append(RenderState { .Expression = &variable_name.base}); - }, - ast.Node.AsmOutput.Kind.Return => |return_type| { - try stack.append(RenderState { .Expression = return_type}); - try stack.append(RenderState { .Text = "-> "}); - }, - } - try stack.append(RenderState { .Text = " ("}); - try stack.append(RenderState { .Expression = asm_output.constraint }); - try stack.append(RenderState { .Text = "] "}); - try stack.append(RenderState { .Expression = asm_output.symbolic_name }); - try stack.append(RenderState { .Text = "["}); + ast.Node.AsmOutput.Kind.Return => |return_type| { + try stream.write("-> "); + try renderExpression(allocator, stream, tree, indent, return_type); }, + } - ast.Node.Id.StructField, - ast.Node.Id.UnionTag, - ast.Node.Id.EnumTag, - ast.Node.Id.ErrorTag, - ast.Node.Id.Root, - ast.Node.Id.VarDecl, - ast.Node.Id.Use, - ast.Node.Id.TestDecl, - ast.Node.Id.ParamDecl => unreachable, - }, - RenderState.Statement => |base| { - switch (base.id) { - ast.Node.Id.VarDecl => { - const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); - try stack.append(RenderState { .VarDecl = var_decl}); - }, - else => { - try stack.append(RenderState { .MaybeSemiColon = base }); - try stack.append(RenderState { .Expression = base }); - }, - } - }, - RenderState.Indent => |new_indent| indent = new_indent, - RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent), - RenderState.Token => |token_index| try renderToken(tree, stream, token_index, indent, true), - RenderState.NonBreakToken => |token_index| try renderToken(tree, stream, token_index, indent, false), - RenderState.MaybeSemiColon => |base| { - if (base.requireSemiColon()) { - const semicolon_index = base.lastToken() + 1; - assert(tree.tokens.at(semicolon_index).id == Token.Id.Semicolon); - try renderToken(tree, stream, semicolon_index, indent, true); - } - }, + try stream.write(")"); + }, + + ast.Node.Id.StructField, + ast.Node.Id.UnionTag, + ast.Node.Id.EnumTag, + ast.Node.Id.ErrorTag, + ast.Node.Id.Root, + ast.Node.Id.VarDecl, + ast.Node.Id.Use, + ast.Node.Id.TestDecl, + ast.Node.Id.ParamDecl => unreachable, + } +} + +fn renderVarDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, var_decl: &ast.Node.VarDecl) (@typeOf(stream).Child.Error || Error)!void { + if (var_decl.visib_token) |visib_token| { + try stream.print("{} ", tree.tokenSlice(visib_token)); + } + + if (var_decl.extern_export_token) |extern_export_token| { + try stream.print("{} ", tree.tokenSlice(extern_export_token)); + + if (var_decl.lib_name) |lib_name| { + try renderExpression(allocator, stream, tree, indent, lib_name); + try stream.write(" "); } } + + if (var_decl.comptime_token) |comptime_token| { + try stream.print("{} ", tree.tokenSlice(comptime_token)); + } + + try stream.print("{} {}", tree.tokenSlice(var_decl.mut_token), tree.tokenSlice(var_decl.name_token)); + + if (var_decl.type_node) |type_node| { + try stream.write(": "); + try renderExpression(allocator, stream, tree, indent, type_node); + } + + if (var_decl.align_node) |align_node| { + try stream.write(" align("); + try renderExpression(allocator, stream, tree, indent, align_node); + try stream.write(")"); + } + + if (var_decl.init_node) |init_node| { + const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = "; + try stream.write(text); + try renderExpression(allocator, stream, tree, indent, init_node); + } + + try renderToken(tree, stream, var_decl.semicolon_token, indent, true); +} + +fn maybeRenderSemicolon(stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { + if (base.requireSemiColon()) { + const semicolon_index = base.lastToken() + 1; + assert(tree.tokens.at(semicolon_index).id == Token.Id.Semicolon); + try renderToken(tree, stream, semicolon_index, indent, true); + } +} + +fn renderParamDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { + const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); + if (param_decl.comptime_token) |comptime_token| { + try stream.print("{} ", tree.tokenSlice(comptime_token)); + } + if (param_decl.noalias_token) |noalias_token| { + try stream.print("{} ", tree.tokenSlice(noalias_token)); + } + if (param_decl.name_token) |name_token| { + try stream.print("{}: ", tree.tokenSlice(name_token)); + } + if (param_decl.var_args_token) |var_args_token| { + try stream.print("{}", tree.tokenSlice(var_args_token)); + } else { + try renderExpression(allocator, stream, tree, indent, param_decl.type_node); + } +} + +fn renderStatement(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { + switch (base.id) { + ast.Node.Id.VarDecl => { + const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); + try renderVarDecl(allocator, stream, tree, indent, var_decl); + }, + else => { + try renderExpression(allocator, stream, tree, indent, base); + try maybeRenderSemicolon(stream, tree, indent, base); + }, + } } -fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, line_break: bool) !void { +fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, line_break: bool) (@typeOf(stream).Child.Error || Error)!void { const token = tree.tokens.at(token_index); try stream.write(tree.tokenSlicePtr(token)); @@ -1255,7 +1212,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent } } -fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) !void { +fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) (@typeOf(stream).Child.Error || Error)!void { const comment = node.doc_comments ?? return; var it = comment.lines.iterator(0); while (it.next()) |line_token_index| { -- cgit v1.2.3 From 548ddd1f0c35033cd7e0d1940975bc7185bf7346 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 May 2018 23:57:15 -0400 Subject: fix AST dumping code in self hosted compiler --- src/ir.cpp | 6 +++++- std/zig/ast.zig | 54 +++++++++++++++++++++++++++++++++++++----------------- std/zig/parse.zig | 35 +---------------------------------- 3 files changed, 43 insertions(+), 52 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 7bc837d908..31d22ca82a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18083,7 +18083,11 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (type_is_invalid(end_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - assert(start_value->value.type->id == TypeTableEntryIdEnum); + if (start_value->value.type->id != TypeTableEntryIdEnum) { + ir_add_error(ira, range->start, buf_sprintf("not an enum type")); + return ira->codegen->builtin_types.entry_invalid; + } + BigInt start_index; bigint_init_bigint(&start_index, &start_value->value.data.x_enum_tag); diff --git a/std/zig/ast.zig b/std/zig/ast.zig index a92555731d..a452ed8906 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -67,6 +67,11 @@ pub const Tree = struct { pub fn tokenLocation(self: &Tree, start_index: usize, token_index: TokenIndex) Location { return self.tokenLocationPtr(start_index, self.tokens.at(token_index)); } + + pub fn dump(self: &Tree) void { + self.root_node.base.dump(0); + } + }; pub const Error = union(enum) { @@ -415,6 +420,20 @@ pub const Node = struct { } } + pub fn dump(self: &Node, indent: usize) void { + { + var i: usize = 0; + while (i < indent) : (i += 1) { + std.debug.warn(" "); + } + } + std.debug.warn("{}\n", @tagName(self.id)); + + var child_i: usize = 0; + while (self.iterate(child_i)) |child| : (child_i += 1) { + child.dump(indent + 2); + } + } pub const Root = struct { base: Node, @@ -426,7 +445,7 @@ pub const Node = struct { pub fn iterate(self: &Root, index: usize) ?&Node { if (index < self.decls.len) { - return self.decls.items[self.decls.len - index - 1]; + return *self.decls.at(index); } return null; } @@ -790,8 +809,16 @@ pub const Node = struct { pub fn iterate(self: &FnProto, index: usize) ?&Node { var i = index; - if (self.body_node) |body_node| { - if (i < 1) return body_node; + if (self.lib_name) |lib_name| { + if (i < 1) return lib_name; + i -= 1; + } + + if (i < self.params.len) return *self.params.at(self.params.len - i - 1); + i -= self.params.len; + + if (self.align_expr) |align_expr| { + if (i < 1) return align_expr; i -= 1; } @@ -807,18 +834,11 @@ pub const Node = struct { }, } - if (self.align_expr) |align_expr| { - if (i < 1) return align_expr; + if (self.body_node) |body_node| { + if (i < 1) return body_node; i -= 1; } - if (i < self.params.len) return self.params.items[self.params.len - i - 1]; - i -= self.params.len; - - if (self.lib_name) |lib_name| { - if (i < 1) return lib_name; - i -= 1; - } return null; } @@ -914,7 +934,7 @@ pub const Node = struct { pub fn iterate(self: &Block, index: usize) ?&Node { var i = index; - if (i < self.statements.len) return self.statements.items[i]; + if (i < self.statements.len) return *self.statements.at(i); i -= self.statements.len; return null; @@ -1596,7 +1616,7 @@ pub const Node = struct { i -= 1; switch (self.op) { - Op.Call => |call_info| { + @TagType(Op).Call => |*call_info| { if (i < call_info.params.len) return *call_info.params.at(i); i -= call_info.params.len; }, @@ -1604,7 +1624,7 @@ pub const Node = struct { if (i < 1) return index_expr; i -= 1; }, - Op.Slice => |range| { + @TagType(Op).Slice => |range| { if (i < 1) return range.start; i -= 1; @@ -1613,11 +1633,11 @@ pub const Node = struct { i -= 1; } }, - Op.ArrayInitializer => |exprs| { + Op.ArrayInitializer => |*exprs| { if (i < exprs.len) return *exprs.at(i); i -= exprs.len; }, - Op.StructInitializer => |fields| { + Op.StructInitializer => |*fields| { if (i < fields.len) return *fields.at(i); i -= fields.len; }, diff --git a/std/zig/parse.zig b/std/zig/parse.zig index c96893fd96..f88fdfab62 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -7,9 +7,8 @@ const Token = std.zig.Token; const TokenIndex = ast.TokenIndex; const Error = ast.Error; -/// Returns an AST tree, allocated with the parser's allocator. /// Result should be freed with tree.deinit() when there are -/// no more references to any AST nodes of the tree. +/// no more references to any of the tokens or nodes. pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { var tree_arena = std.heap.ArenaAllocator.init(allocator); errdefer tree_arena.deinit(); @@ -3466,38 +3465,6 @@ fn putBackToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) void { } } -const RenderAstFrame = struct { - node: &ast.Node, - indent: usize, -}; - -pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) !void { - var stack = std.ArrayList(State).init(allocator); - defer stack.deinit(); - - try stack.append(RenderAstFrame { - .node = &root_node.base, - .indent = 0, - }); - - while (stack.popOrNull()) |frame| { - { - var i: usize = 0; - while (i < frame.indent) : (i += 1) { - try stream.print(" "); - } - } - try stream.print("{}\n", @tagName(frame.node.id)); - var child_i: usize = 0; - while (frame.node.iterate(child_i)) |child| : (child_i += 1) { - try stack.append(RenderAstFrame { - .node = child, - .indent = frame.indent + 2, - }); - } - } -} - test "std.zig.parser" { _ = @import("parser_test.zig"); } -- cgit v1.2.3 From 4c3aa09f2a88f0608c14f5717de21aaa3d56c89e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 May 2018 18:57:57 -0400 Subject: self hosted compiler: remove unused flag --- src-self-hosted/main.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 8dc1d8ce3b..22f49e80d9 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -637,14 +637,12 @@ const usage_fmt = \\ \\Options: \\ --help Print this help and exit - \\ --keep-backups Retain backup entries for every file \\ \\ ; const args_fmt_spec = []Flag { Flag.Bool("--help"), - Flag.Bool("--keep-backups"), }; fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { -- cgit v1.2.3 From 05ecb49bac30041459ae08764edd2aced23d10eb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 13 May 2018 01:07:55 -0400 Subject: README: https links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1f23e133f8..552b784a50 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -![ZIG](http://ziglang.org/zig-logo.svg) +![ZIG](https://ziglang.org/zig-logo.svg) A programming language designed for robustness, optimality, and clarity. -[ziglang.org](http://ziglang.org) +[ziglang.org](https://ziglang.org) ## Feature Highlights -- cgit v1.2.3 From abcd418451051fe8c3b5c283d1701d101337dde8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 13 May 2018 14:20:01 -0400 Subject: std.zig.parse cleanup --- std/zig/parse.zig | 784 ++++++++++++++++++++++-------------------------- std/zig/parser_test.zig | 8 + 2 files changed, 368 insertions(+), 424 deletions(-) diff --git a/std/zig/parse.zig b/std/zig/parse.zig index f88fdfab62..3d6429ba71 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -17,15 +17,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { defer stack.deinit(); const arena = &tree_arena.allocator; - const root_node = try createNode(arena, ast.Node.Root, - ast.Node.Root { - .base = undefined, - .decls = ast.Node.Root.DeclList.init(arena), - .doc_comments = null, - // initialized when we get the eof token - .eof_token = undefined, - } - ); + const root_node = try arena.construct(ast.Node.Root { + .base = ast.Node { .id = ast.Node.Id.Root }, + .decls = ast.Node.Root.DeclList.init(arena), + .doc_comments = null, + // initialized when we get the eof token + .eof_token = undefined, + }); var tree = ast.Tree { .source = source, @@ -113,15 +111,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_comptime => { - const block = try createNode(arena, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = null, - .lbrace = undefined, - .statements = ast.Node.Block.StatementList.init(arena), - .rbrace = undefined, - } - ); + const block = try arena.construct(ast.Node.Block { + .base = ast.Node {.id = ast.Node.Id.Block }, + .label = null, + .lbrace = undefined, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); const node = try arena.construct(ast.Node.Comptime { .base = ast.Node { .id = ast.Node.Id.Comptime, @@ -312,14 +308,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_async => { - const async_node = try createNode(arena, ast.Node.AsyncAttribute, - ast.Node.AsyncAttribute { - .base = undefined, - .async_token = token_index, - .allocator_type = null, - .rangle_bracket = null, - } - ); + const async_node = try arena.construct(ast.Node.AsyncAttribute { + .base = ast.Node {.id = ast.Node.Id.AsyncAttribute }, + .async_token = token_index, + .allocator_type = null, + .rangle_bracket = null, + }); fn_proto.async_attr = async_node; try stack.append(State { @@ -396,27 +390,26 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; - const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl, - ast.Node.ContainerDecl { - .base = undefined, - .ltoken = ctx.ltoken, - .layout = ctx.layout, - .kind = switch (token_ptr.id) { - Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct, - Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union, - Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum, - else => { - *(try tree.errors.addOne()) = Error { - .ExpectedAggregateKw = Error.ExpectedAggregateKw { .token = token_index }, - }; - return tree; - }, + const node = try arena.construct(ast.Node.ContainerDecl { + .base = ast.Node {.id = ast.Node.Id.ContainerDecl }, + .ltoken = ctx.ltoken, + .layout = ctx.layout, + .kind = switch (token_ptr.id) { + Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct, + Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union, + Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedAggregateKw = Error.ExpectedAggregateKw { .token = token_index }, + }; + return tree; }, - .init_arg_expr = ast.Node.ContainerDecl.InitArg.None, - .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(arena), - .rbrace_token = undefined, - } - ); + }, + .init_arg_expr = ast.Node.ContainerDecl.InitArg.None, + .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(arena), + .rbrace_token = undefined, + }); + ctx.opt_ctx.store(&node.base); stack.append(State { .ContainerDecl = node }) catch unreachable; try stack.append(State { .ExpectToken = Token.Id.LBrace }); @@ -844,15 +837,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LBrace => { - const block = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = ctx.label, - .lbrace = token_index, - .statements = ast.Node.Block.StatementList.init(arena), - .rbrace = undefined, - } - ); + const block = try arena.construct(ast.Node.Block { + .base = ast.Node {.id = ast.Node.Id.Block}, + .label = ctx.label, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + ctx.opt_ctx.store(&block.base); stack.append(State { .Block = block }) catch unreachable; continue; }, @@ -957,19 +949,18 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.While => |ctx| { - const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.While, - ast.Node.While { - .base = undefined, - .label = ctx.label, - .inline_token = ctx.inline_token, - .while_token = ctx.loop_token, - .condition = undefined, - .payload = null, - .continue_expr = null, - .body = undefined, - .@"else" = null, - } - ); + const node = try arena.construct(ast.Node.While { + .base = ast.Node {.id = ast.Node.Id.While }, + .label = ctx.label, + .inline_token = ctx.inline_token, + .while_token = ctx.loop_token, + .condition = undefined, + .payload = null, + .continue_expr = null, + .body = undefined, + .@"else" = null, + }); + ctx.opt_ctx.store(&node.base); stack.append(State { .Else = &node.@"else" }) catch unreachable; try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); try stack.append(State { .WhileContinueExpr = &node.continue_expr }); @@ -987,18 +978,17 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.For => |ctx| { - const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.For, - ast.Node.For { - .base = undefined, - .label = ctx.label, - .inline_token = ctx.inline_token, - .for_token = ctx.loop_token, - .array_expr = undefined, - .payload = null, - .body = undefined, - .@"else" = null, - } - ); + const node = try arena.construct(ast.Node.For { + .base = ast.Node {.id = ast.Node.Id.For }, + .label = ctx.label, + .inline_token = ctx.inline_token, + .for_token = ctx.loop_token, + .array_expr = undefined, + .payload = null, + .body = undefined, + .@"else" = null, + }); + ctx.opt_ctx.store(&node.base); stack.append(State { .Else = &node.@"else" }) catch unreachable; try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); try stack.append(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } }); @@ -1009,14 +999,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.Else => |dest| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_else)) |else_token| { - const node = try createNode(arena, ast.Node.Else, - ast.Node.Else { - .base = undefined, - .else_token = else_token, - .payload = null, - .body = undefined, - } - ); + const node = try arena.construct(ast.Node.Else { + .base = ast.Node {.id = ast.Node.Id.Else }, + .else_token = else_token, + .payload = null, + .body = undefined, + }); *dest = node; stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable; @@ -1170,14 +1158,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try createNode(arena, ast.Node.AsmOutput, - ast.Node.AsmOutput { - .base = undefined, - .symbolic_name = undefined, - .constraint = undefined, - .kind = undefined, - } - ); + const node = try arena.construct(ast.Node.AsmOutput { + .base = ast.Node {.id = ast.Node.Id.AsmOutput }, + .symbolic_name = undefined, + .constraint = undefined, + .kind = undefined, + }); try items.push(node); stack.append(State { .AsmOutputItems = items }) catch unreachable; @@ -1223,14 +1209,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try createNode(arena, ast.Node.AsmInput, - ast.Node.AsmInput { - .base = undefined, - .symbolic_name = undefined, - .constraint = undefined, - .expr = undefined, - } - ); + const node = try arena.construct(ast.Node.AsmInput { + .base = ast.Node {.id = ast.Node.Id.AsmInput }, + .symbolic_name = undefined, + .constraint = undefined, + .expr = undefined, + }); try items.push(node); stack.append(State { .AsmInputItems = items }) catch unreachable; @@ -1668,14 +1652,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try createToCtxNode(arena, opt_ctx, ast.Node.Payload, - ast.Node.Payload { - .base = undefined, - .lpipe = token_index, - .error_symbol = undefined, - .rpipe = undefined - } - ); + const node = try arena.construct(ast.Node.Payload { + .base = ast.Node {.id = ast.Node.Id.Payload }, + .lpipe = token_index, + .error_symbol = undefined, + .rpipe = undefined + }); + opt_ctx.store(&node.base); stack.append(State { .ExpectTokenSave = ExpectTokenSave { @@ -1705,15 +1688,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload, - ast.Node.PointerPayload { - .base = undefined, - .lpipe = token_index, - .ptr_token = null, - .value_symbol = undefined, - .rpipe = undefined - } - ); + const node = try arena.construct(ast.Node.PointerPayload { + .base = ast.Node {.id = ast.Node.Id.PointerPayload }, + .lpipe = token_index, + .ptr_token = null, + .value_symbol = undefined, + .rpipe = undefined + }); + opt_ctx.store(&node.base); try stack.append(State { .ExpectTokenSave = ExpectTokenSave { @@ -1749,16 +1731,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload, - ast.Node.PointerIndexPayload { - .base = undefined, - .lpipe = token_index, - .ptr_token = null, - .value_symbol = undefined, - .index_symbol = null, - .rpipe = undefined - } - ); + const node = try arena.construct(ast.Node.PointerIndexPayload { + .base = ast.Node {.id = ast.Node.Id.PointerIndexPayload }, + .lpipe = token_index, + .ptr_token = null, + .value_symbol = undefined, + .index_symbol = null, + .rpipe = undefined + }); + opt_ctx.store(&node.base); stack.append(State { .ExpectTokenSave = ExpectTokenSave { @@ -1785,14 +1766,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression, - ast.Node.ControlFlowExpression { - .base = undefined, - .ltoken = token_index, - .kind = undefined, - .rhs = null, - } - ); + const node = try arena.construct(ast.Node.ControlFlowExpression { + .base = ast.Node {.id = ast.Node.Id.ControlFlowExpression }, + .ltoken = token_index, + .kind = undefined, + .rhs = null, + }); + opt_ctx.store(&node.base); stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable; @@ -1815,19 +1795,18 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token_index, - .op = switch (token_ptr.id) { - Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} }, - Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} }, - Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} }, - else => unreachable, - }, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.PrefixOp { + .base = ast.Node {.id = ast.Node.Id.PrefixOp }, + .op_token = token_index, + .op = switch (token_ptr.id) { + Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} }, + Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} }, + Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} }, + else => unreachable, + }, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; continue; @@ -1850,15 +1829,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = ellipsis3, - .op = ast.Node.InfixOp.Op.Range, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = ellipsis3, + .op = ast.Node.InfixOp.Op.Range, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; continue; } @@ -1876,15 +1854,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToAssignment(token_ptr.id)) |ass_id| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = ass_id, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = token_index, + .op = ass_id, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); continue; @@ -1907,15 +1884,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = unwrap_id, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = token_index, + .op = unwrap_id, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); @@ -1940,15 +1916,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = or_token, - .op = ast.Node.InfixOp.Op.BoolOr, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = or_token, + .op = ast.Node.InfixOp.Op.BoolOr, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; @@ -1965,15 +1940,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = and_token, - .op = ast.Node.InfixOp.Op.BoolAnd, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = and_token, + .op = ast.Node.InfixOp.Op.BoolAnd, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; @@ -1993,15 +1967,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToComparison(token_ptr.id)) |comp_id| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = comp_id, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = token_index, + .op = comp_id, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; @@ -2021,15 +1994,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = pipe, - .op = ast.Node.InfixOp.Op.BitOr, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = pipe, + .op = ast.Node.InfixOp.Op.BitOr, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; @@ -2046,15 +2018,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = caret, - .op = ast.Node.InfixOp.Op.BitXor, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = caret, + .op = ast.Node.InfixOp.Op.BitXor, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; @@ -2071,15 +2042,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = ampersand, - .op = ast.Node.InfixOp.Op.BitAnd, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = ampersand, + .op = ast.Node.InfixOp.Op.BitAnd, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; @@ -2099,15 +2069,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = bitshift_id, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = token_index, + .op = bitshift_id, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; @@ -2130,15 +2099,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToAddition(token_ptr.id)) |add_id| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = add_id, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = token_index, + .op = add_id, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; @@ -2161,15 +2129,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToMultiply(token_ptr.id)) |mult_id| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = mult_id, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = token_index, + .op = mult_id, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; @@ -2211,16 +2178,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { - .base = undefined, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena), - }, - .rtoken = undefined, - } - ); + const node = try arena.construct(ast.Node.SuffixOp { + .base = ast.Node {.id = ast.Node.Id.SuffixOp }, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena), + }, + .rtoken = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .IfToken = Token.Id.LBrace }); try stack.append(State { @@ -2243,15 +2209,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = bang, - .op = ast.Node.InfixOp.Op.ErrorUnion, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = bang, + .op = ast.Node.InfixOp.Op.ErrorUnion, + .rhs = undefined, + }); + opt_ctx.store(&node.base); stack.append(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } }); continue; @@ -2263,25 +2228,22 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| { - var node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token_index, - .op = prefix_id, - .rhs = undefined, - } - ); + var node = try arena.construct(ast.Node.PrefixOp { + .base = ast.Node {.id = ast.Node.Id.PrefixOp }, + .op_token = token_index, + .op = prefix_id, + .rhs = undefined, + }); + opt_ctx.store(&node.base); // Treat '**' token as two derefs if (token_ptr.id == Token.Id.AsteriskAsterisk) { - const child = try createNode(arena, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token_index, - .op = prefix_id, - .rhs = undefined, - } - ); + const child = try arena.construct(ast.Node.PrefixOp { + .base = ast.Node {.id = ast.Node.Id.PrefixOp}, + .op_token = token_index, + .op = prefix_id, + .rhs = undefined, + }); node.rhs = &child.base; node = child; } @@ -2300,14 +2262,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.SuffixOpExpressionBegin => |opt_ctx| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_async)) |async_token| { - const async_node = try createNode(arena, ast.Node.AsyncAttribute, - ast.Node.AsyncAttribute { - .base = undefined, - .async_token = async_token, - .allocator_type = null, - .rangle_bracket = null, - } - ); + const async_node = try arena.construct(ast.Node.AsyncAttribute { + .base = ast.Node {.id = ast.Node.Id.AsyncAttribute}, + .async_token = async_token, + .allocator_type = null, + .rangle_bracket = null, + }); stack.append(State { .AsyncEnd = AsyncEndCtx { .ctx = opt_ctx, @@ -2333,19 +2293,19 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LParen => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { - .base = undefined, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .Call = ast.Node.SuffixOp.Op.Call { - .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena), - .async_attr = null, - } - }, - .rtoken = undefined, - } - ); + const node = try arena.construct(ast.Node.SuffixOp { + .base = ast.Node {.id = ast.Node.Id.SuffixOp }, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .Call = ast.Node.SuffixOp.Op.Call { + .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena), + .async_attr = null, + } + }, + .rtoken = undefined, + }); + opt_ctx.store(&node.base); + stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .ExprListItemOrEnd = ExprListCtx { @@ -2357,31 +2317,31 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBracket => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { - .base = undefined, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .ArrayAccess = undefined, - }, - .rtoken = undefined - } - ); + const node = try arena.construct(ast.Node.SuffixOp { + .base = ast.Node {.id = ast.Node.Id.SuffixOp }, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .ArrayAccess = undefined, + }, + .rtoken = undefined + }); + opt_ctx.store(&node.base); + stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .SliceOrArrayAccess = node }); try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }}); continue; }, Token.Id.Period => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token_index, - .op = ast.Node.InfixOp.Op.Period, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.InfixOp { + .base = ast.Node {.id = ast.Node.Id.InfixOp }, + .lhs = lhs, + .op_token = token_index, + .op = ast.Node.InfixOp.Op.Period, + .rhs = undefined, + }); + opt_ctx.store(&node.base); + stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .Identifier = OptionalCtx { .Required = &node.rhs } }); continue; @@ -2461,14 +2421,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LParen => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression, - ast.Node.GroupedExpression { - .base = undefined, - .lparen = token.index, - .expr = undefined, - .rparen = undefined, - } - ); + const node = try arena.construct(ast.Node.GroupedExpression { + .base = ast.Node {.id = ast.Node.Id.GroupedExpression }, + .lparen = token.index, + .expr = undefined, + .rparen = undefined, + }); + opt_ctx.store(&node.base); + stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.RParen, @@ -2479,14 +2439,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Builtin => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall, - ast.Node.BuiltinCall { - .base = undefined, - .builtin_token = token.index, - .params = ast.Node.BuiltinCall.ParamList.init(arena), - .rparen_token = undefined, - } - ); + const node = try arena.construct(ast.Node.BuiltinCall { + .base = ast.Node {.id = ast.Node.Id.BuiltinCall }, + .builtin_token = token.index, + .params = ast.Node.BuiltinCall.ParamList.init(arena), + .rparen_token = undefined, + }); + opt_ctx.store(&node.base); + stack.append(State { .ExprListItemOrEnd = ExprListCtx { .list = &node.params, @@ -2498,14 +2458,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBracket => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token.index, - .op = undefined, - .rhs = undefined, - } - ); + const node = try arena.construct(ast.Node.PrefixOp { + .base = ast.Node {.id = ast.Node.Id.PrefixOp }, + .op_token = token.index, + .op = undefined, + .rhs = undefined, + }); + opt_ctx.store(&node.base); + stack.append(State { .SliceOrArrayType = node }) catch unreachable; continue; }, @@ -2611,18 +2571,18 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_asm => { - const node = try createToCtxNode(arena, opt_ctx, ast.Node.Asm, - ast.Node.Asm { - .base = undefined, - .asm_token = token.index, - .volatile_token = null, - .template = undefined, - .outputs = ast.Node.Asm.OutputList.init(arena), - .inputs = ast.Node.Asm.InputList.init(arena), - .clobbers = ast.Node.Asm.ClobberList.init(arena), - .rparen = undefined, - } - ); + const node = try arena.construct(ast.Node.Asm { + .base = ast.Node {.id = ast.Node.Id.Asm }, + .asm_token = token.index, + .volatile_token = null, + .template = undefined, + .outputs = ast.Node.Asm.OutputList.init(arena), + .inputs = ast.Node.Asm.InputList.init(arena), + .clobbers = ast.Node.Asm.ClobberList.init(arena), + .rparen = undefined, + }); + opt_ctx.store(&node.base); + stack.append(State { .ExpectTokenSave = ExpectTokenSave { .id = Token.Id.RParen, @@ -3155,31 +3115,29 @@ fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &con token_ptr: &const Token, token_index: TokenIndex) !bool { switch (token_ptr.id) { Token.Id.Keyword_suspend => { - const node = try createToCtxNode(arena, ctx, ast.Node.Suspend, - ast.Node.Suspend { - .base = undefined, - .label = null, - .suspend_token = token_index, - .payload = null, - .body = null, - } - ); + const node = try arena.construct(ast.Node.Suspend { + .base = ast.Node {.id = ast.Node.Id.Suspend }, + .label = null, + .suspend_token = token_index, + .payload = null, + .body = null, + }); + ctx.store(&node.base); stack.append(State { .SuspendBody = node }) catch unreachable; try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); return true; }, Token.Id.Keyword_if => { - const node = try createToCtxNode(arena, ctx, ast.Node.If, - ast.Node.If { - .base = undefined, - .if_token = token_index, - .condition = undefined, - .payload = null, - .body = undefined, - .@"else" = null, - } - ); + const node = try arena.construct(ast.Node.If { + .base = ast.Node {.id = ast.Node.Id.If }, + .if_token = token_index, + .condition = undefined, + .payload = null, + .body = undefined, + .@"else" = null, + }); + ctx.store(&node.base); stack.append(State { .Else = &node.@"else" }) catch unreachable; try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); @@ -3236,14 +3194,14 @@ fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &con return true; }, Token.Id.Keyword_comptime => { - const node = try createToCtxNode(arena, ctx, ast.Node.Comptime, - ast.Node.Comptime { - .base = undefined, - .comptime_token = token_index, - .expr = undefined, - .doc_comments = null, - } - ); + const node = try arena.construct(ast.Node.Comptime { + .base = ast.Node {.id = ast.Node.Id.Comptime }, + .comptime_token = token_index, + .expr = undefined, + .doc_comments = null, + }); + ctx.store(&node.base); + try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); return true; }, @@ -3390,33 +3348,11 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { }; } -fn createNode(arena: &mem.Allocator, comptime T: type, init_to: &const T) !&T { - const node = try arena.create(T); - *node = *init_to; - node.base = blk: { - const id = ast.Node.typeToId(T); - break :blk ast.Node { - .id = id, - }; - }; - - return node; -} - -fn createToCtxNode(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T { - const node = try createNode(arena, T, init_to); - opt_ctx.store(&node.base); - - return node; -} - fn createLiteral(arena: &mem.Allocator, comptime T: type, token_index: TokenIndex) !&T { - return createNode(arena, T, - T { - .base = undefined, - .token = token_index, - } - ); + return arena.construct(T { + .base = ast.Node {.id = ast.Node.typeToId(T)}, + .token = token_index, + }); } fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token_index: TokenIndex) !&T { diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 29b231a4db..0f0ea5fdf1 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,11 @@ +//test "zig fmt: same-line doc comment on variable declaration" { +// try testCanonical( +// \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space +// \\pub const MAP_FILE = 0x0000; /// map from file (default) +// \\ +// ); +//} + test "zig fmt: same-line comment after a statement" { try testCanonical( \\test "" { -- cgit v1.2.3 From 04bca58a3a3a85eeaa36ab4c2cc0881347e123b0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 15 May 2018 00:33:34 -0400 Subject: zig fmt: preserve same line doc comments on var decls --- std/zig/parse.zig | 66 +++++++++++++++++++++++++++++++++---------------- std/zig/parser_test.zig | 20 +++++++++------ 2 files changed, 58 insertions(+), 28 deletions(-) diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 3d6429ba71..f2376f0df3 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -640,12 +640,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { switch (token_ptr.id) { Token.Id.Equal => { var_decl.eq_token = token_index; - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Semicolon, - .ptr = &var_decl.semicolon_token, - }, - }) catch unreachable; + stack.append(State { .VarDeclSemiColon = var_decl }) catch unreachable; try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } }); continue; }, @@ -662,6 +657,30 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, + State.VarDeclSemiColon => |var_decl| { + const semicolon_token = nextToken(&tok_it, &tree); + + if (semicolon_token.ptr.id != Token.Id.Semicolon) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = semicolon_token.index, + .expected_id = Token.Id.Semicolon, + }, + }; + return tree; + } + + var_decl.semicolon_token = semicolon_token.index; + + if (eatToken(&tok_it, &tree, Token.Id.DocComment)) |doc_comment_token| { + const loc = tree.tokenLocation(semicolon_token.ptr.end, doc_comment_token); + if (loc.line == 0) { + try pushDocComment(arena, doc_comment_token, &var_decl.doc_comments); + } else { + putBackToken(&tok_it, &tree); + } + } + }, State.FnDef => |fn_proto| { const token = nextToken(&tok_it, &tree); @@ -2938,6 +2957,7 @@ const State = union(enum) { VarDecl: VarDeclCtx, VarDeclAlign: &ast.Node.VarDecl, VarDeclEq: &ast.Node.VarDecl, + VarDeclSemiColon: &ast.Node.VarDecl, FnDef: &ast.Node.FnProto, FnProto: &ast.Node.FnProto, @@ -3042,25 +3062,29 @@ const State = union(enum) { OptionalTokenSave: OptionalTokenSave, }; +fn pushDocComment(arena: &mem.Allocator, line_comment: TokenIndex, result: &?&ast.Node.DocComment) !void { + const node = blk: { + if (*result) |comment_node| { + break :blk comment_node; + } else { + const comment_node = try arena.construct(ast.Node.DocComment { + .base = ast.Node { + .id = ast.Node.Id.DocComment, + }, + .lines = ast.Node.DocComment.LineList.init(arena), + }); + *result = comment_node; + break :blk comment_node; + } + }; + try node.lines.push(line_comment); +} + fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) !?&ast.Node.DocComment { var result: ?&ast.Node.DocComment = null; while (true) { if (eatToken(tok_it, tree, Token.Id.DocComment)) |line_comment| { - const node = blk: { - if (result) |comment_node| { - break :blk comment_node; - } else { - const comment_node = try arena.construct(ast.Node.DocComment { - .base = ast.Node { - .id = ast.Node.Id.DocComment, - }, - .lines = ast.Node.DocComment.LineList.init(arena), - }); - result = comment_node; - break :blk comment_node; - } - }; - try node.lines.push(line_comment); + try pushDocComment(arena, line_comment, &result); continue; } break; diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 0f0ea5fdf1..f95ecfaee3 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,10 +1,16 @@ -//test "zig fmt: same-line doc comment on variable declaration" { -// try testCanonical( -// \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space -// \\pub const MAP_FILE = 0x0000; /// map from file (default) -// \\ -// ); -//} +test "zig fmt: same-line doc comment on variable declaration" { + try testTransform( + \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space + \\pub const MAP_FILE = 0x0000; /// map from file (default) + \\ + , + \\/// allocated from memory, swap space + \\pub const MAP_ANONYMOUS = 0x1000; + \\/// map from file (default) + \\pub const MAP_FILE = 0x0000; + \\ + ); +} test "zig fmt: same-line comment after a statement" { try testCanonical( -- cgit v1.2.3 From 74b10c08d1bd0b64aa8175dff6dec178c7c3bbee Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 15 May 2018 14:11:41 -0400 Subject: fix @typeInfo not setting a field to comptime --- src/ir.cpp | 1 + test/cases/type_info.zig | 319 ++++++++++++++++++++++++++--------------------- 2 files changed, 178 insertions(+), 142 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 31d22ca82a..cade660651 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16549,6 +16549,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t { size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index); inner_fields[1].data.x_maybe = create_const_vals(1); + inner_fields[1].data.x_maybe->special = ConstValSpecialStatic; inner_fields[1].data.x_maybe->type = ira->codegen->builtin_types.entry_usize; bigint_init_unsigned(&inner_fields[1].data.x_maybe->data.x_bigint, byte_offset); } diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index f10703e3ee..7bf1e68180 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -4,167 +4,199 @@ const TypeInfo = @import("builtin").TypeInfo; const TypeId = @import("builtin").TypeId; test "type info: tag type, void info" { - comptime { - assert(@TagType(TypeInfo) == TypeId); - const void_info = @typeInfo(void); - assert(TypeId(void_info) == TypeId.Void); - assert(void_info.Void == {}); - } + testBasic(); + comptime testBasic(); +} + +fn testBasic() void { + assert(@TagType(TypeInfo) == TypeId); + const void_info = @typeInfo(void); + assert(TypeId(void_info) == TypeId.Void); + assert(void_info.Void == {}); } test "type info: integer, floating point type info" { - comptime { - const u8_info = @typeInfo(u8); - assert(TypeId(u8_info) == TypeId.Int); - assert(!u8_info.Int.is_signed); - assert(u8_info.Int.bits == 8); + testIntFloat(); + comptime testIntFloat(); +} - const f64_info = @typeInfo(f64); - assert(TypeId(f64_info) == TypeId.Float); - assert(f64_info.Float.bits == 64); - } +fn testIntFloat() void { + const u8_info = @typeInfo(u8); + assert(TypeId(u8_info) == TypeId.Int); + assert(!u8_info.Int.is_signed); + assert(u8_info.Int.bits == 8); + + const f64_info = @typeInfo(f64); + assert(TypeId(f64_info) == TypeId.Float); + assert(f64_info.Float.bits == 64); } test "type info: pointer type info" { - comptime { - const u32_ptr_info = @typeInfo(&u32); - assert(TypeId(u32_ptr_info) == TypeId.Pointer); - assert(u32_ptr_info.Pointer.is_const == false); - assert(u32_ptr_info.Pointer.is_volatile == false); - assert(u32_ptr_info.Pointer.alignment == 4); - assert(u32_ptr_info.Pointer.child == u32); - } + testPointer(); + comptime testPointer(); +} + +fn testPointer() void { + const u32_ptr_info = @typeInfo(&u32); + assert(TypeId(u32_ptr_info) == TypeId.Pointer); + assert(u32_ptr_info.Pointer.is_const == false); + assert(u32_ptr_info.Pointer.is_volatile == false); + assert(u32_ptr_info.Pointer.alignment == 4); + assert(u32_ptr_info.Pointer.child == u32); } test "type info: slice type info" { - comptime { - const u32_slice_info = @typeInfo([]u32); - assert(TypeId(u32_slice_info) == TypeId.Slice); - assert(u32_slice_info.Slice.is_const == false); - assert(u32_slice_info.Slice.is_volatile == false); - assert(u32_slice_info.Slice.alignment == 4); - assert(u32_slice_info.Slice.child == u32); - } + testSlice(); + comptime testSlice(); +} + +fn testSlice() void { + const u32_slice_info = @typeInfo([]u32); + assert(TypeId(u32_slice_info) == TypeId.Slice); + assert(u32_slice_info.Slice.is_const == false); + assert(u32_slice_info.Slice.is_volatile == false); + assert(u32_slice_info.Slice.alignment == 4); + assert(u32_slice_info.Slice.child == u32); } test "type info: array type info" { - comptime { - const arr_info = @typeInfo([42]bool); - assert(TypeId(arr_info) == TypeId.Array); - assert(arr_info.Array.len == 42); - assert(arr_info.Array.child == bool); - } + testArray(); + comptime testArray(); +} + +fn testArray() void { + const arr_info = @typeInfo([42]bool); + assert(TypeId(arr_info) == TypeId.Array); + assert(arr_info.Array.len == 42); + assert(arr_info.Array.child == bool); } test "type info: nullable type info" { - comptime { - const null_info = @typeInfo(?void); - assert(TypeId(null_info) == TypeId.Nullable); - assert(null_info.Nullable.child == void); - } + testNullable(); + comptime testNullable(); +} + +fn testNullable() void { + const null_info = @typeInfo(?void); + assert(TypeId(null_info) == TypeId.Nullable); + assert(null_info.Nullable.child == void); } test "type info: promise info" { - comptime { - const null_promise_info = @typeInfo(promise); - assert(TypeId(null_promise_info) == TypeId.Promise); - assert(null_promise_info.Promise.child == @typeOf(undefined)); + testPromise(); + comptime testPromise(); +} - const promise_info = @typeInfo(promise->usize); - assert(TypeId(promise_info) == TypeId.Promise); - assert(promise_info.Promise.child == usize); - } +fn testPromise() void { + const null_promise_info = @typeInfo(promise); + assert(TypeId(null_promise_info) == TypeId.Promise); + assert(null_promise_info.Promise.child == @typeOf(undefined)); + const promise_info = @typeInfo(promise->usize); + assert(TypeId(promise_info) == TypeId.Promise); + assert(promise_info.Promise.child == usize); } test "type info: error set, error union info" { - comptime { - const TestErrorSet = error { - First, - Second, - Third, - }; - - const error_set_info = @typeInfo(TestErrorSet); - assert(TypeId(error_set_info) == TypeId.ErrorSet); - assert(error_set_info.ErrorSet.errors.len == 3); - assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); - assert(error_set_info.ErrorSet.errors[2].value == usize(TestErrorSet.Third)); - - const error_union_info = @typeInfo(TestErrorSet!usize); - assert(TypeId(error_union_info) == TypeId.ErrorUnion); - assert(error_union_info.ErrorUnion.error_set == TestErrorSet); - assert(error_union_info.ErrorUnion.payload == usize); - } + testErrorSet(); + comptime testErrorSet(); +} + +fn testErrorSet() void { + const TestErrorSet = error { + First, + Second, + Third, + }; + + const error_set_info = @typeInfo(TestErrorSet); + assert(TypeId(error_set_info) == TypeId.ErrorSet); + assert(error_set_info.ErrorSet.errors.len == 3); + assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); + assert(error_set_info.ErrorSet.errors[2].value == usize(TestErrorSet.Third)); + + const error_union_info = @typeInfo(TestErrorSet!usize); + assert(TypeId(error_union_info) == TypeId.ErrorUnion); + assert(error_union_info.ErrorUnion.error_set == TestErrorSet); + assert(error_union_info.ErrorUnion.payload == usize); } test "type info: enum info" { - comptime { - const Os = @import("builtin").Os; + testEnum(); + comptime testEnum(); +} - const os_info = @typeInfo(Os); - assert(TypeId(os_info) == TypeId.Enum); - assert(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); - assert(os_info.Enum.fields.len == 32); - assert(mem.eql(u8, os_info.Enum.fields[1].name, "ananas")); - assert(os_info.Enum.fields[10].value == 10); - assert(os_info.Enum.tag_type == u5); - assert(os_info.Enum.defs.len == 0); - } +fn testEnum() void { + const Os = @import("builtin").Os; + + const os_info = @typeInfo(Os); + assert(TypeId(os_info) == TypeId.Enum); + assert(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); + assert(os_info.Enum.fields.len == 32); + assert(mem.eql(u8, os_info.Enum.fields[1].name, "ananas")); + assert(os_info.Enum.fields[10].value == 10); + assert(os_info.Enum.tag_type == u5); + assert(os_info.Enum.defs.len == 0); } test "type info: union info" { - comptime { - const typeinfo_info = @typeInfo(TypeInfo); - assert(TypeId(typeinfo_info) == TypeId.Union); - assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(typeinfo_info.Union.tag_type == TypeId); - assert(typeinfo_info.Union.fields.len == 26); - assert(typeinfo_info.Union.fields[4].enum_field != null); - assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); - assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); - assert(typeinfo_info.Union.defs.len == 21); - - const TestNoTagUnion = union { - Foo: void, - Bar: u32, - }; - - const notag_union_info = @typeInfo(TestNoTagUnion); - assert(TypeId(notag_union_info) == TypeId.Union); - assert(notag_union_info.Union.tag_type == @typeOf(undefined)); - assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(notag_union_info.Union.fields.len == 2); - assert(notag_union_info.Union.fields[0].enum_field == null); - assert(notag_union_info.Union.fields[1].field_type == u32); - - const TestExternUnion = extern union { - foo: &c_void, - }; - - const extern_union_info = @typeInfo(TestExternUnion); - assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); - assert(extern_union_info.Union.tag_type == @typeOf(undefined)); - assert(extern_union_info.Union.fields[0].enum_field == null); - assert(extern_union_info.Union.fields[0].field_type == &c_void); - } + testUnion(); + comptime testUnion(); +} + +fn testUnion() void { + const typeinfo_info = @typeInfo(TypeInfo); + assert(TypeId(typeinfo_info) == TypeId.Union); + assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assert(typeinfo_info.Union.tag_type == TypeId); + assert(typeinfo_info.Union.fields.len == 26); + assert(typeinfo_info.Union.fields[4].enum_field != null); + assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); + assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); + assert(typeinfo_info.Union.defs.len == 21); + + const TestNoTagUnion = union { + Foo: void, + Bar: u32, + }; + + const notag_union_info = @typeInfo(TestNoTagUnion); + assert(TypeId(notag_union_info) == TypeId.Union); + assert(notag_union_info.Union.tag_type == @typeOf(undefined)); + assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assert(notag_union_info.Union.fields.len == 2); + assert(notag_union_info.Union.fields[0].enum_field == null); + assert(notag_union_info.Union.fields[1].field_type == u32); + + const TestExternUnion = extern union { + foo: &c_void, + }; + + const extern_union_info = @typeInfo(TestExternUnion); + assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); + assert(extern_union_info.Union.tag_type == @typeOf(undefined)); + assert(extern_union_info.Union.fields[0].enum_field == null); + assert(extern_union_info.Union.fields[0].field_type == &c_void); } test "type info: struct info" { - comptime { - const struct_info = @typeInfo(TestStruct); - assert(TypeId(struct_info) == TypeId.Struct); - assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); - assert(struct_info.Struct.fields.len == 3); - assert(struct_info.Struct.fields[1].offset == null); - assert(struct_info.Struct.fields[2].field_type == &TestStruct); - assert(struct_info.Struct.defs.len == 2); - assert(struct_info.Struct.defs[0].is_pub); - assert(!struct_info.Struct.defs[0].data.Fn.is_extern); - assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); - assert(struct_info.Struct.defs[0].data.Fn.return_type == void); - assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn(&const TestStruct)void); - } + testStruct(); + comptime testStruct(); +} + +fn testStruct() void { + const struct_info = @typeInfo(TestStruct); + assert(TypeId(struct_info) == TypeId.Struct); + assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); + assert(struct_info.Struct.fields.len == 3); + assert(struct_info.Struct.fields[1].offset == null); + assert(struct_info.Struct.fields[2].field_type == &TestStruct); + assert(struct_info.Struct.defs.len == 2); + assert(struct_info.Struct.defs[0].is_pub); + assert(!struct_info.Struct.defs[0].data.Fn.is_extern); + assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); + assert(struct_info.Struct.defs[0].data.Fn.return_type == void); + assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn(&const TestStruct)void); } const TestStruct = packed struct { @@ -178,21 +210,24 @@ const TestStruct = packed struct { }; test "type info: function type info" { - comptime { - const fn_info = @typeInfo(@typeOf(foo)); - assert(TypeId(fn_info) == TypeId.Fn); - assert(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); - assert(fn_info.Fn.is_generic); - assert(fn_info.Fn.args.len == 2); - assert(fn_info.Fn.is_var_args); - assert(fn_info.Fn.return_type == @typeOf(undefined)); - assert(fn_info.Fn.async_allocator_type == @typeOf(undefined)); - - const test_instance: TestStruct = undefined; - const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); - assert(TypeId(bound_fn_info) == TypeId.BoundFn); - assert(bound_fn_info.BoundFn.args[0].arg_type == &const TestStruct); - } + testFunction(); + comptime testFunction(); +} + +fn testFunction() void { + const fn_info = @typeInfo(@typeOf(foo)); + assert(TypeId(fn_info) == TypeId.Fn); + assert(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); + assert(fn_info.Fn.is_generic); + assert(fn_info.Fn.args.len == 2); + assert(fn_info.Fn.is_var_args); + assert(fn_info.Fn.return_type == @typeOf(undefined)); + assert(fn_info.Fn.async_allocator_type == @typeOf(undefined)); + + const test_instance: TestStruct = undefined; + const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); + assert(TypeId(bound_fn_info) == TypeId.BoundFn); + assert(bound_fn_info.BoundFn.args[0].arg_type == &const TestStruct); } fn foo(comptime a: usize, b: bool, args: ...) usize { -- cgit v1.2.3 From 3625df25d6fe0b11e4c1c33454b621ec8a8c10ba Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 15 May 2018 16:21:47 -0400 Subject: build: add flag to LLD to fix gcc 8 build (#1013) * build: add flag to LLD to fix gcc 8 build * build: add -Wno-unknown-warning-option to work around older gcc --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0aad51c7bc..bda576347e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,7 +196,7 @@ else() if(MSVC) set(ZIG_LLD_COMPILE_FLAGS "-std=c++11 -D_CRT_SECURE_NO_WARNINGS /w") else() - set(ZIG_LLD_COMPILE_FLAGS "-std=c++11 -fno-exceptions -fno-rtti -Wno-comment") + set(ZIG_LLD_COMPILE_FLAGS "-std=c++11 -fno-exceptions -fno-rtti -Wno-comment -Wno-class-memaccess -Wno-unknown-warning-option") endif() set_target_properties(embedded_lld_lib PROPERTIES COMPILE_FLAGS ${ZIG_LLD_COMPILE_FLAGS} -- cgit v1.2.3 From 492a214d4c4f4feb15620dfd05230de0086825e5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 15 May 2018 22:11:03 -0400 Subject: std.fmt.format: support {B} for human readable bytes --- std/fmt/index.zig | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 43e758038f..e26b2f6c8b 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -27,6 +27,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), Character, Buf, BufWidth, + Bytes, + BytesWidth, }; comptime var start_index = 0; @@ -95,6 +97,10 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), '.' => { state = State.Float; }, + 'B' => { + width = 0; + state = State.Bytes; + }, else => @compileError("Unknown format character: " ++ []u8{c}), }, State.Buf => switch (c) { @@ -206,6 +212,30 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), }, else => @compileError("Unexpected character in format string: " ++ []u8{c}), }, + State.Bytes => switch (c) { + '}' => { + try formatBytes(args[next_arg], 0, context, Errors, output); + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + '0' ... '9' => { + width_start = i; + state = State.BytesWidth; + }, + else => @compileError("Unexpected character in format string: " ++ []u8{c}), + }, + State.BytesWidth => switch (c) { + '}' => { + width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); + try formatBytes(args[next_arg], width, context, Errors, output); + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + '0' ... '9' => {}, + else => @compileError("Unexpected character in format string: " ++ []u8{c}), + }, } } comptime { @@ -520,6 +550,25 @@ pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, com } } +pub fn formatBytes(value: var, width: ?usize, + context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void +{ + if (value == 0) { + return output(context, "0B"); + } + + const mags = " KMGTPEZY"; + const magnitude = math.min(math.log2(value) / 10, mags.len - 1); + const new_value = f64(value) / math.pow(f64, 1024, f64(magnitude)); + const suffix = mags[magnitude]; + + try formatFloatDecimal(new_value, width, context, Errors, output); + + if (suffix != ' ') { + try output(context, (&suffix)[0..1]); + } + return output(context, "B"); +} pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void @@ -767,6 +816,12 @@ test "fmt.format" { const result = try bufPrint(buf1[0..], "u3: {}\n", value); assert(mem.eql(u8, result, "u3: 5\n")); } + { + var buf1: [32]u8 = undefined; + const value: usize = 63 * 1024 * 1024; + const result = try bufPrint(buf1[0..], "file size: {B}\n", value); + assert(mem.eql(u8, result, "file size: 63MB\n")); + } { // Dummy field because of https://github.com/zig-lang/zig/issues/557. const Struct = struct { -- cgit v1.2.3 From ee5f9ffad02b3cf263c13ca58c4d2e4526e29a84 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 16 May 2018 00:27:18 -0400 Subject: zig fmt: add comma on last switch prong --- std/zig/parse.zig | 2 +- std/zig/parser_test.zig | 31 +++++++++++++++++++++++++++++++ std/zig/render.zig | 15 ++++++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/std/zig/parse.zig b/std/zig/parse.zig index f2376f0df3..4f1e3dcefe 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -1406,7 +1406,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.SwitchCaseCommaOrEnd => |list_state| { - switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) { + switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) { ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { *list_state.ptr = end; continue; diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index f95ecfaee3..129e62e6dd 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,34 @@ +test "zig fmt: add comma on last switch prong" { + try testTransform( + \\test "aoeu" { + \\switch (self.init_arg_expr) { + \\ InitArg.Type => |t| { }, + \\ InitArg.None, + \\ InitArg.Enum => { } + \\} + \\ switch (self.init_arg_expr) { + \\ InitArg.Type => |t| { }, + \\ InitArg.None, + \\ InitArg.Enum => { }//line comment + \\ } + \\} + , + \\test "aoeu" { + \\ switch (self.init_arg_expr) { + \\ InitArg.Type => |t| {}, + \\ InitArg.None, + \\ InitArg.Enum => {}, + \\ } + \\ switch (self.init_arg_expr) { + \\ InitArg.Type => |t| {}, + \\ InitArg.None, + \\ InitArg.Enum => {}, //line comment + \\ } + \\} + \\ + ); +} + test "zig fmt: same-line doc comment on variable declaration" { try testTransform( \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space diff --git a/std/zig/render.zig b/std/zig/render.zig index 13ef4607f4..5084c1d096 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -831,7 +831,20 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind } try renderExpression(allocator, stream, tree, indent, switch_case.expr); - try renderToken(tree, stream, switch_case.lastToken() + 1, indent, true); + { + // Handle missing comma after last switch case + var index = switch_case.lastToken() + 1; + switch (tree.tokens.at(index).id) { + Token.Id.RBrace => { + try stream.write(","); + }, + Token.Id.LineComment => { + try stream.write(", "); + try renderToken(tree, stream, index, indent, true); + }, + else => try renderToken(tree, stream, index, indent, true), + } + } }, ast.Node.Id.SwitchElse => { const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); -- cgit v1.2.3 From 288fc3a8d361972daeded19d207b410128d70d67 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 16 May 2018 00:43:28 -0400 Subject: convert more std lib files to postfix pointer deref --- std/crypto/test.zig | 3 +- std/fmt/errol/index.zig | 54 +- std/os/darwin.zig | 282 ++++--- std/zig/ast.zig | 71 +- std/zig/parse.zig | 2091 +++++++++++++++++++++-------------------------- std/zig/render.zig | 88 +- 6 files changed, 1211 insertions(+), 1378 deletions(-) diff --git a/std/crypto/test.zig b/std/crypto/test.zig index e41c6a7a2d..3fa24272e5 100644 --- a/std/crypto/test.zig +++ b/std/crypto/test.zig @@ -14,9 +14,8 @@ pub fn assertEqualHash(comptime Hasher: var, comptime expected: []const u8, inpu pub fn assertEqual(comptime expected: []const u8, input: []const u8) void { var expected_bytes: [expected.len / 2]u8 = undefined; for (expected_bytes) |*r, i| { - *r = fmt.parseInt(u8, expected[2*i .. 2*i+2], 16) catch unreachable; + r.* = fmt.parseInt(u8, expected[2 * i .. 2 * i + 2], 16) catch unreachable; } debug.assert(mem.eql(u8, expected_bytes, input)); } - diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index 00c69cd294..f4ec251b77 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -86,7 +86,7 @@ pub fn errol3(value: f64, buffer: []u8) FloatDecimal { const data = enum3_data[i]; const digits = buffer[1..data.str.len + 1]; mem.copy(u8, digits, data.str); - return FloatDecimal { + return FloatDecimal{ .digits = digits, .exp = data.exp, }; @@ -105,7 +105,6 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { return errolFixed(val, buffer); } - // normalize the midpoint const e = math.frexp(val).exponent; @@ -137,11 +136,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { } // compute boundaries - var high = HP { + var high = HP{ .val = mid.val, .off = mid.off + (fpnext(val) - val) * lten * ten / 2.0, }; - var low = HP { + var low = HP{ .val = mid.val, .off = mid.off + (fpprev(val) - val) * lten * ten / 2.0, }; @@ -171,15 +170,12 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { var buf_index: usize = 1; while (true) { var hdig = u8(math.floor(high.val)); - if ((high.val == f64(hdig)) and (high.off < 0)) - hdig -= 1; + if ((high.val == f64(hdig)) and (high.off < 0)) hdig -= 1; var ldig = u8(math.floor(low.val)); - if ((low.val == f64(ldig)) and (low.off < 0)) - ldig -= 1; + if ((low.val == f64(ldig)) and (low.off < 0)) ldig -= 1; - if (ldig != hdig) - break; + if (ldig != hdig) break; buffer[buf_index] = hdig + '0'; buf_index += 1; @@ -191,13 +187,12 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { const tmp = (high.val + low.val) / 2.0; var mdig = u8(math.floor(tmp + 0.5)); - if ((f64(mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) - mdig -= 1; + if ((f64(mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1; buffer[buf_index] = mdig + '0'; buf_index += 1; - return FloatDecimal { + return FloatDecimal{ .digits = buffer[1..buf_index], .exp = exp, }; @@ -235,7 +230,7 @@ fn hpProd(in: &const HP, val: f64) HP { const p = in.val * val; const e = ((hi * hi2 - p) + lo * hi2 + hi * lo2) + lo * lo2; - return HP { + return HP{ .val = p, .off = in.off * val + e, }; @@ -246,8 +241,8 @@ fn hpProd(in: &const HP, val: f64) HP { /// @hi: The high bits. /// @lo: The low bits. fn split(val: f64, hi: &f64, lo: &f64) void { - *hi = gethi(val); - *lo = val - *hi; + hi.* = gethi(val); + lo.* = val - hi.*; } fn gethi(in: f64) f64 { @@ -301,7 +296,6 @@ fn hpMul10(hp: &HP) void { hpNormalize(hp); } - /// Integer conversion algorithm, guaranteed correct, optimal, and best. /// @val: The val. /// @buf: The output buffer. @@ -343,8 +337,7 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { } const m64 = @truncate(u64, @divTrunc(mid, x)); - if (lf != hf) - mi += 19; + if (lf != hf) mi += 19; var buf_index = u64toa(m64, buffer) - 1; @@ -354,7 +347,7 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { buf_index += 1; } - return FloatDecimal { + return FloatDecimal{ .digits = buffer[0..buf_index], .exp = i32(buf_index) + mi, }; @@ -396,25 +389,24 @@ fn errolFixed(val: f64, buffer: []u8) FloatDecimal { buffer[j] = u8(mdig + '0'); j += 1; - if(hdig != ldig or j > 50) - break; + if (hdig != ldig or j > 50) break; } if (mid > 0.5) { - buffer[j-1] += 1; - } else if ((mid == 0.5) and (buffer[j-1] & 0x1) != 0) { - buffer[j-1] += 1; + buffer[j - 1] += 1; + } else if ((mid == 0.5) and (buffer[j - 1] & 0x1) != 0) { + buffer[j - 1] += 1; } } else { - while (buffer[j-1] == '0') { - buffer[j-1] = 0; + while (buffer[j - 1] == '0') { + buffer[j - 1] = 0; j -= 1; } } buffer[j] = 0; - return FloatDecimal { + return FloatDecimal{ .digits = buffer[0..j], .exp = exp, }; @@ -587,7 +579,7 @@ fn u64toa(value_param: u64, buffer: []u8) usize { buffer[buf_index] = c_digits_lut[d8 + 1]; buf_index += 1; } else { - const a = u32(value / kTen16); // 1 to 1844 + const a = u32(value / kTen16); // 1 to 1844 value %= kTen16; if (a < 10) { @@ -686,7 +678,6 @@ fn fpeint(from: f64) u128 { return u128(1) << @truncate(u7, (bits >> 52) -% 1023); } - /// Given two different integers with the same length in terms of the number /// of decimal digits, index the digits from the right-most position starting /// from zero, find the first index where the digits in the two integers @@ -713,7 +704,6 @@ fn mismatch10(a: u64, b: u64) i32 { a_copy /= 10; b_copy /= 10; - if (a_copy == b_copy) - return i; + if (a_copy == b_copy) return i; } } diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 0a62b03ab2..45359e757d 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -10,33 +10,56 @@ pub const STDIN_FILENO = 0; pub const STDOUT_FILENO = 1; pub const STDERR_FILENO = 2; -pub const PROT_NONE = 0x00; /// [MC2] no permissions -pub const PROT_READ = 0x01; /// [MC2] pages can be read -pub const PROT_WRITE = 0x02; /// [MC2] pages can be written -pub const PROT_EXEC = 0x04; /// [MC2] pages can be executed - -pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space -pub const MAP_FILE = 0x0000; /// map from file (default) -pub const MAP_FIXED = 0x0010; /// interpret addr exactly -pub const MAP_HASSEMAPHORE = 0x0200; /// region may contain semaphores -pub const MAP_PRIVATE = 0x0002; /// changes are private -pub const MAP_SHARED = 0x0001; /// share changes -pub const MAP_NOCACHE = 0x0400; /// don't cache pages for this mapping -pub const MAP_NORESERVE = 0x0040; /// don't reserve needed swap area +/// [MC2] no permissions +pub const PROT_NONE = 0x00; +/// [MC2] pages can be read +pub const PROT_READ = 0x01; +/// [MC2] pages can be written +pub const PROT_WRITE = 0x02; +/// [MC2] pages can be executed +pub const PROT_EXEC = 0x04; + +/// allocated from memory, swap space +pub const MAP_ANONYMOUS = 0x1000; +/// map from file (default) +pub const MAP_FILE = 0x0000; +/// interpret addr exactly +pub const MAP_FIXED = 0x0010; +/// region may contain semaphores +pub const MAP_HASSEMAPHORE = 0x0200; +/// changes are private +pub const MAP_PRIVATE = 0x0002; +/// share changes +pub const MAP_SHARED = 0x0001; +/// don't cache pages for this mapping +pub const MAP_NOCACHE = 0x0400; +/// don't reserve needed swap area +pub const MAP_NORESERVE = 0x0040; pub const MAP_FAILED = @maxValue(usize); -pub const WNOHANG = 0x00000001; /// [XSI] no hang in wait/no child to reap -pub const WUNTRACED = 0x00000002; /// [XSI] notify on stop, untraced child - -pub const SA_ONSTACK = 0x0001; /// take signal on signal stack -pub const SA_RESTART = 0x0002; /// restart system on signal return -pub const SA_RESETHAND = 0x0004; /// reset to SIG_DFL when taking signal -pub const SA_NOCLDSTOP = 0x0008; /// do not generate SIGCHLD on child stop -pub const SA_NODEFER = 0x0010; /// don't mask the signal we're delivering -pub const SA_NOCLDWAIT = 0x0020; /// don't keep zombies around -pub const SA_SIGINFO = 0x0040; /// signal handler with SA_SIGINFO args -pub const SA_USERTRAMP = 0x0100; /// do not bounce off kernel's sigtramp -pub const SA_64REGSET = 0x0200; /// signal handler with SA_SIGINFO args with 64bit regs information +/// [XSI] no hang in wait/no child to reap +pub const WNOHANG = 0x00000001; +/// [XSI] notify on stop, untraced child +pub const WUNTRACED = 0x00000002; + +/// take signal on signal stack +pub const SA_ONSTACK = 0x0001; +/// restart system on signal return +pub const SA_RESTART = 0x0002; +/// reset to SIG_DFL when taking signal +pub const SA_RESETHAND = 0x0004; +/// do not generate SIGCHLD on child stop +pub const SA_NOCLDSTOP = 0x0008; +/// don't mask the signal we're delivering +pub const SA_NODEFER = 0x0010; +/// don't keep zombies around +pub const SA_NOCLDWAIT = 0x0020; +/// signal handler with SA_SIGINFO args +pub const SA_SIGINFO = 0x0040; +/// do not bounce off kernel's sigtramp +pub const SA_USERTRAMP = 0x0100; +/// signal handler with SA_SIGINFO args with 64bit regs information +pub const SA_64REGSET = 0x0200; pub const O_LARGEFILE = 0x0000; pub const O_PATH = 0x0000; @@ -46,20 +69,34 @@ pub const X_OK = 1; pub const W_OK = 2; pub const R_OK = 4; -pub const O_RDONLY = 0x0000; /// open for reading only -pub const O_WRONLY = 0x0001; /// open for writing only -pub const O_RDWR = 0x0002; /// open for reading and writing -pub const O_NONBLOCK = 0x0004; /// do not block on open or for data to become available -pub const O_APPEND = 0x0008; /// append on each write -pub const O_CREAT = 0x0200; /// create file if it does not exist -pub const O_TRUNC = 0x0400; /// truncate size to 0 -pub const O_EXCL = 0x0800; /// error if O_CREAT and the file exists -pub const O_SHLOCK = 0x0010; /// atomically obtain a shared lock -pub const O_EXLOCK = 0x0020; /// atomically obtain an exclusive lock -pub const O_NOFOLLOW = 0x0100; /// do not follow symlinks -pub const O_SYMLINK = 0x200000; /// allow open of symlinks -pub const O_EVTONLY = 0x8000; /// descriptor requested for event notifications only -pub const O_CLOEXEC = 0x1000000; /// mark as close-on-exec +/// open for reading only +pub const O_RDONLY = 0x0000; +/// open for writing only +pub const O_WRONLY = 0x0001; +/// open for reading and writing +pub const O_RDWR = 0x0002; +/// do not block on open or for data to become available +pub const O_NONBLOCK = 0x0004; +/// append on each write +pub const O_APPEND = 0x0008; +/// create file if it does not exist +pub const O_CREAT = 0x0200; +/// truncate size to 0 +pub const O_TRUNC = 0x0400; +/// error if O_CREAT and the file exists +pub const O_EXCL = 0x0800; +/// atomically obtain a shared lock +pub const O_SHLOCK = 0x0010; +/// atomically obtain an exclusive lock +pub const O_EXLOCK = 0x0020; +/// do not follow symlinks +pub const O_NOFOLLOW = 0x0100; +/// allow open of symlinks +pub const O_SYMLINK = 0x200000; +/// descriptor requested for event notifications only +pub const O_EVTONLY = 0x8000; +/// mark as close-on-exec +pub const O_CLOEXEC = 0x1000000; pub const O_ACCMODE = 3; pub const O_ALERT = 536870912; @@ -87,52 +124,102 @@ pub const DT_LNK = 10; pub const DT_SOCK = 12; pub const DT_WHT = 14; -pub const SIG_BLOCK = 1; /// block specified signal set -pub const SIG_UNBLOCK = 2; /// unblock specified signal set -pub const SIG_SETMASK = 3; /// set specified signal set - -pub const SIGHUP = 1; /// hangup -pub const SIGINT = 2; /// interrupt -pub const SIGQUIT = 3; /// quit -pub const SIGILL = 4; /// illegal instruction (not reset when caught) -pub const SIGTRAP = 5; /// trace trap (not reset when caught) -pub const SIGABRT = 6; /// abort() -pub const SIGPOLL = 7; /// pollable event ([XSR] generated, not supported) -pub const SIGIOT = SIGABRT; /// compatibility -pub const SIGEMT = 7; /// EMT instruction -pub const SIGFPE = 8; /// floating point exception -pub const SIGKILL = 9; /// kill (cannot be caught or ignored) -pub const SIGBUS = 10; /// bus error -pub const SIGSEGV = 11; /// segmentation violation -pub const SIGSYS = 12; /// bad argument to system call -pub const SIGPIPE = 13; /// write on a pipe with no one to read it -pub const SIGALRM = 14; /// alarm clock -pub const SIGTERM = 15; /// software termination signal from kill -pub const SIGURG = 16; /// urgent condition on IO channel -pub const SIGSTOP = 17; /// sendable stop signal not from tty -pub const SIGTSTP = 18; /// stop signal from tty -pub const SIGCONT = 19; /// continue a stopped process -pub const SIGCHLD = 20; /// to parent on child stop or exit -pub const SIGTTIN = 21; /// to readers pgrp upon background tty read -pub const SIGTTOU = 22; /// like TTIN for output if (tp->t_local<OSTOP) -pub const SIGIO = 23; /// input/output possible signal -pub const SIGXCPU = 24; /// exceeded CPU time limit -pub const SIGXFSZ = 25; /// exceeded file size limit -pub const SIGVTALRM = 26; /// virtual time alarm -pub const SIGPROF = 27; /// profiling time alarm -pub const SIGWINCH = 28; /// window size changes -pub const SIGINFO = 29; /// information request -pub const SIGUSR1 = 30; /// user defined signal 1 -pub const SIGUSR2 = 31; /// user defined signal 2 - -fn wstatus(x: i32) i32 { return x & 0o177; } +/// block specified signal set +pub const SIG_BLOCK = 1; +/// unblock specified signal set +pub const SIG_UNBLOCK = 2; +/// set specified signal set +pub const SIG_SETMASK = 3; + +/// hangup +pub const SIGHUP = 1; +/// interrupt +pub const SIGINT = 2; +/// quit +pub const SIGQUIT = 3; +/// illegal instruction (not reset when caught) +pub const SIGILL = 4; +/// trace trap (not reset when caught) +pub const SIGTRAP = 5; +/// abort() +pub const SIGABRT = 6; +/// pollable event ([XSR] generated, not supported) +pub const SIGPOLL = 7; +/// compatibility +pub const SIGIOT = SIGABRT; +/// EMT instruction +pub const SIGEMT = 7; +/// floating point exception +pub const SIGFPE = 8; +/// kill (cannot be caught or ignored) +pub const SIGKILL = 9; +/// bus error +pub const SIGBUS = 10; +/// segmentation violation +pub const SIGSEGV = 11; +/// bad argument to system call +pub const SIGSYS = 12; +/// write on a pipe with no one to read it +pub const SIGPIPE = 13; +/// alarm clock +pub const SIGALRM = 14; +/// software termination signal from kill +pub const SIGTERM = 15; +/// urgent condition on IO channel +pub const SIGURG = 16; +/// sendable stop signal not from tty +pub const SIGSTOP = 17; +/// stop signal from tty +pub const SIGTSTP = 18; +/// continue a stopped process +pub const SIGCONT = 19; +/// to parent on child stop or exit +pub const SIGCHLD = 20; +/// to readers pgrp upon background tty read +pub const SIGTTIN = 21; +/// like TTIN for output if (tp->t_local<OSTOP) +pub const SIGTTOU = 22; +/// input/output possible signal +pub const SIGIO = 23; +/// exceeded CPU time limit +pub const SIGXCPU = 24; +/// exceeded file size limit +pub const SIGXFSZ = 25; +/// virtual time alarm +pub const SIGVTALRM = 26; +/// profiling time alarm +pub const SIGPROF = 27; +/// window size changes +pub const SIGWINCH = 28; +/// information request +pub const SIGINFO = 29; +/// user defined signal 1 +pub const SIGUSR1 = 30; +/// user defined signal 2 +pub const SIGUSR2 = 31; + +fn wstatus(x: i32) i32 { + return x & 0o177; +} const wstopped = 0o177; -pub fn WEXITSTATUS(x: i32) i32 { return x >> 8; } -pub fn WTERMSIG(x: i32) i32 { return wstatus(x); } -pub fn WSTOPSIG(x: i32) i32 { return x >> 8; } -pub fn WIFEXITED(x: i32) bool { return wstatus(x) == 0; } -pub fn WIFSTOPPED(x: i32) bool { return wstatus(x) == wstopped and WSTOPSIG(x) != 0x13; } -pub fn WIFSIGNALED(x: i32) bool { return wstatus(x) != wstopped and wstatus(x) != 0; } +pub fn WEXITSTATUS(x: i32) i32 { + return x >> 8; +} +pub fn WTERMSIG(x: i32) i32 { + return wstatus(x); +} +pub fn WSTOPSIG(x: i32) i32 { + return x >> 8; +} +pub fn WIFEXITED(x: i32) bool { + return wstatus(x) == 0; +} +pub fn WIFSTOPPED(x: i32) bool { + return wstatus(x) == wstopped and WSTOPSIG(x) != 0x13; +} +pub fn WIFSIGNALED(x: i32) bool { + return wstatus(x) != wstopped and wstatus(x) != 0; +} /// Get the errno from a syscall return value, or 0 for no error. pub fn getErrno(r: usize) usize { @@ -184,11 +271,8 @@ pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize { return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte)); } -pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, - offset: isize) usize -{ - const ptr_result = c.mmap(@ptrCast(&c_void, address), length, - @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); +pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { + const ptr_result = c.mmap(@ptrCast(&c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); return errnoWrap(isize_result); } @@ -202,7 +286,7 @@ pub fn unlink(path: &const u8) usize { } pub fn getcwd(buf: &u8, size: usize) usize { - return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(*c._errno())) else 0; + return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; } pub fn waitpid(pid: i32, status: &i32, options: u32) usize { @@ -223,7 +307,6 @@ pub fn pipe(fds: &[2]i32) usize { return errnoWrap(c.pipe(@ptrCast(&c_int, fds))); } - pub fn getdirentries64(fd: i32, buf_ptr: &u8, buf_len: usize, basep: &i64) usize { return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep))); } @@ -269,7 +352,7 @@ pub fn nanosleep(req: &const timespec, rem: ?×pec) usize { } pub fn realpath(noalias filename: &const u8, noalias resolved_name: &u8) usize { - return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(*c._errno())) else 0; + return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; } pub fn setreuid(ruid: u32, euid: u32) usize { @@ -287,8 +370,8 @@ pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&s pub fn sigaction(sig: u5, noalias act: &const Sigaction, noalias oact: ?&Sigaction) usize { assert(sig != SIGKILL); assert(sig != SIGSTOP); - var cact = c.Sigaction { - .handler = @ptrCast(extern fn(c_int)void, act.handler), + var cact = c.Sigaction{ + .handler = @ptrCast(extern fn(c_int) void, act.handler), .sa_flags = @bitCast(c_int, act.flags), .sa_mask = act.mask, }; @@ -298,8 +381,8 @@ pub fn sigaction(sig: u5, noalias act: &const Sigaction, noalias oact: ?&Sigacti return result; } if (oact) |old| { - *old = Sigaction { - .handler = @ptrCast(extern fn(i32)void, coact.handler), + old.* = Sigaction{ + .handler = @ptrCast(extern fn(i32) void, coact.handler), .flags = @bitCast(u32, coact.sa_flags), .mask = coact.sa_mask, }; @@ -319,23 +402,22 @@ pub const sockaddr = c.sockaddr; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = struct { - handler: extern fn(i32)void, + handler: extern fn(i32) void, mask: sigset_t, flags: u32, }; pub fn sigaddset(set: &sigset_t, signo: u5) void { - *set |= u32(1) << (signo - 1); + set.* |= u32(1) << (signo - 1); } /// Takes the return value from a syscall and formats it back in the way /// that the kernel represents it to libc. Errno was a mistake, let's make /// it go away forever. fn errnoWrap(value: isize) usize { - return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value); + return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); } - pub const timezone = c.timezone; pub const timeval = c.timeval; pub const mach_timebase_info_data = c.mach_timebase_info_data; diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 596b57bdb6..86135fe100 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -40,7 +40,7 @@ pub const Tree = struct { }; pub fn tokenLocationPtr(self: &Tree, start_index: usize, token: &const Token) Location { - var loc = Location { + var loc = Location{ .line = 0, .column = 0, .line_start = start_index, @@ -71,7 +71,6 @@ pub const Tree = struct { pub fn dump(self: &Tree) void { self.root_node.base.dump(0); } - }; pub const Error = union(enum) { @@ -95,7 +94,7 @@ pub const Error = union(enum) { ExpectedCommaOrEnd: ExpectedCommaOrEnd, pub fn render(self: &Error, tokens: &Tree.TokenList, stream: var) !void { - switch (*self) { + switch (self.*) { // TODO https://github.com/zig-lang/zig/issues/683 @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream), @TagType(Error).ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream), @@ -119,7 +118,7 @@ pub const Error = union(enum) { } pub fn loc(self: &Error) TokenIndex { - switch (*self) { + switch (self.*) { // TODO https://github.com/zig-lang/zig/issues/683 @TagType(Error).InvalidToken => |x| return x.token, @TagType(Error).ExpectedVarDeclOrFn => |x| return x.token, @@ -144,15 +143,12 @@ pub const Error = union(enum) { pub const InvalidToken = SingleTokenError("Invalid token {}"); pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found {}"); - pub const ExpectedAggregateKw = SingleTokenError("Expected " ++ - @tagName(Token.Id.Keyword_struct) ++ ", " ++ @tagName(Token.Id.Keyword_union) ++ ", or " ++ - @tagName(Token.Id.Keyword_enum) ++ ", found {}"); + pub const ExpectedAggregateKw = SingleTokenError("Expected " ++ @tagName(Token.Id.Keyword_struct) ++ ", " ++ @tagName(Token.Id.Keyword_union) ++ ", or " ++ @tagName(Token.Id.Keyword_enum) ++ ", found {}"); pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found {}"); pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found {}"); pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found {}"); pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found {}"); - pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or " ++ - @tagName(Token.Id.Identifier) ++ ", found {}"); + pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or " ++ @tagName(Token.Id.Identifier) ++ ", found {}"); pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found {}"); pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found {}"); @@ -165,8 +161,7 @@ pub const Error = union(enum) { node: &Node, pub fn render(self: &ExpectedCall, tokens: &Tree.TokenList, stream: var) !void { - return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", - @tagName(self.node.id)); + return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", @tagName(self.node.id)); } }; @@ -174,8 +169,7 @@ pub const Error = union(enum) { node: &Node, pub fn render(self: &ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void { - return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ - @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id)); + return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id)); } }; @@ -445,17 +439,17 @@ pub const Node = struct { pub fn iterate(self: &Root, index: usize) ?&Node { if (index < self.decls.len) { - return *self.decls.at(index); + return self.decls.at(index).*; } return null; } pub fn firstToken(self: &Root) TokenIndex { - return if (self.decls.len == 0) self.eof_token else (*self.decls.at(0)).firstToken(); + return if (self.decls.len == 0) self.eof_token else (self.decls.at(0).*).firstToken(); } pub fn lastToken(self: &Root) TokenIndex { - return if (self.decls.len == 0) self.eof_token else (*self.decls.at(self.decls.len - 1)).lastToken(); + return if (self.decls.len == 0) self.eof_token else (self.decls.at(self.decls.len - 1).*).lastToken(); } }; @@ -545,7 +539,7 @@ pub const Node = struct { pub fn iterate(self: &ErrorSetDecl, index: usize) ?&Node { var i = index; - if (i < self.decls.len) return *self.decls.at(i); + if (i < self.decls.len) return self.decls.at(i).*; i -= self.decls.len; return null; @@ -598,10 +592,10 @@ pub const Node = struct { i -= 1; }, InitArg.None, - InitArg.Enum => { } + InitArg.Enum => {}, } - if (i < self.fields_and_decls.len) return *self.fields_and_decls.at(i); + if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i).*; i -= self.fields_and_decls.len; return null; @@ -814,7 +808,7 @@ pub const Node = struct { i -= 1; } - if (i < self.params.len) return *self.params.at(self.params.len - i - 1); + if (i < self.params.len) return self.params.at(self.params.len - i - 1).*; i -= self.params.len; if (self.align_expr) |align_expr| { @@ -839,7 +833,6 @@ pub const Node = struct { i -= 1; } - return null; } @@ -934,7 +927,7 @@ pub const Node = struct { pub fn iterate(self: &Block, index: usize) ?&Node { var i = index; - if (i < self.statements.len) return *self.statements.at(i); + if (i < self.statements.len) return self.statements.at(i).*; i -= self.statements.len; return null; @@ -1119,6 +1112,7 @@ pub const Node = struct { base: Node, switch_token: TokenIndex, expr: &Node, + /// these can be SwitchCase nodes or LineComment nodes cases: CaseList, rbrace: TokenIndex, @@ -1131,7 +1125,7 @@ pub const Node = struct { if (i < 1) return self.expr; i -= 1; - if (i < self.cases.len) return *self.cases.at(i); + if (i < self.cases.len) return self.cases.at(i).*; i -= self.cases.len; return null; @@ -1157,7 +1151,7 @@ pub const Node = struct { pub fn iterate(self: &SwitchCase, index: usize) ?&Node { var i = index; - if (i < self.items.len) return *self.items.at(i); + if (i < self.items.len) return self.items.at(i).*; i -= self.items.len; if (self.payload) |payload| { @@ -1172,7 +1166,7 @@ pub const Node = struct { } pub fn firstToken(self: &SwitchCase) TokenIndex { - return (*self.items.at(0)).firstToken(); + return (self.items.at(0).*).firstToken(); } pub fn lastToken(self: &SwitchCase) TokenIndex { @@ -1616,7 +1610,7 @@ pub const Node = struct { switch (self.op) { @TagType(Op).Call => |*call_info| { - if (i < call_info.params.len) return *call_info.params.at(i); + if (i < call_info.params.len) return call_info.params.at(i).*; i -= call_info.params.len; }, Op.ArrayAccess => |index_expr| { @@ -1633,11 +1627,11 @@ pub const Node = struct { } }, Op.ArrayInitializer => |*exprs| { - if (i < exprs.len) return *exprs.at(i); + if (i < exprs.len) return exprs.at(i).*; i -= exprs.len; }, Op.StructInitializer => |*fields| { - if (i < fields.len) return *fields.at(i); + if (i < fields.len) return fields.at(i).*; i -= fields.len; }, } @@ -1830,7 +1824,7 @@ pub const Node = struct { pub fn iterate(self: &BuiltinCall, index: usize) ?&Node { var i = index; - if (i < self.params.len) return *self.params.at(i); + if (i < self.params.len) return self.params.at(i).*; i -= self.params.len; return null; @@ -1873,11 +1867,11 @@ pub const Node = struct { } pub fn firstToken(self: &MultilineStringLiteral) TokenIndex { - return *self.lines.at(0); + return self.lines.at(0).*; } pub fn lastToken(self: &MultilineStringLiteral) TokenIndex { - return *self.lines.at(self.lines.len - 1); + return self.lines.at(self.lines.len - 1).*; } }; @@ -1974,7 +1968,7 @@ pub const Node = struct { const Kind = union(enum) { Variable: &Identifier, - Return: &Node + Return: &Node, }; pub fn iterate(self: &AsmOutput, index: usize) ?&Node { @@ -1994,7 +1988,7 @@ pub const Node = struct { Kind.Return => |return_type| { if (i < 1) return return_type; i -= 1; - } + }, } return null; @@ -2059,13 +2053,13 @@ pub const Node = struct { pub fn iterate(self: &Asm, index: usize) ?&Node { var i = index; - if (i < self.outputs.len) return &(*self.outputs.at(index)).base; + if (i < self.outputs.len) return &(self.outputs.at(index).*).base; i -= self.outputs.len; - if (i < self.inputs.len) return &(*self.inputs.at(index)).base; + if (i < self.inputs.len) return &(self.inputs.at(index).*).base; i -= self.inputs.len; - if (i < self.clobbers.len) return *self.clobbers.at(index); + if (i < self.clobbers.len) return self.clobbers.at(index).*; i -= self.clobbers.len; return null; @@ -2159,11 +2153,11 @@ pub const Node = struct { } pub fn firstToken(self: &DocComment) TokenIndex { - return *self.lines.at(0); + return self.lines.at(0).*; } pub fn lastToken(self: &DocComment) TokenIndex { - return *self.lines.at(self.lines.len - 1); + return self.lines.at(self.lines.len - 1).*; } }; @@ -2192,4 +2186,3 @@ pub const Node = struct { } }; }; - diff --git a/std/zig/parse.zig b/std/zig/parse.zig index f2376f0df3..04aeeccaeb 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -17,15 +17,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { defer stack.deinit(); const arena = &tree_arena.allocator; - const root_node = try arena.construct(ast.Node.Root { - .base = ast.Node { .id = ast.Node.Id.Root }, + const root_node = try arena.construct(ast.Node.Root{ + .base = ast.Node{ .id = ast.Node.Id.Root }, .decls = ast.Node.Root.DeclList.init(arena), .doc_comments = null, // initialized when we get the eof token .eof_token = undefined, }); - var tree = ast.Tree { + var tree = ast.Tree{ .source = source, .root_node = root_node, .arena_allocator = tree_arena, @@ -36,9 +36,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { var tokenizer = Tokenizer.init(tree.source); while (true) { const token_ptr = try tree.tokens.addOne(); - *token_ptr = tokenizer.next(); - if (token_ptr.id == Token.Id.Eof) - break; + token_ptr.* = tokenizer.next(); + if (token_ptr.id == Token.Id.Eof) break; } var tok_it = tree.tokens.iterator(0); @@ -63,33 +62,27 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Keyword_test => { stack.append(State.TopLevel) catch unreachable; - const block = try arena.construct(ast.Node.Block { - .base = ast.Node { - .id = ast.Node.Id.Block, - }, + const block = try arena.construct(ast.Node.Block{ + .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = undefined, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, }); - const test_node = try arena.construct(ast.Node.TestDecl { - .base = ast.Node { - .id = ast.Node.Id.TestDecl, - }, + const test_node = try arena.construct(ast.Node.TestDecl{ + .base = ast.Node{ .id = ast.Node.Id.TestDecl }, .doc_comments = comments, .test_token = token_index, .name = undefined, .body_node = &block.base, }); try root_node.decls.push(&test_node.base); - try stack.append(State { .Block = block }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.LBrace, - .ptr = &block.rbrace, - } - }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } }); + try stack.append(State{ .Block = block }); + try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.LBrace, + .ptr = &block.rbrace, + } }); + try stack.append(State{ .StringLiteral = OptionalCtx{ .Required = &test_node.name } }); continue; }, Token.Id.Eof => { @@ -99,29 +92,25 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, Token.Id.Keyword_pub => { stack.append(State.TopLevel) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &root_node.decls, - .visib_token = token_index, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); + try stack.append(State{ .TopLevelExtern = TopLevelDeclCtx{ + .decls = &root_node.decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } }); continue; }, Token.Id.Keyword_comptime => { - const block = try arena.construct(ast.Node.Block { - .base = ast.Node {.id = ast.Node.Id.Block }, + const block = try arena.construct(ast.Node.Block{ + .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = undefined, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, }); - const node = try arena.construct(ast.Node.Comptime { - .base = ast.Node { - .id = ast.Node.Id.Comptime, - }, + const node = try arena.construct(ast.Node.Comptime{ + .base = ast.Node{ .id = ast.Node.Id.Comptime }, .comptime_token = token_index, .expr = &block.base, .doc_comments = comments, @@ -129,27 +118,23 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try root_node.decls.push(&node.base); stack.append(State.TopLevel) catch unreachable; - try stack.append(State { .Block = block }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.LBrace, - .ptr = &block.rbrace, - } - }); + try stack.append(State{ .Block = block }); + try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.LBrace, + .ptr = &block.rbrace, + } }); continue; }, else => { putBackToken(&tok_it, &tree); stack.append(State.TopLevel) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &root_node.decls, - .visib_token = null, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); + try stack.append(State{ .TopLevelExtern = TopLevelDeclCtx{ + .decls = &root_node.decls, + .visib_token = null, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } }); continue; }, } @@ -159,41 +144,38 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; switch (token_ptr.id) { - Token.Id.Keyword_export, Token.Id.Keyword_inline => { - stack.append(State { - .TopLevelDecl = TopLevelDeclCtx { - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = AnnotatedToken { - .index = token_index, - .ptr = token_ptr, - }, - .lib_name = null, - .comments = ctx.comments, + Token.Id.Keyword_export, + Token.Id.Keyword_inline => { + stack.append(State{ .TopLevelDecl = TopLevelDeclCtx{ + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = AnnotatedToken{ + .index = token_index, + .ptr = token_ptr, }, - }) catch unreachable; + .lib_name = null, + .comments = ctx.comments, + } }) catch unreachable; continue; }, Token.Id.Keyword_extern => { - stack.append(State { - .TopLevelLibname = TopLevelDeclCtx { - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = AnnotatedToken { - .index = token_index, - .ptr = token_ptr, - }, - .lib_name = null, - .comments = ctx.comments, + stack.append(State{ .TopLevelLibname = TopLevelDeclCtx{ + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = AnnotatedToken{ + .index = token_index, + .ptr = token_ptr, }, - }) catch unreachable; + .lib_name = null, + .comments = ctx.comments, + } }) catch unreachable; continue; }, else => { putBackToken(&tok_it, &tree); - stack.append(State { .TopLevelDecl = ctx }) catch unreachable; + stack.append(State{ .TopLevelDecl = ctx }) catch unreachable; continue; - } + }, } }, State.TopLevelLibname => |ctx| { @@ -207,15 +189,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }; }; - stack.append(State { - .TopLevelDecl = TopLevelDeclCtx { - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = ctx.extern_export_inline_token, - .lib_name = lib_name, - .comments = ctx.comments, - }, - }) catch unreachable; + stack.append(State{ .TopLevelDecl = TopLevelDeclCtx{ + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = ctx.extern_export_inline_token, + .lib_name = lib_name, + .comments = ctx.comments, + } }) catch unreachable; continue; }, State.TopLevelDecl => |ctx| { @@ -225,14 +205,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { switch (token_ptr.id) { Token.Id.Keyword_use => { if (ctx.extern_export_inline_token) |annotated_token| { - *(try tree.errors.addOne()) = Error { - .InvalidToken = Error.InvalidToken { .token = annotated_token.index }, - }; + ((try tree.errors.addOne())).* = Error{ .InvalidToken = Error.InvalidToken{ .token = annotated_token.index } }; return tree; } - const node = try arena.construct(ast.Node.Use { - .base = ast.Node {.id = ast.Node.Id.Use }, + const node = try arena.construct(ast.Node.Use{ + .base = ast.Node{ .id = ast.Node.Id.Use }, .visib_token = ctx.visib_token, .expr = undefined, .semicolon_token = undefined, @@ -240,44 +218,39 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try ctx.decls.push(&node.base); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Semicolon, - .ptr = &node.semicolon_token, - } - }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); + stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Semicolon, + .ptr = &node.semicolon_token, + } }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); continue; }, - Token.Id.Keyword_var, Token.Id.Keyword_const => { + Token.Id.Keyword_var, + Token.Id.Keyword_const => { if (ctx.extern_export_inline_token) |annotated_token| { if (annotated_token.ptr.id == Token.Id.Keyword_inline) { - *(try tree.errors.addOne()) = Error { - .InvalidToken = Error.InvalidToken { .token = annotated_token.index }, - }; + ((try tree.errors.addOne())).* = Error{ .InvalidToken = Error.InvalidToken{ .token = annotated_token.index } }; return tree; } } - try stack.append(State { - .VarDecl = VarDeclCtx { - .comments = ctx.comments, - .visib_token = ctx.visib_token, - .lib_name = ctx.lib_name, - .comptime_token = null, - .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null, - .mut_token = token_index, - .list = ctx.decls - } - }); - continue; - }, - Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, - Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - }, + try stack.append(State{ .VarDecl = VarDeclCtx{ + .comments = ctx.comments, + .visib_token = ctx.visib_token, + .lib_name = ctx.lib_name, + .comptime_token = null, + .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null, + .mut_token = token_index, + .list = ctx.decls, + } }); + continue; + }, + Token.Id.Keyword_fn, + Token.Id.Keyword_nakedcc, + Token.Id.Keyword_stdcallcc, + Token.Id.Keyword_async => { + const fn_proto = try arena.construct(ast.Node.FnProto{ + .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, .name_token = null, @@ -293,36 +266,33 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .align_expr = null, }); try ctx.decls.push(&fn_proto.base); - stack.append(State { .FnDef = fn_proto }) catch unreachable; - try stack.append(State { .FnProto = fn_proto }); + stack.append(State{ .FnDef = fn_proto }) catch unreachable; + try stack.append(State{ .FnProto = fn_proto }); switch (token_ptr.id) { - Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { + Token.Id.Keyword_nakedcc, + Token.Id.Keyword_stdcallcc => { fn_proto.cc_token = token_index; - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } - }); + try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + } }); continue; }, Token.Id.Keyword_async => { - const async_node = try arena.construct(ast.Node.AsyncAttribute { - .base = ast.Node {.id = ast.Node.Id.AsyncAttribute }, + const async_node = try arena.construct(ast.Node.AsyncAttribute{ + .base = ast.Node{ .id = ast.Node.Id.AsyncAttribute }, .async_token = token_index, .allocator_type = null, .rangle_bracket = null, }); fn_proto.async_attr = async_node; - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } - }); - try stack.append(State { .AsyncAllocator = async_node }); + try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + } }); + try stack.append(State{ .AsyncAllocator = async_node }); continue; }, Token.Id.Keyword_fn => { @@ -333,9 +303,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, else => { - *(try tree.errors.addOne()) = Error { - .ExpectedVarDeclOrFn = Error.ExpectedVarDeclOrFn { .token = token_index }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedVarDeclOrFn = Error.ExpectedVarDeclOrFn{ .token = token_index } }; return tree; }, } @@ -343,34 +311,30 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.TopLevelExternOrField => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |identifier| { std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct); - const node = try arena.construct(ast.Node.StructField { - .base = ast.Node { - .id = ast.Node.Id.StructField, - }, + const node = try arena.construct(ast.Node.StructField{ + .base = ast.Node{ .id = ast.Node.Id.StructField }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, .name_token = identifier, .type_expr = undefined, }); const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); - *node_ptr = &node.base; + node_ptr.* = &node.base; - stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } }); - try stack.append(State { .ExpectToken = Token.Id.Colon }); + stack.append(State{ .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.type_expr } }); + try stack.append(State{ .ExpectToken = Token.Id.Colon }); continue; } stack.append(State{ .ContainerDecl = ctx.container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &ctx.container_decl.fields_and_decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = null, - .lib_name = null, - .comments = ctx.comments, - } - }); + try stack.append(State{ .TopLevelExtern = TopLevelDeclCtx{ + .decls = &ctx.container_decl.fields_and_decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = null, + .lib_name = null, + .comments = ctx.comments, + } }); continue; }, @@ -382,7 +346,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { putBackToken(&tok_it, &tree); continue; } - stack.append(State { .Expression = ctx }) catch unreachable; + stack.append(State{ .Expression = ctx }) catch unreachable; continue; }, @@ -390,8 +354,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; - const node = try arena.construct(ast.Node.ContainerDecl { - .base = ast.Node {.id = ast.Node.Id.ContainerDecl }, + const node = try arena.construct(ast.Node.ContainerDecl{ + .base = ast.Node{ .id = ast.Node.Id.ContainerDecl }, .ltoken = ctx.ltoken, .layout = ctx.layout, .kind = switch (token_ptr.id) { @@ -399,9 +363,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union, Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum, else => { - *(try tree.errors.addOne()) = Error { - .ExpectedAggregateKw = Error.ExpectedAggregateKw { .token = token_index }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedAggregateKw = Error.ExpectedAggregateKw{ .token = token_index } }; return tree; }, }, @@ -411,9 +373,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); ctx.opt_ctx.store(&node.base); - stack.append(State { .ContainerDecl = node }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.LBrace }); - try stack.append(State { .ContainerInitArgStart = node }); + stack.append(State{ .ContainerDecl = node }) catch unreachable; + try stack.append(State{ .ExpectToken = Token.Id.LBrace }); + try stack.append(State{ .ContainerInitArgStart = node }); continue; }, @@ -422,8 +384,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - try stack.append(State { .ContainerInitArg = container_decl }); + stack.append(State{ .ExpectToken = Token.Id.RParen }) catch unreachable; + try stack.append(State{ .ContainerInitArg = container_decl }); continue; }, @@ -433,23 +395,21 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const init_arg_token_ptr = init_arg_token.ptr; switch (init_arg_token_ptr.id) { Token.Id.Keyword_enum => { - container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null}; + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg{ .Enum = null }; const lparen_tok = nextToken(&tok_it, &tree); const lparen_tok_index = lparen_tok.index; const lparen_tok_ptr = lparen_tok.ptr; if (lparen_tok_ptr.id == Token.Id.LParen) { - try stack.append(State { .ExpectToken = Token.Id.RParen } ); - try stack.append(State { .Expression = OptionalCtx { - .RequiredNull = &container_decl.init_arg_expr.Enum, - } }); + try stack.append(State{ .ExpectToken = Token.Id.RParen }); + try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &container_decl.init_arg_expr.Enum } }); } else { putBackToken(&tok_it, &tree); } }, else => { putBackToken(&tok_it, &tree); - container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined }; - stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg{ .Type = undefined }; + stack.append(State{ .Expression = OptionalCtx{ .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; }, } continue; @@ -468,26 +428,24 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Identifier => { switch (container_decl.kind) { ast.Node.ContainerDecl.Kind.Struct => { - const node = try arena.construct(ast.Node.StructField { - .base = ast.Node { - .id = ast.Node.Id.StructField, - }, + const node = try arena.construct(ast.Node.StructField{ + .base = ast.Node{ .id = ast.Node.Id.StructField }, .doc_comments = comments, .visib_token = null, .name_token = token_index, .type_expr = undefined, }); const node_ptr = try container_decl.fields_and_decls.addOne(); - *node_ptr = &node.base; + node_ptr.* = &node.base; - try stack.append(State { .FieldListCommaOrEnd = container_decl }); - try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } }); - try stack.append(State { .ExpectToken = Token.Id.Colon }); + try stack.append(State{ .FieldListCommaOrEnd = container_decl }); + try stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.type_expr } }); + try stack.append(State{ .ExpectToken = Token.Id.Colon }); continue; }, ast.Node.ContainerDecl.Kind.Union => { - const node = try arena.construct(ast.Node.UnionTag { - .base = ast.Node {.id = ast.Node.Id.UnionTag }, + const node = try arena.construct(ast.Node.UnionTag{ + .base = ast.Node{ .id = ast.Node.Id.UnionTag }, .name_token = token_index, .type_expr = null, .value_expr = null, @@ -495,24 +453,24 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try container_decl.fields_and_decls.push(&node.base); - stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - try stack.append(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } }); - try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } }); - try stack.append(State { .IfToken = Token.Id.Colon }); + stack.append(State{ .FieldListCommaOrEnd = container_decl }) catch unreachable; + try stack.append(State{ .FieldInitValue = OptionalCtx{ .RequiredNull = &node.value_expr } }); + try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &node.type_expr } }); + try stack.append(State{ .IfToken = Token.Id.Colon }); continue; }, ast.Node.ContainerDecl.Kind.Enum => { - const node = try arena.construct(ast.Node.EnumTag { - .base = ast.Node { .id = ast.Node.Id.EnumTag }, + const node = try arena.construct(ast.Node.EnumTag{ + .base = ast.Node{ .id = ast.Node.Id.EnumTag }, .name_token = token_index, .value = null, .doc_comments = comments, }); try container_decl.fields_and_decls.push(&node.base); - stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &node.value } }); - try stack.append(State { .IfToken = Token.Id.Equal }); + stack.append(State{ .FieldListCommaOrEnd = container_decl }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &node.value } }); + try stack.append(State{ .IfToken = Token.Id.Equal }); continue; }, } @@ -520,48 +478,40 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Keyword_pub => { switch (container_decl.kind) { ast.Node.ContainerDecl.Kind.Struct => { - try stack.append(State { - .TopLevelExternOrField = TopLevelExternOrFieldCtx { - .visib_token = token_index, - .container_decl = container_decl, - .comments = comments, - } - }); + try stack.append(State{ .TopLevelExternOrField = TopLevelExternOrFieldCtx{ + .visib_token = token_index, + .container_decl = container_decl, + .comments = comments, + } }); continue; }, else => { stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = token_index, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); + try stack.append(State{ .TopLevelExtern = TopLevelDeclCtx{ + .decls = &container_decl.fields_and_decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } }); continue; - } + }, } }, Token.Id.Keyword_export => { stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = token_index, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); + try stack.append(State{ .TopLevelExtern = TopLevelDeclCtx{ + .decls = &container_decl.fields_and_decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } }); continue; }, Token.Id.RBrace => { if (comments != null) { - *(try tree.errors.addOne()) = Error { - .UnattachedDocComment = Error.UnattachedDocComment { .token = token_index }, - }; + ((try tree.errors.addOne())).* = Error{ .UnattachedDocComment = Error.UnattachedDocComment{ .token = token_index } }; return tree; } container_decl.rbrace_token = token_index; @@ -570,26 +520,21 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { else => { putBackToken(&tok_it, &tree); stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = null, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); + try stack.append(State{ .TopLevelExtern = TopLevelDeclCtx{ + .decls = &container_decl.fields_and_decls, + .visib_token = null, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } }); continue; - } + }, } }, - State.VarDecl => |ctx| { - const var_decl = try arena.construct(ast.Node.VarDecl { - .base = ast.Node { - .id = ast.Node.Id.VarDecl, - }, + const var_decl = try arena.construct(ast.Node.VarDecl{ + .base = ast.Node{ .id = ast.Node.Id.VarDecl }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, .mut_token = ctx.mut_token, @@ -606,27 +551,25 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try ctx.list.push(&var_decl.base); - try stack.append(State { .VarDeclAlign = var_decl }); - try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Identifier, - .ptr = &var_decl.name_token, - } - }); + try stack.append(State{ .VarDeclAlign = var_decl }); + try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &var_decl.type_node } }); + try stack.append(State{ .IfToken = Token.Id.Colon }); + try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Identifier, + .ptr = &var_decl.name_token, + } }); continue; }, State.VarDeclAlign => |var_decl| { - try stack.append(State { .VarDeclEq = var_decl }); + try stack.append(State{ .VarDeclEq = var_decl }); const next_token = nextToken(&tok_it, &tree); const next_token_index = next_token.index; const next_token_ptr = next_token.ptr; if (next_token_ptr.id == Token.Id.Keyword_align) { - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State{ .ExpectToken = Token.Id.RParen }); + try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &var_decl.align_node } }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); continue; } @@ -640,8 +583,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { switch (token_ptr.id) { Token.Id.Equal => { var_decl.eq_token = token_index; - stack.append(State { .VarDeclSemiColon = var_decl }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } }); + stack.append(State{ .VarDeclSemiColon = var_decl }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &var_decl.init_node } }); continue; }, Token.Id.Semicolon => { @@ -649,11 +592,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, else => { - *(try tree.errors.addOne()) = Error { - .ExpectedEqOrSemi = Error.ExpectedEqOrSemi { .token = token_index }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedEqOrSemi = Error.ExpectedEqOrSemi{ .token = token_index } }; return tree; - } + }, } }, @@ -661,12 +602,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const semicolon_token = nextToken(&tok_it, &tree); if (semicolon_token.ptr.id != Token.Id.Semicolon) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = semicolon_token.index, - .expected_id = Token.Id.Semicolon, - }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ + .token = semicolon_token.index, + .expected_id = Token.Id.Semicolon, + } }; return tree; } @@ -686,32 +625,30 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; - switch(token_ptr.id) { + switch (token_ptr.id) { Token.Id.LBrace => { - const block = try arena.construct(ast.Node.Block { - .base = ast.Node { .id = ast.Node.Id.Block }, + const block = try arena.construct(ast.Node.Block{ + .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, }); fn_proto.body_node = &block.base; - stack.append(State { .Block = block }) catch unreachable; + stack.append(State{ .Block = block }) catch unreachable; continue; }, Token.Id.Semicolon => continue, else => { - *(try tree.errors.addOne()) = Error { - .ExpectedSemiOrLBrace = Error.ExpectedSemiOrLBrace { .token = token_index }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedSemiOrLBrace = Error.ExpectedSemiOrLBrace{ .token = token_index } }; return tree; }, } }, State.FnProto => |fn_proto| { - stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable; - try stack.append(State { .ParamDecl = fn_proto }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); + stack.append(State{ .FnProtoAlign = fn_proto }) catch unreachable; + try stack.append(State{ .ParamDecl = fn_proto }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |name_token| { fn_proto.name_token = name_token; @@ -719,12 +656,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.FnProtoAlign => |fn_proto| { - stack.append(State { .FnProtoReturnType = fn_proto }) catch unreachable; + stack.append(State{ .FnProtoReturnType = fn_proto }) catch unreachable; if (eatToken(&tok_it, &tree, Token.Id.Keyword_align)) |align_token| { - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State{ .ExpectToken = Token.Id.RParen }); + try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &fn_proto.align_expr } }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); } continue; }, @@ -734,42 +671,37 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Bang => { - fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined }; - stack.append(State { - .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet }, - }) catch unreachable; + fn_proto.return_type = ast.Node.FnProto.ReturnType{ .InferErrorSet = undefined }; + stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &fn_proto.return_type.InferErrorSet } }) catch unreachable; continue; }, else => { // TODO: this is a special case. Remove this when #760 is fixed if (token_ptr.id == Token.Id.Keyword_error) { if ((??tok_it.peek()).id == Token.Id.LBrace) { - const error_type_node = try arena.construct(ast.Node.ErrorType { - .base = ast.Node { .id = ast.Node.Id.ErrorType }, + const error_type_node = try arena.construct(ast.Node.ErrorType{ + .base = ast.Node{ .id = ast.Node.Id.ErrorType }, .token = token_index, }); - fn_proto.return_type = ast.Node.FnProto.ReturnType { - .Explicit = &error_type_node.base, - }; + fn_proto.return_type = ast.Node.FnProto.ReturnType{ .Explicit = &error_type_node.base }; continue; } } putBackToken(&tok_it, &tree); - fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined }; - stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; + fn_proto.return_type = ast.Node.FnProto.ReturnType{ .Explicit = undefined }; + stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &fn_proto.return_type.Explicit } }) catch unreachable; continue; }, } }, - State.ParamDecl => |fn_proto| { if (eatToken(&tok_it, &tree, Token.Id.RParen)) |_| { continue; } - const param_decl = try arena.construct(ast.Node.ParamDecl { - .base = ast.Node {.id = ast.Node.Id.ParamDecl }, + const param_decl = try arena.construct(ast.Node.ParamDecl{ + .base = ast.Node{ .id = ast.Node.Id.ParamDecl }, .comptime_token = null, .noalias_token = null, .name_token = null, @@ -778,14 +710,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try fn_proto.params.push(¶m_decl.base); - stack.append(State { - .ParamDeclEnd = ParamDeclEndCtx { - .param_decl = param_decl, - .fn_proto = fn_proto, - } - }) catch unreachable; - try stack.append(State { .ParamDeclName = param_decl }); - try stack.append(State { .ParamDeclAliasOrComptime = param_decl }); + stack.append(State{ .ParamDeclEnd = ParamDeclEndCtx{ + .param_decl = param_decl, + .fn_proto = fn_proto, + } }) catch unreachable; + try stack.append(State{ .ParamDeclName = param_decl }); + try stack.append(State{ .ParamDeclAliasOrComptime = param_decl }); continue; }, State.ParamDeclAliasOrComptime => |param_decl| { @@ -811,21 +741,19 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.ParamDeclEnd => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| { ctx.param_decl.var_args_token = ellipsis3; - stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + stack.append(State{ .ExpectToken = Token.Id.RParen }) catch unreachable; continue; } - try stack.append(State { .ParamDeclComma = ctx.fn_proto }); - try stack.append(State { - .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node } - }); + try stack.append(State{ .ParamDeclComma = ctx.fn_proto }); + try stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &ctx.param_decl.type_node } }); continue; }, State.ParamDeclComma => |fn_proto| { switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) { ExpectCommaOrEndResult.end_token => |t| { if (t == null) { - stack.append(State { .ParamDecl = fn_proto }) catch unreachable; + stack.append(State{ .ParamDecl = fn_proto }) catch unreachable; } continue; }, @@ -838,12 +766,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.MaybeLabeledExpression => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Colon)) |_| { - stack.append(State { - .LabeledExpression = LabelCtx { - .label = ctx.label, - .opt_ctx = ctx.opt_ctx, - } - }) catch unreachable; + stack.append(State{ .LabeledExpression = LabelCtx{ + .label = ctx.label, + .opt_ctx = ctx.opt_ctx, + } }) catch unreachable; continue; } @@ -856,69 +782,59 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LBrace => { - const block = try arena.construct(ast.Node.Block { - .base = ast.Node {.id = ast.Node.Id.Block}, + const block = try arena.construct(ast.Node.Block{ + .base = ast.Node{ .id = ast.Node.Id.Block }, .label = ctx.label, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, }); ctx.opt_ctx.store(&block.base); - stack.append(State { .Block = block }) catch unreachable; + stack.append(State{ .Block = block }) catch unreachable; continue; }, Token.Id.Keyword_while => { - stack.append(State { - .While = LoopCtx { - .label = ctx.label, - .inline_token = null, - .loop_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; + stack.append(State{ .While = LoopCtx{ + .label = ctx.label, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + } }) catch unreachable; continue; }, Token.Id.Keyword_for => { - stack.append(State { - .For = LoopCtx { - .label = ctx.label, - .inline_token = null, - .loop_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; + stack.append(State{ .For = LoopCtx{ + .label = ctx.label, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + } }) catch unreachable; continue; }, Token.Id.Keyword_suspend => { - const node = try arena.construct(ast.Node.Suspend { - .base = ast.Node { - .id = ast.Node.Id.Suspend, - }, + const node = try arena.construct(ast.Node.Suspend{ + .base = ast.Node{ .id = ast.Node.Id.Suspend }, .label = ctx.label, .suspend_token = token_index, .payload = null, .body = null, }); ctx.opt_ctx.store(&node.base); - stack.append(State { .SuspendBody = node }) catch unreachable; - try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + stack.append(State{ .SuspendBody = node }) catch unreachable; + try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); continue; }, Token.Id.Keyword_inline => { - stack.append(State { - .Inline = InlineCtx { - .label = ctx.label, - .inline_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; + stack.append(State{ .Inline = InlineCtx{ + .label = ctx.label, + .inline_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + } }) catch unreachable; continue; }, else => { if (ctx.opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedLabelable = Error.ExpectedLabelable { .token = token_index }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedLabelable = Error.ExpectedLabelable{ .token = token_index } }; return tree; } @@ -933,32 +849,26 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_while => { - stack.append(State { - .While = LoopCtx { - .inline_token = ctx.inline_token, - .label = ctx.label, - .loop_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; + stack.append(State{ .While = LoopCtx{ + .inline_token = ctx.inline_token, + .label = ctx.label, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + } }) catch unreachable; continue; }, Token.Id.Keyword_for => { - stack.append(State { - .For = LoopCtx { - .inline_token = ctx.inline_token, - .label = ctx.label, - .loop_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; + stack.append(State{ .For = LoopCtx{ + .inline_token = ctx.inline_token, + .label = ctx.label, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + } }) catch unreachable; continue; }, else => { if (ctx.opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedInlinable = Error.ExpectedInlinable { .token = token_index }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedInlinable = Error.ExpectedInlinable{ .token = token_index } }; return tree; } @@ -968,8 +878,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, State.While => |ctx| { - const node = try arena.construct(ast.Node.While { - .base = ast.Node {.id = ast.Node.Id.While }, + const node = try arena.construct(ast.Node.While{ + .base = ast.Node{ .id = ast.Node.Id.While }, .label = ctx.label, .inline_token = ctx.inline_token, .while_token = ctx.loop_token, @@ -980,25 +890,25 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .@"else" = null, }); ctx.opt_ctx.store(&node.base); - stack.append(State { .Else = &node.@"else" }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.append(State { .WhileContinueExpr = &node.continue_expr }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); + stack.append(State{ .Else = &node.@"else" }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }); + try stack.append(State{ .WhileContinueExpr = &node.continue_expr }); + try stack.append(State{ .IfToken = Token.Id.Colon }); + try stack.append(State{ .PointerPayload = OptionalCtx{ .Optional = &node.payload } }); + try stack.append(State{ .ExpectToken = Token.Id.RParen }); + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.condition } }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); continue; }, State.WhileContinueExpr => |dest| { - stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); + stack.append(State{ .ExpectToken = Token.Id.RParen }) catch unreachable; + try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = dest } }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); continue; }, State.For => |ctx| { - const node = try arena.construct(ast.Node.For { - .base = ast.Node {.id = ast.Node.Id.For }, + const node = try arena.construct(ast.Node.For{ + .base = ast.Node{ .id = ast.Node.Id.For }, .label = ctx.label, .inline_token = ctx.inline_token, .for_token = ctx.loop_token, @@ -1008,33 +918,32 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .@"else" = null, }); ctx.opt_ctx.store(&node.base); - stack.append(State { .Else = &node.@"else" }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.append(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.array_expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); + stack.append(State{ .Else = &node.@"else" }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }); + try stack.append(State{ .PointerIndexPayload = OptionalCtx{ .Optional = &node.payload } }); + try stack.append(State{ .ExpectToken = Token.Id.RParen }); + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.array_expr } }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); continue; }, State.Else => |dest| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_else)) |else_token| { - const node = try arena.construct(ast.Node.Else { - .base = ast.Node {.id = ast.Node.Id.Else }, + const node = try arena.construct(ast.Node.Else{ + .base = ast.Node{ .id = ast.Node.Id.Else }, .else_token = else_token, .payload = null, .body = undefined, }); - *dest = node; + dest.* = node; - stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable; - try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }) catch unreachable; + try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); continue; } else { continue; } }, - State.Block => |block| { const token = nextToken(&tok_it, &tree); const token_index = token.index; @@ -1046,7 +955,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, else => { putBackToken(&tok_it, &tree); - stack.append(State { .Block = block }) catch unreachable; + stack.append(State{ .Block = block }) catch unreachable; var any_comments = false; while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| { @@ -1055,7 +964,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } if (any_comments) continue; - try stack.append(State { .Statement = block }); + try stack.append(State{ .Statement = block }); continue; }, } @@ -1066,33 +975,29 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_comptime => { - stack.append(State { - .ComptimeStatement = ComptimeStatementCtx { - .comptime_token = token_index, - .block = block, - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_var, Token.Id.Keyword_const => { - stack.append(State { - .VarDecl = VarDeclCtx { - .comments = null, - .visib_token = null, - .comptime_token = null, - .extern_export_token = null, - .lib_name = null, - .mut_token = token_index, - .list = &block.statements, - } - }) catch unreachable; + stack.append(State{ .ComptimeStatement = ComptimeStatementCtx{ + .comptime_token = token_index, + .block = block, + } }) catch unreachable; continue; }, - Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { - const node = try arena.construct(ast.Node.Defer { - .base = ast.Node { - .id = ast.Node.Id.Defer, - }, + Token.Id.Keyword_var, + Token.Id.Keyword_const => { + stack.append(State{ .VarDecl = VarDeclCtx{ + .comments = null, + .visib_token = null, + .comptime_token = null, + .extern_export_token = null, + .lib_name = null, + .mut_token = token_index, + .list = &block.statements, + } }) catch unreachable; + continue; + }, + Token.Id.Keyword_defer, + Token.Id.Keyword_errdefer => { + const node = try arena.construct(ast.Node.Defer{ + .base = ast.Node{ .id = ast.Node.Id.Defer }, .defer_token = token_index, .kind = switch (token_ptr.id) { Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, @@ -1102,15 +1007,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .expr = undefined, }); const node_ptr = try block.statements.addOne(); - *node_ptr = &node.base; + node_ptr.* = &node.base; - stack.append(State { .Semicolon = node_ptr }) catch unreachable; - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); + stack.append(State{ .Semicolon = node_ptr }) catch unreachable; + try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); continue; }, Token.Id.LBrace => { - const inner_block = try arena.construct(ast.Node.Block { - .base = ast.Node { .id = ast.Node.Id.Block }, + const inner_block = try arena.construct(ast.Node.Block{ + .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), @@ -1118,16 +1023,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try block.statements.push(&inner_block.base); - stack.append(State { .Block = inner_block }) catch unreachable; + stack.append(State{ .Block = inner_block }) catch unreachable; continue; }, else => { putBackToken(&tok_it, &tree); const statement = try block.statements.addOne(); - try stack.append(State { .Semicolon = statement }); - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); + try stack.append(State{ .Semicolon = statement }); + try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); continue; - } + }, } }, State.ComptimeStatement => |ctx| { @@ -1135,34 +1040,33 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; switch (token_ptr.id) { - Token.Id.Keyword_var, Token.Id.Keyword_const => { - stack.append(State { - .VarDecl = VarDeclCtx { - .comments = null, - .visib_token = null, - .comptime_token = ctx.comptime_token, - .extern_export_token = null, - .lib_name = null, - .mut_token = token_index, - .list = &ctx.block.statements, - } - }) catch unreachable; + Token.Id.Keyword_var, + Token.Id.Keyword_const => { + stack.append(State{ .VarDecl = VarDeclCtx{ + .comments = null, + .visib_token = null, + .comptime_token = ctx.comptime_token, + .extern_export_token = null, + .lib_name = null, + .mut_token = token_index, + .list = &ctx.block.statements, + } }) catch unreachable; continue; }, else => { putBackToken(&tok_it, &tree); putBackToken(&tok_it, &tree); const statement = try ctx.block.statements.addOne(); - try stack.append(State { .Semicolon = statement }); - try stack.append(State { .Expression = OptionalCtx { .Required = statement } }); + try stack.append(State{ .Semicolon = statement }); + try stack.append(State{ .Expression = OptionalCtx{ .Required = statement } }); continue; - } + }, } }, State.Semicolon => |node_ptr| { - const node = *node_ptr; + const node = node_ptr.*; if (node.requireSemiColon()) { - stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; + stack.append(State{ .ExpectToken = Token.Id.Semicolon }) catch unreachable; continue; } continue; @@ -1177,22 +1081,22 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.AsmOutput { - .base = ast.Node {.id = ast.Node.Id.AsmOutput }, + const node = try arena.construct(ast.Node.AsmOutput{ + .base = ast.Node{ .id = ast.Node.Id.AsmOutput }, .symbolic_name = undefined, .constraint = undefined, .kind = undefined, }); try items.push(node); - stack.append(State { .AsmOutputItems = items }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.Comma }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .AsmOutputReturnOrType = node }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); - try stack.append(State { .ExpectToken = Token.Id.RBracket }); - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + stack.append(State{ .AsmOutputItems = items }) catch unreachable; + try stack.append(State{ .IfToken = Token.Id.Comma }); + try stack.append(State{ .ExpectToken = Token.Id.RParen }); + try stack.append(State{ .AsmOutputReturnOrType = node }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); + try stack.append(State{ .StringLiteral = OptionalCtx{ .Required = &node.constraint } }); + try stack.append(State{ .ExpectToken = Token.Id.RBracket }); + try stack.append(State{ .Identifier = OptionalCtx{ .Required = &node.symbolic_name } }); continue; }, State.AsmOutputReturnOrType => |node| { @@ -1201,20 +1105,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Identifier => { - node.kind = ast.Node.AsmOutput.Kind { .Variable = try createLiteral(arena, ast.Node.Identifier, token_index) }; + node.kind = ast.Node.AsmOutput.Kind{ .Variable = try createLiteral(arena, ast.Node.Identifier, token_index) }; continue; }, Token.Id.Arrow => { - node.kind = ast.Node.AsmOutput.Kind { .Return = undefined }; - try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } }); + node.kind = ast.Node.AsmOutput.Kind{ .Return = undefined }; + try stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.kind.Return } }); continue; }, else => { - *(try tree.errors.addOne()) = Error { - .ExpectedAsmOutputReturnOrType = Error.ExpectedAsmOutputReturnOrType { - .token = token_index, - }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedAsmOutputReturnOrType = Error.ExpectedAsmOutputReturnOrType{ .token = token_index } }; return tree; }, } @@ -1228,49 +1128,48 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.AsmInput { - .base = ast.Node {.id = ast.Node.Id.AsmInput }, + const node = try arena.construct(ast.Node.AsmInput{ + .base = ast.Node{ .id = ast.Node.Id.AsmInput }, .symbolic_name = undefined, .constraint = undefined, .expr = undefined, }); try items.push(node); - stack.append(State { .AsmInputItems = items }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.Comma }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); - try stack.append(State { .ExpectToken = Token.Id.RBracket }); - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + stack.append(State{ .AsmInputItems = items }) catch unreachable; + try stack.append(State{ .IfToken = Token.Id.Comma }); + try stack.append(State{ .ExpectToken = Token.Id.RParen }); + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); + try stack.append(State{ .StringLiteral = OptionalCtx{ .Required = &node.constraint } }); + try stack.append(State{ .ExpectToken = Token.Id.RBracket }); + try stack.append(State{ .Identifier = OptionalCtx{ .Required = &node.symbolic_name } }); continue; }, State.AsmClobberItems => |items| { - stack.append(State { .AsmClobberItems = items }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.Comma }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } }); + stack.append(State{ .AsmClobberItems = items }) catch unreachable; + try stack.append(State{ .IfToken = Token.Id.Comma }); + try stack.append(State{ .StringLiteral = OptionalCtx{ .Required = try items.addOne() } }); continue; }, - State.ExprListItemOrEnd => |list_state| { if (eatToken(&tok_it, &tree, list_state.end)) |token_index| { - *list_state.ptr = token_index; + (list_state.ptr).* = token_index; continue; } - stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } }); + stack.append(State{ .ExprListCommaOrEnd = list_state }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .Required = try list_state.list.addOne() } }); continue; }, State.ExprListCommaOrEnd => |list_state| { switch (expectCommaOrEnd(&tok_it, &tree, list_state.end)) { ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { - *list_state.ptr = end; + (list_state.ptr).* = end; continue; } else { - stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable; + stack.append(State{ .ExprListItemOrEnd = list_state }) catch unreachable; continue; }, ExpectCommaOrEndResult.parse_error => |e| { @@ -1285,44 +1184,38 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; + (list_state.ptr).* = rbrace; continue; } - const node = try arena.construct(ast.Node.FieldInitializer { - .base = ast.Node { - .id = ast.Node.Id.FieldInitializer, - }, + const node = try arena.construct(ast.Node.FieldInitializer{ + .base = ast.Node{ .id = ast.Node.Id.FieldInitializer }, .period_token = undefined, .name_token = undefined, .expr = undefined, }); try list_state.list.push(&node.base); - stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } }); - try stack.append(State { .ExpectToken = Token.Id.Equal }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Identifier, - .ptr = &node.name_token, - } - }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Period, - .ptr = &node.period_token, - } - }); + stack.append(State{ .FieldInitListCommaOrEnd = list_state }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); + try stack.append(State{ .ExpectToken = Token.Id.Equal }); + try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Identifier, + .ptr = &node.name_token, + } }); + try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Period, + .ptr = &node.period_token, + } }); continue; }, State.FieldInitListCommaOrEnd => |list_state| { switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) { ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { - *list_state.ptr = end; + (list_state.ptr).* = end; continue; } else { - stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable; + stack.append(State{ .FieldInitListItemOrEnd = list_state }) catch unreachable; continue; }, ExpectCommaOrEndResult.parse_error => |e| { @@ -1337,7 +1230,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { container_decl.rbrace_token = end; continue; } else { - try stack.append(State { .ContainerDecl = container_decl }); + try stack.append(State{ .ContainerDecl = container_decl }); continue; }, ExpectCommaOrEndResult.parse_error => |e| { @@ -1352,23 +1245,23 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; + (list_state.ptr).* = rbrace; continue; } const node_ptr = try list_state.list.addOne(); - try stack.append(State { .ErrorTagListCommaOrEnd = list_state }); - try stack.append(State { .ErrorTag = node_ptr }); + try stack.append(State{ .ErrorTagListCommaOrEnd = list_state }); + try stack.append(State{ .ErrorTag = node_ptr }); continue; }, State.ErrorTagListCommaOrEnd => |list_state| { switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) { ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { - *list_state.ptr = end; + (list_state.ptr).* = end; continue; } else { - stack.append(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable; + stack.append(State{ .ErrorTagListItemOrEnd = list_state }) catch unreachable; continue; }, ExpectCommaOrEndResult.parse_error => |e| { @@ -1383,24 +1276,22 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; + (list_state.ptr).* = rbrace; continue; } const comments = try eatDocComments(arena, &tok_it, &tree); - const node = try arena.construct(ast.Node.SwitchCase { - .base = ast.Node { - .id = ast.Node.Id.SwitchCase, - }, + const node = try arena.construct(ast.Node.SwitchCase{ + .base = ast.Node{ .id = ast.Node.Id.SwitchCase }, .items = ast.Node.SwitchCase.ItemList.init(arena), .payload = null, .expr = undefined, }); try list_state.list.push(&node.base); - try stack.append(State { .SwitchCaseCommaOrEnd = list_state }); - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); - try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.append(State { .SwitchCaseFirstItem = &node.items }); + try stack.append(State{ .SwitchCaseCommaOrEnd = list_state }); + try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); + try stack.append(State{ .PointerPayload = OptionalCtx{ .Optional = &node.payload } }); + try stack.append(State{ .SwitchCaseFirstItem = &node.items }); continue; }, @@ -1408,10 +1299,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.SwitchCaseCommaOrEnd => |list_state| { switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) { ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { - *list_state.ptr = end; + (list_state.ptr).* = end; continue; } else { - try stack.append(State { .SwitchCaseOrEnd = list_state }); + try stack.append(State{ .SwitchCaseOrEnd = list_state }); continue; }, ExpectCommaOrEndResult.parse_error => |e| { @@ -1426,29 +1317,29 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (token_ptr.id == Token.Id.Keyword_else) { - const else_node = try arena.construct(ast.Node.SwitchElse { - .base = ast.Node{ .id = ast.Node.Id.SwitchElse}, + const else_node = try arena.construct(ast.Node.SwitchElse{ + .base = ast.Node{ .id = ast.Node.Id.SwitchElse }, .token = token_index, }); try case_items.push(&else_node.base); - try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); + try stack.append(State{ .ExpectToken = Token.Id.EqualAngleBracketRight }); continue; } else { putBackToken(&tok_it, &tree); - try stack.append(State { .SwitchCaseItem = case_items }); + try stack.append(State{ .SwitchCaseItem = case_items }); continue; } }, State.SwitchCaseItem => |case_items| { - stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; - try stack.append(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } }); + stack.append(State{ .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; + try stack.append(State{ .RangeExpressionBegin = OptionalCtx{ .Required = try case_items.addOne() } }); }, State.SwitchCaseItemCommaOrEnd => |case_items| { switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.EqualAngleBracketRight)) { ExpectCommaOrEndResult.end_token => |t| { if (t == null) { - stack.append(State { .SwitchCaseItem = case_items }) catch unreachable; + stack.append(State{ .SwitchCaseItem = case_items }) catch unreachable; } continue; }, @@ -1460,10 +1351,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, - State.SuspendBody => |suspend_node| { if (suspend_node.payload != null) { - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } }); + try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } }); } continue; }, @@ -1473,13 +1363,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } async_node.rangle_bracket = TokenIndex(0); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.AngleBracketRight, - .ptr = &??async_node.rangle_bracket, - } - }); - try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } }); + try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.AngleBracketRight, + .ptr = &??async_node.rangle_bracket, + } }); + try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &async_node.allocator_type } }); continue; }, State.AsyncEnd => |ctx| { @@ -1498,27 +1386,20 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - *(try tree.errors.addOne()) = Error { - .ExpectedCall = Error.ExpectedCall { .node = node }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedCall = Error.ExpectedCall{ .node = node } }; return tree; }, else => { - *(try tree.errors.addOne()) = Error { - .ExpectedCallOrFnProto = Error.ExpectedCallOrFnProto { .node = node }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedCallOrFnProto = Error.ExpectedCallOrFnProto{ .node = node } }; return tree; - } + }, } }, - State.ExternType => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_fn)) |fn_token| { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - }, + const fn_proto = try arena.construct(ast.Node.FnProto{ + .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = ctx.comments, .visib_token = null, .name_token = null, @@ -1534,17 +1415,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .align_expr = null, }); ctx.opt_ctx.store(&fn_proto.base); - stack.append(State { .FnProto = fn_proto }) catch unreachable; + stack.append(State{ .FnProto = fn_proto }) catch unreachable; continue; } - stack.append(State { - .ContainerKind = ContainerKindCtx { - .opt_ctx = ctx.opt_ctx, - .ltoken = ctx.extern_token, - .layout = ast.Node.ContainerDecl.Layout.Extern, - }, - }) catch unreachable; + stack.append(State{ .ContainerKind = ContainerKindCtx{ + .opt_ctx = ctx.opt_ctx, + .ltoken = ctx.extern_token, + .layout = ast.Node.ContainerDecl.Layout.Extern, + } }) catch unreachable; continue; }, State.SliceOrArrayAccess => |node| { @@ -1554,20 +1433,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { switch (token_ptr.id) { Token.Id.Ellipsis2 => { const start = node.op.ArrayAccess; - node.op = ast.Node.SuffixOp.Op { - .Slice = ast.Node.SuffixOp.Op.Slice { - .start = start, - .end = null, - } - }; + node.op = ast.Node.SuffixOp.Op{ .Slice = ast.Node.SuffixOp.Op.Slice{ + .start = start, + .end = null, + } }; - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RBracket, - .ptr = &node.rtoken, - } - }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } }); + stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.RBracket, + .ptr = &node.rtoken, + } }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .Optional = &node.op.Slice.end } }); continue; }, Token.Id.RBracket => { @@ -1575,33 +1450,29 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, else => { - *(try tree.errors.addOne()) = Error { - .ExpectedSliceOrRBracket = Error.ExpectedSliceOrRBracket { .token = token_index }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedSliceOrRBracket = Error.ExpectedSliceOrRBracket{ .token = token_index } }; return tree; - } + }, } }, State.SliceOrArrayType => |node| { if (eatToken(&tok_it, &tree, Token.Id.RBracket)) |_| { - node.op = ast.Node.PrefixOp.Op { - .SliceType = ast.Node.PrefixOp.AddrOfInfo { - .align_expr = null, - .bit_offset_start_token = null, - .bit_offset_end_token = null, - .const_token = null, - .volatile_token = null, - } - }; - stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - try stack.append(State { .AddrOfModifiers = &node.op.SliceType }); + node.op = ast.Node.PrefixOp.Op{ .SliceType = ast.Node.PrefixOp.AddrOfInfo{ + .align_expr = null, + .bit_offset_start_token = null, + .bit_offset_end_token = null, + .const_token = null, + .volatile_token = null, + } }; + stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; + try stack.append(State{ .AddrOfModifiers = &node.op.SliceType }); continue; } - node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined }; - stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.RBracket }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } }); + node.op = ast.Node.PrefixOp.Op{ .ArrayType = undefined }; + stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; + try stack.append(State{ .ExpectToken = Token.Id.RBracket }); + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.op.ArrayType } }); continue; }, State.AddrOfModifiers => |addr_of_info| { @@ -1612,22 +1483,18 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Keyword_align => { stack.append(state) catch unreachable; if (addr_of_info.align_expr != null) { - *(try tree.errors.addOne()) = Error { - .ExtraAlignQualifier = Error.ExtraAlignQualifier { .token = token_index }, - }; + ((try tree.errors.addOne())).* = Error{ .ExtraAlignQualifier = Error.ExtraAlignQualifier{ .token = token_index } }; return tree; } - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); + try stack.append(State{ .ExpectToken = Token.Id.RParen }); + try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &addr_of_info.align_expr } }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); continue; }, Token.Id.Keyword_const => { stack.append(state) catch unreachable; if (addr_of_info.const_token != null) { - *(try tree.errors.addOne()) = Error { - .ExtraConstQualifier = Error.ExtraConstQualifier { .token = token_index }, - }; + ((try tree.errors.addOne())).* = Error{ .ExtraConstQualifier = Error.ExtraConstQualifier{ .token = token_index } }; return tree; } addr_of_info.const_token = token_index; @@ -1636,9 +1503,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Keyword_volatile => { stack.append(state) catch unreachable; if (addr_of_info.volatile_token != null) { - *(try tree.errors.addOne()) = Error { - .ExtraVolatileQualifier = Error.ExtraVolatileQualifier { .token = token_index }, - }; + ((try tree.errors.addOne())).* = Error{ .ExtraVolatileQualifier = Error.ExtraVolatileQualifier{ .token = token_index } }; return tree; } addr_of_info.volatile_token = token_index; @@ -1651,19 +1516,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } }, - State.Payload => |opt_ctx| { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; if (token_ptr.id != Token.Id.Pipe) { if (opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = token_index, - .expected_id = Token.Id.Pipe, - }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ + .token = token_index, + .expected_id = Token.Id.Pipe, + } }; return tree; } @@ -1671,21 +1533,19 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.Payload { - .base = ast.Node {.id = ast.Node.Id.Payload }, + const node = try arena.construct(ast.Node.Payload{ + .base = ast.Node{ .id = ast.Node.Id.Payload }, .lpipe = token_index, .error_symbol = undefined, - .rpipe = undefined + .rpipe = undefined, }); opt_ctx.store(&node.base); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } - }) catch unreachable; - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } }); + stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } }) catch unreachable; + try stack.append(State{ .Identifier = OptionalCtx{ .Required = &node.error_symbol } }); continue; }, State.PointerPayload => |opt_ctx| { @@ -1694,12 +1554,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; if (token_ptr.id != Token.Id.Pipe) { if (opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = token_index, - .expected_id = Token.Id.Pipe, - }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ + .token = token_index, + .expected_id = Token.Id.Pipe, + } }; return tree; } @@ -1707,28 +1565,24 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.PointerPayload { - .base = ast.Node {.id = ast.Node.Id.PointerPayload }, + const node = try arena.construct(ast.Node.PointerPayload{ + .base = ast.Node{ .id = ast.Node.Id.PointerPayload }, .lpipe = token_index, .ptr_token = null, .value_symbol = undefined, - .rpipe = undefined + .rpipe = undefined, }); opt_ctx.store(&node.base); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } - }); - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); - try stack.append(State { - .OptionalTokenSave = OptionalTokenSave { - .id = Token.Id.Asterisk, - .ptr = &node.ptr_token, - } - }); + try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } }); + try stack.append(State{ .Identifier = OptionalCtx{ .Required = &node.value_symbol } }); + try stack.append(State{ .OptionalTokenSave = OptionalTokenSave{ + .id = Token.Id.Asterisk, + .ptr = &node.ptr_token, + } }); continue; }, State.PointerIndexPayload => |opt_ctx| { @@ -1737,12 +1591,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; if (token_ptr.id != Token.Id.Pipe) { if (opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = token_index, - .expected_id = Token.Id.Pipe, - }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ + .token = token_index, + .expected_id = Token.Id.Pipe, + } }; return tree; } @@ -1750,61 +1602,58 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.PointerIndexPayload { - .base = ast.Node {.id = ast.Node.Id.PointerIndexPayload }, + const node = try arena.construct(ast.Node.PointerIndexPayload{ + .base = ast.Node{ .id = ast.Node.Id.PointerIndexPayload }, .lpipe = token_index, .ptr_token = null, .value_symbol = undefined, .index_symbol = null, - .rpipe = undefined + .rpipe = undefined, }); opt_ctx.store(&node.base); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } - }) catch unreachable; - try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } }); - try stack.append(State { .IfToken = Token.Id.Comma }); - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); - try stack.append(State { - .OptionalTokenSave = OptionalTokenSave { - .id = Token.Id.Asterisk, - .ptr = &node.ptr_token, - } - }); + stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } }) catch unreachable; + try stack.append(State{ .Identifier = OptionalCtx{ .RequiredNull = &node.index_symbol } }); + try stack.append(State{ .IfToken = Token.Id.Comma }); + try stack.append(State{ .Identifier = OptionalCtx{ .Required = &node.value_symbol } }); + try stack.append(State{ .OptionalTokenSave = OptionalTokenSave{ + .id = Token.Id.Asterisk, + .ptr = &node.ptr_token, + } }); continue; }, - State.Expression => |opt_ctx| { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; switch (token_ptr.id) { - Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { - const node = try arena.construct(ast.Node.ControlFlowExpression { - .base = ast.Node {.id = ast.Node.Id.ControlFlowExpression }, + Token.Id.Keyword_return, + Token.Id.Keyword_break, + Token.Id.Keyword_continue => { + const node = try arena.construct(ast.Node.ControlFlowExpression{ + .base = ast.Node{ .id = ast.Node.Id.ControlFlowExpression }, .ltoken = token_index, .kind = undefined, .rhs = null, }); opt_ctx.store(&node.base); - stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable; + stack.append(State{ .Expression = OptionalCtx{ .Optional = &node.rhs } }) catch unreachable; switch (token_ptr.id) { Token.Id.Keyword_break => { - node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null }; - try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } }); - try stack.append(State { .IfToken = Token.Id.Colon }); + node.kind = ast.Node.ControlFlowExpression.Kind{ .Break = null }; + try stack.append(State{ .Identifier = OptionalCtx{ .RequiredNull = &node.kind.Break } }); + try stack.append(State{ .IfToken = Token.Id.Colon }); }, Token.Id.Keyword_continue => { - node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null }; - try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } }); - try stack.append(State { .IfToken = Token.Id.Colon }); + node.kind = ast.Node.ControlFlowExpression.Kind{ .Continue = null }; + try stack.append(State{ .Identifier = OptionalCtx{ .RequiredNull = &node.kind.Continue } }); + try stack.append(State{ .IfToken = Token.Id.Colon }); }, Token.Id.Keyword_return => { node.kind = ast.Node.ControlFlowExpression.Kind.Return; @@ -1813,56 +1662,58 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } continue; }, - Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { - const node = try arena.construct(ast.Node.PrefixOp { - .base = ast.Node {.id = ast.Node.Id.PrefixOp }, + Token.Id.Keyword_try, + Token.Id.Keyword_cancel, + Token.Id.Keyword_resume => { + const node = try arena.construct(ast.Node.PrefixOp{ + .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = switch (token_ptr.id) { - Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} }, - Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} }, - Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} }, + Token.Id.Keyword_try => ast.Node.PrefixOp.Op{ .Try = void{} }, + Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op{ .Cancel = void{} }, + Token.Id.Keyword_resume => ast.Node.PrefixOp.Op{ .Resume = void{} }, else => unreachable, }, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; continue; }, else => { if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) { putBackToken(&tok_it, &tree); - stack.append(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable; + stack.append(State{ .UnwrapExpressionBegin = opt_ctx }) catch unreachable; } continue; - } + }, } }, State.RangeExpressionBegin => |opt_ctx| { - stack.append(State { .RangeExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .Expression = opt_ctx }); + stack.append(State{ .RangeExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .Expression = opt_ctx }); continue; }, State.RangeExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = ellipsis3, .op = ast.Node.InfixOp.Op.Range, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; continue; } }, State.AssignmentExpressionBegin => |opt_ctx| { - stack.append(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .Expression = opt_ctx }); + stack.append(State{ .AssignmentExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .Expression = opt_ctx }); continue; }, @@ -1873,16 +1724,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToAssignment(token_ptr.id)) |ass_id| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = ass_id, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }); continue; } else { putBackToken(&tok_it, &tree); @@ -1891,8 +1742,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.UnwrapExpressionBegin => |opt_ctx| { - stack.append(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BoolOrExpressionBegin = opt_ctx }); + stack.append(State{ .UnwrapExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .BoolOrExpressionBegin = opt_ctx }); continue; }, @@ -1903,8 +1754,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = unwrap_id, @@ -1912,11 +1763,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); opt_ctx.store(&node.base); - stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }); if (node.op == ast.Node.InfixOp.Op.Catch) { - try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } }); + try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.op.Catch } }); } continue; } else { @@ -1926,8 +1777,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.BoolOrExpressionBegin => |opt_ctx| { - stack.append(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BoolAndExpressionBegin = opt_ctx }); + stack.append(State{ .BoolOrExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .BoolAndExpressionBegin = opt_ctx }); continue; }, @@ -1935,23 +1786,23 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = or_token, .op = ast.Node.InfixOp.Op.BoolOr, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .BoolAndExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); continue; } }, State.BoolAndExpressionBegin => |opt_ctx| { - stack.append(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .ComparisonExpressionBegin = opt_ctx }); + stack.append(State{ .BoolAndExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .ComparisonExpressionBegin = opt_ctx }); continue; }, @@ -1959,23 +1810,23 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = and_token, .op = ast.Node.InfixOp.Op.BoolAnd, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .ComparisonExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); continue; } }, State.ComparisonExpressionBegin => |opt_ctx| { - stack.append(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BinaryOrExpressionBegin = opt_ctx }); + stack.append(State{ .ComparisonExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .BinaryOrExpressionBegin = opt_ctx }); continue; }, @@ -1986,16 +1837,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToComparison(token_ptr.id)) |comp_id| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = comp_id, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .BinaryOrExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); continue; } else { putBackToken(&tok_it, &tree); @@ -2004,8 +1855,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.BinaryOrExpressionBegin => |opt_ctx| { - stack.append(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BinaryXorExpressionBegin = opt_ctx }); + stack.append(State{ .BinaryOrExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .BinaryXorExpressionBegin = opt_ctx }); continue; }, @@ -2013,23 +1864,23 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = pipe, .op = ast.Node.InfixOp.Op.BitOr, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .BinaryXorExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); continue; } }, State.BinaryXorExpressionBegin => |opt_ctx| { - stack.append(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BinaryAndExpressionBegin = opt_ctx }); + stack.append(State{ .BinaryXorExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .BinaryAndExpressionBegin = opt_ctx }); continue; }, @@ -2037,23 +1888,23 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = caret, .op = ast.Node.InfixOp.Op.BitXor, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .BinaryAndExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); continue; } }, State.BinaryAndExpressionBegin => |opt_ctx| { - stack.append(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BitShiftExpressionBegin = opt_ctx }); + stack.append(State{ .BinaryAndExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .BitShiftExpressionBegin = opt_ctx }); continue; }, @@ -2061,23 +1912,23 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = ampersand, .op = ast.Node.InfixOp.Op.BitAnd, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .BitShiftExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); continue; } }, State.BitShiftExpressionBegin => |opt_ctx| { - stack.append(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .AdditionExpressionBegin = opt_ctx }); + stack.append(State{ .BitShiftExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .AdditionExpressionBegin = opt_ctx }); continue; }, @@ -2088,16 +1939,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = bitshift_id, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .AdditionExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); continue; } else { putBackToken(&tok_it, &tree); @@ -2106,8 +1957,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.AdditionExpressionBegin => |opt_ctx| { - stack.append(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .MultiplyExpressionBegin = opt_ctx }); + stack.append(State{ .AdditionExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .MultiplyExpressionBegin = opt_ctx }); continue; }, @@ -2118,16 +1969,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToAddition(token_ptr.id)) |add_id| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = add_id, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .MultiplyExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); continue; } else { putBackToken(&tok_it, &tree); @@ -2136,8 +1987,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.MultiplyExpressionBegin => |opt_ctx| { - stack.append(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .CurlySuffixExpressionBegin = opt_ctx }); + stack.append(State{ .MultiplyExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .CurlySuffixExpressionBegin = opt_ctx }); continue; }, @@ -2148,16 +1999,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToMultiply(token_ptr.id)) |mult_id| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = mult_id, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .CurlySuffixExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); continue; } else { putBackToken(&tok_it, &tree); @@ -2166,9 +2017,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.CurlySuffixExpressionBegin => |opt_ctx| { - stack.append(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.LBrace }); - try stack.append(State { .TypeExprBegin = opt_ctx }); + stack.append(State{ .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .IfToken = Token.Id.LBrace }); + try stack.append(State{ .TypeExprBegin = opt_ctx }); continue; }, @@ -2176,51 +2027,43 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if ((??tok_it.peek()).id == Token.Id.Period) { - const node = try arena.construct(ast.Node.SuffixOp { - .base = ast.Node { .id = ast.Node.Id.SuffixOp }, + const node = try arena.construct(ast.Node.SuffixOp{ + .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena), - }, + .op = ast.Node.SuffixOp.Op{ .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena) }, .rtoken = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.LBrace }); - try stack.append(State { - .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)) { - .list = &node.op.StructInitializer, - .ptr = &node.rtoken, - } - }); + stack.append(State{ .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .IfToken = Token.Id.LBrace }); + try stack.append(State{ .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)){ + .list = &node.op.StructInitializer, + .ptr = &node.rtoken, + } }); continue; } - const node = try arena.construct(ast.Node.SuffixOp { - .base = ast.Node {.id = ast.Node.Id.SuffixOp }, + const node = try arena.construct(ast.Node.SuffixOp{ + .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena), - }, + .op = ast.Node.SuffixOp.Op{ .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena) }, .rtoken = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.LBrace }); - try stack.append(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.op.ArrayInitializer, - .end = Token.Id.RBrace, - .ptr = &node.rtoken, - } - }); + stack.append(State{ .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .IfToken = Token.Id.LBrace }); + try stack.append(State{ .ExprListItemOrEnd = ExprListCtx{ + .list = &node.op.ArrayInitializer, + .end = Token.Id.RBrace, + .ptr = &node.rtoken, + } }); continue; }, State.TypeExprBegin => |opt_ctx| { - stack.append(State { .TypeExprEnd = opt_ctx }) catch unreachable; - try stack.append(State { .PrefixOpExpression = opt_ctx }); + stack.append(State{ .TypeExprEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .PrefixOpExpression = opt_ctx }); continue; }, @@ -2228,16 +2071,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() ?? continue; if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = bang, .op = ast.Node.InfixOp.Op.ErrorUnion, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .PrefixOpExpression = OptionalCtx{ .Required = &node.rhs } }); continue; } }, @@ -2247,8 +2090,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| { - var node = try arena.construct(ast.Node.PrefixOp { - .base = ast.Node {.id = ast.Node.Id.PrefixOp }, + var node = try arena.construct(ast.Node.PrefixOp{ + .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = prefix_id, .rhs = undefined, @@ -2257,8 +2100,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { // Treat '**' token as two derefs if (token_ptr.id == Token.Id.AsteriskAsterisk) { - const child = try arena.construct(ast.Node.PrefixOp { - .base = ast.Node {.id = ast.Node.Id.PrefixOp}, + const child = try arena.construct(ast.Node.PrefixOp{ + .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = prefix_id, .rhs = undefined, @@ -2267,40 +2110,38 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { node = child; } - stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; if (node.op == ast.Node.PrefixOp.Op.AddrOf) { - try stack.append(State { .AddrOfModifiers = &node.op.AddrOf }); + try stack.append(State{ .AddrOfModifiers = &node.op.AddrOf }); } continue; } else { putBackToken(&tok_it, &tree); - stack.append(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable; + stack.append(State{ .SuffixOpExpressionBegin = opt_ctx }) catch unreachable; continue; } }, State.SuffixOpExpressionBegin => |opt_ctx| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_async)) |async_token| { - const async_node = try arena.construct(ast.Node.AsyncAttribute { - .base = ast.Node {.id = ast.Node.Id.AsyncAttribute}, + const async_node = try arena.construct(ast.Node.AsyncAttribute{ + .base = ast.Node{ .id = ast.Node.Id.AsyncAttribute }, .async_token = async_token, .allocator_type = null, .rangle_bracket = null, }); - stack.append(State { - .AsyncEnd = AsyncEndCtx { - .ctx = opt_ctx, - .attribute = async_node, - } - }) catch unreachable; - try stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }); - try stack.append(State { .PrimaryExpression = opt_ctx.toRequired() }); - try stack.append(State { .AsyncAllocator = async_node }); + stack.append(State{ .AsyncEnd = AsyncEndCtx{ + .ctx = opt_ctx, + .attribute = async_node, + } }) catch unreachable; + try stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }); + try stack.append(State{ .PrimaryExpression = opt_ctx.toRequired() }); + try stack.append(State{ .AsyncAllocator = async_node }); continue; } - stack.append(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .PrimaryExpression = opt_ctx }); + stack.append(State{ .SuffixOpExpressionEnd = opt_ctx }) catch unreachable; + try stack.append(State{ .PrimaryExpression = opt_ctx }); continue; }, @@ -2312,48 +2153,42 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LParen => { - const node = try arena.construct(ast.Node.SuffixOp { - .base = ast.Node {.id = ast.Node.Id.SuffixOp }, + const node = try arena.construct(ast.Node.SuffixOp{ + .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .Call = ast.Node.SuffixOp.Op.Call { - .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena), - .async_attr = null, - } - }, + .op = ast.Node.SuffixOp.Op{ .Call = ast.Node.SuffixOp.Op.Call{ + .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena), + .async_attr = null, + } }, .rtoken = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.op.Call.params, - .end = Token.Id.RParen, - .ptr = &node.rtoken, - } - }); + stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .ExprListItemOrEnd = ExprListCtx{ + .list = &node.op.Call.params, + .end = Token.Id.RParen, + .ptr = &node.rtoken, + } }); continue; }, Token.Id.LBracket => { - const node = try arena.construct(ast.Node.SuffixOp { - .base = ast.Node {.id = ast.Node.Id.SuffixOp }, + const node = try arena.construct(ast.Node.SuffixOp{ + .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .ArrayAccess = undefined, - }, - .rtoken = undefined + .op = ast.Node.SuffixOp.Op{ .ArrayAccess = undefined }, + .rtoken = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .SliceOrArrayAccess = node }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }}); + stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .SliceOrArrayAccess = node }); + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.op.ArrayAccess } }); continue; }, Token.Id.Period => { - const node = try arena.construct(ast.Node.InfixOp { - .base = ast.Node {.id = ast.Node.Id.InfixOp }, + const node = try arena.construct(ast.Node.InfixOp{ + .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = ast.Node.InfixOp.Op.Period, @@ -2361,8 +2196,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); opt_ctx.store(&node.base); - stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.rhs } }); + stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.append(State{ .Identifier = OptionalCtx{ .Required = &node.rhs } }); continue; }, else => { @@ -2391,7 +2226,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token.index); continue; }, - Token.Id.Keyword_true, Token.Id.Keyword_false => { + Token.Id.Keyword_true, + Token.Id.Keyword_false => { _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token.index); continue; }, @@ -2412,10 +2248,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_promise => { - const node = try arena.construct(ast.Node.PromiseType { - .base = ast.Node { - .id = ast.Node.Id.PromiseType, - }, + const node = try arena.construct(ast.Node.PromiseType{ + .base = ast.Node{ .id = ast.Node.Id.PromiseType }, .promise_token = token.index, .result = null, }); @@ -2427,121 +2261,108 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { putBackToken(&tok_it, &tree); continue; } - node.result = ast.Node.PromiseType.Result { + node.result = ast.Node.PromiseType.Result{ .arrow_token = next_token_index, .return_type = undefined, }; const return_type_ptr = &((??node.result).return_type); - try stack.append(State { .Expression = OptionalCtx { .Required = return_type_ptr, } }); + try stack.append(State{ .Expression = OptionalCtx{ .Required = return_type_ptr } }); continue; }, - Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { + Token.Id.StringLiteral, + Token.Id.MultilineStringLiteralLine => { opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) ?? unreachable); continue; }, Token.Id.LParen => { - const node = try arena.construct(ast.Node.GroupedExpression { - .base = ast.Node {.id = ast.Node.Id.GroupedExpression }, + const node = try arena.construct(ast.Node.GroupedExpression{ + .base = ast.Node{ .id = ast.Node.Id.GroupedExpression }, .lparen = token.index, .expr = undefined, .rparen = undefined, }); opt_ctx.store(&node.base); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RParen, - .ptr = &node.rparen, - } - }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); + stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.RParen, + .ptr = &node.rparen, + } }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); continue; }, Token.Id.Builtin => { - const node = try arena.construct(ast.Node.BuiltinCall { - .base = ast.Node {.id = ast.Node.Id.BuiltinCall }, + const node = try arena.construct(ast.Node.BuiltinCall{ + .base = ast.Node{ .id = ast.Node.Id.BuiltinCall }, .builtin_token = token.index, .params = ast.Node.BuiltinCall.ParamList.init(arena), .rparen_token = undefined, }); opt_ctx.store(&node.base); - stack.append(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.params, - .end = Token.Id.RParen, - .ptr = &node.rparen_token, - } - }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.LParen, }); + stack.append(State{ .ExprListItemOrEnd = ExprListCtx{ + .list = &node.params, + .end = Token.Id.RParen, + .ptr = &node.rparen_token, + } }) catch unreachable; + try stack.append(State{ .ExpectToken = Token.Id.LParen }); continue; }, Token.Id.LBracket => { - const node = try arena.construct(ast.Node.PrefixOp { - .base = ast.Node {.id = ast.Node.Id.PrefixOp }, + const node = try arena.construct(ast.Node.PrefixOp{ + .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token.index, .op = undefined, .rhs = undefined, }); opt_ctx.store(&node.base); - stack.append(State { .SliceOrArrayType = node }) catch unreachable; + stack.append(State{ .SliceOrArrayType = node }) catch unreachable; continue; }, Token.Id.Keyword_error => { - stack.append(State { - .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx { - .error_token = token.index, - .opt_ctx = opt_ctx - } - }) catch unreachable; + stack.append(State{ .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx{ + .error_token = token.index, + .opt_ctx = opt_ctx, + } }) catch unreachable; continue; }, Token.Id.Keyword_packed => { - stack.append(State { - .ContainerKind = ContainerKindCtx { - .opt_ctx = opt_ctx, - .ltoken = token.index, - .layout = ast.Node.ContainerDecl.Layout.Packed, - }, - }) catch unreachable; + stack.append(State{ .ContainerKind = ContainerKindCtx{ + .opt_ctx = opt_ctx, + .ltoken = token.index, + .layout = ast.Node.ContainerDecl.Layout.Packed, + } }) catch unreachable; continue; }, Token.Id.Keyword_extern => { - stack.append(State { - .ExternType = ExternTypeCtx { - .opt_ctx = opt_ctx, - .extern_token = token.index, - .comments = null, - }, - }) catch unreachable; + stack.append(State{ .ExternType = ExternTypeCtx{ + .opt_ctx = opt_ctx, + .extern_token = token.index, + .comments = null, + } }) catch unreachable; continue; }, - Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => { + Token.Id.Keyword_struct, + Token.Id.Keyword_union, + Token.Id.Keyword_enum => { putBackToken(&tok_it, &tree); - stack.append(State { - .ContainerKind = ContainerKindCtx { - .opt_ctx = opt_ctx, - .ltoken = token.index, - .layout = ast.Node.ContainerDecl.Layout.Auto, - }, - }) catch unreachable; + stack.append(State{ .ContainerKind = ContainerKindCtx{ + .opt_ctx = opt_ctx, + .ltoken = token.index, + .layout = ast.Node.ContainerDecl.Layout.Auto, + } }) catch unreachable; continue; }, Token.Id.Identifier => { - stack.append(State { - .MaybeLabeledExpression = MaybeLabeledExpressionCtx { - .label = token.index, - .opt_ctx = opt_ctx - } - }) catch unreachable; + stack.append(State{ .MaybeLabeledExpression = MaybeLabeledExpressionCtx{ + .label = token.index, + .opt_ctx = opt_ctx, + } }) catch unreachable; continue; }, Token.Id.Keyword_fn => { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - }, + const fn_proto = try arena.construct(ast.Node.FnProto{ + .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = null, .visib_token = null, .name_token = null, @@ -2557,14 +2378,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .align_expr = null, }); opt_ctx.store(&fn_proto.base); - stack.append(State { .FnProto = fn_proto }) catch unreachable; + stack.append(State{ .FnProto = fn_proto }) catch unreachable; continue; }, - Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - }, + Token.Id.Keyword_nakedcc, + Token.Id.Keyword_stdcallcc => { + const fn_proto = try arena.construct(ast.Node.FnProto{ + .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = null, .visib_token = null, .name_token = null, @@ -2580,18 +2400,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .align_expr = null, }); opt_ctx.store(&fn_proto.base); - stack.append(State { .FnProto = fn_proto }) catch unreachable; - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token - } - }); + stack.append(State{ .FnProto = fn_proto }) catch unreachable; + try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + } }); continue; }, Token.Id.Keyword_asm => { - const node = try arena.construct(ast.Node.Asm { - .base = ast.Node {.id = ast.Node.Id.Asm }, + const node = try arena.construct(ast.Node.Asm{ + .base = ast.Node{ .id = ast.Node.Id.Asm }, .asm_token = token.index, .volatile_token = null, .template = undefined, @@ -2602,94 +2420,77 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); opt_ctx.store(&node.base); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RParen, - .ptr = &node.rparen, - } - }) catch unreachable; - try stack.append(State { .AsmClobberItems = &node.clobbers }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .AsmInputItems = &node.inputs }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .AsmOutputItems = &node.outputs }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.template } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - try stack.append(State { - .OptionalTokenSave = OptionalTokenSave { - .id = Token.Id.Keyword_volatile, - .ptr = &node.volatile_token, - } - }); + stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.RParen, + .ptr = &node.rparen, + } }) catch unreachable; + try stack.append(State{ .AsmClobberItems = &node.clobbers }); + try stack.append(State{ .IfToken = Token.Id.Colon }); + try stack.append(State{ .AsmInputItems = &node.inputs }); + try stack.append(State{ .IfToken = Token.Id.Colon }); + try stack.append(State{ .AsmOutputItems = &node.outputs }); + try stack.append(State{ .IfToken = Token.Id.Colon }); + try stack.append(State{ .StringLiteral = OptionalCtx{ .Required = &node.template } }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); + try stack.append(State{ .OptionalTokenSave = OptionalTokenSave{ + .id = Token.Id.Keyword_volatile, + .ptr = &node.volatile_token, + } }); }, Token.Id.Keyword_inline => { - stack.append(State { - .Inline = InlineCtx { - .label = null, - .inline_token = token.index, - .opt_ctx = opt_ctx, - } - }) catch unreachable; + stack.append(State{ .Inline = InlineCtx{ + .label = null, + .inline_token = token.index, + .opt_ctx = opt_ctx, + } }) catch unreachable; continue; }, else => { if (!try parseBlockExpr(&stack, arena, opt_ctx, token.ptr, token.index)) { putBackToken(&tok_it, &tree); if (opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token.index }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr{ .token = token.index } }; return tree; } } continue; - } + }, } }, - State.ErrorTypeOrSetDecl => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.LBrace) == null) { _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token); continue; } - const node = try arena.construct(ast.Node.ErrorSetDecl { - .base = ast.Node { - .id = ast.Node.Id.ErrorSetDecl, - }, + const node = try arena.construct(ast.Node.ErrorSetDecl{ + .base = ast.Node{ .id = ast.Node.Id.ErrorSetDecl }, .error_token = ctx.error_token, .decls = ast.Node.ErrorSetDecl.DeclList.init(arena), .rbrace_token = undefined, }); ctx.opt_ctx.store(&node.base); - stack.append(State { - .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)) { - .list = &node.decls, - .ptr = &node.rbrace_token, - } - }) catch unreachable; + stack.append(State{ .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)){ + .list = &node.decls, + .ptr = &node.rbrace_token, + } }) catch unreachable; continue; }, State.StringLiteral => |opt_ctx| { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; - opt_ctx.store( - (try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) ?? { - putBackToken(&tok_it, &tree); - if (opt_ctx != OptionalCtx.Optional) { - *(try tree.errors.addOne()) = Error { - .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index }, - }; - return tree; - } - - continue; + opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) ?? { + putBackToken(&tok_it, &tree); + if (opt_ctx != OptionalCtx.Optional) { + ((try tree.errors.addOne())).* = Error{ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr{ .token = token_index } }; + return tree; } - ); + + continue; + }); }, State.Identifier => |opt_ctx| { @@ -2702,12 +2503,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = token_index, - .expected_id = Token.Id.Identifier, - }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ + .token = token_index, + .expected_id = Token.Id.Identifier, + } }; return tree; } }, @@ -2718,23 +2517,19 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const ident_token_index = ident_token.index; const ident_token_ptr = ident_token.ptr; if (ident_token_ptr.id != Token.Id.Identifier) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = ident_token_index, - .expected_id = Token.Id.Identifier, - }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ + .token = ident_token_index, + .expected_id = Token.Id.Identifier, + } }; return tree; } - const node = try arena.construct(ast.Node.ErrorTag { - .base = ast.Node { - .id = ast.Node.Id.ErrorTag, - }, + const node = try arena.construct(ast.Node.ErrorTag{ + .base = ast.Node{ .id = ast.Node.Id.ErrorTag }, .doc_comments = comments, .name_token = ident_token_index, }); - *node_ptr = &node.base; + node_ptr.* = &node.base; continue; }, @@ -2743,12 +2538,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (token_ptr.id != token_id) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = token_index, - .expected_id = token_id, - }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ + .token = token_index, + .expected_id = token_id, + } }; return tree; } continue; @@ -2758,15 +2551,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (token_ptr.id != expect_token_save.id) { - *(try tree.errors.addOne()) = Error { - .ExpectedToken = Error.ExpectedToken { - .token = token_index, - .expected_id = expect_token_save.id, - }, - }; + ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ + .token = token_index, + .expected_id = expect_token_save.id, + } }; return tree; } - *expect_token_save.ptr = token_index; + (expect_token_save.ptr).* = token_index; continue; }, State.IfToken => |token_id| { @@ -2779,7 +2570,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.IfTokenSave => |if_token_save| { if (eatToken(&tok_it, &tree, if_token_save.id)) |token_index| { - *if_token_save.ptr = token_index; + (if_token_save.ptr).* = token_index; continue; } @@ -2788,7 +2579,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.OptionalTokenSave => |optional_token_save| { if (eatToken(&tok_it, &tree, optional_token_save.id)) |token_index| { - *optional_token_save.ptr = token_index; + (optional_token_save.ptr).* = token_index; continue; } @@ -2911,28 +2702,28 @@ const OptionalCtx = union(enum) { Required: &&ast.Node, pub fn store(self: &const OptionalCtx, value: &ast.Node) void { - switch (*self) { - OptionalCtx.Optional => |ptr| *ptr = value, - OptionalCtx.RequiredNull => |ptr| *ptr = value, - OptionalCtx.Required => |ptr| *ptr = value, + switch (self.*) { + OptionalCtx.Optional => |ptr| ptr.* = value, + OptionalCtx.RequiredNull => |ptr| ptr.* = value, + OptionalCtx.Required => |ptr| ptr.* = value, } } pub fn get(self: &const OptionalCtx) ?&ast.Node { - switch (*self) { - OptionalCtx.Optional => |ptr| return *ptr, - OptionalCtx.RequiredNull => |ptr| return ??*ptr, - OptionalCtx.Required => |ptr| return *ptr, + switch (self.*) { + OptionalCtx.Optional => |ptr| return ptr.*, + OptionalCtx.RequiredNull => |ptr| return ??ptr.*, + OptionalCtx.Required => |ptr| return ptr.*, } } pub fn toRequired(self: &const OptionalCtx) OptionalCtx { - switch (*self) { + switch (self.*) { OptionalCtx.Optional => |ptr| { - return OptionalCtx { .RequiredNull = ptr }; + return OptionalCtx{ .RequiredNull = ptr }; }, - OptionalCtx.RequiredNull => |ptr| return *self, - OptionalCtx.Required => |ptr| return *self, + OptionalCtx.RequiredNull => |ptr| return self.*, + OptionalCtx.Required => |ptr| return self.*, } } }; @@ -3054,7 +2845,6 @@ const State = union(enum) { Identifier: OptionalCtx, ErrorTag: &&ast.Node, - IfToken: @TagType(Token.Id), IfTokenSave: ExpectTokenSave, ExpectToken: @TagType(Token.Id), @@ -3064,16 +2854,14 @@ const State = union(enum) { fn pushDocComment(arena: &mem.Allocator, line_comment: TokenIndex, result: &?&ast.Node.DocComment) !void { const node = blk: { - if (*result) |comment_node| { + if (result.*) |comment_node| { break :blk comment_node; } else { - const comment_node = try arena.construct(ast.Node.DocComment { - .base = ast.Node { - .id = ast.Node.Id.DocComment, - }, + const comment_node = try arena.construct(ast.Node.DocComment{ + .base = ast.Node{ .id = ast.Node.Id.DocComment }, .lines = ast.Node.DocComment.LineList.init(arena), }); - *result = comment_node; + result.* = comment_node; break :blk comment_node; } }; @@ -3094,24 +2882,20 @@ fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, t fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) !?&ast.Node.LineComment { const token = eatToken(tok_it, tree, Token.Id.LineComment) ?? return null; - return try arena.construct(ast.Node.LineComment { - .base = ast.Node { - .id = ast.Node.Id.LineComment, - }, + return try arena.construct(ast.Node.LineComment{ + .base = ast.Node{ .id = ast.Node.Id.LineComment }, .token = token, }); } -fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, - token_ptr: &const Token, token_index: TokenIndex, tree: &ast.Tree) !?&ast.Node -{ +fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, token_ptr: &const Token, token_index: TokenIndex, tree: &ast.Tree) !?&ast.Node { switch (token_ptr.id) { Token.Id.StringLiteral => { return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base; }, Token.Id.MultilineStringLiteralLine => { - const node = try arena.construct(ast.Node.MultilineStringLiteral { - .base = ast.Node { .id = ast.Node.Id.MultilineStringLiteral }, + const node = try arena.construct(ast.Node.MultilineStringLiteral{ + .base = ast.Node{ .id = ast.Node.Id.MultilineStringLiteral }, .lines = ast.Node.MultilineStringLiteral.LineList.init(arena), }); try node.lines.push(token_index); @@ -3135,12 +2919,11 @@ fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterato } } -fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx, - token_ptr: &const Token, token_index: TokenIndex) !bool { +fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx, token_ptr: &const Token, token_index: TokenIndex) !bool { switch (token_ptr.id) { Token.Id.Keyword_suspend => { - const node = try arena.construct(ast.Node.Suspend { - .base = ast.Node {.id = ast.Node.Id.Suspend }, + const node = try arena.construct(ast.Node.Suspend{ + .base = ast.Node{ .id = ast.Node.Id.Suspend }, .label = null, .suspend_token = token_index, .payload = null, @@ -3148,13 +2931,13 @@ fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &con }); ctx.store(&node.base); - stack.append(State { .SuspendBody = node }) catch unreachable; - try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + stack.append(State{ .SuspendBody = node }) catch unreachable; + try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); return true; }, Token.Id.Keyword_if => { - const node = try arena.construct(ast.Node.If { - .base = ast.Node {.id = ast.Node.Id.If }, + const node = try arena.construct(ast.Node.If{ + .base = ast.Node{ .id = ast.Node.Id.If }, .if_token = token_index, .condition = undefined, .payload = null, @@ -3163,41 +2946,35 @@ fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &con }); ctx.store(&node.base); - stack.append(State { .Else = &node.@"else" }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); + stack.append(State{ .Else = &node.@"else" }) catch unreachable; + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }); + try stack.append(State{ .PointerPayload = OptionalCtx{ .Optional = &node.payload } }); + try stack.append(State{ .ExpectToken = Token.Id.RParen }); + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.condition } }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); return true; }, Token.Id.Keyword_while => { - stack.append(State { - .While = LoopCtx { - .label = null, - .inline_token = null, - .loop_token = token_index, - .opt_ctx = *ctx, - } - }) catch unreachable; + stack.append(State{ .While = LoopCtx{ + .label = null, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.*, + } }) catch unreachable; return true; }, Token.Id.Keyword_for => { - stack.append(State { - .For = LoopCtx { - .label = null, - .inline_token = null, - .loop_token = token_index, - .opt_ctx = *ctx, - } - }) catch unreachable; + stack.append(State{ .For = LoopCtx{ + .label = null, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.*, + } }) catch unreachable; return true; }, Token.Id.Keyword_switch => { - const node = try arena.construct(ast.Node.Switch { - .base = ast.Node { - .id = ast.Node.Id.Switch, - }, + const node = try arena.construct(ast.Node.Switch{ + .base = ast.Node{ .id = ast.Node.Id.Switch }, .switch_token = token_index, .expr = undefined, .cases = ast.Node.Switch.CaseList.init(arena), @@ -3205,45 +2982,43 @@ fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &con }); ctx.store(&node.base); - stack.append(State { - .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)) { - .list = &node.cases, - .ptr = &node.rbrace, - }, - }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.LBrace }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); + stack.append(State{ .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)){ + .list = &node.cases, + .ptr = &node.rbrace, + } }) catch unreachable; + try stack.append(State{ .ExpectToken = Token.Id.LBrace }); + try stack.append(State{ .ExpectToken = Token.Id.RParen }); + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); return true; }, Token.Id.Keyword_comptime => { - const node = try arena.construct(ast.Node.Comptime { - .base = ast.Node {.id = ast.Node.Id.Comptime }, + const node = try arena.construct(ast.Node.Comptime{ + .base = ast.Node{ .id = ast.Node.Id.Comptime }, .comptime_token = token_index, .expr = undefined, .doc_comments = null, }); ctx.store(&node.base); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); return true; }, Token.Id.LBrace => { - const block = try arena.construct(ast.Node.Block { - .base = ast.Node {.id = ast.Node.Id.Block }, + const block = try arena.construct(ast.Node.Block{ + .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, }); ctx.store(&block.base); - stack.append(State { .Block = block }) catch unreachable; + stack.append(State{ .Block = block }) catch unreachable; return true; }, else => { return false; - } + }, } } @@ -3257,20 +3032,16 @@ fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, end: const token_index = token.index; const token_ptr = token.ptr; switch (token_ptr.id) { - Token.Id.Comma => return ExpectCommaOrEndResult { .end_token = null}, + Token.Id.Comma => return ExpectCommaOrEndResult{ .end_token = null }, else => { if (end == token_ptr.id) { - return ExpectCommaOrEndResult { .end_token = token_index }; + return ExpectCommaOrEndResult{ .end_token = token_index }; } - return ExpectCommaOrEndResult { - .parse_error = Error { - .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd { - .token = token_index, - .end_id = end, - }, - }, - }; + return ExpectCommaOrEndResult{ .parse_error = Error{ .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd{ + .token = token_index, + .end_id = end, + } } }; }, } } @@ -3278,103 +3049,102 @@ fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, end: fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op { // TODO: We have to cast all cases because of this: // error: expected type '?InfixOp', found '?@TagType(InfixOp)' - return switch (*id) { - Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = {} }, - Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = {} }, - Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = {} }, - Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = {} }, - Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = {} }, - Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = {} }, - Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = {} }, - Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = {} }, - Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = {} }, - Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = {} }, - Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = {} }, - Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = {} }, - Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = {} }, - Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = {} }, + return switch (id.*) { + Token.Id.AmpersandEqual => ast.Node.InfixOp.Op{ .AssignBitAnd = {} }, + Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op{ .AssignBitShiftLeft = {} }, + Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op{ .AssignBitShiftRight = {} }, + Token.Id.AsteriskEqual => ast.Node.InfixOp.Op{ .AssignTimes = {} }, + Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op{ .AssignTimesWarp = {} }, + Token.Id.CaretEqual => ast.Node.InfixOp.Op{ .AssignBitXor = {} }, + Token.Id.Equal => ast.Node.InfixOp.Op{ .Assign = {} }, + Token.Id.MinusEqual => ast.Node.InfixOp.Op{ .AssignMinus = {} }, + Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op{ .AssignMinusWrap = {} }, + Token.Id.PercentEqual => ast.Node.InfixOp.Op{ .AssignMod = {} }, + Token.Id.PipeEqual => ast.Node.InfixOp.Op{ .AssignBitOr = {} }, + Token.Id.PlusEqual => ast.Node.InfixOp.Op{ .AssignPlus = {} }, + Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op{ .AssignPlusWrap = {} }, + Token.Id.SlashEqual => ast.Node.InfixOp.Op{ .AssignDiv = {} }, else => null, }; } fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null }, - Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} }, + Token.Id.Keyword_catch => ast.Node.InfixOp.Op{ .Catch = null }, + Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op{ .UnwrapMaybe = void{} }, else => null, }; } fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} }, - Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} }, - Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} }, - Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} }, - Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} }, - Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} }, + Token.Id.BangEqual => ast.Node.InfixOp.Op{ .BangEqual = void{} }, + Token.Id.EqualEqual => ast.Node.InfixOp.Op{ .EqualEqual = void{} }, + Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op{ .LessThan = void{} }, + Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op{ .LessOrEqual = void{} }, + Token.Id.AngleBracketRight => ast.Node.InfixOp.Op{ .GreaterThan = void{} }, + Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op{ .GreaterOrEqual = void{} }, else => null, }; } fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} }, - Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} }, + Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op{ .BitShiftLeft = void{} }, + Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op{ .BitShiftRight = void{} }, else => null, }; } fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} }, - Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} }, - Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} }, - Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} }, - Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} }, + Token.Id.Minus => ast.Node.InfixOp.Op{ .Sub = void{} }, + Token.Id.MinusPercent => ast.Node.InfixOp.Op{ .SubWrap = void{} }, + Token.Id.Plus => ast.Node.InfixOp.Op{ .Add = void{} }, + Token.Id.PlusPercent => ast.Node.InfixOp.Op{ .AddWrap = void{} }, + Token.Id.PlusPlus => ast.Node.InfixOp.Op{ .ArrayCat = void{} }, else => null, }; } fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} }, - Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} }, - Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} }, - Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} }, - Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} }, - Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} }, + Token.Id.Slash => ast.Node.InfixOp.Op{ .Div = void{} }, + Token.Id.Asterisk => ast.Node.InfixOp.Op{ .Mult = void{} }, + Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op{ .ArrayMult = void{} }, + Token.Id.AsteriskPercent => ast.Node.InfixOp.Op{ .MultWrap = void{} }, + Token.Id.Percent => ast.Node.InfixOp.Op{ .Mod = void{} }, + Token.Id.PipePipe => ast.Node.InfixOp.Op{ .MergeErrorSets = void{} }, else => null, }; } fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { return switch (id) { - Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} }, - Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} }, - Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} }, - Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} }, - Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} }, - Token.Id.Ampersand => ast.Node.PrefixOp.Op { - .AddrOf = ast.Node.PrefixOp.AddrOfInfo { - .align_expr = null, - .bit_offset_start_token = null, - .bit_offset_end_token = null, - .const_token = null, - .volatile_token = null, - }, - }, - Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} }, - Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} }, - Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} }, - Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } }, + Token.Id.Bang => ast.Node.PrefixOp.Op{ .BoolNot = void{} }, + Token.Id.Tilde => ast.Node.PrefixOp.Op{ .BitNot = void{} }, + Token.Id.Minus => ast.Node.PrefixOp.Op{ .Negation = void{} }, + Token.Id.MinusPercent => ast.Node.PrefixOp.Op{ .NegationWrap = void{} }, + Token.Id.Asterisk, + Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op{ .Deref = void{} }, + Token.Id.Ampersand => ast.Node.PrefixOp.Op{ .AddrOf = ast.Node.PrefixOp.AddrOfInfo{ + .align_expr = null, + .bit_offset_start_token = null, + .bit_offset_end_token = null, + .const_token = null, + .volatile_token = null, + } }, + Token.Id.QuestionMark => ast.Node.PrefixOp.Op{ .MaybeType = void{} }, + Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op{ .UnwrapMaybe = void{} }, + Token.Id.Keyword_await => ast.Node.PrefixOp.Op{ .Await = void{} }, + Token.Id.Keyword_try => ast.Node.PrefixOp.Op{ .Try = void{} }, else => null, }; } fn createLiteral(arena: &mem.Allocator, comptime T: type, token_index: TokenIndex) !&T { - return arena.construct(T { - .base = ast.Node {.id = ast.Node.typeToId(T)}, + return arena.construct(T{ + .base = ast.Node{ .id = ast.Node.typeToId(T) }, .token = token_index, }); } @@ -3389,15 +3159,14 @@ fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, compti fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, id: @TagType(Token.Id)) ?TokenIndex { const token = nextToken(tok_it, tree); - if (token.ptr.id == id) - return token.index; + if (token.ptr.id == id) return token.index; putBackToken(tok_it, tree); return null; } fn nextToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) AnnotatedToken { - const result = AnnotatedToken { + const result = AnnotatedToken{ .index = tok_it.index, .ptr = ??tok_it.next(), }; diff --git a/std/zig/render.zig b/std/zig/render.zig index 24a6566b56..52bc074d5d 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -7,7 +7,7 @@ const Token = std.zig.Token; const indent_delta = 4; -pub const Error = error { +pub const Error = error{ /// Ran out of memory allocating call stack frames to complete rendering. OutOfMemory, }; @@ -17,9 +17,9 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf( var it = tree.root_node.decls.iterator(0); while (it.next()) |decl| { - try renderTopLevelDecl(allocator, stream, tree, 0, *decl); + try renderTopLevelDecl(allocator, stream, tree, 0, decl.*); if (it.peek()) |next_decl| { - const n = if (nodeLineOffset(tree, *decl, *next_decl) >= 2) u8(2) else u8(1); + const n = if (nodeLineOffset(tree, decl.*, next_decl.*) >= 2) u8(2) else u8(1); try stream.writeByteNTimes('\n', n); } } @@ -154,10 +154,10 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind var it = block.statements.iterator(0); while (it.next()) |statement| { try stream.writeByteNTimes(' ', block_indent); - try renderStatement(allocator, stream, tree, block_indent, *statement); + try renderStatement(allocator, stream, tree, block_indent, statement.*); if (it.peek()) |next_statement| { - const n = if (nodeLineOffset(tree, *statement, *next_statement) >= 2) u8(2) else u8(1); + const n = if (nodeLineOffset(tree, statement.*, next_statement.*) >= 2) u8(2) else u8(1); try stream.writeByteNTimes('\n', n); } } @@ -203,7 +203,6 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try stream.write(" "); try renderExpression(allocator, stream, tree, indent, body); } - }, ast.Node.Id.InfixOp => { @@ -335,7 +334,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind var it = call_info.params.iterator(0); while (it.next()) |param_node| { - try renderExpression(allocator, stream, tree, indent, *param_node); + try renderExpression(allocator, stream, tree, indent, param_node.*); if (it.peek() != null) { try stream.write(", "); } @@ -351,7 +350,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try stream.write("]"); }, - ast.Node.SuffixOp.Op.SuffixOp { + ast.Node.SuffixOp.Op.SuffixOp => { try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); try stream.write(".*"); }, @@ -375,7 +374,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind } if (field_inits.len == 1) { - const field_init = *field_inits.at(0); + const field_init = field_inits.at(0).*; try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); try stream.write("{ "); @@ -392,12 +391,12 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind var it = field_inits.iterator(0); while (it.next()) |field_init| { try stream.writeByteNTimes(' ', new_indent); - try renderExpression(allocator, stream, tree, new_indent, *field_init); - if ((*field_init).id != ast.Node.Id.LineComment) { + try renderExpression(allocator, stream, tree, new_indent, field_init.*); + if ((field_init.*).id != ast.Node.Id.LineComment) { try stream.write(","); } if (it.peek()) |next_field_init| { - const n = if (nodeLineOffset(tree, *field_init, *next_field_init) >= 2) u8(2) else u8(1); + const n = if (nodeLineOffset(tree, field_init.*, next_field_init.*) >= 2) u8(2) else u8(1); try stream.writeByteNTimes('\n', n); } } @@ -408,14 +407,13 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind }, ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| { - if (exprs.len == 0) { try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); try stream.write("{}"); return; } if (exprs.len == 1) { - const expr = *exprs.at(0); + const expr = exprs.at(0).*; try renderExpression(allocator, stream, tree, indent, suffix_op.lhs); try stream.write("{"); @@ -432,11 +430,11 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind var it = exprs.iterator(0); while (it.next()) |expr| { try stream.writeByteNTimes(' ', new_indent); - try renderExpression(allocator, stream, tree, new_indent, *expr); + try renderExpression(allocator, stream, tree, new_indent, expr.*); try stream.write(","); if (it.peek()) |next_expr| { - const n = if (nodeLineOffset(tree, *expr, *next_expr) >= 2) u8(2) else u8(1); + const n = if (nodeLineOffset(tree, expr.*, next_expr.*) >= 2) u8(2) else u8(1); try stream.writeByteNTimes('\n', n); } } @@ -469,7 +467,6 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind ast.Node.ControlFlowExpression.Kind.Return => { try stream.print("return"); }, - } if (flow_expr.rhs) |rhs| { @@ -575,7 +572,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind switch (container_decl.layout) { ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "), ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "), - ast.Node.ContainerDecl.Layout.Auto => { }, + ast.Node.ContainerDecl.Layout.Auto => {}, } switch (container_decl.kind) { @@ -611,10 +608,10 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind var it = container_decl.fields_and_decls.iterator(0); while (it.next()) |decl| { try stream.writeByteNTimes(' ', new_indent); - try renderTopLevelDecl(allocator, stream, tree, new_indent, *decl); + try renderTopLevelDecl(allocator, stream, tree, new_indent, decl.*); if (it.peek()) |next_decl| { - const n = if (nodeLineOffset(tree, *decl, *next_decl) >= 2) u8(2) else u8(1); + const n = if (nodeLineOffset(tree, decl.*, next_decl.*) >= 2) u8(2) else u8(1); try stream.writeByteNTimes('\n', n); } } @@ -634,7 +631,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind } if (err_set_decl.decls.len == 1) blk: { - const node = *err_set_decl.decls.at(0); + const node = err_set_decl.decls.at(0).*; // if there are any doc comments or same line comments // don't try to put it all on one line @@ -644,7 +641,6 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind break :blk; } - try stream.write("error{"); try renderTopLevelDecl(allocator, stream, tree, indent, node); try stream.write("}"); @@ -657,12 +653,12 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind var it = err_set_decl.decls.iterator(0); while (it.next()) |node| { try stream.writeByteNTimes(' ', new_indent); - try renderTopLevelDecl(allocator, stream, tree, new_indent, *node); - if ((*node).id != ast.Node.Id.LineComment) { + try renderTopLevelDecl(allocator, stream, tree, new_indent, node.*); + if ((node.*).id != ast.Node.Id.LineComment) { try stream.write(","); } if (it.peek()) |next_node| { - const n = if (nodeLineOffset(tree, *node, *next_node) >= 2) u8(2) else u8(1); + const n = if (nodeLineOffset(tree, node.*, next_node.*) >= 2) u8(2) else u8(1); try stream.writeByteNTimes('\n', n); } } @@ -676,9 +672,9 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); try stream.print("\n"); - var i : usize = 0; + var i: usize = 0; while (i < multiline_str_literal.lines.len) : (i += 1) { - const t = *multiline_str_literal.lines.at(i); + const t = multiline_str_literal.lines.at(i).*; try stream.writeByteNTimes(' ', indent + indent_delta); try stream.print("{}", tree.tokenSlice(t)); } @@ -695,7 +691,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind var it = builtin_call.params.iterator(0); while (it.next()) |param_node| { - try renderExpression(allocator, stream, tree, indent, *param_node); + try renderExpression(allocator, stream, tree, indent, param_node.*); if (it.peek() != null) { try stream.write(", "); } @@ -740,7 +736,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind var it = fn_proto.params.iterator(0); while (it.next()) |param_decl_node| { - try renderParamDecl(allocator, stream, tree, indent, *param_decl_node); + try renderParamDecl(allocator, stream, tree, indent, param_decl_node.*); if (it.peek() != null) { try stream.write(", "); @@ -764,7 +760,6 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderExpression(allocator, stream, tree, indent, node); }, } - }, ast.Node.Id.PromiseType => { @@ -801,10 +796,10 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind var it = switch_node.cases.iterator(0); while (it.next()) |node| { try stream.writeByteNTimes(' ', new_indent); - try renderExpression(allocator, stream, tree, new_indent, *node); + try renderExpression(allocator, stream, tree, new_indent, node.*); if (it.peek()) |next_node| { - const n = if (nodeLineOffset(tree, *node, *next_node) >= 2) u8(2) else u8(1); + const n = if (nodeLineOffset(tree, node.*, next_node.*) >= 2) u8(2) else u8(1); try stream.writeByteNTimes('\n', n); } } @@ -819,7 +814,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind var it = switch_case.items.iterator(0); while (it.next()) |node| { - try renderExpression(allocator, stream, tree, indent, *node); + try renderExpression(allocator, stream, tree, indent, node.*); if (it.peek() != null) { try stream.write(",\n"); @@ -846,8 +841,10 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try stream.print("{}", tree.tokenSlice(else_node.else_token)); const block_body = switch (else_node.body.id) { - ast.Node.Id.Block, ast.Node.Id.If, - ast.Node.Id.For, ast.Node.Id.While, + ast.Node.Id.Block, + ast.Node.Id.If, + ast.Node.Id.For, + ast.Node.Id.While, ast.Node.Id.Switch => true, else => false, }; @@ -972,7 +969,11 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderExpression(allocator, stream, tree, indent, if_node.body); switch (if_node.body.id) { - ast.Node.Id.Block, ast.Node.Id.If, ast.Node.Id.For, ast.Node.Id.While, ast.Node.Id.Switch => { + ast.Node.Id.Block, + ast.Node.Id.If, + ast.Node.Id.For, + ast.Node.Id.While, + ast.Node.Id.Switch => { if (if_node.@"else") |@"else"| { if (if_node.body.id == ast.Node.Id.Block) { try stream.write(" "); @@ -995,7 +996,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderExpression(allocator, stream, tree, indent, @"else".body); } - } + }, } }, @@ -1018,11 +1019,11 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind { var it = asm_node.outputs.iterator(0); while (it.next()) |asm_output| { - const node = &(*asm_output).base; + const node = &(asm_output.*).base; try renderExpression(allocator, stream, tree, indent_extra, node); if (it.peek()) |next_asm_output| { - const next_node = &(*next_asm_output).base; + const next_node = &(next_asm_output.*).base; const n = if (nodeLineOffset(tree, node, next_node) >= 2) u8(2) else u8(1); try stream.writeByte(','); try stream.writeByteNTimes('\n', n); @@ -1038,11 +1039,11 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind { var it = asm_node.inputs.iterator(0); while (it.next()) |asm_input| { - const node = &(*asm_input).base; + const node = &(asm_input.*).base; try renderExpression(allocator, stream, tree, indent_extra, node); if (it.peek()) |next_asm_input| { - const next_node = &(*next_asm_input).base; + const next_node = &(next_asm_input.*).base; const n = if (nodeLineOffset(tree, node, next_node) >= 2) u8(2) else u8(1); try stream.writeByte(','); try stream.writeByteNTimes('\n', n); @@ -1058,7 +1059,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind { var it = asm_node.clobbers.iterator(0); while (it.next()) |node| { - try renderExpression(allocator, stream, tree, indent_once, *node); + try renderExpression(allocator, stream, tree, indent_once, node.*); if (it.peek() != null) { try stream.write(", "); @@ -1220,8 +1221,7 @@ fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) (@type const comment = node.doc_comments ?? return; var it = comment.lines.iterator(0); while (it.next()) |line_token_index| { - try stream.print("{}\n", tree.tokenSlice(*line_token_index)); + try stream.print("{}\n", tree.tokenSlice(line_token_index.*)); try stream.writeByteNTimes(' ', indent); } } - -- cgit v1.2.3 From 4a3d689550286fbf7859321991c8f7b7e6d87207 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 16 May 2018 18:22:39 -0400 Subject: std.fmt: use SI prefixes for printing bytes closes #1015 --- std/fmt/index.zig | 87 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index e26b2f6c8b..9170acbe89 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -28,6 +28,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), Buf, BufWidth, Bytes, + BytesBase, BytesWidth, }; @@ -99,6 +100,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), }, 'B' => { width = 0; + radix = 1000; state = State.Bytes; }, else => @compileError("Unknown format character: " ++ []u8{c}), @@ -214,7 +216,24 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), }, State.Bytes => switch (c) { '}' => { - try formatBytes(args[next_arg], 0, context, Errors, output); + try formatBytes(args[next_arg], 0, radix, context, Errors, output); + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + 'i' => { + radix = 1024; + state = State.BytesBase; + }, + '0' ... '9' => { + width_start = i; + state = State.BytesWidth; + }, + else => @compileError("Unexpected character in format string: " ++ []u8{c}), + }, + State.BytesBase => switch (c) { + '}' => { + try formatBytes(args[next_arg], 0, radix, context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; @@ -228,7 +247,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), State.BytesWidth => switch (c) { '}' => { width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatBytes(args[next_arg], width, context, Errors, output); + try formatBytes(args[next_arg], width, radix, context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; @@ -550,7 +569,7 @@ pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, com } } -pub fn formatBytes(value: var, width: ?usize, +pub fn formatBytes(value: var, width: ?usize, comptime radix: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { if (value == 0) { @@ -558,16 +577,26 @@ pub fn formatBytes(value: var, width: ?usize, } const mags = " KMGTPEZY"; - const magnitude = math.min(math.log2(value) / 10, mags.len - 1); - const new_value = f64(value) / math.pow(f64, 1024, f64(magnitude)); + const magnitude = switch (radix) { + 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags.len - 1), + 1024 => math.min(math.log2(value) / 10, mags.len - 1), + else => unreachable, + }; + const new_value = f64(value) / math.pow(f64, f64(radix), f64(magnitude)); const suffix = mags[magnitude]; try formatFloatDecimal(new_value, width, context, Errors, output); - if (suffix != ' ') { - try output(context, (&suffix)[0..1]); + if (suffix == ' ') { + return output(context, "B"); } - return output(context, "B"); + + const buf = switch (radix) { + 1000 => []u8 { suffix, 'B' }, + 1024 => []u8 { suffix, 'i', 'B' }, + else => unreachable, + }; + return output(context, buf); } pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, @@ -787,41 +816,27 @@ test "parse unsigned comptime" { test "fmt.format" { { - var buf1: [32]u8 = undefined; const value: ?i32 = 1234; - const result = try bufPrint(buf1[0..], "nullable: {}\n", value); - assert(mem.eql(u8, result, "nullable: 1234\n")); + try testFmt("nullable: 1234\n", "nullable: {}\n", value); } { - var buf1: [32]u8 = undefined; const value: ?i32 = null; - const result = try bufPrint(buf1[0..], "nullable: {}\n", value); - assert(mem.eql(u8, result, "nullable: null\n")); + try testFmt("nullable: null\n", "nullable: {}\n", value); } { - var buf1: [32]u8 = undefined; const value: error!i32 = 1234; - const result = try bufPrint(buf1[0..], "error union: {}\n", value); - assert(mem.eql(u8, result, "error union: 1234\n")); + try testFmt("error union: 1234\n", "error union: {}\n", value); } { - var buf1: [32]u8 = undefined; const value: error!i32 = error.InvalidChar; - const result = try bufPrint(buf1[0..], "error union: {}\n", value); - assert(mem.eql(u8, result, "error union: error.InvalidChar\n")); + try testFmt("error union: error.InvalidChar\n", "error union: {}\n", value); } { - var buf1: [32]u8 = undefined; const value: u3 = 0b101; - const result = try bufPrint(buf1[0..], "u3: {}\n", value); - assert(mem.eql(u8, result, "u3: 5\n")); - } - { - var buf1: [32]u8 = undefined; - const value: usize = 63 * 1024 * 1024; - const result = try bufPrint(buf1[0..], "file size: {B}\n", value); - assert(mem.eql(u8, result, "file size: 63MB\n")); + try testFmt("u3: 5\n", "u3: {}\n", value); } + try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); + try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024)); { // Dummy field because of https://github.com/zig-lang/zig/issues/557. const Struct = struct { @@ -1041,6 +1056,20 @@ test "fmt.format" { } } +fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { + var buf: [100]u8 = undefined; + const result = try bufPrint(buf[0..], template, args); + if (mem.eql(u8, result, expected)) + return; + + std.debug.warn("\n====== expected this output: =========\n"); + std.debug.warn("{}", expected); + std.debug.warn("\n======== instead found this: =========\n"); + std.debug.warn("{}", result); + std.debug.warn("\n======================================\n"); + return error.TestFailed; +} + pub fn trim(buf: []const u8) []const u8 { var start: usize = 0; while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) { } -- cgit v1.2.3 From 967bad43a053bf7d9d54dd352fcd8416c767785e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 16 May 2018 20:18:38 -0400 Subject: OpenBSD has the same C integer sizes as Linux Thanks Jan S for this information closes #1016 --- src/target.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target.cpp b/src/target.cpp index 57970888fc..c53ed74d14 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -702,6 +702,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { case OsLinux: case OsMacOSX: case OsZen: + case OsOpenBSD: switch (id) { case CIntTypeShort: case CIntTypeUShort: @@ -742,7 +743,6 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { case OsKFreeBSD: case OsLv2: case OsNetBSD: - case OsOpenBSD: case OsSolaris: case OsHaiku: case OsMinix: -- cgit v1.2.3 From 9ea0e4ca6803cd10cfbffbdf80033a90f0e544d5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 17 May 2018 00:15:52 -0400 Subject: zig fmt: same line comments after tokens in expression --- std/zig/parser_test.zig | 43 +++++++++++++++++++++++++++++++++++++++++++ std/zig/render.zig | 41 ++++++++++++++++++++++++----------------- 2 files changed, 67 insertions(+), 17 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 129e62e6dd..ea26f823b5 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,46 @@ +test "zig fmt: same line comments in expression" { + try testCanonical( + \\test "aoeu" { + \\ const x = ( // a + \\ 0 // b + \\ ); // c + \\} + \\ + ); +} + +//test "zig fmt: line comment between if block and else keyword" { +// try testTransform( +// test "aoeu" { +// // cexp(finite|nan +- i inf|nan) = nan + i nan +// if ((hx & 0x7fffffff) != 0x7f800000) { +// return Complex(f32).new(y - y, y - y); +// } +// // cexp(-inf +- i inf|nan) = 0 + i0 +// else if (hx & 0x80000000 != 0) { +// return Complex(f32).new(0, 0); +// } +// // cexp(+inf +- i inf|nan) = inf + i nan +// else { +// return Complex(f32).new(x, y - y); +// } +// } +// , +// test "aoeu" { +// // cexp(finite|nan +- i inf|nan) = nan + i nan +// if ((hx & 0x7fffffff) != 0x7f800000) { +// return Complex(f32).new(y - y, y - y); +// } // cexp(-inf +- i inf|nan) = 0 + i0 +// else if (hx & 0x80000000 != 0) { +// return Complex(f32).new(0, 0); +// } // cexp(+inf +- i inf|nan) = inf + i nan +// else { +// return Complex(f32).new(x, y - y); +// } +// } +// ); +//} + test "zig fmt: add comma on last switch prong" { try testTransform( \\test "aoeu" { diff --git a/std/zig/render.zig b/std/zig/render.zig index 5084c1d096..9ce8ba0cbf 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -81,7 +81,7 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i } try stream.print("{}: ", tree.tokenSlice(field.name_token)); try renderExpression(allocator, stream, tree, indent, field.type_expr); - try renderToken(tree, stream, field.lastToken() + 1, indent, true); + try renderToken(tree, stream, field.lastToken() + 1, indent, true, true); }, ast.Node.Id.UnionTag => { const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); @@ -513,9 +513,9 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind ast.Node.Id.GroupedExpression => { const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); - try stream.write("("); + try renderToken(tree, stream, grouped_expr.lparen, indent, false, false); try renderExpression(allocator, stream, tree, indent, grouped_expr.expr); - try stream.write(")"); + try renderToken(tree, stream, grouped_expr.rparen, indent, false, false); }, ast.Node.Id.FieldInitializer => { @@ -527,7 +527,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind ast.Node.Id.IntegerLiteral => { const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(integer_literal.token)); + try renderToken(tree, stream, integer_literal.token, indent, false, false); }, ast.Node.Id.FloatLiteral => { const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); @@ -535,7 +535,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind }, ast.Node.Id.StringLiteral => { const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); - try stream.print("{}", tree.tokenSlice(string_literal.token)); + try renderToken(tree, stream, string_literal.token, indent, false, false); }, ast.Node.Id.CharLiteral => { const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); @@ -840,9 +840,9 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind }, Token.Id.LineComment => { try stream.write(", "); - try renderToken(tree, stream, index, indent, true); + try renderToken(tree, stream, index, indent, true, true); }, - else => try renderToken(tree, stream, index, indent, true), + else => try renderToken(tree, stream, index, indent, true, true), } } }, @@ -971,7 +971,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try stream.print("{} (", tree.tokenSlice(if_node.if_token)); try renderExpression(allocator, stream, tree, indent, if_node.condition); - try renderToken(tree, stream, if_node.condition.lastToken() + 1, indent, false); + try renderToken(tree, stream, if_node.condition.lastToken() + 1, indent, false, true); if (if_node.payload) |payload| { try renderExpression(allocator, stream, tree, indent, payload); @@ -1126,11 +1126,11 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind fn renderVarDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, var_decl: &ast.Node.VarDecl) (@typeOf(stream).Child.Error || Error)!void { if (var_decl.visib_token) |visib_token| { - try stream.print("{} ", tree.tokenSlice(visib_token)); + try renderToken(tree, stream, visib_token, indent, false, true); } if (var_decl.extern_export_token) |extern_export_token| { - try stream.print("{} ", tree.tokenSlice(extern_export_token)); + try renderToken(tree, stream, extern_export_token, indent, false, true); if (var_decl.lib_name) |lib_name| { try renderExpression(allocator, stream, tree, indent, lib_name); @@ -1139,10 +1139,11 @@ fn renderVarDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent } if (var_decl.comptime_token) |comptime_token| { - try stream.print("{} ", tree.tokenSlice(comptime_token)); + try renderToken(tree, stream, comptime_token, indent, false, true); } - try stream.print("{} {}", tree.tokenSlice(var_decl.mut_token), tree.tokenSlice(var_decl.name_token)); + try renderToken(tree, stream, var_decl.mut_token, indent, false, true); + try renderToken(tree, stream, var_decl.name_token, indent, false, false); if (var_decl.type_node) |type_node| { try stream.write(": "); @@ -1161,14 +1162,14 @@ fn renderVarDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent try renderExpression(allocator, stream, tree, indent, init_node); } - try renderToken(tree, stream, var_decl.semicolon_token, indent, true); + try renderToken(tree, stream, var_decl.semicolon_token, indent, true, false); } fn maybeRenderSemicolon(stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { if (base.requireSemiColon()) { const semicolon_index = base.lastToken() + 1; assert(tree.tokens.at(semicolon_index).id == Token.Id.Semicolon); - try renderToken(tree, stream, semicolon_index, indent, true); + try renderToken(tree, stream, semicolon_index, indent, true, true); } } @@ -1203,7 +1204,7 @@ fn renderStatement(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, inde } } -fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, line_break: bool) (@typeOf(stream).Child.Error || Error)!void { +fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, line_break: bool, space: bool) (@typeOf(stream).Child.Error || Error)!void { const token = tree.tokens.at(token_index); try stream.write(tree.tokenSlicePtr(token)); @@ -1214,13 +1215,19 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent try stream.print(" {}", tree.tokenSlicePtr(next_token)); if (!line_break) { try stream.write("\n"); - try stream.writeByteNTimes(' ', indent + indent_delta); + + const after_comment_token = tree.tokens.at(token_index + 2); + const next_line_indent = switch (after_comment_token.id) { + Token.Id.RParen, Token.Id.RBrace, Token.Id.RBracket => indent, + else => indent + indent_delta, + }; + try stream.writeByteNTimes(' ', next_line_indent); return; } } } - if (!line_break) { + if (!line_break and space) { try stream.writeByte(' '); } } -- cgit v1.2.3 From 37c6afa5b4c25e008206d8a036e02b7fd7189459 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 17 May 2018 00:31:47 -0400 Subject: zig fmt: line comment between if block and else keyword --- std/zig/parse.zig | 1 + std/zig/parser_test.zig | 67 ++++++++++++++++++++++++++----------------------- std/zig/render.zig | 9 +++++++ 3 files changed, 45 insertions(+), 32 deletions(-) diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 4f1e3dcefe..094d368c23 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -1017,6 +1017,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.Else => |dest| { + while (try eatLineComment(arena, &tok_it, &tree)) |_| { } if (eatToken(&tok_it, &tree, Token.Id.Keyword_else)) |else_token| { const node = try arena.construct(ast.Node.Else { .base = ast.Node {.id = ast.Node.Id.Else }, diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index ea26f823b5..38e61fe8fe 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,38 @@ +test "zig fmt: line comment between if block and else keyword" { + try testTransform( + \\test "aoeu" { + \\ // cexp(finite|nan +- i inf|nan) = nan + i nan + \\ if ((hx & 0x7fffffff) != 0x7f800000) { + \\ return Complex(f32).new(y - y, y - y); + \\ } + \\ // cexp(-inf +- i inf|nan) = 0 + i0 + \\ else if (hx & 0x80000000 != 0) { + \\ return Complex(f32).new(0, 0); + \\ } + \\ // cexp(+inf +- i inf|nan) = inf + i nan + \\ // another comment + \\ else { + \\ return Complex(f32).new(x, y - y); + \\ } + \\} + , + \\test "aoeu" { + \\ // cexp(finite|nan +- i inf|nan) = nan + i nan + \\ if ((hx & 0x7fffffff) != 0x7f800000) { + \\ return Complex(f32).new(y - y, y - y); + \\ } // cexp(-inf +- i inf|nan) = 0 + i0 + \\ else if (hx & 0x80000000 != 0) { + \\ return Complex(f32).new(0, 0); + \\ } // cexp(+inf +- i inf|nan) = inf + i nan + \\ // another comment + \\ else { + \\ return Complex(f32).new(x, y - y); + \\ } + \\} + \\ + ); +} + test "zig fmt: same line comments in expression" { try testCanonical( \\test "aoeu" { @@ -9,38 +44,6 @@ test "zig fmt: same line comments in expression" { ); } -//test "zig fmt: line comment between if block and else keyword" { -// try testTransform( -// test "aoeu" { -// // cexp(finite|nan +- i inf|nan) = nan + i nan -// if ((hx & 0x7fffffff) != 0x7f800000) { -// return Complex(f32).new(y - y, y - y); -// } -// // cexp(-inf +- i inf|nan) = 0 + i0 -// else if (hx & 0x80000000 != 0) { -// return Complex(f32).new(0, 0); -// } -// // cexp(+inf +- i inf|nan) = inf + i nan -// else { -// return Complex(f32).new(x, y - y); -// } -// } -// , -// test "aoeu" { -// // cexp(finite|nan +- i inf|nan) = nan + i nan -// if ((hx & 0x7fffffff) != 0x7f800000) { -// return Complex(f32).new(y - y, y - y); -// } // cexp(-inf +- i inf|nan) = 0 + i0 -// else if (hx & 0x80000000 != 0) { -// return Complex(f32).new(0, 0); -// } // cexp(+inf +- i inf|nan) = inf + i nan -// else { -// return Complex(f32).new(x, y - y); -// } -// } -// ); -//} - test "zig fmt: add comma on last switch prong" { try testTransform( \\test "aoeu" { diff --git a/std/zig/render.zig b/std/zig/render.zig index 9ce8ba0cbf..980addc77a 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -852,6 +852,15 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind }, ast.Node.Id.Else => { const else_node = @fieldParentPtr(ast.Node.Else, "base", base); + + var prev_tok_index = else_node.else_token - 1; + while (tree.tokens.at(prev_tok_index).id == Token.Id.LineComment) : (prev_tok_index -= 1) { } + prev_tok_index += 1; + while (prev_tok_index < else_node.else_token) : (prev_tok_index += 1) { + try stream.print("{}\n", tree.tokenSlice(prev_tok_index)); + try stream.writeByteNTimes(' ', indent); + } + try stream.print("{}", tree.tokenSlice(else_node.else_token)); const block_body = switch (else_node.body.id) { -- cgit v1.2.3 From b48d354600406d39b29fe7327b09a62d2584a83f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 17 May 2018 00:44:55 -0400 Subject: zig fmt: fix comment after if before another if --- std/segmented_list.zig | 18 ++++++++++++------ std/zig/parse.zig | 9 ++++++++- std/zig/parser_test.zig | 15 +++++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/std/segmented_list.zig b/std/segmented_list.zig index a89d332556..977a725033 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -298,21 +298,27 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return &it.list.dynamic_segments[it.shelf_index][it.box_index]; } + + pub fn set(it: &Iterator, index: usize) void { + if (index < prealloc_item_count) { + it.index = index; + return; + } + it.shelf_index = shelfIndex(index); + it.box_index = boxIndex(index, it.shelf_index); + it.shelf_size = shelfSize(it.shelf_index); + } }; pub fn iterator(self: &Self, start_index: usize) Iterator { var it = Iterator { .list = self, - .index = start_index, + .index = undefined, .shelf_index = undefined, .box_index = undefined, .shelf_size = undefined, }; - if (start_index >= prealloc_item_count) { - it.shelf_index = shelfIndex(start_index); - it.box_index = boxIndex(start_index, it.shelf_index); - it.shelf_size = shelfSize(it.shelf_index); - } + it.set(start_index); return it; } }; diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 094d368c23..970c49c4b2 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -1017,7 +1017,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.Else => |dest| { - while (try eatLineComment(arena, &tok_it, &tree)) |_| { } + const old_index = tok_it.index; + var need_index_restore = false; + while (try eatLineComment(arena, &tok_it, &tree)) |_| { + need_index_restore = true; + } if (eatToken(&tok_it, &tree, Token.Id.Keyword_else)) |else_token| { const node = try arena.construct(ast.Node.Else { .base = ast.Node {.id = ast.Node.Id.Else }, @@ -1031,6 +1035,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); continue; } else { + if (need_index_restore) { + tok_it.set(old_index); + } continue; } }, diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 38e61fe8fe..094c4d51f7 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,18 @@ +test "zig fmt: comment after if before another if" { + try testCanonical( + \\test "aoeu" { + \\ if (x) { + \\ foo(); + \\ } + \\ // comment + \\ if (x) { + \\ bar(); + \\ } + \\} + \\ + ); +} + test "zig fmt: line comment between if block and else keyword" { try testTransform( \\test "aoeu" { -- cgit v1.2.3 From 942d384831196acf24868c32ef84409b05441960 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 17 May 2018 00:52:36 -0400 Subject: fix std.SegmentedList.Iterator.set --- std/segmented_list.zig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/std/segmented_list.zig b/std/segmented_list.zig index 977a725033..b324c6c4bd 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -300,10 +300,8 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } pub fn set(it: &Iterator, index: usize) void { - if (index < prealloc_item_count) { - it.index = index; - return; - } + it.index = index; + if (index < prealloc_item_count) return; it.shelf_index = shelfIndex(index); it.box_index = boxIndex(index, it.shelf_index); it.shelf_size = shelfSize(it.shelf_index); -- cgit v1.2.3 From b73307befb49dd3b131d99ecf1c7f3fb54578ec8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 17 May 2018 00:56:14 -0400 Subject: more std lib to postfix deref with zig fmt --- std/math/complex/exp.zig | 28 +++++++++++----------------- std/math/complex/ldexp.zig | 17 +++++++---------- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig index 03f7f9e41b..8fe069a43d 100644 --- a/std/math/complex/exp.zig +++ b/std/math/complex/exp.zig @@ -19,8 +19,8 @@ pub fn exp(z: var) Complex(@typeOf(z.re)) { fn exp32(z: &const Complex(f32)) Complex(f32) { @setFloatMode(this, @import("builtin").FloatMode.Strict); - const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955 - const cexp_overflow = 0x43400074; // (max_exp - min_denom_exp) * ln2 + const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955 + const cexp_overflow = 0x43400074; // (max_exp - min_denom_exp) * ln2 const x = z.re; const y = z.im; @@ -41,12 +41,10 @@ fn exp32(z: &const Complex(f32)) Complex(f32) { // cexp(finite|nan +- i inf|nan) = nan + i nan if ((hx & 0x7fffffff) != 0x7f800000) { return Complex(f32).new(y - y, y - y); - } - // cexp(-inf +- i inf|nan) = 0 + i0 + } // cexp(-inf +- i inf|nan) = 0 + i0 else if (hx & 0x80000000 != 0) { return Complex(f32).new(0, 0); - } - // cexp(+inf +- i inf|nan) = inf + i nan + } // cexp(+inf +- i inf|nan) = inf + i nan else { return Complex(f32).new(x, y - y); } @@ -55,8 +53,7 @@ fn exp32(z: &const Complex(f32)) Complex(f32) { // 88.7 <= x <= 192 so must scale if (hx >= exp_overflow and hx <= cexp_overflow) { return ldexp_cexp(z, 0); - } - // - x < exp_overflow => exp(x) won't overflow (common) + } // - x < exp_overflow => exp(x) won't overflow (common) // - x > cexp_overflow, so exp(x) * s overflows for s > 0 // - x = +-inf // - x = nan @@ -67,8 +64,8 @@ fn exp32(z: &const Complex(f32)) Complex(f32) { } fn exp64(z: &const Complex(f64)) Complex(f64) { - const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710 - const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2 + const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710 + const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2 const x = z.re; const y = z.im; @@ -95,12 +92,10 @@ fn exp64(z: &const Complex(f64)) Complex(f64) { // cexp(finite|nan +- i inf|nan) = nan + i nan if (lx != 0 or (hx & 0x7fffffff) != 0x7ff00000) { return Complex(f64).new(y - y, y - y); - } - // cexp(-inf +- i inf|nan) = 0 + i0 + } // cexp(-inf +- i inf|nan) = 0 + i0 else if (hx & 0x80000000 != 0) { return Complex(f64).new(0, 0); - } - // cexp(+inf +- i inf|nan) = inf + i nan + } // cexp(+inf +- i inf|nan) = inf + i nan else { return Complex(f64).new(x, y - y); } @@ -109,9 +104,8 @@ fn exp64(z: &const Complex(f64)) Complex(f64) { // 709.7 <= x <= 1454.3 so must scale if (hx >= exp_overflow and hx <= cexp_overflow) { const r = ldexp_cexp(z, 0); - return *r; - } - // - x < exp_overflow => exp(x) won't overflow (common) + return r.*; + } // - x < exp_overflow => exp(x) won't overflow (common) // - x > cexp_overflow, so exp(x) * s overflows for s > 0 // - x = +-inf // - x = nan diff --git a/std/math/complex/ldexp.zig b/std/math/complex/ldexp.zig index 4fb5a6815f..7ebefff40c 100644 --- a/std/math/complex/ldexp.zig +++ b/std/math/complex/ldexp.zig @@ -15,12 +15,12 @@ pub fn ldexp_cexp(z: var, expt: i32) Complex(@typeOf(z.re)) { } fn frexp_exp32(x: f32, expt: &i32) f32 { - const k = 235; // reduction constant - const kln2 = 162.88958740; // k * ln2 + const k = 235; // reduction constant + const kln2 = 162.88958740; // k * ln2 const exp_x = math.exp(x - kln2); const hx = @bitCast(u32, exp_x); - *expt = i32(hx >> 23) - (0x7f + 127) + k; + expt.* = i32(hx >> 23) - (0x7f + 127) + k; return @bitCast(f32, (hx & 0x7fffff) | ((0x7f + 127) << 23)); } @@ -35,15 +35,12 @@ fn ldexp_cexp32(z: &const Complex(f32), expt: i32) Complex(f32) { const half_expt2 = exptf - half_expt1; const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23); - return Complex(f32).new( - math.cos(z.im) * exp_x * scale1 * scale2, - math.sin(z.im) * exp_x * scale1 * scale2, - ); + return Complex(f32).new(math.cos(z.im) * exp_x * scale1 * scale2, math.sin(z.im) * exp_x * scale1 * scale2); } fn frexp_exp64(x: f64, expt: &i32) f64 { - const k = 1799; // reduction constant - const kln2 = 1246.97177782734161156; // k * ln2 + const k = 1799; // reduction constant + const kln2 = 1246.97177782734161156; // k * ln2 const exp_x = math.exp(x - kln2); @@ -51,7 +48,7 @@ fn frexp_exp64(x: f64, expt: &i32) f64 { const hx = u32(fx >> 32); const lx = @truncate(u32, fx); - *expt = i32(hx >> 20) - (0x3ff + 1023) + k; + expt.* = i32(hx >> 20) - (0x3ff + 1023) + k; const high_word = (hx & 0xfffff) | ((0x3ff + 1023) << 20); return @bitCast(f64, (u64(high_word) << 32) | lx); -- cgit v1.2.3 From c38b165db4a16ba6a5c6d13537177db656fc4033 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 17 May 2018 23:21:44 -0400 Subject: all tests passing with postfix deref syntax --- build.zig | 59 ++++++---- doc/langref.html.in | 60 +++++----- src-self-hosted/arg.zig | 74 ++++++------ src-self-hosted/module.zig | 18 +-- src-self-hosted/target.zig | 7 +- std/zig/ast.zig | 1 + std/zig/parse.zig | 2 +- std/zig/render.zig | 3 +- test/compare_output.zig | 4 +- test/compile_errors.zig | 30 ++--- test/standalone/brace_expansion/main.zig | 43 +++---- test/tests.zig | 190 +++++++++++++++---------------- test/translate_c.zig | 100 ++++++++-------- 13 files changed, 302 insertions(+), 289 deletions(-) diff --git a/build.zig b/build.zig index b72641a2ef..a4e3dbcdfa 100644 --- a/build.zig +++ b/build.zig @@ -16,7 +16,7 @@ pub fn build(b: &Builder) !void { var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); const rel_zig_exe = try os.path.relative(b.allocator, b.build_root, b.zig_exe); - var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8 { + var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{ docgen_exe.getOutputPath(), rel_zig_exe, "doc/langref.html.in", @@ -30,7 +30,10 @@ pub fn build(b: &Builder) !void { const test_step = b.step("test", "Run all the tests"); // find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library - const build_info = try b.exec([][]const u8{b.zig_exe, "BUILD_INFO"}); + const build_info = try b.exec([][]const u8{ + b.zig_exe, + "BUILD_INFO", + }); var index: usize = 0; const cmake_binary_dir = nextValue(&index, build_info); const cxx_compiler = nextValue(&index, build_info); @@ -67,7 +70,10 @@ pub fn build(b: &Builder) !void { dependOnLib(exe, llvm); if (exe.target.getOs() == builtin.Os.linux) { - const libstdcxx_path_padded = try b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"}); + const libstdcxx_path_padded = try b.exec([][]const u8{ + cxx_compiler, + "-print-file-name=libstdc++.a", + }); const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next(); if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) { warn( @@ -111,17 +117,11 @@ pub fn build(b: &Builder) !void { test_step.dependOn(docs_step); - test_step.dependOn(tests.addPkgTests(b, test_filter, - "test/behavior.zig", "behavior", "Run the behavior tests", - with_lldb)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", with_lldb)); - test_step.dependOn(tests.addPkgTests(b, test_filter, - "std/index.zig", "std", "Run the standard library tests", - with_lldb)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", with_lldb)); - test_step.dependOn(tests.addPkgTests(b, test_filter, - "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", - with_lldb)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", with_lldb)); test_step.dependOn(tests.addCompareOutputTests(b, test_filter)); test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); @@ -149,8 +149,7 @@ fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) vo fn addCppLib(b: &Builder, lib_exe_obj: &std.build.LibExeObjStep, cmake_binary_dir: []const u8, lib_name: []const u8) void { const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib"; - lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", - b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable); + lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable); } const LibraryDep = struct { @@ -161,11 +160,21 @@ const LibraryDep = struct { }; fn findLLVM(b: &Builder, llvm_config_exe: []const u8) !LibraryDep { - const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"}); - const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"}); - const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"}); + const libs_output = try b.exec([][]const u8{ + llvm_config_exe, + "--libs", + "--system-libs", + }); + const includes_output = try b.exec([][]const u8{ + llvm_config_exe, + "--includedir", + }); + const libdir_output = try b.exec([][]const u8{ + llvm_config_exe, + "--libdir", + }); - var result = LibraryDep { + var result = LibraryDep{ .libs = ArrayList([]const u8).init(b.allocator), .system_libs = ArrayList([]const u8).init(b.allocator), .includes = ArrayList([]const u8).init(b.allocator), @@ -227,17 +236,17 @@ pub fn installCHeaders(b: &Builder, c_header_files: []const u8) void { } fn nextValue(index: &usize, build_info: []const u8) []const u8 { - const start = *index; - while (true) : (*index += 1) { - switch (build_info[*index]) { + const start = index.*; + while (true) : (index.* += 1) { + switch (build_info[index.*]) { '\n' => { - const result = build_info[start..*index]; - *index += 1; + const result = build_info[start..index.*]; + index.* += 1; return result; }, '\r' => { - const result = build_info[start..*index]; - *index += 2; + const result = build_info[start..index.*]; + index.* += 2; return result; }, else => continue, diff --git a/doc/langref.html.in b/doc/langref.html.in index cdfe7d9228..d047c7d9f2 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1232,7 +1232,7 @@ mem.eql(u8, pattern, "ababab") -

*a
+
a.*

The codepoint U+000a (LF) (which is encoded as the single-byte value 0x0a) is the line terminator character. This character always terminates a line of zig source code (except possbly the last line of the file).

-

For some discussion on the rationale behind these design decisions, see issue #663

+

For some discussion on the rationale behind these design decisions, see issue #663

{#header_close#} {#header_open|Grammar#}
Root = many(TopLevelItem) EOF
diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig
index 60123c6fd8..4a35e47b15 100644
--- a/example/hello_world/hello_libc.zig
+++ b/example/hello_world/hello_libc.zig
@@ -1,5 +1,5 @@
 const c = @cImport({
-    // See https://github.com/zig-lang/zig/issues/515
+    // See https://github.com/ziglang/zig/issues/515
     @cDefine("_NO_CRT_STDIO_INLINE", "1");
     @cInclude("stdio.h");
     @cInclude("string.h");
diff --git a/src/analyze.cpp b/src/analyze.cpp
index d6137a4286..c59fde8ef6 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -1007,7 +1007,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
     if (fn_type_id->return_type != nullptr) {
         ensure_complete_type(g, fn_type_id->return_type);
     } else {
-        zig_panic("TODO implement inferred return types https://github.com/zig-lang/zig/issues/447");
+        zig_panic("TODO implement inferred return types https://github.com/ziglang/zig/issues/447");
     }
 
     TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
@@ -1556,7 +1556,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
             return g->builtin_types.entry_invalid;
         }
         add_node_error(g, proto_node,
-            buf_sprintf("TODO implement inferred return types https://github.com/zig-lang/zig/issues/447"));
+            buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
         return g->builtin_types.entry_invalid;
         //return get_generic_fn_type(g, &fn_type_id);
     }
diff --git a/src/codegen.cpp b/src/codegen.cpp
index f1e102392a..69542b3e67 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -582,7 +582,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
             addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull");
         }
         // Note: byval is disabled on windows due to an LLVM bug:
-        // https://github.com/zig-lang/zig/issues/536
+        // https://github.com/ziglang/zig/issues/536
         if (is_byval && g->zig_target.os != OsWindows) {
             addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "byval");
         }
@@ -3067,7 +3067,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
     for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
         FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i];
         // Note: byval is disabled on windows due to an LLVM bug:
-        // https://github.com/zig-lang/zig/issues/536
+        // https://github.com/ziglang/zig/issues/536
         if (gen_info->is_byval && g->zig_target.os != OsWindows) {
             addLLVMCallsiteAttr(result, (unsigned)gen_info->gen_index, "byval");
         }
@@ -6730,7 +6730,7 @@ static void init(CodeGen *g) {
     const char *target_specific_features;
     if (g->is_native_target) {
         // LLVM creates invalid binaries on Windows sometimes.
-        // See https://github.com/zig-lang/zig/issues/508
+        // See https://github.com/ziglang/zig/issues/508
         // As a workaround we do not use target native features on Windows.
         if (g->zig_target.os == OsWindows) {
             target_specific_cpu_args = "";
diff --git a/src/ir.cpp b/src/ir.cpp
index e2cbba48a7..440063d58d 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -12130,7 +12130,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
             casted_arg->value.type->id == TypeTableEntryIdNumLitFloat)
     {
         ir_add_error(ira, casted_arg,
-            buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/zig-lang/zig/issues/557"));
+            buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/ziglang/zig/issues/557"));
         return false;
     }
 
@@ -12331,7 +12331,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
 
         if (fn_proto_node->data.fn_proto.is_var_args) {
             ir_add_error(ira, &call_instruction->base,
-                    buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/zig-lang/zig/issues/313"));
+                    buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/ziglang/zig/issues/313"));
             return ira->codegen->builtin_types.entry_invalid;
         }
 
@@ -12424,7 +12424,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
         }
         if (call_instruction->is_async && fn_type_id->is_var_args) {
             ir_add_error(ira, call_instruction->fn_ref,
-                buf_sprintf("compiler bug: TODO: implement var args async functions. https://github.com/zig-lang/zig/issues/557"));
+                buf_sprintf("compiler bug: TODO: implement var args async functions. https://github.com/ziglang/zig/issues/557"));
             return ira->codegen->builtin_types.entry_invalid;
         }
 
@@ -12507,7 +12507,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
                     VariableTableEntry *arg_var = get_fn_var_by_index(parent_fn_entry, arg_tuple_i);
                     if (arg_var == nullptr) {
                         ir_add_error(ira, arg,
-                            buf_sprintf("compiler bug: var args can't handle void. https://github.com/zig-lang/zig/issues/557"));
+                            buf_sprintf("compiler bug: var args can't handle void. https://github.com/ziglang/zig/issues/557"));
                         return ira->codegen->builtin_types.entry_invalid;
                     }
                     IrInstruction *arg_var_ptr_inst = ir_get_var_ptr(ira, arg, arg_var, true, false);
diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig
index 288a2b3b48..35180da8d1 100644
--- a/std/atomic/queue.zig
+++ b/std/atomic/queue.zig
@@ -16,7 +16,7 @@ pub fn Queue(comptime T: type) type {
             data: T,
         };
 
-        // TODO: well defined copy elision: https://github.com/zig-lang/zig/issues/287
+        // TODO: well defined copy elision: https://github.com/ziglang/zig/issues/287
         pub fn init(self: &Self) void {
             self.root.next = null;
             self.head = &self.root;
diff --git a/std/event.zig b/std/event.zig
index b2e7e3ae38..558bd2a188 100644
--- a/std/event.zig
+++ b/std/event.zig
@@ -148,7 +148,7 @@ pub const Loop = struct {
 };
 
 pub async fn connect(loop: &Loop, _address: &const std.net.Address) !std.os.File {
-    var address = _address.*; // TODO https://github.com/zig-lang/zig/issues/733
+    var address = _address.*; // TODO https://github.com/ziglang/zig/issues/733
 
     const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp);
     errdefer std.os.close(sockfd);
@@ -172,7 +172,7 @@ test "listen on a port, send bytes, receive bytes" {
 
         async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address, _socket: &const std.os.File) void {
             const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
-            var socket = _socket.*; // TODO https://github.com/zig-lang/zig/issues/733
+            var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733
             defer socket.close();
             const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) {
                 error.OutOfMemory => @panic("unable to handle connection: out of memory"),
@@ -186,8 +186,8 @@ test "listen on a port, send bytes, receive bytes" {
         }
 
         async fn errorableHandler(self: &Self, _addr: &const std.net.Address, _socket: &const std.os.File) !void {
-            const addr = _addr.*; // TODO https://github.com/zig-lang/zig/issues/733
-            var socket = _socket.*; // TODO https://github.com/zig-lang/zig/issues/733
+            const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733
+            var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733
 
             var adapter = std.io.FileOutStream.init(&socket);
             var stream = &adapter.stream;
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index 0af772b7dc..624751822a 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -824,7 +824,7 @@ test "fmt.format" {
     try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
     try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));
     {
-        // Dummy field because of https://github.com/zig-lang/zig/issues/557.
+        // Dummy field because of https://github.com/ziglang/zig/issues/557.
         const Struct = struct {
             unused: u8,
         };
diff --git a/std/mem.zig b/std/mem.zig
index 3ca87b35d3..617c1de2f5 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -702,7 +702,7 @@ test "std.mem.rotate" {
     }));
 }
 
-// TODO: When https://github.com/zig-lang/zig/issues/649 is solved these can be done by
+// TODO: When https://github.com/ziglang/zig/issues/649 is solved these can be done by
 // endian-casting the pointer and then dereferencing
 
 pub fn endianSwapIfLe(comptime T: type, x: T) T {
diff --git a/std/os/index.zig b/std/os/index.zig
index 7d19cd82c6..01e2092e1c 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -239,7 +239,7 @@ pub fn close(handle: FileHandle) void {
 /// Calls POSIX read, and keeps trying if it gets interrupted.
 pub fn posixRead(fd: i32, buf: []u8) !void {
     // Linux can return EINVAL when read amount is > 0x7ffff000
-    // See https://github.com/zig-lang/zig/pull/743#issuecomment-363158274
+    // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274
     const max_buf_len = 0x7ffff000;
 
     var index: usize = 0;
@@ -281,7 +281,7 @@ pub const PosixWriteError = error{
 /// Calls POSIX write, and keeps trying if it gets interrupted.
 pub fn posixWrite(fd: i32, bytes: []const u8) !void {
     // Linux can return EINVAL when write amount is > 0x7ffff000
-    // See https://github.com/zig-lang/zig/pull/743#issuecomment-363165856
+    // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856
     const max_bytes_len = 0x7ffff000;
 
     var index: usize = 0;
@@ -2513,7 +2513,7 @@ pub const SpawnThreadError = error{
 /// caller must call wait on the returned thread
 pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread {
     // TODO compile-time call graph analysis to determine stack upper bound
-    // https://github.com/zig-lang/zig/issues/157
+    // https://github.com/ziglang/zig/issues/157
     const default_stack_size = 8 * 1024 * 1024;
 
     const Context = @typeOf(context);
diff --git a/std/os/test.zig b/std/os/test.zig
index 56d6e8b309..4dfe76224a 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -12,7 +12,7 @@ const AtomicOrder = builtin.AtomicOrder;
 test "makePath, put some files in it, deleteTree" {
     if (builtin.os == builtin.Os.windows) {
         // TODO implement os.Dir for windows
-        // https://github.com/zig-lang/zig/issues/709
+        // https://github.com/ziglang/zig/issues/709
         return;
     }
     try os.makePath(a, "os_test_tmp/b/c");
diff --git a/std/os/time.zig b/std/os/time.zig
index 4fd2c4e924..3af150ab6a 100644
--- a/std/os/time.zig
+++ b/std/os/time.zig
@@ -135,7 +135,7 @@ pub const Timer = struct {
     
     //At some point we may change our minds on RAW, but for now we're
     //  sticking with posix standard MONOTONIC. For more information, see: 
-    //  https://github.com/zig-lang/zig/pull/933
+    //  https://github.com/ziglang/zig/pull/933
     //
     //const monotonic_clock_id = switch(builtin.os) {
     //    Os.linux => linux.CLOCK_MONOTONIC_RAW,
diff --git a/std/special/compiler_rt/comparetf2.zig b/std/special/compiler_rt/comparetf2.zig
index c189e5803b..760c3689c0 100644
--- a/std/special/compiler_rt/comparetf2.zig
+++ b/std/special/compiler_rt/comparetf2.zig
@@ -1,4 +1,4 @@
-// TODO https://github.com/zig-lang/zig/issues/305
+// TODO https://github.com/ziglang/zig/issues/305
 // and then make the return types of some of these functions the enum instead of c_int
 const LE_LESS = c_int(-1);
 const LE_EQUAL = c_int(0);
@@ -59,7 +59,7 @@ pub extern fn __letf2(a: f128, b: f128) c_int {
     ;
 }
 
-// TODO https://github.com/zig-lang/zig/issues/305
+// TODO https://github.com/ziglang/zig/issues/305
 // and then make the return types of some of these functions the enum instead of c_int
 const GE_LESS = c_int(-1);
 const GE_EQUAL = c_int(0);
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index c1552b0220..1f15046a79 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -113,7 +113,7 @@ pub const Error = union(enum) {
 
     pub fn render(self: &Error, tokens: &Tree.TokenList, stream: var) !void {
         switch (self.*) {
-            // TODO https://github.com/zig-lang/zig/issues/683
+            // TODO https://github.com/ziglang/zig/issues/683
             @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream),
             @TagType(Error).ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream),
             @TagType(Error).ExpectedAggregateKw => |*x| return x.render(tokens, stream),
@@ -137,7 +137,7 @@ pub const Error = union(enum) {
 
     pub fn loc(self: &Error) TokenIndex {
         switch (self.*) {
-            // TODO https://github.com/zig-lang/zig/issues/683
+            // TODO https://github.com/ziglang/zig/issues/683
             @TagType(Error).InvalidToken => |x| return x.token,
             @TagType(Error).ExpectedVarDeclOrFn => |x| return x.token,
             @TagType(Error).ExpectedAggregateKw => |x| return x.token,
diff --git a/test/build_examples.zig b/test/build_examples.zig
index a3b44b9136..7a4c0f35d9 100644
--- a/test/build_examples.zig
+++ b/test/build_examples.zig
@@ -9,7 +9,7 @@ pub fn addCases(cases: &tests.BuildExamplesContext) void {
     cases.add("example/guess_number/main.zig");
     if (!is_windows) {
         // TODO get this test passing on windows
-        // See https://github.com/zig-lang/zig/issues/538
+        // See https://github.com/ziglang/zig/issues/538
         cases.addBuildFile("example/shared_library/build.zig");
         cases.addBuildFile("example/mix_o_files/build.zig");
     }
diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig
index 4aa97861ac..e983947a4c 100644
--- a/test/cases/coroutines.zig
+++ b/test/cases/coroutines.zig
@@ -204,7 +204,7 @@ test "error return trace across suspend points - async return" {
     cancel p2;
 }
 
-// TODO https://github.com/zig-lang/zig/issues/760
+// TODO https://github.com/ziglang/zig/issues/760
 fn nonFailing() (promise->error!void) {
     return async suspendThenFail() catch unreachable;
 }
diff --git a/test/cases/misc.zig b/test/cases/misc.zig
index 66487a4946..deeeca8c3a 100644
--- a/test/cases/misc.zig
+++ b/test/cases/misc.zig
@@ -543,7 +543,7 @@ test "@typeName" {
     comptime {
         assert(mem.eql(u8, @typeName(i64), "i64"));
         assert(mem.eql(u8, @typeName(&usize), "&usize"));
-        // https://github.com/zig-lang/zig/issues/675
+        // https://github.com/ziglang/zig/issues/675
         assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)"));
         assert(mem.eql(u8, @typeName(Struct), "Struct"));
         assert(mem.eql(u8, @typeName(Union), "Union"));
diff --git a/test/compare_output.zig b/test/compare_output.zig
index b01e87d4eb..905ffd37a9 100644
--- a/test/compare_output.zig
+++ b/test/compare_output.zig
@@ -131,7 +131,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
         \\const is_windows = builtin.os == builtin.Os.windows;
         \\const c = @cImport({
         \\    if (is_windows) {
-        \\        // See https://github.com/zig-lang/zig/issues/515
+        \\        // See https://github.com/ziglang/zig/issues/515
         \\        @cDefine("_NO_CRT_STDIO_INLINE", "1");
         \\        @cInclude("io.h");
         \\        @cInclude("fcntl.h");
@@ -316,7 +316,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
         \\const is_windows = builtin.os == builtin.Os.windows;
         \\const c = @cImport({
         \\    if (is_windows) {
-        \\        // See https://github.com/zig-lang/zig/issues/515
+        \\        // See https://github.com/ziglang/zig/issues/515
         \\        @cDefine("_NO_CRT_STDIO_INLINE", "1");
         \\        @cInclude("io.h");
         \\        @cInclude("fcntl.h");
-- 
cgit v1.2.3


From b74dda34b6a8b5f04d1865e2f23aab43229815f9 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Thu, 24 May 2018 21:51:58 -0400
Subject: std.zig.tokenizer: support hex escape in char literals

---
 std/zig/parser_test.zig | 13 +++++++++++++
 std/zig/tokenizer.zig   | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+)

diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index c11db78775..d114179bf7 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,16 @@
+test "zig fmt: float literal with exponent" {
+    try testCanonical(
+        \\test "aoeu" {
+        \\    switch (state) {
+        \\        TermState.Start => switch (c) {
+        \\            '\x1b' => state = TermState.Escape,
+        \\            else => try out.writeByte(c),
+        \\        },
+        \\    }
+        \\}
+        \\
+    );
+}
 test "zig fmt: float literal with exponent" {
     try testCanonical(
         \\pub const f64_true_min = 4.94065645841246544177e-324;
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index afea7fc899..f4cd847dff 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -220,6 +220,8 @@ pub const Tokenizer = struct {
         MultilineStringLiteralLineBackslash,
         CharLiteral,
         CharLiteralBackslash,
+        CharLiteralEscape1,
+        CharLiteralEscape2,
         CharLiteralEnd,
         Backslash,
         Equal,
@@ -612,9 +614,32 @@ pub const Tokenizer = struct {
                         result.id = Token.Id.Invalid;
                         break;
                     },
+                    'x' => {
+                        state = State.CharLiteralEscape1;
+                    },
+                    else => {
+                        state = State.CharLiteralEnd;
+                    },
+                },
+
+                State.CharLiteralEscape1 => switch (c) {
+                    '0'...'9', 'a'...'z', 'A'...'F' => {
+                        state = State.CharLiteralEscape2;
+                    },
                     else => {
+                        result.id = Token.Id.Invalid;
+                        break;
+                    },
+                },
+
+                State.CharLiteralEscape2 => switch (c) {
+                    '0'...'9', 'a'...'z', 'A'...'F' => {
                         state = State.CharLiteralEnd;
                     },
+                    else => {
+                        result.id = Token.Id.Invalid;
+                        break;
+                    },
                 },
 
                 State.CharLiteralEnd => switch (c) {
@@ -988,6 +1013,8 @@ pub const Tokenizer = struct {
                 State.MultilineStringLiteralLineBackslash,
                 State.CharLiteral,
                 State.CharLiteralBackslash,
+                State.CharLiteralEscape1,
+                State.CharLiteralEscape2,
                 State.CharLiteralEnd,
                 State.StringLiteralBackslash => {
                     result.id = Token.Id.Invalid;
@@ -1127,6 +1154,13 @@ test "tokenizer" {
     });
 }
 
+test "tokenizer - char literal with hex escape" {
+    testTokenize( \\'\x1b'
+    , []Token.Id {
+        Token.Id.CharLiteral,
+    });
+}
+
 test "tokenizer - float literal e exponent" {
     testTokenize("a = 4.94065645841246544177e-324;\n", []Token.Id {
         Token.Id.Identifier,
-- 
cgit v1.2.3


From e6afea99a9642a4fe12b65ef94fee0ee34d7a36b Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Fri, 25 May 2018 00:37:58 -0400
Subject: zig fmt: support aligned ptr with bit fields

---
 std/zig/ast.zig         | 22 +++++++++++++++++-----
 std/zig/parse.zig       | 48 +++++++++++++++++++++++++++++++++++++++---------
 std/zig/parser_test.zig |  9 +++++++++
 std/zig/render.zig      | 48 ++++++++++++++++++++++++++++++++++++++----------
 4 files changed, 103 insertions(+), 24 deletions(-)

diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 1f15046a79..6c848b4a54 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -98,6 +98,7 @@ pub const Error = union(enum) {
     UnattachedDocComment: UnattachedDocComment,
     ExpectedEqOrSemi: ExpectedEqOrSemi,
     ExpectedSemiOrLBrace: ExpectedSemiOrLBrace,
+    ExpectedColonOrRParen: ExpectedColonOrRParen,
     ExpectedLabelable: ExpectedLabelable,
     ExpectedInlinable: ExpectedInlinable,
     ExpectedAsmOutputReturnOrType: ExpectedAsmOutputReturnOrType,
@@ -120,6 +121,7 @@ pub const Error = union(enum) {
             @TagType(Error).UnattachedDocComment => |*x| return x.render(tokens, stream),
             @TagType(Error).ExpectedEqOrSemi => |*x| return x.render(tokens, stream),
             @TagType(Error).ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream),
+            @TagType(Error).ExpectedColonOrRParen => |*x| return x.render(tokens, stream),
             @TagType(Error).ExpectedLabelable => |*x| return x.render(tokens, stream),
             @TagType(Error).ExpectedInlinable => |*x| return x.render(tokens, stream),
             @TagType(Error).ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream),
@@ -144,6 +146,7 @@ pub const Error = union(enum) {
             @TagType(Error).UnattachedDocComment => |x| return x.token,
             @TagType(Error).ExpectedEqOrSemi => |x| return x.token,
             @TagType(Error).ExpectedSemiOrLBrace => |x| return x.token,
+            @TagType(Error).ExpectedColonOrRParen => |x| return x.token,
             @TagType(Error).ExpectedLabelable => |x| return x.token,
             @TagType(Error).ExpectedInlinable => |x| return x.token,
             @TagType(Error).ExpectedAsmOutputReturnOrType => |x| return x.token,
@@ -164,6 +167,7 @@ pub const Error = union(enum) {
     pub const ExpectedAggregateKw = SingleTokenError("Expected " ++ @tagName(Token.Id.Keyword_struct) ++ ", " ++ @tagName(Token.Id.Keyword_union) ++ ", or " ++ @tagName(Token.Id.Keyword_enum) ++ ", found {}");
     pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found {}");
     pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found {}");
+    pub const ExpectedColonOrRParen = SingleTokenError("Expected ':' or ')', found {}");
     pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found {}");
     pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found {}");
     pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or " ++ @tagName(Token.Id.Identifier) ++ ", found {}");
@@ -1487,7 +1491,7 @@ pub const Node = struct {
         op: Op,
         rhs: &Node,
 
-        const Op = union(enum) {
+        pub const Op = union(enum) {
             AddrOf: AddrOfInfo,
             ArrayType: &Node,
             Await,
@@ -1504,12 +1508,20 @@ pub const Node = struct {
             UnwrapMaybe,
         };
 
-        const AddrOfInfo = struct {
-            align_expr: ?&Node,
-            bit_offset_start_token: ?TokenIndex,
-            bit_offset_end_token: ?TokenIndex,
+        pub const AddrOfInfo = struct {
+            align_info: ?Align,
             const_token: ?TokenIndex,
             volatile_token: ?TokenIndex,
+
+            pub const Align = struct {
+                node: &Node,
+                bit_range: ?BitRange,
+
+                pub const BitRange = struct {
+                    start: &Node,
+                    end: &Node,
+                };
+            };
         };
 
         pub fn iterate(self: &PrefixOp, index: usize) ?&Node {
diff --git a/std/zig/parse.zig b/std/zig/parse.zig
index 826ea2c3e1..d60f03c55e 100644
--- a/std/zig/parse.zig
+++ b/std/zig/parse.zig
@@ -1450,9 +1450,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
             State.SliceOrArrayType => |node| {
                 if (eatToken(&tok_it, &tree, Token.Id.RBracket)) |_| {
                     node.op = ast.Node.PrefixOp.Op{ .SliceType = ast.Node.PrefixOp.AddrOfInfo{
-                        .align_expr = null,
-                        .bit_offset_start_token = null,
-                        .bit_offset_end_token = null,
+                        .align_info = null,
                         .const_token = null,
                         .volatile_token = null,
                     } };
@@ -1467,6 +1465,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
                 try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.op.ArrayType } });
                 continue;
             },
+
             State.AddrOfModifiers => |addr_of_info| {
                 const token = nextToken(&tok_it, &tree);
                 const token_index = token.index;
@@ -1474,12 +1473,19 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
                 switch (token_ptr.id) {
                     Token.Id.Keyword_align => {
                         stack.append(state) catch unreachable;
-                        if (addr_of_info.align_expr != null) {
+                        if (addr_of_info.align_info != null) {
                             ((try tree.errors.addOne())).* = Error{ .ExtraAlignQualifier = Error.ExtraAlignQualifier{ .token = token_index } };
                             return tree;
                         }
-                        try stack.append(State{ .ExpectToken = Token.Id.RParen });
-                        try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &addr_of_info.align_expr } });
+                        addr_of_info.align_info = ast.Node.PrefixOp.AddrOfInfo.Align {
+                            .node = undefined,
+                            .bit_range = null,
+                        };
+                        // TODO https://github.com/ziglang/zig/issues/1022
+                        const align_info = &??addr_of_info.align_info;
+
+                        try stack.append(State{ .AlignBitRange = align_info });
+                        try stack.append(State{ .Expression = OptionalCtx{ .Required = &align_info.node } });
                         try stack.append(State{ .ExpectToken = Token.Id.LParen });
                         continue;
                     },
@@ -1508,6 +1514,31 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
                 }
             },
 
+            State.AlignBitRange => |align_info| {
+                const token = nextToken(&tok_it, &tree);
+                switch (token.ptr.id) {
+                    Token.Id.Colon => {
+                        align_info.bit_range = ast.Node.PrefixOp.AddrOfInfo.Align.BitRange(undefined);
+                        const bit_range = &??align_info.bit_range;
+
+                        try stack.append(State{ .ExpectToken = Token.Id.RParen });
+                        try stack.append(State{ .Expression = OptionalCtx{ .Required = &bit_range.end } });
+                        try stack.append(State{ .ExpectToken = Token.Id.Colon });
+                        try stack.append(State{ .Expression = OptionalCtx{ .Required = &bit_range.start } });
+                        continue;
+                    },
+                    Token.Id.RParen => continue,
+                    else => {
+                        (try tree.errors.addOne()).* = Error{
+                            .ExpectedColonOrRParen = Error.ExpectedColonOrRParen{
+                                .token = token.index,
+                            }
+                        };
+                        return tree;
+                    },
+                }
+            },
+
             State.Payload => |opt_ctx| {
                 const token = nextToken(&tok_it, &tree);
                 const token_index = token.index;
@@ -2801,6 +2832,7 @@ const State = union(enum) {
     SliceOrArrayAccess: &ast.Node.SuffixOp,
     SliceOrArrayType: &ast.Node.PrefixOp,
     AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo,
+    AlignBitRange: &ast.Node.PrefixOp.AddrOfInfo.Align,
 
     Payload: OptionalCtx,
     PointerPayload: OptionalCtx,
@@ -3120,9 +3152,7 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op {
         Token.Id.Asterisk,
         Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op{ .PointerType = void{} },
         Token.Id.Ampersand => ast.Node.PrefixOp.Op{ .AddrOf = ast.Node.PrefixOp.AddrOfInfo{
-            .align_expr = null,
-            .bit_offset_start_token = null,
-            .bit_offset_end_token = null,
+            .align_info = null,
             .const_token = null,
             .volatile_token = null,
         } },
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index d114179bf7..a29b8c2547 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,12 @@
+test "zig fmt: float literal with exponent" {
+    try testCanonical(
+        \\test "bit field alignment" {
+        \\    assert(@typeOf(&blah.b) == &align(1:3:6) const u3);
+        \\}
+        \\
+    );
+}
+
 test "zig fmt: float literal with exponent" {
     try testCanonical(
         \\test "aoeu" {
diff --git a/std/zig/render.zig b/std/zig/render.zig
index f48b13c987..3940a61fc1 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -253,17 +253,30 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
             switch (prefix_op_node.op) {
                 ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| {
                     try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.None); // &
-                    if (addr_of_info.align_expr) |align_expr| {
+                    if (addr_of_info.align_info) |align_info| {
                         const align_token = tree.nextToken(prefix_op_node.op_token);
                         try renderToken(tree, stream, align_token, indent, Space.None); // align
 
-                        const lparen_token = tree.prevToken(align_expr.firstToken());
+                        const lparen_token = tree.prevToken(align_info.node.firstToken());
                         try renderToken(tree, stream, lparen_token, indent, Space.None); // (
 
-                        try renderExpression(allocator, stream, tree, indent, align_expr, Space.None);
+                        try renderExpression(allocator, stream, tree, indent, align_info.node, Space.None);
 
-                        const rparen_token = tree.nextToken(align_expr.lastToken());
-                        try renderToken(tree, stream, rparen_token, indent, Space.Space); // )
+                        if (align_info.bit_range) |bit_range| {
+                            const colon1 = tree.prevToken(bit_range.start.firstToken());
+                            const colon2 = tree.prevToken(bit_range.end.firstToken());
+
+                            try renderToken(tree, stream, colon1, indent, Space.None); // :
+                            try renderExpression(allocator, stream, tree, indent, bit_range.start, Space.None);
+                            try renderToken(tree, stream, colon2, indent, Space.None); // :
+                            try renderExpression(allocator, stream, tree, indent, bit_range.end, Space.None);
+
+                            const rparen_token = tree.nextToken(bit_range.end.lastToken());
+                            try renderToken(tree, stream, rparen_token, indent, Space.Space); // )
+                        } else {
+                            const rparen_token = tree.nextToken(align_info.node.lastToken());
+                            try renderToken(tree, stream, rparen_token, indent, Space.Space); // )
+                        }
                     }
                     if (addr_of_info.const_token) |const_token| {
                         try renderToken(tree, stream, const_token, indent, Space.Space); // const
@@ -272,21 +285,35 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                         try renderToken(tree, stream, volatile_token, indent, Space.Space); // volatile
                     }
                 },
+
                 ast.Node.PrefixOp.Op.SliceType => |addr_of_info| {
                     try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.None); // [
                     try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, Space.None); // ]
 
-                    if (addr_of_info.align_expr) |align_expr| {
+                    if (addr_of_info.align_info) |align_info| {
                         const align_token = tree.nextToken(prefix_op_node.op_token);
                         try renderToken(tree, stream, align_token, indent, Space.None); // align
 
-                        const lparen_token = tree.prevToken(align_expr.firstToken());
+                        const lparen_token = tree.prevToken(align_info.node.firstToken());
                         try renderToken(tree, stream, lparen_token, indent, Space.None); // (
 
-                        try renderExpression(allocator, stream, tree, indent, align_expr, Space.None);
+                        try renderExpression(allocator, stream, tree, indent, align_info.node, Space.None);
 
-                        const rparen_token = tree.nextToken(align_expr.lastToken());
-                        try renderToken(tree, stream, rparen_token, indent, Space.Space); // )
+                        if (align_info.bit_range) |bit_range| {
+                            const colon1 = tree.prevToken(bit_range.start.firstToken());
+                            const colon2 = tree.prevToken(bit_range.end.firstToken());
+
+                            try renderToken(tree, stream, colon1, indent, Space.None); // :
+                            try renderExpression(allocator, stream, tree, indent, bit_range.start, Space.None);
+                            try renderToken(tree, stream, colon2, indent, Space.None); // :
+                            try renderExpression(allocator, stream, tree, indent, bit_range.end, Space.None);
+
+                            const rparen_token = tree.nextToken(bit_range.end.lastToken());
+                            try renderToken(tree, stream, rparen_token, indent, Space.Space); // )
+                        } else {
+                            const rparen_token = tree.nextToken(align_info.node.lastToken());
+                            try renderToken(tree, stream, rparen_token, indent, Space.Space); // )
+                        }
                     }
                     if (addr_of_info.const_token) |const_token| {
                         try renderToken(tree, stream, const_token, indent, Space.Space);
@@ -295,6 +322,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                         try renderToken(tree, stream, volatile_token, indent, Space.Space);
                     }
                 },
+
                 ast.Node.PrefixOp.Op.ArrayType => |array_index| {
                     try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.None); // [
                     try renderExpression(allocator, stream, tree, indent, array_index, Space.None);
-- 
cgit v1.2.3


From ca49b6f6b4ffa3ff4324a45501eef98848a2d141 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Fri, 25 May 2018 00:39:05 -0400
Subject: struct fields with no explicit type are not supported

the c++ codebase lets it slide

the self hosted parser correctly reports a parse error
---
 test/cases/syntax.zig | 1 -
 1 file changed, 1 deletion(-)

diff --git a/test/cases/syntax.zig b/test/cases/syntax.zig
index 6c851c0ff3..140a86d5c1 100644
--- a/test/cases/syntax.zig
+++ b/test/cases/syntax.zig
@@ -2,7 +2,6 @@
 
 const struct_trailing_comma = struct { x: i32, y: i32, };
 const struct_no_comma = struct { x: i32, y: i32 };
-const struct_no_comma_void_type = struct { x: i32, y };
 const struct_fn_no_comma = struct { fn m() void {} y: i32 };
 
 const enum_no_comma = enum { A, B };
-- 
cgit v1.2.3


From dfc3e11748615f10cf6958c61a934ef852b5aa94 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Fri, 25 May 2018 01:03:15 -0400
Subject: zig fmt: fix handling of comments at top of file

---
 std/zig/parse.zig       |  7 +++++++
 std/zig/parser_test.zig | 20 ++++++++++++++++++++
 std/zig/render.zig      | 16 +++++++++++++++-
 3 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/std/zig/parse.zig b/std/zig/parse.zig
index d60f03c55e..5d6897a0bc 100644
--- a/std/zig/parse.zig
+++ b/std/zig/parse.zig
@@ -41,6 +41,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
     }
     var tok_it = tree.tokens.iterator(0);
 
+    // skip over line comments at the top of the file
+    while (true) {
+        const next_tok = tok_it.peek() ?? break;
+        if (next_tok.id != Token.Id.LineComment) break;
+        _ = tok_it.next();
+    }
+
     try stack.append(State.TopLevel);
 
     while (true) {
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index a29b8c2547..bd1d142a23 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,23 @@
+test "zig fmt: first thing in file is line comment" {
+    try testCanonical(
+        \\// Introspection and determination of system libraries needed by zig.
+        \\
+        \\// Introspection and determination of system libraries needed by zig.
+        \\
+        \\const std = @import("std");
+        \\
+    );
+}
+
+test "zig fmt: line comment after doc comment" {
+    try testCanonical(
+        \\/// doc comment
+        \\// line comment
+        \\fn foo() void {}
+        \\
+    );
+}
+
 test "zig fmt: float literal with exponent" {
     try testCanonical(
         \\test "bit field alignment" {
diff --git a/std/zig/render.zig b/std/zig/render.zig
index 3940a61fc1..c2545bddfd 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -15,6 +15,20 @@ pub const Error = error{
 pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf(stream).Child.Error || Error)!void {
     comptime assert(@typeId(@typeOf(stream)) == builtin.TypeId.Pointer);
 
+    // render all the line comments at the beginning of the file
+    var tok_it = tree.tokens.iterator(0);
+    while (tok_it.next()) |token| {
+        if (token.id != Token.Id.LineComment) break;
+        try stream.print("{}\n", tree.tokenSlicePtr(token));
+        if (tok_it.peek()) |next_token| {
+            const loc = tree.tokenLocationPtr(token.end, next_token);
+            if (loc.line >= 2) {
+                try stream.writeByte('\n');
+            }
+        }
+    }
+
+
     var it = tree.root_node.decls.iterator(0);
     while (it.next()) |decl| {
         try renderTopLevelDecl(allocator, stream, tree, 0, decl.*);
@@ -1455,7 +1469,7 @@ fn renderDocComments(tree: &ast.Tree, stream: var, node: var, indent: usize) (@t
     const comment = node.doc_comments ?? return;
     var it = comment.lines.iterator(0);
     while (it.next()) |line_token_index| {
-        try stream.print("{}\n", tree.tokenSlice(line_token_index.*));
+        try renderToken(tree, stream, line_token_index.*, indent, Space.Newline);
         try stream.writeByteNTimes(' ', indent);
     }
 }
-- 
cgit v1.2.3


From 08f95d0c2fac127558c84f7dfb469ac43fb4f425 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Fri, 25 May 2018 01:10:54 -0400
Subject: enum fields with a type are not supported

the c++ codebase lets it slide

the self hosted parser correctly reports a parse error
---
 test/cases/syntax.zig | 1 -
 1 file changed, 1 deletion(-)

diff --git a/test/cases/syntax.zig b/test/cases/syntax.zig
index 140a86d5c1..9512834f5a 100644
--- a/test/cases/syntax.zig
+++ b/test/cases/syntax.zig
@@ -5,7 +5,6 @@ const struct_no_comma = struct { x: i32, y: i32 };
 const struct_fn_no_comma = struct { fn m() void {} y: i32 };
 
 const enum_no_comma = enum { A, B };
-const enum_no_comma_type = enum { A, B: i32 };
 
 fn container_init() void {
     const S = struct { x: i32, y: i32 };
-- 
cgit v1.2.3


From 000c01a36aeef162d743ebf4bd8ee67380f26672 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Fri, 25 May 2018 01:45:14 -0400
Subject: zig fmt: handle missing trailing comma in array literals

---
 std/zig/parser_test.zig | 20 +++++++++++++++++++
 std/zig/render.zig      | 52 ++++++++++++++++++++++++++++---------------------
 2 files changed, 50 insertions(+), 22 deletions(-)

diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index bd1d142a23..cabfb48387 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,23 @@
+test "zig fmt: first thing in file is line comment" {
+    try testTransform(
+        \\comptime {
+        \\    return []u16{'m', 's', 'y', 's', '-' // hi
+        \\   };
+        \\}
+    ,
+        \\comptime {
+        \\    return []u16{
+        \\        'm',
+        \\        's',
+        \\        'y',
+        \\        's',
+        \\        '-', // hi
+        \\    };
+        \\}
+        \\
+    );
+}
+
 test "zig fmt: first thing in file is line comment" {
     try testCanonical(
         \\// Introspection and determination of system libraries needed by zig.
diff --git a/std/zig/render.zig b/std/zig/render.zig
index c2545bddfd..87debcb5f0 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -490,13 +490,16 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                     var it = exprs.iterator(0);
                     while (it.next()) |expr| {
                         try stream.writeByteNTimes(' ', new_indent);
-                        try renderExpression(allocator, stream, tree, new_indent, expr.*, Space.None);
-
-                        const comma = tree.nextToken(expr.*.lastToken());
-                        try renderToken(tree, stream, comma, new_indent, Space.Newline); // ,
 
                         if (it.peek()) |next_expr| {
+                            try renderExpression(allocator, stream, tree, new_indent, expr.*, Space.None);
+
+                            const comma = tree.nextToken(expr.*.lastToken());
+                            try renderToken(tree, stream, comma, new_indent, Space.Newline); // ,
+
                             try renderExtraNewline(tree, stream, next_expr.*);
+                        } else {
+                            try renderTrailingComma(allocator, stream, tree, indent, expr.*, Space.Newline);
                         }
                     }
 
@@ -950,24 +953,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                 try renderExpression(allocator, stream, tree, indent, payload, Space.Space);
             }
 
-            // add a trailing comma if necessary
-            const end_token = switch_case.lastToken() + 1;
-            switch (tree.tokens.at(end_token).id) {
-                Token.Id.Comma => {
-                    try renderExpression(allocator, stream, tree, indent, switch_case.expr, Space.None);
-                    try renderToken(tree, stream, end_token, indent, space); // ,
-                },
-                Token.Id.LineComment => {
-                    try renderExpression(allocator, stream, tree, indent, switch_case.expr, Space.NoComment);
-                    try stream.write(", ");
-                    try renderToken(tree, stream, end_token, indent, space);
-                },
-                else => {
-                    try renderExpression(allocator, stream, tree, indent, switch_case.expr, Space.None);
-                    try stream.write(",\n");
-                    assert(space == Space.Newline);
-                },
-            }
+            try renderTrailingComma(allocator, stream, tree, indent, switch_case.expr, space);
         },
         ast.Node.Id.SwitchElse => {
             const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base);
@@ -1473,3 +1459,25 @@ fn renderDocComments(tree: &ast.Tree, stream: var, node: var, indent: usize) (@t
         try stream.writeByteNTimes(' ', indent);
     }
 }
+
+fn renderTrailingComma(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node,
+    space: Space) (@typeOf(stream).Child.Error || Error)!void
+{
+    const end_token = base.lastToken() + 1;
+    switch (tree.tokens.at(end_token).id) {
+        Token.Id.Comma => {
+            try renderExpression(allocator, stream, tree, indent, base, Space.None);
+            try renderToken(tree, stream, end_token, indent, space); // ,
+        },
+        Token.Id.LineComment => {
+            try renderExpression(allocator, stream, tree, indent, base, Space.NoComment);
+            try stream.write(", ");
+            try renderToken(tree, stream, end_token, indent, space);
+        },
+        else => {
+            try renderExpression(allocator, stream, tree, indent, base, Space.None);
+            try stream.write(",\n");
+            assert(space == Space.Newline);
+        },
+    }
+}
-- 
cgit v1.2.3


From 3f302f841136ebbcc4da0707fff6faaac4c03e55 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Fri, 25 May 2018 01:52:59 -0400
Subject: handle more cases of inserting trailing commas

---
 std/zig/render.zig | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/std/zig/render.zig b/std/zig/render.zig
index 87debcb5f0..61c66aaaf8 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -449,13 +449,16 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                     var it = field_inits.iterator(0);
                     while (it.next()) |field_init| {
                         try stream.writeByteNTimes(' ', new_indent);
-                        try renderExpression(allocator, stream, tree, new_indent, field_init.*, Space.None);
-
-                        const comma = tree.nextToken(field_init.*.lastToken());
-                        try renderToken(tree, stream, comma, new_indent, Space.Newline);
 
                         if (it.peek()) |next_field_init| {
+                            try renderExpression(allocator, stream, tree, new_indent, field_init.*, Space.None);
+
+                            const comma = tree.nextToken(field_init.*.lastToken());
+                            try renderToken(tree, stream, comma, new_indent, Space.Newline);
+
                             try renderExtraNewline(tree, stream, next_field_init.*);
+                        } else {
+                            try renderTrailingComma(allocator, stream, tree, new_indent, field_init.*, Space.Newline);
                         }
                     }
 
@@ -499,7 +502,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
 
                             try renderExtraNewline(tree, stream, next_expr.*);
                         } else {
-                            try renderTrailingComma(allocator, stream, tree, indent, expr.*, Space.Newline);
+                            try renderTrailingComma(allocator, stream, tree, new_indent, expr.*, Space.Newline);
                         }
                     }
 
@@ -743,11 +746,14 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
             var it = err_set_decl.decls.iterator(0);
             while (it.next()) |node| {
                 try stream.writeByteNTimes(' ', new_indent);
-                try renderExpression(allocator, stream, tree, new_indent, node.*, Space.None);
-                try renderToken(tree, stream, tree.nextToken(node.*.lastToken()), new_indent, Space.Newline); // ,
 
                 if (it.peek()) |next_node| {
+                    try renderExpression(allocator, stream, tree, new_indent, node.*, Space.None);
+                    try renderToken(tree, stream, tree.nextToken(node.*.lastToken()), new_indent, Space.Newline); // ,
+
                     try renderExtraNewline(tree, stream, next_node.*);
+                } else {
+                    try renderTrailingComma(allocator, stream, tree, new_indent, node.*, Space.Newline);
                 }
             }
 
-- 
cgit v1.2.3


From 56cb7f1740bf369b7cd2cac29db559c5c6bccc22 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Fri, 25 May 2018 02:08:43 -0400
Subject: update json_test to be compliant with zig source encoding

See #663
---
 std/json_test.zig | 60 ++++++++++++++-----------------------------------------
 1 file changed, 15 insertions(+), 45 deletions(-)

diff --git a/std/json_test.zig b/std/json_test.zig
index 90a2ddbd50..60a5d1288c 100644
--- a/std/json_test.zig
+++ b/std/json_test.zig
@@ -431,15 +431,11 @@ test "y_string_two-byte-utf-8" {
 }
 
 test "y_string_u+2028_line_sep" {
-    ok(
-        \\["
"]
-    );
+    ok("[\"\xe2\x80\xa8\"]");
 }
 
 test "y_string_u+2029_par_sep" {
-    ok(
-        \\["
"]
-    );
+    ok("[\"\xe2\x80\xa9\"]");
 }
 
 test "y_string_uescaped_newline" {
@@ -455,9 +451,7 @@ test "y_string_uEscape" {
 }
 
 test "y_string_unescaped_char_delete" {
-    ok(
-        \\[""]
-    );
+    ok("[\"\x7f\"]");
 }
 
 test "y_string_unicode_2" {
@@ -527,9 +521,7 @@ test "y_string_utf8" {
 }
 
 test "y_string_with_del_character" {
-    ok(
-        \\["aa"]
-    );
+    ok("[\"a\x7fa\"]");
 }
 
 test "y_structure_lonely_false" {
@@ -718,9 +710,7 @@ test "n_array_number_and_several_commas" {
 }
 
 test "n_array_spaces_vertical_tab_formfeed" {
-    err(
-        \\["a"\f]
-    );
+    err("[\"\x0aa\"\\f]");
 }
 
 test "n_array_star_inside" {
@@ -774,9 +764,7 @@ test "n_incomplete_true" {
 }
 
 test "n_multidigit_number_then_00" {
-    err(
-        \\123
-    );
+    err("123\x00");
 }
 
 test "n_number_0.1.2" {
@@ -1309,9 +1297,7 @@ test "n_string_escaped_ctrl_char_tab" {
 }
 
 test "n_string_escaped_emoji" {
-    err(
-        \\["\🌀"]
-    );
+    err("[\"\x5c\xc3\xb0\xc2\x9f\xc2\x8c\xc2\x80\"]");
 }
 
 test "n_string_escape_x" {
@@ -1357,9 +1343,7 @@ test "n_string_invalid_unicode_escape" {
 }
 
 test "n_string_invalid_utf8_after_escape" {
-    err(
-        \\["\å"]
-    );
+    err("[\"\\\x75\xc3\xa5\"]");
 }
 
 test "n_string_invalid-utf-8-in-escape" {
@@ -1405,9 +1389,7 @@ test "n_string_start_escape_unclosed" {
 }
 
 test "n_string_unescaped_crtl_char" {
-    err(
-        \\["aa"]
-    );
+    err("[\"a\x00a\"]");
 }
 
 test "n_string_unescaped_newline" {
@@ -1418,9 +1400,7 @@ test "n_string_unescaped_newline" {
 }
 
 test "n_string_unescaped_tab" {
-    err(
-        \\["	"]
-    );
+    err("[\"\t\"]");
 }
 
 test "n_string_unicode_CapitalU" {
@@ -1532,9 +1512,7 @@ test "n_structure_no_data" {
 }
 
 test "n_structure_null-byte-outside-string" {
-    err(
-        \\[]
-    );
+    err("[\x00]");
 }
 
 test "n_structure_number_with_trailing_garbage" {
@@ -1718,9 +1696,7 @@ test "n_structure_UTF8_BOM_no_data" {
 }
 
 test "n_structure_whitespace_formfeed" {
-    err(
-        \\[]
-    );
+    err("[\x0c]");
 }
 
 test "n_structure_whitespace_U+2060_word_joiner" {
@@ -1900,21 +1876,15 @@ test "i_string_truncated-utf-8" {
 }
 
 test "i_string_utf16BE_no_BOM" {
-    any(
-        \\["é"]
-    );
+    any("\x00\x5b\x00\x22\x00\xc3\xa9\x00\x22\x00\x5d");
 }
 
 test "i_string_utf16LE_no_BOM" {
-    any(
-        \\["é"]
-    );
+    any("\x5b\x00\x22\x00\xc3\xa9\x00\x22\x00\x5d\x00");
 }
 
 test "i_string_UTF-16LE_with_BOM" {
-    any(
-        \\ÿþ["é"]
-    );
+    any("\xc3\xbf\xc3\xbe\x5b\x00\x22\x00\xc3\xa9\x00\x22\x00\x5d\x00");
 }
 
 test "i_string_UTF-8_invalid_sequence" {
-- 
cgit v1.2.3


From a630d3e851a62c0e8971cbd2183e215606f35ab1 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Fri, 25 May 2018 02:19:53 -0400
Subject: zig fmt: fix rendering of align keyword of slice type

---
 std/zig/parser_test.zig |  9 +++++++++
 std/zig/render.zig      | 12 ++++++------
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index cabfb48387..ada132e775 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,12 @@
+test "zig fmt: slice align" {
+    try testCanonical(
+        \\const A = struct {
+        \\    items: []align(A) T,
+        \\};
+        \\
+    );
+}
+
 test "zig fmt: first thing in file is line comment" {
     try testTransform(
         \\comptime {
diff --git a/std/zig/render.zig b/std/zig/render.zig
index 61c66aaaf8..215e57ceb8 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -268,10 +268,10 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                 ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| {
                     try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.None); // &
                     if (addr_of_info.align_info) |align_info| {
-                        const align_token = tree.nextToken(prefix_op_node.op_token);
-                        try renderToken(tree, stream, align_token, indent, Space.None); // align
-
                         const lparen_token = tree.prevToken(align_info.node.firstToken());
+                        const align_token = tree.prevToken(lparen_token);
+
+                        try renderToken(tree, stream, align_token, indent, Space.None); // align
                         try renderToken(tree, stream, lparen_token, indent, Space.None); // (
 
                         try renderExpression(allocator, stream, tree, indent, align_info.node, Space.None);
@@ -305,10 +305,10 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                     try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, Space.None); // ]
 
                     if (addr_of_info.align_info) |align_info| {
-                        const align_token = tree.nextToken(prefix_op_node.op_token);
-                        try renderToken(tree, stream, align_token, indent, Space.None); // align
-
                         const lparen_token = tree.prevToken(align_info.node.firstToken());
+                        const align_token = tree.prevToken(lparen_token);
+
+                        try renderToken(tree, stream, align_token, indent, Space.None); // align
                         try renderToken(tree, stream, lparen_token, indent, Space.None); // (
 
                         try renderExpression(allocator, stream, tree, indent, align_info.node, Space.None);
-- 
cgit v1.2.3


From 4405897cbd9105fddb512545594f336b597d91e9 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Fri, 25 May 2018 20:34:53 -0400
Subject: zig fmt: support trailing comma on switch case items

---
 std/zig/parse.zig       | 25 +++++++++++++++++--------
 std/zig/parser_test.zig | 35 ++++++++++++++++++++++++++++-------
 std/zig/render.zig      | 44 ++++++++++++++++++++++++++++++++++----------
 3 files changed, 79 insertions(+), 25 deletions(-)

diff --git a/std/zig/parse.zig b/std/zig/parse.zig
index 5d6897a0bc..a6eb22a2d0 100644
--- a/std/zig/parse.zig
+++ b/std/zig/parse.zig
@@ -1325,21 +1325,30 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
                     continue;
                 } else {
                     prevToken(&tok_it, &tree);
-                    try stack.append(State{ .SwitchCaseItem = switch_case });
+                    stack.append(State{ .SwitchCaseItemCommaOrEnd = switch_case }) catch unreachable;
+                    try stack.append(State{ .RangeExpressionBegin = OptionalCtx{ .Required = try switch_case.items.addOne() } });
                     continue;
                 }
             },
-            State.SwitchCaseItem => |node| {
-                stack.append(State{ .SwitchCaseItemCommaOrEnd = node }) catch unreachable;
-                try stack.append(State{ .RangeExpressionBegin = OptionalCtx{ .Required = try node.items.addOne() } });
+            State.SwitchCaseItemOrEnd => |switch_case| {
+                const token = nextToken(&tok_it, &tree);
+                if (token.ptr.id == Token.Id.EqualAngleBracketRight) {
+                    switch_case.arrow_token = token.index;
+                    continue;
+                } else {
+                    prevToken(&tok_it, &tree);
+                    stack.append(State{ .SwitchCaseItemCommaOrEnd = switch_case }) catch unreachable;
+                    try stack.append(State{ .RangeExpressionBegin = OptionalCtx{ .Required = try switch_case.items.addOne() } });
+                    continue;
+                }
             },
-            State.SwitchCaseItemCommaOrEnd => |node| {
+            State.SwitchCaseItemCommaOrEnd => |switch_case| {
                 switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.EqualAngleBracketRight)) {
                     ExpectCommaOrEndResult.end_token => |end_token| {
                         if (end_token) |t| {
-                            node.arrow_token = t;
+                            switch_case.arrow_token = t;
                         } else {
-                            stack.append(State{ .SwitchCaseItem = node }) catch unreachable;
+                            stack.append(State{ .SwitchCaseItemOrEnd = switch_case }) catch unreachable;
                         }
                         continue;
                     },
@@ -2828,8 +2837,8 @@ const State = union(enum) {
     SwitchCaseOrEnd: ListSave(ast.Node.Switch.CaseList),
     SwitchCaseCommaOrEnd: ListSave(ast.Node.Switch.CaseList),
     SwitchCaseFirstItem: &ast.Node.SwitchCase,
-    SwitchCaseItem: &ast.Node.SwitchCase,
     SwitchCaseItemCommaOrEnd: &ast.Node.SwitchCase,
+    SwitchCaseItemOrEnd: &ast.Node.SwitchCase,
 
     SuspendBody: &ast.Node.Suspend,
     AsyncAllocator: &ast.Node.AsyncAttribute,
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index ada132e775..9d5e64a66f 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,27 @@
+test "zig fmt: switch cases trailing comma" {
+    try testTransform(
+        \\fn switch_cases(x: i32) void {
+        \\    switch (x) {
+        \\        1,2,3 => {},
+        \\        4,5, => {},
+        \\        6...8, => {},
+        \\        else => {},
+        \\    }
+        \\}
+    ,
+        \\fn switch_cases(x: i32) void {
+        \\    switch (x) {
+        \\        1, 2, 3 => {},
+        \\        4,
+        \\        5, => {},
+        \\        6 ... 8 => {},
+        \\        else => {},
+        \\    }
+        \\}
+        \\
+    );
+}
+
 test "zig fmt: slice align" {
     try testCanonical(
         \\const A = struct {
@@ -7,7 +31,7 @@ test "zig fmt: slice align" {
     );
 }
 
-test "zig fmt: first thing in file is line comment" {
+test "zig fmt: add trailing comma to array literal" {
     try testTransform(
         \\comptime {
         \\    return []u16{'m', 's', 'y', 's', '-' // hi
@@ -217,13 +241,11 @@ test "zig fmt: add comma on last switch prong" {
         \\test "aoeu" {
         \\    switch (self.init_arg_expr) {
         \\        InitArg.Type => |t| {},
-        \\        InitArg.None,
-        \\        InitArg.Enum => {},
+        \\        InitArg.None, InitArg.Enum => {},
         \\    }
         \\    switch (self.init_arg_expr) {
         \\        InitArg.Type => |t| {},
-        \\        InitArg.None,
-        \\        InitArg.Enum => {}, //line comment
+        \\        InitArg.None, InitArg.Enum => {}, //line comment
         \\    }
         \\}
         \\
@@ -1003,8 +1025,7 @@ test "zig fmt: switch" {
         \\    switch (0) {
         \\        0 => {},
         \\        1 => unreachable,
-        \\        2,
-        \\        3 => {},
+        \\        2, 3 => {},
         \\        4 ... 7 => {},
         \\        1 + 4 * 3 + 22 => {},
         \\        else => {
diff --git a/std/zig/render.zig b/std/zig/render.zig
index 215e57ceb8..4691d836c3 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -939,17 +939,41 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
         ast.Node.Id.SwitchCase => {
             const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
 
-            var it = switch_case.items.iterator(0);
-            while (it.next()) |node| {
-                if (it.peek()) |next_node| {
-                    try renderExpression(allocator, stream, tree, indent, node.*, Space.None);
+            assert(switch_case.items.len != 0);
+            const src_has_trailing_comma = blk: {
+                const last_node = switch_case.items.at(switch_case.items.len - 1).*;
+                const maybe_comma = tree.nextToken(last_node.lastToken());
+                break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma;
+            };
 
-                    const comma_token = tree.nextToken(node.*.lastToken());
-                    try renderToken(tree, stream, comma_token, indent, Space.Newline); // ,
-                    try renderExtraNewline(tree, stream, next_node.*);
-                    try stream.writeByteNTimes(' ', indent);
-                } else {
-                    try renderExpression(allocator, stream, tree, indent, node.*, Space.Space);
+            if (switch_case.items.len == 1 or !src_has_trailing_comma) {
+                var it = switch_case.items.iterator(0);
+                while (it.next()) |node| {
+                    if (it.peek()) |next_node| {
+                        try renderExpression(allocator, stream, tree, indent, node.*, Space.None);
+
+                        const comma_token = tree.nextToken(node.*.lastToken());
+                        try renderToken(tree, stream, comma_token, indent, Space.Space); // ,
+                        try renderExtraNewline(tree, stream, next_node.*);
+                    } else {
+                        try renderExpression(allocator, stream, tree, indent, node.*, Space.Space);
+                    }
+                }
+            } else {
+                var it = switch_case.items.iterator(0);
+                while (true) {
+                    const node = ??it.next();
+                    if (it.peek()) |next_node| {
+                        try renderExpression(allocator, stream, tree, indent, node.*, Space.None);
+
+                        const comma_token = tree.nextToken(node.*.lastToken());
+                        try renderToken(tree, stream, comma_token, indent, Space.Newline); // ,
+                        try renderExtraNewline(tree, stream, next_node.*);
+                        try stream.writeByteNTimes(' ', indent);
+                    } else {
+                        try renderTrailingComma(allocator, stream, tree, indent, node.*, Space.Space);
+                        break;
+                    }
                 }
             }
 
-- 
cgit v1.2.3


From c029f4bfc47b5d6d825f7ae7a3f224e9e9d6ce0b Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Fri, 25 May 2018 20:41:14 -0400
Subject: trailing comma after var args is not supported

---
 test/cases/syntax.zig | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/test/cases/syntax.zig b/test/cases/syntax.zig
index 9512834f5a..20fb6cd203 100644
--- a/test/cases/syntax.zig
+++ b/test/cases/syntax.zig
@@ -34,16 +34,11 @@ fn switch_prongs(x: i32) void {
 
 const fn_no_comma = fn(i32, i32)void;
 const fn_trailing_comma = fn(i32, i32,)void;
-const fn_vararg_trailing_comma = fn(i32, i32, ...,)void;
 
 fn fn_calls() void {
     fn add(x: i32, y: i32,) i32 { x + y };
     _ = add(1, 2);
     _ = add(1, 2,);
-
-    fn swallow(x: ...,) void {};
-    _ = swallow(1,2,3,);
-    _ = swallow();
 }
 
 fn asm_lists() void {
-- 
cgit v1.2.3


From 85ca611af1d7401dd336f4655a0ab15640cc8424 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Sat, 26 May 2018 15:37:47 -0400
Subject: zig fmt: put nested struct inits on newlines

See #1003
---
 std/zig/parser_test.zig |  9 +++++++++
 std/zig/render.zig      | 12 +++++++++---
 2 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 9d5e64a66f..d321f78668 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,12 @@
+test "zig fmt: nested struct literal with one item" {
+    try testCanonical(
+        \\const a = foo{
+        \\    .item = bar{ .a = b },
+        \\};
+        \\
+    );
+}
+
 test "zig fmt: switch cases trailing comma" {
     try testTransform(
         \\fn switch_cases(x: i32) void {
diff --git a/std/zig/render.zig b/std/zig/render.zig
index 4691d836c3..04939324a8 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -431,12 +431,18 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                         return;
                     }
 
-                    if (field_inits.len == 1) {
-                        const field_init = field_inits.at(0).*;
+                    if (field_inits.len == 1) blk: {
+                        const field_init = ??field_inits.at(0).*.cast(ast.Node.FieldInitializer);
+
+                        if (field_init.expr.cast(ast.Node.SuffixOp)) |nested_suffix_op| {
+                            if (nested_suffix_op.op == ast.Node.SuffixOp.Op.StructInitializer) {
+                                break :blk;
+                            }
+                        }
 
                         try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None);
                         try renderToken(tree, stream, lbrace, indent, Space.Space);
-                        try renderExpression(allocator, stream, tree, indent, field_init, Space.Space);
+                        try renderExpression(allocator, stream, tree, indent, &field_init.base, Space.Space);
                         try renderToken(tree, stream, suffix_op.rtoken, indent, space);
                         return;
                     }
-- 
cgit v1.2.3


From 0ab888c639dffcc48bc08a6ff186aa3da0ecbf74 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Sat, 26 May 2018 16:37:55 -0400
Subject: zig fmt: parse extra comma in asm expressions

---
 std/zig/ast.zig         |  2 +-
 std/zig/parse.zig       |  8 +++--
 std/zig/parser_test.zig | 44 ++++++++++++++++++++++-
 std/zig/render.zig      | 95 ++++++++++++++++++++++++++++++++++++-------------
 4 files changed, 120 insertions(+), 29 deletions(-)

diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 6c848b4a54..addd3a37e8 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -2071,7 +2071,7 @@ pub const Node = struct {
 
         const OutputList = SegmentedList(&AsmOutput, 2);
         const InputList = SegmentedList(&AsmInput, 2);
-        const ClobberList = SegmentedList(&Node, 2);
+        const ClobberList = SegmentedList(TokenIndex, 2);
 
         pub fn iterate(self: &Asm, index: usize) ?&Node {
             var i = index;
diff --git a/std/zig/parse.zig b/std/zig/parse.zig
index a6eb22a2d0..1bc64c3ddb 100644
--- a/std/zig/parse.zig
+++ b/std/zig/parse.zig
@@ -1153,9 +1153,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
                 continue;
             },
             State.AsmClobberItems => |items| {
-                stack.append(State{ .AsmClobberItems = items }) catch unreachable;
-                try stack.append(State{ .IfToken = Token.Id.Comma });
-                try stack.append(State{ .StringLiteral = OptionalCtx{ .Required = try items.addOne() } });
+                while (eatToken(&tok_it, &tree, Token.Id.StringLiteral)) |strlit| {
+                    try items.push(strlit);
+                    if (eatToken(&tok_it, &tree, Token.Id.Comma) == null)
+                        break;
+                }
                 continue;
             },
 
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index d321f78668..ab15ca3a58 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,44 @@
+test "zig fmt: simple asm" {
+    try testTransform(
+        \\comptime {
+        \\    asm volatile (
+        \\        \\.globl aoeu;
+        \\        \\.type aoeu, @function;
+        \\        \\.set aoeu, derp;
+        \\    );
+        \\
+        \\    asm ("not real assembly"
+        \\        :[a] "x" (x),);
+        \\    asm ("not real assembly"
+        \\        :[a] "x" (->i32),:[a] "x" (1),);
+        \\    asm ("still not real assembly"
+        \\        :::"a","b",);
+        \\}
+    ,
+        \\comptime {
+        \\    asm volatile (
+        \\        \\.globl aoeu;
+        \\        \\.type aoeu, @function;
+        \\        \\.set aoeu, derp;
+        \\    );
+        \\
+        \\    asm ("not real assembly"
+        \\        : [a] "x" (x)
+        \\    );
+        \\    asm ("not real assembly"
+        \\        : [a] "x" (-> i32)
+        \\        : [a] "x" (1)
+        \\    );
+        \\    asm ("still not real assembly"
+        \\        :
+        \\        :
+        \\        : "a", "b"
+        \\    );
+        \\}
+        \\
+    );
+}
+
 test "zig fmt: nested struct literal with one item" {
     try testCanonical(
         \\const a = foo{
@@ -1295,7 +1336,8 @@ test "zig fmt: inline asm" {
         \\        : [ret] "={rax}" (-> usize)
         \\        : [number] "{rax}" (number),
         \\          [arg1] "{rdi}" (arg1)
-        \\        : "rcx", "r11");
+        \\        : "rcx", "r11"
+        \\    );
         \\}
         \\
     );
diff --git a/std/zig/render.zig b/std/zig/render.zig
index 04939324a8..c37f6e37c7 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -1203,19 +1203,35 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                 try renderToken(tree, stream, tree.nextToken(asm_node.asm_token), indent, Space.None); // (
             }
 
+            if (asm_node.outputs.len == 0 and asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {
+                try renderExpression(allocator, stream, tree, indent, asm_node.template, Space.None);
+                try renderToken(tree, stream, asm_node.rparen, indent, space);
+                return;
+            }
+
             try renderExpression(allocator, stream, tree, indent, asm_node.template, Space.Newline);
+
             const indent_once = indent + indent_delta;
             try stream.writeByteNTimes(' ', indent_once);
-            try stream.print(": ");
+
+            const colon1 = tree.nextToken(asm_node.template.lastToken());
             const indent_extra = indent_once + 2;
 
-            {
+            const colon2 = if (asm_node.outputs.len == 0) blk: {
+                try renderToken(tree, stream, colon1, indent, Space.Newline); // :
+                try stream.writeByteNTimes(' ', indent_once);
+
+                break :blk tree.nextToken(colon1);
+            } else blk: {
+                try renderToken(tree, stream, colon1, indent, Space.Space); // :
+
                 var it = asm_node.outputs.iterator(0);
-                while (it.next()) |asm_output| {
+                while (true) {
+                    const asm_output = ??it.next();
                     const node = &(asm_output.*).base;
-                    try renderExpression(allocator, stream, tree, indent_extra, node, Space.None);
 
                     if (it.peek()) |next_asm_output| {
+                        try renderExpression(allocator, stream, tree, indent_extra, node, Space.None);
                         const next_node = &(next_asm_output.*).base;
 
                         const comma = tree.prevToken(next_asm_output.*.firstToken());
@@ -1223,21 +1239,38 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                         try renderExtraNewline(tree, stream, next_node);
 
                         try stream.writeByteNTimes(' ', indent_extra);
+                    } else if (asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {
+                        try renderExpression(allocator, stream, tree, indent_extra, node, Space.Newline);
+                        try stream.writeByteNTimes(' ', indent);
+                        try renderToken(tree, stream, asm_node.rparen, indent, space);
+                        return;
+                    } else {
+                        try renderExpression(allocator, stream, tree, indent_extra, node, Space.Newline);
+                        try stream.writeByteNTimes(' ', indent_once);
+                        const comma_or_colon = tree.nextToken(node.lastToken());
+                        break :blk switch (tree.tokens.at(comma_or_colon).id) {
+                            Token.Id.Comma => tree.nextToken(comma_or_colon),
+                            else => comma_or_colon,
+                        };
                     }
                 }
-            }
+            };
 
-            try stream.write("\n");
-            try stream.writeByteNTimes(' ', indent_once);
-            try stream.write(": ");
+            const colon3 = if (asm_node.inputs.len == 0) blk: {
+                try renderToken(tree, stream, colon2, indent, Space.Newline); // :
+                try stream.writeByteNTimes(' ', indent_once);
+
+                break :blk tree.nextToken(colon2);
+            } else blk: {
+                try renderToken(tree, stream, colon2, indent, Space.Space); // :
 
-            {
                 var it = asm_node.inputs.iterator(0);
-                while (it.next()) |asm_input| {
+                while (true) {
+                    const asm_input = ??it.next();
                     const node = &(asm_input.*).base;
-                    try renderExpression(allocator, stream, tree, indent_extra, node, Space.None);
 
                     if (it.peek()) |next_asm_input| {
+                        try renderExpression(allocator, stream, tree, indent_extra, node, Space.None);
                         const next_node = &(next_asm_input.*).base;
 
                         const comma = tree.prevToken(next_asm_input.*.firstToken());
@@ -1245,26 +1278,40 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                         try renderExtraNewline(tree, stream, next_node);
 
                         try stream.writeByteNTimes(' ', indent_extra);
+                    } else if (asm_node.clobbers.len == 0) {
+                        try renderExpression(allocator, stream, tree, indent_extra, node, Space.Newline);
+                        try stream.writeByteNTimes(' ', indent);
+                        try renderToken(tree, stream, asm_node.rparen, indent, space); // )
+                        return;
+                    } else {
+                        try renderExpression(allocator, stream, tree, indent_extra, node, Space.Newline);
+                        try stream.writeByteNTimes(' ', indent_once);
+                        const comma_or_colon = tree.nextToken(node.lastToken());
+                        break :blk switch (tree.tokens.at(comma_or_colon).id) {
+                            Token.Id.Comma => tree.nextToken(comma_or_colon),
+                            else => comma_or_colon,
+                        };
                     }
                 }
-            }
+            };
 
-            try stream.write("\n");
-            try stream.writeByteNTimes(' ', indent_once);
-            try stream.write(": ");
+            try renderToken(tree, stream, colon3, indent, Space.Space); // :
 
-            {
-                var it = asm_node.clobbers.iterator(0);
-                while (it.next()) |node| {
-                    try renderExpression(allocator, stream, tree, indent_once, node.*, Space.None);
+            var it = asm_node.clobbers.iterator(0);
+            while (true) {
+                const clobber_token = ??it.next();
 
-                    if (it.peek() != null) {
-                        try stream.write(", ");
-                    }
+                if (it.peek() == null) {
+                    try renderToken(tree, stream, clobber_token.*, indent_once, Space.Newline);
+                    try stream.writeByteNTimes(' ', indent);
+                    try renderToken(tree, stream, asm_node.rparen, indent, space);
+                    return;
+                } else {
+                    try renderToken(tree, stream, clobber_token.*, indent_once, Space.None);
+                    const comma = tree.nextToken(clobber_token.*);
+                    try renderToken(tree, stream, comma, indent_once, Space.Space); // ,
                 }
             }
-
-            try renderToken(tree, stream, asm_node.rparen, indent, space);
         },
 
         ast.Node.Id.AsmInput => {
-- 
cgit v1.2.3


From 0bef1f98247160cbd5101c763404c76ae287b2bd Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Sat, 26 May 2018 16:43:33 -0400
Subject: zig fmt: fix rendering of struct with no trailing comma on last field

---
 std/zig/parser_test.zig | 15 +++++++++++++++
 std/zig/render.zig      |  3 +--
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index ab15ca3a58..e76d588088 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,18 @@
+test "zig fmt: no trailing comma on struct decl" {
+    try testTransform(
+        \\const RoundParam = struct {
+        \\    k: usize, s: u32, t: u32
+        \\};
+    ,
+        \\const RoundParam = struct {
+        \\    k: usize,
+        \\    s: u32,
+        \\    t: u32,
+        \\};
+        \\
+    );
+}
+
 test "zig fmt: simple asm" {
     try testTransform(
         \\comptime {
diff --git a/std/zig/render.zig b/std/zig/render.zig
index c37f6e37c7..dea06e3a7d 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -102,8 +102,7 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i
             }
             try renderToken(tree, stream, field.name_token, indent, Space.None); // name
             try renderToken(tree, stream, tree.nextToken(field.name_token), indent, Space.Space); // :
-            try renderExpression(allocator, stream, tree, indent, field.type_expr, Space.None); // type
-            try renderToken(tree, stream, tree.nextToken(field.lastToken()), indent, Space.Newline); // ,
+            try renderTrailingComma(allocator, stream, tree, indent, field.type_expr, Space.Newline); // type,
         },
 
         ast.Node.Id.UnionTag => {
-- 
cgit v1.2.3


From 7e900d28be00e03eee2ec3703c0c45247b4748b1 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Sat, 26 May 2018 18:10:06 -0400
Subject: zig fmt: no space on switch range operator

---
 std/zig/parser_test.zig | 6 +++---
 std/zig/render.zig      | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index e76d588088..dfef594df7 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -69,7 +69,7 @@ test "zig fmt: switch cases trailing comma" {
         \\    switch (x) {
         \\        1,2,3 => {},
         \\        4,5, => {},
-        \\        6...8, => {},
+        \\        6... 8, => {},
         \\        else => {},
         \\    }
         \\}
@@ -79,7 +79,7 @@ test "zig fmt: switch cases trailing comma" {
         \\        1, 2, 3 => {},
         \\        4,
         \\        5, => {},
-        \\        6 ... 8 => {},
+        \\        6...8 => {},
         \\        else => {},
         \\    }
         \\}
@@ -1091,7 +1091,7 @@ test "zig fmt: switch" {
         \\        0 => {},
         \\        1 => unreachable,
         \\        2, 3 => {},
-        \\        4 ... 7 => {},
+        \\        4...7 => {},
         \\        1 + 4 * 3 + 22 => {},
         \\        else => {
         \\            const a = 1;
diff --git a/std/zig/render.zig b/std/zig/render.zig
index dea06e3a7d..67ec8f7632 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -244,7 +244,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
 
             const op_token = tree.tokens.at(infix_op_node.op_token);
             const op_space = switch (infix_op_node.op) {
-                ast.Node.InfixOp.Op.Period, ast.Node.InfixOp.Op.ErrorUnion => Space.None,
+                ast.Node.InfixOp.Op.Period, ast.Node.InfixOp.Op.ErrorUnion, ast.Node.InfixOp.Op.Range => Space.None,
                 else => Space.Space,
             };
             try renderExpression(allocator, stream, tree, indent, infix_op_node.lhs, op_space);
-- 
cgit v1.2.3


From b8d4e05361d6a01ae1c0bf931e27e2bfdb25551d Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Sat, 26 May 2018 18:29:14 -0400
Subject: zig fmt: handle empty block with comment inside

---
 std/zig/parser_test.zig | 11 +++++++++++
 std/zig/render.zig      |  2 +-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index dfef594df7..c115c5848f 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,14 @@
+test "zig fmt: empty block with only comment" {
+    try testCanonical(
+        \\comptime {
+        \\    {
+        \\        // comment
+        \\    }
+        \\}
+        \\
+    );
+}
+
 test "zig fmt: no trailing comma on struct decl" {
     try testTransform(
         \\const RoundParam = struct {
diff --git a/std/zig/render.zig b/std/zig/render.zig
index 67ec8f7632..90acb0e412 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -1519,7 +1519,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent
 
                     const after_comment_token = tree.tokens.at(token_index + offset);
                     const next_line_indent = switch (after_comment_token.id) {
-                        Token.Id.RParen, Token.Id.RBrace, Token.Id.RBracket => indent,
+                        Token.Id.RParen, Token.Id.RBrace, Token.Id.RBracket => indent - indent_delta,
                         else => indent,
                     };
                     try stream.writeByteNTimes(' ', next_line_indent);
-- 
cgit v1.2.3


From cabf7fa93b7250a69a3cfad417537c2a46414779 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Sat, 26 May 2018 18:44:10 -0400
Subject: zig fmt: fn calls with trailing comma with params on new lines

---
 std/zig/parser_test.zig | 13 +++++++++++++
 std/zig/render.zig      | 38 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 49 insertions(+), 2 deletions(-)

diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index c115c5848f..4ce355dc87 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,16 @@
+test "zig fmt: trailing comma on fn call" {
+    try testCanonical(
+        \\comptime {
+        \\    var module = try Module.create(
+        \\        allocator,
+        \\        zig_lib_dir,
+        \\        full_cache_dir,
+        \\    );
+        \\}
+        \\
+    );
+}
+
 test "zig fmt: empty block with only comment" {
     try testCanonical(
         \\comptime {
diff --git a/std/zig/render.zig b/std/zig/render.zig
index 90acb0e412..f068e652bb 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -374,7 +374,42 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                     try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None);
 
                     const lparen = tree.nextToken(suffix_op.lhs.lastToken());
-                    try renderToken(tree, stream, lparen, indent, Space.None);
+
+                    if (call_info.params.len == 0) {
+                        try renderToken(tree, stream, lparen, indent, Space.None);
+                        try renderToken(tree, stream, suffix_op.rtoken, indent, space);
+                        return;
+                    }
+
+                    const src_has_trailing_comma = blk: {
+                        const maybe_comma = tree.prevToken(suffix_op.rtoken);
+                        break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma;
+                    };
+
+                    if (src_has_trailing_comma) {
+                        const new_indent = indent + indent_delta;
+                        try renderToken(tree, stream, lparen, new_indent, Space.Newline);
+
+                        var it = call_info.params.iterator(0);
+                        while (true) {
+                            const param_node = ??it.next();
+                            try stream.writeByteNTimes(' ', new_indent);
+
+                            if (it.peek()) |next_node| {
+                                try renderExpression(allocator, stream, tree, new_indent, param_node.*, Space.None);
+                                const comma = tree.nextToken(param_node.*.lastToken());
+                                try renderToken(tree, stream, comma, new_indent, Space.Newline); // ,
+                                try renderExtraNewline(tree, stream, next_node.*);
+                            } else {
+                                try renderTrailingComma(allocator, stream, tree, new_indent, param_node.*, Space.Newline);
+                                try stream.writeByteNTimes(' ', indent);
+                                try renderToken(tree, stream, suffix_op.rtoken, indent, space);
+                                return;
+                            }
+                        }
+                    }
+
+                    try renderToken(tree, stream, lparen, indent, Space.None); // (
 
                     var it = call_info.params.iterator(0);
                     while (it.next()) |param_node| {
@@ -385,7 +420,6 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                             try renderToken(tree, stream, comma, indent, Space.Space);
                         }
                     }
-
                     try renderToken(tree, stream, suffix_op.rtoken, indent, space);
                 },
 
-- 
cgit v1.2.3


From 349365d9a483bc6be8c1677149ec1a31a789e4b2 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Sat, 26 May 2018 19:32:28 -0400
Subject: zig fmt: better multiline string formatting

---
 std/zig/parser_test.zig | 29 +++++++++++++++++++++++++++++
 std/zig/render.zig      | 36 ++++++++++++++++++++++++++++++------
 std/zig/tokenizer.zig   | 12 ------------
 3 files changed, 59 insertions(+), 18 deletions(-)

diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 4ce355dc87..152056c6b4 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,32 @@
+test "zig fmt: multiline string with backslash at end of line" {
+    try testCanonical(
+        \\comptime {
+        \\    err(
+        \\        \\\
+        \\    );
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: multiline string parameter in fn call with trailing comma" {
+    try testCanonical(
+        \\fn foo() void {
+        \\    try stdout.print(
+        \\        \\ZIG_CMAKE_BINARY_DIR {}
+        \\        \\ZIG_C_HEADER_FILES   {}
+        \\        \\ZIG_DIA_GUIDS_LIB    {}
+        \\        \\
+        \\    ,
+        \\        std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),
+        \\        std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),
+        \\        std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),
+        \\    );
+        \\}
+        \\
+    );
+}
+
 test "zig fmt: trailing comma on fn call" {
     try testCanonical(
         \\comptime {
diff --git a/std/zig/render.zig b/std/zig/render.zig
index f068e652bb..409828e070 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -393,15 +393,21 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                         var it = call_info.params.iterator(0);
                         while (true) {
                             const param_node = ??it.next();
-                            try stream.writeByteNTimes(' ', new_indent);
+
+                            const param_node_new_indent = if (param_node.*.id == ast.Node.Id.MultilineStringLiteral) blk: {
+                                break :blk indent;
+                            } else blk: {
+                                try stream.writeByteNTimes(' ', new_indent);
+                                break :blk new_indent;
+                            };
 
                             if (it.peek()) |next_node| {
-                                try renderExpression(allocator, stream, tree, new_indent, param_node.*, Space.None);
+                                try renderExpression(allocator, stream, tree, param_node_new_indent, param_node.*, Space.None);
                                 const comma = tree.nextToken(param_node.*.lastToken());
                                 try renderToken(tree, stream, comma, new_indent, Space.Newline); // ,
                                 try renderExtraNewline(tree, stream, next_node.*);
                             } else {
-                                try renderTrailingComma(allocator, stream, tree, new_indent, param_node.*, Space.Newline);
+                                try renderTrailingComma(allocator, stream, tree, param_node_new_indent, param_node.*, Space.Newline);
                                 try stream.writeByteNTimes(' ', indent);
                                 try renderToken(tree, stream, suffix_op.rtoken, indent, space);
                                 return;
@@ -1502,7 +1508,13 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent
     if (next_token.id != Token.Id.LineComment) {
         switch (space) {
             Space.None, Space.NoNewline, Space.NoIndent => return,
-            Space.Newline => return stream.write("\n"),
+            Space.Newline => {
+                if (next_token.id == Token.Id.MultilineStringLiteralLine) {
+                    return;
+                } else {
+                    return stream.write("\n");
+                }
+            },
             Space.Space => return stream.writeByte(' '),
             Space.NoComment => unreachable,
         }
@@ -1526,7 +1538,13 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent
                     };
                     try stream.writeByteNTimes(' ', next_line_indent);
                 },
-                Space.Newline, Space.NoIndent => try stream.write("\n"),
+                Space.Newline, Space.NoIndent => {
+                    if (next_token.id == Token.Id.MultilineStringLiteralLine) {
+                        return;
+                    } else {
+                        return stream.write("\n");
+                    }
+                },
                 Space.NoNewline => {},
                 Space.NoComment => unreachable,
             }
@@ -1547,7 +1565,13 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent
         next_token = tree.tokens.at(token_index + offset);
         if (next_token.id != Token.Id.LineComment) {
             switch (space) {
-                Space.Newline, Space.NoIndent => try stream.writeByte('\n'),
+                Space.Newline, Space.NoIndent => {
+                    if (next_token.id == Token.Id.MultilineStringLiteralLine) {
+                        return;
+                    } else {
+                        return stream.write("\n");
+                    }
+                },
                 Space.None, Space.Space => {
                     try stream.writeByte('\n');
 
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index f4cd847dff..b90a40108f 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -217,7 +217,6 @@ pub const Tokenizer = struct {
         StringLiteral,
         StringLiteralBackslash,
         MultilineStringLiteralLine,
-        MultilineStringLiteralLineBackslash,
         CharLiteral,
         CharLiteralBackslash,
         CharLiteralEscape1,
@@ -655,9 +654,6 @@ pub const Tokenizer = struct {
                 },
 
                 State.MultilineStringLiteralLine => switch (c) {
-                    '\\' => {
-                        state = State.MultilineStringLiteralLineBackslash;
-                    },
                     '\n' => {
                         self.index += 1;
                         break;
@@ -665,13 +661,6 @@ pub const Tokenizer = struct {
                     else => self.checkLiteralCharacter(),
                 },
 
-                State.MultilineStringLiteralLineBackslash => switch (c) {
-                    '\n' => break, // Look for this error later.
-                    else => {
-                        state = State.MultilineStringLiteralLine;
-                    },
-                },
-
                 State.Bang => switch (c) {
                     '=' => {
                         result.id = Token.Id.BangEqual;
@@ -1010,7 +999,6 @@ pub const Tokenizer = struct {
                 State.FloatExponentUnsignedHex,
                 State.SawAtSign,
                 State.Backslash,
-                State.MultilineStringLiteralLineBackslash,
                 State.CharLiteral,
                 State.CharLiteralBackslash,
                 State.CharLiteralEscape1,
-- 
cgit v1.2.3


From 118d41ef8325b4c1ca8be1fea66c2e8368b67e32 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Sat, 26 May 2018 22:59:46 -0400
Subject: zig fmt: support array literal row size hint

See #1003
---
 std/mem.zig             |   8 ++++
 std/zig/parser_test.zig |  59 ++++++++++++++++++++++++++
 std/zig/render.zig      | 108 ++++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 167 insertions(+), 8 deletions(-)

diff --git a/std/mem.zig b/std/mem.zig
index 617c1de2f5..70f2fe22ac 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -177,6 +177,14 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
     return true;
 }
 
+/// Returns true if all elements in a slice are equal to the scalar value provided
+pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool {
+    for (slice) |item| {
+        if (item != scalar) return false;
+    }
+    return true;
+}
+
 /// Copies ::m to newly allocated memory. Caller is responsible to free it.
 pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T {
     const new_buf = try allocator.alloc(T, m.len);
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 152056c6b4..909220a6f6 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,62 @@
+test "zig fmt: array literal with hint" {
+    try testTransform(
+        \\const a = []u8{
+        \\    1, 2, //
+        \\    3,
+        \\    4,
+        \\    5,
+        \\    6,
+        \\    7 };
+        \\const a = []u8{
+        \\    1, 2, //
+        \\    3,
+        \\    4,
+        \\    5,
+        \\    6,
+        \\    7, 8 };
+        \\const a = []u8{
+        \\    1, 2, //
+        \\    3,
+        \\    4,
+        \\    5,
+        \\    6, // blah
+        \\    7, 8 };
+        \\const a = []u8{
+        \\    1, 2, //
+        \\    3, //
+        \\    4,
+        \\    5,
+        \\    6,
+        \\    7 };
+    ,
+        \\const a = []u8{
+        \\    1, 2, //
+        \\    3, 4, //
+        \\    5, 6, //
+        \\    7,
+        \\};
+        \\const a = []u8{
+        \\    1, 2, //
+        \\    3, 4, //
+        \\    5, 6, //
+        \\    7, 8, //
+        \\};
+        \\const a = []u8{
+        \\    1, 2, //
+        \\    3, 4, //
+        \\    5, 6, // blah
+        \\    7, 8, //
+        \\};
+        \\const a = []u8{
+        \\    1, 2, //
+        \\    3, 4, //
+        \\    5, 6, //
+        \\    7,
+        \\};
+        \\
+    );
+}
+
 test "zig fmt: multiline string with backslash at end of line" {
     try testCanonical(
         \\comptime {
diff --git a/std/zig/render.zig b/std/zig/render.zig
index 409828e070..fa3755b719 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -19,7 +19,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf(
     var tok_it = tree.tokens.iterator(0);
     while (tok_it.next()) |token| {
         if (token.id != Token.Id.LineComment) break;
-        try stream.print("{}\n", tree.tokenSlicePtr(token));
+        try stream.print("{}\n", mem.trimRight(u8, tree.tokenSlicePtr(token), " "));
         if (tok_it.peek()) |next_token| {
             const loc = tree.tokenLocationPtr(token.end, next_token);
             if (loc.line >= 2) {
@@ -532,12 +532,73 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
 
                     try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None);
 
+                    // scan to find row size
+                    const maybe_row_size: ?usize = blk: {
+                        var count: usize = 0;
+                        var it = exprs.iterator(0);
+                        var prev_token = (??it.peek()).*.lastToken() + 1;
+                        while (it.next()) |expr| {
+                            const expr_last_token = expr.*.lastToken() + 1;
+                            const next_token = tree.tokens.at(expr_last_token + 1);
+                            const loc = tree.tokenLocationPtr(tree.tokens.at(prev_token).end, next_token);
+                            if (loc.line != 0) break :blk null;
+                            if (next_token.id == Token.Id.LineComment) {
+                                const trimmed = mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ");
+                                if (trimmed.len == 2) {
+                                    break :blk count;
+                                } else {
+                                    break :blk null;
+                                }
+                            }
+                            prev_token = expr_last_token;
+                            count += 1;
+                        }
+                        break :blk null;
+                    };
+
+
                     const new_indent = indent + indent_delta;
                     try renderToken(tree, stream, lbrace, new_indent, Space.Newline);
+                    try stream.writeByteNTimes(' ', new_indent);
+
+                    if (maybe_row_size) |row_size| {
+                        var it = exprs.iterator(0);
+                        var i: usize = 0;
+                        while (it.next()) |expr| {
+                            if (it.peek()) |next_expr| {
+                                try renderExpression(allocator, stream, tree, new_indent, expr.*, Space.None);
+
+                                const comma = tree.nextToken(expr.*.lastToken());
+
+                                if (i != row_size) {
+                                    try renderToken(tree, stream, comma, new_indent, Space.IgnoreEmptyComment); // ,
+                                    i += 1;
+                                    continue;
+                                }
+                                i = 0;
+
+                                try renderToken(tree, stream, comma, new_indent, Space.NoIndent); // ,
+
+                                const next_token = tree.tokens.at(comma + 1);
+                                if (next_token.id != Token.Id.LineComment) {
+                                    try stream.print(" //\n");
+                                }
+
+                                try renderExtraNewline(tree, stream, next_expr.*);
+                                try stream.writeByteNTimes(' ', new_indent);
+                            } else if (i == row_size) {
+                                try renderTrailingCommaAndEmptyComment(allocator, stream, tree, new_indent, expr.*); // , //
+                            } else {
+                                try renderTrailingComma(allocator, stream, tree, new_indent, expr.*, Space.Newline); // ,
+                            }
+                        }
+                        try stream.writeByteNTimes(' ', indent);
+                        try renderToken(tree, stream, suffix_op.rtoken, indent, space);
+                        return;
+                    }
 
                     var it = exprs.iterator(0);
                     while (it.next()) |expr| {
-                        try stream.writeByteNTimes(' ', new_indent);
 
                         if (it.peek()) |next_expr| {
                             try renderExpression(allocator, stream, tree, new_indent, expr.*, Space.None);
@@ -546,6 +607,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind
                             try renderToken(tree, stream, comma, new_indent, Space.Newline); // ,
 
                             try renderExtraNewline(tree, stream, next_expr.*);
+                            try stream.writeByteNTimes(' ', new_indent);
                         } else {
                             try renderTrailingComma(allocator, stream, tree, new_indent, expr.*, Space.Newline);
                         }
@@ -1496,11 +1558,12 @@ const Space = enum {
     NoNewline,
     NoIndent,
     NoComment,
+    IgnoreEmptyComment,
 };
 
 fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, space: Space) (@typeOf(stream).Child.Error || Error)!void {
     var token = tree.tokens.at(token_index);
-    try stream.write(tree.tokenSlicePtr(token));
+    try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(token), " "));
 
     if (space == Space.NoComment) return;
 
@@ -1515,15 +1578,19 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent
                     return stream.write("\n");
                 }
             },
-            Space.Space => return stream.writeByte(' '),
+            Space.Space, Space.IgnoreEmptyComment => return stream.writeByte(' '),
             Space.NoComment => unreachable,
         }
     }
 
+    if (space == Space.IgnoreEmptyComment and mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ").len == 2) {
+        return stream.writeByte(' ');
+    }
+
     var loc = tree.tokenLocationPtr(token.end, next_token);
     var offset: usize = 1;
     if (loc.line == 0) {
-        try stream.print(" {}", tree.tokenSlicePtr(next_token));
+        try stream.print(" {}", mem.trimRight(u8, tree.tokenSlicePtr(next_token), " "));
         offset = 2;
         token = next_token;
         next_token = tree.tokens.at(token_index + offset);
@@ -1546,7 +1613,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent
                     }
                 },
                 Space.NoNewline => {},
-                Space.NoComment => unreachable,
+                Space.NoComment, Space.IgnoreEmptyComment => unreachable,
             }
             return;
         }
@@ -1558,7 +1625,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent
         const newline_count = if (loc.line == 1) u8(1) else u8(2);
         try stream.writeByteNTimes('\n', newline_count);
         try stream.writeByteNTimes(' ', indent);
-        try stream.write(tree.tokenSlicePtr(next_token));
+        try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(next_token), " "));
 
         offset += 1;
         token = next_token;
@@ -1583,7 +1650,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent
                     try stream.writeByteNTimes(' ', next_line_indent);
                 },
                 Space.NoNewline => {},
-                Space.NoComment => unreachable,
+                Space.NoComment, Space.IgnoreEmptyComment => unreachable,
             }
             return;
         }
@@ -1621,3 +1688,28 @@ fn renderTrailingComma(allocator: &mem.Allocator, stream: var, tree: &ast.Tree,
         },
     }
 }
+
+fn renderTrailingCommaAndEmptyComment(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void
+{
+    const end_token = base.lastToken() + 1;
+    switch (tree.tokens.at(end_token).id) {
+        Token.Id.Comma => {
+            try renderExpression(allocator, stream, tree, indent, base, Space.None);
+            try renderToken(tree, stream, end_token, indent, Space.Space); // ,
+
+            const next_token = tree.tokens.at(end_token + 1);
+            if (next_token.id != Token.Id.LineComment) {
+                try stream.print("//\n");
+            }
+        },
+        Token.Id.LineComment => {
+            try renderExpression(allocator, stream, tree, indent, base, Space.NoComment);
+            try stream.write(", ");
+            try renderToken(tree, stream, end_token, indent, Space.Newline);
+        },
+        else => {
+            try renderExpression(allocator, stream, tree, indent, base, Space.None);
+            try stream.write(", //\n");
+        },
+    }
+}
-- 
cgit v1.2.3


From b184ae5ca5d3d3c0a4b9de564a6e30555e596e65 Mon Sep 17 00:00:00 2001
From: Andrew Kelley 
Date: Sat, 26 May 2018 18:16:39 -0400
Subject: run zig fmt on some of the codebase

See #1003
---
 doc/docgen.zig                     | 182 +++++---
 example/guess_number/main.zig      |   2 +-
 example/hello_world/hello_libc.zig |   3 +-
 example/mix_o_files/build.zig      |   4 +-
 example/shared_library/build.zig   |   4 +-
 src-self-hosted/introspect.zig     |   4 +-
 src-self-hosted/ir.zig             |   1 -
 src-self-hosted/main.zig           | 169 +++++---
 src-self-hosted/target.zig         |   3 +-
 std/array_list.zig                 |   9 +-
 std/base64.zig                     | 117 ++---
 std/buf_map.zig                    |   4 +-
 std/buf_set.zig                    |   4 +-
 std/build.zig                      |  59 +--
 std/c/darwin.zig                   |   2 +-
 std/c/index.zig                    |  12 +-
 std/crypto/md5.zig                 | 138 +++---
 std/crypto/sha1.zig                |  86 ++--
 std/crypto/sha2.zig                | 861 +++++++++++++++++++------------------
 std/crypto/test.zig                |   2 +-
 std/crypto/throughput_test.zig     |   2 +-
 std/cstr.zig                       |   4 +-
 std/debug/failing_allocator.zig    |   4 +-
 std/debug/index.zig                |  18 +-
 std/dwarf.zig                      |   2 -
 std/elf.zig                        |  42 +-
 std/event.zig                      |  13 +-
 std/fmt/errol/enum3.zig            |   7 +-
 28 files changed, 913 insertions(+), 845 deletions(-)

diff --git a/doc/docgen.zig b/doc/docgen.zig
index bd9dc6c147..7dc444f127 100644
--- a/doc/docgen.zig
+++ b/doc/docgen.zig
@@ -95,7 +95,7 @@ const Tokenizer = struct {
     };
 
     fn init(source_file_name: []const u8, buffer: []const u8) Tokenizer {
-        return Tokenizer {
+        return Tokenizer{
             .buffer = buffer,
             .index = 0,
             .state = State.Start,
@@ -105,7 +105,7 @@ const Tokenizer = struct {
     }
 
     fn next(self: &Tokenizer) Token {
-        var result = Token {
+        var result = Token{
             .id = Token.Id.Eof,
             .start = self.index,
             .end = undefined,
@@ -197,7 +197,7 @@ const Tokenizer = struct {
     };
 
     fn getTokenLocation(self: &Tokenizer, token: &const Token) Location {
-        var loc = Location {
+        var loc = Location{
             .line = 0,
             .column = 0,
             .line_start = 0,
@@ -346,7 +346,7 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
                 break;
             },
             Token.Id.Content => {
-                try nodes.append(Node {.Content = tokenizer.buffer[token.start..token.end] });
+                try nodes.append(Node{ .Content = tokenizer.buffer[token.start..token.end] });
             },
             Token.Id.BracketOpen => {
                 const tag_token = try eatToken(tokenizer, Token.Id.TagContent);
@@ -365,11 +365,13 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
                     header_stack_size += 1;
 
                     const urlized = try urlize(allocator, content);
-                    try nodes.append(Node{.HeaderOpen = HeaderOpen {
-                        .name = content,
-                        .url = urlized,
-                        .n = header_stack_size,
-                    }});
+                    try nodes.append(Node{
+                        .HeaderOpen = HeaderOpen{
+                            .name = content,
+                            .url = urlized,
+                            .n = header_stack_size,
+                        },
+                    });
                     if (try urls.put(urlized, tag_token)) |other_tag_token| {
                         parseError(tokenizer, tag_token, "duplicate header url: #{}", urlized) catch {};
                         parseError(tokenizer, other_tag_token, "other tag here") catch {};
@@ -407,14 +409,14 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
                         switch (see_also_tok.id) {
                             Token.Id.TagContent => {
                                 const content = tokenizer.buffer[see_also_tok.start..see_also_tok.end];
-                                try list.append(SeeAlsoItem {
+                                try list.append(SeeAlsoItem{
                                     .name = content,
                                     .token = see_also_tok,
                                 });
                             },
                             Token.Id.Separator => {},
                             Token.Id.BracketClose => {
-                                try nodes.append(Node {.SeeAlso = list.toOwnedSlice() } );
+                                try nodes.append(Node{ .SeeAlso = list.toOwnedSlice() });
                                 break;
                             },
                             else => return parseError(tokenizer, see_also_tok, "invalid see_also token"),
@@ -438,8 +440,8 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
                         }
                     };
 
-                    try nodes.append(Node {
-                        .Link = Link {
+                    try nodes.append(Node{
+                        .Link = Link{
                             .url = try urlize(allocator, url_name),
                             .name = name,
                             .token = name_tok,
@@ -463,24 +465,24 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
                     var code_kind_id: Code.Id = undefined;
                     var is_inline = false;
                     if (mem.eql(u8, code_kind_str, "exe")) {
-                        code_kind_id = Code.Id { .Exe = ExpectedOutcome.Succeed };
+                        code_kind_id = Code.Id{ .Exe = ExpectedOutcome.Succeed };
                     } else if (mem.eql(u8, code_kind_str, "exe_err")) {
-                        code_kind_id = Code.Id { .Exe = ExpectedOutcome.Fail };
+                        code_kind_id = Code.Id{ .Exe = ExpectedOutcome.Fail };
                     } else if (mem.eql(u8, code_kind_str, "test")) {
                         code_kind_id = Code.Id.Test;
                     } else if (mem.eql(u8, code_kind_str, "test_err")) {
-                        code_kind_id = Code.Id { .TestError = name};
+                        code_kind_id = Code.Id{ .TestError = name };
                         name = "test";
                     } else if (mem.eql(u8, code_kind_str, "test_safety")) {
-                        code_kind_id = Code.Id { .TestSafety = name};
+                        code_kind_id = Code.Id{ .TestSafety = name };
                         name = "test";
                     } else if (mem.eql(u8, code_kind_str, "obj")) {
-                        code_kind_id = Code.Id { .Obj = null };
+                        code_kind_id = Code.Id{ .Obj = null };
                     } else if (mem.eql(u8, code_kind_str, "obj_err")) {
-                        code_kind_id = Code.Id { .Obj = name };
+                        code_kind_id = Code.Id{ .Obj = name };
                         name = "test";
                     } else if (mem.eql(u8, code_kind_str, "syntax")) {
-                        code_kind_id = Code.Id { .Obj = null };
+                        code_kind_id = Code.Id{ .Obj = null };
                         is_inline = true;
                     } else {
                         return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {}", code_kind_str);
@@ -514,17 +516,20 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
                             return parseError(tokenizer, end_code_tag, "invalid token inside code_begin: {}", end_tag_name);
                         }
                         _ = try eatToken(tokenizer, Token.Id.BracketClose);
-                    } else unreachable; // TODO issue #707
-                    try nodes.append(Node {.Code = Code {
-                        .id = code_kind_id,
-                        .name = name,
-                        .source_token = source_token,
-                        .is_inline = is_inline,
-                        .mode = mode,
-                        .link_objects = link_objects.toOwnedSlice(),
-                        .target_windows = target_windows,
-                        .link_libc = link_libc,
-                    }});
+                    } else
+                        unreachable; // TODO issue #707
+                    try nodes.append(Node{
+                        .Code = Code{
+                            .id = code_kind_id,
+                            .name = name,
+                            .source_token = source_token,
+                            .is_inline = is_inline,
+                            .mode = mode,
+                            .link_objects = link_objects.toOwnedSlice(),
+                            .target_windows = target_windows,
+                            .link_libc = link_libc,
+                        },
+                    });
                     tokenizer.code_node_count += 1;
                 } else {
                     return parseError(tokenizer, tag_token, "unrecognized tag name: {}", tag_name);
@@ -534,7 +539,7 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
         }
     }
 
-    return Toc {
+    return Toc{
         .nodes = nodes.toOwnedSlice(),
         .toc = toc_buf.toOwnedSlice(),
         .urls = urls,
@@ -727,16 +732,19 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
                 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
                 const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);
                 try io.writeFile(allocator, tmp_source_file_name, trimmed_raw_source);
-                
+
                 switch (code.id) {
                     Code.Id.Exe => |expected_outcome| {
                         const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, exe_ext);
                         const tmp_bin_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_bin_ext);
                         var build_args = std.ArrayList([]const u8).init(allocator);
                         defer build_args.deinit();
-                        try build_args.appendSlice([][]const u8 {zig_exe,
-                            "build-exe", tmp_source_file_name,
-                            "--output", tmp_bin_file_name,
+                        try build_args.appendSlice([][]const u8{
+                            zig_exe,
+                            "build-exe",
+                            tmp_source_file_name,
+                            "--output",
+                            tmp_bin_file_name,
                         });
                         try out.print("
$ zig build-exe {}.zig", code.name);
                         switch (code.mode) {
@@ -766,10 +774,9 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
                             try build_args.append("c");
                             try out.print(" --library c");
                         }
-                        _ = exec(allocator, build_args.toSliceConst()) catch return parseError(
-                            tokenizer, code.source_token, "example failed to compile");
+                        _ = exec(allocator, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile");
 
-                        const run_args = [][]const u8 {tmp_bin_file_name};
+                        const run_args = [][]const u8{tmp_bin_file_name};
 
                         const result = if (expected_outcome == ExpectedOutcome.Fail) blk: {
                             const result = try os.ChildProcess.exec(allocator, run_args, null, null, max_doc_file_size);
@@ -777,7 +784,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
                                 os.ChildProcess.Term.Exited => |exit_code| {
                                     if (exit_code == 0) {
                                         warn("{}\nThe following command incorrectly succeeded:\n", result.stderr);
-                                        for (run_args) |arg| warn("{} ", arg) else warn("\n");
+                                        for (run_args) |arg|
+                                            warn("{} ", arg)
+                                        else
+                                            warn("\n");
                                         return parseError(tokenizer, code.source_token, "example incorrectly compiled");
                                     }
                                 },
@@ -785,11 +795,9 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
                             }
                             break :blk result;
                         } else blk: {
-                            break :blk exec(allocator, run_args) catch return parseError(
-                                tokenizer, code.source_token, "example crashed");
+                            break :blk exec(allocator, run_args) catch return parseError(tokenizer, code.source_token, "example crashed");
                         };
 
-                        
                         const escaped_stderr = try escapeHtml(allocator, result.stderr);
                         const escaped_stdout = try escapeHtml(allocator, result.stdout);
 
@@ -802,7 +810,11 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
                         var test_args = std.ArrayList([]const u8).init(allocator);
                         defer test_args.deinit();
 
-                        try test_args.appendSlice([][]const u8 {zig_exe, "test", tmp_source_file_name});
+                        try test_args.appendSlice([][]const u8{
+                            zig_exe,
+                            "test",
+                            tmp_source_file_name,
+                        });
                         try out.print("
$ zig test {}.zig", code.name);
                         switch (code.mode) {
                             builtin.Mode.Debug => {},
@@ -821,13 +833,15 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
                         }
                         if (code.target_windows) {
                             try test_args.appendSlice([][]const u8{
-                                "--target-os", "windows",
-                                "--target-arch", "x86_64",
-                                "--target-environ", "msvc",
+                                "--target-os",
+                                "windows",
+                                "--target-arch",
+                                "x86_64",
+                                "--target-environ",
+                                "msvc",
                             });
                         }
-                        const result = exec(allocator, test_args.toSliceConst()) catch return parseError(
-                            tokenizer, code.source_token, "test failed");
+                        const result = exec(allocator, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed");
                         const escaped_stderr = try escapeHtml(allocator, result.stderr);
                         const escaped_stdout = try escapeHtml(allocator, result.stdout);
                         try out.print("\n{}{}
\n", escaped_stderr, escaped_stdout); @@ -836,7 +850,13 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var var test_args = std.ArrayList([]const u8).init(allocator); defer test_args.deinit(); - try test_args.appendSlice([][]const u8 {zig_exe, "test", "--color", "on", tmp_source_file_name}); + try test_args.appendSlice([][]const u8{ + zig_exe, + "test", + "--color", + "on", + tmp_source_file_name, + }); try out.print("
$ zig test {}.zig", code.name);
                         switch (code.mode) {
                             builtin.Mode.Debug => {},
@@ -858,13 +878,19 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
                             os.ChildProcess.Term.Exited => |exit_code| {
                                 if (exit_code == 0) {
                                     warn("{}\nThe following command incorrectly succeeded:\n", result.stderr);
-                                    for (test_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
+                                    for (test_args.toSliceConst()) |arg|
+                                        warn("{} ", arg)
+                                    else
+                                        warn("\n");
                                     return parseError(tokenizer, code.source_token, "example incorrectly compiled");
                                 }
                             },
                             else => {
                                 warn("{}\nThe following command crashed:\n", result.stderr);
-                                for (test_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
+                                for (test_args.toSliceConst()) |arg|
+                                    warn("{} ", arg)
+                                else
+                                    warn("\n");
                                 return parseError(tokenizer, code.source_token, "example compile crashed");
                             },
                         }
@@ -881,7 +907,11 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
                         var test_args = std.ArrayList([]const u8).init(allocator);
                         defer test_args.deinit();
 
-                        try test_args.appendSlice([][]const u8 {zig_exe, "test", tmp_source_file_name});
+                        try test_args.appendSlice([][]const u8{
+                            zig_exe,
+                            "test",
+                            tmp_source_file_name,
+                        });
                         switch (code.mode) {
                             builtin.Mode.Debug => {},
                             builtin.Mode.ReleaseSafe => try test_args.append("--release-safe"),
@@ -894,13 +924,19 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
                             os.ChildProcess.Term.Exited => |exit_code| {
                                 if (exit_code == 0) {
                                     warn("{}\nThe following command incorrectly succeeded:\n", result.stderr);
-                                    for (test_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
+                                    for (test_args.toSliceConst()) |arg|
+                                        warn("{} ", arg)
+                                    else
+                                        warn("\n");
                                     return parseError(tokenizer, code.source_token, "example test incorrectly succeeded");
                                 }
                             },
                             else => {
                                 warn("{}\nThe following command crashed:\n", result.stderr);
-                                for (test_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
+                                for (test_args.toSliceConst()) |arg|
+                                    warn("{} ", arg)
+                                else
+                                    warn("\n");
                                 return parseError(tokenizer, code.source_token, "example compile crashed");
                             },
                         }
@@ -918,9 +954,15 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
                         var build_args = std.ArrayList([]const u8).init(allocator);
                         defer build_args.deinit();
 
-                        try build_args.appendSlice([][]const u8 {zig_exe, "build-obj", tmp_source_file_name,
-                            "--color", "on",
-                            "--output", tmp_obj_file_name});
+                        try build_args.appendSlice([][]const u8{
+                            zig_exe,
+                            "build-obj",
+                            tmp_source_file_name,
+                            "--color",
+                            "on",
+                            "--output",
+                            tmp_obj_file_name,
+                        });
 
                         if (!code.is_inline) {
                             try out.print("
$ zig build-obj {}.zig", code.name);
@@ -954,13 +996,19 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
                                 os.ChildProcess.Term.Exited => |exit_code| {
                                     if (exit_code == 0) {
                                         warn("{}\nThe following command incorrectly succeeded:\n", result.stderr);
-                                        for (build_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
+                                        for (build_args.toSliceConst()) |arg|
+                                            warn("{} ", arg)
+                                        else
+                                            warn("\n");
                                         return parseError(tokenizer, code.source_token, "example build incorrectly succeeded");
                                     }
                                 },
                                 else => {
                                     warn("{}\nThe following command crashed:\n", result.stderr);
-                                    for (build_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
+                                    for (build_args.toSliceConst()) |arg|
+                                        warn("{} ", arg)
+                                    else
+                                        warn("\n");
                                     return parseError(tokenizer, code.source_token, "example compile crashed");
                                 },
                             }
@@ -975,8 +1023,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
                                 try out.print("
\n"); } } else { - _ = exec(allocator, build_args.toSliceConst()) catch return parseError( - tokenizer, code.source_token, "example failed to compile"); + _ = exec(allocator, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile"); } if (!code.is_inline) { try out.print("
\n"); @@ -987,7 +1034,6 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var }, } } - } fn exec(allocator: &mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult { @@ -996,13 +1042,19 @@ fn exec(allocator: &mem.Allocator, args: []const []const u8) !os.ChildProcess.Ex os.ChildProcess.Term.Exited => |exit_code| { if (exit_code != 0) { warn("{}\nThe following command exited with code {}:\n", result.stderr, exit_code); - for (args) |arg| warn("{} ", arg) else warn("\n"); + for (args) |arg| + warn("{} ", arg) + else + warn("\n"); return error.ChildExitError; } }, else => { warn("{}\nThe following command crashed:\n", result.stderr); - for (args) |arg| warn("{} ", arg) else warn("\n"); + for (args) |arg| + warn("{} ", arg) + else + warn("\n"); return error.ChildCrashed; }, } diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig index 7178c5274a..bed132b25c 100644 --- a/example/guess_number/main.zig +++ b/example/guess_number/main.zig @@ -23,7 +23,7 @@ pub fn main() !void { while (true) { try stdout.print("\nGuess a number between 1 and 100: "); - var line_buf : [20]u8 = undefined; + var line_buf: [20]u8 = undefined; const line_len = io.readLine(line_buf[0..]) catch |err| switch (err) { error.InputTooLong => { diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig index 4a35e47b15..1df8f04ce4 100644 --- a/example/hello_world/hello_libc.zig +++ b/example/hello_world/hello_libc.zig @@ -8,8 +8,7 @@ const c = @cImport({ const msg = c"Hello, world!\n"; export fn main(argc: c_int, argv: &&u8) c_int { - if (c.printf(msg) != c_int(c.strlen(msg))) - return -1; + if (c.printf(msg) != c_int(c.strlen(msg))) return -1; return 0; } diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig index 4380486867..e5d2e6a446 100644 --- a/example/mix_o_files/build.zig +++ b/example/mix_o_files/build.zig @@ -4,9 +4,7 @@ pub fn build(b: &Builder) void { const obj = b.addObject("base64", "base64.zig"); const exe = b.addCExecutable("test"); - exe.addCompileFlags([][]const u8 { - "-std=c99", - }); + exe.addCompileFlags([][]const u8{"-std=c99"}); exe.addSourceFile("test.c"); exe.addObject(obj); diff --git a/example/shared_library/build.zig b/example/shared_library/build.zig index 2b5a178b35..30c714c6c6 100644 --- a/example/shared_library/build.zig +++ b/example/shared_library/build.zig @@ -4,9 +4,7 @@ pub fn build(b: &Builder) void { const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); const exe = b.addCExecutable("test"); - exe.addCompileFlags([][]const u8 { - "-std=c99", - }); + exe.addCompileFlags([][]const u8{"-std=c99"}); exe.addSourceFile("test.c"); exe.linkLibrary(lib); diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig index 3f1fefdd5a..adab00286b 100644 --- a/src-self-hosted/introspect.zig +++ b/src-self-hosted/introspect.zig @@ -48,9 +48,7 @@ pub fn resolveZigLibDir(allocator: &mem.Allocator) ![]u8 { \\Unable to find zig lib directory: {}. \\Reinstall Zig or use --zig-install-prefix. \\ - , - @errorName(err) - ); + , @errorName(err)); return error.ZigLibDirNotFound; }; diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index b66a0abdee..c4550b5179 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -108,5 +108,4 @@ pub const Instruction = struct { ArgType, Export, }; - }; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 22f49e80d9..f54bdbdaf0 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -37,7 +37,7 @@ const usage = \\ zen Print zen of zig and exit \\ \\ - ; +; const Command = struct { name: []const u8, @@ -63,22 +63,61 @@ pub fn main() !void { os.exit(1); } - const commands = []Command { - Command { .name = "build", .exec = cmdBuild }, - Command { .name = "build-exe", .exec = cmdBuildExe }, - Command { .name = "build-lib", .exec = cmdBuildLib }, - Command { .name = "build-obj", .exec = cmdBuildObj }, - Command { .name = "fmt", .exec = cmdFmt }, - Command { .name = "run", .exec = cmdRun }, - Command { .name = "targets", .exec = cmdTargets }, - Command { .name = "test", .exec = cmdTest }, - Command { .name = "translate-c", .exec = cmdTranslateC }, - Command { .name = "version", .exec = cmdVersion }, - Command { .name = "zen", .exec = cmdZen }, + const commands = []Command{ + Command{ + .name = "build", + .exec = cmdBuild, + }, + Command{ + .name = "build-exe", + .exec = cmdBuildExe, + }, + Command{ + .name = "build-lib", + .exec = cmdBuildLib, + }, + Command{ + .name = "build-obj", + .exec = cmdBuildObj, + }, + Command{ + .name = "fmt", + .exec = cmdFmt, + }, + Command{ + .name = "run", + .exec = cmdRun, + }, + Command{ + .name = "targets", + .exec = cmdTargets, + }, + Command{ + .name = "test", + .exec = cmdTest, + }, + Command{ + .name = "translate-c", + .exec = cmdTranslateC, + }, + Command{ + .name = "version", + .exec = cmdVersion, + }, + Command{ + .name = "zen", + .exec = cmdZen, + }, // undocumented commands - Command { .name = "help", .exec = cmdHelp }, - Command { .name = "internal", .exec = cmdInternal }, + Command{ + .name = "help", + .exec = cmdHelp, + }, + Command{ + .name = "internal", + .exec = cmdInternal, + }, }; for (commands) |command| { @@ -120,9 +159,9 @@ const usage_build = \\ --verbose-cimport Enable compiler debug output for C imports \\ \\ - ; +; -const args_build_spec = []Flag { +const args_build_spec = []Flag{ Flag.Bool("--help"), Flag.Bool("--init"), Flag.Arg1("--build-file"), @@ -148,7 +187,7 @@ const missing_build_file = \\ \\See: `zig build --help` or `zig help` for more options. \\ - ; +; fn cmdBuild(allocator: &Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_build_spec, args); @@ -317,15 +356,23 @@ const usage_build_generic = \\ --ver-patch [ver] Dynamic library semver patch version \\ \\ - ; +; -const args_build_generic = []Flag { +const args_build_generic = []Flag{ Flag.Bool("--help"), - Flag.Option("--color", []const []const u8 { "auto", "off", "on" }), + Flag.Option("--color", []const []const u8{ + "auto", + "off", + "on", + }), Flag.ArgMergeN("--assembly", 1), Flag.Arg1("--cache-dir"), - Flag.Option("--emit", []const []const u8 { "asm", "bin", "llvm-ir" }), + Flag.Option("--emit", []const []const u8{ + "asm", + "bin", + "llvm-ir", + }), Flag.Bool("--enable-timing-info"), Flag.Arg1("--libc-include-dir"), Flag.Arg1("--name"), @@ -471,7 +518,7 @@ fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Mo os.exit(1); }; - const asm_a= flags.many("assembly"); + const asm_a = flags.many("assembly"); const obj_a = flags.many("object"); if (in_file == null and (obj_a == null or (??obj_a).len == 0) and (asm_a == null or (??asm_a).len == 0)) { try stderr.write("Expected source file argument or at least one --object or --assembly argument\n"); @@ -493,17 +540,16 @@ fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Mo const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1); defer allocator.free(zig_lib_dir); - var module = - try Module.create( - allocator, - root_name, - zig_root_source_file, - Target.Native, - out_type, - build_mode, - zig_lib_dir, - full_cache_dir - ); + var module = try Module.create( + allocator, + root_name, + zig_root_source_file, + Target.Native, + out_type, + build_mode, + zig_lib_dir, + full_cache_dir, + ); defer module.destroy(); module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") ?? "0", 10); @@ -588,10 +634,10 @@ fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Mo } if (flags.single("mmacosx-version-min")) |ver| { - module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver }; + module.darwin_version_min = Module.DarwinVersionMin{ .MacOS = ver }; } if (flags.single("mios-version-min")) |ver| { - module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver }; + module.darwin_version_min = Module.DarwinVersionMin{ .Ios = ver }; } module.emit_file_type = emit_type; @@ -639,11 +685,9 @@ const usage_fmt = \\ --help Print this help and exit \\ \\ - ; +; -const args_fmt_spec = []Flag { - Flag.Bool("--help"), -}; +const args_fmt_spec = []Flag{Flag.Bool("--help")}; fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_fmt_spec, args); @@ -675,7 +719,6 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { }; defer tree.deinit(); - var error_it = tree.errors.iterator(0); while (error_it.next()) |parse_error| { const token = tree.tokens.at(parse_error.loc()); @@ -721,8 +764,7 @@ fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void { inline while (i < @memberCount(builtin.Arch)) : (i += 1) { comptime const arch_tag = @memberName(builtin.Arch, i); // NOTE: Cannot use empty string, see #918. - comptime const native_str = - if (comptime mem.eql(u8, arch_tag, @tagName(builtin.arch))) " (native)\n" else "\n"; + comptime const native_str = if (comptime mem.eql(u8, arch_tag, @tagName(builtin.arch))) " (native)\n" else "\n"; try stdout.print(" {}{}", arch_tag, native_str); } @@ -735,8 +777,7 @@ fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void { inline while (i < @memberCount(builtin.Os)) : (i += 1) { comptime const os_tag = @memberName(builtin.Os, i); // NOTE: Cannot use empty string, see #918. - comptime const native_str = - if (comptime mem.eql(u8, os_tag, @tagName(builtin.os))) " (native)\n" else "\n"; + comptime const native_str = if (comptime mem.eql(u8, os_tag, @tagName(builtin.os))) " (native)\n" else "\n"; try stdout.print(" {}{}", os_tag, native_str); } @@ -749,8 +790,7 @@ fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void { inline while (i < @memberCount(builtin.Environ)) : (i += 1) { comptime const environ_tag = @memberName(builtin.Environ, i); // NOTE: Cannot use empty string, see #918. - comptime const native_str = - if (comptime mem.eql(u8, environ_tag, @tagName(builtin.environ))) " (native)\n" else "\n"; + comptime const native_str = if (comptime mem.eql(u8, environ_tag, @tagName(builtin.environ))) " (native)\n" else "\n"; try stdout.print(" {}{}", environ_tag, native_str); } @@ -772,12 +812,9 @@ const usage_test = \\ --help Print this help and exit \\ \\ - ; - -const args_test_spec = []Flag { - Flag.Bool("--help"), -}; +; +const args_test_spec = []Flag{Flag.Bool("--help")}; fn cmdTest(allocator: &Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_build_spec, args); @@ -810,21 +847,18 @@ const usage_run = \\ --help Print this help and exit \\ \\ - ; - -const args_run_spec = []Flag { - Flag.Bool("--help"), -}; +; +const args_run_spec = []Flag{Flag.Bool("--help")}; fn cmdRun(allocator: &Allocator, args: []const []const u8) !void { var compile_args = args; - var runtime_args: []const []const u8 = []const []const u8 {}; + var runtime_args: []const []const u8 = []const []const u8{}; for (args) |argv, i| { if (mem.eql(u8, argv, "--")) { compile_args = args[0..i]; - runtime_args = args[i+1..]; + runtime_args = args[i + 1..]; break; } } @@ -858,9 +892,9 @@ const usage_translate_c = \\ --output [path] Output file to write generated zig file (default: stdout) \\ \\ - ; +; -const args_translate_c_spec = []Flag { +const args_translate_c_spec = []Flag{ Flag.Bool("--help"), Flag.Bool("--enable-timing-info"), Flag.Arg1("--libc-include-dir"), @@ -934,7 +968,7 @@ const info_zen = \\ * Together we serve end users. \\ \\ - ; +; fn cmdZen(allocator: &Allocator, args: []const []const u8) !void { try stdout.write(info_zen); @@ -949,7 +983,7 @@ const usage_internal = \\ build-info Print static compiler build-info \\ \\ - ; +; fn cmdInternal(allocator: &Allocator, args: []const []const u8) !void { if (args.len == 0) { @@ -957,9 +991,10 @@ fn cmdInternal(allocator: &Allocator, args: []const []const u8) !void { os.exit(1); } - const sub_commands = []Command { - Command { .name = "build-info", .exec = cmdInternalBuildInfo }, - }; + const sub_commands = []Command{Command{ + .name = "build-info", + .exec = cmdInternalBuildInfo, + }}; for (sub_commands) |sub_command| { if (mem.eql(u8, sub_command.name, args[0])) { @@ -983,7 +1018,7 @@ fn cmdInternalBuildInfo(allocator: &Allocator, args: []const []const u8) !void { \\ZIG_C_HEADER_FILES {} \\ZIG_DIA_GUIDS_LIB {} \\ - , + , std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR), std.cstr.toSliceConst(c.ZIG_CXX_COMPILER), std.cstr.toSliceConst(c.ZIG_LLVM_CONFIG_EXE), diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index 27a90bd096..7983a3ddec 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -38,8 +38,7 @@ pub const Target = union(enum) { pub fn isDarwin(self: &const Target) bool { return switch (self.getOs()) { - builtin.Os.ios, - builtin.Os.macosx => true, + builtin.Os.ios, builtin.Os.macosx => true, else => false, }; } diff --git a/std/array_list.zig b/std/array_list.zig index d1165c626d..679f7d73b8 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -150,7 +150,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { }; pub fn iterator(self: &const Self) Iterator { - return Iterator { .list = self, .count = 0 }; + return Iterator{ + .list = self, + .count = 0, + }; } }; } @@ -207,7 +210,7 @@ test "iterator ArrayList test" { try list.append(2); try list.append(3); - var count : i32 = 0; + var count: i32 = 0; var it = list.iterator(); while (it.next()) |next| { assert(next == count + 1); @@ -225,7 +228,7 @@ test "iterator ArrayList test" { } it.reset(); - assert(?? it.next() == 1); + assert(??it.next() == 1); } test "insert ArrayList test" { diff --git a/std/base64.zig b/std/base64.zig index 13f3ea5714..515738a99e 100644 --- a/std/base64.zig +++ b/std/base64.zig @@ -41,12 +41,10 @@ pub const Base64Encoder = struct { dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f]; out_index += 1; - dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | - ((source[i + 1] & 0xf0) >> 4)]; + dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)]; out_index += 1; - dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) | - ((source[i + 2] & 0xc0) >> 6)]; + dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) | ((source[i + 2] & 0xc0) >> 6)]; out_index += 1; dest[out_index] = encoder.alphabet_chars[source[i + 2] & 0x3f]; @@ -64,8 +62,7 @@ pub const Base64Encoder = struct { dest[out_index] = encoder.pad_char; out_index += 1; } else { - dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | - ((source[i + 1] & 0xf0) >> 4)]; + dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)]; out_index += 1; dest[out_index] = encoder.alphabet_chars[(source[i + 1] & 0xf) << 2]; @@ -131,26 +128,20 @@ pub const Base64Decoder = struct { // common case if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter; if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter; - dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | - decoder.char_to_index[source[src_cursor + 1]] >> 4; - dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | - decoder.char_to_index[source[src_cursor + 2]] >> 2; - dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 | - decoder.char_to_index[source[src_cursor + 3]]; + dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4; + dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2; + dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 | decoder.char_to_index[source[src_cursor + 3]]; dest_cursor += 3; } else if (source[src_cursor + 2] != decoder.pad_char) { // one pad char if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter; - dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | - decoder.char_to_index[source[src_cursor + 1]] >> 4; - dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | - decoder.char_to_index[source[src_cursor + 2]] >> 2; + dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4; + dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2; if (decoder.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding; dest_cursor += 2; } else { // two pad chars - dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | - decoder.char_to_index[source[src_cursor + 1]] >> 4; + dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4; if (decoder.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding; dest_cursor += 1; } @@ -165,7 +156,7 @@ pub const Base64DecoderWithIgnore = struct { decoder: Base64Decoder, char_is_ignored: [256]bool, pub fn init(alphabet_chars: []const u8, pad_char: u8, ignore_chars: []const u8) Base64DecoderWithIgnore { - var result = Base64DecoderWithIgnore { + var result = Base64DecoderWithIgnore{ .decoder = Base64Decoder.init(alphabet_chars, pad_char), .char_is_ignored = []bool{false} ** 256, }; @@ -223,10 +214,12 @@ pub const Base64DecoderWithIgnore = struct { } else if (decoder_with_ignore.char_is_ignored[c]) { // we can even ignore chars during the padding continue; - } else return error.InvalidCharacter; + } else + return error.InvalidCharacter; } break; - } else return error.InvalidCharacter; + } else + return error.InvalidCharacter; } switch (available_chars) { @@ -234,22 +227,17 @@ pub const Base64DecoderWithIgnore = struct { // common case if (dest_cursor + 3 > dest.len) return error.OutputTooSmall; assert(pad_char_count == 0); - dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | - decoder.char_to_index[next_4_chars[1]] >> 4; - dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | - decoder.char_to_index[next_4_chars[2]] >> 2; - dest[dest_cursor + 2] = decoder.char_to_index[next_4_chars[2]] << 6 | - decoder.char_to_index[next_4_chars[3]]; + dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4; + dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | decoder.char_to_index[next_4_chars[2]] >> 2; + dest[dest_cursor + 2] = decoder.char_to_index[next_4_chars[2]] << 6 | decoder.char_to_index[next_4_chars[3]]; dest_cursor += 3; continue; }, 3 => { if (dest_cursor + 2 > dest.len) return error.OutputTooSmall; if (pad_char_count != 1) return error.InvalidPadding; - dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | - decoder.char_to_index[next_4_chars[1]] >> 4; - dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | - decoder.char_to_index[next_4_chars[2]] >> 2; + dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4; + dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | decoder.char_to_index[next_4_chars[2]] >> 2; if (decoder.char_to_index[next_4_chars[2]] << 6 != 0) return error.InvalidPadding; dest_cursor += 2; break; @@ -257,8 +245,7 @@ pub const Base64DecoderWithIgnore = struct { 2 => { if (dest_cursor + 1 > dest.len) return error.OutputTooSmall; if (pad_char_count != 2) return error.InvalidPadding; - dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | - decoder.char_to_index[next_4_chars[1]] >> 4; + dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4; if (decoder.char_to_index[next_4_chars[1]] << 4 != 0) return error.InvalidPadding; dest_cursor += 1; break; @@ -280,7 +267,6 @@ pub const Base64DecoderWithIgnore = struct { } }; - pub const standard_decoder_unsafe = Base64DecoderUnsafe.init(standard_alphabet_chars, standard_pad_char); pub const Base64DecoderUnsafe = struct { @@ -291,7 +277,7 @@ pub const Base64DecoderUnsafe = struct { pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64DecoderUnsafe { assert(alphabet_chars.len == 64); - var result = Base64DecoderUnsafe { + var result = Base64DecoderUnsafe{ .char_to_index = undefined, .pad_char = pad_char, }; @@ -321,16 +307,13 @@ pub const Base64DecoderUnsafe = struct { } while (in_buf_len > 4) { - dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | - decoder.char_to_index[source[src_index + 1]] >> 4; + dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4; dest_index += 1; - dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | - decoder.char_to_index[source[src_index + 2]] >> 2; + dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2; dest_index += 1; - dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | - decoder.char_to_index[source[src_index + 3]]; + dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]]; dest_index += 1; src_index += 4; @@ -338,18 +321,15 @@ pub const Base64DecoderUnsafe = struct { } if (in_buf_len > 1) { - dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | - decoder.char_to_index[source[src_index + 1]] >> 4; + dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4; dest_index += 1; } if (in_buf_len > 2) { - dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | - decoder.char_to_index[source[src_index + 2]] >> 2; + dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2; dest_index += 1; } if (in_buf_len > 3) { - dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | - decoder.char_to_index[source[src_index + 3]]; + dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]]; dest_index += 1; } } @@ -367,7 +347,6 @@ fn calcDecodedSizeExactUnsafe(source: []const u8, pad_char: u8) usize { return result; } - test "base64" { @setEvalBranchQuota(8000); testBase64() catch unreachable; @@ -375,26 +354,26 @@ test "base64" { } fn testBase64() !void { - try testAllApis("", ""); - try testAllApis("f", "Zg=="); - try testAllApis("fo", "Zm8="); - try testAllApis("foo", "Zm9v"); - try testAllApis("foob", "Zm9vYg=="); - try testAllApis("fooba", "Zm9vYmE="); + try testAllApis("", ""); + try testAllApis("f", "Zg=="); + try testAllApis("fo", "Zm8="); + try testAllApis("foo", "Zm9v"); + try testAllApis("foob", "Zm9vYg=="); + try testAllApis("fooba", "Zm9vYmE="); try testAllApis("foobar", "Zm9vYmFy"); - try testDecodeIgnoreSpace("", " "); - try testDecodeIgnoreSpace("f", "Z g= ="); - try testDecodeIgnoreSpace("fo", " Zm8="); - try testDecodeIgnoreSpace("foo", "Zm9v "); - try testDecodeIgnoreSpace("foob", "Zm9vYg = = "); - try testDecodeIgnoreSpace("fooba", "Zm9v YmE="); + try testDecodeIgnoreSpace("", " "); + try testDecodeIgnoreSpace("f", "Z g= ="); + try testDecodeIgnoreSpace("fo", " Zm8="); + try testDecodeIgnoreSpace("foo", "Zm9v "); + try testDecodeIgnoreSpace("foob", "Zm9vYg = = "); + try testDecodeIgnoreSpace("fooba", "Zm9v YmE="); try testDecodeIgnoreSpace("foobar", " Z m 9 v Y m F y "); // test getting some api errors - try testError("A", error.InvalidPadding); - try testError("AA", error.InvalidPadding); - try testError("AAA", error.InvalidPadding); + try testError("A", error.InvalidPadding); + try testError("AA", error.InvalidPadding); + try testError("AAA", error.InvalidPadding); try testError("A..A", error.InvalidCharacter); try testError("AA=A", error.InvalidCharacter); try testError("AA/=", error.InvalidPadding); @@ -427,8 +406,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void // Base64DecoderWithIgnore { - const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init( - standard_alphabet_chars, standard_pad_char, ""); + const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, ""); var buffer: [0x100]u8 = undefined; var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)]; var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded); @@ -446,8 +424,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void } fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !void { - const standard_decoder_ignore_space = Base64DecoderWithIgnore.init( - standard_alphabet_chars, standard_pad_char, " "); + const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " "); var buffer: [0x100]u8 = undefined; var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)]; var written = try standard_decoder_ignore_space.decode(decoded, encoded); @@ -455,8 +432,7 @@ fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !voi } fn testError(encoded: []const u8, expected_err: error) !void { - const standard_decoder_ignore_space = Base64DecoderWithIgnore.init( - standard_alphabet_chars, standard_pad_char, " "); + const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " "); var buffer: [0x100]u8 = undefined; if (standard_decoder.calcSize(encoded)) |decoded_size| { var decoded = buffer[0..decoded_size]; @@ -471,8 +447,7 @@ fn testError(encoded: []const u8, expected_err: error) !void { } fn testOutputTooSmallError(encoded: []const u8) !void { - const standard_decoder_ignore_space = Base64DecoderWithIgnore.init( - standard_alphabet_chars, standard_pad_char, " "); + const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " "); var buffer: [0x100]u8 = undefined; var decoded = buffer[0..calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1]; if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| { diff --git a/std/buf_map.zig b/std/buf_map.zig index 57c5830bbe..930fc36a78 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -12,9 +12,7 @@ pub const BufMap = struct { const BufMapHashMap = HashMap([]const u8, []const u8, mem.hash_slice_u8, mem.eql_slice_u8); pub fn init(allocator: &Allocator) BufMap { - var self = BufMap { - .hash_map = BufMapHashMap.init(allocator), - }; + var self = BufMap{ .hash_map = BufMapHashMap.init(allocator) }; return self; } diff --git a/std/buf_set.zig b/std/buf_set.zig index 1badb5bf18..c5a80e16fb 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -10,9 +10,7 @@ pub const BufSet = struct { const BufSetHashMap = HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); pub fn init(a: &Allocator) BufSet { - var self = BufSet { - .hash_map = BufSetHashMap.init(a), - }; + var self = BufSet{ .hash_map = BufSetHashMap.init(a) }; return self; } diff --git a/std/build.zig b/std/build.zig index 276176c63c..f86c3d394f 100644 --- a/std/build.zig +++ b/std/build.zig @@ -420,15 +420,7 @@ pub const Builder = struct { const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false; const release_small = self.option(bool, "release-small", "size optimizations on and safety off") ?? false; - const mode = if (release_safe and !release_fast and !release_small) - builtin.Mode.ReleaseSafe - else if (release_fast and !release_safe and !release_small) - builtin.Mode.ReleaseFast - else if (release_small and !release_fast and !release_safe) - builtin.Mode.ReleaseSmall - else if (!release_fast and !release_safe and !release_small) - builtin.Mode.Debug - else x: { + const mode = if (release_safe and !release_fast and !release_small) builtin.Mode.ReleaseSafe else if (release_fast and !release_safe and !release_small) builtin.Mode.ReleaseFast else if (release_small and !release_fast and !release_safe) builtin.Mode.ReleaseSmall else if (!release_fast and !release_safe and !release_small) builtin.Mode.Debug else x: { warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)"); self.markInvalidUserInput(); break :x builtin.Mode.Debug; @@ -649,11 +641,7 @@ pub const Builder = struct { if (builtin.environ == builtin.Environ.msvc) { return "cl.exe"; } else { - return os.getEnvVarOwned(self.allocator, "CC") catch |err| - if (err == error.EnvironmentVariableNotFound) - ([]const u8)("cc") - else - debug.panic("Unable to get environment variable: {}", err); + return os.getEnvVarOwned(self.allocator, "CC") catch |err| if (err == error.EnvironmentVariableNotFound) ([]const u8)("cc") else debug.panic("Unable to get environment variable: {}", err); } } @@ -782,8 +770,7 @@ pub const Target = union(enum) { pub fn isDarwin(self: &const Target) bool { return switch (self.getOs()) { - builtin.Os.ios, - builtin.Os.macosx => true, + builtin.Os.ios, builtin.Os.macosx => true, else => false, }; } @@ -990,8 +977,7 @@ pub const LibExeObjStep = struct { self.out_filename = self.builder.fmt("lib{}.a", self.name); } else { switch (self.target.getOs()) { - builtin.Os.ios, - builtin.Os.macosx => { + builtin.Os.ios, builtin.Os.macosx => { self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", self.name, self.version.major, self.version.minor, self.version.patch); self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", self.name, self.version.major); self.name_only_filename = self.builder.fmt("lib{}.dylib", self.name); @@ -1011,11 +997,13 @@ pub const LibExeObjStep = struct { } pub fn setTarget(self: &LibExeObjStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { - self.target = Target{ .Cross = CrossTarget{ - .arch = target_arch, - .os = target_os, - .environ = target_environ, - } }; + self.target = Target{ + .Cross = CrossTarget{ + .arch = target_arch, + .os = target_os, + .environ = target_environ, + }, + }; self.computeOutFileNames(); } @@ -1079,10 +1067,7 @@ pub const LibExeObjStep = struct { } pub fn getOutputPath(self: &LibExeObjStep) []const u8 { - return if (self.output_path) |output_path| - output_path - else - os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename) catch unreachable; + return if (self.output_path) |output_path| output_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename) catch unreachable; } pub fn setOutputHPath(self: &LibExeObjStep, file_path: []const u8) void { @@ -1095,10 +1080,7 @@ pub const LibExeObjStep = struct { } pub fn getOutputHPath(self: &LibExeObjStep) []const u8 { - return if (self.output_h_path) |output_h_path| - output_h_path - else - os.path.join(self.builder.allocator, self.builder.cache_root, self.out_h_filename) catch unreachable; + return if (self.output_h_path) |output_h_path| output_h_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_h_filename) catch unreachable; } pub fn addAssemblyFile(self: &LibExeObjStep, path: []const u8) void { @@ -1352,8 +1334,7 @@ pub const LibExeObjStep = struct { args.append("ssp-buffer-size=4") catch unreachable; } }, - builtin.Mode.ReleaseFast, - builtin.Mode.ReleaseSmall => { + builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => { args.append("-O2") catch unreachable; args.append("-fno-stack-protector") catch unreachable; }, @@ -1652,11 +1633,13 @@ pub const TestStep = struct { } pub fn setTarget(self: &TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { - self.target = Target{ .Cross = CrossTarget{ - .arch = target_arch, - .os = target_os, - .environ = target_environ, - } }; + self.target = Target{ + .Cross = CrossTarget{ + .arch = target_arch, + .os = target_os, + .environ = target_environ, + }, + }; } pub fn setExecCmd(self: &TestStep, args: []const ?[]const u8) void { diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 7ac57514c9..24be832d01 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -60,7 +60,7 @@ pub const sigset_t = u32; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name. pub const Sigaction = extern struct { - handler: extern fn(c_int)void, + handler: extern fn(c_int) void, sa_mask: sigset_t, sa_flags: c_int, }; diff --git a/std/c/index.zig b/std/c/index.zig index 34269d2aa2..b5e6b48751 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -1,7 +1,7 @@ const builtin = @import("builtin"); const Os = builtin.Os; -pub use switch(builtin.os) { +pub use switch (builtin.os) { Os.linux => @import("linux.zig"), Os.windows => @import("windows.zig"), Os.macosx, Os.ios => @import("darwin.zig"), @@ -21,8 +21,7 @@ pub extern "c" fn raise(sig: c_int) c_int; pub extern "c" fn read(fd: c_int, buf: &c_void, nbyte: usize) isize; pub extern "c" fn stat(noalias path: &const u8, noalias buf: &Stat) c_int; pub extern "c" fn write(fd: c_int, buf: &const c_void, nbyte: usize) isize; -pub extern "c" fn mmap(addr: ?&c_void, len: usize, prot: c_int, flags: c_int, - fd: c_int, offset: isize) ?&c_void; +pub extern "c" fn mmap(addr: ?&c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?&c_void; pub extern "c" fn munmap(addr: &c_void, len: usize) c_int; pub extern "c" fn unlink(path: &const u8) c_int; pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8; @@ -34,8 +33,7 @@ pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int; pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int; pub extern "c" fn rename(old: &const u8, new: &const u8) c_int; pub extern "c" fn chdir(path: &const u8) c_int; -pub extern "c" fn execve(path: &const u8, argv: &const ?&const u8, - envp: &const ?&const u8) c_int; +pub extern "c" fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) c_int; pub extern "c" fn dup(fd: c_int) c_int; pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize; @@ -54,9 +52,7 @@ pub extern "c" fn realloc(&c_void, usize) ?&c_void; pub extern "c" fn free(&c_void) void; pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int; -pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t, - noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void, - noalias arg: ?&c_void) c_int; +pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t, noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void, noalias arg: ?&c_void) c_int; pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int; pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int; pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int; diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig index 705b2428a7..e0473884cd 100644 --- a/std/crypto/md5.zig +++ b/std/crypto/md5.zig @@ -6,12 +6,25 @@ const debug = @import("../debug/index.zig"); const fmt = @import("../fmt/index.zig"); const RoundParam = struct { - a: usize, b: usize, c: usize, d: usize, - k: usize, s: u32, t: u32 + a: usize, + b: usize, + c: usize, + d: usize, + k: usize, + s: u32, + t: u32, }; fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) RoundParam { - return RoundParam { .a = a, .b = b, .c = c, .d = d, .k = k, .s = s, .t = t }; + return RoundParam{ + .a = a, + .b = b, + .c = c, + .d = d, + .k = k, + .s = s, + .t = t, + }; } pub const Md5 = struct { @@ -99,7 +112,7 @@ pub const Md5 = struct { d.round(d.buf[0..]); for (d.s) |s, j| { - mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Little); + mem.writeInt(out[4 * j..4 * j + 4], s, builtin.Endian.Little); } } @@ -112,30 +125,33 @@ pub const Md5 = struct { while (i < 16) : (i += 1) { // NOTE: Performing or's separately improves perf by ~10% s[i] = 0; - s[i] |= u32(b[i*4+0]); - s[i] |= u32(b[i*4+1]) << 8; - s[i] |= u32(b[i*4+2]) << 16; - s[i] |= u32(b[i*4+3]) << 24; + s[i] |= u32(b[i * 4 + 0]); + s[i] |= u32(b[i * 4 + 1]) << 8; + s[i] |= u32(b[i * 4 + 2]) << 16; + s[i] |= u32(b[i * 4 + 3]) << 24; } - var v: [4]u32 = []u32 { - d.s[0], d.s[1], d.s[2], d.s[3], + var v: [4]u32 = []u32{ + d.s[0], + d.s[1], + d.s[2], + d.s[3], }; - const round0 = comptime []RoundParam { - Rp(0, 1, 2, 3, 0, 7, 0xD76AA478), - Rp(3, 0, 1, 2, 1, 12, 0xE8C7B756), - Rp(2, 3, 0, 1, 2, 17, 0x242070DB), - Rp(1, 2, 3, 0, 3, 22, 0xC1BDCEEE), - Rp(0, 1, 2, 3, 4, 7, 0xF57C0FAF), - Rp(3, 0, 1, 2, 5, 12, 0x4787C62A), - Rp(2, 3, 0, 1, 6, 17, 0xA8304613), - Rp(1, 2, 3, 0, 7, 22, 0xFD469501), - Rp(0, 1, 2, 3, 8, 7, 0x698098D8), - Rp(3, 0, 1, 2, 9, 12, 0x8B44F7AF), + const round0 = comptime []RoundParam{ + Rp(0, 1, 2, 3, 0, 7, 0xD76AA478), + Rp(3, 0, 1, 2, 1, 12, 0xE8C7B756), + Rp(2, 3, 0, 1, 2, 17, 0x242070DB), + Rp(1, 2, 3, 0, 3, 22, 0xC1BDCEEE), + Rp(0, 1, 2, 3, 4, 7, 0xF57C0FAF), + Rp(3, 0, 1, 2, 5, 12, 0x4787C62A), + Rp(2, 3, 0, 1, 6, 17, 0xA8304613), + Rp(1, 2, 3, 0, 7, 22, 0xFD469501), + Rp(0, 1, 2, 3, 8, 7, 0x698098D8), + Rp(3, 0, 1, 2, 9, 12, 0x8B44F7AF), Rp(2, 3, 0, 1, 10, 17, 0xFFFF5BB1), Rp(1, 2, 3, 0, 11, 22, 0x895CD7BE), - Rp(0, 1, 2, 3, 12, 7, 0x6B901122), + Rp(0, 1, 2, 3, 12, 7, 0x6B901122), Rp(3, 0, 1, 2, 13, 12, 0xFD987193), Rp(2, 3, 0, 1, 14, 17, 0xA679438E), Rp(1, 2, 3, 0, 15, 22, 0x49B40821), @@ -145,22 +161,22 @@ pub const Md5 = struct { v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); } - const round1 = comptime []RoundParam { - Rp(0, 1, 2, 3, 1, 5, 0xF61E2562), - Rp(3, 0, 1, 2, 6, 9, 0xC040B340), + const round1 = comptime []RoundParam{ + Rp(0, 1, 2, 3, 1, 5, 0xF61E2562), + Rp(3, 0, 1, 2, 6, 9, 0xC040B340), Rp(2, 3, 0, 1, 11, 14, 0x265E5A51), - Rp(1, 2, 3, 0, 0, 20, 0xE9B6C7AA), - Rp(0, 1, 2, 3, 5, 5, 0xD62F105D), - Rp(3, 0, 1, 2, 10, 9, 0x02441453), + Rp(1, 2, 3, 0, 0, 20, 0xE9B6C7AA), + Rp(0, 1, 2, 3, 5, 5, 0xD62F105D), + Rp(3, 0, 1, 2, 10, 9, 0x02441453), Rp(2, 3, 0, 1, 15, 14, 0xD8A1E681), - Rp(1, 2, 3, 0, 4, 20, 0xE7D3FBC8), - Rp(0, 1, 2, 3, 9, 5, 0x21E1CDE6), - Rp(3, 0, 1, 2, 14, 9, 0xC33707D6), - Rp(2, 3, 0, 1, 3, 14, 0xF4D50D87), - Rp(1, 2, 3, 0, 8, 20, 0x455A14ED), - Rp(0, 1, 2, 3, 13, 5, 0xA9E3E905), - Rp(3, 0, 1, 2, 2, 9, 0xFCEFA3F8), - Rp(2, 3, 0, 1, 7, 14, 0x676F02D9), + Rp(1, 2, 3, 0, 4, 20, 0xE7D3FBC8), + Rp(0, 1, 2, 3, 9, 5, 0x21E1CDE6), + Rp(3, 0, 1, 2, 14, 9, 0xC33707D6), + Rp(2, 3, 0, 1, 3, 14, 0xF4D50D87), + Rp(1, 2, 3, 0, 8, 20, 0x455A14ED), + Rp(0, 1, 2, 3, 13, 5, 0xA9E3E905), + Rp(3, 0, 1, 2, 2, 9, 0xFCEFA3F8), + Rp(2, 3, 0, 1, 7, 14, 0x676F02D9), Rp(1, 2, 3, 0, 12, 20, 0x8D2A4C8A), }; inline for (round1) |r| { @@ -168,46 +184,46 @@ pub const Md5 = struct { v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); } - const round2 = comptime []RoundParam { - Rp(0, 1, 2, 3, 5, 4, 0xFFFA3942), - Rp(3, 0, 1, 2, 8, 11, 0x8771F681), + const round2 = comptime []RoundParam{ + Rp(0, 1, 2, 3, 5, 4, 0xFFFA3942), + Rp(3, 0, 1, 2, 8, 11, 0x8771F681), Rp(2, 3, 0, 1, 11, 16, 0x6D9D6122), Rp(1, 2, 3, 0, 14, 23, 0xFDE5380C), - Rp(0, 1, 2, 3, 1, 4, 0xA4BEEA44), - Rp(3, 0, 1, 2, 4, 11, 0x4BDECFA9), - Rp(2, 3, 0, 1, 7, 16, 0xF6BB4B60), + Rp(0, 1, 2, 3, 1, 4, 0xA4BEEA44), + Rp(3, 0, 1, 2, 4, 11, 0x4BDECFA9), + Rp(2, 3, 0, 1, 7, 16, 0xF6BB4B60), Rp(1, 2, 3, 0, 10, 23, 0xBEBFBC70), - Rp(0, 1, 2, 3, 13, 4, 0x289B7EC6), - Rp(3, 0, 1, 2, 0, 11, 0xEAA127FA), - Rp(2, 3, 0, 1, 3, 16, 0xD4EF3085), - Rp(1, 2, 3, 0, 6, 23, 0x04881D05), - Rp(0, 1, 2, 3, 9, 4, 0xD9D4D039), + Rp(0, 1, 2, 3, 13, 4, 0x289B7EC6), + Rp(3, 0, 1, 2, 0, 11, 0xEAA127FA), + Rp(2, 3, 0, 1, 3, 16, 0xD4EF3085), + Rp(1, 2, 3, 0, 6, 23, 0x04881D05), + Rp(0, 1, 2, 3, 9, 4, 0xD9D4D039), Rp(3, 0, 1, 2, 12, 11, 0xE6DB99E5), Rp(2, 3, 0, 1, 15, 16, 0x1FA27CF8), - Rp(1, 2, 3, 0, 2, 23, 0xC4AC5665), + Rp(1, 2, 3, 0, 2, 23, 0xC4AC5665), }; inline for (round2) |r| { v[r.a] = v[r.a] +% (v[r.b] ^ v[r.c] ^ v[r.d]) +% r.t +% s[r.k]; v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); } - const round3 = comptime []RoundParam { - Rp(0, 1, 2, 3, 0, 6, 0xF4292244), - Rp(3, 0, 1, 2, 7, 10, 0x432AFF97), + const round3 = comptime []RoundParam{ + Rp(0, 1, 2, 3, 0, 6, 0xF4292244), + Rp(3, 0, 1, 2, 7, 10, 0x432AFF97), Rp(2, 3, 0, 1, 14, 15, 0xAB9423A7), - Rp(1, 2, 3, 0, 5, 21, 0xFC93A039), - Rp(0, 1, 2, 3, 12, 6, 0x655B59C3), - Rp(3, 0, 1, 2, 3, 10, 0x8F0CCC92), + Rp(1, 2, 3, 0, 5, 21, 0xFC93A039), + Rp(0, 1, 2, 3, 12, 6, 0x655B59C3), + Rp(3, 0, 1, 2, 3, 10, 0x8F0CCC92), Rp(2, 3, 0, 1, 10, 15, 0xFFEFF47D), - Rp(1, 2, 3, 0, 1, 21, 0x85845DD1), - Rp(0, 1, 2, 3, 8, 6, 0x6FA87E4F), + Rp(1, 2, 3, 0, 1, 21, 0x85845DD1), + Rp(0, 1, 2, 3, 8, 6, 0x6FA87E4F), Rp(3, 0, 1, 2, 15, 10, 0xFE2CE6E0), - Rp(2, 3, 0, 1, 6, 15, 0xA3014314), + Rp(2, 3, 0, 1, 6, 15, 0xA3014314), Rp(1, 2, 3, 0, 13, 21, 0x4E0811A1), - Rp(0, 1, 2, 3, 4, 6, 0xF7537E82), + Rp(0, 1, 2, 3, 4, 6, 0xF7537E82), Rp(3, 0, 1, 2, 11, 10, 0xBD3AF235), - Rp(2, 3, 0, 1, 2, 15, 0x2AD7D2BB), - Rp(1, 2, 3, 0, 9, 21, 0xEB86D391), + Rp(2, 3, 0, 1, 2, 15, 0x2AD7D2BB), + Rp(1, 2, 3, 0, 9, 21, 0xEB86D391), }; inline for (round3) |r| { v[r.a] = v[r.a] +% (v[r.c] ^ (v[r.b] | ~v[r.d])) +% r.t +% s[r.k]; @@ -255,7 +271,7 @@ test "md5 streaming" { } test "md5 aligned final" { - var block = []u8 {0} ** Md5.block_size; + var block = []u8{0} ** Md5.block_size; var out: [Md5.digest_size]u8 = undefined; var h = Md5.init(); diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig index 333597b12d..77324b482f 100644 --- a/std/crypto/sha1.zig +++ b/std/crypto/sha1.zig @@ -7,11 +7,23 @@ const builtin = @import("builtin"); pub const u160 = @IntType(false, 160); const RoundParam = struct { - a: usize, b: usize, c: usize, d: usize, e: usize, i: u32, + a: usize, + b: usize, + c: usize, + d: usize, + e: usize, + i: u32, }; fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam { - return RoundParam { .a = a, .b = b, .c = c, .d = d, .e = e, .i = i }; + return RoundParam{ + .a = a, + .b = b, + .c = c, + .d = d, + .e = e, + .i = i, + }; } pub const Sha1 = struct { @@ -99,7 +111,7 @@ pub const Sha1 = struct { d.round(d.buf[0..]); for (d.s) |s, j| { - mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Big); + mem.writeInt(out[4 * j..4 * j + 4], s, builtin.Endian.Big); } } @@ -108,21 +120,25 @@ pub const Sha1 = struct { var s: [16]u32 = undefined; - var v: [5]u32 = []u32 { - d.s[0], d.s[1], d.s[2], d.s[3], d.s[4], + var v: [5]u32 = []u32{ + d.s[0], + d.s[1], + d.s[2], + d.s[3], + d.s[4], }; - const round0a = comptime []RoundParam { - Rp(0, 1, 2, 3, 4, 0), - Rp(4, 0, 1, 2, 3, 1), - Rp(3, 4, 0, 1, 2, 2), - Rp(2, 3, 4, 0, 1, 3), - Rp(1, 2, 3, 4, 0, 4), - Rp(0, 1, 2, 3, 4, 5), - Rp(4, 0, 1, 2, 3, 6), - Rp(3, 4, 0, 1, 2, 7), - Rp(2, 3, 4, 0, 1, 8), - Rp(1, 2, 3, 4, 0, 9), + const round0a = comptime []RoundParam{ + Rp(0, 1, 2, 3, 4, 0), + Rp(4, 0, 1, 2, 3, 1), + Rp(3, 4, 0, 1, 2, 2), + Rp(2, 3, 4, 0, 1, 3), + Rp(1, 2, 3, 4, 0, 4), + Rp(0, 1, 2, 3, 4, 5), + Rp(4, 0, 1, 2, 3, 6), + Rp(3, 4, 0, 1, 2, 7), + Rp(2, 3, 4, 0, 1, 8), + Rp(1, 2, 3, 4, 0, 9), Rp(0, 1, 2, 3, 4, 10), Rp(4, 0, 1, 2, 3, 11), Rp(3, 4, 0, 1, 2, 12), @@ -131,32 +147,27 @@ pub const Sha1 = struct { Rp(0, 1, 2, 3, 4, 15), }; inline for (round0a) |r| { - s[r.i] = (u32(b[r.i * 4 + 0]) << 24) | - (u32(b[r.i * 4 + 1]) << 16) | - (u32(b[r.i * 4 + 2]) << 8) | - (u32(b[r.i * 4 + 3]) << 0); + s[r.i] = (u32(b[r.i * 4 + 0]) << 24) | (u32(b[r.i * 4 + 1]) << 16) | (u32(b[r.i * 4 + 2]) << 8) | (u32(b[r.i * 4 + 3]) << 0); - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] - +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); v[r.b] = math.rotl(u32, v[r.b], u32(30)); } - const round0b = comptime []RoundParam { + const round0b = comptime []RoundParam{ Rp(4, 0, 1, 2, 3, 16), Rp(3, 4, 0, 1, 2, 17), Rp(2, 3, 4, 0, 1, 18), Rp(1, 2, 3, 4, 0, 19), }; inline for (round0b) |r| { - const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; + const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf]; s[r.i & 0xf] = math.rotl(u32, t, u32(1)); - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] - +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); v[r.b] = math.rotl(u32, v[r.b], u32(30)); } - const round1 = comptime []RoundParam { + const round1 = comptime []RoundParam{ Rp(0, 1, 2, 3, 4, 20), Rp(4, 0, 1, 2, 3, 21), Rp(3, 4, 0, 1, 2, 22), @@ -179,15 +190,14 @@ pub const Sha1 = struct { Rp(1, 2, 3, 4, 0, 39), }; inline for (round1) |r| { - const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; + const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf]; s[r.i & 0xf] = math.rotl(u32, t, u32(1)); - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x6ED9EBA1 +% s[r.i & 0xf] - +% (v[r.b] ^ v[r.c] ^ v[r.d]); + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x6ED9EBA1 +% s[r.i & 0xf] +% (v[r.b] ^ v[r.c] ^ v[r.d]); v[r.b] = math.rotl(u32, v[r.b], u32(30)); } - const round2 = comptime []RoundParam { + const round2 = comptime []RoundParam{ Rp(0, 1, 2, 3, 4, 40), Rp(4, 0, 1, 2, 3, 41), Rp(3, 4, 0, 1, 2, 42), @@ -210,15 +220,14 @@ pub const Sha1 = struct { Rp(1, 2, 3, 4, 0, 59), }; inline for (round2) |r| { - const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; + const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf]; s[r.i & 0xf] = math.rotl(u32, t, u32(1)); - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x8F1BBCDC +% s[r.i & 0xf] - +% ((v[r.b] & v[r.c]) ^ (v[r.b] & v[r.d]) ^ (v[r.c] & v[r.d])); + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x8F1BBCDC +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) ^ (v[r.b] & v[r.d]) ^ (v[r.c] & v[r.d])); v[r.b] = math.rotl(u32, v[r.b], u32(30)); } - const round3 = comptime []RoundParam { + const round3 = comptime []RoundParam{ Rp(0, 1, 2, 3, 4, 60), Rp(4, 0, 1, 2, 3, 61), Rp(3, 4, 0, 1, 2, 62), @@ -241,11 +250,10 @@ pub const Sha1 = struct { Rp(1, 2, 3, 4, 0, 79), }; inline for (round3) |r| { - const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; + const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf]; s[r.i & 0xf] = math.rotl(u32, t, u32(1)); - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0xCA62C1D6 +% s[r.i & 0xf] - +% (v[r.b] ^ v[r.c] ^ v[r.d]); + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0xCA62C1D6 +% s[r.i & 0xf] +% (v[r.b] ^ v[r.c] ^ v[r.d]); v[r.b] = math.rotl(u32, v[r.b], u32(30)); } @@ -286,7 +294,7 @@ test "sha1 streaming" { } test "sha1 aligned final" { - var block = []u8 {0} ** Sha1.block_size; + var block = []u8{0} ** Sha1.block_size; var out: [Sha1.digest_size]u8 = undefined; var h = Sha1.init(); diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig index b70450c0ad..a55182aacd 100644 --- a/std/crypto/sha2.zig +++ b/std/crypto/sha2.zig @@ -9,12 +9,31 @@ const htest = @import("test.zig"); // Sha224 + Sha256 const RoundParam256 = struct { - a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, - i: usize, k: u32, + a: usize, + b: usize, + c: usize, + d: usize, + e: usize, + f: usize, + g: usize, + h: usize, + i: usize, + k: u32, }; fn Rp256(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u32) RoundParam256 { - return RoundParam256 { .a = a, .b = b, .c = c, .d = d, .e = e, .f = f, .g = g, .h = h, .i = i, .k = k }; + return RoundParam256{ + .a = a, + .b = b, + .c = c, + .d = d, + .e = e, + .f = f, + .g = g, + .h = h, + .i = i, + .k = k, + }; } const Sha2Params32 = struct { @@ -29,7 +48,7 @@ const Sha2Params32 = struct { out_len: usize, }; -const Sha224Params = Sha2Params32 { +const Sha224Params = Sha2Params32{ .iv0 = 0xC1059ED8, .iv1 = 0x367CD507, .iv2 = 0x3070DD17, @@ -41,7 +60,7 @@ const Sha224Params = Sha2Params32 { .out_len = 224, }; -const Sha256Params = Sha2Params32 { +const Sha256Params = Sha2Params32{ .iv0 = 0x6A09E667, .iv1 = 0xBB67AE85, .iv2 = 0x3C6EF372, @@ -56,216 +75,215 @@ const Sha256Params = Sha2Params32 { pub const Sha224 = Sha2_32(Sha224Params); pub const Sha256 = Sha2_32(Sha256Params); -fn Sha2_32(comptime params: Sha2Params32) type { return struct { - const Self = this; - const block_size = 64; - const digest_size = params.out_len / 8; - - s: [8]u32, - // Streaming Cache - buf: [64]u8, - buf_len: u8, - total_len: u64, - - pub fn init() Self { - var d: Self = undefined; - d.reset(); - return d; - } - - pub fn reset(d: &Self) void { - d.s[0] = params.iv0; - d.s[1] = params.iv1; - d.s[2] = params.iv2; - d.s[3] = params.iv3; - d.s[4] = params.iv4; - d.s[5] = params.iv5; - d.s[6] = params.iv6; - d.s[7] = params.iv7; - d.buf_len = 0; - d.total_len = 0; - } - - pub fn hash(b: []const u8, out: []u8) void { - var d = Self.init(); - d.update(b); - d.final(out); - } - - pub fn update(d: &Self, b: []const u8) void { - var off: usize = 0; - - // Partial buffer exists from previous update. Copy into buffer then hash. - if (d.buf_len != 0 and d.buf_len + b.len > 64) { - off += 64 - d.buf_len; - mem.copy(u8, d.buf[d.buf_len..], b[0..off]); +fn Sha2_32(comptime params: Sha2Params32) type { + return struct { + const Self = this; + const block_size = 64; + const digest_size = params.out_len / 8; + + s: [8]u32, + // Streaming Cache + buf: [64]u8, + buf_len: u8, + total_len: u64, + + pub fn init() Self { + var d: Self = undefined; + d.reset(); + return d; + } - d.round(d.buf[0..]); + pub fn reset(d: &Self) void { + d.s[0] = params.iv0; + d.s[1] = params.iv1; + d.s[2] = params.iv2; + d.s[3] = params.iv3; + d.s[4] = params.iv4; + d.s[5] = params.iv5; + d.s[6] = params.iv6; + d.s[7] = params.iv7; d.buf_len = 0; + d.total_len = 0; } - // Full middle blocks. - while (off + 64 <= b.len) : (off += 64) { - d.round(b[off..off + 64]); + pub fn hash(b: []const u8, out: []u8) void { + var d = Self.init(); + d.update(b); + d.final(out); } - // Copy any remainder for next pass. - mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + pub fn update(d: &Self, b: []const u8) void { + var off: usize = 0; - d.total_len += b.len; - } + // Partial buffer exists from previous update. Copy into buffer then hash. + if (d.buf_len != 0 and d.buf_len + b.len > 64) { + off += 64 - d.buf_len; + mem.copy(u8, d.buf[d.buf_len..], b[0..off]); - pub fn final(d: &Self, out: []u8) void { - debug.assert(out.len >= params.out_len / 8); + d.round(d.buf[0..]); + d.buf_len = 0; + } - // The buffer here will never be completely full. - mem.set(u8, d.buf[d.buf_len..], 0); + // Full middle blocks. + while (off + 64 <= b.len) : (off += 64) { + d.round(b[off..off + 64]); + } - // Append padding bits. - d.buf[d.buf_len] = 0x80; - d.buf_len += 1; + // Copy any remainder for next pass. + mem.copy(u8, d.buf[d.buf_len..], b[off..]); + d.buf_len += u8(b[off..].len); - // > 448 mod 512 so need to add an extra round to wrap around. - if (64 - d.buf_len < 8) { - d.round(d.buf[0..]); - mem.set(u8, d.buf[0..], 0); + d.total_len += b.len; } - // Append message length. - var i: usize = 1; - var len = d.total_len >> 5; - d.buf[63] = u8(d.total_len & 0x1f) << 3; - while (i < 8) : (i += 1) { - d.buf[63 - i] = u8(len & 0xff); - len >>= 8; - } + pub fn final(d: &Self, out: []u8) void { + debug.assert(out.len >= params.out_len / 8); - d.round(d.buf[0..]); + // The buffer here will never be completely full. + mem.set(u8, d.buf[d.buf_len..], 0); - // May truncate for possible 224 output - const rr = d.s[0 .. params.out_len / 32]; + // Append padding bits. + d.buf[d.buf_len] = 0x80; + d.buf_len += 1; - for (rr) |s, j| { - mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Big); - } - } + // > 448 mod 512 so need to add an extra round to wrap around. + if (64 - d.buf_len < 8) { + d.round(d.buf[0..]); + mem.set(u8, d.buf[0..], 0); + } + + // Append message length. + var i: usize = 1; + var len = d.total_len >> 5; + d.buf[63] = u8(d.total_len & 0x1f) << 3; + while (i < 8) : (i += 1) { + d.buf[63 - i] = u8(len & 0xff); + len >>= 8; + } - fn round(d: &Self, b: []const u8) void { - debug.assert(b.len == 64); + d.round(d.buf[0..]); - var s: [64]u32 = undefined; + // May truncate for possible 224 output + const rr = d.s[0..params.out_len / 32]; - var i: usize = 0; - while (i < 16) : (i += 1) { - s[i] = 0; - s[i] |= u32(b[i*4+0]) << 24; - s[i] |= u32(b[i*4+1]) << 16; - s[i] |= u32(b[i*4+2]) << 8; - s[i] |= u32(b[i*4+3]) << 0; - } - while (i < 64) : (i += 1) { - s[i] = - s[i-16] +% s[i-7] +% - (math.rotr(u32, s[i-15], u32(7)) ^ math.rotr(u32, s[i-15], u32(18)) ^ (s[i-15] >> 3)) +% - (math.rotr(u32, s[i-2], u32(17)) ^ math.rotr(u32, s[i-2], u32(19)) ^ (s[i-2] >> 10)); + for (rr) |s, j| { + mem.writeInt(out[4 * j..4 * j + 4], s, builtin.Endian.Big); + } } - var v: [8]u32 = []u32 { - d.s[0], d.s[1], d.s[2], d.s[3], d.s[4], d.s[5], d.s[6], d.s[7], - }; - - const round0 = comptime []RoundParam256 { - Rp256(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98), - Rp256(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x71374491), - Rp256(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCF), - Rp256(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA5), - Rp256(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25B), - Rp256(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1), - Rp256(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4), - Rp256(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5), - Rp256(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98), - Rp256(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B01), - Rp256(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE), - Rp256(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3), - Rp256(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74), - Rp256(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE), - Rp256(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A7), - Rp256(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174), - Rp256(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C1), - Rp256(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786), - Rp256(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC6), - Rp256(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC), - Rp256(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F), - Rp256(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA), - Rp256(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DC), - Rp256(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA), - Rp256(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152), - Rp256(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D), - Rp256(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C8), - Rp256(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7), - Rp256(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF3), - Rp256(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147), - Rp256(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351), - Rp256(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x14292967), - Rp256(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A85), - Rp256(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B2138), - Rp256(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC), - Rp256(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D13), - Rp256(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A7354), - Rp256(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB), - Rp256(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E), - Rp256(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C85), - Rp256(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A1), - Rp256(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664B), - Rp256(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70), - Rp256(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A3), - Rp256(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819), - Rp256(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD6990624), - Rp256(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E3585), - Rp256(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA070), - Rp256(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116), - Rp256(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C08), - Rp256(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774C), - Rp256(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5), - Rp256(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3), - Rp256(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4A), - Rp256(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F), - Rp256(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3), - Rp256(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE), - Rp256(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F), - Rp256(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814), - Rp256(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC70208), - Rp256(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA), - Rp256(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEB), - Rp256(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7), - Rp256(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2), - }; - inline for (round0) |r| { - v[r.h] = - v[r.h] +% - (math.rotr(u32, v[r.e], u32(6)) ^ math.rotr(u32, v[r.e], u32(11)) ^ math.rotr(u32, v[r.e], u32(25))) +% - (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% - r.k +% s[r.i]; - - v[r.d] = v[r.d] +% v[r.h]; - - v[r.h] = - v[r.h] +% - (math.rotr(u32, v[r.a], u32(2)) ^ math.rotr(u32, v[r.a], u32(13)) ^ math.rotr(u32, v[r.a], u32(22))) +% - ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c])); + fn round(d: &Self, b: []const u8) void { + debug.assert(b.len == 64); + + var s: [64]u32 = undefined; + + var i: usize = 0; + while (i < 16) : (i += 1) { + s[i] = 0; + s[i] |= u32(b[i * 4 + 0]) << 24; + s[i] |= u32(b[i * 4 + 1]) << 16; + s[i] |= u32(b[i * 4 + 2]) << 8; + s[i] |= u32(b[i * 4 + 3]) << 0; + } + while (i < 64) : (i += 1) { + s[i] = s[i - 16] +% s[i - 7] +% (math.rotr(u32, s[i - 15], u32(7)) ^ math.rotr(u32, s[i - 15], u32(18)) ^ (s[i - 15] >> 3)) +% (math.rotr(u32, s[i - 2], u32(17)) ^ math.rotr(u32, s[i - 2], u32(19)) ^ (s[i - 2] >> 10)); + } + + var v: [8]u32 = []u32{ + d.s[0], + d.s[1], + d.s[2], + d.s[3], + d.s[4], + d.s[5], + d.s[6], + d.s[7], + }; + + const round0 = comptime []RoundParam256{ + Rp256(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98), + Rp256(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x71374491), + Rp256(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCF), + Rp256(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA5), + Rp256(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25B), + Rp256(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1), + Rp256(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4), + Rp256(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5), + Rp256(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98), + Rp256(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B01), + Rp256(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE), + Rp256(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3), + Rp256(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74), + Rp256(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE), + Rp256(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A7), + Rp256(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174), + Rp256(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C1), + Rp256(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786), + Rp256(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC6), + Rp256(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC), + Rp256(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F), + Rp256(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA), + Rp256(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DC), + Rp256(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA), + Rp256(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152), + Rp256(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D), + Rp256(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C8), + Rp256(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7), + Rp256(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF3), + Rp256(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147), + Rp256(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351), + Rp256(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x14292967), + Rp256(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A85), + Rp256(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B2138), + Rp256(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC), + Rp256(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D13), + Rp256(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A7354), + Rp256(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB), + Rp256(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E), + Rp256(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C85), + Rp256(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A1), + Rp256(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664B), + Rp256(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70), + Rp256(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A3), + Rp256(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819), + Rp256(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD6990624), + Rp256(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E3585), + Rp256(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA070), + Rp256(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116), + Rp256(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C08), + Rp256(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774C), + Rp256(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5), + Rp256(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3), + Rp256(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4A), + Rp256(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F), + Rp256(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3), + Rp256(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE), + Rp256(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F), + Rp256(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814), + Rp256(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC70208), + Rp256(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA), + Rp256(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEB), + Rp256(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7), + Rp256(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2), + }; + inline for (round0) |r| { + v[r.h] = v[r.h] +% (math.rotr(u32, v[r.e], u32(6)) ^ math.rotr(u32, v[r.e], u32(11)) ^ math.rotr(u32, v[r.e], u32(25))) +% (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% r.k +% s[r.i]; + + v[r.d] = v[r.d] +% v[r.h]; + + v[r.h] = v[r.h] +% (math.rotr(u32, v[r.a], u32(2)) ^ math.rotr(u32, v[r.a], u32(13)) ^ math.rotr(u32, v[r.a], u32(22))) +% ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c])); + } + + d.s[0] +%= v[0]; + d.s[1] +%= v[1]; + d.s[2] +%= v[2]; + d.s[3] +%= v[3]; + d.s[4] +%= v[4]; + d.s[5] +%= v[5]; + d.s[6] +%= v[6]; + d.s[7] +%= v[7]; } - - d.s[0] +%= v[0]; - d.s[1] +%= v[1]; - d.s[2] +%= v[2]; - d.s[3] +%= v[3]; - d.s[4] +%= v[4]; - d.s[5] +%= v[5]; - d.s[6] +%= v[6]; - d.s[7] +%= v[7]; - } -};} + }; +} test "sha224 single" { htest.assertEqualHash(Sha224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""); @@ -320,7 +338,7 @@ test "sha256 streaming" { } test "sha256 aligned final" { - var block = []u8 {0} ** Sha256.block_size; + var block = []u8{0} ** Sha256.block_size; var out: [Sha256.digest_size]u8 = undefined; var h = Sha256.init(); @@ -328,17 +346,35 @@ test "sha256 aligned final" { h.final(out[0..]); } - ///////////////////// // Sha384 + Sha512 const RoundParam512 = struct { - a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, - i: usize, k: u64, + a: usize, + b: usize, + c: usize, + d: usize, + e: usize, + f: usize, + g: usize, + h: usize, + i: usize, + k: u64, }; fn Rp512(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u64) RoundParam512 { - return RoundParam512 { .a = a, .b = b, .c = c, .d = d, .e = e, .f = f, .g = g, .h = h, .i = i, .k = k }; + return RoundParam512{ + .a = a, + .b = b, + .c = c, + .d = d, + .e = e, + .f = f, + .g = g, + .h = h, + .i = i, + .k = k, + }; } const Sha2Params64 = struct { @@ -353,7 +389,7 @@ const Sha2Params64 = struct { out_len: usize, }; -const Sha384Params = Sha2Params64 { +const Sha384Params = Sha2Params64{ .iv0 = 0xCBBB9D5DC1059ED8, .iv1 = 0x629A292A367CD507, .iv2 = 0x9159015A3070DD17, @@ -365,7 +401,7 @@ const Sha384Params = Sha2Params64 { .out_len = 384, }; -const Sha512Params = Sha2Params64 { +const Sha512Params = Sha2Params64{ .iv0 = 0x6A09E667F3BCC908, .iv1 = 0xBB67AE8584CAA73B, .iv2 = 0x3C6EF372FE94F82B, @@ -374,242 +410,241 @@ const Sha512Params = Sha2Params64 { .iv5 = 0x9B05688C2B3E6C1F, .iv6 = 0x1F83D9ABFB41BD6B, .iv7 = 0x5BE0CD19137E2179, - .out_len = 512 + .out_len = 512, }; pub const Sha384 = Sha2_64(Sha384Params); pub const Sha512 = Sha2_64(Sha512Params); -fn Sha2_64(comptime params: Sha2Params64) type { return struct { - const Self = this; - const block_size = 128; - const digest_size = params.out_len / 8; - - s: [8]u64, - // Streaming Cache - buf: [128]u8, - buf_len: u8, - total_len: u128, - - pub fn init() Self { - var d: Self = undefined; - d.reset(); - return d; - } - - pub fn reset(d: &Self) void { - d.s[0] = params.iv0; - d.s[1] = params.iv1; - d.s[2] = params.iv2; - d.s[3] = params.iv3; - d.s[4] = params.iv4; - d.s[5] = params.iv5; - d.s[6] = params.iv6; - d.s[7] = params.iv7; - d.buf_len = 0; - d.total_len = 0; - } - - pub fn hash(b: []const u8, out: []u8) void { - var d = Self.init(); - d.update(b); - d.final(out); - } - - pub fn update(d: &Self, b: []const u8) void { - var off: usize = 0; - - // Partial buffer exists from previous update. Copy into buffer then hash. - if (d.buf_len != 0 and d.buf_len + b.len > 128) { - off += 128 - d.buf_len; - mem.copy(u8, d.buf[d.buf_len..], b[0..off]); +fn Sha2_64(comptime params: Sha2Params64) type { + return struct { + const Self = this; + const block_size = 128; + const digest_size = params.out_len / 8; + + s: [8]u64, + // Streaming Cache + buf: [128]u8, + buf_len: u8, + total_len: u128, + + pub fn init() Self { + var d: Self = undefined; + d.reset(); + return d; + } - d.round(d.buf[0..]); + pub fn reset(d: &Self) void { + d.s[0] = params.iv0; + d.s[1] = params.iv1; + d.s[2] = params.iv2; + d.s[3] = params.iv3; + d.s[4] = params.iv4; + d.s[5] = params.iv5; + d.s[6] = params.iv6; + d.s[7] = params.iv7; d.buf_len = 0; + d.total_len = 0; } - // Full middle blocks. - while (off + 128 <= b.len) : (off += 128) { - d.round(b[off..off + 128]); + pub fn hash(b: []const u8, out: []u8) void { + var d = Self.init(); + d.update(b); + d.final(out); } - // Copy any remainder for next pass. - mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + pub fn update(d: &Self, b: []const u8) void { + var off: usize = 0; - d.total_len += b.len; - } + // Partial buffer exists from previous update. Copy into buffer then hash. + if (d.buf_len != 0 and d.buf_len + b.len > 128) { + off += 128 - d.buf_len; + mem.copy(u8, d.buf[d.buf_len..], b[0..off]); - pub fn final(d: &Self, out: []u8) void { - debug.assert(out.len >= params.out_len / 8); + d.round(d.buf[0..]); + d.buf_len = 0; + } - // The buffer here will never be completely full. - mem.set(u8, d.buf[d.buf_len..], 0); + // Full middle blocks. + while (off + 128 <= b.len) : (off += 128) { + d.round(b[off..off + 128]); + } - // Append padding bits. - d.buf[d.buf_len] = 0x80; - d.buf_len += 1; + // Copy any remainder for next pass. + mem.copy(u8, d.buf[d.buf_len..], b[off..]); + d.buf_len += u8(b[off..].len); - // > 896 mod 1024 so need to add an extra round to wrap around. - if (128 - d.buf_len < 16) { - d.round(d.buf[0..]); - mem.set(u8, d.buf[0..], 0); + d.total_len += b.len; } - // Append message length. - var i: usize = 1; - var len = d.total_len >> 5; - d.buf[127] = u8(d.total_len & 0x1f) << 3; - while (i < 16) : (i += 1) { - d.buf[127 - i] = u8(len & 0xff); - len >>= 8; - } + pub fn final(d: &Self, out: []u8) void { + debug.assert(out.len >= params.out_len / 8); - d.round(d.buf[0..]); + // The buffer here will never be completely full. + mem.set(u8, d.buf[d.buf_len..], 0); - // May truncate for possible 384 output - const rr = d.s[0 .. params.out_len / 64]; + // Append padding bits. + d.buf[d.buf_len] = 0x80; + d.buf_len += 1; - for (rr) |s, j| { - mem.writeInt(out[8*j .. 8*j + 8], s, builtin.Endian.Big); - } - } - - fn round(d: &Self, b: []const u8) void { - debug.assert(b.len == 128); - - var s: [80]u64 = undefined; - - var i: usize = 0; - while (i < 16) : (i += 1) { - s[i] = 0; - s[i] |= u64(b[i*8+0]) << 56; - s[i] |= u64(b[i*8+1]) << 48; - s[i] |= u64(b[i*8+2]) << 40; - s[i] |= u64(b[i*8+3]) << 32; - s[i] |= u64(b[i*8+4]) << 24; - s[i] |= u64(b[i*8+5]) << 16; - s[i] |= u64(b[i*8+6]) << 8; - s[i] |= u64(b[i*8+7]) << 0; - } - while (i < 80) : (i += 1) { - s[i] = - s[i-16] +% s[i-7] +% - (math.rotr(u64, s[i-15], u64(1)) ^ math.rotr(u64, s[i-15], u64(8)) ^ (s[i-15] >> 7)) +% - (math.rotr(u64, s[i-2], u64(19)) ^ math.rotr(u64, s[i-2], u64(61)) ^ (s[i-2] >> 6)); - } + // > 896 mod 1024 so need to add an extra round to wrap around. + if (128 - d.buf_len < 16) { + d.round(d.buf[0..]); + mem.set(u8, d.buf[0..], 0); + } - var v: [8]u64 = []u64 { - d.s[0], d.s[1], d.s[2], d.s[3], d.s[4], d.s[5], d.s[6], d.s[7], - }; - - const round0 = comptime []RoundParam512 { - Rp512(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98D728AE22), - Rp512(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x7137449123EF65CD), - Rp512(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCFEC4D3B2F), - Rp512(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA58189DBBC), - Rp512(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25BF348B538), - Rp512(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1B605D019), - Rp512(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4AF194F9B), - Rp512(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5DA6D8118), - Rp512(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98A3030242), - Rp512(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B0145706FBE), - Rp512(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE4EE4B28C), - Rp512(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3D5FFB4E2), - Rp512(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74F27B896F), - Rp512(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE3B1696B1), - Rp512(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A725C71235), - Rp512(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174CF692694), - Rp512(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C19EF14AD2), - Rp512(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786384F25E3), - Rp512(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC68B8CD5B5), - Rp512(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC77AC9C65), - Rp512(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F592B0275), - Rp512(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA6EA6E483), - Rp512(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DCBD41FBD4), - Rp512(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA831153B5), - Rp512(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152EE66DFAB), - Rp512(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D2DB43210), - Rp512(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C898FB213F), - Rp512(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7BEEF0EE4), - Rp512(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF33DA88FC2), - Rp512(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147930AA725), - Rp512(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351E003826F), - Rp512(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x142929670A0E6E70), - Rp512(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A8546D22FFC), - Rp512(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B21385C26C926), - Rp512(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC5AC42AED), - Rp512(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D139D95B3DF), - Rp512(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A73548BAF63DE), - Rp512(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB3C77B2A8), - Rp512(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E47EDAEE6), - Rp512(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C851482353B), - Rp512(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A14CF10364), - Rp512(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664BBC423001), - Rp512(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70D0F89791), - Rp512(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A30654BE30), - Rp512(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819D6EF5218), - Rp512(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD69906245565A910), - Rp512(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E35855771202A), - Rp512(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA07032BBD1B8), - Rp512(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116B8D2D0C8), - Rp512(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C085141AB53), - Rp512(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774CDF8EEB99), - Rp512(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5E19B48A8), - Rp512(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3C5C95A63), - Rp512(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4AE3418ACB), - Rp512(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F7763E373), - Rp512(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3D6B2B8A3), - Rp512(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE5DEFB2FC), - Rp512(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F43172F60), - Rp512(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814A1F0AB72), - Rp512(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC702081A6439EC), - Rp512(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA23631E28), - Rp512(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEBDE82BDE9), - Rp512(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7B2C67915), - Rp512(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2E372532B), - Rp512(0, 1, 2, 3, 4, 5, 6, 7, 64, 0xCA273ECEEA26619C), - Rp512(7, 0, 1, 2, 3, 4, 5, 6, 65, 0xD186B8C721C0C207), - Rp512(6, 7, 0, 1, 2, 3, 4, 5, 66, 0xEADA7DD6CDE0EB1E), - Rp512(5, 6, 7, 0, 1, 2, 3, 4, 67, 0xF57D4F7FEE6ED178), - Rp512(4, 5, 6, 7, 0, 1, 2, 3, 68, 0x06F067AA72176FBA), - Rp512(3, 4, 5, 6, 7, 0, 1, 2, 69, 0x0A637DC5A2C898A6), - Rp512(2, 3, 4, 5, 6, 7, 0, 1, 70, 0x113F9804BEF90DAE), - Rp512(1, 2, 3, 4, 5, 6, 7, 0, 71, 0x1B710B35131C471B), - Rp512(0, 1, 2, 3, 4, 5, 6, 7, 72, 0x28DB77F523047D84), - Rp512(7, 0, 1, 2, 3, 4, 5, 6, 73, 0x32CAAB7B40C72493), - Rp512(6, 7, 0, 1, 2, 3, 4, 5, 74, 0x3C9EBE0A15C9BEBC), - Rp512(5, 6, 7, 0, 1, 2, 3, 4, 75, 0x431D67C49C100D4C), - Rp512(4, 5, 6, 7, 0, 1, 2, 3, 76, 0x4CC5D4BECB3E42B6), - Rp512(3, 4, 5, 6, 7, 0, 1, 2, 77, 0x597F299CFC657E2A), - Rp512(2, 3, 4, 5, 6, 7, 0, 1, 78, 0x5FCB6FAB3AD6FAEC), - Rp512(1, 2, 3, 4, 5, 6, 7, 0, 79, 0x6C44198C4A475817), - }; - inline for (round0) |r| { - v[r.h] = - v[r.h] +% - (math.rotr(u64, v[r.e], u64(14)) ^ math.rotr(u64, v[r.e], u64(18)) ^ math.rotr(u64, v[r.e], u64(41))) +% - (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% - r.k +% s[r.i]; - - v[r.d] = v[r.d] +% v[r.h]; - - v[r.h] = - v[r.h] +% - (math.rotr(u64, v[r.a], u64(28)) ^ math.rotr(u64, v[r.a], u64(34)) ^ math.rotr(u64, v[r.a], u64(39))) +% - ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c])); + // Append message length. + var i: usize = 1; + var len = d.total_len >> 5; + d.buf[127] = u8(d.total_len & 0x1f) << 3; + while (i < 16) : (i += 1) { + d.buf[127 - i] = u8(len & 0xff); + len >>= 8; + } + + d.round(d.buf[0..]); + + // May truncate for possible 384 output + const rr = d.s[0..params.out_len / 64]; + + for (rr) |s, j| { + mem.writeInt(out[8 * j..8 * j + 8], s, builtin.Endian.Big); + } } - d.s[0] +%= v[0]; - d.s[1] +%= v[1]; - d.s[2] +%= v[2]; - d.s[3] +%= v[3]; - d.s[4] +%= v[4]; - d.s[5] +%= v[5]; - d.s[6] +%= v[6]; - d.s[7] +%= v[7]; - } -};} + fn round(d: &Self, b: []const u8) void { + debug.assert(b.len == 128); + + var s: [80]u64 = undefined; + + var i: usize = 0; + while (i < 16) : (i += 1) { + s[i] = 0; + s[i] |= u64(b[i * 8 + 0]) << 56; + s[i] |= u64(b[i * 8 + 1]) << 48; + s[i] |= u64(b[i * 8 + 2]) << 40; + s[i] |= u64(b[i * 8 + 3]) << 32; + s[i] |= u64(b[i * 8 + 4]) << 24; + s[i] |= u64(b[i * 8 + 5]) << 16; + s[i] |= u64(b[i * 8 + 6]) << 8; + s[i] |= u64(b[i * 8 + 7]) << 0; + } + while (i < 80) : (i += 1) { + s[i] = s[i - 16] +% s[i - 7] +% (math.rotr(u64, s[i - 15], u64(1)) ^ math.rotr(u64, s[i - 15], u64(8)) ^ (s[i - 15] >> 7)) +% (math.rotr(u64, s[i - 2], u64(19)) ^ math.rotr(u64, s[i - 2], u64(61)) ^ (s[i - 2] >> 6)); + } + + var v: [8]u64 = []u64{ + d.s[0], + d.s[1], + d.s[2], + d.s[3], + d.s[4], + d.s[5], + d.s[6], + d.s[7], + }; + + const round0 = comptime []RoundParam512{ + Rp512(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98D728AE22), + Rp512(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x7137449123EF65CD), + Rp512(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCFEC4D3B2F), + Rp512(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA58189DBBC), + Rp512(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25BF348B538), + Rp512(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1B605D019), + Rp512(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4AF194F9B), + Rp512(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5DA6D8118), + Rp512(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98A3030242), + Rp512(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B0145706FBE), + Rp512(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE4EE4B28C), + Rp512(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3D5FFB4E2), + Rp512(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74F27B896F), + Rp512(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE3B1696B1), + Rp512(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A725C71235), + Rp512(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174CF692694), + Rp512(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C19EF14AD2), + Rp512(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786384F25E3), + Rp512(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC68B8CD5B5), + Rp512(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC77AC9C65), + Rp512(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F592B0275), + Rp512(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA6EA6E483), + Rp512(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DCBD41FBD4), + Rp512(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA831153B5), + Rp512(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152EE66DFAB), + Rp512(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D2DB43210), + Rp512(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C898FB213F), + Rp512(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7BEEF0EE4), + Rp512(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF33DA88FC2), + Rp512(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147930AA725), + Rp512(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351E003826F), + Rp512(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x142929670A0E6E70), + Rp512(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A8546D22FFC), + Rp512(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B21385C26C926), + Rp512(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC5AC42AED), + Rp512(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D139D95B3DF), + Rp512(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A73548BAF63DE), + Rp512(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB3C77B2A8), + Rp512(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E47EDAEE6), + Rp512(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C851482353B), + Rp512(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A14CF10364), + Rp512(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664BBC423001), + Rp512(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70D0F89791), + Rp512(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A30654BE30), + Rp512(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819D6EF5218), + Rp512(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD69906245565A910), + Rp512(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E35855771202A), + Rp512(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA07032BBD1B8), + Rp512(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116B8D2D0C8), + Rp512(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C085141AB53), + Rp512(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774CDF8EEB99), + Rp512(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5E19B48A8), + Rp512(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3C5C95A63), + Rp512(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4AE3418ACB), + Rp512(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F7763E373), + Rp512(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3D6B2B8A3), + Rp512(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE5DEFB2FC), + Rp512(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F43172F60), + Rp512(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814A1F0AB72), + Rp512(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC702081A6439EC), + Rp512(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA23631E28), + Rp512(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEBDE82BDE9), + Rp512(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7B2C67915), + Rp512(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2E372532B), + Rp512(0, 1, 2, 3, 4, 5, 6, 7, 64, 0xCA273ECEEA26619C), + Rp512(7, 0, 1, 2, 3, 4, 5, 6, 65, 0xD186B8C721C0C207), + Rp512(6, 7, 0, 1, 2, 3, 4, 5, 66, 0xEADA7DD6CDE0EB1E), + Rp512(5, 6, 7, 0, 1, 2, 3, 4, 67, 0xF57D4F7FEE6ED178), + Rp512(4, 5, 6, 7, 0, 1, 2, 3, 68, 0x06F067AA72176FBA), + Rp512(3, 4, 5, 6, 7, 0, 1, 2, 69, 0x0A637DC5A2C898A6), + Rp512(2, 3, 4, 5, 6, 7, 0, 1, 70, 0x113F9804BEF90DAE), + Rp512(1, 2, 3, 4, 5, 6, 7, 0, 71, 0x1B710B35131C471B), + Rp512(0, 1, 2, 3, 4, 5, 6, 7, 72, 0x28DB77F523047D84), + Rp512(7, 0, 1, 2, 3, 4, 5, 6, 73, 0x32CAAB7B40C72493), + Rp512(6, 7, 0, 1, 2, 3, 4, 5, 74, 0x3C9EBE0A15C9BEBC), + Rp512(5, 6, 7, 0, 1, 2, 3, 4, 75, 0x431D67C49C100D4C), + Rp512(4, 5, 6, 7, 0, 1, 2, 3, 76, 0x4CC5D4BECB3E42B6), + Rp512(3, 4, 5, 6, 7, 0, 1, 2, 77, 0x597F299CFC657E2A), + Rp512(2, 3, 4, 5, 6, 7, 0, 1, 78, 0x5FCB6FAB3AD6FAEC), + Rp512(1, 2, 3, 4, 5, 6, 7, 0, 79, 0x6C44198C4A475817), + }; + inline for (round0) |r| { + v[r.h] = v[r.h] +% (math.rotr(u64, v[r.e], u64(14)) ^ math.rotr(u64, v[r.e], u64(18)) ^ math.rotr(u64, v[r.e], u64(41))) +% (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% r.k +% s[r.i]; + + v[r.d] = v[r.d] +% v[r.h]; + + v[r.h] = v[r.h] +% (math.rotr(u64, v[r.a], u64(28)) ^ math.rotr(u64, v[r.a], u64(34)) ^ math.rotr(u64, v[r.a], u64(39))) +% ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c])); + } + + d.s[0] +%= v[0]; + d.s[1] +%= v[1]; + d.s[2] +%= v[2]; + d.s[3] +%= v[3]; + d.s[4] +%= v[4]; + d.s[5] +%= v[5]; + d.s[6] +%= v[6]; + d.s[7] +%= v[7]; + } + }; +} test "sha384 single" { const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"; @@ -680,7 +715,7 @@ test "sha512 streaming" { } test "sha512 aligned final" { - var block = []u8 {0} ** Sha512.block_size; + var block = []u8{0} ** Sha512.block_size; var out: [Sha512.digest_size]u8 = undefined; var h = Sha512.init(); diff --git a/std/crypto/test.zig b/std/crypto/test.zig index 3fa24272e5..c0a96a98de 100644 --- a/std/crypto/test.zig +++ b/std/crypto/test.zig @@ -14,7 +14,7 @@ pub fn assertEqualHash(comptime Hasher: var, comptime expected: []const u8, inpu pub fn assertEqual(comptime expected: []const u8, input: []const u8) void { var expected_bytes: [expected.len / 2]u8 = undefined; for (expected_bytes) |*r, i| { - r.* = fmt.parseInt(u8, expected[2 * i .. 2 * i + 2], 16) catch unreachable; + r.* = fmt.parseInt(u8, expected[2 * i..2 * i + 2], 16) catch unreachable; } debug.assert(mem.eql(u8, expected_bytes, input)); diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig index 0756f9a4eb..c5c4f9fe10 100644 --- a/std/crypto/throughput_test.zig +++ b/std/crypto/throughput_test.zig @@ -11,7 +11,7 @@ const Timer = time.Timer; const HashFunction = @import("md5.zig").Md5; const MiB = 1024 * 1024; -const BytesToHash = 1024 * MiB; +const BytesToHash = 1024 * MiB; pub fn main() !void { var stdout_file = try std.io.getStdOut(); diff --git a/std/cstr.zig b/std/cstr.zig index d396dcbce3..c9f3026064 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -9,7 +9,6 @@ pub const line_sep = switch (builtin.os) { else => "\n", }; - pub fn len(ptr: &const u8) usize { var count: usize = 0; while (ptr[count] != 0) : (count += 1) {} @@ -95,7 +94,7 @@ pub const NullTerminated2DArray = struct { } index_buf[i] = null; - return NullTerminated2DArray { + return NullTerminated2DArray{ .allocator = allocator, .byte_count = byte_count, .ptr = @ptrCast(?&?&u8, buf.ptr), @@ -107,4 +106,3 @@ pub const NullTerminated2DArray = struct { self.allocator.free(buf[0..self.byte_count]); } }; - diff --git a/std/debug/failing_allocator.zig b/std/debug/failing_allocator.zig index f876b7902d..6b5edff5bf 100644 --- a/std/debug/failing_allocator.zig +++ b/std/debug/failing_allocator.zig @@ -13,14 +13,14 @@ pub const FailingAllocator = struct { deallocations: usize, pub fn init(allocator: &mem.Allocator, fail_index: usize) FailingAllocator { - return FailingAllocator { + return FailingAllocator{ .internal_allocator = allocator, .fail_index = fail_index, .index = 0, .allocated_bytes = 0, .freed_bytes = 0, .deallocations = 0, - .allocator = mem.Allocator { + .allocator = mem.Allocator{ .allocFn = alloc, .reallocFn = realloc, .freeFn = free, diff --git a/std/debug/index.zig b/std/debug/index.zig index 36ac2e8a3f..92e565b391 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -227,8 +227,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: us else => return err, } } else |err| switch (err) { - error.MissingDebugInfo, - error.InvalidDebugInfo => { + error.MissingDebugInfo, error.InvalidDebugInfo => { try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name); }, else => return err, @@ -597,10 +596,12 @@ fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) ! } fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue { - return FormValue{ .Const = Constant{ - .signed = signed, - .payload = try readAllocBytes(allocator, in_stream, size), - } }; + return FormValue{ + .Const = Constant{ + .signed = signed, + .payload = try readAllocBytes(allocator, in_stream, size), + }, + }; } fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 { @@ -621,7 +622,7 @@ fn parseFormValueRef(allocator: &mem.Allocator, in_stream: var, comptime T: type return parseFormValueRefLen(allocator, in_stream, block_len); } -const ParseFormValueError = error { +const ParseFormValueError = error{ EndOfStream, Io, BadFd, @@ -645,8 +646,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64 DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2), DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4), DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8), - DW.FORM_udata, - DW.FORM_sdata => { + DW.FORM_udata, DW.FORM_sdata => { const block_len = try readULeb128(in_stream); const signed = form_id == DW.FORM_sdata; return parseFormValueConstant(allocator, in_stream, signed, block_len); diff --git a/std/dwarf.zig b/std/dwarf.zig index 04456d9e5c..76ed122447 100644 --- a/std/dwarf.zig +++ b/std/dwarf.zig @@ -337,7 +337,6 @@ pub const AT_PGI_lbase = 0x3a00; pub const AT_PGI_soffset = 0x3a01; pub const AT_PGI_lstride = 0x3a02; - pub const OP_addr = 0x03; pub const OP_deref = 0x06; pub const OP_const1u = 0x08; @@ -577,7 +576,6 @@ pub const ATE_HP_unsigned_fixed = 0x8e; // Cobol. pub const ATE_HP_VAX_complex_float = 0x8f; // F or G floating complex. pub const ATE_HP_VAX_complex_float_d = 0x90; // D floating complex. - pub const CFA_advance_loc = 0x40; pub const CFA_offset = 0x80; pub const CFA_restore = 0xc0; diff --git a/std/elf.zig b/std/elf.zig index 1764829bc8..29b9473f98 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -123,13 +123,11 @@ pub const DT_SYMINFO = 0x6ffffeff; pub const DT_ADDRRNGHI = 0x6ffffeff; pub const DT_ADDRNUM = 11; - pub const DT_VERSYM = 0x6ffffff0; pub const DT_RELACOUNT = 0x6ffffff9; pub const DT_RELCOUNT = 0x6ffffffa; - pub const DT_FLAGS_1 = 0x6ffffffb; pub const DT_VERDEF = 0x6ffffffc; @@ -139,13 +137,10 @@ pub const DT_VERNEED = 0x6ffffffe; pub const DT_VERNEEDNUM = 0x6fffffff; pub const DT_VERSIONTAGNUM = 16; - - pub const DT_AUXILIARY = 0x7ffffffd; pub const DT_FILTER = 0x7fffffff; pub const DT_EXTRANUM = 3; - pub const DT_SPARC_REGISTER = 0x70000001; pub const DT_SPARC_NUM = 2; @@ -434,9 +429,7 @@ pub const Elf = struct { try elf.in_file.seekForward(4); const header_size = try in.readInt(elf.endian, u16); - if ((elf.is_64 and header_size != 64) or - (!elf.is_64 and header_size != 52)) - { + if ((elf.is_64 and header_size != 64) or (!elf.is_64 and header_size != 52)) { return error.InvalidFormat; } @@ -467,16 +460,16 @@ pub const Elf = struct { if (sh_entry_size != 64) return error.InvalidFormat; for (elf.section_headers) |*elf_section| { - elf_section.name = try in.readInt(elf.endian, u32); - elf_section.sh_type = try in.readInt(elf.endian, u32); - elf_section.flags = try in.readInt(elf.endian, u64); - elf_section.addr = try in.readInt(elf.endian, u64); - elf_section.offset = try in.readInt(elf.endian, u64); - elf_section.size = try in.readInt(elf.endian, u64); - elf_section.link = try in.readInt(elf.endian, u32); - elf_section.info = try in.readInt(elf.endian, u32); - elf_section.addr_align = try in.readInt(elf.endian, u64); - elf_section.ent_size = try in.readInt(elf.endian, u64); + elf_section.name = try in.readInt(elf.endian, u32); + elf_section.sh_type = try in.readInt(elf.endian, u32); + elf_section.flags = try in.readInt(elf.endian, u64); + elf_section.addr = try in.readInt(elf.endian, u64); + elf_section.offset = try in.readInt(elf.endian, u64); + elf_section.size = try in.readInt(elf.endian, u64); + elf_section.link = try in.readInt(elf.endian, u32); + elf_section.info = try in.readInt(elf.endian, u32); + elf_section.addr_align = try in.readInt(elf.endian, u64); + elf_section.ent_size = try in.readInt(elf.endian, u64); } } else { if (sh_entry_size != 40) return error.InvalidFormat; @@ -513,8 +506,7 @@ pub const Elf = struct { pub fn close(elf: &Elf) void { elf.allocator.free(elf.section_headers); - if (elf.auto_close_stream) - elf.in_file.close(); + if (elf.auto_close_stream) elf.in_file.close(); } pub fn findSection(elf: &Elf, name: []const u8) !?&SectionHeader { @@ -852,27 +844,27 @@ pub const Elf_MIPS_ABIFlags_v0 = extern struct { flags2: Elf32_Word, }; -pub const Ehdr = switch(@sizeOf(usize)) { +pub const Ehdr = switch (@sizeOf(usize)) { 4 => Elf32_Ehdr, 8 => Elf64_Ehdr, else => @compileError("expected pointer size of 32 or 64"), }; -pub const Phdr = switch(@sizeOf(usize)) { +pub const Phdr = switch (@sizeOf(usize)) { 4 => Elf32_Phdr, 8 => Elf64_Phdr, else => @compileError("expected pointer size of 32 or 64"), }; -pub const Sym = switch(@sizeOf(usize)) { +pub const Sym = switch (@sizeOf(usize)) { 4 => Elf32_Sym, 8 => Elf64_Sym, else => @compileError("expected pointer size of 32 or 64"), }; -pub const Verdef = switch(@sizeOf(usize)) { +pub const Verdef = switch (@sizeOf(usize)) { 4 => Elf32_Verdef, 8 => Elf64_Verdef, else => @compileError("expected pointer size of 32 or 64"), }; -pub const Verdaux = switch(@sizeOf(usize)) { +pub const Verdaux = switch (@sizeOf(usize)) { 4 => Elf32_Verdaux, 8 => Elf64_Verdaux, else => @compileError("expected pointer size of 32 or 64"), diff --git a/std/event.zig b/std/event.zig index 558bd2a188..6ee8ab35f1 100644 --- a/std/event.zig +++ b/std/event.zig @@ -76,19 +76,14 @@ pub const TcpServer = struct { } continue; }, - error.ConnectionAborted, - error.FileDescriptorClosed => continue, + error.ConnectionAborted, error.FileDescriptorClosed => continue, error.PageFault => unreachable, error.InvalidSyscall => unreachable, error.FileDescriptorNotASocket => unreachable, error.OperationNotSupported => unreachable, - error.SystemFdQuotaExceeded, - error.SystemResources, - error.ProtocolFailure, - error.BlockedByFirewall, - error.Unexpected => { + error.SystemFdQuotaExceeded, error.SystemResources, error.ProtocolFailure, error.BlockedByFirewall, error.Unexpected => { @panic("TODO handle this error"); }, } @@ -121,7 +116,6 @@ pub const Loop = struct { pub fn removeFd(self: &Loop, fd: i32) void { std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; } - async fn waitFd(self: &Loop, fd: i32) !void { defer self.removeFd(fd); suspend |p| { @@ -169,7 +163,6 @@ test "listen on a port, send bytes, receive bytes" { tcp_server: TcpServer, const Self = this; - async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address, _socket: &const std.os.File) void { const self = @fieldParentPtr(Self, "tcp_server", tcp_server); var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 @@ -184,7 +177,6 @@ test "listen on a port, send bytes, receive bytes" { cancel p; } } - async fn errorableHandler(self: &Self, _addr: &const std.net.Address, _socket: &const std.os.File) !void { const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733 var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 @@ -207,7 +199,6 @@ test "listen on a port, send bytes, receive bytes" { defer cancel p; loop.run(); } - async fn doAsyncTest(loop: &Loop, address: &const std.net.Address) void { errdefer @panic("test failure"); diff --git a/std/fmt/errol/enum3.zig b/std/fmt/errol/enum3.zig index f8299d3c6f..7663f9b5d9 100644 --- a/std/fmt/errol/enum3.zig +++ b/std/fmt/errol/enum3.zig @@ -1,4 +1,4 @@ -pub const enum3 = []u64 { +pub const enum3 = []u64{ 0x4e2e2785c3a2a20b, 0x240a28877a09a4e1, 0x728fca36c06cf106, @@ -439,13 +439,13 @@ const Slab = struct { }; fn slab(str: []const u8, exp: i32) Slab { - return Slab { + return Slab{ .str = str, .exp = exp, }; } -pub const enum3_data = []Slab { +pub const enum3_data = []Slab{ slab("40648030339495312", 69), slab("4498645355592131", -134), slab("678321594594593", 244), @@ -879,4 +879,3 @@ pub const enum3_data = []Slab { slab("32216657306260762", 218), slab("30423431424080128", 219), }; - -- cgit v1.2.3 From afdfbc0367b261ea43d5618027e23022c01e2c93 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 26 May 2018 23:17:24 -0400 Subject: zig fmt: delete empty comments that do nothing --- std/zig/parser_test.zig | 16 ++++++++++++++++ std/zig/render.zig | 9 +++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 909220a6f6..a2ee77796b 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -28,6 +28,12 @@ test "zig fmt: array literal with hint" { \\ 5, \\ 6, \\ 7 }; + \\const a = []u8{ + \\ 1, 2, + \\ 3, 4, // + \\ 5, 6, // + \\ 7, 8, // + \\}; , \\const a = []u8{ \\ 1, 2, // @@ -53,6 +59,16 @@ test "zig fmt: array literal with hint" { \\ 5, 6, // \\ 7, \\}; + \\const a = []u8{ + \\ 1, + \\ 2, + \\ 3, + \\ 4, + \\ 5, + \\ 6, + \\ 7, + \\ 8, + \\}; \\ ); } diff --git a/std/zig/render.zig b/std/zig/render.zig index fa3755b719..49138e0e78 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -1583,8 +1583,13 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent } } - if (space == Space.IgnoreEmptyComment and mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ").len == 2) { - return stream.writeByte(' '); + const comment_is_empty = mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ").len == 2; + if (comment_is_empty) { + switch (space) { + Space.IgnoreEmptyComment => return stream.writeByte(' '), + Space.Newline => return stream.writeByte('\n'), + else => {}, + } } var loc = tree.tokenLocationPtr(token.end, next_token); -- cgit v1.2.3 From 3fed10883bd6916147cce8060949040770532daf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 26 May 2018 23:24:43 -0400 Subject: zig fmt: array literals with no trailing comma all on one line --- std/zig/parser_test.zig | 10 ++++------ std/zig/render.zig | 32 ++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index a2ee77796b..e83f99c972 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -229,16 +229,14 @@ test "zig fmt: add trailing comma to array literal" { \\comptime { \\ return []u16{'m', 's', 'y', 's', '-' // hi \\ }; + \\ return []u16{'m', 's', 'y', 's', + \\ '-'}; \\} , \\comptime { - \\ return []u16{ - \\ 'm', - \\ 's', - \\ 'y', - \\ 's', - \\ '-', // hi + \\ return []u16{ 'm', 's', 'y', 's', '-' // hi \\ }; + \\ return []u16{ 'm', 's', 'y', 's', '-'}; \\} \\ ); diff --git a/std/zig/render.zig b/std/zig/render.zig index 49138e0e78..9805b7cf75 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -557,11 +557,11 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind }; - const new_indent = indent + indent_delta; - try renderToken(tree, stream, lbrace, new_indent, Space.Newline); - try stream.writeByteNTimes(' ', new_indent); - if (maybe_row_size) |row_size| { + const new_indent = indent + indent_delta; + try renderToken(tree, stream, lbrace, new_indent, Space.Newline); + try stream.writeByteNTimes(' ', new_indent); + var it = exprs.iterator(0); var i: usize = 0; while (it.next()) |expr| { @@ -597,6 +597,30 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind return; } + const src_has_trailing_comma = blk: { + const maybe_comma = tree.prevToken(suffix_op.rtoken); + break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma; + }; + if (!src_has_trailing_comma) { + try renderToken(tree, stream, lbrace, indent, Space.Space); + var it = exprs.iterator(0); + while (it.next()) |expr| { + try renderExpression(allocator, stream, tree, indent, expr.*, Space.None); + + if (it.peek()) |next_expr| { + const comma = tree.nextToken(expr.*.lastToken()); + try renderToken(tree, stream, comma, indent, Space.Space); // , + } + } + + try renderToken(tree, stream, suffix_op.rtoken, indent, space); + return; + } + + const new_indent = indent + indent_delta; + try renderToken(tree, stream, lbrace, new_indent, Space.Newline); + try stream.writeByteNTimes(' ', new_indent); + var it = exprs.iterator(0); while (it.next()) |expr| { -- cgit v1.2.3 From 122a74724cb527ae6e1997c2c77118047fb50a2c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 May 2018 16:23:33 -0400 Subject: zig fmt: use simple newlines rather than empty comments to hint now the first row of an array literal is the hint to zig fmt for how long each row should be. See #1003 --- std/zig/parser_test.zig | 40 +++++++++++++--------- std/zig/render.zig | 91 +++++++++++++++++-------------------------------- 2 files changed, 55 insertions(+), 76 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index e83f99c972..550d8fa111 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -29,34 +29,36 @@ test "zig fmt: array literal with hint" { \\ 6, \\ 7 }; \\const a = []u8{ - \\ 1, 2, + \\ 1, + \\ 2, \\ 3, 4, // \\ 5, 6, // \\ 7, 8, // \\}; , \\const a = []u8{ - \\ 1, 2, // - \\ 3, 4, // - \\ 5, 6, // + \\ 1, 2, + \\ 3, 4, + \\ 5, 6, \\ 7, \\}; \\const a = []u8{ - \\ 1, 2, // - \\ 3, 4, // - \\ 5, 6, // - \\ 7, 8, // + \\ 1, 2, + \\ 3, 4, + \\ 5, 6, + \\ 7, 8, \\}; \\const a = []u8{ - \\ 1, 2, // - \\ 3, 4, // + \\ 1, 2, + \\ 3, 4, \\ 5, 6, // blah - \\ 7, 8, // + \\ 7, 8, \\}; \\const a = []u8{ - \\ 1, 2, // - \\ 3, 4, // - \\ 5, 6, // + \\ 1, 2, + \\ 3, // + \\ 4, + \\ 5, 6, \\ 7, \\}; \\const a = []u8{ @@ -231,12 +233,18 @@ test "zig fmt: add trailing comma to array literal" { \\ }; \\ return []u16{'m', 's', 'y', 's', \\ '-'}; + \\ return []u16{'m', 's', 'y', 's', '-'}; \\} , \\comptime { - \\ return []u16{ 'm', 's', 'y', 's', '-' // hi + \\ return []u16{ + \\ 'm', 's', 'y', 's', '-', // hi + \\ }; + \\ return []u16{ + \\ 'm', 's', 'y', 's', + \\ '-', \\ }; - \\ return []u16{ 'm', 's', 'y', 's', '-'}; + \\ return []u16{ 'm', 's', 'y', 's', '-' }; \\} \\ ); diff --git a/std/zig/render.zig b/std/zig/render.zig index 9805b7cf75..1528ae31aa 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -534,36 +534,42 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind // scan to find row size const maybe_row_size: ?usize = blk: { - var count: usize = 0; + var count: usize = 1; var it = exprs.iterator(0); - var prev_token = (??it.peek()).*.lastToken() + 1; - while (it.next()) |expr| { - const expr_last_token = expr.*.lastToken() + 1; - const next_token = tree.tokens.at(expr_last_token + 1); - const loc = tree.tokenLocationPtr(tree.tokens.at(prev_token).end, next_token); - if (loc.line != 0) break :blk null; - if (next_token.id == Token.Id.LineComment) { - const trimmed = mem.trimRight(u8, tree.tokenSlicePtr(next_token), " "); - if (trimmed.len == 2) { - break :blk count; - } else { - break :blk null; + while (true) { + const expr = (??it.next()).*; + if (it.peek()) |next_expr| { + const expr_last_token = expr.*.lastToken() + 1; + const loc = tree.tokenLocation(tree.tokens.at(expr_last_token).end, next_expr.*.firstToken()); + if (loc.line != 0) break :blk count; + count += 1; + } else { + const expr_last_token = expr.*.lastToken(); + const loc = tree.tokenLocation(tree.tokens.at(expr_last_token).end, suffix_op.rtoken); + if (loc.line == 0) { + // all on one line + const src_has_trailing_comma = trailblk: { + const maybe_comma = tree.prevToken(suffix_op.rtoken); + break :trailblk tree.tokens.at(maybe_comma).id == Token.Id.Comma; + }; + if (src_has_trailing_comma) { + break :blk 1; // force row size 1 + } else { + break :blk null; // no newlines + } } + break :blk count; } - prev_token = expr_last_token; - count += 1; } - break :blk null; }; - if (maybe_row_size) |row_size| { const new_indent = indent + indent_delta; try renderToken(tree, stream, lbrace, new_indent, Space.Newline); try stream.writeByteNTimes(' ', new_indent); var it = exprs.iterator(0); - var i: usize = 0; + var i: usize = 1; while (it.next()) |expr| { if (it.peek()) |next_expr| { try renderExpression(allocator, stream, tree, new_indent, expr.*, Space.None); @@ -571,23 +577,16 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const comma = tree.nextToken(expr.*.lastToken()); if (i != row_size) { - try renderToken(tree, stream, comma, new_indent, Space.IgnoreEmptyComment); // , + try renderToken(tree, stream, comma, new_indent, Space.Space); // , i += 1; continue; } - i = 0; - - try renderToken(tree, stream, comma, new_indent, Space.NoIndent); // , + i = 1; - const next_token = tree.tokens.at(comma + 1); - if (next_token.id != Token.Id.LineComment) { - try stream.print(" //\n"); - } + try renderToken(tree, stream, comma, new_indent, Space.Newline); // , try renderExtraNewline(tree, stream, next_expr.*); try stream.writeByteNTimes(' ', new_indent); - } else if (i == row_size) { - try renderTrailingCommaAndEmptyComment(allocator, stream, tree, new_indent, expr.*); // , // } else { try renderTrailingComma(allocator, stream, tree, new_indent, expr.*, Space.Newline); // , } @@ -595,50 +594,22 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try stream.writeByteNTimes(' ', indent); try renderToken(tree, stream, suffix_op.rtoken, indent, space); return; - } - - const src_has_trailing_comma = blk: { - const maybe_comma = tree.prevToken(suffix_op.rtoken); - break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma; - }; - if (!src_has_trailing_comma) { + } else { try renderToken(tree, stream, lbrace, indent, Space.Space); var it = exprs.iterator(0); while (it.next()) |expr| { - try renderExpression(allocator, stream, tree, indent, expr.*, Space.None); - if (it.peek()) |next_expr| { + try renderExpression(allocator, stream, tree, indent, expr.*, Space.None); const comma = tree.nextToken(expr.*.lastToken()); try renderToken(tree, stream, comma, indent, Space.Space); // , + } else { + try renderExpression(allocator, stream, tree, indent, expr.*, Space.Space); } } try renderToken(tree, stream, suffix_op.rtoken, indent, space); return; } - - const new_indent = indent + indent_delta; - try renderToken(tree, stream, lbrace, new_indent, Space.Newline); - try stream.writeByteNTimes(' ', new_indent); - - var it = exprs.iterator(0); - while (it.next()) |expr| { - - if (it.peek()) |next_expr| { - try renderExpression(allocator, stream, tree, new_indent, expr.*, Space.None); - - const comma = tree.nextToken(expr.*.lastToken()); - try renderToken(tree, stream, comma, new_indent, Space.Newline); // , - - try renderExtraNewline(tree, stream, next_expr.*); - try stream.writeByteNTimes(' ', new_indent); - } else { - try renderTrailingComma(allocator, stream, tree, new_indent, expr.*, Space.Newline); - } - } - - try stream.writeByteNTimes(' ', indent); - try renderToken(tree, stream, suffix_op.rtoken, indent, space); }, } }, -- cgit v1.2.3 From fd13a757855e185b4250c65fe89bbd72c9479a52 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 May 2018 16:59:42 -0400 Subject: zig fmt: allow same line struct literal with no trailing comma See #1003 --- std/zig/parser_test.zig | 15 +++++++++++++++ std/zig/render.zig | 31 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 550d8fa111..72818bc074 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,18 @@ +test "zig fmt: struct literal no trailing comma" { + try testTransform( + \\const a = foo{ .x = 1, .y = 2 }; + \\const a = foo{ .x = 1, + \\ .y = 2 }; + , + \\const a = foo{ .x = 1, .y = 2 }; + \\const a = foo{ + \\ .x = 1, + \\ .y = 2, + \\}; + \\ + ); +} + test "zig fmt: array literal with hint" { try testTransform( \\const a = []u8{ diff --git a/std/zig/render.zig b/std/zig/render.zig index 1528ae31aa..86e972c173 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -486,6 +486,37 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind return; } + const src_has_trailing_comma = blk: { + const maybe_comma = tree.prevToken(suffix_op.rtoken); + break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma; + }; + + const src_same_line = blk: { + const loc = tree.tokenLocation(tree.tokens.at(lbrace).end, suffix_op.rtoken); + break :blk loc.line == 0; + }; + + if (!src_has_trailing_comma and src_same_line) { + // render all on one line, no trailing comma + try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); + try renderToken(tree, stream, lbrace, indent, Space.Space); + + var it = field_inits.iterator(0); + while (it.next()) |field_init| { + if (it.peek() != null) { + try renderExpression(allocator, stream, tree, indent, field_init.*, Space.None); + + const comma = tree.nextToken(field_init.*.lastToken()); + try renderToken(tree, stream, comma, indent, Space.Space); + } else { + try renderExpression(allocator, stream, tree, indent, field_init.*, Space.Space); + } + } + + try renderToken(tree, stream, suffix_op.rtoken, indent, space); + return; + } + try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); try renderToken(tree, stream, lbrace, indent, Space.Newline); -- cgit v1.2.3 From 6c1fda3f99ebef60f4cb45b6bd66e76c85483c36 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 May 2018 17:09:55 -0400 Subject: zig fmt: fix switch body indent --- std/zig/parser_test.zig | 12 ++++++++++++ std/zig/render.zig | 6 +++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 72818bc074..dc522b3ef4 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,15 @@ +test "zig fmt: switch comment before prong" { + try testCanonical( + \\comptime { + \\ switch (a) { + \\ // hi + \\ 0 => {}, + \\ } + \\} + \\ + ); +} + test "zig fmt: struct literal no trailing comma" { try testTransform( \\const a = foo{ .x = 1, .y = 2 }; diff --git a/std/zig/render.zig b/std/zig/render.zig index 86e972c173..2f94d1cf8a 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -1050,11 +1050,11 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderExpression(allocator, stream, tree, indent, switch_node.expr, Space.None); - try renderToken(tree, stream, rparen, indent, Space.Space); // ) - try renderToken(tree, stream, lbrace, indent, Space.Newline); // { - const new_indent = indent + indent_delta; + try renderToken(tree, stream, rparen, indent, Space.Space); // ) + try renderToken(tree, stream, lbrace, new_indent, Space.Newline); // { + var it = switch_node.cases.iterator(0); while (it.next()) |node| { try stream.writeByteNTimes(' ', new_indent); -- cgit v1.2.3 From 530da363521ca7c3b22e6451874cef24d49683cc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 May 2018 17:43:17 -0400 Subject: zig fmt: fix enum decl with no trailing comma See #1003 --- std/zig/parser_test.zig | 12 +++++++++ std/zig/render.zig | 69 ++++++++++++++++++++++++------------------------- 2 files changed, 46 insertions(+), 35 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index dc522b3ef4..dc586e53b4 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,15 @@ +test "zig fmt: enum decl with no trailing comma" { + try testTransform( + \\const StrLitKind = enum {Normal, C}; + , + \\const StrLitKind = enum { + \\ Normal, + \\ C, + \\}; + \\ + ); +} + test "zig fmt: switch comment before prong" { try testCanonical( \\comptime { diff --git a/std/zig/render.zig b/std/zig/render.zig index 2f94d1cf8a..ed760a4b22 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -110,22 +110,29 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i try renderDocComments(tree, stream, tag, indent); - const name_space = if (tag.type_expr == null and tag.value_expr != null) Space.Space else Space.None; - try renderToken(tree, stream, tag.name_token, indent, name_space); // name + if (tag.type_expr == null and tag.value_expr == null) { + return renderTokenAndTrailingComma(tree, stream, tag.name_token, indent, Space.Newline); // name, + } + + if (tag.type_expr == null) { + try renderToken(tree, stream, tag.name_token, indent, Space.Space); // name + } else { + try renderToken(tree, stream, tag.name_token, indent, Space.None); // name + } if (tag.type_expr) |type_expr| { try renderToken(tree, stream, tree.nextToken(tag.name_token), indent, Space.Space); // : - const after_type_space = if (tag.value_expr == null) Space.None else Space.Space; - try renderExpression(allocator, stream, tree, indent, type_expr, after_type_space); - } - - if (tag.value_expr) |value_expr| { - try renderToken(tree, stream, tree.prevToken(value_expr.firstToken()), indent, Space.Space); // = - try renderExpression(allocator, stream, tree, indent, value_expr, Space.None); + if (tag.value_expr == null) { + return renderTrailingComma(allocator, stream, tree, indent, type_expr, Space.Newline); // type, + } else { + try renderExpression(allocator, stream, tree, indent, type_expr, Space.Space); // type + } } - try renderToken(tree, stream, tree.nextToken(decl.lastToken()), indent, Space.Newline); // , + const value_expr = ??tag.value_expr; + try renderToken(tree, stream, tree.prevToken(value_expr.firstToken()), indent, Space.Space); // = + try renderTrailingComma(allocator, stream, tree, indent, value_expr, Space.Newline); // value, }, ast.Node.Id.EnumTag => { @@ -133,15 +140,14 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i try renderDocComments(tree, stream, tag, indent); - const after_name_space = if (tag.value == null) Space.None else Space.Space; - try renderToken(tree, stream, tag.name_token, indent, after_name_space); // name - if (tag.value) |value| { + try renderToken(tree, stream, tag.name_token, indent, Space.Space); // name + try renderToken(tree, stream, tree.nextToken(tag.name_token), indent, Space.Space); // = - try renderExpression(allocator, stream, tree, indent, value, Space.None); + try renderTrailingComma(allocator, stream, tree, indent, value, Space.Newline); + } else { + try renderTokenAndTrailingComma(tree, stream, tag.name_token, indent, Space.Newline); // name } - - try renderToken(tree, stream, tree.nextToken(decl.lastToken()), indent, Space.Newline); // , }, ast.Node.Id.Comptime => { @@ -1584,7 +1590,6 @@ const Space = enum { NoNewline, NoIndent, NoComment, - IgnoreEmptyComment, }; fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, space: Space) (@typeOf(stream).Child.Error || Error)!void { @@ -1604,7 +1609,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent return stream.write("\n"); } }, - Space.Space, Space.IgnoreEmptyComment => return stream.writeByte(' '), + Space.Space => return stream.writeByte(' '), Space.NoComment => unreachable, } } @@ -1612,7 +1617,6 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent const comment_is_empty = mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ").len == 2; if (comment_is_empty) { switch (space) { - Space.IgnoreEmptyComment => return stream.writeByte(' '), Space.Newline => return stream.writeByte('\n'), else => {}, } @@ -1644,7 +1648,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent } }, Space.NoNewline => {}, - Space.NoComment, Space.IgnoreEmptyComment => unreachable, + Space.NoComment => unreachable, } return; } @@ -1681,7 +1685,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent try stream.writeByteNTimes(' ', next_line_indent); }, Space.NoNewline => {}, - Space.NoComment, Space.IgnoreEmptyComment => unreachable, + Space.NoComment => unreachable, } return; } @@ -1720,27 +1724,22 @@ fn renderTrailingComma(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, } } -fn renderTrailingCommaAndEmptyComment(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void -{ - const end_token = base.lastToken() + 1; +fn renderTokenAndTrailingComma(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, space: Space) (@typeOf(stream).Child.Error || Error)!void { + const end_token = token_index + 1; switch (tree.tokens.at(end_token).id) { Token.Id.Comma => { - try renderExpression(allocator, stream, tree, indent, base, Space.None); - try renderToken(tree, stream, end_token, indent, Space.Space); // , - - const next_token = tree.tokens.at(end_token + 1); - if (next_token.id != Token.Id.LineComment) { - try stream.print("//\n"); - } + try renderToken(tree, stream, token_index, indent, Space.None); + try renderToken(tree, stream, end_token, indent, space); // , }, Token.Id.LineComment => { - try renderExpression(allocator, stream, tree, indent, base, Space.NoComment); + try renderToken(tree, stream, token_index, indent, Space.NoComment); try stream.write(", "); - try renderToken(tree, stream, end_token, indent, Space.Newline); + try renderToken(tree, stream, end_token, indent, space); }, else => { - try renderExpression(allocator, stream, tree, indent, base, Space.None); - try stream.write(", //\n"); + try renderToken(tree, stream, token_index, indent, Space.None); + try stream.write(",\n"); + assert(space == Space.Newline); }, } } -- cgit v1.2.3 From 354ab1c5c8ec62c485fbc297817a0e70ab625a71 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 May 2018 21:18:41 -0400 Subject: zig fmt: render fn decl with trailing comma 1 line per param --- std/zig/parser_test.zig | 15 ++++- std/zig/render.zig | 158 +++++++++++++++++++++++++----------------------- 2 files changed, 95 insertions(+), 78 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index dc586e53b4..edf1b002b0 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,15 @@ +test "zig fmt: fn decl with trailing comma" { + try testTransform( + \\fn foo(a: i32, b: i32,) void {} + , + \\fn foo( + \\ a: i32, + \\ b: i32, + \\) void {} + \\ + ); +} + test "zig fmt: enum decl with no trailing comma" { try testTransform( \\const StrLitKind = enum {Normal, C}; @@ -247,7 +259,8 @@ test "zig fmt: switch cases trailing comma" { \\ switch (x) { \\ 1, 2, 3 => {}, \\ 4, - \\ 5, => {}, + \\ 5, + \\ => {}, \\ 6...8 => {}, \\ else => {}, \\ } diff --git a/std/zig/render.zig b/std/zig/render.zig index ed760a4b22..b8481b1348 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -102,7 +102,7 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i } try renderToken(tree, stream, field.name_token, indent, Space.None); // name try renderToken(tree, stream, tree.nextToken(field.name_token), indent, Space.Space); // : - try renderTrailingComma(allocator, stream, tree, indent, field.type_expr, Space.Newline); // type, + try renderExpression(allocator, stream, tree, indent, field.type_expr, Space.Comma); // type, }, ast.Node.Id.UnionTag => { @@ -111,7 +111,7 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i try renderDocComments(tree, stream, tag, indent); if (tag.type_expr == null and tag.value_expr == null) { - return renderTokenAndTrailingComma(tree, stream, tag.name_token, indent, Space.Newline); // name, + return renderToken(tree, stream, tag.name_token, indent, Space.Comma); // name, } if (tag.type_expr == null) { @@ -124,7 +124,7 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i try renderToken(tree, stream, tree.nextToken(tag.name_token), indent, Space.Space); // : if (tag.value_expr == null) { - return renderTrailingComma(allocator, stream, tree, indent, type_expr, Space.Newline); // type, + return renderExpression(allocator, stream, tree, indent, type_expr, Space.Comma); // type, } else { try renderExpression(allocator, stream, tree, indent, type_expr, Space.Space); // type } @@ -132,7 +132,7 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i const value_expr = ??tag.value_expr; try renderToken(tree, stream, tree.prevToken(value_expr.firstToken()), indent, Space.Space); // = - try renderTrailingComma(allocator, stream, tree, indent, value_expr, Space.Newline); // value, + try renderExpression(allocator, stream, tree, indent, value_expr, Space.Comma); // value, }, ast.Node.Id.EnumTag => { @@ -144,9 +144,9 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i try renderToken(tree, stream, tag.name_token, indent, Space.Space); // name try renderToken(tree, stream, tree.nextToken(tag.name_token), indent, Space.Space); // = - try renderTrailingComma(allocator, stream, tree, indent, value, Space.Newline); + try renderExpression(allocator, stream, tree, indent, value, Space.Comma); } else { - try renderTokenAndTrailingComma(tree, stream, tag.name_token, indent, Space.Newline); // name + try renderToken(tree, stream, tag.name_token, indent, Space.Comma); // name } }, @@ -413,7 +413,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderToken(tree, stream, comma, new_indent, Space.Newline); // , try renderExtraNewline(tree, stream, next_node.*); } else { - try renderTrailingComma(allocator, stream, tree, param_node_new_indent, param_node.*, Space.Newline); + try renderExpression(allocator, stream, tree, param_node_new_indent, param_node.*, Space.Comma); try stream.writeByteNTimes(' ', indent); try renderToken(tree, stream, suffix_op.rtoken, indent, space); return; @@ -540,7 +540,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderExtraNewline(tree, stream, next_field_init.*); } else { - try renderTrailingComma(allocator, stream, tree, new_indent, field_init.*, Space.Newline); + try renderExpression(allocator, stream, tree, new_indent, field_init.*, Space.Comma); } } @@ -625,7 +625,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderExtraNewline(tree, stream, next_expr.*); try stream.writeByteNTimes(' ', new_indent); } else { - try renderTrailingComma(allocator, stream, tree, new_indent, expr.*, Space.Newline); // , + try renderExpression(allocator, stream, tree, new_indent, expr.*, Space.Comma); // , } } try stream.writeByteNTimes(' ', indent); @@ -892,7 +892,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderExtraNewline(tree, stream, next_node.*); } else { - try renderTrailingComma(allocator, stream, tree, new_indent, node.*, Space.Newline); + try renderExpression(allocator, stream, tree, new_indent, node.*, Space.Comma); } } @@ -976,29 +976,55 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderExpression(allocator, stream, tree, indent, &async_attr.base, Space.Space); } - if (fn_proto.name_token) |name_token| blk: { + const lparen = if (fn_proto.name_token) |name_token| blk: { try renderToken(tree, stream, fn_proto.fn_token, indent, Space.Space); // fn try renderToken(tree, stream, name_token, indent, Space.None); // name - try renderToken(tree, stream, tree.nextToken(name_token), indent, Space.None); // ( + break :blk tree.nextToken(name_token); } else blk: { try renderToken(tree, stream, fn_proto.fn_token, indent, Space.None); // fn - try renderToken(tree, stream, tree.nextToken(fn_proto.fn_token), indent, Space.None); // ( - } - - var it = fn_proto.params.iterator(0); - while (it.next()) |param_decl_node| { - try renderParamDecl(allocator, stream, tree, indent, param_decl_node.*); - - if (it.peek() != null) { - const comma = tree.nextToken(param_decl_node.*.lastToken()); - try renderToken(tree, stream, comma, indent, Space.Space); // , - } - } + break :blk tree.nextToken(fn_proto.fn_token); + }; const rparen = tree.prevToken(switch (fn_proto.return_type) { ast.Node.FnProto.ReturnType.Explicit => |node| node.firstToken(), ast.Node.FnProto.ReturnType.InferErrorSet => |node| tree.prevToken(node.firstToken()), }); + + const src_params_trailing_comma = blk: { + const maybe_comma = tree.prevToken(rparen); + break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma; + }; + const src_params_same_line = blk: { + const loc = tree.tokenLocation(tree.tokens.at(lparen).end, rparen); + break :blk loc.line == 0; + }; + + if (!src_params_trailing_comma and src_params_same_line) { + try renderToken(tree, stream, lparen, indent, Space.None); // ( + + // render all on one line, no trailing comma + var it = fn_proto.params.iterator(0); + while (it.next()) |param_decl_node| { + try renderParamDecl(allocator, stream, tree, indent, param_decl_node.*, Space.None); + + if (it.peek() != null) { + const comma = tree.nextToken(param_decl_node.*.lastToken()); + try renderToken(tree, stream, comma, indent, Space.Space); // , + } + } + } else { + // one param per line + const new_indent = indent + indent_delta; + try renderToken(tree, stream, lparen, new_indent, Space.Newline); // ( + + var it = fn_proto.params.iterator(0); + while (it.next()) |param_decl_node| { + try stream.writeByteNTimes(' ', new_indent); + try renderParamDecl(allocator, stream, tree, indent, param_decl_node.*, Space.Comma); + } + try stream.writeByteNTimes(' ', indent); + } + try renderToken(tree, stream, rparen, indent, Space.Space); // ) if (fn_proto.align_expr) |align_expr| { @@ -1064,7 +1090,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind var it = switch_node.cases.iterator(0); while (it.next()) |node| { try stream.writeByteNTimes(' ', new_indent); - try renderExpression(allocator, stream, tree, new_indent, node.*, Space.Newline); + try renderExpression(allocator, stream, tree, new_indent, node.*, Space.Comma); if (it.peek()) |next_node| { try renderExtraNewline(tree, stream, next_node.*); @@ -1110,7 +1136,8 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderExtraNewline(tree, stream, next_node.*); try stream.writeByteNTimes(' ', indent); } else { - try renderTrailingComma(allocator, stream, tree, indent, node.*, Space.Space); + try renderExpression(allocator, stream, tree, indent, node.*, Space.Comma); + try stream.writeByteNTimes(' ', indent); break; } } @@ -1122,7 +1149,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderExpression(allocator, stream, tree, indent, payload, Space.Space); } - try renderTrailingComma(allocator, stream, tree, indent, switch_case.expr, space); + try renderExpression(allocator, stream, tree, indent, switch_case.expr, space); }, ast.Node.Id.SwitchElse => { const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); @@ -1543,7 +1570,7 @@ fn renderVarDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent try renderToken(tree, stream, var_decl.semicolon_token, indent, Space.Newline); } -fn renderParamDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { +fn renderParamDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node, space: Space) (@typeOf(stream).Child.Error || Error)!void { const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); if (param_decl.comptime_token) |comptime_token| { @@ -1557,9 +1584,9 @@ fn renderParamDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, inde try renderToken(tree, stream, tree.nextToken(name_token), indent, Space.Space); // : } if (param_decl.var_args_token) |var_args_token| { - try renderToken(tree, stream, var_args_token, indent, Space.None); + try renderToken(tree, stream, var_args_token, indent, space); } else { - try renderExpression(allocator, stream, tree, indent, param_decl.type_node, Space.None); + try renderExpression(allocator, stream, tree, indent, param_decl.type_node, space); } } @@ -1586,6 +1613,7 @@ fn renderStatement(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, inde const Space = enum { None, Newline, + Comma, Space, NoNewline, NoIndent, @@ -1596,9 +1624,27 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent var token = tree.tokens.at(token_index); try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(token), " ")); - if (space == Space.NoComment) return; - var next_token = tree.tokens.at(token_index + 1); + + switch (space) { + Space.NoComment => return, + Space.Comma => switch (next_token.id) { + Token.Id.Comma => return renderToken(tree, stream, token_index + 1, indent, Space.Newline), + Token.Id.LineComment => { + try stream.write(", "); + return renderToken(tree, stream, token_index + 1, indent, Space.Newline); + }, + else => { + if (tree.tokens.at(token_index + 2).id == Token.Id.MultilineStringLiteralLine) { + return stream.write(","); + } else { + return stream.write(",\n"); + } + }, + }, + else => {}, + } + if (next_token.id != Token.Id.LineComment) { switch (space) { Space.None, Space.NoNewline, Space.NoIndent => return, @@ -1610,7 +1656,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent } }, Space.Space => return stream.writeByte(' '), - Space.NoComment => unreachable, + Space.NoComment, Space.Comma => unreachable, } } @@ -1648,7 +1694,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent } }, Space.NoNewline => {}, - Space.NoComment => unreachable, + Space.NoComment, Space.Comma => unreachable, } return; } @@ -1685,7 +1731,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent try stream.writeByteNTimes(' ', next_line_indent); }, Space.NoNewline => {}, - Space.NoComment => unreachable, + Space.NoComment, Space.Comma => unreachable, } return; } @@ -1701,45 +1747,3 @@ fn renderDocComments(tree: &ast.Tree, stream: var, node: var, indent: usize) (@t try stream.writeByteNTimes(' ', indent); } } - -fn renderTrailingComma(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node, - space: Space) (@typeOf(stream).Child.Error || Error)!void -{ - const end_token = base.lastToken() + 1; - switch (tree.tokens.at(end_token).id) { - Token.Id.Comma => { - try renderExpression(allocator, stream, tree, indent, base, Space.None); - try renderToken(tree, stream, end_token, indent, space); // , - }, - Token.Id.LineComment => { - try renderExpression(allocator, stream, tree, indent, base, Space.NoComment); - try stream.write(", "); - try renderToken(tree, stream, end_token, indent, space); - }, - else => { - try renderExpression(allocator, stream, tree, indent, base, Space.None); - try stream.write(",\n"); - assert(space == Space.Newline); - }, - } -} - -fn renderTokenAndTrailingComma(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, space: Space) (@typeOf(stream).Child.Error || Error)!void { - const end_token = token_index + 1; - switch (tree.tokens.at(end_token).id) { - Token.Id.Comma => { - try renderToken(tree, stream, token_index, indent, Space.None); - try renderToken(tree, stream, end_token, indent, space); // , - }, - Token.Id.LineComment => { - try renderToken(tree, stream, token_index, indent, Space.NoComment); - try stream.write(", "); - try renderToken(tree, stream, end_token, indent, space); - }, - else => { - try renderToken(tree, stream, token_index, indent, Space.None); - try stream.write(",\n"); - assert(space == Space.Newline); - }, - } -} -- cgit v1.2.3 From 71badebd08a1e5da9326fc7126c4de0fba4ae3d0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 May 2018 21:28:32 -0400 Subject: zig fmt: respect line breaks after infix operators --- std/zig/parser_test.zig | 17 +++++++++++++++++ std/zig/render.zig | 12 +++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index edf1b002b0..f722c7f08f 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,20 @@ +test "zig fmt: respect line breaks after infix operators" { + try testCanonical( + \\comptime { + \\ self.crc = + \\ lookup_tables[0][p[7]] ^ + \\ lookup_tables[1][p[6]] ^ + \\ lookup_tables[2][p[5]] ^ + \\ lookup_tables[3][p[4]] ^ + \\ lookup_tables[4][@truncate(u8, self.crc >> 24)] ^ + \\ lookup_tables[5][@truncate(u8, self.crc >> 16)] ^ + \\ lookup_tables[6][@truncate(u8, self.crc >> 8)] ^ + \\ lookup_tables[7][@truncate(u8, self.crc >> 0)]; + \\} + \\ + ); +} + test "zig fmt: fn decl with trailing comma" { try testTransform( \\fn foo(a: i32, b: i32,) void {} diff --git a/std/zig/render.zig b/std/zig/render.zig index b8481b1348..56f3d24b8e 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -254,7 +254,17 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind else => Space.Space, }; try renderExpression(allocator, stream, tree, indent, infix_op_node.lhs, op_space); - try renderToken(tree, stream, infix_op_node.op_token, indent, op_space); + + const after_op_space = blk: { + const loc = tree.tokenLocation(tree.tokens.at(infix_op_node.op_token).end, + tree.nextToken(infix_op_node.op_token)); + break :blk if (loc.line == 0) op_space else Space.Newline; + }; + + try renderToken(tree, stream, infix_op_node.op_token, indent, after_op_space); + if (after_op_space == Space.Newline) { + try stream.writeByteNTimes(' ', indent + indent_delta); + } switch (infix_op_node.op) { ast.Node.InfixOp.Op.Catch => |maybe_payload| if (maybe_payload) |payload| { -- cgit v1.2.3 From 77ec81b0353eac45a66b4ed79821c8c954032345 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 May 2018 22:22:01 -0400 Subject: zig fmt: respect line breaks in if-else --- std/zig/parser_test.zig | 19 ++++++++ std/zig/render.zig | 116 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 107 insertions(+), 28 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index f722c7f08f..30e7eb3e7c 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,22 @@ +test "zig fmt: respect line breaks in if-else" { + try testCanonical( + \\comptime { + \\ return if (cond) a else b; + \\ return if (cond) + \\ a + \\ else + \\ b; + \\ return if (cond) + \\ a + \\ else if (cond) + \\ b + \\ else + \\ c; + \\} + \\ + ); +} + test "zig fmt: respect line breaks after infix operators" { try testCanonical( \\comptime { diff --git a/std/zig/render.zig b/std/zig/render.zig index 56f3d24b8e..90bc74fd63 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -1315,49 +1315,109 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind ast.Node.Id.If => { const if_node = @fieldParentPtr(ast.Node.If, "base", base); - try renderToken(tree, stream, if_node.if_token, indent, Space.Space); - try renderToken(tree, stream, tree.prevToken(if_node.condition.firstToken()), indent, Space.None); + const lparen = tree.prevToken(if_node.condition.firstToken()); + const rparen = tree.nextToken(if_node.condition.lastToken()); - try renderExpression(allocator, stream, tree, indent, if_node.condition, Space.None); - try renderToken(tree, stream, tree.nextToken(if_node.condition.lastToken()), indent, Space.Space); + try renderToken(tree, stream, if_node.if_token, indent, Space.Space); // if + try renderToken(tree, stream, lparen, indent, Space.None); // ( - if (if_node.payload) |payload| { - try renderExpression(allocator, stream, tree, indent, payload, Space.Space); - } + try renderExpression(allocator, stream, tree, indent, if_node.condition, Space.None); // condition - switch (if_node.body.id) { + const body_is_block = switch (if_node.body.id) { ast.Node.Id.Block, ast.Node.Id.If, ast.Node.Id.For, ast.Node.Id.While, - ast.Node.Id.Switch => { - if (if_node.@"else") |@"else"| { - if (if_node.body.id == ast.Node.Id.Block) { - try renderExpression(allocator, stream, tree, indent, if_node.body, Space.Space); - } else { - try renderExpression(allocator, stream, tree, indent, if_node.body, Space.Newline); - try stream.writeByteNTimes(' ', indent); - } + ast.Node.Id.Switch, + => true, + else => false, + }; - try renderExpression(allocator, stream, tree, indent, &@"else".base, space); - } else { - try renderExpression(allocator, stream, tree, indent, if_node.body, space); - } - }, - else => { - if (if_node.@"else") |@"else"| { - try renderExpression(allocator, stream, tree, indent, if_node.body, Space.Space); - try renderToken(tree, stream, @"else".else_token, indent, Space.Space); + if (body_is_block) { + try renderToken(tree, stream, rparen, indent, Space.Space); // ) + + if (if_node.payload) |payload| { + try renderExpression(allocator, stream, tree, indent, payload, Space.Space); // |x| + } + + if (if_node.@"else") |@"else"| { + try renderExpression(allocator, stream, tree, indent, if_node.body, Space.Space); + return renderExpression(allocator, stream, tree, indent, &@"else".base, space); + } else { + return renderExpression(allocator, stream, tree, indent, if_node.body, space); + } + } + + const src_has_newline = blk: { + const loc = tree.tokenLocation(tree.tokens.at(rparen).end, if_node.body.lastToken()); + break :blk loc.line != 0; + }; + + if (src_has_newline) { + const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space; + try renderToken(tree, stream, rparen, indent, after_rparen_space); // ) + + if (if_node.payload) |payload| { + try renderExpression(allocator, stream, tree, indent, payload, Space.Newline); + } + + const new_indent = indent + indent_delta; + try stream.writeByteNTimes(' ', new_indent); + + if (if_node.@"else") |@"else"| { + const else_is_block = switch (@"else".body.id) { + ast.Node.Id.Block, + ast.Node.Id.If, + ast.Node.Id.For, + ast.Node.Id.While, + ast.Node.Id.Switch, + => true, + else => false, + }; + try renderExpression(allocator, stream, tree, new_indent, if_node.body, Space.Newline); + try stream.writeByteNTimes(' ', indent); + + if (else_is_block) { + try renderToken(tree, stream, @"else".else_token, indent, Space.Space); // else if (@"else".payload) |payload| { try renderExpression(allocator, stream, tree, indent, payload, Space.Space); } - try renderExpression(allocator, stream, tree, indent, @"else".body, space); + return renderExpression(allocator, stream, tree, indent, @"else".body, space); } else { - try renderExpression(allocator, stream, tree, indent, if_node.body, space); + const after_else_space = if (@"else".payload == null) Space.Newline else Space.Space; + try renderToken(tree, stream, @"else".else_token, indent, after_else_space); // else + + if (@"else".payload) |payload| { + try renderExpression(allocator, stream, tree, indent, payload, Space.Newline); + } + try stream.writeByteNTimes(' ', new_indent); + + return renderExpression(allocator, stream, tree, new_indent, @"else".body, space); } - }, + } else { + return renderExpression(allocator, stream, tree, new_indent, if_node.body, space); + } + } + + try renderToken(tree, stream, rparen, indent, Space.Space); // ) + + if (if_node.payload) |payload| { + try renderExpression(allocator, stream, tree, indent, payload, Space.Space); + } + + if (if_node.@"else") |@"else"| { + try renderExpression(allocator, stream, tree, indent, if_node.body, Space.Space); + try renderToken(tree, stream, @"else".else_token, indent, Space.Space); + + if (@"else".payload) |payload| { + try renderExpression(allocator, stream, tree, indent, payload, Space.Space); + } + + return renderExpression(allocator, stream, tree, indent, @"else".body, space); + } else { + return renderExpression(allocator, stream, tree, indent, if_node.body, space); } }, -- cgit v1.2.3 From 0d1b47362c7623ba923a6831dab0989133caf9ab Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 May 2018 22:41:05 -0400 Subject: zig fmt: if-else with comment before else --- std/zig/parser_test.zig | 18 ++++++++++++++++++ std/zig/render.zig | 20 ++++++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 30e7eb3e7c..0f8b2d798a 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,21 @@ +test "zig fmt: if-else with comment before else" { + try testCanonical( + \\comptime { + \\ // cexp(finite|nan +- i inf|nan) = nan + i nan + \\ if ((hx & 0x7fffffff) != 0x7f800000) { + \\ return Complex(f32).new(y - y, y - y); + \\ } // cexp(-inf +- i inf|nan) = 0 + i0 + \\ else if (hx & 0x80000000 != 0) { + \\ return Complex(f32).new(0, 0); + \\ } // cexp(+inf +- i inf|nan) = inf + i nan + \\ else { + \\ return Complex(f32).new(x, y - y); + \\ } + \\} + \\ + ); +} + test "zig fmt: respect line breaks in if-else" { try testCanonical( \\comptime { diff --git a/std/zig/render.zig b/std/zig/render.zig index 90bc74fd63..af8f3f385c 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -1341,7 +1341,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind } if (if_node.@"else") |@"else"| { - try renderExpression(allocator, stream, tree, indent, if_node.body, Space.Space); + try renderExpression(allocator, stream, tree, indent, if_node.body, Space.SpaceOrOutdent); return renderExpression(allocator, stream, tree, indent, &@"else".base, space); } else { return renderExpression(allocator, stream, tree, indent, if_node.body, space); @@ -1685,8 +1685,8 @@ const Space = enum { Newline, Comma, Space, + SpaceOrOutdent, NoNewline, - NoIndent, NoComment, }; @@ -1717,7 +1717,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent if (next_token.id != Token.Id.LineComment) { switch (space) { - Space.None, Space.NoNewline, Space.NoIndent => return, + Space.None, Space.NoNewline => return, Space.Newline => { if (next_token.id == Token.Id.MultilineStringLiteralLine) { return; @@ -1725,7 +1725,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent return stream.write("\n"); } }, - Space.Space => return stream.writeByte(' '), + Space.Space, Space.SpaceOrOutdent => return stream.writeByte(' '), Space.NoComment, Space.Comma => unreachable, } } @@ -1756,7 +1756,11 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent }; try stream.writeByteNTimes(' ', next_line_indent); }, - Space.Newline, Space.NoIndent => { + Space.SpaceOrOutdent => { + try stream.writeByte('\n'); + try stream.writeByteNTimes(' ', indent); + }, + Space.Newline => { if (next_token.id == Token.Id.MultilineStringLiteralLine) { return; } else { @@ -1783,7 +1787,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent next_token = tree.tokens.at(token_index + offset); if (next_token.id != Token.Id.LineComment) { switch (space) { - Space.Newline, Space.NoIndent => { + Space.Newline => { if (next_token.id == Token.Id.MultilineStringLiteralLine) { return; } else { @@ -1800,6 +1804,10 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent }; try stream.writeByteNTimes(' ', next_line_indent); }, + Space.SpaceOrOutdent => { + try stream.writeByte('\n'); + try stream.writeByteNTimes(' ', indent); + }, Space.NoNewline => {}, Space.NoComment, Space.Comma => unreachable, } -- cgit v1.2.3 From 530d17542207151f768a14a0cc8311181599b9b9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 May 2018 23:41:09 -0400 Subject: zig fmt: fix spacing when moving doc comment on var decls --- std/zig/parser_test.zig | 39 +++++++++++++++++++++++++-------------- std/zig/render.zig | 33 +++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 0f8b2d798a..0106b990bc 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,28 @@ +test "zig fmt: same-line doc comment on variable declaration" { + try testTransform( + \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space + \\pub const MAP_FILE = 0x0000; /// map from file (default) + \\ + \\pub const EMEDIUMTYPE = 124; /// Wrong medium type + \\ + \\// nameserver query return codes + \\pub const ENSROK = 0; /// DNS server returned answer with no data + , + \\/// allocated from memory, swap space + \\pub const MAP_ANONYMOUS = 0x1000; + \\/// map from file (default) + \\pub const MAP_FILE = 0x0000; + \\ + \\/// Wrong medium type + \\pub const EMEDIUMTYPE = 124; + \\ + \\// nameserver query return codes + \\/// DNS server returned answer with no data + \\pub const ENSROK = 0; + \\ + ); +} + test "zig fmt: if-else with comment before else" { try testCanonical( \\comptime { @@ -557,20 +582,6 @@ test "zig fmt: add comma on last switch prong" { ); } -test "zig fmt: same-line doc comment on variable declaration" { - try testTransform( - \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space - \\pub const MAP_FILE = 0x0000; /// map from file (default) - \\ - , - \\/// allocated from memory, swap space - \\pub const MAP_ANONYMOUS = 0x1000; - \\/// map from file (default) - \\pub const MAP_FILE = 0x0000; - \\ - ); -} - test "zig fmt: same-line comment after a statement" { try testCanonical( \\test "" { diff --git a/std/zig/render.zig b/std/zig/render.zig index af8f3f385c..3248b33aa7 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -39,11 +39,12 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf( } fn renderExtraNewline(tree: &ast.Tree, stream: var, node: &ast.Node) !void { - var first_token = node.firstToken(); - while (tree.tokens.at(first_token - 1).id == Token.Id.DocComment) { - first_token -= 1; + const first_token = node.firstToken(); + var prev_token = first_token; + while (tree.tokens.at(prev_token - 1).id == Token.Id.DocComment) { + prev_token -= 1; } - const prev_token_end = tree.tokens.at(first_token - 1).end; + const prev_token_end = tree.tokens.at(prev_token - 1).end; const loc = tree.tokenLocation(prev_token_end, first_token); if (loc.line >= 2) { try stream.writeByte('\n'); @@ -1715,7 +1716,17 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent else => {}, } - if (next_token.id != Token.Id.LineComment) { + // Skip over same line doc comments + var offset: usize = 1; + if (next_token.id == Token.Id.DocComment) { + const loc = tree.tokenLocationPtr(token.end, next_token); + if (loc.line == 0) { + offset += 1; + next_token = tree.tokens.at(token_index + offset); + } + } + + if (next_token.id != Token.Id.LineComment) blk: { switch (space) { Space.None, Space.NoNewline => return, Space.Newline => { @@ -1739,7 +1750,6 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent } var loc = tree.tokenLocationPtr(token.end, next_token); - var offset: usize = 1; if (loc.line == 0) { try stream.print(" {}", mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ")); offset = 2; @@ -1820,8 +1830,15 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent fn renderDocComments(tree: &ast.Tree, stream: var, node: var, indent: usize) (@typeOf(stream).Child.Error || Error)!void { const comment = node.doc_comments ?? return; var it = comment.lines.iterator(0); + const first_token = node.firstToken(); while (it.next()) |line_token_index| { - try renderToken(tree, stream, line_token_index.*, indent, Space.Newline); - try stream.writeByteNTimes(' ', indent); + if (line_token_index.* < first_token) { + try renderToken(tree, stream, line_token_index.*, indent, Space.Newline); + try stream.writeByteNTimes(' ', indent); + } else { + try renderToken(tree, stream, line_token_index.*, indent, Space.NoComment); + try stream.write("\n"); + try stream.writeByteNTimes(' ', indent); + } } } -- cgit v1.2.3 From eda6898c5b253367174172db909ee23013f32733 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 May 2018 03:15:12 -0400 Subject: zig fmt: handle if and while indentation better --- std/zig/ast.zig | 24 +- std/zig/parser_test.zig | 92 ++++ std/zig/render.zig | 1077 ++++++++++++++++++++++++----------------------- 3 files changed, 658 insertions(+), 535 deletions(-) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index addd3a37e8..3b705e2ee6 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -68,6 +68,14 @@ pub const Tree = struct { return self.tokenLocationPtr(start_index, self.tokens.at(token_index)); } + pub fn tokensOnSameLine(self: &Tree, token1_index: TokenIndex, token2_index: TokenIndex) bool { + return self.tokensOnSameLinePtr(self.tokens.at(token1_index), self.tokens.at(token2_index)); + } + + pub fn tokensOnSameLinePtr(self: &Tree, token1: &const Token, token2: &const Token) bool { + return mem.indexOfScalar(u8, self.source[token1.end..token2.start], '\n') == null; + } + pub fn dump(self: &Tree) void { self.root_node.base.dump(0); } @@ -1529,14 +1537,14 @@ pub const Node = struct { switch (self.op) { Op.SliceType => |addr_of_info| { - if (addr_of_info.align_expr) |align_expr| { - if (i < 1) return align_expr; + if (addr_of_info.align_info) |align_info| { + if (i < 1) return align_info.node; i -= 1; } }, Op.AddrOf => |addr_of_info| { - if (addr_of_info.align_expr) |align_expr| { - if (i < 1) return align_expr; + if (addr_of_info.align_info) |align_info| { + if (i < 1) return align_info.node; i -= 1; } }, @@ -1553,7 +1561,9 @@ pub const Node = struct { Op.NegationWrap, Op.Try, Op.Resume, - Op.UnwrapMaybe => {}, + Op.UnwrapMaybe, + Op.PointerType, + => {}, } if (i < 1) return self.rhs; @@ -1656,6 +1666,7 @@ pub const Node = struct { if (i < fields.len) return fields.at(i).*; i -= fields.len; }, + Op.Deref => {}, } return null; @@ -2082,9 +2093,6 @@ pub const Node = struct { if (i < self.inputs.len) return &(self.inputs.at(index).*).base; i -= self.inputs.len; - if (i < self.clobbers.len) return self.clobbers.at(index).*; - i -= self.clobbers.len; - return null; } diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 0106b990bc..8ed748ed30 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,95 @@ +test "zig fmt: if condition wraps" { + try testTransform( + \\comptime { + \\ if (cond and + \\ cond) { + \\ return x; + \\ } + \\ while (cond and + \\ cond) { + \\ return x; + \\ } + \\ if (a == b and + \\ c) { + \\ a = b; + \\ } + \\ while (a == b and + \\ c) { + \\ a = b; + \\ } + \\ if ((cond and + \\ cond)) { + \\ return x; + \\ } + \\ while ((cond and + \\ cond)) { + \\ return x; + \\ } + \\ var a = if (a) |*f| x: { + \\ break :x &a.b; + \\ } else |err| err; + \\} + , + \\comptime { + \\ if (cond and + \\ cond) + \\ { + \\ return x; + \\ } + \\ while (cond and + \\ cond) + \\ { + \\ return x; + \\ } + \\ if (a == b and + \\ c) + \\ { + \\ a = b; + \\ } + \\ while (a == b and + \\ c) + \\ { + \\ a = b; + \\ } + \\ if ((cond and + \\ cond)) + \\ { + \\ return x; + \\ } + \\ while ((cond and + \\ cond)) + \\ { + \\ return x; + \\ } + \\ var a = if (a) |*f| x: { + \\ break :x &a.b; + \\ } else |err| err; + \\} + \\ + ); +} + +test "zig fmt: if condition has line break but must not wrap" { + try testCanonical( + \\comptime { + \\ if (self.user_input_options.put(name, UserInputOption{ + \\ .name = name, + \\ .used = false, + \\ }) catch unreachable) |*prev_value| { + \\ foo(); + \\ bar(); + \\ } + \\ if (put( + \\ a, + \\ b, + \\ )) { + \\ foo(); + \\ } + \\} + \\ + ); +} + test "zig fmt: same-line doc comment on variable declaration" { try testTransform( \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space diff --git a/std/zig/render.zig b/std/zig/render.zig index 3248b33aa7..16aed808fc 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -28,17 +28,17 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf( } } - + var start_col: usize = 0; var it = tree.root_node.decls.iterator(0); while (it.next()) |decl| { - try renderTopLevelDecl(allocator, stream, tree, 0, decl.*); + try renderTopLevelDecl(allocator, stream, tree, 0, &start_col, decl.*); if (it.peek()) |next_decl| { - try renderExtraNewline(tree, stream, next_decl.*); + try renderExtraNewline(tree, stream, &start_col, next_decl.*); } } } -fn renderExtraNewline(tree: &ast.Tree, stream: var, node: &ast.Node) !void { +fn renderExtraNewline(tree: &ast.Tree, stream: var, start_col: &usize, node: &ast.Node) !void { const first_token = node.firstToken(); var prev_token = first_token; while (tree.tokens.at(prev_token - 1).id == Token.Id.DocComment) { @@ -48,22 +48,23 @@ fn renderExtraNewline(tree: &ast.Tree, stream: var, node: &ast.Node) !void { const loc = tree.tokenLocation(prev_token_end, first_token); if (loc.line >= 2) { try stream.writeByte('\n'); + start_col.* = 0; } } -fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, decl: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { +fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, start_col: &usize, decl: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { switch (decl.id) { ast.Node.Id.FnProto => { const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); - try renderDocComments(tree, stream, fn_proto, indent); + try renderDocComments(tree, stream, fn_proto, indent, start_col); if (fn_proto.body_node) |body_node| { - try renderExpression(allocator, stream, tree, indent, decl, Space.Space); - try renderExpression(allocator, stream, tree, indent, body_node, Space.Newline); + try renderExpression(allocator, stream, tree, indent, start_col, decl, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, body_node, Space.Newline); } else { - try renderExpression(allocator, stream, tree, indent, decl, Space.None); - try renderToken(tree, stream, tree.nextToken(decl.lastToken()), indent, Space.Newline); + try renderExpression(allocator, stream, tree, indent, start_col, decl, Space.None); + try renderToken(tree, stream, tree.nextToken(decl.lastToken()), indent, start_col, Space.Newline); } }, @@ -71,153 +72,154 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); if (use_decl.visib_token) |visib_token| { - try renderToken(tree, stream, visib_token, indent, Space.Space); // pub + try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub } - try renderToken(tree, stream, use_decl.use_token, indent, Space.Space); // use - try renderExpression(allocator, stream, tree, indent, use_decl.expr, Space.None); - try renderToken(tree, stream, use_decl.semicolon_token, indent, Space.Newline); // ; + try renderToken(tree, stream, use_decl.use_token, indent, start_col, Space.Space); // use + try renderExpression(allocator, stream, tree, indent, start_col, use_decl.expr, Space.None); + try renderToken(tree, stream, use_decl.semicolon_token, indent, start_col, Space.Newline); // ; }, ast.Node.Id.VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); - try renderDocComments(tree, stream, var_decl, indent); - try renderVarDecl(allocator, stream, tree, indent, var_decl); + try renderDocComments(tree, stream, var_decl, indent, start_col); + try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl); }, ast.Node.Id.TestDecl => { const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); - try renderDocComments(tree, stream, test_decl, indent); - try renderToken(tree, stream, test_decl.test_token, indent, Space.Space); - try renderExpression(allocator, stream, tree, indent, test_decl.name, Space.Space); - try renderExpression(allocator, stream, tree, indent, test_decl.body_node, Space.Newline); + try renderDocComments(tree, stream, test_decl, indent, start_col); + try renderToken(tree, stream, test_decl.test_token, indent, start_col, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, test_decl.name, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, test_decl.body_node, Space.Newline); }, ast.Node.Id.StructField => { const field = @fieldParentPtr(ast.Node.StructField, "base", decl); - try renderDocComments(tree, stream, field, indent); + try renderDocComments(tree, stream, field, indent, start_col); if (field.visib_token) |visib_token| { - try renderToken(tree, stream, visib_token, indent, Space.Space); // pub + try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub } - try renderToken(tree, stream, field.name_token, indent, Space.None); // name - try renderToken(tree, stream, tree.nextToken(field.name_token), indent, Space.Space); // : - try renderExpression(allocator, stream, tree, indent, field.type_expr, Space.Comma); // type, + try renderToken(tree, stream, field.name_token, indent, start_col, Space.None); // name + try renderToken(tree, stream, tree.nextToken(field.name_token), indent, start_col, Space.Space); // : + try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr, Space.Comma); // type, }, ast.Node.Id.UnionTag => { const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); - try renderDocComments(tree, stream, tag, indent); + try renderDocComments(tree, stream, tag, indent, start_col); if (tag.type_expr == null and tag.value_expr == null) { - return renderToken(tree, stream, tag.name_token, indent, Space.Comma); // name, + return renderToken(tree, stream, tag.name_token, indent, start_col, Space.Comma); // name, } if (tag.type_expr == null) { - try renderToken(tree, stream, tag.name_token, indent, Space.Space); // name + try renderToken(tree, stream, tag.name_token, indent, start_col, Space.Space); // name } else { - try renderToken(tree, stream, tag.name_token, indent, Space.None); // name + try renderToken(tree, stream, tag.name_token, indent, start_col, Space.None); // name } if (tag.type_expr) |type_expr| { - try renderToken(tree, stream, tree.nextToken(tag.name_token), indent, Space.Space); // : + try renderToken(tree, stream, tree.nextToken(tag.name_token), indent, start_col, Space.Space); // : if (tag.value_expr == null) { - return renderExpression(allocator, stream, tree, indent, type_expr, Space.Comma); // type, + try renderExpression(allocator, stream, tree, indent, start_col, type_expr, Space.Comma); // type, + return; } else { - try renderExpression(allocator, stream, tree, indent, type_expr, Space.Space); // type + try renderExpression(allocator, stream, tree, indent, start_col, type_expr, Space.Space); // type } } const value_expr = ??tag.value_expr; - try renderToken(tree, stream, tree.prevToken(value_expr.firstToken()), indent, Space.Space); // = - try renderExpression(allocator, stream, tree, indent, value_expr, Space.Comma); // value, + try renderToken(tree, stream, tree.prevToken(value_expr.firstToken()), indent, start_col, Space.Space); // = + try renderExpression(allocator, stream, tree, indent, start_col, value_expr, Space.Comma); // value, }, ast.Node.Id.EnumTag => { const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); - try renderDocComments(tree, stream, tag, indent); + try renderDocComments(tree, stream, tag, indent, start_col); if (tag.value) |value| { - try renderToken(tree, stream, tag.name_token, indent, Space.Space); // name + try renderToken(tree, stream, tag.name_token, indent, start_col, Space.Space); // name - try renderToken(tree, stream, tree.nextToken(tag.name_token), indent, Space.Space); // = - try renderExpression(allocator, stream, tree, indent, value, Space.Comma); + try renderToken(tree, stream, tree.nextToken(tag.name_token), indent, start_col, Space.Space); // = + try renderExpression(allocator, stream, tree, indent, start_col, value, Space.Comma); } else { - try renderToken(tree, stream, tag.name_token, indent, Space.Comma); // name + try renderToken(tree, stream, tag.name_token, indent, start_col, Space.Comma); // name } }, ast.Node.Id.Comptime => { assert(!decl.requireSemiColon()); - try renderExpression(allocator, stream, tree, indent, decl, Space.Newline); + try renderExpression(allocator, stream, tree, indent, start_col, decl, Space.Newline); }, else => unreachable, } } -fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node, space: Space) (@typeOf(stream).Child.Error || Error)!void { +fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, start_col: &usize, base: &ast.Node, space: Space,) (@typeOf(stream).Child.Error || Error)!void { switch (base.id) { ast.Node.Id.Identifier => { const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); - try renderToken(tree, stream, identifier.token, indent, space); + return renderToken(tree, stream, identifier.token, indent, start_col, space); }, ast.Node.Id.Block => { const block = @fieldParentPtr(ast.Node.Block, "base", base); if (block.label) |label| { - try renderToken(tree, stream, label, indent, Space.None); - try renderToken(tree, stream, tree.nextToken(label), indent, Space.Space); + try renderToken(tree, stream, label, indent, start_col, Space.None); + try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); } if (block.statements.len == 0) { - try renderToken(tree, stream, block.lbrace, indent + indent_delta, Space.None); - try renderToken(tree, stream, block.rbrace, indent, space); + try renderToken(tree, stream, block.lbrace, indent + indent_delta, start_col, Space.None); + return renderToken(tree, stream, block.rbrace, indent, start_col, space); } else { const block_indent = indent + indent_delta; - try renderToken(tree, stream, block.lbrace, block_indent, Space.Newline); + try renderToken(tree, stream, block.lbrace, block_indent, start_col, Space.Newline); var it = block.statements.iterator(0); while (it.next()) |statement| { try stream.writeByteNTimes(' ', block_indent); - try renderStatement(allocator, stream, tree, block_indent, statement.*); + try renderStatement(allocator, stream, tree, block_indent, start_col, statement.*); if (it.peek()) |next_statement| { - try renderExtraNewline(tree, stream, next_statement.*); + try renderExtraNewline(tree, stream, start_col, next_statement.*); } } try stream.writeByteNTimes(' ', indent); - try renderToken(tree, stream, block.rbrace, indent, space); + return renderToken(tree, stream, block.rbrace, indent, start_col, space); } }, ast.Node.Id.Defer => { const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); - try renderToken(tree, stream, defer_node.defer_token, indent, Space.Space); - try renderExpression(allocator, stream, tree, indent, defer_node.expr, space); + try renderToken(tree, stream, defer_node.defer_token, indent, start_col, Space.Space); + return renderExpression(allocator, stream, tree, indent, start_col, defer_node.expr, space); }, ast.Node.Id.Comptime => { const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); - try renderToken(tree, stream, comptime_node.comptime_token, indent, Space.Space); - try renderExpression(allocator, stream, tree, indent, comptime_node.expr, space); + try renderToken(tree, stream, comptime_node.comptime_token, indent, start_col, Space.Space); + return renderExpression(allocator, stream, tree, indent, start_col, comptime_node.expr, space); }, ast.Node.Id.AsyncAttribute => { const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); if (async_attr.allocator_type) |allocator_type| { - try renderToken(tree, stream, async_attr.async_token, indent, Space.None); + try renderToken(tree, stream, async_attr.async_token, indent, start_col, Space.None); - try renderToken(tree, stream, tree.nextToken(async_attr.async_token), indent, Space.None); - try renderExpression(allocator, stream, tree, indent, allocator_type, Space.None); - try renderToken(tree, stream, tree.nextToken(allocator_type.lastToken()), indent, space); + try renderToken(tree, stream, tree.nextToken(async_attr.async_token), indent, start_col, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, allocator_type, Space.None); + return renderToken(tree, stream, tree.nextToken(allocator_type.lastToken()), indent, start_col, space); } else { - try renderToken(tree, stream, async_attr.async_token, indent, space); + return renderToken(tree, stream, async_attr.async_token, indent, start_col, space); } }, @@ -225,24 +227,24 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); if (suspend_node.label) |label| { - try renderToken(tree, stream, label, indent, Space.None); - try renderToken(tree, stream, tree.nextToken(label), indent, Space.Space); + try renderToken(tree, stream, label, indent, start_col, Space.None); + try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); } if (suspend_node.payload) |payload| { if (suspend_node.body) |body| { - try renderToken(tree, stream, suspend_node.suspend_token, indent, Space.Space); - try renderExpression(allocator, stream, tree, indent, payload, Space.Space); - try renderExpression(allocator, stream, tree, indent, body, space); + try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space); + return renderExpression(allocator, stream, tree, indent, start_col, body, space); } else { - try renderToken(tree, stream, suspend_node.suspend_token, indent, Space.Space); - try renderExpression(allocator, stream, tree, indent, payload, space); + try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); + return renderExpression(allocator, stream, tree, indent, start_col, payload, space); } } else if (suspend_node.body) |body| { - try renderToken(tree, stream, suspend_node.suspend_token, indent, Space.Space); - try renderExpression(allocator, stream, tree, indent, body, space); + try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); + return renderExpression(allocator, stream, tree, indent, start_col, body, space); } else { - try renderToken(tree, stream, suspend_node.suspend_token, indent, space); + return renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, space); } }, @@ -254,7 +256,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind ast.Node.InfixOp.Op.Period, ast.Node.InfixOp.Op.ErrorUnion, ast.Node.InfixOp.Op.Range => Space.None, else => Space.Space, }; - try renderExpression(allocator, stream, tree, indent, infix_op_node.lhs, op_space); + try renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.lhs, op_space); const after_op_space = blk: { const loc = tree.tokenLocation(tree.tokens.at(infix_op_node.op_token).end, @@ -262,19 +264,20 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind break :blk if (loc.line == 0) op_space else Space.Newline; }; - try renderToken(tree, stream, infix_op_node.op_token, indent, after_op_space); + try renderToken(tree, stream, infix_op_node.op_token, indent, start_col, after_op_space); if (after_op_space == Space.Newline) { try stream.writeByteNTimes(' ', indent + indent_delta); + start_col.* = indent + indent_delta; } switch (infix_op_node.op) { ast.Node.InfixOp.Op.Catch => |maybe_payload| if (maybe_payload) |payload| { - try renderExpression(allocator, stream, tree, indent, payload, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space); }, else => {}, } - try renderExpression(allocator, stream, tree, indent, infix_op_node.rhs, space); + return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space); }, ast.Node.Id.PrefixOp => { @@ -282,81 +285,81 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind switch (prefix_op_node.op) { ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { - try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.None); // & + try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // & if (addr_of_info.align_info) |align_info| { const lparen_token = tree.prevToken(align_info.node.firstToken()); const align_token = tree.prevToken(lparen_token); - try renderToken(tree, stream, align_token, indent, Space.None); // align - try renderToken(tree, stream, lparen_token, indent, Space.None); // ( + try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align + try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // ( - try renderExpression(allocator, stream, tree, indent, align_info.node, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None); if (align_info.bit_range) |bit_range| { const colon1 = tree.prevToken(bit_range.start.firstToken()); const colon2 = tree.prevToken(bit_range.end.firstToken()); - try renderToken(tree, stream, colon1, indent, Space.None); // : - try renderExpression(allocator, stream, tree, indent, bit_range.start, Space.None); - try renderToken(tree, stream, colon2, indent, Space.None); // : - try renderExpression(allocator, stream, tree, indent, bit_range.end, Space.None); + try renderToken(tree, stream, colon1, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None); + try renderToken(tree, stream, colon2, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None); const rparen_token = tree.nextToken(bit_range.end.lastToken()); - try renderToken(tree, stream, rparen_token, indent, Space.Space); // ) + try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) } else { const rparen_token = tree.nextToken(align_info.node.lastToken()); - try renderToken(tree, stream, rparen_token, indent, Space.Space); // ) + try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) } } if (addr_of_info.const_token) |const_token| { - try renderToken(tree, stream, const_token, indent, Space.Space); // const + try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const } if (addr_of_info.volatile_token) |volatile_token| { - try renderToken(tree, stream, volatile_token, indent, Space.Space); // volatile + try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile } }, ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { - try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.None); // [ - try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, Space.None); // ] + try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [ + try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ] if (addr_of_info.align_info) |align_info| { const lparen_token = tree.prevToken(align_info.node.firstToken()); const align_token = tree.prevToken(lparen_token); - try renderToken(tree, stream, align_token, indent, Space.None); // align - try renderToken(tree, stream, lparen_token, indent, Space.None); // ( + try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align + try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // ( - try renderExpression(allocator, stream, tree, indent, align_info.node, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None); if (align_info.bit_range) |bit_range| { const colon1 = tree.prevToken(bit_range.start.firstToken()); const colon2 = tree.prevToken(bit_range.end.firstToken()); - try renderToken(tree, stream, colon1, indent, Space.None); // : - try renderExpression(allocator, stream, tree, indent, bit_range.start, Space.None); - try renderToken(tree, stream, colon2, indent, Space.None); // : - try renderExpression(allocator, stream, tree, indent, bit_range.end, Space.None); + try renderToken(tree, stream, colon1, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None); + try renderToken(tree, stream, colon2, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None); const rparen_token = tree.nextToken(bit_range.end.lastToken()); - try renderToken(tree, stream, rparen_token, indent, Space.Space); // ) + try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) } else { const rparen_token = tree.nextToken(align_info.node.lastToken()); - try renderToken(tree, stream, rparen_token, indent, Space.Space); // ) + try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) } } if (addr_of_info.const_token) |const_token| { - try renderToken(tree, stream, const_token, indent, Space.Space); + try renderToken(tree, stream, const_token, indent, start_col, Space.Space); } if (addr_of_info.volatile_token) |volatile_token| { - try renderToken(tree, stream, volatile_token, indent, Space.Space); + try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); } }, ast.Node.PrefixOp.Op.ArrayType => |array_index| { - try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.None); // [ - try renderExpression(allocator, stream, tree, indent, array_index, Space.None); - try renderToken(tree, stream, tree.nextToken(array_index.lastToken()), indent, Space.None); // ] + try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [ + try renderExpression(allocator, stream, tree, indent, start_col, array_index, Space.None); + try renderToken(tree, stream, tree.nextToken(array_index.lastToken()), indent, start_col, Space.None); // ] }, ast.Node.PrefixOp.Op.BitNot, ast.Node.PrefixOp.Op.BoolNot, @@ -365,18 +368,18 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind ast.Node.PrefixOp.Op.UnwrapMaybe, ast.Node.PrefixOp.Op.MaybeType, ast.Node.PrefixOp.Op.PointerType => { - try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.None); + try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); }, ast.Node.PrefixOp.Op.Try, ast.Node.PrefixOp.Op.Await, ast.Node.PrefixOp.Op.Cancel, ast.Node.PrefixOp.Op.Resume => { - try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.Space); + try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space); }, } - try renderExpression(allocator, stream, tree, indent, prefix_op_node.rhs, space); + return renderExpression(allocator, stream, tree, indent, start_col, prefix_op_node.rhs, space); }, ast.Node.Id.SuffixOp => { @@ -385,17 +388,16 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind switch (suffix_op.op) { @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| { if (call_info.async_attr) |async_attr| { - try renderExpression(allocator, stream, tree, indent, &async_attr.base, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, &async_attr.base, Space.Space); } - try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); const lparen = tree.nextToken(suffix_op.lhs.lastToken()); if (call_info.params.len == 0) { - try renderToken(tree, stream, lparen, indent, Space.None); - try renderToken(tree, stream, suffix_op.rtoken, indent, space); - return; + try renderToken(tree, stream, lparen, indent, start_col, Space.None); + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } const src_has_trailing_comma = blk: { @@ -405,7 +407,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind if (src_has_trailing_comma) { const new_indent = indent + indent_delta; - try renderToken(tree, stream, lparen, new_indent, Space.Newline); + try renderToken(tree, stream, lparen, new_indent, start_col, Space.Newline); var it = call_info.params.iterator(0); while (true) { @@ -419,72 +421,70 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind }; if (it.peek()) |next_node| { - try renderExpression(allocator, stream, tree, param_node_new_indent, param_node.*, Space.None); + try renderExpression(allocator, stream, tree, param_node_new_indent, start_col, param_node.*, Space.None); const comma = tree.nextToken(param_node.*.lastToken()); - try renderToken(tree, stream, comma, new_indent, Space.Newline); // , - try renderExtraNewline(tree, stream, next_node.*); + try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline); // , + try renderExtraNewline(tree, stream, start_col, next_node.*); } else { - try renderExpression(allocator, stream, tree, param_node_new_indent, param_node.*, Space.Comma); + try renderExpression(allocator, stream, tree, param_node_new_indent, start_col, param_node.*, Space.Comma); try stream.writeByteNTimes(' ', indent); - try renderToken(tree, stream, suffix_op.rtoken, indent, space); - return; + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } } } - try renderToken(tree, stream, lparen, indent, Space.None); // ( + try renderToken(tree, stream, lparen, indent, start_col, Space.None); // ( var it = call_info.params.iterator(0); while (it.next()) |param_node| { - try renderExpression(allocator, stream, tree, indent, param_node.*, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, param_node.*, Space.None); if (it.peek() != null) { const comma = tree.nextToken(param_node.*.lastToken()); - try renderToken(tree, stream, comma, indent, Space.Space); + try renderToken(tree, stream, comma, indent, start_col, Space.Space); } } - try renderToken(tree, stream, suffix_op.rtoken, indent, space); + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); }, ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { const lbracket = tree.prevToken(index_expr.firstToken()); const rbracket = tree.nextToken(index_expr.lastToken()); - try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); - try renderToken(tree, stream, lbracket, indent, Space.None); // [ - try renderExpression(allocator, stream, tree, indent, index_expr, Space.None); - try renderToken(tree, stream, rbracket, indent, space); // ] + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [ + try renderExpression(allocator, stream, tree, indent, start_col, index_expr, Space.None); + return renderToken(tree, stream, rbracket, indent, start_col, space); // ] }, ast.Node.SuffixOp.Op.Deref => { - try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); - try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, Space.None); // . - try renderToken(tree, stream, suffix_op.rtoken, indent, space); // * + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // . + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // * }, @TagType(ast.Node.SuffixOp.Op).Slice => |range| { - try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); const lbracket = tree.prevToken(range.start.firstToken()); const dotdot = tree.nextToken(range.start.lastToken()); - try renderToken(tree, stream, lbracket, indent, Space.None); // [ - try renderExpression(allocator, stream, tree, indent, range.start, Space.None); - try renderToken(tree, stream, dotdot, indent, Space.None); // .. + try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [ + try renderExpression(allocator, stream, tree, indent, start_col, range.start, Space.None); + try renderToken(tree, stream, dotdot, indent, start_col, Space.None); // .. if (range.end) |end| { - try renderExpression(allocator, stream, tree, indent, end, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, end, Space.None); } - try renderToken(tree, stream, suffix_op.rtoken, indent, space); // ] + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ] }, ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| { const lbrace = tree.nextToken(suffix_op.lhs.lastToken()); if (field_inits.len == 0) { - try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); - try renderToken(tree, stream, lbrace, indent, Space.None); - try renderToken(tree, stream, suffix_op.rtoken, indent, space); - return; + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + try renderToken(tree, stream, lbrace, indent, start_col, Space.None); + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } if (field_inits.len == 1) blk: { @@ -496,11 +496,10 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind } } - try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); - try renderToken(tree, stream, lbrace, indent, Space.Space); - try renderExpression(allocator, stream, tree, indent, &field_init.base, Space.Space); - try renderToken(tree, stream, suffix_op.rtoken, indent, space); - return; + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + try renderToken(tree, stream, lbrace, indent, start_col, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, &field_init.base, Space.Space); + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } const src_has_trailing_comma = blk: { @@ -515,27 +514,26 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind if (!src_has_trailing_comma and src_same_line) { // render all on one line, no trailing comma - try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); - try renderToken(tree, stream, lbrace, indent, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + try renderToken(tree, stream, lbrace, indent, start_col, Space.Space); var it = field_inits.iterator(0); while (it.next()) |field_init| { if (it.peek() != null) { - try renderExpression(allocator, stream, tree, indent, field_init.*, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, field_init.*, Space.None); const comma = tree.nextToken(field_init.*.lastToken()); - try renderToken(tree, stream, comma, indent, Space.Space); + try renderToken(tree, stream, comma, indent, start_col, Space.Space); } else { - try renderExpression(allocator, stream, tree, indent, field_init.*, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, field_init.*, Space.Space); } } - try renderToken(tree, stream, suffix_op.rtoken, indent, space); - return; + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } - try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); - try renderToken(tree, stream, lbrace, indent, Space.Newline); + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + try renderToken(tree, stream, lbrace, indent, start_col, Space.Newline); const new_indent = indent + indent_delta; @@ -544,41 +542,39 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try stream.writeByteNTimes(' ', new_indent); if (it.peek()) |next_field_init| { - try renderExpression(allocator, stream, tree, new_indent, field_init.*, Space.None); + try renderExpression(allocator, stream, tree, new_indent, start_col, field_init.*, Space.None); const comma = tree.nextToken(field_init.*.lastToken()); - try renderToken(tree, stream, comma, new_indent, Space.Newline); + try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline); - try renderExtraNewline(tree, stream, next_field_init.*); + try renderExtraNewline(tree, stream, start_col, next_field_init.*); } else { - try renderExpression(allocator, stream, tree, new_indent, field_init.*, Space.Comma); + try renderExpression(allocator, stream, tree, new_indent, start_col, field_init.*, Space.Comma); } } try stream.writeByteNTimes(' ', indent); - try renderToken(tree, stream, suffix_op.rtoken, indent, space); + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); }, ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| { const lbrace = tree.nextToken(suffix_op.lhs.lastToken()); if (exprs.len == 0) { - try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); - try renderToken(tree, stream, lbrace, indent, Space.None); - try renderToken(tree, stream, suffix_op.rtoken, indent, space); - return; + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + try renderToken(tree, stream, lbrace, indent, start_col, Space.None); + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } if (exprs.len == 1) { const expr = exprs.at(0).*; - try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); - try renderToken(tree, stream, lbrace, indent, Space.None); - try renderExpression(allocator, stream, tree, indent, expr, Space.None); - try renderToken(tree, stream, suffix_op.rtoken, indent, space); - return; + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); + try renderToken(tree, stream, lbrace, indent, start_col, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None); + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } - try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); // scan to find row size const maybe_row_size: ?usize = blk: { @@ -613,50 +609,48 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind if (maybe_row_size) |row_size| { const new_indent = indent + indent_delta; - try renderToken(tree, stream, lbrace, new_indent, Space.Newline); + try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline); try stream.writeByteNTimes(' ', new_indent); var it = exprs.iterator(0); var i: usize = 1; while (it.next()) |expr| { if (it.peek()) |next_expr| { - try renderExpression(allocator, stream, tree, new_indent, expr.*, Space.None); + try renderExpression(allocator, stream, tree, new_indent, start_col, expr.*, Space.None); const comma = tree.nextToken(expr.*.lastToken()); if (i != row_size) { - try renderToken(tree, stream, comma, new_indent, Space.Space); // , + try renderToken(tree, stream, comma, new_indent, start_col, Space.Space); // , i += 1; continue; } i = 1; - try renderToken(tree, stream, comma, new_indent, Space.Newline); // , + try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline); // , - try renderExtraNewline(tree, stream, next_expr.*); + try renderExtraNewline(tree, stream, start_col, next_expr.*); try stream.writeByteNTimes(' ', new_indent); } else { - try renderExpression(allocator, stream, tree, new_indent, expr.*, Space.Comma); // , + try renderExpression(allocator, stream, tree, new_indent, start_col, expr.*, Space.Comma); // , } } try stream.writeByteNTimes(' ', indent); - try renderToken(tree, stream, suffix_op.rtoken, indent, space); - return; + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } else { - try renderToken(tree, stream, lbrace, indent, Space.Space); + try renderToken(tree, stream, lbrace, indent, start_col, Space.Space); var it = exprs.iterator(0); while (it.next()) |expr| { if (it.peek()) |next_expr| { - try renderExpression(allocator, stream, tree, indent, expr.*, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, expr.*, Space.None); const comma = tree.nextToken(expr.*.lastToken()); - try renderToken(tree, stream, comma, indent, Space.Space); // , + try renderToken(tree, stream, comma, indent, start_col, Space.Space); // , } else { - try renderExpression(allocator, stream, tree, indent, expr.*, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, expr.*, Space.Space); } } - try renderToken(tree, stream, suffix_op.rtoken, indent, space); - return; + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } }, } @@ -667,195 +661,204 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind switch (flow_expr.kind) { ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { - const kw_space = if (maybe_label != null or flow_expr.rhs != null) Space.Space else space; - try renderToken(tree, stream, flow_expr.ltoken, indent, kw_space); + if (maybe_label == null and flow_expr.rhs == null) { + return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // break + } + + try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // break if (maybe_label) |label| { const colon = tree.nextToken(flow_expr.ltoken); - try renderToken(tree, stream, colon, indent, Space.None); + try renderToken(tree, stream, colon, indent, start_col, Space.None); // : - const expr_space = if (flow_expr.rhs != null) Space.Space else space; - try renderExpression(allocator, stream, tree, indent, label, expr_space); + if (flow_expr.rhs == null) { + return renderExpression(allocator, stream, tree, indent, start_col, label, space); // label + } + try renderExpression(allocator, stream, tree, indent, start_col, label, Space.Space); // label } }, ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { - const kw_space = if (maybe_label != null or flow_expr.rhs != null) Space.Space else space; - try renderToken(tree, stream, flow_expr.ltoken, indent, kw_space); + assert(flow_expr.rhs == null); + + if (maybe_label == null and flow_expr.rhs == null) { + return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // continue + } + + try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // continue if (maybe_label) |label| { const colon = tree.nextToken(flow_expr.ltoken); - try renderToken(tree, stream, colon, indent, Space.None); + try renderToken(tree, stream, colon, indent, start_col, Space.None); // : - const expr_space = if (flow_expr.rhs != null) Space.Space else space; - try renderExpression(allocator, stream, tree, indent, label, space); + return renderExpression(allocator, stream, tree, indent, start_col, label, space); } }, ast.Node.ControlFlowExpression.Kind.Return => { - const kw_space = if (flow_expr.rhs != null) Space.Space else space; - try renderToken(tree, stream, flow_expr.ltoken, indent, kw_space); + if (flow_expr.rhs == null) { + return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); + } + try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); }, } - if (flow_expr.rhs) |rhs| { - try renderExpression(allocator, stream, tree, indent, rhs, space); - } + return renderExpression(allocator, stream, tree, indent, start_col, ??flow_expr.rhs, space); }, ast.Node.Id.Payload => { const payload = @fieldParentPtr(ast.Node.Payload, "base", base); - try renderToken(tree, stream, payload.lpipe, indent, Space.None); - try renderExpression(allocator, stream, tree, indent, payload.error_symbol, Space.None); - try renderToken(tree, stream, payload.rpipe, indent, space); + try renderToken(tree, stream, payload.lpipe, indent, start_col, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, payload.error_symbol, Space.None); + return renderToken(tree, stream, payload.rpipe, indent, start_col, space); }, ast.Node.Id.PointerPayload => { const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); - try renderToken(tree, stream, payload.lpipe, indent, Space.None); + try renderToken(tree, stream, payload.lpipe, indent, start_col, Space.None); if (payload.ptr_token) |ptr_token| { - try renderToken(tree, stream, ptr_token, indent, Space.None); + try renderToken(tree, stream, ptr_token, indent, start_col, Space.None); } - try renderExpression(allocator, stream, tree, indent, payload.value_symbol, Space.None); - try renderToken(tree, stream, payload.rpipe, indent, space); + try renderExpression(allocator, stream, tree, indent, start_col, payload.value_symbol, Space.None); + return renderToken(tree, stream, payload.rpipe, indent, start_col, space); }, ast.Node.Id.PointerIndexPayload => { const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); - try renderToken(tree, stream, payload.lpipe, indent, Space.None); + try renderToken(tree, stream, payload.lpipe, indent, start_col, Space.None); if (payload.ptr_token) |ptr_token| { - try renderToken(tree, stream, ptr_token, indent, Space.None); + try renderToken(tree, stream, ptr_token, indent, start_col, Space.None); } - try renderExpression(allocator, stream, tree, indent, payload.value_symbol, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, payload.value_symbol, Space.None); if (payload.index_symbol) |index_symbol| { const comma = tree.nextToken(payload.value_symbol.lastToken()); - try renderToken(tree, stream, comma, indent, Space.Space); - try renderExpression(allocator, stream, tree, indent, index_symbol, Space.None); + try renderToken(tree, stream, comma, indent, start_col, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, index_symbol, Space.None); } - try renderToken(tree, stream, payload.rpipe, indent, space); + return renderToken(tree, stream, payload.rpipe, indent, start_col, space); }, ast.Node.Id.GroupedExpression => { const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); - try renderToken(tree, stream, grouped_expr.lparen, indent, Space.None); - try renderExpression(allocator, stream, tree, indent, grouped_expr.expr, Space.None); - try renderToken(tree, stream, grouped_expr.rparen, indent, space); + try renderToken(tree, stream, grouped_expr.lparen, indent, start_col, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, grouped_expr.expr, Space.None); + return renderToken(tree, stream, grouped_expr.rparen, indent, start_col, space); }, ast.Node.Id.FieldInitializer => { const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); - try renderToken(tree, stream, field_init.period_token, indent, Space.None); // . - try renderToken(tree, stream, field_init.name_token, indent, Space.Space); // name - try renderToken(tree, stream, tree.nextToken(field_init.name_token), indent, Space.Space); // = - try renderExpression(allocator, stream, tree, indent, field_init.expr, space); + try renderToken(tree, stream, field_init.period_token, indent, start_col, Space.None); // . + try renderToken(tree, stream, field_init.name_token, indent, start_col, Space.Space); // name + try renderToken(tree, stream, tree.nextToken(field_init.name_token), indent, start_col, Space.Space); // = + return renderExpression(allocator, stream, tree, indent, start_col, field_init.expr, space); }, ast.Node.Id.IntegerLiteral => { const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); - try renderToken(tree, stream, integer_literal.token, indent, space); + return renderToken(tree, stream, integer_literal.token, indent, start_col, space); }, ast.Node.Id.FloatLiteral => { const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); - try renderToken(tree, stream, float_literal.token, indent, space); + return renderToken(tree, stream, float_literal.token, indent, start_col, space); }, ast.Node.Id.StringLiteral => { const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); - try renderToken(tree, stream, string_literal.token, indent, space); + return renderToken(tree, stream, string_literal.token, indent, start_col, space); }, ast.Node.Id.CharLiteral => { const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); - try renderToken(tree, stream, char_literal.token, indent, space); + return renderToken(tree, stream, char_literal.token, indent, start_col, space); }, ast.Node.Id.BoolLiteral => { const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); - try renderToken(tree, stream, bool_literal.token, indent, space); + return renderToken(tree, stream, bool_literal.token, indent, start_col, space); }, ast.Node.Id.NullLiteral => { const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); - try renderToken(tree, stream, null_literal.token, indent, space); + return renderToken(tree, stream, null_literal.token, indent, start_col, space); }, ast.Node.Id.ThisLiteral => { const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base); - try renderToken(tree, stream, this_literal.token, indent, space); + return renderToken(tree, stream, this_literal.token, indent, start_col, space); }, ast.Node.Id.Unreachable => { const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); - try renderToken(tree, stream, unreachable_node.token, indent, space); + return renderToken(tree, stream, unreachable_node.token, indent, start_col, space); }, ast.Node.Id.ErrorType => { const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); - try renderToken(tree, stream, error_type.token, indent, space); + return renderToken(tree, stream, error_type.token, indent, start_col, space); }, ast.Node.Id.VarType => { const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); - try renderToken(tree, stream, var_type.token, indent, space); + return renderToken(tree, stream, var_type.token, indent, start_col, space); }, ast.Node.Id.ContainerDecl => { const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); if (container_decl.layout_token) |layout_token| { - try renderToken(tree, stream, layout_token, indent, Space.Space); + try renderToken(tree, stream, layout_token, indent, start_col, Space.Space); } switch (container_decl.init_arg_expr) { ast.Node.ContainerDecl.InitArg.None => { - try renderToken(tree, stream, container_decl.kind_token, indent, Space.Space); // union + try renderToken(tree, stream, container_decl.kind_token, indent, start_col, Space.Space); // union }, ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { - try renderToken(tree, stream, container_decl.kind_token, indent, Space.None); // union + try renderToken(tree, stream, container_decl.kind_token, indent, start_col, Space.None); // union const lparen = tree.nextToken(container_decl.kind_token); const enum_token = tree.nextToken(lparen); - try renderToken(tree, stream, lparen, indent, Space.None); // ( - try renderToken(tree, stream, enum_token, indent, Space.None); // enum + try renderToken(tree, stream, lparen, indent, start_col, Space.None); // ( + try renderToken(tree, stream, enum_token, indent, start_col, Space.None); // enum if (enum_tag_type) |expr| { - try renderToken(tree, stream, tree.nextToken(enum_token), indent, Space.None); // ( - try renderExpression(allocator, stream, tree, indent, expr, Space.None); + try renderToken(tree, stream, tree.nextToken(enum_token), indent, start_col, Space.None); // ( + try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None); const rparen = tree.nextToken(expr.lastToken()); - try renderToken(tree, stream, rparen, indent, Space.None); // ) - try renderToken(tree, stream, tree.nextToken(rparen), indent, Space.Space); // ) + try renderToken(tree, stream, rparen, indent, start_col, Space.None); // ) + try renderToken(tree, stream, tree.nextToken(rparen), indent, start_col, Space.Space); // ) } else { - try renderToken(tree, stream, tree.nextToken(enum_token), indent, Space.Space); // ) + try renderToken(tree, stream, tree.nextToken(enum_token), indent, start_col, Space.Space); // ) } }, ast.Node.ContainerDecl.InitArg.Type => |type_expr| { - try renderToken(tree, stream, container_decl.kind_token, indent, Space.None); // union + try renderToken(tree, stream, container_decl.kind_token, indent, start_col, Space.None); // union const lparen = tree.nextToken(container_decl.kind_token); const rparen = tree.nextToken(type_expr.lastToken()); - try renderToken(tree, stream, lparen, indent, Space.None); // ( - try renderExpression(allocator, stream, tree, indent, type_expr, Space.None); - try renderToken(tree, stream, rparen, indent, Space.Space); // ) + try renderToken(tree, stream, lparen, indent, start_col, Space.None); // ( + try renderExpression(allocator, stream, tree, indent, start_col, type_expr, Space.None); + try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // ) }, } if (container_decl.fields_and_decls.len == 0) { - try renderToken(tree, stream, container_decl.lbrace_token, indent + indent_delta, Space.None); // { - try renderToken(tree, stream, container_decl.rbrace_token, indent, space); // } + try renderToken(tree, stream, container_decl.lbrace_token, indent + indent_delta, start_col, Space.None); // { + return renderToken(tree, stream, container_decl.rbrace_token, indent, start_col, space); // } } else { const new_indent = indent + indent_delta; - try renderToken(tree, stream, container_decl.lbrace_token, new_indent, Space.Newline); // { + try renderToken(tree, stream, container_decl.lbrace_token, new_indent, start_col, Space.Newline); // { var it = container_decl.fields_and_decls.iterator(0); while (it.next()) |decl| { try stream.writeByteNTimes(' ', new_indent); - try renderTopLevelDecl(allocator, stream, tree, new_indent, decl.*); + try renderTopLevelDecl(allocator, stream, tree, new_indent, start_col, decl.*); if (it.peek()) |next_decl| { - try renderExtraNewline(tree, stream, next_decl.*); + try renderExtraNewline(tree, stream, start_col, next_decl.*); } } try stream.writeByteNTimes(' ', indent); - try renderToken(tree, stream, container_decl.rbrace_token, indent, space); // } + return renderToken(tree, stream, container_decl.rbrace_token, indent, start_col, space); // } } }, @@ -865,10 +868,9 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const lbrace = tree.nextToken(err_set_decl.error_token); if (err_set_decl.decls.len == 0) { - try renderToken(tree, stream, err_set_decl.error_token, indent, Space.None); - try renderToken(tree, stream, lbrace, indent, Space.None); - try renderToken(tree, stream, err_set_decl.rbrace_token, indent, space); - return; + try renderToken(tree, stream, err_set_decl.error_token, indent, start_col, Space.None); + try renderToken(tree, stream, lbrace, indent, start_col, Space.None); + return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); } if (err_set_decl.decls.len == 1) blk: { @@ -882,15 +884,14 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind break :blk; } - try renderToken(tree, stream, err_set_decl.error_token, indent, Space.None); // error - try renderToken(tree, stream, lbrace, indent, Space.None); // { - try renderExpression(allocator, stream, tree, indent, node, Space.None); - try renderToken(tree, stream, err_set_decl.rbrace_token, indent, space); // } - return; + try renderToken(tree, stream, err_set_decl.error_token, indent, start_col, Space.None); // error + try renderToken(tree, stream, lbrace, indent, start_col, Space.None); // { + try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None); + return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // } } - try renderToken(tree, stream, err_set_decl.error_token, indent, Space.None); // error - try renderToken(tree, stream, lbrace, indent, Space.Newline); // { + try renderToken(tree, stream, err_set_decl.error_token, indent, start_col, Space.None); // error + try renderToken(tree, stream, lbrace, indent, start_col, Space.Newline); // { const new_indent = indent + indent_delta; var it = err_set_decl.decls.iterator(0); @@ -898,24 +899,24 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try stream.writeByteNTimes(' ', new_indent); if (it.peek()) |next_node| { - try renderExpression(allocator, stream, tree, new_indent, node.*, Space.None); - try renderToken(tree, stream, tree.nextToken(node.*.lastToken()), new_indent, Space.Newline); // , + try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.None); + try renderToken(tree, stream, tree.nextToken(node.*.lastToken()), new_indent, start_col, Space.Newline); // , - try renderExtraNewline(tree, stream, next_node.*); + try renderExtraNewline(tree, stream, start_col, next_node.*); } else { - try renderExpression(allocator, stream, tree, new_indent, node.*, Space.Comma); + try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.Comma); } } try stream.writeByteNTimes(' ', indent); - try renderToken(tree, stream, err_set_decl.rbrace_token, indent, space); // } + return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // } }, ast.Node.Id.ErrorTag => { const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", base); - try renderDocComments(tree, stream, tag, indent); - try renderToken(tree, stream, tag.name_token, indent, space); // name + try renderDocComments(tree, stream, tag, indent, start_col); + return renderToken(tree, stream, tag.name_token, indent, start_col, space); // name }, ast.Node.Id.MultilineStringLiteral => { @@ -933,32 +934,32 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind if (!skip_first_indent) { try stream.writeByteNTimes(' ', indent + indent_delta); } - try renderToken(tree, stream, t, indent, Space.None); + try renderToken(tree, stream, t, indent, start_col, Space.None); skip_first_indent = false; } try stream.writeByteNTimes(' ', indent); }, ast.Node.Id.UndefinedLiteral => { const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); - try renderToken(tree, stream, undefined_literal.token, indent, space); + return renderToken(tree, stream, undefined_literal.token, indent, start_col, space); }, ast.Node.Id.BuiltinCall => { const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); - try renderToken(tree, stream, builtin_call.builtin_token, indent, Space.None); // @name - try renderToken(tree, stream, tree.nextToken(builtin_call.builtin_token), indent, Space.None); // ( + try renderToken(tree, stream, builtin_call.builtin_token, indent, start_col, Space.None); // @name + try renderToken(tree, stream, tree.nextToken(builtin_call.builtin_token), indent, start_col, Space.None); // ( var it = builtin_call.params.iterator(0); while (it.next()) |param_node| { - try renderExpression(allocator, stream, tree, indent, param_node.*, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, param_node.*, Space.None); if (it.peek() != null) { const comma_token = tree.nextToken(param_node.*.lastToken()); - try renderToken(tree, stream, comma_token, indent, Space.Space); // , + try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // , } } - try renderToken(tree, stream, builtin_call.rparen_token, indent, space); // ) + return renderToken(tree, stream, builtin_call.rparen_token, indent, start_col, space); // ) }, ast.Node.Id.FnProto => { @@ -968,31 +969,31 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const visib_token = tree.tokens.at(visib_token_index); assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export); - try renderToken(tree, stream, visib_token_index, indent, Space.Space); // pub + try renderToken(tree, stream, visib_token_index, indent, start_col, Space.Space); // pub } if (fn_proto.extern_export_inline_token) |extern_export_inline_token| { - try renderToken(tree, stream, extern_export_inline_token, indent, Space.Space); // extern/export + try renderToken(tree, stream, extern_export_inline_token, indent, start_col, Space.Space); // extern/export } if (fn_proto.lib_name) |lib_name| { - try renderExpression(allocator, stream, tree, indent, lib_name, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, lib_name, Space.Space); } if (fn_proto.cc_token) |cc_token| { - try renderToken(tree, stream, cc_token, indent, Space.Space); // stdcallcc + try renderToken(tree, stream, cc_token, indent, start_col, Space.Space); // stdcallcc } if (fn_proto.async_attr) |async_attr| { - try renderExpression(allocator, stream, tree, indent, &async_attr.base, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, &async_attr.base, Space.Space); } const lparen = if (fn_proto.name_token) |name_token| blk: { - try renderToken(tree, stream, fn_proto.fn_token, indent, Space.Space); // fn - try renderToken(tree, stream, name_token, indent, Space.None); // name + try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.Space); // fn + try renderToken(tree, stream, name_token, indent, start_col, Space.None); // name break :blk tree.nextToken(name_token); } else blk: { - try renderToken(tree, stream, fn_proto.fn_token, indent, Space.None); // fn + try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.None); // fn break :blk tree.nextToken(fn_proto.fn_token); }; @@ -1011,51 +1012,51 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind }; if (!src_params_trailing_comma and src_params_same_line) { - try renderToken(tree, stream, lparen, indent, Space.None); // ( + try renderToken(tree, stream, lparen, indent, start_col, Space.None); // ( // render all on one line, no trailing comma var it = fn_proto.params.iterator(0); while (it.next()) |param_decl_node| { - try renderParamDecl(allocator, stream, tree, indent, param_decl_node.*, Space.None); + try renderParamDecl(allocator, stream, tree, indent, start_col, param_decl_node.*, Space.None); if (it.peek() != null) { const comma = tree.nextToken(param_decl_node.*.lastToken()); - try renderToken(tree, stream, comma, indent, Space.Space); // , + try renderToken(tree, stream, comma, indent, start_col, Space.Space); // , } } } else { // one param per line const new_indent = indent + indent_delta; - try renderToken(tree, stream, lparen, new_indent, Space.Newline); // ( + try renderToken(tree, stream, lparen, new_indent, start_col, Space.Newline); // ( var it = fn_proto.params.iterator(0); while (it.next()) |param_decl_node| { try stream.writeByteNTimes(' ', new_indent); - try renderParamDecl(allocator, stream, tree, indent, param_decl_node.*, Space.Comma); + try renderParamDecl(allocator, stream, tree, indent, start_col, param_decl_node.*, Space.Comma); } try stream.writeByteNTimes(' ', indent); } - try renderToken(tree, stream, rparen, indent, Space.Space); // ) + try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // ) if (fn_proto.align_expr) |align_expr| { const align_rparen = tree.nextToken(align_expr.lastToken()); const align_lparen = tree.prevToken(align_expr.firstToken()); const align_kw = tree.prevToken(align_lparen); - try renderToken(tree, stream, align_kw, indent, Space.None); // align - try renderToken(tree, stream, align_lparen, indent, Space.None); // ( - try renderExpression(allocator, stream, tree, indent, align_expr, Space.None); - try renderToken(tree, stream, align_rparen, indent, Space.Space); // ) + try renderToken(tree, stream, align_kw, indent, start_col, Space.None); // align + try renderToken(tree, stream, align_lparen, indent, start_col, Space.None); // ( + try renderExpression(allocator, stream, tree, indent, start_col, align_expr, Space.None); + try renderToken(tree, stream, align_rparen, indent, start_col, Space.Space); // ) } switch (fn_proto.return_type) { ast.Node.FnProto.ReturnType.Explicit => |node| { - try renderExpression(allocator, stream, tree, indent, node, space); + return renderExpression(allocator, stream, tree, indent, start_col, node, space); }, ast.Node.FnProto.ReturnType.InferErrorSet => |node| { - try renderToken(tree, stream, tree.prevToken(node.firstToken()), indent, Space.None); // ! - try renderExpression(allocator, stream, tree, indent, node, space); + try renderToken(tree, stream, tree.prevToken(node.firstToken()), indent, start_col, Space.None); // ! + return renderExpression(allocator, stream, tree, indent, start_col, node, space); }, } }, @@ -1064,11 +1065,11 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base); if (promise_type.result) |result| { - try renderToken(tree, stream, promise_type.promise_token, indent, Space.None); // promise - try renderToken(tree, stream, result.arrow_token, indent, Space.None); // -> - try renderExpression(allocator, stream, tree, indent, result.return_type, space); + try renderToken(tree, stream, promise_type.promise_token, indent, start_col, Space.None); // promise + try renderToken(tree, stream, result.arrow_token, indent, start_col, Space.None); // -> + return renderExpression(allocator, stream, tree, indent, start_col, result.return_type, space); } else { - try renderToken(tree, stream, promise_type.promise_token, indent, space); // promise + return renderToken(tree, stream, promise_type.promise_token, indent, start_col, space); // promise } }, @@ -1077,39 +1078,38 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind ast.Node.Id.Switch => { const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); - try renderToken(tree, stream, switch_node.switch_token, indent, Space.Space); // switch - try renderToken(tree, stream, tree.nextToken(switch_node.switch_token), indent, Space.None); // ( + try renderToken(tree, stream, switch_node.switch_token, indent, start_col, Space.Space); // switch + try renderToken(tree, stream, tree.nextToken(switch_node.switch_token), indent, start_col, Space.None); // ( const rparen = tree.nextToken(switch_node.expr.lastToken()); const lbrace = tree.nextToken(rparen); if (switch_node.cases.len == 0) { - try renderExpression(allocator, stream, tree, indent, switch_node.expr, Space.None); - try renderToken(tree, stream, rparen, indent, Space.Space); // ) - try renderToken(tree, stream, lbrace, indent, Space.None); // { - try renderToken(tree, stream, switch_node.rbrace, indent, space); // } - return; + try renderExpression(allocator, stream, tree, indent, start_col, switch_node.expr, Space.None); + try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // ) + try renderToken(tree, stream, lbrace, indent, start_col, Space.None); // { + return renderToken(tree, stream, switch_node.rbrace, indent, start_col, space); // } } - try renderExpression(allocator, stream, tree, indent, switch_node.expr, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, switch_node.expr, Space.None); const new_indent = indent + indent_delta; - try renderToken(tree, stream, rparen, indent, Space.Space); // ) - try renderToken(tree, stream, lbrace, new_indent, Space.Newline); // { + try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // ) + try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline); // { var it = switch_node.cases.iterator(0); while (it.next()) |node| { try stream.writeByteNTimes(' ', new_indent); - try renderExpression(allocator, stream, tree, new_indent, node.*, Space.Comma); + try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.Comma); if (it.peek()) |next_node| { - try renderExtraNewline(tree, stream, next_node.*); + try renderExtraNewline(tree, stream, start_col, next_node.*); } } try stream.writeByteNTimes(' ', indent); - try renderToken(tree, stream, switch_node.rbrace, indent, space); // } + return renderToken(tree, stream, switch_node.rbrace, indent, start_col, space); // } }, ast.Node.Id.SwitchCase => { @@ -1126,13 +1126,13 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind var it = switch_case.items.iterator(0); while (it.next()) |node| { if (it.peek()) |next_node| { - try renderExpression(allocator, stream, tree, indent, node.*, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None); const comma_token = tree.nextToken(node.*.lastToken()); - try renderToken(tree, stream, comma_token, indent, Space.Space); // , - try renderExtraNewline(tree, stream, next_node.*); + try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // , + try renderExtraNewline(tree, stream, start_col, next_node.*); } else { - try renderExpression(allocator, stream, tree, indent, node.*, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.Space); } } } else { @@ -1140,85 +1140,97 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind while (true) { const node = ??it.next(); if (it.peek()) |next_node| { - try renderExpression(allocator, stream, tree, indent, node.*, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None); const comma_token = tree.nextToken(node.*.lastToken()); - try renderToken(tree, stream, comma_token, indent, Space.Newline); // , - try renderExtraNewline(tree, stream, next_node.*); + try renderToken(tree, stream, comma_token, indent, start_col, Space.Newline); // , + try renderExtraNewline(tree, stream, start_col, next_node.*); try stream.writeByteNTimes(' ', indent); } else { - try renderExpression(allocator, stream, tree, indent, node.*, Space.Comma); + try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.Comma); try stream.writeByteNTimes(' ', indent); break; } } } - try renderToken(tree, stream, switch_case.arrow_token, indent, Space.Space); // => + try renderToken(tree, stream, switch_case.arrow_token, indent, start_col, Space.Space); // => if (switch_case.payload) |payload| { - try renderExpression(allocator, stream, tree, indent, payload, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space); } - try renderExpression(allocator, stream, tree, indent, switch_case.expr, space); + return renderExpression(allocator, stream, tree, indent, start_col, switch_case.expr, space); }, ast.Node.Id.SwitchElse => { const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); - try renderToken(tree, stream, switch_else.token, indent, space); + return renderToken(tree, stream, switch_else.token, indent, start_col, space); }, ast.Node.Id.Else => { const else_node = @fieldParentPtr(ast.Node.Else, "base", base); - const block_body = switch (else_node.body.id) { - ast.Node.Id.Block, - ast.Node.Id.If, - ast.Node.Id.For, - ast.Node.Id.While, - ast.Node.Id.Switch => true, - else => false, - }; + const body_is_block = nodeIsBlock(else_node.body); + const same_line = body_is_block or tree.tokensOnSameLine(else_node.else_token, else_node.body.lastToken()); - const after_else_space = if (block_body or else_node.payload != null) Space.Space else Space.Newline; - try renderToken(tree, stream, else_node.else_token, indent, after_else_space); + const after_else_space = if (same_line or else_node.payload != null) Space.Space else Space.Newline; + try renderToken(tree, stream, else_node.else_token, indent, start_col, after_else_space); if (else_node.payload) |payload| { - const payload_space = if (block_body) Space.Space else Space.Newline; - try renderExpression(allocator, stream, tree, indent, payload, Space.Space); + const payload_space = if (same_line) Space.Space else Space.Newline; + try renderExpression(allocator, stream, tree, indent, start_col, payload, payload_space); } - if (block_body) { - try renderExpression(allocator, stream, tree, indent, else_node.body, space); - } else { - try stream.writeByteNTimes(' ', indent + indent_delta); - try renderExpression(allocator, stream, tree, indent, else_node.body, space); + if (same_line) { + return renderExpression(allocator, stream, tree, indent, start_col, else_node.body, space); } + + try stream.writeByteNTimes(' ', indent + indent_delta); + start_col.* = indent + indent_delta; + return renderExpression(allocator, stream, tree, indent, start_col, else_node.body, space); }, ast.Node.Id.While => { const while_node = @fieldParentPtr(ast.Node.While, "base", base); if (while_node.label) |label| { - try renderToken(tree, stream, label, indent, Space.None); // label - try renderToken(tree, stream, tree.nextToken(label), indent, Space.Space); // : + try renderToken(tree, stream, label, indent, start_col, Space.None); // label + try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); // : } if (while_node.inline_token) |inline_token| { - try renderToken(tree, stream, inline_token, indent, Space.Space); // inline + try renderToken(tree, stream, inline_token, indent, start_col, Space.Space); // inline } - try renderToken(tree, stream, while_node.while_token, indent, Space.Space); // while - try renderToken(tree, stream, tree.nextToken(while_node.while_token), indent, Space.None); // ( - try renderExpression(allocator, stream, tree, indent, while_node.condition, Space.None); + try renderToken(tree, stream, while_node.while_token, indent, start_col, Space.Space); // while + try renderToken(tree, stream, tree.nextToken(while_node.while_token), indent, start_col, Space.None); // ( + try renderExpression(allocator, stream, tree, indent, start_col, while_node.condition, Space.None); + + const cond_rparen = tree.nextToken(while_node.condition.lastToken()); + + const body_is_block = nodeIsBlock(while_node.body); + + var block_start_space: Space = undefined; + var after_body_space: Space = undefined; + + if (body_is_block) { + block_start_space = Space.BlockStart; + after_body_space = if (while_node.@"else" == null) space else Space.SpaceOrOutdent; + } else if (tree.tokensOnSameLine(cond_rparen, while_node.body.lastToken())) { + block_start_space = Space.Space; + after_body_space = if (while_node.@"else" == null) space else Space.Space; + } else { + block_start_space = Space.Newline; + after_body_space = if (while_node.@"else" == null) space else Space.Newline; + } { - const rparen = tree.nextToken(while_node.condition.lastToken()); - const rparen_space = if (while_node.payload != null or while_node.continue_expr != null or - while_node.body.id == ast.Node.Id.Block) Space.Space else Space.Newline; - try renderToken(tree, stream, rparen, indent, rparen_space); // ) + const rparen_space = if (while_node.payload != null or while_node.continue_expr != null) Space.Space else block_start_space; + try renderToken(tree, stream, cond_rparen, indent, start_col, rparen_space); // ) } if (while_node.payload) |payload| { - try renderExpression(allocator, stream, tree, indent, payload, Space.Space); + const payload_space = if (while_node.continue_expr != null) Space.Space else block_start_space; + try renderExpression(allocator, stream, tree, indent, start_col, payload, payload_space); } if (while_node.continue_expr) |continue_expr| { @@ -1226,37 +1238,29 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const lparen = tree.prevToken(continue_expr.firstToken()); const colon = tree.prevToken(lparen); - try renderToken(tree, stream, colon, indent, Space.Space); // : - try renderToken(tree, stream, lparen, indent, Space.None); // ( + try renderToken(tree, stream, colon, indent, start_col, Space.Space); // : + try renderToken(tree, stream, lparen, indent, start_col, Space.None); // ( - try renderExpression(allocator, stream, tree, indent, continue_expr, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, continue_expr, Space.None); - const rparen_space = if (while_node.body.id == ast.Node.Id.Block) Space.Space else Space.Newline; - try renderToken(tree, stream, rparen, indent, rparen_space); // ) + try renderToken(tree, stream, rparen, indent, start_col, block_start_space); // ) } - const body_space = blk: { - if (while_node.@"else" != null) { - break :blk if (while_node.body.id == ast.Node.Id.Block) Space.Space else Space.Newline; - } else { - break :blk space; - } - }; - - if (while_node.body.id == ast.Node.Id.Block) { - try renderExpression(allocator, stream, tree, indent, while_node.body, body_space); - } else { - try stream.writeByteNTimes(' ', indent + indent_delta); - try renderExpression(allocator, stream, tree, indent, while_node.body, body_space); + var new_indent = indent; + if (block_start_space == Space.Newline) { + new_indent += indent_delta; + try stream.writeByteNTimes(' ', new_indent); + start_col.* = new_indent; } + try renderExpression(allocator, stream, tree, indent, start_col, while_node.body, after_body_space); + if (while_node.@"else") |@"else"| { - if (while_node.body.id == ast.Node.Id.Block) { - } else { + if (after_body_space == Space.Newline) { try stream.writeByteNTimes(' ', indent); + start_col.* = indent; } - - try renderExpression(allocator, stream, tree, indent, &@"else".base, space); + return renderExpression(allocator, stream, tree, indent, start_col, &@"else".base, space); } }, @@ -1264,26 +1268,26 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const for_node = @fieldParentPtr(ast.Node.For, "base", base); if (for_node.label) |label| { - try renderToken(tree, stream, label, indent, Space.None); // label - try renderToken(tree, stream, tree.nextToken(label), indent, Space.Space); // : + try renderToken(tree, stream, label, indent, start_col, Space.None); // label + try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); // : } if (for_node.inline_token) |inline_token| { - try renderToken(tree, stream, inline_token, indent, Space.Space); // inline + try renderToken(tree, stream, inline_token, indent, start_col, Space.Space); // inline } - try renderToken(tree, stream, for_node.for_token, indent, Space.Space); // for - try renderToken(tree, stream, tree.nextToken(for_node.for_token), indent, Space.None); // ( - try renderExpression(allocator, stream, tree, indent, for_node.array_expr, Space.None); + try renderToken(tree, stream, for_node.for_token, indent, start_col, Space.Space); // for + try renderToken(tree, stream, tree.nextToken(for_node.for_token), indent, start_col, Space.None); // ( + try renderExpression(allocator, stream, tree, indent, start_col, for_node.array_expr, Space.None); const rparen = tree.nextToken(for_node.array_expr.lastToken()); const rparen_space = if (for_node.payload != null or for_node.body.id == ast.Node.Id.Block) Space.Space else Space.Newline; - try renderToken(tree, stream, rparen, indent, rparen_space); // ) + try renderToken(tree, stream, rparen, indent, start_col, rparen_space); // ) if (for_node.payload) |payload| { const payload_space = if (for_node.body.id == ast.Node.Id.Block) Space.Space else Space.Newline; - try renderExpression(allocator, stream, tree, indent, payload, payload_space); + try renderExpression(allocator, stream, tree, indent, start_col, payload, payload_space); } const body_space = blk: { @@ -1298,10 +1302,10 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind } }; if (for_node.body.id == ast.Node.Id.Block) { - try renderExpression(allocator, stream, tree, indent, for_node.body, body_space); + try renderExpression(allocator, stream, tree, indent, start_col, for_node.body, body_space); } else { try stream.writeByteNTimes(' ', indent + indent_delta); - try renderExpression(allocator, stream, tree, indent, for_node.body, body_space); + try renderExpression(allocator, stream, tree, indent, start_col, for_node.body, body_space); } if (for_node.@"else") |@"else"| { @@ -1309,7 +1313,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try stream.writeByteNTimes(' ', indent); } - try renderExpression(allocator, stream, tree, indent, &@"else".base, space); + return renderExpression(allocator, stream, tree, indent, start_col, &@"else".base, space); } }, @@ -1319,128 +1323,109 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const lparen = tree.prevToken(if_node.condition.firstToken()); const rparen = tree.nextToken(if_node.condition.lastToken()); - try renderToken(tree, stream, if_node.if_token, indent, Space.Space); // if - try renderToken(tree, stream, lparen, indent, Space.None); // ( + try renderToken(tree, stream, if_node.if_token, indent, start_col, Space.Space); // if + try renderToken(tree, stream, lparen, indent, start_col, Space.None); // ( - try renderExpression(allocator, stream, tree, indent, if_node.condition, Space.None); // condition + try renderExpression(allocator, stream, tree, indent, start_col, if_node.condition, Space.None); // condition - const body_is_block = switch (if_node.body.id) { - ast.Node.Id.Block, - ast.Node.Id.If, - ast.Node.Id.For, - ast.Node.Id.While, - ast.Node.Id.Switch, - => true, - else => false, - }; + const body_is_block = nodeIsBlock(if_node.body); if (body_is_block) { - try renderToken(tree, stream, rparen, indent, Space.Space); // ) + const after_rparen_space = if (if_node.payload == null) Space.BlockStart else Space.Space; + try renderToken(tree, stream, rparen, indent, start_col, after_rparen_space); // ) if (if_node.payload) |payload| { - try renderExpression(allocator, stream, tree, indent, payload, Space.Space); // |x| + try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.BlockStart); // |x| } if (if_node.@"else") |@"else"| { - try renderExpression(allocator, stream, tree, indent, if_node.body, Space.SpaceOrOutdent); - return renderExpression(allocator, stream, tree, indent, &@"else".base, space); + try renderExpression(allocator, stream, tree, indent, start_col, if_node.body, Space.SpaceOrOutdent); + return renderExpression(allocator, stream, tree, indent, start_col, &@"else".base, space); } else { - return renderExpression(allocator, stream, tree, indent, if_node.body, space); + return renderExpression(allocator, stream, tree, indent, start_col, if_node.body, space); } } - const src_has_newline = blk: { - const loc = tree.tokenLocation(tree.tokens.at(rparen).end, if_node.body.lastToken()); - break :blk loc.line != 0; - }; + const src_has_newline = !tree.tokensOnSameLine(rparen, if_node.body.lastToken()); if (src_has_newline) { const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space; - try renderToken(tree, stream, rparen, indent, after_rparen_space); // ) + try renderToken(tree, stream, rparen, indent, start_col, after_rparen_space); // ) if (if_node.payload) |payload| { - try renderExpression(allocator, stream, tree, indent, payload, Space.Newline); + try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Newline); } const new_indent = indent + indent_delta; try stream.writeByteNTimes(' ', new_indent); if (if_node.@"else") |@"else"| { - const else_is_block = switch (@"else".body.id) { - ast.Node.Id.Block, - ast.Node.Id.If, - ast.Node.Id.For, - ast.Node.Id.While, - ast.Node.Id.Switch, - => true, - else => false, - }; - try renderExpression(allocator, stream, tree, new_indent, if_node.body, Space.Newline); + const else_is_block = nodeIsBlock(@"else".body); + try renderExpression(allocator, stream, tree, new_indent, start_col, if_node.body, Space.Newline); try stream.writeByteNTimes(' ', indent); if (else_is_block) { - try renderToken(tree, stream, @"else".else_token, indent, Space.Space); // else + try renderToken(tree, stream, @"else".else_token, indent, start_col, Space.Space); // else if (@"else".payload) |payload| { - try renderExpression(allocator, stream, tree, indent, payload, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space); } - return renderExpression(allocator, stream, tree, indent, @"else".body, space); + return renderExpression(allocator, stream, tree, indent, start_col, @"else".body, space); } else { const after_else_space = if (@"else".payload == null) Space.Newline else Space.Space; - try renderToken(tree, stream, @"else".else_token, indent, after_else_space); // else + try renderToken(tree, stream, @"else".else_token, indent, start_col, after_else_space); // else if (@"else".payload) |payload| { - try renderExpression(allocator, stream, tree, indent, payload, Space.Newline); + try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Newline); } try stream.writeByteNTimes(' ', new_indent); - return renderExpression(allocator, stream, tree, new_indent, @"else".body, space); + return renderExpression(allocator, stream, tree, new_indent, start_col, @"else".body, space); } } else { - return renderExpression(allocator, stream, tree, new_indent, if_node.body, space); + return renderExpression(allocator, stream, tree, new_indent, start_col, if_node.body, space); } } - try renderToken(tree, stream, rparen, indent, Space.Space); // ) + try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // ) if (if_node.payload) |payload| { - try renderExpression(allocator, stream, tree, indent, payload, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space); } if (if_node.@"else") |@"else"| { - try renderExpression(allocator, stream, tree, indent, if_node.body, Space.Space); - try renderToken(tree, stream, @"else".else_token, indent, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, if_node.body, Space.Space); + try renderToken(tree, stream, @"else".else_token, indent, start_col, Space.Space); if (@"else".payload) |payload| { - try renderExpression(allocator, stream, tree, indent, payload, Space.Space); + try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space); } - return renderExpression(allocator, stream, tree, indent, @"else".body, space); + return renderExpression(allocator, stream, tree, indent, start_col, @"else".body, space); } else { - return renderExpression(allocator, stream, tree, indent, if_node.body, space); + return renderExpression(allocator, stream, tree, indent, start_col, if_node.body, space); } }, ast.Node.Id.Asm => { const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); - try renderToken(tree, stream, asm_node.asm_token, indent, Space.Space); // asm + try renderToken(tree, stream, asm_node.asm_token, indent, start_col, Space.Space); // asm if (asm_node.volatile_token) |volatile_token| { - try renderToken(tree, stream, volatile_token, indent, Space.Space); // volatile - try renderToken(tree, stream, tree.nextToken(volatile_token), indent, Space.None); // ( + try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile + try renderToken(tree, stream, tree.nextToken(volatile_token), indent, start_col, Space.None); // ( } else { - try renderToken(tree, stream, tree.nextToken(asm_node.asm_token), indent, Space.None); // ( + try renderToken(tree, stream, tree.nextToken(asm_node.asm_token), indent, start_col, Space.None); // ( } if (asm_node.outputs.len == 0 and asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) { - try renderExpression(allocator, stream, tree, indent, asm_node.template, Space.None); - try renderToken(tree, stream, asm_node.rparen, indent, space); - return; + try renderExpression(allocator, stream, tree, indent, start_col, asm_node.template, Space.None); + return renderToken(tree, stream, asm_node.rparen, indent, start_col, space); } - try renderExpression(allocator, stream, tree, indent, asm_node.template, Space.Newline); + try renderExpression(allocator, stream, tree, indent, start_col, asm_node.template, Space.Newline); const indent_once = indent + indent_delta; try stream.writeByteNTimes(' ', indent_once); @@ -1449,12 +1434,12 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const indent_extra = indent_once + 2; const colon2 = if (asm_node.outputs.len == 0) blk: { - try renderToken(tree, stream, colon1, indent, Space.Newline); // : + try renderToken(tree, stream, colon1, indent, start_col, Space.Newline); // : try stream.writeByteNTimes(' ', indent_once); break :blk tree.nextToken(colon1); } else blk: { - try renderToken(tree, stream, colon1, indent, Space.Space); // : + try renderToken(tree, stream, colon1, indent, start_col, Space.Space); // : var it = asm_node.outputs.iterator(0); while (true) { @@ -1462,21 +1447,20 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const node = &(asm_output.*).base; if (it.peek()) |next_asm_output| { - try renderExpression(allocator, stream, tree, indent_extra, node, Space.None); + try renderExpression(allocator, stream, tree, indent_extra, start_col, node, Space.None); const next_node = &(next_asm_output.*).base; const comma = tree.prevToken(next_asm_output.*.firstToken()); - try renderToken(tree, stream, comma, indent_extra, Space.Newline); // , - try renderExtraNewline(tree, stream, next_node); + try renderToken(tree, stream, comma, indent_extra, start_col, Space.Newline); // , + try renderExtraNewline(tree, stream, start_col, next_node); try stream.writeByteNTimes(' ', indent_extra); } else if (asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) { - try renderExpression(allocator, stream, tree, indent_extra, node, Space.Newline); + try renderExpression(allocator, stream, tree, indent_extra, start_col, node, Space.Newline); try stream.writeByteNTimes(' ', indent); - try renderToken(tree, stream, asm_node.rparen, indent, space); - return; + return renderToken(tree, stream, asm_node.rparen, indent, start_col, space); } else { - try renderExpression(allocator, stream, tree, indent_extra, node, Space.Newline); + try renderExpression(allocator, stream, tree, indent_extra, start_col, node, Space.Newline); try stream.writeByteNTimes(' ', indent_once); const comma_or_colon = tree.nextToken(node.lastToken()); break :blk switch (tree.tokens.at(comma_or_colon).id) { @@ -1488,12 +1472,12 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind }; const colon3 = if (asm_node.inputs.len == 0) blk: { - try renderToken(tree, stream, colon2, indent, Space.Newline); // : + try renderToken(tree, stream, colon2, indent, start_col, Space.Newline); // : try stream.writeByteNTimes(' ', indent_once); break :blk tree.nextToken(colon2); } else blk: { - try renderToken(tree, stream, colon2, indent, Space.Space); // : + try renderToken(tree, stream, colon2, indent, start_col, Space.Space); // : var it = asm_node.inputs.iterator(0); while (true) { @@ -1501,21 +1485,20 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const node = &(asm_input.*).base; if (it.peek()) |next_asm_input| { - try renderExpression(allocator, stream, tree, indent_extra, node, Space.None); + try renderExpression(allocator, stream, tree, indent_extra, start_col, node, Space.None); const next_node = &(next_asm_input.*).base; const comma = tree.prevToken(next_asm_input.*.firstToken()); - try renderToken(tree, stream, comma, indent_extra, Space.Newline); // , - try renderExtraNewline(tree, stream, next_node); + try renderToken(tree, stream, comma, indent_extra, start_col, Space.Newline); // , + try renderExtraNewline(tree, stream, start_col, next_node); try stream.writeByteNTimes(' ', indent_extra); } else if (asm_node.clobbers.len == 0) { - try renderExpression(allocator, stream, tree, indent_extra, node, Space.Newline); + try renderExpression(allocator, stream, tree, indent_extra, start_col, node, Space.Newline); try stream.writeByteNTimes(' ', indent); - try renderToken(tree, stream, asm_node.rparen, indent, space); // ) - return; + return renderToken(tree, stream, asm_node.rparen, indent, start_col, space); // ) } else { - try renderExpression(allocator, stream, tree, indent_extra, node, Space.Newline); + try renderExpression(allocator, stream, tree, indent_extra, start_col, node, Space.Newline); try stream.writeByteNTimes(' ', indent_once); const comma_or_colon = tree.nextToken(node.lastToken()); break :blk switch (tree.tokens.at(comma_or_colon).id) { @@ -1526,21 +1509,20 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind } }; - try renderToken(tree, stream, colon3, indent, Space.Space); // : + try renderToken(tree, stream, colon3, indent, start_col, Space.Space); // : var it = asm_node.clobbers.iterator(0); while (true) { const clobber_token = ??it.next(); if (it.peek() == null) { - try renderToken(tree, stream, clobber_token.*, indent_once, Space.Newline); + try renderToken(tree, stream, clobber_token.*, indent_once, start_col, Space.Newline); try stream.writeByteNTimes(' ', indent); - try renderToken(tree, stream, asm_node.rparen, indent, space); - return; + return renderToken(tree, stream, asm_node.rparen, indent, start_col, space); } else { - try renderToken(tree, stream, clobber_token.*, indent_once, Space.None); + try renderToken(tree, stream, clobber_token.*, indent_once, start_col, Space.None); const comma = tree.nextToken(clobber_token.*); - try renderToken(tree, stream, comma, indent_once, Space.Space); // , + try renderToken(tree, stream, comma, indent_once, start_col, Space.Space); // , } } }, @@ -1549,34 +1531,34 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); try stream.write("["); - try renderExpression(allocator, stream, tree, indent, asm_input.symbolic_name, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, asm_input.symbolic_name, Space.None); try stream.write("] "); - try renderExpression(allocator, stream, tree, indent, asm_input.constraint, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, asm_input.constraint, Space.None); try stream.write(" ("); - try renderExpression(allocator, stream, tree, indent, asm_input.expr, Space.None); - try renderToken(tree, stream, asm_input.lastToken(), indent, space); // ) + try renderExpression(allocator, stream, tree, indent, start_col, asm_input.expr, Space.None); + return renderToken(tree, stream, asm_input.lastToken(), indent, start_col, space); // ) }, ast.Node.Id.AsmOutput => { const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); try stream.write("["); - try renderExpression(allocator, stream, tree, indent, asm_output.symbolic_name, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, asm_output.symbolic_name, Space.None); try stream.write("] "); - try renderExpression(allocator, stream, tree, indent, asm_output.constraint, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, asm_output.constraint, Space.None); try stream.write(" ("); switch (asm_output.kind) { ast.Node.AsmOutput.Kind.Variable => |variable_name| { - try renderExpression(allocator, stream, tree, indent, &variable_name.base, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, &variable_name.base, Space.None); }, ast.Node.AsmOutput.Kind.Return => |return_type| { try stream.write("-> "); - try renderExpression(allocator, stream, tree, indent, return_type, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, return_type, Space.None); }, } - try renderToken(tree, stream, asm_output.lastToken(), indent, space); // ) + return renderToken(tree, stream, asm_output.lastToken(), indent, start_col, space); // ) }, ast.Node.Id.StructField, @@ -1590,92 +1572,92 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind } } -fn renderVarDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, - var_decl: &ast.Node.VarDecl) (@typeOf(stream).Child.Error || Error)!void +fn renderVarDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, start_col: &usize, + var_decl: &ast.Node.VarDecl,) (@typeOf(stream).Child.Error || Error)!void { if (var_decl.visib_token) |visib_token| { - try renderToken(tree, stream, visib_token, indent, Space.Space); // pub + try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub } if (var_decl.extern_export_token) |extern_export_token| { - try renderToken(tree, stream, extern_export_token, indent, Space.Space); // extern + try renderToken(tree, stream, extern_export_token, indent, start_col, Space.Space); // extern if (var_decl.lib_name) |lib_name| { - try renderExpression(allocator, stream, tree, indent, lib_name, Space.Space); // "lib" + try renderExpression(allocator, stream, tree, indent, start_col, lib_name, Space.Space); // "lib" } } if (var_decl.comptime_token) |comptime_token| { - try renderToken(tree, stream, comptime_token, indent, Space.Space); // comptime + try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space); // comptime } - try renderToken(tree, stream, var_decl.mut_token, indent, Space.Space); // var + try renderToken(tree, stream, var_decl.mut_token, indent, start_col, Space.Space); // var const name_space = if (var_decl.type_node == null and (var_decl.align_node != null or var_decl.init_node != null)) Space.Space else Space.None; - try renderToken(tree, stream, var_decl.name_token, indent, name_space); + try renderToken(tree, stream, var_decl.name_token, indent, start_col, name_space); if (var_decl.type_node) |type_node| { - try renderToken(tree, stream, tree.nextToken(var_decl.name_token), indent, Space.Space); + try renderToken(tree, stream, tree.nextToken(var_decl.name_token), indent, start_col, Space.Space); const s = if (var_decl.align_node != null or var_decl.init_node != null) Space.Space else Space.None; - try renderExpression(allocator, stream, tree, indent, type_node, s); + try renderExpression(allocator, stream, tree, indent, start_col, type_node, s); } if (var_decl.align_node) |align_node| { const lparen = tree.prevToken(align_node.firstToken()); const align_kw = tree.prevToken(lparen); const rparen = tree.nextToken(align_node.lastToken()); - try renderToken(tree, stream, align_kw, indent, Space.None); // align - try renderToken(tree, stream, lparen, indent, Space.None); // ( - try renderExpression(allocator, stream, tree, indent, align_node, Space.None); + try renderToken(tree, stream, align_kw, indent, start_col, Space.None); // align + try renderToken(tree, stream, lparen, indent, start_col, Space.None); // ( + try renderExpression(allocator, stream, tree, indent, start_col, align_node, Space.None); const s = if (var_decl.init_node != null) Space.Space else Space.None; - try renderToken(tree, stream, rparen, indent, s); // ) + try renderToken(tree, stream, rparen, indent, start_col, s); // ) } if (var_decl.init_node) |init_node| { const s = if (init_node.id == ast.Node.Id.MultilineStringLiteral) Space.None else Space.Space; - try renderToken(tree, stream, var_decl.eq_token, indent, s); // = - try renderExpression(allocator, stream, tree, indent, init_node, Space.None); + try renderToken(tree, stream, var_decl.eq_token, indent, start_col, s); // = + try renderExpression(allocator, stream, tree, indent, start_col, init_node, Space.None); } - try renderToken(tree, stream, var_decl.semicolon_token, indent, Space.Newline); + try renderToken(tree, stream, var_decl.semicolon_token, indent, start_col, Space.Newline); } -fn renderParamDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node, space: Space) (@typeOf(stream).Child.Error || Error)!void { +fn renderParamDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, start_col: &usize, base: &ast.Node, space: Space,) (@typeOf(stream).Child.Error || Error)!void { const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); if (param_decl.comptime_token) |comptime_token| { - try renderToken(tree, stream, comptime_token, indent, Space.Space); + try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space); } if (param_decl.noalias_token) |noalias_token| { - try renderToken(tree, stream, noalias_token, indent, Space.Space); + try renderToken(tree, stream, noalias_token, indent, start_col, Space.Space); } if (param_decl.name_token) |name_token| { - try renderToken(tree, stream, name_token, indent, Space.None); - try renderToken(tree, stream, tree.nextToken(name_token), indent, Space.Space); // : + try renderToken(tree, stream, name_token, indent, start_col, Space.None); + try renderToken(tree, stream, tree.nextToken(name_token), indent, start_col, Space.Space); // : } if (param_decl.var_args_token) |var_args_token| { - try renderToken(tree, stream, var_args_token, indent, space); + try renderToken(tree, stream, var_args_token, indent, start_col, space); } else { - try renderExpression(allocator, stream, tree, indent, param_decl.type_node, space); + try renderExpression(allocator, stream, tree, indent, start_col, param_decl.type_node, space); } } -fn renderStatement(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { +fn renderStatement(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, start_col: &usize, base: &ast.Node,) (@typeOf(stream).Child.Error || Error)!void { switch (base.id) { ast.Node.Id.VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); - try renderVarDecl(allocator, stream, tree, indent, var_decl); + try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl); }, else => { if (base.requireSemiColon()) { - try renderExpression(allocator, stream, tree, indent, base, Space.None); + try renderExpression(allocator, stream, tree, indent, start_col, base, Space.None); const semicolon_index = tree.nextToken(base.lastToken()); assert(tree.tokens.at(semicolon_index).id == Token.Id.Semicolon); - try renderToken(tree, stream, semicolon_index, indent, Space.Newline); + try renderToken(tree, stream, semicolon_index, indent, start_col, Space.Newline); } else { - try renderExpression(allocator, stream, tree, indent, base, Space.Newline); + try renderExpression(allocator, stream, tree, indent, start_col, base, Space.Newline); } }, } @@ -1689,32 +1671,44 @@ const Space = enum { SpaceOrOutdent, NoNewline, NoComment, + BlockStart, }; -fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, space: Space) (@typeOf(stream).Child.Error || Error)!void { +fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, start_col: &usize, space: Space) (@typeOf(stream).Child.Error || Error)!void { + if (space == Space.BlockStart) { + if (start_col.* < indent + indent_delta) + return renderToken(tree, stream, token_index, indent, start_col, Space.Space); + try renderToken(tree, stream, token_index, indent, start_col, Space.Newline); + try stream.writeByteNTimes(' ', indent); + start_col.* = indent; + return; + } + var token = tree.tokens.at(token_index); try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(token), " ")); + if (space == Space.NoComment) + return; + var next_token = tree.tokens.at(token_index + 1); - switch (space) { - Space.NoComment => return, - Space.Comma => switch (next_token.id) { - Token.Id.Comma => return renderToken(tree, stream, token_index + 1, indent, Space.Newline), - Token.Id.LineComment => { - try stream.write(", "); - return renderToken(tree, stream, token_index + 1, indent, Space.Newline); - }, - else => { - if (tree.tokens.at(token_index + 2).id == Token.Id.MultilineStringLiteralLine) { - return stream.write(","); - } else { - return stream.write(",\n"); - } - }, + if (space == Space.Comma) switch (next_token.id) { + Token.Id.Comma => return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline), + Token.Id.LineComment => { + try stream.write(", "); + return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline); }, - else => {}, - } + else => { + if (tree.tokens.at(token_index + 2).id == Token.Id.MultilineStringLiteralLine) { + try stream.write(","); + return; + } else { + try stream.write(",\n"); + start_col.* = 0; + return; + } + }, + }; // Skip over same line doc comments var offset: usize = 1; @@ -1733,18 +1727,27 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent if (next_token.id == Token.Id.MultilineStringLiteralLine) { return; } else { - return stream.write("\n"); + try stream.write("\n"); + start_col.* = 0; + return; } }, - Space.Space, Space.SpaceOrOutdent => return stream.writeByte(' '), - Space.NoComment, Space.Comma => unreachable, + Space.Space, Space.SpaceOrOutdent => { + try stream.writeByte(' '); + return; + }, + Space.NoComment, Space.Comma, Space.BlockStart => unreachable, } } const comment_is_empty = mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ").len == 2; if (comment_is_empty) { switch (space) { - Space.Newline => return stream.writeByte('\n'), + Space.Newline => { + try stream.writeByte('\n'); + start_col.* = 0; + return; + }, else => {}, } } @@ -1765,20 +1768,24 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent else => indent + indent_delta, }; try stream.writeByteNTimes(' ', next_line_indent); + start_col.* = next_line_indent; }, Space.SpaceOrOutdent => { try stream.writeByte('\n'); try stream.writeByteNTimes(' ', indent); + start_col.* = indent; }, Space.Newline => { if (next_token.id == Token.Id.MultilineStringLiteralLine) { return; } else { - return stream.write("\n"); + try stream.write("\n"); + start_col.* = 0; + return; } }, Space.NoNewline => {}, - Space.NoComment, Space.Comma => unreachable, + Space.NoComment, Space.Comma, Space.BlockStart => unreachable, } return; } @@ -1801,7 +1808,9 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent if (next_token.id == Token.Id.MultilineStringLiteralLine) { return; } else { - return stream.write("\n"); + try stream.write("\n"); + start_col.* = 0; + return; } }, Space.None, Space.Space => { @@ -1813,13 +1822,15 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent else => indent, }; try stream.writeByteNTimes(' ', next_line_indent); + start_col.* = next_line_indent; }, Space.SpaceOrOutdent => { try stream.writeByte('\n'); try stream.writeByteNTimes(' ', indent); + start_col.* = indent; }, Space.NoNewline => {}, - Space.NoComment, Space.Comma => unreachable, + Space.NoComment, Space.Comma, Space.BlockStart => unreachable, } return; } @@ -1827,18 +1838,30 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent } } -fn renderDocComments(tree: &ast.Tree, stream: var, node: var, indent: usize) (@typeOf(stream).Child.Error || Error)!void { +fn renderDocComments(tree: &ast.Tree, stream: var, node: var, indent: usize, start_col: &usize,) (@typeOf(stream).Child.Error || Error)!void { const comment = node.doc_comments ?? return; var it = comment.lines.iterator(0); const first_token = node.firstToken(); while (it.next()) |line_token_index| { if (line_token_index.* < first_token) { - try renderToken(tree, stream, line_token_index.*, indent, Space.Newline); + try renderToken(tree, stream, line_token_index.*, indent, start_col, Space.Newline); try stream.writeByteNTimes(' ', indent); } else { - try renderToken(tree, stream, line_token_index.*, indent, Space.NoComment); + try renderToken(tree, stream, line_token_index.*, indent, start_col, Space.NoComment); try stream.write("\n"); try stream.writeByteNTimes(' ', indent); } } } + +fn nodeIsBlock(base: &const ast.Node) bool { + return switch (base.id) { + ast.Node.Id.Block, + ast.Node.Id.If, + ast.Node.Id.For, + ast.Node.Id.While, + ast.Node.Id.Switch, + => true, + else => false, + }; +} -- cgit v1.2.3 From cd325e408e2d23734ba84e412b6f1706cf442434 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 May 2018 03:25:03 -0400 Subject: zig fmt: fix extra space after comma before multi line string --- std/zig/parser_test.zig | 11 +++++++++++ std/zig/render.zig | 2 ++ 2 files changed, 13 insertions(+) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 8ed748ed30..c72888a984 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,14 @@ +test "zig fmt: 2nd arg multiline string" { + try testCanonical( + \\comptime { + \\ cases.addAsm("hello world linux x86_64", + \\ \\.text + \\ , "Hello, world!\n"); + \\} + \\ + ); +} + test "zig fmt: if condition wraps" { try testTransform( \\comptime { diff --git a/std/zig/render.zig b/std/zig/render.zig index 16aed808fc..c8efce63c5 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -1733,6 +1733,8 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent } }, Space.Space, Space.SpaceOrOutdent => { + if (next_token.id == Token.Id.MultilineStringLiteralLine) + return; try stream.writeByte(' '); return; }, -- cgit v1.2.3 From cdf30c31ea36365859dd81c207aede3c45c4e022 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 May 2018 03:47:27 -0400 Subject: zig fmt: fix implementation of firstToken() for fn call --- std/zig/ast.zig | 4 ++++ std/zig/parser_test.zig | 11 +++++++++++ std/zig/render.zig | 10 +++++----- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 3b705e2ee6..e86c40e310 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1673,6 +1673,10 @@ pub const Node = struct { } pub fn firstToken(self: &SuffixOp) TokenIndex { + switch (self.op) { + @TagType(Op).Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(), + else => {}, + } return self.lhs.firstToken(); } diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index c72888a984..1f60fae270 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,14 @@ +test "zig fmt: async call in if condition" { + try testCanonical( + \\comptime { + \\ if (async b()) { + \\ a(); + \\ } + \\} + \\ + ); +} + test "zig fmt: 2nd arg multiline string" { try testCanonical( \\comptime { diff --git a/std/zig/render.zig b/std/zig/render.zig index c8efce63c5..dce659f1ef 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -213,13 +213,13 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); if (async_attr.allocator_type) |allocator_type| { - try renderToken(tree, stream, async_attr.async_token, indent, start_col, Space.None); + try renderToken(tree, stream, async_attr.async_token, indent, start_col, Space.None); // async - try renderToken(tree, stream, tree.nextToken(async_attr.async_token), indent, start_col, Space.None); - try renderExpression(allocator, stream, tree, indent, start_col, allocator_type, Space.None); - return renderToken(tree, stream, tree.nextToken(allocator_type.lastToken()), indent, start_col, space); + try renderToken(tree, stream, tree.nextToken(async_attr.async_token), indent, start_col, Space.None); // < + try renderExpression(allocator, stream, tree, indent, start_col, allocator_type, Space.None); // allocator + return renderToken(tree, stream, tree.nextToken(allocator_type.lastToken()), indent, start_col, space); // > } else { - return renderToken(tree, stream, async_attr.async_token, indent, start_col, space); + return renderToken(tree, stream, async_attr.async_token, indent, start_col, space); // async } }, -- cgit v1.2.3 From 0c16cd2d0ed78be2d160f9c865cd0a8703348232 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 May 2018 20:23:55 -0400 Subject: run zig fmt on the codebase See #1003 --- std/base64.zig | 1 + std/crypto/blake2.zig | 248 +-- std/fmt/errol/index.zig | 3 +- std/fmt/errol/lookup.zig | 1200 ++++++------- std/fmt/index.zig | 70 +- std/hash/adler.zig | 17 +- std/hash/crc.zig | 5 +- std/hash/fnv.zig | 6 +- std/hash/siphash.zig | 6 +- std/heap.zig | 14 +- std/io_test.zig | 2 +- std/json.zig | 106 +- std/macho.zig | 20 +- std/math/atan.zig | 32 +- std/math/atan2.zig | 6 +- std/math/complex/index.zig | 22 +- std/math/complex/tanh.zig | 4 +- std/math/exp.zig | 30 +- std/math/exp2.zig | 280 +-- std/math/expm1.zig | 24 +- std/math/index.zig | 2 +- std/math/log1p.zig | 3 +- std/math/pow.zig | 1 - std/net.zig | 32 +- std/os/child_process.zig | 11 +- std/os/darwin.zig | 66 + std/os/darwin_errno.zig | 402 +++-- std/os/epoch.zig | 46 +- std/os/file.zig | 59 +- std/os/get_user_id.zig | 6 +- std/os/index.zig | 168 +- std/os/linux/errno.zig | 569 ++++-- std/os/linux/index.zig | 3 +- std/os/linux/test.zig | 12 +- std/os/linux/vdso.zig | 20 +- std/os/linux/x86_64.zig | 114 +- std/os/path.zig | 93 +- std/os/time.zig | 69 +- std/os/windows/error.zig | 1188 +++++++++++++ std/os/windows/index.zig | 178 +- std/os/windows/util.zig | 37 +- std/os/zen.zig | 138 +- std/rand/index.zig | 92 +- std/rand/ziggurat.zig | 30 +- std/segmented_list.zig | 6 +- std/sort.zig | 1 - std/special/bootstrap.zig | 8 +- std/special/bootstrap_lib.zig | 8 +- std/special/build_runner.zig | 6 +- std/special/builtin.zig | 60 +- std/special/compiler_rt/comparetf2.zig | 57 +- std/special/compiler_rt/fixunsdfti_test.zig | 1 - std/special/compiler_rt/index.zig | 14 +- std/unicode.zig | 22 +- std/zig/ast.zig | 9 +- std/zig/parse.zig | 927 +++++----- std/zig/parser_test.zig | 22 +- std/zig/render.zig | 60 +- std/zig/tokenizer.zig | 202 +-- test/cases/align.zig | 12 +- test/cases/array.zig | 12 +- test/cases/bugs/394.zig | 6 +- test/cases/bugs/656.zig | 6 +- test/cases/bugs/828.zig | 8 +- test/cases/cast.zig | 31 +- test/cases/const_slice_child.zig | 2 +- test/cases/coroutines.zig | 23 +- test/cases/enum.zig | 22 +- test/cases/enum_with_members.zig | 8 +- test/cases/error.zig | 22 +- test/cases/eval.zig | 52 +- test/cases/field_parent_ptr.zig | 2 +- test/cases/fn.zig | 2 +- test/cases/fn_in_struct_in_comptime.zig | 2 +- test/cases/for.zig | 6 +- test/cases/generics.zig | 10 +- test/cases/incomplete_struct_param_tld.zig | 8 +- test/cases/math.zig | 2 +- test/cases/misc.zig | 42 +- test/cases/null.zig | 6 +- test/cases/reflection.zig | 2 +- test/cases/slice.zig | 2 +- test/cases/struct.zig | 40 +- test/cases/struct_contains_slice_of_itself.zig | 16 +- test/cases/switch.zig | 54 +- test/cases/switch_prong_err_enum.zig | 4 +- test/cases/switch_prong_implicit_cast.zig | 8 +- test/cases/this.zig | 2 +- test/cases/try.zig | 3 +- test/cases/type_info.zig | 4 +- test/cases/union.zig | 5 +- test/cases/var_args.zig | 2 +- test/cases/void.zig | 2 +- test/cases/while.zig | 4 +- test/compare_output.zig | 4 +- test/compile_errors.zig | 2220 ++++++++++++++++-------- test/gen_h.zig | 1 - test/standalone/brace_expansion/main.zig | 13 +- test/standalone/issue_339/test.zig | 5 +- test/standalone/pkg_import/pkg.zig | 4 +- test/standalone/use_alias/main.zig | 2 +- test/translate_c.zig | 49 +- 102 files changed, 5969 insertions(+), 3599 deletions(-) diff --git a/std/base64.zig b/std/base64.zig index 515738a99e..b714250992 100644 --- a/std/base64.zig +++ b/std/base64.zig @@ -81,6 +81,7 @@ pub const Base64Decoder = struct { /// e.g. 'A' => 0. /// undefined for any value not in the 64 alphabet chars. char_to_index: [256]u8, + /// true only for the 64 chars in the alphabet, not the pad char. char_in_alphabet: [256]bool, pad_char: u8, diff --git a/std/crypto/blake2.zig b/std/crypto/blake2.zig index 18025d08eb..b48e2fb4ba 100644 --- a/std/crypto/blake2.zig +++ b/std/crypto/blake2.zig @@ -49,16 +49,16 @@ fn Blake2s(comptime out_len: usize) type { }; const sigma = [10][16]u8{ - []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, - []const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, - []const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, - []const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, - []const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, - []const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, - []const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, - []const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, - []const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + []const u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + []const u8{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + []const u8{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + []const u8{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + []const u8{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + []const u8{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + []const u8{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + []const u8{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + []const u8{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + []const u8{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, }; h: [8]u32, @@ -282,222 +282,18 @@ fn Blake2b(comptime out_len: usize) type { }; const sigma = [12][16]u8{ - []const u8{ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - }, - []const u8{ - 14, - 10, - 4, - 8, - 9, - 15, - 13, - 6, - 1, - 12, - 0, - 2, - 11, - 7, - 5, - 3, - }, - []const u8{ - 11, - 8, - 12, - 0, - 5, - 2, - 15, - 13, - 10, - 14, - 3, - 6, - 7, - 1, - 9, - 4, - }, - []const u8{ - 7, - 9, - 3, - 1, - 13, - 12, - 11, - 14, - 2, - 6, - 5, - 10, - 4, - 0, - 15, - 8, - }, - []const u8{ - 9, - 0, - 5, - 7, - 2, - 4, - 10, - 15, - 14, - 1, - 11, - 12, - 6, - 8, - 3, - 13, - }, - []const u8{ - 2, - 12, - 6, - 10, - 0, - 11, - 8, - 3, - 4, - 13, - 7, - 5, - 15, - 14, - 1, - 9, - }, - []const u8{ - 12, - 5, - 1, - 15, - 14, - 13, - 4, - 10, - 0, - 7, - 6, - 3, - 9, - 2, - 8, - 11, - }, - []const u8{ - 13, - 11, - 7, - 14, - 12, - 1, - 3, - 9, - 5, - 0, - 15, - 4, - 8, - 6, - 2, - 10, - }, - []const u8{ - 6, - 15, - 14, - 9, - 11, - 3, - 0, - 8, - 12, - 2, - 13, - 7, - 1, - 4, - 10, - 5, - }, - []const u8{ - 10, - 2, - 8, - 4, - 7, - 6, - 1, - 5, - 15, - 11, - 9, - 14, - 3, - 12, - 13, - 0, - }, - []const u8{ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - }, - []const u8{ - 14, - 10, - 4, - 8, - 9, - 15, - 13, - 6, - 1, - 12, - 0, - 2, - 11, - 7, - 5, - 3, - }, + []const u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + []const u8{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + []const u8{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + []const u8{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + []const u8{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + []const u8{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + []const u8{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + []const u8{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + []const u8{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + []const u8{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + []const u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + []const u8{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, }; h: [8]u64, diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index f4ec251b77..9c8fe343b5 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -98,7 +98,6 @@ pub fn errol3(value: f64, buffer: []u8) FloatDecimal { /// Uncorrected Errol3 double to ASCII conversion. fn errol3u(val: f64, buffer: []u8) FloatDecimal { // check if in integer or fixed range - if (val > 9.007199254740992e15 and val < 3.40282366920938e+38) { return errolInt(val, buffer); } else if (val >= 16.0 and val < 9.007199254740992e15) { @@ -420,7 +419,7 @@ fn fpprev(val: f64) f64 { return @bitCast(f64, @bitCast(u64, val) -% 1); } -pub const c_digits_lut = []u8 { +pub const c_digits_lut = []u8{ '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', diff --git a/std/fmt/errol/lookup.zig b/std/fmt/errol/lookup.zig index b7b89ba732..bd0a4ac8d1 100644 --- a/std/fmt/errol/lookup.zig +++ b/std/fmt/errol/lookup.zig @@ -3,604 +3,604 @@ pub const HP = struct { off: f64, }; pub const lookup_table = []HP{ - HP{.val=1.000000e+308, .off= -1.097906362944045488e+291 }, - HP{.val=1.000000e+307, .off= 1.396894023974354241e+290 }, - HP{.val=1.000000e+306, .off= -1.721606459673645508e+289 }, - HP{.val=1.000000e+305, .off= 6.074644749446353973e+288 }, - HP{.val=1.000000e+304, .off= 6.074644749446353567e+287 }, - HP{.val=1.000000e+303, .off= -1.617650767864564452e+284 }, - HP{.val=1.000000e+302, .off= -7.629703079084895055e+285 }, - HP{.val=1.000000e+301, .off= -5.250476025520442286e+284 }, - HP{.val=1.000000e+300, .off= -5.250476025520441956e+283 }, - HP{.val=1.000000e+299, .off= -5.250476025520441750e+282 }, - HP{.val=1.000000e+298, .off= 4.043379652465702264e+281 }, - HP{.val=1.000000e+297, .off= -1.765280146275637946e+280 }, - HP{.val=1.000000e+296, .off= 1.865132227937699609e+279 }, - HP{.val=1.000000e+295, .off= 1.865132227937699609e+278 }, - HP{.val=1.000000e+294, .off= -6.643646774124810287e+277 }, - HP{.val=1.000000e+293, .off= 7.537651562646039934e+276 }, - HP{.val=1.000000e+292, .off= -1.325659897835741608e+275 }, - HP{.val=1.000000e+291, .off= 4.213909764965371606e+274 }, - HP{.val=1.000000e+290, .off= -6.172783352786715670e+273 }, - HP{.val=1.000000e+289, .off= -6.172783352786715670e+272 }, - HP{.val=1.000000e+288, .off= -7.630473539575035471e+270 }, - HP{.val=1.000000e+287, .off= -7.525217352494018700e+270 }, - HP{.val=1.000000e+286, .off= -3.298861103408696612e+269 }, - HP{.val=1.000000e+285, .off= 1.984084207947955778e+268 }, - HP{.val=1.000000e+284, .off= -7.921438250845767591e+267 }, - HP{.val=1.000000e+283, .off= 4.460464822646386735e+266 }, - HP{.val=1.000000e+282, .off= -3.278224598286209647e+265 }, - HP{.val=1.000000e+281, .off= -3.278224598286209737e+264 }, - HP{.val=1.000000e+280, .off= -3.278224598286209961e+263 }, - HP{.val=1.000000e+279, .off= -5.797329227496039232e+262 }, - HP{.val=1.000000e+278, .off= 3.649313132040821498e+261 }, - HP{.val=1.000000e+277, .off= -2.867878510995372374e+259 }, - HP{.val=1.000000e+276, .off= -5.206914080024985409e+259 }, - HP{.val=1.000000e+275, .off= 4.018322599210230404e+258 }, - HP{.val=1.000000e+274, .off= 7.862171215558236495e+257 }, - HP{.val=1.000000e+273, .off= 5.459765830340732821e+256 }, - HP{.val=1.000000e+272, .off= -6.552261095746788047e+255 }, - HP{.val=1.000000e+271, .off= 4.709014147460262298e+254 }, - HP{.val=1.000000e+270, .off= -4.675381888545612729e+253 }, - HP{.val=1.000000e+269, .off= -4.675381888545612892e+252 }, - HP{.val=1.000000e+268, .off= 2.656177514583977380e+251 }, - HP{.val=1.000000e+267, .off= 2.656177514583977190e+250 }, - HP{.val=1.000000e+266, .off= -3.071603269111014892e+249 }, - HP{.val=1.000000e+265, .off= -6.651466258920385440e+248 }, - HP{.val=1.000000e+264, .off= -4.414051890289528972e+247 }, - HP{.val=1.000000e+263, .off= -1.617283929500958387e+246 }, - HP{.val=1.000000e+262, .off= -1.617283929500958241e+245 }, - HP{.val=1.000000e+261, .off= 7.122615947963323868e+244 }, - HP{.val=1.000000e+260, .off= -6.533477610574617382e+243 }, - HP{.val=1.000000e+259, .off= 7.122615947963323982e+242 }, - HP{.val=1.000000e+258, .off= -5.679971763165996225e+241 }, - HP{.val=1.000000e+257, .off= -3.012765990014054219e+240 }, - HP{.val=1.000000e+256, .off= -3.012765990014054219e+239 }, - HP{.val=1.000000e+255, .off= 1.154743030535854616e+238 }, - HP{.val=1.000000e+254, .off= 6.364129306223240767e+237 }, - HP{.val=1.000000e+253, .off= 6.364129306223241129e+236 }, - HP{.val=1.000000e+252, .off= -9.915202805299840595e+235 }, - HP{.val=1.000000e+251, .off= -4.827911520448877980e+234 }, - HP{.val=1.000000e+250, .off= 7.890316691678530146e+233 }, - HP{.val=1.000000e+249, .off= 7.890316691678529484e+232 }, - HP{.val=1.000000e+248, .off= -4.529828046727141859e+231 }, - HP{.val=1.000000e+247, .off= 4.785280507077111924e+230 }, - HP{.val=1.000000e+246, .off= -6.858605185178205305e+229 }, - HP{.val=1.000000e+245, .off= -4.432795665958347728e+228 }, - HP{.val=1.000000e+244, .off= -7.465057564983169531e+227 }, - HP{.val=1.000000e+243, .off= -7.465057564983169741e+226 }, - HP{.val=1.000000e+242, .off= -5.096102956370027445e+225 }, - HP{.val=1.000000e+241, .off= -5.096102956370026952e+224 }, - HP{.val=1.000000e+240, .off= -1.394611380411992474e+223 }, - HP{.val=1.000000e+239, .off= 9.188208545617793960e+221 }, - HP{.val=1.000000e+238, .off= -4.864759732872650359e+221 }, - HP{.val=1.000000e+237, .off= 5.979453868566904629e+220 }, - HP{.val=1.000000e+236, .off= -5.316601966265964857e+219 }, - HP{.val=1.000000e+235, .off= -5.316601966265964701e+218 }, - HP{.val=1.000000e+234, .off= -1.786584517880693123e+217 }, - HP{.val=1.000000e+233, .off= 2.625937292600896716e+216 }, - HP{.val=1.000000e+232, .off= -5.647541102052084079e+215 }, - HP{.val=1.000000e+231, .off= -5.647541102052083888e+214 }, - HP{.val=1.000000e+230, .off= -9.956644432600511943e+213 }, - HP{.val=1.000000e+229, .off= 8.161138937705571862e+211 }, - HP{.val=1.000000e+228, .off= 7.549087847752475275e+211 }, - HP{.val=1.000000e+227, .off= -9.283347037202319948e+210 }, - HP{.val=1.000000e+226, .off= 3.866992716668613820e+209 }, - HP{.val=1.000000e+225, .off= 7.154577655136347262e+208 }, - HP{.val=1.000000e+224, .off= 3.045096482051680688e+207 }, - HP{.val=1.000000e+223, .off= -4.660180717482069567e+206 }, - HP{.val=1.000000e+222, .off= -4.660180717482070101e+205 }, - HP{.val=1.000000e+221, .off= -4.660180717482069544e+204 }, - HP{.val=1.000000e+220, .off= 3.562757926310489022e+202 }, - HP{.val=1.000000e+219, .off= 3.491561111451748149e+202 }, - HP{.val=1.000000e+218, .off= -8.265758834125874135e+201 }, - HP{.val=1.000000e+217, .off= 3.981449442517482365e+200 }, - HP{.val=1.000000e+216, .off= -2.142154695804195936e+199 }, - HP{.val=1.000000e+215, .off= 9.339603063548950188e+198 }, - HP{.val=1.000000e+214, .off= 4.555537330485139746e+197 }, - HP{.val=1.000000e+213, .off= 1.565496247320257804e+196 }, - HP{.val=1.000000e+212, .off= 9.040598955232462036e+195 }, - HP{.val=1.000000e+211, .off= 4.368659762787334780e+194 }, - HP{.val=1.000000e+210, .off= 7.288621758065539072e+193 }, - HP{.val=1.000000e+209, .off= -7.311188218325485628e+192 }, - HP{.val=1.000000e+208, .off= 1.813693016918905189e+191 }, - HP{.val=1.000000e+207, .off= -3.889357755108838992e+190 }, - HP{.val=1.000000e+206, .off= -3.889357755108838992e+189 }, - HP{.val=1.000000e+205, .off= -1.661603547285501360e+188 }, - HP{.val=1.000000e+204, .off= 1.123089212493670643e+187 }, - HP{.val=1.000000e+203, .off= 1.123089212493670643e+186 }, - HP{.val=1.000000e+202, .off= 9.825254086803583029e+185 }, - HP{.val=1.000000e+201, .off= -3.771878529305654999e+184 }, - HP{.val=1.000000e+200, .off= 3.026687778748963675e+183 }, - HP{.val=1.000000e+199, .off= -9.720624048853446693e+182 }, - HP{.val=1.000000e+198, .off= -1.753554156601940139e+181 }, - HP{.val=1.000000e+197, .off= 4.885670753607648963e+180 }, - HP{.val=1.000000e+196, .off= 4.885670753607648963e+179 }, - HP{.val=1.000000e+195, .off= 2.292223523057028076e+178 }, - HP{.val=1.000000e+194, .off= 5.534032561245303825e+177 }, - HP{.val=1.000000e+193, .off= -6.622751331960730683e+176 }, - HP{.val=1.000000e+192, .off= -4.090088020876139692e+175 }, - HP{.val=1.000000e+191, .off= -7.255917159731877552e+174 }, - HP{.val=1.000000e+190, .off= -7.255917159731877992e+173 }, - HP{.val=1.000000e+189, .off= -2.309309130269787104e+172 }, - HP{.val=1.000000e+188, .off= -2.309309130269787019e+171 }, - HP{.val=1.000000e+187, .off= 9.284303438781988230e+170 }, - HP{.val=1.000000e+186, .off= 2.038295583124628364e+169 }, - HP{.val=1.000000e+185, .off= 2.038295583124628532e+168 }, - HP{.val=1.000000e+184, .off= -1.735666841696912925e+167 }, - HP{.val=1.000000e+183, .off= 5.340512704843477241e+166 }, - HP{.val=1.000000e+182, .off= -6.453119872723839321e+165 }, - HP{.val=1.000000e+181, .off= 8.288920849235306587e+164 }, - HP{.val=1.000000e+180, .off= -9.248546019891598293e+162 }, - HP{.val=1.000000e+179, .off= 1.954450226518486016e+162 }, - HP{.val=1.000000e+178, .off= -5.243811844750628197e+161 }, - HP{.val=1.000000e+177, .off= -7.448980502074320639e+159 }, - HP{.val=1.000000e+176, .off= -7.448980502074319858e+158 }, - HP{.val=1.000000e+175, .off= 6.284654753766312753e+158 }, - HP{.val=1.000000e+174, .off= -6.895756753684458388e+157 }, - HP{.val=1.000000e+173, .off= -1.403918625579970616e+156 }, - HP{.val=1.000000e+172, .off= -8.268716285710580522e+155 }, - HP{.val=1.000000e+171, .off= 4.602779327034313170e+154 }, - HP{.val=1.000000e+170, .off= -3.441905430931244940e+153 }, - HP{.val=1.000000e+169, .off= 6.613950516525702884e+152 }, - HP{.val=1.000000e+168, .off= 6.613950516525702652e+151 }, - HP{.val=1.000000e+167, .off= -3.860899428741951187e+150 }, - HP{.val=1.000000e+166, .off= 5.959272394946474605e+149 }, - HP{.val=1.000000e+165, .off= 1.005101065481665103e+149 }, - HP{.val=1.000000e+164, .off= -1.783349948587918355e+146 }, - HP{.val=1.000000e+163, .off= 6.215006036188360099e+146 }, - HP{.val=1.000000e+162, .off= 6.215006036188360099e+145 }, - HP{.val=1.000000e+161, .off= -3.774589324822814903e+144 }, - HP{.val=1.000000e+160, .off= -6.528407745068226929e+142 }, - HP{.val=1.000000e+159, .off= 7.151530601283157561e+142 }, - HP{.val=1.000000e+158, .off= 4.712664546348788765e+141 }, - HP{.val=1.000000e+157, .off= 1.664081977680827856e+140 }, - HP{.val=1.000000e+156, .off= 1.664081977680827750e+139 }, - HP{.val=1.000000e+155, .off= -7.176231540910168265e+137 }, - HP{.val=1.000000e+154, .off= -3.694754568805822650e+137 }, - HP{.val=1.000000e+153, .off= 2.665969958768462622e+134 }, - HP{.val=1.000000e+152, .off= -4.625108135904199522e+135 }, - HP{.val=1.000000e+151, .off= -1.717753238721771919e+134 }, - HP{.val=1.000000e+150, .off= 1.916440382756262433e+133 }, - HP{.val=1.000000e+149, .off= -4.897672657515052040e+132 }, - HP{.val=1.000000e+148, .off= -4.897672657515052198e+131 }, - HP{.val=1.000000e+147, .off= 2.200361759434233991e+130 }, - HP{.val=1.000000e+146, .off= 6.636633270027537273e+129 }, - HP{.val=1.000000e+145, .off= 1.091293881785907977e+128 }, - HP{.val=1.000000e+144, .off= -2.374543235865110597e+127 }, - HP{.val=1.000000e+143, .off= -2.374543235865110537e+126 }, - HP{.val=1.000000e+142, .off= -5.082228484029969099e+125 }, - HP{.val=1.000000e+141, .off= -1.697621923823895943e+124 }, - HP{.val=1.000000e+140, .off= -5.928380124081487212e+123 }, - HP{.val=1.000000e+139, .off= -3.284156248920492522e+122 }, - HP{.val=1.000000e+138, .off= -3.284156248920492706e+121 }, - HP{.val=1.000000e+137, .off= -3.284156248920492476e+120 }, - HP{.val=1.000000e+136, .off= -5.866406127007401066e+119 }, - HP{.val=1.000000e+135, .off= 3.817030915818506056e+118 }, - HP{.val=1.000000e+134, .off= 7.851796350329300951e+117 }, - HP{.val=1.000000e+133, .off= -2.235117235947686077e+116 }, - HP{.val=1.000000e+132, .off= 9.170432597638723691e+114 }, - HP{.val=1.000000e+131, .off= 8.797444499042767883e+114 }, - HP{.val=1.000000e+130, .off= -5.978307824605161274e+113 }, - HP{.val=1.000000e+129, .off= 1.782556435814758516e+111 }, - HP{.val=1.000000e+128, .off= -7.517448691651820362e+111 }, - HP{.val=1.000000e+127, .off= 4.507089332150205498e+110 }, - HP{.val=1.000000e+126, .off= 7.513223838100711695e+109 }, - HP{.val=1.000000e+125, .off= 7.513223838100712113e+108 }, - HP{.val=1.000000e+124, .off= 5.164681255326878494e+107 }, - HP{.val=1.000000e+123, .off= 2.229003026859587122e+106 }, - HP{.val=1.000000e+122, .off= -1.440594758724527399e+105 }, - HP{.val=1.000000e+121, .off= -3.734093374714598783e+104 }, - HP{.val=1.000000e+120, .off= 1.999653165260579757e+103 }, - HP{.val=1.000000e+119, .off= 5.583244752745066693e+102 }, - HP{.val=1.000000e+118, .off= 3.343500010567262234e+101 }, - HP{.val=1.000000e+117, .off= -5.055542772599503556e+100 }, - HP{.val=1.000000e+116, .off= -1.555941612946684331e+99 }, - HP{.val=1.000000e+115, .off= -1.555941612946684331e+98 }, - HP{.val=1.000000e+114, .off= -1.555941612946684293e+97 }, - HP{.val=1.000000e+113, .off= -1.555941612946684246e+96 }, - HP{.val=1.000000e+112, .off= 6.988006530736955847e+95 }, - HP{.val=1.000000e+111, .off= 4.318022735835818244e+94 }, - HP{.val=1.000000e+110, .off= -2.356936751417025578e+93 }, - HP{.val=1.000000e+109, .off= 1.814912928116001926e+92 }, - HP{.val=1.000000e+108, .off= -3.399899171300282744e+91 }, - HP{.val=1.000000e+107, .off= 3.118615952970072913e+90 }, - HP{.val=1.000000e+106, .off= -9.103599905036843605e+89 }, - HP{.val=1.000000e+105, .off= 6.174169917471802325e+88 }, - HP{.val=1.000000e+104, .off= -1.915675085734668657e+86 }, - HP{.val=1.000000e+103, .off= -1.915675085734668864e+85 }, - HP{.val=1.000000e+102, .off= 2.295048673475466221e+85 }, - HP{.val=1.000000e+101, .off= 2.295048673475466135e+84 }, - HP{.val=1.000000e+100, .off= -1.590289110975991792e+83 }, - HP{.val=1.000000e+99, .off= 3.266383119588331155e+82 }, - HP{.val=1.000000e+98, .off= 2.309629754856292029e+80 }, - HP{.val=1.000000e+97, .off= -7.357587384771124533e+80 }, - HP{.val=1.000000e+96, .off= -4.986165397190889509e+79 }, - HP{.val=1.000000e+95, .off= -2.021887912715594741e+78 }, - HP{.val=1.000000e+94, .off= -2.021887912715594638e+77 }, - HP{.val=1.000000e+93, .off= -4.337729697461918675e+76 }, - HP{.val=1.000000e+92, .off= -4.337729697461918997e+75 }, - HP{.val=1.000000e+91, .off= -7.956232486128049702e+74 }, - HP{.val=1.000000e+90, .off= 3.351588728453609882e+73 }, - HP{.val=1.000000e+89, .off= 5.246334248081951113e+71 }, - HP{.val=1.000000e+88, .off= 4.058327554364963672e+71 }, - HP{.val=1.000000e+87, .off= 4.058327554364963918e+70 }, - HP{.val=1.000000e+86, .off= -1.463069523067487266e+69 }, - HP{.val=1.000000e+85, .off= -1.463069523067487314e+68 }, - HP{.val=1.000000e+84, .off= -5.776660989811589441e+67 }, - HP{.val=1.000000e+83, .off= -3.080666323096525761e+66 }, - HP{.val=1.000000e+82, .off= 3.659320343691134468e+65 }, - HP{.val=1.000000e+81, .off= 7.871812010433421235e+64 }, - HP{.val=1.000000e+80, .off= -2.660986470836727449e+61 }, - HP{.val=1.000000e+79, .off= 3.264399249934044627e+62 }, - HP{.val=1.000000e+78, .off= -8.493621433689703070e+60 }, - HP{.val=1.000000e+77, .off= 1.721738727445414063e+60 }, - HP{.val=1.000000e+76, .off= -4.706013449590547218e+59 }, - HP{.val=1.000000e+75, .off= 7.346021882351880518e+58 }, - HP{.val=1.000000e+74, .off= 4.835181188197207515e+57 }, - HP{.val=1.000000e+73, .off= 1.696630320503867482e+56 }, - HP{.val=1.000000e+72, .off= 5.619818905120542959e+55 }, - HP{.val=1.000000e+71, .off= -4.188152556421145598e+54 }, - HP{.val=1.000000e+70, .off= -7.253143638152923145e+53 }, - HP{.val=1.000000e+69, .off= -7.253143638152923145e+52 }, - HP{.val=1.000000e+68, .off= 4.719477774861832896e+51 }, - HP{.val=1.000000e+67, .off= 1.726322421608144052e+50 }, - HP{.val=1.000000e+66, .off= 5.467766613175255107e+49 }, - HP{.val=1.000000e+65, .off= 7.909613737163661911e+47 }, - HP{.val=1.000000e+64, .off= -2.132041900945439564e+47 }, - HP{.val=1.000000e+63, .off= -5.785795994272697265e+46 }, - HP{.val=1.000000e+62, .off= -3.502199685943161329e+45 }, - HP{.val=1.000000e+61, .off= 5.061286470292598274e+44 }, - HP{.val=1.000000e+60, .off= 5.061286470292598472e+43 }, - HP{.val=1.000000e+59, .off= 2.831211950439536034e+42 }, - HP{.val=1.000000e+58, .off= 5.618805100255863927e+41 }, - HP{.val=1.000000e+57, .off= -4.834669211555366251e+40 }, - HP{.val=1.000000e+56, .off= -9.190283508143378583e+39 }, - HP{.val=1.000000e+55, .off= -1.023506702040855158e+38 }, - HP{.val=1.000000e+54, .off= -7.829154040459624616e+37 }, - HP{.val=1.000000e+53, .off= 6.779051325638372659e+35 }, - HP{.val=1.000000e+52, .off= 6.779051325638372290e+34 }, - HP{.val=1.000000e+51, .off= 6.779051325638371598e+33 }, - HP{.val=1.000000e+50, .off= -7.629769841091887392e+33 }, - HP{.val=1.000000e+49, .off= 5.350972305245182400e+32 }, - HP{.val=1.000000e+48, .off= -4.384584304507619764e+31 }, - HP{.val=1.000000e+47, .off= -4.384584304507619876e+30 }, - HP{.val=1.000000e+46, .off= 6.860180964052978705e+28 }, - HP{.val=1.000000e+45, .off= 7.024271097546444878e+28 }, - HP{.val=1.000000e+44, .off= -8.821361405306422641e+27 }, - HP{.val=1.000000e+43, .off= -1.393721169594140991e+26 }, - HP{.val=1.000000e+42, .off= -4.488571267807591679e+25 }, - HP{.val=1.000000e+41, .off= -6.200086450407783195e+23 }, - HP{.val=1.000000e+40, .off= -3.037860284270036669e+23 }, - HP{.val=1.000000e+39, .off= 6.029083362839682141e+22 }, - HP{.val=1.000000e+38, .off= 2.251190176543965970e+21 }, - HP{.val=1.000000e+37, .off= 4.612373417978788577e+20 }, - HP{.val=1.000000e+36, .off= -4.242063737401796198e+19 }, - HP{.val=1.000000e+35, .off= 3.136633892082024448e+18 }, - HP{.val=1.000000e+34, .off= 5.442476901295718400e+17 }, - HP{.val=1.000000e+33, .off= 5.442476901295718400e+16 }, - HP{.val=1.000000e+32, .off= -5.366162204393472000e+15 }, - HP{.val=1.000000e+31, .off= 3.641037050347520000e+14 }, - HP{.val=1.000000e+30, .off= -1.988462483865600000e+13 }, - HP{.val=1.000000e+29, .off= 8.566849142784000000e+12 }, - HP{.val=1.000000e+28, .off= 4.168802631680000000e+11 }, - HP{.val=1.000000e+27, .off= -1.328755507200000000e+10 }, - HP{.val=1.000000e+26, .off= -4.764729344000000000e+09 }, - HP{.val=1.000000e+25, .off= -9.059696640000000000e+08 }, - HP{.val=1.000000e+24, .off= 1.677721600000000000e+07 }, - HP{.val=1.000000e+23, .off= 8.388608000000000000e+06 }, - HP{.val=1.000000e+22, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+21, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+20, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+19, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+18, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+17, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+16, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+15, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+14, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+13, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+12, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+11, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+10, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+09, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+08, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+07, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+06, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+05, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+04, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+03, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+02, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+01, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e+00, .off= 0.000000000000000000e+00 }, - HP{.val=1.000000e-01, .off= -5.551115123125783010e-18 }, - HP{.val=1.000000e-02, .off= -2.081668171172168436e-19 }, - HP{.val=1.000000e-03, .off= -2.081668171172168557e-20 }, - HP{.val=1.000000e-04, .off= -4.792173602385929943e-21 }, - HP{.val=1.000000e-05, .off= -8.180305391403130547e-22 }, - HP{.val=1.000000e-06, .off= 4.525188817411374069e-23 }, - HP{.val=1.000000e-07, .off= 4.525188817411373922e-24 }, - HP{.val=1.000000e-08, .off= -2.092256083012847109e-25 }, - HP{.val=1.000000e-09, .off= -6.228159145777985254e-26 }, - HP{.val=1.000000e-10, .off= -3.643219731549774344e-27 }, - HP{.val=1.000000e-11, .off= 6.050303071806019080e-28 }, - HP{.val=1.000000e-12, .off= 2.011335237074438524e-29 }, - HP{.val=1.000000e-13, .off= -3.037374556340037101e-30 }, - HP{.val=1.000000e-14, .off= 1.180690645440101289e-32 }, - HP{.val=1.000000e-15, .off= -7.770539987666107583e-32 }, - HP{.val=1.000000e-16, .off= 2.090221327596539779e-33 }, - HP{.val=1.000000e-17, .off= -7.154242405462192144e-34 }, - HP{.val=1.000000e-18, .off= -7.154242405462192572e-35 }, - HP{.val=1.000000e-19, .off= 2.475407316473986894e-36 }, - HP{.val=1.000000e-20, .off= 5.484672854579042914e-37 }, - HP{.val=1.000000e-21, .off= 9.246254777210362522e-38 }, - HP{.val=1.000000e-22, .off= -4.859677432657087182e-39 }, - HP{.val=1.000000e-23, .off= 3.956530198510069291e-40 }, - HP{.val=1.000000e-24, .off= 7.629950044829717753e-41 }, - HP{.val=1.000000e-25, .off= -3.849486974919183692e-42 }, - HP{.val=1.000000e-26, .off= -3.849486974919184170e-43 }, - HP{.val=1.000000e-27, .off= -3.849486974919184070e-44 }, - HP{.val=1.000000e-28, .off= 2.876745653839937870e-45 }, - HP{.val=1.000000e-29, .off= 5.679342582489572168e-46 }, - HP{.val=1.000000e-30, .off= -8.333642060758598930e-47 }, - HP{.val=1.000000e-31, .off= -8.333642060758597958e-48 }, - HP{.val=1.000000e-32, .off= -5.596730997624190224e-49 }, - HP{.val=1.000000e-33, .off= -5.596730997624190604e-50 }, - HP{.val=1.000000e-34, .off= 7.232539610818348498e-51 }, - HP{.val=1.000000e-35, .off= -7.857545194582380514e-53 }, - HP{.val=1.000000e-36, .off= 5.896157255772251528e-53 }, - HP{.val=1.000000e-37, .off= -6.632427322784915796e-54 }, - HP{.val=1.000000e-38, .off= 3.808059826012723592e-55 }, - HP{.val=1.000000e-39, .off= 7.070712060011985131e-56 }, - HP{.val=1.000000e-40, .off= 7.070712060011985584e-57 }, - HP{.val=1.000000e-41, .off= -5.761291134237854167e-59 }, - HP{.val=1.000000e-42, .off= -3.762312935688689794e-59 }, - HP{.val=1.000000e-43, .off= -7.745042713519821150e-60 }, - HP{.val=1.000000e-44, .off= 4.700987842202462817e-61 }, - HP{.val=1.000000e-45, .off= 1.589480203271891964e-62 }, - HP{.val=1.000000e-46, .off= -2.299904345391321765e-63 }, - HP{.val=1.000000e-47, .off= 2.561826340437695261e-64 }, - HP{.val=1.000000e-48, .off= 2.561826340437695345e-65 }, - HP{.val=1.000000e-49, .off= 6.360053438741614633e-66 }, - HP{.val=1.000000e-50, .off= -7.616223705782342295e-68 }, - HP{.val=1.000000e-51, .off= -7.616223705782343324e-69 }, - HP{.val=1.000000e-52, .off= -7.616223705782342295e-70 }, - HP{.val=1.000000e-53, .off= -3.079876214757872338e-70 }, - HP{.val=1.000000e-54, .off= -3.079876214757872821e-71 }, - HP{.val=1.000000e-55, .off= 5.423954167728123147e-73 }, - HP{.val=1.000000e-56, .off= -3.985444122640543680e-73 }, - HP{.val=1.000000e-57, .off= 4.504255013759498850e-74 }, - HP{.val=1.000000e-58, .off= -2.570494266573869991e-75 }, - HP{.val=1.000000e-59, .off= -2.570494266573869930e-76 }, - HP{.val=1.000000e-60, .off= 2.956653608686574324e-77 }, - HP{.val=1.000000e-61, .off= -3.952281235388981376e-78 }, - HP{.val=1.000000e-62, .off= -3.952281235388981376e-79 }, - HP{.val=1.000000e-63, .off= -6.651083908855995172e-80 }, - HP{.val=1.000000e-64, .off= 3.469426116645307030e-81 }, - HP{.val=1.000000e-65, .off= 7.686305293937516319e-82 }, - HP{.val=1.000000e-66, .off= 2.415206322322254927e-83 }, - HP{.val=1.000000e-67, .off= 5.709643179581793251e-84 }, - HP{.val=1.000000e-68, .off= -6.644495035141475923e-85 }, - HP{.val=1.000000e-69, .off= 3.650620143794581913e-86 }, - HP{.val=1.000000e-70, .off= 4.333966503770636492e-88 }, - HP{.val=1.000000e-71, .off= 8.476455383920859113e-88 }, - HP{.val=1.000000e-72, .off= 3.449543675455986564e-89 }, - HP{.val=1.000000e-73, .off= 3.077238576654418974e-91 }, - HP{.val=1.000000e-74, .off= 4.234998629903623140e-91 }, - HP{.val=1.000000e-75, .off= 4.234998629903623412e-92 }, - HP{.val=1.000000e-76, .off= 7.303182045714702338e-93 }, - HP{.val=1.000000e-77, .off= 7.303182045714701699e-94 }, - HP{.val=1.000000e-78, .off= 1.121271649074855759e-96 }, - HP{.val=1.000000e-79, .off= 1.121271649074855863e-97 }, - HP{.val=1.000000e-80, .off= 3.857468248661243988e-97 }, - HP{.val=1.000000e-81, .off= 3.857468248661244248e-98 }, - HP{.val=1.000000e-82, .off= 3.857468248661244410e-99 }, - HP{.val=1.000000e-83, .off= -3.457651055545315679e-100 }, - HP{.val=1.000000e-84, .off= -3.457651055545315933e-101 }, - HP{.val=1.000000e-85, .off= 2.257285900866059216e-102 }, - HP{.val=1.000000e-86, .off= -8.458220892405268345e-103 }, - HP{.val=1.000000e-87, .off= -1.761029146610688867e-104 }, - HP{.val=1.000000e-88, .off= 6.610460535632536565e-105 }, - HP{.val=1.000000e-89, .off= -3.853901567171494935e-106 }, - HP{.val=1.000000e-90, .off= 5.062493089968513723e-108 }, - HP{.val=1.000000e-91, .off= -2.218844988608365240e-108 }, - HP{.val=1.000000e-92, .off= 1.187522883398155383e-109 }, - HP{.val=1.000000e-93, .off= 9.703442563414457296e-110 }, - HP{.val=1.000000e-94, .off= 4.380992763404268896e-111 }, - HP{.val=1.000000e-95, .off= 1.054461638397900823e-112 }, - HP{.val=1.000000e-96, .off= 9.370789450913819736e-113 }, - HP{.val=1.000000e-97, .off= -3.623472756142303998e-114 }, - HP{.val=1.000000e-98, .off= 6.122223899149788839e-115 }, - HP{.val=1.000000e-99, .off= -1.999189980260288281e-116 }, - HP{.val=1.000000e-100, .off= -1.999189980260288281e-117 }, - HP{.val=1.000000e-101, .off= -5.171617276904849634e-118 }, - HP{.val=1.000000e-102, .off= 6.724985085512256320e-119 }, - HP{.val=1.000000e-103, .off= 4.246526260008692213e-120 }, - HP{.val=1.000000e-104, .off= 7.344599791888147003e-121 }, - HP{.val=1.000000e-105, .off= 3.472007877038828407e-122 }, - HP{.val=1.000000e-106, .off= 5.892377823819652194e-123 }, - HP{.val=1.000000e-107, .off= -1.585470431324073925e-125 }, - HP{.val=1.000000e-108, .off= -3.940375084977444795e-125 }, - HP{.val=1.000000e-109, .off= 7.869099673288519908e-127 }, - HP{.val=1.000000e-110, .off= -5.122196348054018581e-127 }, - HP{.val=1.000000e-111, .off= -8.815387795168313713e-128 }, - HP{.val=1.000000e-112, .off= 5.034080131510290214e-129 }, - HP{.val=1.000000e-113, .off= 2.148774313452247863e-130 }, - HP{.val=1.000000e-114, .off= -5.064490231692858416e-131 }, - HP{.val=1.000000e-115, .off= -5.064490231692858166e-132 }, - HP{.val=1.000000e-116, .off= 5.708726942017560559e-134 }, - HP{.val=1.000000e-117, .off= -2.951229134482377772e-134 }, - HP{.val=1.000000e-118, .off= 1.451398151372789513e-135 }, - HP{.val=1.000000e-119, .off= -1.300243902286690040e-136 }, - HP{.val=1.000000e-120, .off= 2.139308664787659449e-137 }, - HP{.val=1.000000e-121, .off= 2.139308664787659329e-138 }, - HP{.val=1.000000e-122, .off= -5.922142664292847471e-139 }, - HP{.val=1.000000e-123, .off= -5.922142664292846912e-140 }, - HP{.val=1.000000e-124, .off= 6.673875037395443799e-141 }, - HP{.val=1.000000e-125, .off= -1.198636026159737932e-142 }, - HP{.val=1.000000e-126, .off= 5.361789860136246995e-143 }, - HP{.val=1.000000e-127, .off= -2.838742497733733936e-144 }, - HP{.val=1.000000e-128, .off= -5.401408859568103261e-145 }, - HP{.val=1.000000e-129, .off= 7.411922949603743011e-146 }, - HP{.val=1.000000e-130, .off= -8.604741811861064385e-147 }, - HP{.val=1.000000e-131, .off= 1.405673664054439890e-148 }, - HP{.val=1.000000e-132, .off= 1.405673664054439933e-149 }, - HP{.val=1.000000e-133, .off= -6.414963426504548053e-150 }, - HP{.val=1.000000e-134, .off= -3.971014335704864578e-151 }, - HP{.val=1.000000e-135, .off= -3.971014335704864748e-152 }, - HP{.val=1.000000e-136, .off= -1.523438813303585576e-154 }, - HP{.val=1.000000e-137, .off= 2.234325152653707766e-154 }, - HP{.val=1.000000e-138, .off= -6.715683724786540160e-155 }, - HP{.val=1.000000e-139, .off= -2.986513359186437306e-156 }, - HP{.val=1.000000e-140, .off= 1.674949597813692102e-157 }, - HP{.val=1.000000e-141, .off= -4.151879098436469092e-158 }, - HP{.val=1.000000e-142, .off= -4.151879098436469295e-159 }, - HP{.val=1.000000e-143, .off= 4.952540739454407825e-160 }, - HP{.val=1.000000e-144, .off= 4.952540739454407667e-161 }, - HP{.val=1.000000e-145, .off= 8.508954738630531443e-162 }, - HP{.val=1.000000e-146, .off= -2.604839008794855481e-163 }, - HP{.val=1.000000e-147, .off= 2.952057864917838382e-164 }, - HP{.val=1.000000e-148, .off= 6.425118410988271757e-165 }, - HP{.val=1.000000e-149, .off= 2.083792728400229858e-166 }, - HP{.val=1.000000e-150, .off= -6.295358232172964237e-168 }, - HP{.val=1.000000e-151, .off= 6.153785555826519421e-168 }, - HP{.val=1.000000e-152, .off= -6.564942029880634994e-169 }, - HP{.val=1.000000e-153, .off= -3.915207116191644540e-170 }, - HP{.val=1.000000e-154, .off= 2.709130168030831503e-171 }, - HP{.val=1.000000e-155, .off= -1.431080634608215966e-172 }, - HP{.val=1.000000e-156, .off= -4.018712386257620994e-173 }, - HP{.val=1.000000e-157, .off= 5.684906682427646782e-174 }, - HP{.val=1.000000e-158, .off= -6.444617153428937489e-175 }, - HP{.val=1.000000e-159, .off= 1.136335243981427681e-176 }, - HP{.val=1.000000e-160, .off= 1.136335243981427725e-177 }, - HP{.val=1.000000e-161, .off= -2.812077463003137395e-178 }, - HP{.val=1.000000e-162, .off= 4.591196362592922204e-179 }, - HP{.val=1.000000e-163, .off= 7.675893789924613703e-180 }, - HP{.val=1.000000e-164, .off= 3.820022005759999543e-181 }, - HP{.val=1.000000e-165, .off= -9.998177244457686588e-183 }, - HP{.val=1.000000e-166, .off= -4.012217555824373639e-183 }, - HP{.val=1.000000e-167, .off= -2.467177666011174334e-185 }, - HP{.val=1.000000e-168, .off= -4.953592503130188139e-185 }, - HP{.val=1.000000e-169, .off= -2.011795792799518887e-186 }, - HP{.val=1.000000e-170, .off= 1.665450095113817423e-187 }, - HP{.val=1.000000e-171, .off= 1.665450095113817487e-188 }, - HP{.val=1.000000e-172, .off= -4.080246604750770577e-189 }, - HP{.val=1.000000e-173, .off= -4.080246604750770677e-190 }, - HP{.val=1.000000e-174, .off= 4.085789420184387951e-192 }, - HP{.val=1.000000e-175, .off= 4.085789420184388146e-193 }, - HP{.val=1.000000e-176, .off= 4.085789420184388146e-194 }, - HP{.val=1.000000e-177, .off= 4.792197640035244894e-194 }, - HP{.val=1.000000e-178, .off= 4.792197640035244742e-195 }, - HP{.val=1.000000e-179, .off= -2.057206575616014662e-196 }, - HP{.val=1.000000e-180, .off= -2.057206575616014662e-197 }, - HP{.val=1.000000e-181, .off= -4.732755097354788053e-198 }, - HP{.val=1.000000e-182, .off= -4.732755097354787867e-199 }, - HP{.val=1.000000e-183, .off= -5.522105321379546765e-201 }, - HP{.val=1.000000e-184, .off= -5.777891238658996019e-201 }, - HP{.val=1.000000e-185, .off= 7.542096444923057046e-203 }, - HP{.val=1.000000e-186, .off= 8.919335748431433483e-203 }, - HP{.val=1.000000e-187, .off= -1.287071881492476028e-204 }, - HP{.val=1.000000e-188, .off= 5.091932887209967018e-205 }, - HP{.val=1.000000e-189, .off= -6.868701054107114024e-206 }, - HP{.val=1.000000e-190, .off= -1.885103578558330118e-207 }, - HP{.val=1.000000e-191, .off= -1.885103578558330205e-208 }, - HP{.val=1.000000e-192, .off= -9.671974634103305058e-209 }, - HP{.val=1.000000e-193, .off= -4.805180224387695640e-210 }, - HP{.val=1.000000e-194, .off= -1.763433718315439838e-211 }, - HP{.val=1.000000e-195, .off= -9.367799983496079132e-212 }, - HP{.val=1.000000e-196, .off= -4.615071067758179837e-213 }, - HP{.val=1.000000e-197, .off= 1.325840076914194777e-214 }, - HP{.val=1.000000e-198, .off= 8.751979007754662425e-215 }, - HP{.val=1.000000e-199, .off= 1.789973760091724198e-216 }, - HP{.val=1.000000e-200, .off= 1.789973760091724077e-217 }, - HP{.val=1.000000e-201, .off= 5.416018159916171171e-218 }, - HP{.val=1.000000e-202, .off= -3.649092839644947067e-219 }, - HP{.val=1.000000e-203, .off= -3.649092839644947067e-220 }, - HP{.val=1.000000e-204, .off= -1.080338554413850956e-222 }, - HP{.val=1.000000e-205, .off= -1.080338554413850841e-223 }, - HP{.val=1.000000e-206, .off= -2.874486186850417807e-223 }, - HP{.val=1.000000e-207, .off= 7.499710055933455072e-224 }, - HP{.val=1.000000e-208, .off= -9.790617015372999087e-225 }, - HP{.val=1.000000e-209, .off= -4.387389805589732612e-226 }, - HP{.val=1.000000e-210, .off= -4.387389805589732612e-227 }, - HP{.val=1.000000e-211, .off= -8.608661063232909897e-228 }, - HP{.val=1.000000e-212, .off= 4.582811616902018972e-229 }, - HP{.val=1.000000e-213, .off= 4.582811616902019155e-230 }, - HP{.val=1.000000e-214, .off= 8.705146829444184930e-231 }, - HP{.val=1.000000e-215, .off= -4.177150709750081830e-232 }, - HP{.val=1.000000e-216, .off= -4.177150709750082366e-233 }, - HP{.val=1.000000e-217, .off= -8.202868690748290237e-234 }, - HP{.val=1.000000e-218, .off= -3.170721214500530119e-235 }, - HP{.val=1.000000e-219, .off= -3.170721214500529857e-236 }, - HP{.val=1.000000e-220, .off= 7.606440013180328441e-238 }, - HP{.val=1.000000e-221, .off= -1.696459258568569049e-238 }, - HP{.val=1.000000e-222, .off= -4.767838333426821244e-239 }, - HP{.val=1.000000e-223, .off= 2.910609353718809138e-240 }, - HP{.val=1.000000e-224, .off= -1.888420450747209784e-241 }, - HP{.val=1.000000e-225, .off= 4.110366804835314035e-242 }, - HP{.val=1.000000e-226, .off= 7.859608839574391006e-243 }, - HP{.val=1.000000e-227, .off= 5.516332567862468419e-244 }, - HP{.val=1.000000e-228, .off= -3.270953451057244613e-245 }, - HP{.val=1.000000e-229, .off= -6.932322625607124670e-246 }, - HP{.val=1.000000e-230, .off= -4.643966891513449762e-247 }, - HP{.val=1.000000e-231, .off= 1.076922443720738305e-248 }, - HP{.val=1.000000e-232, .off= -2.498633390800628939e-249 }, - HP{.val=1.000000e-233, .off= 4.205533798926934891e-250 }, - HP{.val=1.000000e-234, .off= 4.205533798926934891e-251 }, - HP{.val=1.000000e-235, .off= 4.205533798926934697e-252 }, - HP{.val=1.000000e-236, .off= -4.523850562697497656e-253 }, - HP{.val=1.000000e-237, .off= 9.320146633177728298e-255 }, - HP{.val=1.000000e-238, .off= 9.320146633177728062e-256 }, - HP{.val=1.000000e-239, .off= -7.592774752331086440e-256 }, - HP{.val=1.000000e-240, .off= 3.063212017229987840e-257 }, - HP{.val=1.000000e-241, .off= 3.063212017229987562e-258 }, - HP{.val=1.000000e-242, .off= 3.063212017229987562e-259 }, - HP{.val=1.000000e-243, .off= 4.616527473176159842e-261 }, - HP{.val=1.000000e-244, .off= 6.965550922098544975e-261 }, - HP{.val=1.000000e-245, .off= 6.965550922098544749e-262 }, - HP{.val=1.000000e-246, .off= 4.424965697574744679e-263 }, - HP{.val=1.000000e-247, .off= -1.926497363734756420e-264 }, - HP{.val=1.000000e-248, .off= 2.043167049583681740e-265 }, - HP{.val=1.000000e-249, .off= -5.399953725388390154e-266 }, - HP{.val=1.000000e-250, .off= -5.399953725388389982e-267 }, - HP{.val=1.000000e-251, .off= -1.523328321757102663e-268 }, - HP{.val=1.000000e-252, .off= 5.745344310051561161e-269 }, - HP{.val=1.000000e-253, .off= -6.369110076296211879e-270 }, - HP{.val=1.000000e-254, .off= 8.773957906638504842e-271 }, - HP{.val=1.000000e-255, .off= -6.904595826956931908e-273 }, - HP{.val=1.000000e-256, .off= 2.267170882721243669e-273 }, - HP{.val=1.000000e-257, .off= 2.267170882721243669e-274 }, - HP{.val=1.000000e-258, .off= 4.577819683828225398e-275 }, - HP{.val=1.000000e-259, .off= -6.975424321706684210e-276 }, - HP{.val=1.000000e-260, .off= 3.855741933482293648e-277 }, - HP{.val=1.000000e-261, .off= 1.599248963651256552e-278 }, - HP{.val=1.000000e-262, .off= -1.221367248637539543e-279 }, - HP{.val=1.000000e-263, .off= -1.221367248637539494e-280 }, - HP{.val=1.000000e-264, .off= -1.221367248637539647e-281 }, - HP{.val=1.000000e-265, .off= 1.533140771175737943e-282 }, - HP{.val=1.000000e-266, .off= 1.533140771175737895e-283 }, - HP{.val=1.000000e-267, .off= 1.533140771175738074e-284 }, - HP{.val=1.000000e-268, .off= 4.223090009274641634e-285 }, - HP{.val=1.000000e-269, .off= 4.223090009274641634e-286 }, - HP{.val=1.000000e-270, .off= -4.183001359784432924e-287 }, - HP{.val=1.000000e-271, .off= 3.697709298708449474e-288 }, - HP{.val=1.000000e-272, .off= 6.981338739747150474e-289 }, - HP{.val=1.000000e-273, .off= -9.436808465446354751e-290 }, - HP{.val=1.000000e-274, .off= 3.389869038611071740e-291 }, - HP{.val=1.000000e-275, .off= 6.596538414625427829e-292 }, - HP{.val=1.000000e-276, .off= -9.436808465446354618e-293 }, - HP{.val=1.000000e-277, .off= 3.089243784609725523e-294 }, - HP{.val=1.000000e-278, .off= 6.220756847123745836e-295 }, - HP{.val=1.000000e-279, .off= -5.522417137303829470e-296 }, - HP{.val=1.000000e-280, .off= 4.263561183052483059e-297 }, - HP{.val=1.000000e-281, .off= -1.852675267170212272e-298 }, - HP{.val=1.000000e-282, .off= -1.852675267170212378e-299 }, - HP{.val=1.000000e-283, .off= 5.314789322934508480e-300 }, - HP{.val=1.000000e-284, .off= -3.644541414696392675e-301 }, - HP{.val=1.000000e-285, .off= -7.377595888709267777e-302 }, - HP{.val=1.000000e-286, .off= -5.044436842451220838e-303 }, - HP{.val=1.000000e-287, .off= -2.127988034628661760e-304 }, - HP{.val=1.000000e-288, .off= -5.773549044406860911e-305 }, - HP{.val=1.000000e-289, .off= -1.216597782184112068e-306 }, - HP{.val=1.000000e-290, .off= -6.912786859962547924e-307 }, - HP{.val=1.000000e-291, .off= 3.767567660872018813e-308 }, + HP{ .val = 1.000000e+308, .off = -1.097906362944045488e+291 }, + HP{ .val = 1.000000e+307, .off = 1.396894023974354241e+290 }, + HP{ .val = 1.000000e+306, .off = -1.721606459673645508e+289 }, + HP{ .val = 1.000000e+305, .off = 6.074644749446353973e+288 }, + HP{ .val = 1.000000e+304, .off = 6.074644749446353567e+287 }, + HP{ .val = 1.000000e+303, .off = -1.617650767864564452e+284 }, + HP{ .val = 1.000000e+302, .off = -7.629703079084895055e+285 }, + HP{ .val = 1.000000e+301, .off = -5.250476025520442286e+284 }, + HP{ .val = 1.000000e+300, .off = -5.250476025520441956e+283 }, + HP{ .val = 1.000000e+299, .off = -5.250476025520441750e+282 }, + HP{ .val = 1.000000e+298, .off = 4.043379652465702264e+281 }, + HP{ .val = 1.000000e+297, .off = -1.765280146275637946e+280 }, + HP{ .val = 1.000000e+296, .off = 1.865132227937699609e+279 }, + HP{ .val = 1.000000e+295, .off = 1.865132227937699609e+278 }, + HP{ .val = 1.000000e+294, .off = -6.643646774124810287e+277 }, + HP{ .val = 1.000000e+293, .off = 7.537651562646039934e+276 }, + HP{ .val = 1.000000e+292, .off = -1.325659897835741608e+275 }, + HP{ .val = 1.000000e+291, .off = 4.213909764965371606e+274 }, + HP{ .val = 1.000000e+290, .off = -6.172783352786715670e+273 }, + HP{ .val = 1.000000e+289, .off = -6.172783352786715670e+272 }, + HP{ .val = 1.000000e+288, .off = -7.630473539575035471e+270 }, + HP{ .val = 1.000000e+287, .off = -7.525217352494018700e+270 }, + HP{ .val = 1.000000e+286, .off = -3.298861103408696612e+269 }, + HP{ .val = 1.000000e+285, .off = 1.984084207947955778e+268 }, + HP{ .val = 1.000000e+284, .off = -7.921438250845767591e+267 }, + HP{ .val = 1.000000e+283, .off = 4.460464822646386735e+266 }, + HP{ .val = 1.000000e+282, .off = -3.278224598286209647e+265 }, + HP{ .val = 1.000000e+281, .off = -3.278224598286209737e+264 }, + HP{ .val = 1.000000e+280, .off = -3.278224598286209961e+263 }, + HP{ .val = 1.000000e+279, .off = -5.797329227496039232e+262 }, + HP{ .val = 1.000000e+278, .off = 3.649313132040821498e+261 }, + HP{ .val = 1.000000e+277, .off = -2.867878510995372374e+259 }, + HP{ .val = 1.000000e+276, .off = -5.206914080024985409e+259 }, + HP{ .val = 1.000000e+275, .off = 4.018322599210230404e+258 }, + HP{ .val = 1.000000e+274, .off = 7.862171215558236495e+257 }, + HP{ .val = 1.000000e+273, .off = 5.459765830340732821e+256 }, + HP{ .val = 1.000000e+272, .off = -6.552261095746788047e+255 }, + HP{ .val = 1.000000e+271, .off = 4.709014147460262298e+254 }, + HP{ .val = 1.000000e+270, .off = -4.675381888545612729e+253 }, + HP{ .val = 1.000000e+269, .off = -4.675381888545612892e+252 }, + HP{ .val = 1.000000e+268, .off = 2.656177514583977380e+251 }, + HP{ .val = 1.000000e+267, .off = 2.656177514583977190e+250 }, + HP{ .val = 1.000000e+266, .off = -3.071603269111014892e+249 }, + HP{ .val = 1.000000e+265, .off = -6.651466258920385440e+248 }, + HP{ .val = 1.000000e+264, .off = -4.414051890289528972e+247 }, + HP{ .val = 1.000000e+263, .off = -1.617283929500958387e+246 }, + HP{ .val = 1.000000e+262, .off = -1.617283929500958241e+245 }, + HP{ .val = 1.000000e+261, .off = 7.122615947963323868e+244 }, + HP{ .val = 1.000000e+260, .off = -6.533477610574617382e+243 }, + HP{ .val = 1.000000e+259, .off = 7.122615947963323982e+242 }, + HP{ .val = 1.000000e+258, .off = -5.679971763165996225e+241 }, + HP{ .val = 1.000000e+257, .off = -3.012765990014054219e+240 }, + HP{ .val = 1.000000e+256, .off = -3.012765990014054219e+239 }, + HP{ .val = 1.000000e+255, .off = 1.154743030535854616e+238 }, + HP{ .val = 1.000000e+254, .off = 6.364129306223240767e+237 }, + HP{ .val = 1.000000e+253, .off = 6.364129306223241129e+236 }, + HP{ .val = 1.000000e+252, .off = -9.915202805299840595e+235 }, + HP{ .val = 1.000000e+251, .off = -4.827911520448877980e+234 }, + HP{ .val = 1.000000e+250, .off = 7.890316691678530146e+233 }, + HP{ .val = 1.000000e+249, .off = 7.890316691678529484e+232 }, + HP{ .val = 1.000000e+248, .off = -4.529828046727141859e+231 }, + HP{ .val = 1.000000e+247, .off = 4.785280507077111924e+230 }, + HP{ .val = 1.000000e+246, .off = -6.858605185178205305e+229 }, + HP{ .val = 1.000000e+245, .off = -4.432795665958347728e+228 }, + HP{ .val = 1.000000e+244, .off = -7.465057564983169531e+227 }, + HP{ .val = 1.000000e+243, .off = -7.465057564983169741e+226 }, + HP{ .val = 1.000000e+242, .off = -5.096102956370027445e+225 }, + HP{ .val = 1.000000e+241, .off = -5.096102956370026952e+224 }, + HP{ .val = 1.000000e+240, .off = -1.394611380411992474e+223 }, + HP{ .val = 1.000000e+239, .off = 9.188208545617793960e+221 }, + HP{ .val = 1.000000e+238, .off = -4.864759732872650359e+221 }, + HP{ .val = 1.000000e+237, .off = 5.979453868566904629e+220 }, + HP{ .val = 1.000000e+236, .off = -5.316601966265964857e+219 }, + HP{ .val = 1.000000e+235, .off = -5.316601966265964701e+218 }, + HP{ .val = 1.000000e+234, .off = -1.786584517880693123e+217 }, + HP{ .val = 1.000000e+233, .off = 2.625937292600896716e+216 }, + HP{ .val = 1.000000e+232, .off = -5.647541102052084079e+215 }, + HP{ .val = 1.000000e+231, .off = -5.647541102052083888e+214 }, + HP{ .val = 1.000000e+230, .off = -9.956644432600511943e+213 }, + HP{ .val = 1.000000e+229, .off = 8.161138937705571862e+211 }, + HP{ .val = 1.000000e+228, .off = 7.549087847752475275e+211 }, + HP{ .val = 1.000000e+227, .off = -9.283347037202319948e+210 }, + HP{ .val = 1.000000e+226, .off = 3.866992716668613820e+209 }, + HP{ .val = 1.000000e+225, .off = 7.154577655136347262e+208 }, + HP{ .val = 1.000000e+224, .off = 3.045096482051680688e+207 }, + HP{ .val = 1.000000e+223, .off = -4.660180717482069567e+206 }, + HP{ .val = 1.000000e+222, .off = -4.660180717482070101e+205 }, + HP{ .val = 1.000000e+221, .off = -4.660180717482069544e+204 }, + HP{ .val = 1.000000e+220, .off = 3.562757926310489022e+202 }, + HP{ .val = 1.000000e+219, .off = 3.491561111451748149e+202 }, + HP{ .val = 1.000000e+218, .off = -8.265758834125874135e+201 }, + HP{ .val = 1.000000e+217, .off = 3.981449442517482365e+200 }, + HP{ .val = 1.000000e+216, .off = -2.142154695804195936e+199 }, + HP{ .val = 1.000000e+215, .off = 9.339603063548950188e+198 }, + HP{ .val = 1.000000e+214, .off = 4.555537330485139746e+197 }, + HP{ .val = 1.000000e+213, .off = 1.565496247320257804e+196 }, + HP{ .val = 1.000000e+212, .off = 9.040598955232462036e+195 }, + HP{ .val = 1.000000e+211, .off = 4.368659762787334780e+194 }, + HP{ .val = 1.000000e+210, .off = 7.288621758065539072e+193 }, + HP{ .val = 1.000000e+209, .off = -7.311188218325485628e+192 }, + HP{ .val = 1.000000e+208, .off = 1.813693016918905189e+191 }, + HP{ .val = 1.000000e+207, .off = -3.889357755108838992e+190 }, + HP{ .val = 1.000000e+206, .off = -3.889357755108838992e+189 }, + HP{ .val = 1.000000e+205, .off = -1.661603547285501360e+188 }, + HP{ .val = 1.000000e+204, .off = 1.123089212493670643e+187 }, + HP{ .val = 1.000000e+203, .off = 1.123089212493670643e+186 }, + HP{ .val = 1.000000e+202, .off = 9.825254086803583029e+185 }, + HP{ .val = 1.000000e+201, .off = -3.771878529305654999e+184 }, + HP{ .val = 1.000000e+200, .off = 3.026687778748963675e+183 }, + HP{ .val = 1.000000e+199, .off = -9.720624048853446693e+182 }, + HP{ .val = 1.000000e+198, .off = -1.753554156601940139e+181 }, + HP{ .val = 1.000000e+197, .off = 4.885670753607648963e+180 }, + HP{ .val = 1.000000e+196, .off = 4.885670753607648963e+179 }, + HP{ .val = 1.000000e+195, .off = 2.292223523057028076e+178 }, + HP{ .val = 1.000000e+194, .off = 5.534032561245303825e+177 }, + HP{ .val = 1.000000e+193, .off = -6.622751331960730683e+176 }, + HP{ .val = 1.000000e+192, .off = -4.090088020876139692e+175 }, + HP{ .val = 1.000000e+191, .off = -7.255917159731877552e+174 }, + HP{ .val = 1.000000e+190, .off = -7.255917159731877992e+173 }, + HP{ .val = 1.000000e+189, .off = -2.309309130269787104e+172 }, + HP{ .val = 1.000000e+188, .off = -2.309309130269787019e+171 }, + HP{ .val = 1.000000e+187, .off = 9.284303438781988230e+170 }, + HP{ .val = 1.000000e+186, .off = 2.038295583124628364e+169 }, + HP{ .val = 1.000000e+185, .off = 2.038295583124628532e+168 }, + HP{ .val = 1.000000e+184, .off = -1.735666841696912925e+167 }, + HP{ .val = 1.000000e+183, .off = 5.340512704843477241e+166 }, + HP{ .val = 1.000000e+182, .off = -6.453119872723839321e+165 }, + HP{ .val = 1.000000e+181, .off = 8.288920849235306587e+164 }, + HP{ .val = 1.000000e+180, .off = -9.248546019891598293e+162 }, + HP{ .val = 1.000000e+179, .off = 1.954450226518486016e+162 }, + HP{ .val = 1.000000e+178, .off = -5.243811844750628197e+161 }, + HP{ .val = 1.000000e+177, .off = -7.448980502074320639e+159 }, + HP{ .val = 1.000000e+176, .off = -7.448980502074319858e+158 }, + HP{ .val = 1.000000e+175, .off = 6.284654753766312753e+158 }, + HP{ .val = 1.000000e+174, .off = -6.895756753684458388e+157 }, + HP{ .val = 1.000000e+173, .off = -1.403918625579970616e+156 }, + HP{ .val = 1.000000e+172, .off = -8.268716285710580522e+155 }, + HP{ .val = 1.000000e+171, .off = 4.602779327034313170e+154 }, + HP{ .val = 1.000000e+170, .off = -3.441905430931244940e+153 }, + HP{ .val = 1.000000e+169, .off = 6.613950516525702884e+152 }, + HP{ .val = 1.000000e+168, .off = 6.613950516525702652e+151 }, + HP{ .val = 1.000000e+167, .off = -3.860899428741951187e+150 }, + HP{ .val = 1.000000e+166, .off = 5.959272394946474605e+149 }, + HP{ .val = 1.000000e+165, .off = 1.005101065481665103e+149 }, + HP{ .val = 1.000000e+164, .off = -1.783349948587918355e+146 }, + HP{ .val = 1.000000e+163, .off = 6.215006036188360099e+146 }, + HP{ .val = 1.000000e+162, .off = 6.215006036188360099e+145 }, + HP{ .val = 1.000000e+161, .off = -3.774589324822814903e+144 }, + HP{ .val = 1.000000e+160, .off = -6.528407745068226929e+142 }, + HP{ .val = 1.000000e+159, .off = 7.151530601283157561e+142 }, + HP{ .val = 1.000000e+158, .off = 4.712664546348788765e+141 }, + HP{ .val = 1.000000e+157, .off = 1.664081977680827856e+140 }, + HP{ .val = 1.000000e+156, .off = 1.664081977680827750e+139 }, + HP{ .val = 1.000000e+155, .off = -7.176231540910168265e+137 }, + HP{ .val = 1.000000e+154, .off = -3.694754568805822650e+137 }, + HP{ .val = 1.000000e+153, .off = 2.665969958768462622e+134 }, + HP{ .val = 1.000000e+152, .off = -4.625108135904199522e+135 }, + HP{ .val = 1.000000e+151, .off = -1.717753238721771919e+134 }, + HP{ .val = 1.000000e+150, .off = 1.916440382756262433e+133 }, + HP{ .val = 1.000000e+149, .off = -4.897672657515052040e+132 }, + HP{ .val = 1.000000e+148, .off = -4.897672657515052198e+131 }, + HP{ .val = 1.000000e+147, .off = 2.200361759434233991e+130 }, + HP{ .val = 1.000000e+146, .off = 6.636633270027537273e+129 }, + HP{ .val = 1.000000e+145, .off = 1.091293881785907977e+128 }, + HP{ .val = 1.000000e+144, .off = -2.374543235865110597e+127 }, + HP{ .val = 1.000000e+143, .off = -2.374543235865110537e+126 }, + HP{ .val = 1.000000e+142, .off = -5.082228484029969099e+125 }, + HP{ .val = 1.000000e+141, .off = -1.697621923823895943e+124 }, + HP{ .val = 1.000000e+140, .off = -5.928380124081487212e+123 }, + HP{ .val = 1.000000e+139, .off = -3.284156248920492522e+122 }, + HP{ .val = 1.000000e+138, .off = -3.284156248920492706e+121 }, + HP{ .val = 1.000000e+137, .off = -3.284156248920492476e+120 }, + HP{ .val = 1.000000e+136, .off = -5.866406127007401066e+119 }, + HP{ .val = 1.000000e+135, .off = 3.817030915818506056e+118 }, + HP{ .val = 1.000000e+134, .off = 7.851796350329300951e+117 }, + HP{ .val = 1.000000e+133, .off = -2.235117235947686077e+116 }, + HP{ .val = 1.000000e+132, .off = 9.170432597638723691e+114 }, + HP{ .val = 1.000000e+131, .off = 8.797444499042767883e+114 }, + HP{ .val = 1.000000e+130, .off = -5.978307824605161274e+113 }, + HP{ .val = 1.000000e+129, .off = 1.782556435814758516e+111 }, + HP{ .val = 1.000000e+128, .off = -7.517448691651820362e+111 }, + HP{ .val = 1.000000e+127, .off = 4.507089332150205498e+110 }, + HP{ .val = 1.000000e+126, .off = 7.513223838100711695e+109 }, + HP{ .val = 1.000000e+125, .off = 7.513223838100712113e+108 }, + HP{ .val = 1.000000e+124, .off = 5.164681255326878494e+107 }, + HP{ .val = 1.000000e+123, .off = 2.229003026859587122e+106 }, + HP{ .val = 1.000000e+122, .off = -1.440594758724527399e+105 }, + HP{ .val = 1.000000e+121, .off = -3.734093374714598783e+104 }, + HP{ .val = 1.000000e+120, .off = 1.999653165260579757e+103 }, + HP{ .val = 1.000000e+119, .off = 5.583244752745066693e+102 }, + HP{ .val = 1.000000e+118, .off = 3.343500010567262234e+101 }, + HP{ .val = 1.000000e+117, .off = -5.055542772599503556e+100 }, + HP{ .val = 1.000000e+116, .off = -1.555941612946684331e+99 }, + HP{ .val = 1.000000e+115, .off = -1.555941612946684331e+98 }, + HP{ .val = 1.000000e+114, .off = -1.555941612946684293e+97 }, + HP{ .val = 1.000000e+113, .off = -1.555941612946684246e+96 }, + HP{ .val = 1.000000e+112, .off = 6.988006530736955847e+95 }, + HP{ .val = 1.000000e+111, .off = 4.318022735835818244e+94 }, + HP{ .val = 1.000000e+110, .off = -2.356936751417025578e+93 }, + HP{ .val = 1.000000e+109, .off = 1.814912928116001926e+92 }, + HP{ .val = 1.000000e+108, .off = -3.399899171300282744e+91 }, + HP{ .val = 1.000000e+107, .off = 3.118615952970072913e+90 }, + HP{ .val = 1.000000e+106, .off = -9.103599905036843605e+89 }, + HP{ .val = 1.000000e+105, .off = 6.174169917471802325e+88 }, + HP{ .val = 1.000000e+104, .off = -1.915675085734668657e+86 }, + HP{ .val = 1.000000e+103, .off = -1.915675085734668864e+85 }, + HP{ .val = 1.000000e+102, .off = 2.295048673475466221e+85 }, + HP{ .val = 1.000000e+101, .off = 2.295048673475466135e+84 }, + HP{ .val = 1.000000e+100, .off = -1.590289110975991792e+83 }, + HP{ .val = 1.000000e+99, .off = 3.266383119588331155e+82 }, + HP{ .val = 1.000000e+98, .off = 2.309629754856292029e+80 }, + HP{ .val = 1.000000e+97, .off = -7.357587384771124533e+80 }, + HP{ .val = 1.000000e+96, .off = -4.986165397190889509e+79 }, + HP{ .val = 1.000000e+95, .off = -2.021887912715594741e+78 }, + HP{ .val = 1.000000e+94, .off = -2.021887912715594638e+77 }, + HP{ .val = 1.000000e+93, .off = -4.337729697461918675e+76 }, + HP{ .val = 1.000000e+92, .off = -4.337729697461918997e+75 }, + HP{ .val = 1.000000e+91, .off = -7.956232486128049702e+74 }, + HP{ .val = 1.000000e+90, .off = 3.351588728453609882e+73 }, + HP{ .val = 1.000000e+89, .off = 5.246334248081951113e+71 }, + HP{ .val = 1.000000e+88, .off = 4.058327554364963672e+71 }, + HP{ .val = 1.000000e+87, .off = 4.058327554364963918e+70 }, + HP{ .val = 1.000000e+86, .off = -1.463069523067487266e+69 }, + HP{ .val = 1.000000e+85, .off = -1.463069523067487314e+68 }, + HP{ .val = 1.000000e+84, .off = -5.776660989811589441e+67 }, + HP{ .val = 1.000000e+83, .off = -3.080666323096525761e+66 }, + HP{ .val = 1.000000e+82, .off = 3.659320343691134468e+65 }, + HP{ .val = 1.000000e+81, .off = 7.871812010433421235e+64 }, + HP{ .val = 1.000000e+80, .off = -2.660986470836727449e+61 }, + HP{ .val = 1.000000e+79, .off = 3.264399249934044627e+62 }, + HP{ .val = 1.000000e+78, .off = -8.493621433689703070e+60 }, + HP{ .val = 1.000000e+77, .off = 1.721738727445414063e+60 }, + HP{ .val = 1.000000e+76, .off = -4.706013449590547218e+59 }, + HP{ .val = 1.000000e+75, .off = 7.346021882351880518e+58 }, + HP{ .val = 1.000000e+74, .off = 4.835181188197207515e+57 }, + HP{ .val = 1.000000e+73, .off = 1.696630320503867482e+56 }, + HP{ .val = 1.000000e+72, .off = 5.619818905120542959e+55 }, + HP{ .val = 1.000000e+71, .off = -4.188152556421145598e+54 }, + HP{ .val = 1.000000e+70, .off = -7.253143638152923145e+53 }, + HP{ .val = 1.000000e+69, .off = -7.253143638152923145e+52 }, + HP{ .val = 1.000000e+68, .off = 4.719477774861832896e+51 }, + HP{ .val = 1.000000e+67, .off = 1.726322421608144052e+50 }, + HP{ .val = 1.000000e+66, .off = 5.467766613175255107e+49 }, + HP{ .val = 1.000000e+65, .off = 7.909613737163661911e+47 }, + HP{ .val = 1.000000e+64, .off = -2.132041900945439564e+47 }, + HP{ .val = 1.000000e+63, .off = -5.785795994272697265e+46 }, + HP{ .val = 1.000000e+62, .off = -3.502199685943161329e+45 }, + HP{ .val = 1.000000e+61, .off = 5.061286470292598274e+44 }, + HP{ .val = 1.000000e+60, .off = 5.061286470292598472e+43 }, + HP{ .val = 1.000000e+59, .off = 2.831211950439536034e+42 }, + HP{ .val = 1.000000e+58, .off = 5.618805100255863927e+41 }, + HP{ .val = 1.000000e+57, .off = -4.834669211555366251e+40 }, + HP{ .val = 1.000000e+56, .off = -9.190283508143378583e+39 }, + HP{ .val = 1.000000e+55, .off = -1.023506702040855158e+38 }, + HP{ .val = 1.000000e+54, .off = -7.829154040459624616e+37 }, + HP{ .val = 1.000000e+53, .off = 6.779051325638372659e+35 }, + HP{ .val = 1.000000e+52, .off = 6.779051325638372290e+34 }, + HP{ .val = 1.000000e+51, .off = 6.779051325638371598e+33 }, + HP{ .val = 1.000000e+50, .off = -7.629769841091887392e+33 }, + HP{ .val = 1.000000e+49, .off = 5.350972305245182400e+32 }, + HP{ .val = 1.000000e+48, .off = -4.384584304507619764e+31 }, + HP{ .val = 1.000000e+47, .off = -4.384584304507619876e+30 }, + HP{ .val = 1.000000e+46, .off = 6.860180964052978705e+28 }, + HP{ .val = 1.000000e+45, .off = 7.024271097546444878e+28 }, + HP{ .val = 1.000000e+44, .off = -8.821361405306422641e+27 }, + HP{ .val = 1.000000e+43, .off = -1.393721169594140991e+26 }, + HP{ .val = 1.000000e+42, .off = -4.488571267807591679e+25 }, + HP{ .val = 1.000000e+41, .off = -6.200086450407783195e+23 }, + HP{ .val = 1.000000e+40, .off = -3.037860284270036669e+23 }, + HP{ .val = 1.000000e+39, .off = 6.029083362839682141e+22 }, + HP{ .val = 1.000000e+38, .off = 2.251190176543965970e+21 }, + HP{ .val = 1.000000e+37, .off = 4.612373417978788577e+20 }, + HP{ .val = 1.000000e+36, .off = -4.242063737401796198e+19 }, + HP{ .val = 1.000000e+35, .off = 3.136633892082024448e+18 }, + HP{ .val = 1.000000e+34, .off = 5.442476901295718400e+17 }, + HP{ .val = 1.000000e+33, .off = 5.442476901295718400e+16 }, + HP{ .val = 1.000000e+32, .off = -5.366162204393472000e+15 }, + HP{ .val = 1.000000e+31, .off = 3.641037050347520000e+14 }, + HP{ .val = 1.000000e+30, .off = -1.988462483865600000e+13 }, + HP{ .val = 1.000000e+29, .off = 8.566849142784000000e+12 }, + HP{ .val = 1.000000e+28, .off = 4.168802631680000000e+11 }, + HP{ .val = 1.000000e+27, .off = -1.328755507200000000e+10 }, + HP{ .val = 1.000000e+26, .off = -4.764729344000000000e+09 }, + HP{ .val = 1.000000e+25, .off = -9.059696640000000000e+08 }, + HP{ .val = 1.000000e+24, .off = 1.677721600000000000e+07 }, + HP{ .val = 1.000000e+23, .off = 8.388608000000000000e+06 }, + HP{ .val = 1.000000e+22, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+21, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+20, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+19, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+18, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+17, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+16, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+15, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+14, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+13, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+12, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+11, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+10, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+09, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+08, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+07, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+06, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+05, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+04, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+03, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+02, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+01, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e+00, .off = 0.000000000000000000e+00 }, + HP{ .val = 1.000000e-01, .off = -5.551115123125783010e-18 }, + HP{ .val = 1.000000e-02, .off = -2.081668171172168436e-19 }, + HP{ .val = 1.000000e-03, .off = -2.081668171172168557e-20 }, + HP{ .val = 1.000000e-04, .off = -4.792173602385929943e-21 }, + HP{ .val = 1.000000e-05, .off = -8.180305391403130547e-22 }, + HP{ .val = 1.000000e-06, .off = 4.525188817411374069e-23 }, + HP{ .val = 1.000000e-07, .off = 4.525188817411373922e-24 }, + HP{ .val = 1.000000e-08, .off = -2.092256083012847109e-25 }, + HP{ .val = 1.000000e-09, .off = -6.228159145777985254e-26 }, + HP{ .val = 1.000000e-10, .off = -3.643219731549774344e-27 }, + HP{ .val = 1.000000e-11, .off = 6.050303071806019080e-28 }, + HP{ .val = 1.000000e-12, .off = 2.011335237074438524e-29 }, + HP{ .val = 1.000000e-13, .off = -3.037374556340037101e-30 }, + HP{ .val = 1.000000e-14, .off = 1.180690645440101289e-32 }, + HP{ .val = 1.000000e-15, .off = -7.770539987666107583e-32 }, + HP{ .val = 1.000000e-16, .off = 2.090221327596539779e-33 }, + HP{ .val = 1.000000e-17, .off = -7.154242405462192144e-34 }, + HP{ .val = 1.000000e-18, .off = -7.154242405462192572e-35 }, + HP{ .val = 1.000000e-19, .off = 2.475407316473986894e-36 }, + HP{ .val = 1.000000e-20, .off = 5.484672854579042914e-37 }, + HP{ .val = 1.000000e-21, .off = 9.246254777210362522e-38 }, + HP{ .val = 1.000000e-22, .off = -4.859677432657087182e-39 }, + HP{ .val = 1.000000e-23, .off = 3.956530198510069291e-40 }, + HP{ .val = 1.000000e-24, .off = 7.629950044829717753e-41 }, + HP{ .val = 1.000000e-25, .off = -3.849486974919183692e-42 }, + HP{ .val = 1.000000e-26, .off = -3.849486974919184170e-43 }, + HP{ .val = 1.000000e-27, .off = -3.849486974919184070e-44 }, + HP{ .val = 1.000000e-28, .off = 2.876745653839937870e-45 }, + HP{ .val = 1.000000e-29, .off = 5.679342582489572168e-46 }, + HP{ .val = 1.000000e-30, .off = -8.333642060758598930e-47 }, + HP{ .val = 1.000000e-31, .off = -8.333642060758597958e-48 }, + HP{ .val = 1.000000e-32, .off = -5.596730997624190224e-49 }, + HP{ .val = 1.000000e-33, .off = -5.596730997624190604e-50 }, + HP{ .val = 1.000000e-34, .off = 7.232539610818348498e-51 }, + HP{ .val = 1.000000e-35, .off = -7.857545194582380514e-53 }, + HP{ .val = 1.000000e-36, .off = 5.896157255772251528e-53 }, + HP{ .val = 1.000000e-37, .off = -6.632427322784915796e-54 }, + HP{ .val = 1.000000e-38, .off = 3.808059826012723592e-55 }, + HP{ .val = 1.000000e-39, .off = 7.070712060011985131e-56 }, + HP{ .val = 1.000000e-40, .off = 7.070712060011985584e-57 }, + HP{ .val = 1.000000e-41, .off = -5.761291134237854167e-59 }, + HP{ .val = 1.000000e-42, .off = -3.762312935688689794e-59 }, + HP{ .val = 1.000000e-43, .off = -7.745042713519821150e-60 }, + HP{ .val = 1.000000e-44, .off = 4.700987842202462817e-61 }, + HP{ .val = 1.000000e-45, .off = 1.589480203271891964e-62 }, + HP{ .val = 1.000000e-46, .off = -2.299904345391321765e-63 }, + HP{ .val = 1.000000e-47, .off = 2.561826340437695261e-64 }, + HP{ .val = 1.000000e-48, .off = 2.561826340437695345e-65 }, + HP{ .val = 1.000000e-49, .off = 6.360053438741614633e-66 }, + HP{ .val = 1.000000e-50, .off = -7.616223705782342295e-68 }, + HP{ .val = 1.000000e-51, .off = -7.616223705782343324e-69 }, + HP{ .val = 1.000000e-52, .off = -7.616223705782342295e-70 }, + HP{ .val = 1.000000e-53, .off = -3.079876214757872338e-70 }, + HP{ .val = 1.000000e-54, .off = -3.079876214757872821e-71 }, + HP{ .val = 1.000000e-55, .off = 5.423954167728123147e-73 }, + HP{ .val = 1.000000e-56, .off = -3.985444122640543680e-73 }, + HP{ .val = 1.000000e-57, .off = 4.504255013759498850e-74 }, + HP{ .val = 1.000000e-58, .off = -2.570494266573869991e-75 }, + HP{ .val = 1.000000e-59, .off = -2.570494266573869930e-76 }, + HP{ .val = 1.000000e-60, .off = 2.956653608686574324e-77 }, + HP{ .val = 1.000000e-61, .off = -3.952281235388981376e-78 }, + HP{ .val = 1.000000e-62, .off = -3.952281235388981376e-79 }, + HP{ .val = 1.000000e-63, .off = -6.651083908855995172e-80 }, + HP{ .val = 1.000000e-64, .off = 3.469426116645307030e-81 }, + HP{ .val = 1.000000e-65, .off = 7.686305293937516319e-82 }, + HP{ .val = 1.000000e-66, .off = 2.415206322322254927e-83 }, + HP{ .val = 1.000000e-67, .off = 5.709643179581793251e-84 }, + HP{ .val = 1.000000e-68, .off = -6.644495035141475923e-85 }, + HP{ .val = 1.000000e-69, .off = 3.650620143794581913e-86 }, + HP{ .val = 1.000000e-70, .off = 4.333966503770636492e-88 }, + HP{ .val = 1.000000e-71, .off = 8.476455383920859113e-88 }, + HP{ .val = 1.000000e-72, .off = 3.449543675455986564e-89 }, + HP{ .val = 1.000000e-73, .off = 3.077238576654418974e-91 }, + HP{ .val = 1.000000e-74, .off = 4.234998629903623140e-91 }, + HP{ .val = 1.000000e-75, .off = 4.234998629903623412e-92 }, + HP{ .val = 1.000000e-76, .off = 7.303182045714702338e-93 }, + HP{ .val = 1.000000e-77, .off = 7.303182045714701699e-94 }, + HP{ .val = 1.000000e-78, .off = 1.121271649074855759e-96 }, + HP{ .val = 1.000000e-79, .off = 1.121271649074855863e-97 }, + HP{ .val = 1.000000e-80, .off = 3.857468248661243988e-97 }, + HP{ .val = 1.000000e-81, .off = 3.857468248661244248e-98 }, + HP{ .val = 1.000000e-82, .off = 3.857468248661244410e-99 }, + HP{ .val = 1.000000e-83, .off = -3.457651055545315679e-100 }, + HP{ .val = 1.000000e-84, .off = -3.457651055545315933e-101 }, + HP{ .val = 1.000000e-85, .off = 2.257285900866059216e-102 }, + HP{ .val = 1.000000e-86, .off = -8.458220892405268345e-103 }, + HP{ .val = 1.000000e-87, .off = -1.761029146610688867e-104 }, + HP{ .val = 1.000000e-88, .off = 6.610460535632536565e-105 }, + HP{ .val = 1.000000e-89, .off = -3.853901567171494935e-106 }, + HP{ .val = 1.000000e-90, .off = 5.062493089968513723e-108 }, + HP{ .val = 1.000000e-91, .off = -2.218844988608365240e-108 }, + HP{ .val = 1.000000e-92, .off = 1.187522883398155383e-109 }, + HP{ .val = 1.000000e-93, .off = 9.703442563414457296e-110 }, + HP{ .val = 1.000000e-94, .off = 4.380992763404268896e-111 }, + HP{ .val = 1.000000e-95, .off = 1.054461638397900823e-112 }, + HP{ .val = 1.000000e-96, .off = 9.370789450913819736e-113 }, + HP{ .val = 1.000000e-97, .off = -3.623472756142303998e-114 }, + HP{ .val = 1.000000e-98, .off = 6.122223899149788839e-115 }, + HP{ .val = 1.000000e-99, .off = -1.999189980260288281e-116 }, + HP{ .val = 1.000000e-100, .off = -1.999189980260288281e-117 }, + HP{ .val = 1.000000e-101, .off = -5.171617276904849634e-118 }, + HP{ .val = 1.000000e-102, .off = 6.724985085512256320e-119 }, + HP{ .val = 1.000000e-103, .off = 4.246526260008692213e-120 }, + HP{ .val = 1.000000e-104, .off = 7.344599791888147003e-121 }, + HP{ .val = 1.000000e-105, .off = 3.472007877038828407e-122 }, + HP{ .val = 1.000000e-106, .off = 5.892377823819652194e-123 }, + HP{ .val = 1.000000e-107, .off = -1.585470431324073925e-125 }, + HP{ .val = 1.000000e-108, .off = -3.940375084977444795e-125 }, + HP{ .val = 1.000000e-109, .off = 7.869099673288519908e-127 }, + HP{ .val = 1.000000e-110, .off = -5.122196348054018581e-127 }, + HP{ .val = 1.000000e-111, .off = -8.815387795168313713e-128 }, + HP{ .val = 1.000000e-112, .off = 5.034080131510290214e-129 }, + HP{ .val = 1.000000e-113, .off = 2.148774313452247863e-130 }, + HP{ .val = 1.000000e-114, .off = -5.064490231692858416e-131 }, + HP{ .val = 1.000000e-115, .off = -5.064490231692858166e-132 }, + HP{ .val = 1.000000e-116, .off = 5.708726942017560559e-134 }, + HP{ .val = 1.000000e-117, .off = -2.951229134482377772e-134 }, + HP{ .val = 1.000000e-118, .off = 1.451398151372789513e-135 }, + HP{ .val = 1.000000e-119, .off = -1.300243902286690040e-136 }, + HP{ .val = 1.000000e-120, .off = 2.139308664787659449e-137 }, + HP{ .val = 1.000000e-121, .off = 2.139308664787659329e-138 }, + HP{ .val = 1.000000e-122, .off = -5.922142664292847471e-139 }, + HP{ .val = 1.000000e-123, .off = -5.922142664292846912e-140 }, + HP{ .val = 1.000000e-124, .off = 6.673875037395443799e-141 }, + HP{ .val = 1.000000e-125, .off = -1.198636026159737932e-142 }, + HP{ .val = 1.000000e-126, .off = 5.361789860136246995e-143 }, + HP{ .val = 1.000000e-127, .off = -2.838742497733733936e-144 }, + HP{ .val = 1.000000e-128, .off = -5.401408859568103261e-145 }, + HP{ .val = 1.000000e-129, .off = 7.411922949603743011e-146 }, + HP{ .val = 1.000000e-130, .off = -8.604741811861064385e-147 }, + HP{ .val = 1.000000e-131, .off = 1.405673664054439890e-148 }, + HP{ .val = 1.000000e-132, .off = 1.405673664054439933e-149 }, + HP{ .val = 1.000000e-133, .off = -6.414963426504548053e-150 }, + HP{ .val = 1.000000e-134, .off = -3.971014335704864578e-151 }, + HP{ .val = 1.000000e-135, .off = -3.971014335704864748e-152 }, + HP{ .val = 1.000000e-136, .off = -1.523438813303585576e-154 }, + HP{ .val = 1.000000e-137, .off = 2.234325152653707766e-154 }, + HP{ .val = 1.000000e-138, .off = -6.715683724786540160e-155 }, + HP{ .val = 1.000000e-139, .off = -2.986513359186437306e-156 }, + HP{ .val = 1.000000e-140, .off = 1.674949597813692102e-157 }, + HP{ .val = 1.000000e-141, .off = -4.151879098436469092e-158 }, + HP{ .val = 1.000000e-142, .off = -4.151879098436469295e-159 }, + HP{ .val = 1.000000e-143, .off = 4.952540739454407825e-160 }, + HP{ .val = 1.000000e-144, .off = 4.952540739454407667e-161 }, + HP{ .val = 1.000000e-145, .off = 8.508954738630531443e-162 }, + HP{ .val = 1.000000e-146, .off = -2.604839008794855481e-163 }, + HP{ .val = 1.000000e-147, .off = 2.952057864917838382e-164 }, + HP{ .val = 1.000000e-148, .off = 6.425118410988271757e-165 }, + HP{ .val = 1.000000e-149, .off = 2.083792728400229858e-166 }, + HP{ .val = 1.000000e-150, .off = -6.295358232172964237e-168 }, + HP{ .val = 1.000000e-151, .off = 6.153785555826519421e-168 }, + HP{ .val = 1.000000e-152, .off = -6.564942029880634994e-169 }, + HP{ .val = 1.000000e-153, .off = -3.915207116191644540e-170 }, + HP{ .val = 1.000000e-154, .off = 2.709130168030831503e-171 }, + HP{ .val = 1.000000e-155, .off = -1.431080634608215966e-172 }, + HP{ .val = 1.000000e-156, .off = -4.018712386257620994e-173 }, + HP{ .val = 1.000000e-157, .off = 5.684906682427646782e-174 }, + HP{ .val = 1.000000e-158, .off = -6.444617153428937489e-175 }, + HP{ .val = 1.000000e-159, .off = 1.136335243981427681e-176 }, + HP{ .val = 1.000000e-160, .off = 1.136335243981427725e-177 }, + HP{ .val = 1.000000e-161, .off = -2.812077463003137395e-178 }, + HP{ .val = 1.000000e-162, .off = 4.591196362592922204e-179 }, + HP{ .val = 1.000000e-163, .off = 7.675893789924613703e-180 }, + HP{ .val = 1.000000e-164, .off = 3.820022005759999543e-181 }, + HP{ .val = 1.000000e-165, .off = -9.998177244457686588e-183 }, + HP{ .val = 1.000000e-166, .off = -4.012217555824373639e-183 }, + HP{ .val = 1.000000e-167, .off = -2.467177666011174334e-185 }, + HP{ .val = 1.000000e-168, .off = -4.953592503130188139e-185 }, + HP{ .val = 1.000000e-169, .off = -2.011795792799518887e-186 }, + HP{ .val = 1.000000e-170, .off = 1.665450095113817423e-187 }, + HP{ .val = 1.000000e-171, .off = 1.665450095113817487e-188 }, + HP{ .val = 1.000000e-172, .off = -4.080246604750770577e-189 }, + HP{ .val = 1.000000e-173, .off = -4.080246604750770677e-190 }, + HP{ .val = 1.000000e-174, .off = 4.085789420184387951e-192 }, + HP{ .val = 1.000000e-175, .off = 4.085789420184388146e-193 }, + HP{ .val = 1.000000e-176, .off = 4.085789420184388146e-194 }, + HP{ .val = 1.000000e-177, .off = 4.792197640035244894e-194 }, + HP{ .val = 1.000000e-178, .off = 4.792197640035244742e-195 }, + HP{ .val = 1.000000e-179, .off = -2.057206575616014662e-196 }, + HP{ .val = 1.000000e-180, .off = -2.057206575616014662e-197 }, + HP{ .val = 1.000000e-181, .off = -4.732755097354788053e-198 }, + HP{ .val = 1.000000e-182, .off = -4.732755097354787867e-199 }, + HP{ .val = 1.000000e-183, .off = -5.522105321379546765e-201 }, + HP{ .val = 1.000000e-184, .off = -5.777891238658996019e-201 }, + HP{ .val = 1.000000e-185, .off = 7.542096444923057046e-203 }, + HP{ .val = 1.000000e-186, .off = 8.919335748431433483e-203 }, + HP{ .val = 1.000000e-187, .off = -1.287071881492476028e-204 }, + HP{ .val = 1.000000e-188, .off = 5.091932887209967018e-205 }, + HP{ .val = 1.000000e-189, .off = -6.868701054107114024e-206 }, + HP{ .val = 1.000000e-190, .off = -1.885103578558330118e-207 }, + HP{ .val = 1.000000e-191, .off = -1.885103578558330205e-208 }, + HP{ .val = 1.000000e-192, .off = -9.671974634103305058e-209 }, + HP{ .val = 1.000000e-193, .off = -4.805180224387695640e-210 }, + HP{ .val = 1.000000e-194, .off = -1.763433718315439838e-211 }, + HP{ .val = 1.000000e-195, .off = -9.367799983496079132e-212 }, + HP{ .val = 1.000000e-196, .off = -4.615071067758179837e-213 }, + HP{ .val = 1.000000e-197, .off = 1.325840076914194777e-214 }, + HP{ .val = 1.000000e-198, .off = 8.751979007754662425e-215 }, + HP{ .val = 1.000000e-199, .off = 1.789973760091724198e-216 }, + HP{ .val = 1.000000e-200, .off = 1.789973760091724077e-217 }, + HP{ .val = 1.000000e-201, .off = 5.416018159916171171e-218 }, + HP{ .val = 1.000000e-202, .off = -3.649092839644947067e-219 }, + HP{ .val = 1.000000e-203, .off = -3.649092839644947067e-220 }, + HP{ .val = 1.000000e-204, .off = -1.080338554413850956e-222 }, + HP{ .val = 1.000000e-205, .off = -1.080338554413850841e-223 }, + HP{ .val = 1.000000e-206, .off = -2.874486186850417807e-223 }, + HP{ .val = 1.000000e-207, .off = 7.499710055933455072e-224 }, + HP{ .val = 1.000000e-208, .off = -9.790617015372999087e-225 }, + HP{ .val = 1.000000e-209, .off = -4.387389805589732612e-226 }, + HP{ .val = 1.000000e-210, .off = -4.387389805589732612e-227 }, + HP{ .val = 1.000000e-211, .off = -8.608661063232909897e-228 }, + HP{ .val = 1.000000e-212, .off = 4.582811616902018972e-229 }, + HP{ .val = 1.000000e-213, .off = 4.582811616902019155e-230 }, + HP{ .val = 1.000000e-214, .off = 8.705146829444184930e-231 }, + HP{ .val = 1.000000e-215, .off = -4.177150709750081830e-232 }, + HP{ .val = 1.000000e-216, .off = -4.177150709750082366e-233 }, + HP{ .val = 1.000000e-217, .off = -8.202868690748290237e-234 }, + HP{ .val = 1.000000e-218, .off = -3.170721214500530119e-235 }, + HP{ .val = 1.000000e-219, .off = -3.170721214500529857e-236 }, + HP{ .val = 1.000000e-220, .off = 7.606440013180328441e-238 }, + HP{ .val = 1.000000e-221, .off = -1.696459258568569049e-238 }, + HP{ .val = 1.000000e-222, .off = -4.767838333426821244e-239 }, + HP{ .val = 1.000000e-223, .off = 2.910609353718809138e-240 }, + HP{ .val = 1.000000e-224, .off = -1.888420450747209784e-241 }, + HP{ .val = 1.000000e-225, .off = 4.110366804835314035e-242 }, + HP{ .val = 1.000000e-226, .off = 7.859608839574391006e-243 }, + HP{ .val = 1.000000e-227, .off = 5.516332567862468419e-244 }, + HP{ .val = 1.000000e-228, .off = -3.270953451057244613e-245 }, + HP{ .val = 1.000000e-229, .off = -6.932322625607124670e-246 }, + HP{ .val = 1.000000e-230, .off = -4.643966891513449762e-247 }, + HP{ .val = 1.000000e-231, .off = 1.076922443720738305e-248 }, + HP{ .val = 1.000000e-232, .off = -2.498633390800628939e-249 }, + HP{ .val = 1.000000e-233, .off = 4.205533798926934891e-250 }, + HP{ .val = 1.000000e-234, .off = 4.205533798926934891e-251 }, + HP{ .val = 1.000000e-235, .off = 4.205533798926934697e-252 }, + HP{ .val = 1.000000e-236, .off = -4.523850562697497656e-253 }, + HP{ .val = 1.000000e-237, .off = 9.320146633177728298e-255 }, + HP{ .val = 1.000000e-238, .off = 9.320146633177728062e-256 }, + HP{ .val = 1.000000e-239, .off = -7.592774752331086440e-256 }, + HP{ .val = 1.000000e-240, .off = 3.063212017229987840e-257 }, + HP{ .val = 1.000000e-241, .off = 3.063212017229987562e-258 }, + HP{ .val = 1.000000e-242, .off = 3.063212017229987562e-259 }, + HP{ .val = 1.000000e-243, .off = 4.616527473176159842e-261 }, + HP{ .val = 1.000000e-244, .off = 6.965550922098544975e-261 }, + HP{ .val = 1.000000e-245, .off = 6.965550922098544749e-262 }, + HP{ .val = 1.000000e-246, .off = 4.424965697574744679e-263 }, + HP{ .val = 1.000000e-247, .off = -1.926497363734756420e-264 }, + HP{ .val = 1.000000e-248, .off = 2.043167049583681740e-265 }, + HP{ .val = 1.000000e-249, .off = -5.399953725388390154e-266 }, + HP{ .val = 1.000000e-250, .off = -5.399953725388389982e-267 }, + HP{ .val = 1.000000e-251, .off = -1.523328321757102663e-268 }, + HP{ .val = 1.000000e-252, .off = 5.745344310051561161e-269 }, + HP{ .val = 1.000000e-253, .off = -6.369110076296211879e-270 }, + HP{ .val = 1.000000e-254, .off = 8.773957906638504842e-271 }, + HP{ .val = 1.000000e-255, .off = -6.904595826956931908e-273 }, + HP{ .val = 1.000000e-256, .off = 2.267170882721243669e-273 }, + HP{ .val = 1.000000e-257, .off = 2.267170882721243669e-274 }, + HP{ .val = 1.000000e-258, .off = 4.577819683828225398e-275 }, + HP{ .val = 1.000000e-259, .off = -6.975424321706684210e-276 }, + HP{ .val = 1.000000e-260, .off = 3.855741933482293648e-277 }, + HP{ .val = 1.000000e-261, .off = 1.599248963651256552e-278 }, + HP{ .val = 1.000000e-262, .off = -1.221367248637539543e-279 }, + HP{ .val = 1.000000e-263, .off = -1.221367248637539494e-280 }, + HP{ .val = 1.000000e-264, .off = -1.221367248637539647e-281 }, + HP{ .val = 1.000000e-265, .off = 1.533140771175737943e-282 }, + HP{ .val = 1.000000e-266, .off = 1.533140771175737895e-283 }, + HP{ .val = 1.000000e-267, .off = 1.533140771175738074e-284 }, + HP{ .val = 1.000000e-268, .off = 4.223090009274641634e-285 }, + HP{ .val = 1.000000e-269, .off = 4.223090009274641634e-286 }, + HP{ .val = 1.000000e-270, .off = -4.183001359784432924e-287 }, + HP{ .val = 1.000000e-271, .off = 3.697709298708449474e-288 }, + HP{ .val = 1.000000e-272, .off = 6.981338739747150474e-289 }, + HP{ .val = 1.000000e-273, .off = -9.436808465446354751e-290 }, + HP{ .val = 1.000000e-274, .off = 3.389869038611071740e-291 }, + HP{ .val = 1.000000e-275, .off = 6.596538414625427829e-292 }, + HP{ .val = 1.000000e-276, .off = -9.436808465446354618e-293 }, + HP{ .val = 1.000000e-277, .off = 3.089243784609725523e-294 }, + HP{ .val = 1.000000e-278, .off = 6.220756847123745836e-295 }, + HP{ .val = 1.000000e-279, .off = -5.522417137303829470e-296 }, + HP{ .val = 1.000000e-280, .off = 4.263561183052483059e-297 }, + HP{ .val = 1.000000e-281, .off = -1.852675267170212272e-298 }, + HP{ .val = 1.000000e-282, .off = -1.852675267170212378e-299 }, + HP{ .val = 1.000000e-283, .off = 5.314789322934508480e-300 }, + HP{ .val = 1.000000e-284, .off = -3.644541414696392675e-301 }, + HP{ .val = 1.000000e-285, .off = -7.377595888709267777e-302 }, + HP{ .val = 1.000000e-286, .off = -5.044436842451220838e-303 }, + HP{ .val = 1.000000e-287, .off = -2.127988034628661760e-304 }, + HP{ .val = 1.000000e-288, .off = -5.773549044406860911e-305 }, + HP{ .val = 1.000000e-289, .off = -1.216597782184112068e-306 }, + HP{ .val = 1.000000e-290, .off = -6.912786859962547924e-307 }, + HP{ .val = 1.000000e-291, .off = 3.767567660872018813e-308 }, }; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 624751822a..71ac764b0b 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -107,7 +107,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), '}' => { return output(context, args[next_arg]); }, - '0' ... '9' => { + '0'...'9' => { width_start = i; state = State.BufWidth; }, @@ -127,7 +127,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), state = State.Start; start_index = i + 1; }, - '0' ... '9' => { + '0'...'9' => { width_start = i; state = State.IntegerWidth; }, @@ -141,7 +141,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), state = State.Start; start_index = i + 1; }, - '0' ... '9' => {}, + '0'...'9' => {}, else => @compileError("Unexpected character in format string: " ++ []u8{c}), }, State.FloatScientific => switch (c) { @@ -151,7 +151,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), state = State.Start; start_index = i + 1; }, - '0' ... '9' => { + '0'...'9' => { width_start = i; state = State.FloatScientificWidth; }, @@ -165,7 +165,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), state = State.Start; start_index = i + 1; }, - '0' ... '9' => {}, + '0'...'9' => {}, else => @compileError("Unexpected character in format string: " ++ []u8{c}), }, State.Float => switch (c) { @@ -175,7 +175,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), state = State.Start; start_index = i + 1; }, - '0' ... '9' => { + '0'...'9' => { width_start = i; state = State.FloatWidth; }, @@ -189,7 +189,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), state = State.Start; start_index = i + 1; }, - '0' ... '9' => {}, + '0'...'9' => {}, else => @compileError("Unexpected character in format string: " ++ []u8{c}), }, State.BufWidth => switch (c) { @@ -200,7 +200,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), state = State.Start; start_index = i + 1; }, - '0' ... '9' => {}, + '0'...'9' => {}, else => @compileError("Unexpected character in format string: " ++ []u8{c}), }, State.Character => switch (c) { @@ -223,7 +223,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), radix = 1024; state = State.BytesBase; }, - '0' ... '9' => { + '0'...'9' => { width_start = i; state = State.BytesWidth; }, @@ -236,7 +236,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), state = State.Start; start_index = i + 1; }, - '0' ... '9' => { + '0'...'9' => { width_start = i; state = State.BytesWidth; }, @@ -250,7 +250,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), state = State.Start; start_index = i + 1; }, - '0' ... '9' => {}, + '0'...'9' => {}, else => @compileError("Unexpected character in format string: " ++ []u8{c}), }, } @@ -562,9 +562,14 @@ pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, com } } -pub fn formatBytes(value: var, width: ?usize, comptime radix: usize, - context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void -{ +pub fn formatBytes( + value: var, + width: ?usize, + comptime radix: usize, + context: var, + comptime Errors: type, + output: fn(@typeOf(context), []const u8) Errors!void, +) Errors!void { if (value == 0) { return output(context, "0B"); } @@ -585,16 +590,22 @@ pub fn formatBytes(value: var, width: ?usize, comptime radix: usize, } const buf = switch (radix) { - 1000 => []u8 { suffix, 'B' }, - 1024 => []u8 { suffix, 'i', 'B' }, + 1000 => []u8{ suffix, 'B' }, + 1024 => []u8{ suffix, 'i', 'B' }, else => unreachable, }; return output(context, buf); } -pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, - context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void -{ +pub fn formatInt( + value: var, + base: u8, + uppercase: bool, + width: usize, + context: var, + comptime Errors: type, + output: fn(@typeOf(context), []const u8) Errors!void, +) Errors!void { if (@typeOf(value).is_signed) { return formatIntSigned(value, base, uppercase, width, context, Errors, output); } else { @@ -717,9 +728,9 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { const value = switch (c) { - '0' ... '9' => c - '0', - 'A' ... 'Z' => c - 'A' + 10, - 'a' ... 'z' => c - 'a' + 10, + '0'...'9' => c - '0', + 'A'...'Z' => c - 'A' + 10, + 'a'...'z' => c - 'a' + 10, else => return error.InvalidCharacter, }; @@ -730,8 +741,8 @@ pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { fn digitToChar(digit: u8, uppercase: bool) u8 { return switch (digit) { - 0 ... 9 => digit + '0', - 10 ... 35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10), + 0...9 => digit + '0', + 10...35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10), else => unreachable, }; } @@ -754,8 +765,7 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { var size: usize = 0; - format(&size, error{}, countSize, fmt, args) catch |err| switch (err) { - }; + format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; const buf = try allocator.alloc(u8, size); return bufPrint(buf, fmt, args); } @@ -1043,8 +1053,7 @@ test "fmt.format" { fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { var buf: [100]u8 = undefined; const result = try bufPrint(buf[0..], template, args); - if (mem.eql(u8, result, expected)) - return; + if (mem.eql(u8, result, expected)) return; std.debug.warn("\n====== expected this output: =========\n"); std.debug.warn("{}", expected); @@ -1082,10 +1091,7 @@ test "fmt.trim" { pub fn isWhiteSpace(byte: u8) bool { return switch (byte) { - ' ', - '\t', - '\n', - '\r' => true, + ' ', '\t', '\n', '\r' => true, else => false, }; } diff --git a/std/hash/adler.zig b/std/hash/adler.zig index c77a5aaf50..12dab1457c 100644 --- a/std/hash/adler.zig +++ b/std/hash/adler.zig @@ -13,9 +13,7 @@ pub const Adler32 = struct { adler: u32, pub fn init() Adler32 { - return Adler32 { - .adler = 1, - }; + return Adler32{ .adler = 1 }; } // This fast variant is taken from zlib. It reduces the required modulos and unrolls longer @@ -33,8 +31,7 @@ pub const Adler32 = struct { if (s2 >= base) { s2 -= base; } - } - else if (input.len < 16) { + } else if (input.len < 16) { for (input) |b| { s1 +%= b; s2 +%= s1; @@ -44,8 +41,7 @@ pub const Adler32 = struct { } s2 %= base; - } - else { + } else { var i: usize = 0; while (i + nmax <= input.len) : (i += nmax) { const n = nmax / 16; // note: 16 | nmax @@ -98,15 +94,14 @@ test "adler32 sanity" { } test "adler32 long" { - const long1 = []u8 {1} ** 1024; + const long1 = []u8{1} ** 1024; debug.assert(Adler32.hash(long1[0..]) == 0x06780401); - const long2 = []u8 {1} ** 1025; + const long2 = []u8{1} ** 1025; debug.assert(Adler32.hash(long2[0..]) == 0x0a7a0402); } test "adler32 very long" { - const long = []u8 {1} ** 5553; + const long = []u8{1} ** 5553; debug.assert(Adler32.hash(long[0..]) == 0x707f15b2); } - diff --git a/std/hash/crc.zig b/std/hash/crc.zig index e4c1405a1c..72c4e467c2 100644 --- a/std/hash/crc.zig +++ b/std/hash/crc.zig @@ -69,7 +69,6 @@ pub fn Crc32WithPoly(comptime poly: u32) type { self.crc ^= (u32(p[2]) << 16); self.crc ^= (u32(p[3]) << 24); - self.crc = lookup_tables[0][p[7]] ^ lookup_tables[1][p[6]] ^ @@ -77,8 +76,8 @@ pub fn Crc32WithPoly(comptime poly: u32) type { lookup_tables[3][p[4]] ^ lookup_tables[4][@truncate(u8, self.crc >> 24)] ^ lookup_tables[5][@truncate(u8, self.crc >> 16)] ^ - lookup_tables[6][@truncate(u8, self.crc >> 8)] ^ - lookup_tables[7][@truncate(u8, self.crc >> 0)]; + lookup_tables[6][@truncate(u8, self.crc >> 8)] ^ + lookup_tables[7][@truncate(u8, self.crc >> 0)]; } while (i < input.len) : (i += 1) { diff --git a/std/hash/fnv.zig b/std/hash/fnv.zig index 88b965b76a..c2439e0ebc 100644 --- a/std/hash/fnv.zig +++ b/std/hash/fnv.zig @@ -7,7 +7,7 @@ const std = @import("../index.zig"); const debug = std.debug; -pub const Fnv1a_32 = Fnv1a(u32, 0x01000193 , 0x811c9dc5); +pub const Fnv1a_32 = Fnv1a(u32, 0x01000193, 0x811c9dc5); pub const Fnv1a_64 = Fnv1a(u64, 0x100000001b3, 0xcbf29ce484222325); pub const Fnv1a_128 = Fnv1a(u128, 0x1000000000000000000013b, 0x6c62272e07bb014262b821756295c58d); @@ -18,9 +18,7 @@ fn Fnv1a(comptime T: type, comptime prime: T, comptime offset: T) type { value: T, pub fn init() Self { - return Self { - .value = offset, - }; + return Self{ .value = offset }; } pub fn update(self: &Self, input: []const u8) void { diff --git a/std/hash/siphash.zig b/std/hash/siphash.zig index 301c35cf05..b75866a403 100644 --- a/std/hash/siphash.zig +++ b/std/hash/siphash.zig @@ -45,7 +45,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) const k0 = mem.readInt(key[0..8], u64, Endian.Little); const k1 = mem.readInt(key[8..16], u64, Endian.Little); - var d = Self { + var d = Self{ .v0 = k0 ^ 0x736f6d6570736575, .v1 = k1 ^ 0x646f72616e646f6d, .v2 = k0 ^ 0x6c7967656e657261, @@ -162,7 +162,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) const test_key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"; test "siphash64-2-4 sanity" { - const vectors = [][]const u8 { + const vectors = [][]const u8{ "\x31\x0e\x0e\xdd\x47\xdb\x6f\x72", // "" "\xfd\x67\xdc\x93\xc5\x39\xf8\x74", // "\x00" "\x5a\x4f\xa9\xd9\x09\x80\x6c\x0d", // "\x00\x01" ... etc @@ -241,7 +241,7 @@ test "siphash64-2-4 sanity" { } test "siphash128-2-4 sanity" { - const vectors = [][]const u8 { + const vectors = [][]const u8{ "\xa3\x81\x7f\x04\xba\x25\xa8\xe6\x6d\xf6\x72\x14\xc7\x55\x02\x93", "\xda\x87\xc1\xd8\x6b\x99\xaf\x44\x34\x76\x59\x11\x9b\x22\xfc\x45", "\x81\x77\x22\x8d\xa4\xa4\x5d\xc7\xfc\xa3\x8b\xde\xf6\x0a\xff\xe4", diff --git a/std/heap.zig b/std/heap.zig index 7b753524a6..8d4938a7c3 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -68,9 +68,7 @@ pub const DirectAllocator = struct { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { - Os.linux, - Os.macosx, - Os.ios => { + Os.linux, Os.macosx, Os.ios => { const p = os.posix; const alloc_size = if (alignment <= os.page_size) n else n + alignment; const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0); @@ -121,9 +119,7 @@ pub const DirectAllocator = struct { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { - Os.linux, - Os.macosx, - Os.ios => { + Os.linux, Os.macosx, Os.ios => { if (new_size <= old_mem.len) { const base_addr = @ptrToInt(old_mem.ptr); const old_addr_end = base_addr + old_mem.len; @@ -168,9 +164,7 @@ pub const DirectAllocator = struct { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { - Os.linux, - Os.macosx, - Os.ios => { + Os.linux, Os.macosx, Os.ios => { _ = os.posix.munmap(@ptrToInt(bytes.ptr), bytes.len); }, Os.windows => { @@ -430,7 +424,7 @@ fn testAllocator(allocator: &mem.Allocator) !void { } fn testAllocatorLargeAlignment(allocator: &mem.Allocator) mem.Allocator.Error!void { - //Maybe a platform's page_size is actually the same as or + //Maybe a platform's page_size is actually the same as or // very near usize? if (os.page_size << 2 > @maxValue(usize)) return; diff --git a/std/io_test.zig b/std/io_test.zig index 5f53556785..ca4eeb3aaa 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -42,7 +42,7 @@ test "write a file, read it, then delete it" { assert(mem.eql(u8, contents[0.."begin".len], "begin")); assert(mem.eql(u8, contents["begin".len..contents.len - "end".len], data)); - assert(mem.eql(u8, contents[contents.len - "end".len ..], "end")); + assert(mem.eql(u8, contents[contents.len - "end".len..], "end")); } try os.deleteFile(allocator, tmp_file_name); } diff --git a/std/json.zig b/std/json.zig index e969899403..c88ce59139 100644 --- a/std/json.zig +++ b/std/json.zig @@ -252,7 +252,7 @@ pub const StreamingJsonParser = struct { p.after_value_state = State.TopLevelEnd; p.count = 0; }, - '1' ... '9' => { + '1'...'9' => { p.number_is_integer = true; p.state = State.NumberMaybeDigitOrDotOrExponent; p.after_value_state = State.TopLevelEnd; @@ -281,10 +281,7 @@ pub const StreamingJsonParser = struct { p.after_value_state = State.TopLevelEnd; p.count = 0; }, - 0x09, - 0x0A, - 0x0D, - 0x20 => { + 0x09, 0x0A, 0x0D, 0x20 => { // whitespace }, else => { @@ -293,10 +290,7 @@ pub const StreamingJsonParser = struct { }, State.TopLevelEnd => switch (c) { - 0x09, - 0x0A, - 0x0D, - 0x20 => { + 0x09, 0x0A, 0x0D, 0x20 => { // whitespace }, else => { @@ -392,7 +386,7 @@ pub const StreamingJsonParser = struct { p.state = State.NumberMaybeDotOrExponent; p.count = 0; }, - '1' ... '9' => { + '1'...'9' => { p.state = State.NumberMaybeDigitOrDotOrExponent; p.count = 0; }, @@ -412,10 +406,7 @@ pub const StreamingJsonParser = struct { p.state = State.NullLiteral1; p.count = 0; }, - 0x09, - 0x0A, - 0x0D, - 0x20 => { + 0x09, 0x0A, 0x0D, 0x20 => { // whitespace }, else => { @@ -461,7 +452,7 @@ pub const StreamingJsonParser = struct { p.state = State.NumberMaybeDotOrExponent; p.count = 0; }, - '1' ... '9' => { + '1'...'9' => { p.state = State.NumberMaybeDigitOrDotOrExponent; p.count = 0; }, @@ -481,10 +472,7 @@ pub const StreamingJsonParser = struct { p.state = State.NullLiteral1; p.count = 0; }, - 0x09, - 0x0A, - 0x0D, - 0x20 => { + 0x09, 0x0A, 0x0D, 0x20 => { // whitespace }, else => { @@ -533,10 +521,7 @@ pub const StreamingJsonParser = struct { token.* = Token.initMarker(Token.Id.ObjectEnd); }, - 0x09, - 0x0A, - 0x0D, - 0x20 => { + 0x09, 0x0A, 0x0D, 0x20 => { // whitespace }, else => { @@ -549,10 +534,7 @@ pub const StreamingJsonParser = struct { p.state = State.ValueBegin; p.after_string_state = State.ValueEnd; }, - 0x09, - 0x0A, - 0x0D, - 0x20 => { + 0x09, 0x0A, 0x0D, 0x20 => { // whitespace }, else => { @@ -561,7 +543,7 @@ pub const StreamingJsonParser = struct { }, State.String => switch (c) { - 0x00 ... 0x1F => { + 0x00...0x1F => { return error.InvalidControlCharacter; }, '"' => { @@ -576,19 +558,16 @@ pub const StreamingJsonParser = struct { '\\' => { p.state = State.StringEscapeCharacter; }, - 0x20, - 0x21, - 0x23 ... 0x5B, - 0x5D ... 0x7F => { + 0x20, 0x21, 0x23...0x5B, 0x5D...0x7F => { // non-control ascii }, - 0xC0 ... 0xDF => { + 0xC0...0xDF => { p.state = State.StringUtf8Byte1; }, - 0xE0 ... 0xEF => { + 0xE0...0xEF => { p.state = State.StringUtf8Byte2; }, - 0xF0 ... 0xFF => { + 0xF0...0xFF => { p.state = State.StringUtf8Byte3; }, else => { @@ -620,14 +599,7 @@ pub const StreamingJsonParser = struct { // The current JSONTestSuite tests rely on both of this behaviour being present // however, so we default to the status quo where both are accepted until this // is further clarified. - '"', - '\\', - '/', - 'b', - 'f', - 'n', - 'r', - 't' => { + '"', '\\', '/', 'b', 'f', 'n', 'r', 't' => { p.string_has_escape = true; p.state = State.String; }, @@ -641,36 +613,28 @@ pub const StreamingJsonParser = struct { }, State.StringEscapeHexUnicode4 => switch (c) { - '0' ... '9', - 'A' ... 'F', - 'a' ... 'f' => { + '0'...'9', 'A'...'F', 'a'...'f' => { p.state = State.StringEscapeHexUnicode3; }, else => return error.InvalidUnicodeHexSymbol, }, State.StringEscapeHexUnicode3 => switch (c) { - '0' ... '9', - 'A' ... 'F', - 'a' ... 'f' => { + '0'...'9', 'A'...'F', 'a'...'f' => { p.state = State.StringEscapeHexUnicode2; }, else => return error.InvalidUnicodeHexSymbol, }, State.StringEscapeHexUnicode2 => switch (c) { - '0' ... '9', - 'A' ... 'F', - 'a' ... 'f' => { + '0'...'9', 'A'...'F', 'a'...'f' => { p.state = State.StringEscapeHexUnicode1; }, else => return error.InvalidUnicodeHexSymbol, }, State.StringEscapeHexUnicode1 => switch (c) { - '0' ... '9', - 'A' ... 'F', - 'a' ... 'f' => { + '0'...'9', 'A'...'F', 'a'...'f' => { p.state = State.String; }, else => return error.InvalidUnicodeHexSymbol, @@ -682,7 +646,7 @@ pub const StreamingJsonParser = struct { '0' => { p.state = State.NumberMaybeDotOrExponent; }, - '1' ... '9' => { + '1'...'9' => { p.state = State.NumberMaybeDigitOrDotOrExponent; }, else => { @@ -698,8 +662,7 @@ pub const StreamingJsonParser = struct { p.number_is_integer = false; p.state = State.NumberFractionalRequired; }, - 'e', - 'E' => { + 'e', 'E' => { p.number_is_integer = false; p.state = State.NumberExponent; }, @@ -718,12 +681,11 @@ pub const StreamingJsonParser = struct { p.number_is_integer = false; p.state = State.NumberFractionalRequired; }, - 'e', - 'E' => { + 'e', 'E' => { p.number_is_integer = false; p.state = State.NumberExponent; }, - '0' ... '9' => { + '0'...'9' => { // another digit }, else => { @@ -737,7 +699,7 @@ pub const StreamingJsonParser = struct { State.NumberFractionalRequired => { p.complete = p.after_value_state == State.TopLevelEnd; switch (c) { - '0' ... '9' => { + '0'...'9' => { p.state = State.NumberFractional; }, else => { @@ -749,11 +711,10 @@ pub const StreamingJsonParser = struct { State.NumberFractional => { p.complete = p.after_value_state == State.TopLevelEnd; switch (c) { - '0' ... '9' => { + '0'...'9' => { // another digit }, - 'e', - 'E' => { + 'e', 'E' => { p.number_is_integer = false; p.state = State.NumberExponent; }, @@ -768,8 +729,7 @@ pub const StreamingJsonParser = struct { State.NumberMaybeExponent => { p.complete = p.after_value_state == State.TopLevelEnd; switch (c) { - 'e', - 'E' => { + 'e', 'E' => { p.number_is_integer = false; p.state = State.NumberExponent; }, @@ -782,12 +742,11 @@ pub const StreamingJsonParser = struct { }, State.NumberExponent => switch (c) { - '-', - '+' => { + '-', '+' => { p.complete = false; p.state = State.NumberExponentDigitsRequired; }, - '0' ... '9' => { + '0'...'9' => { p.complete = p.after_value_state == State.TopLevelEnd; p.state = State.NumberExponentDigits; }, @@ -797,7 +756,7 @@ pub const StreamingJsonParser = struct { }, State.NumberExponentDigitsRequired => switch (c) { - '0' ... '9' => { + '0'...'9' => { p.complete = p.after_value_state == State.TopLevelEnd; p.state = State.NumberExponentDigits; }, @@ -809,7 +768,7 @@ pub const StreamingJsonParser = struct { State.NumberExponentDigits => { p.complete = p.after_value_state == State.TopLevelEnd; switch (c) { - '0' ... '9' => { + '0'...'9' => { // another digit }, else => { @@ -1257,8 +1216,7 @@ pub const JsonParser = struct { Token.Id.Null => { try p.stack.append(Value.Null); }, - Token.Id.ObjectEnd, - Token.Id.ArrayEnd => { + Token.Id.ObjectEnd, Token.Id.ArrayEnd => { unreachable; }, }, diff --git a/std/macho.zig b/std/macho.zig index 70e2c09788..615569e4b4 100644 --- a/std/macho.zig +++ b/std/macho.zig @@ -58,15 +58,15 @@ pub const SymbolTable = struct { // code, its displacement is different. pub fn deinit(self: &SymbolTable) void { self.allocator.free(self.symbols); - self.symbols = []const Symbol {}; + self.symbols = []const Symbol{}; self.allocator.free(self.strings); - self.strings = []const u8 {}; + self.strings = []const u8{}; } pub fn search(self: &const SymbolTable, address: usize) ?&const Symbol { var min: usize = 0; - var max: usize = self.symbols.len - 1; // Exclude sentinel. + var max: usize = self.symbols.len - 1; // Exclude sentinel. while (min < max) { const mid = min + (max - min) / 2; const curr = &self.symbols[mid]; @@ -118,10 +118,11 @@ pub fn loadSymbols(allocator: &mem.Allocator, in: &io.FileInStream) !SymbolTable try in.stream.readNoEof(strings); var nsyms: usize = 0; - for (syms) |sym| if (isSymbol(sym)) nsyms += 1; + for (syms) |sym| + if (isSymbol(sym)) nsyms += 1; if (nsyms == 0) return error.MissingDebugInfo; - var symbols = try allocator.alloc(Symbol, nsyms + 1); // Room for sentinel. + var symbols = try allocator.alloc(Symbol, nsyms + 1); // Room for sentinel. errdefer allocator.free(symbols); var pie_slide: usize = 0; @@ -132,7 +133,7 @@ pub fn loadSymbols(allocator: &mem.Allocator, in: &io.FileInStream) !SymbolTable const end = ??mem.indexOfScalarPos(u8, strings, start, 0); const name = strings[start..end]; const address = sym.n_value; - symbols[nsym] = Symbol { .name = name, .address = address }; + symbols[nsym] = Symbol{ .name = name, .address = address }; nsym += 1; if (is_pie and mem.eql(u8, name, "_SymbolTable_deinit")) { pie_slide = @ptrToInt(SymbolTable.deinit) - address; @@ -145,13 +146,14 @@ pub fn loadSymbols(allocator: &mem.Allocator, in: &io.FileInStream) !SymbolTable // Insert the sentinel. Since we don't know where the last function ends, // we arbitrarily limit it to the start address + 4 KB. const top = symbols[nsyms - 1].address + 4096; - symbols[nsyms] = Symbol { .name = "", .address = top }; + symbols[nsyms] = Symbol{ .name = "", .address = top }; if (pie_slide != 0) { - for (symbols) |*symbol| symbol.address += pie_slide; + for (symbols) |*symbol| + symbol.address += pie_slide; } - return SymbolTable { + return SymbolTable{ .allocator = allocator, .symbols = symbols, .strings = strings, diff --git a/std/math/atan.zig b/std/math/atan.zig index c315adc42d..7eceef98e3 100644 --- a/std/math/atan.zig +++ b/std/math/atan.zig @@ -17,25 +17,25 @@ pub fn atan(x: var) @typeOf(x) { } fn atan32(x_: f32) f32 { - const atanhi = []const f32 { + const atanhi = []const f32{ 4.6364760399e-01, // atan(0.5)hi 7.8539812565e-01, // atan(1.0)hi 9.8279368877e-01, // atan(1.5)hi 1.5707962513e+00, // atan(inf)hi }; - const atanlo = []const f32 { + const atanlo = []const f32{ 5.0121582440e-09, // atan(0.5)lo 3.7748947079e-08, // atan(1.0)lo 3.4473217170e-08, // atan(1.5)lo 7.5497894159e-08, // atan(inf)lo }; - const aT = []const f32 { + const aT = []const f32{ 3.3333328366e-01, - -1.9999158382e-01, + -1.9999158382e-01, 1.4253635705e-01, - -1.0648017377e-01, + -1.0648017377e-01, 6.1687607318e-02, }; @@ -80,8 +80,7 @@ fn atan32(x_: f32) f32 { id = 1; x = (x - 1.0) / (x + 1.0); } - } - else { + } else { // |x| < 2.4375 if (ix < 0x401C0000) { id = 2; @@ -109,31 +108,31 @@ fn atan32(x_: f32) f32 { } fn atan64(x_: f64) f64 { - const atanhi = []const f64 { + const atanhi = []const f64{ 4.63647609000806093515e-01, // atan(0.5)hi 7.85398163397448278999e-01, // atan(1.0)hi 9.82793723247329054082e-01, // atan(1.5)hi 1.57079632679489655800e+00, // atan(inf)hi }; - const atanlo = []const f64 { + const atanlo = []const f64{ 2.26987774529616870924e-17, // atan(0.5)lo 3.06161699786838301793e-17, // atan(1.0)lo 1.39033110312309984516e-17, // atan(1.5)lo 6.12323399573676603587e-17, // atan(inf)lo }; - const aT = []const f64 { + const aT = []const f64{ 3.33333333333329318027e-01, - -1.99999999998764832476e-01, + -1.99999999998764832476e-01, 1.42857142725034663711e-01, - -1.11111104054623557880e-01, + -1.11111104054623557880e-01, 9.09088713343650656196e-02, - -7.69187620504482999495e-02, + -7.69187620504482999495e-02, 6.66107313738753120669e-02, - -5.83357013379057348645e-02, + -5.83357013379057348645e-02, 4.97687799461593236017e-02, - -3.65315727442169155270e-02, + -3.65315727442169155270e-02, 1.62858201153657823623e-02, }; @@ -179,8 +178,7 @@ fn atan64(x_: f64) f64 { id = 1; x = (x - 1.0) / (x + 1.0); } - } - else { + } else { // |x| < 2.4375 if (ix < 0x40038000) { id = 2; diff --git a/std/math/atan2.zig b/std/math/atan2.zig index 0892f0b438..f0a8e67c46 100644 --- a/std/math/atan2.zig +++ b/std/math/atan2.zig @@ -53,8 +53,7 @@ fn atan2_32(y: f32, x: f32) f32 { if (iy == 0) { switch (m) { - 0, - 1 => return y, // atan(+-0, +...) + 0, 1 => return y, // atan(+-0, +...) 2 => return pi, // atan(+0, -...) 3 => return -pi, // atan(-0, -...) else => unreachable, @@ -144,8 +143,7 @@ fn atan2_64(y: f64, x: f64) f64 { if (iy | ly == 0) { switch (m) { - 0, - 1 => return y, // atan(+-0, +...) + 0, 1 => return y, // atan(+-0, +...) 2 => return pi, // atan(+0, -...) 3 => return -pi, // atan(-0, -...) else => unreachable, diff --git a/std/math/complex/index.zig b/std/math/complex/index.zig index a4d493307e..5902ffaa19 100644 --- a/std/math/complex/index.zig +++ b/std/math/complex/index.zig @@ -31,28 +31,28 @@ pub fn Complex(comptime T: type) type { im: T, pub fn new(re: T, im: T) Self { - return Self { + return Self{ .re = re, .im = im, }; } pub fn add(self: &const Self, other: &const Self) Self { - return Self { + return Self{ .re = self.re + other.re, .im = self.im + other.im, }; } pub fn sub(self: &const Self, other: &const Self) Self { - return Self { + return Self{ .re = self.re - other.re, .im = self.im - other.im, }; } pub fn mul(self: &const Self, other: &const Self) Self { - return Self { + return Self{ .re = self.re * other.re - self.im * other.im, .im = self.im * other.re + self.re * other.im, }; @@ -63,14 +63,14 @@ pub fn Complex(comptime T: type) type { const im_num = self.im * other.re - self.re * other.im; const den = other.re * other.re + other.im * other.im; - return Self { + return Self{ .re = re_num / den, .im = im_num / den, }; } pub fn conjugate(self: &const Self) Self { - return Self { + return Self{ .re = self.re, .im = -self.im, }; @@ -78,7 +78,7 @@ pub fn Complex(comptime T: type) type { pub fn reciprocal(self: &const Self) Self { const m = self.re * self.re + self.im * self.im; - return Self { + return Self{ .re = self.re / m, .im = -self.im / m, }; @@ -121,8 +121,8 @@ test "complex.div" { const b = Complex(f32).new(2, 7); const c = a.div(b); - debug.assert(math.approxEq(f32, c.re, f32(31)/53, epsilon) and - math.approxEq(f32, c.im, f32(-29)/53, epsilon)); + debug.assert(math.approxEq(f32, c.re, f32(31) / 53, epsilon) and + math.approxEq(f32, c.im, f32(-29) / 53, epsilon)); } test "complex.conjugate" { @@ -136,8 +136,8 @@ test "complex.reciprocal" { const a = Complex(f32).new(5, 3); const c = a.reciprocal(); - debug.assert(math.approxEq(f32, c.re, f32(5)/34, epsilon) and - math.approxEq(f32, c.im, f32(-3)/34, epsilon)); + debug.assert(math.approxEq(f32, c.re, f32(5) / 34, epsilon) and + math.approxEq(f32, c.im, f32(-3) / 34, epsilon)); } test "complex.magnitude" { diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig index 6af62f48ae..34250b1b4a 100644 --- a/std/math/complex/tanh.zig +++ b/std/math/complex/tanh.zig @@ -98,7 +98,7 @@ test "complex.ctanh32" { const a = Complex(f32).new(5, 3); const c = tanh(a); - debug.assert(math.approxEq(f32, c.re, 0.999913, epsilon)); + debug.assert(math.approxEq(f32, c.re, 0.999913, epsilon)); debug.assert(math.approxEq(f32, c.im, -0.000025, epsilon)); } @@ -106,6 +106,6 @@ test "complex.ctanh64" { const a = Complex(f64).new(5, 3); const c = tanh(a); - debug.assert(math.approxEq(f64, c.re, 0.999913, epsilon)); + debug.assert(math.approxEq(f64, c.re, 0.999913, epsilon)); debug.assert(math.approxEq(f64, c.im, -0.000025, epsilon)); } diff --git a/std/math/exp.zig b/std/math/exp.zig index 21aa558c57..76dc47d04b 100644 --- a/std/math/exp.zig +++ b/std/math/exp.zig @@ -20,10 +20,10 @@ pub fn exp(x: var) @typeOf(x) { fn exp32(x_: f32) f32 { @setFloatMode(this, builtin.FloatMode.Strict); - const half = []f32 { 0.5, -0.5 }; + const half = []f32{ 0.5, -0.5 }; const ln2hi = 6.9314575195e-1; const ln2lo = 1.4286067653e-6; - const invln2 = 1.4426950216e+0; + const invln2 = 1.4426950216e+0; const P1 = 1.6666625440e-1; const P2 = -2.7667332906e-3; @@ -47,7 +47,7 @@ fn exp32(x_: f32) f32 { return x * 0x1.0p127; } if (sign != 0) { - math.forceEval(-0x1.0p-149 / x); // overflow + math.forceEval(-0x1.0p-149 / x); // overflow // x <= -103.972084 if (hx >= 0x42CFF1B5) { return 0; @@ -64,8 +64,7 @@ fn exp32(x_: f32) f32 { // |x| > 1.5 * ln2 if (hx > 0x3F851592) { k = i32(invln2 * x + half[usize(sign)]); - } - else { + } else { k = 1 - sign - sign; } @@ -79,8 +78,7 @@ fn exp32(x_: f32) f32 { k = 0; hi = x; lo = 0; - } - else { + } else { math.forceEval(0x1.0p127 + x); // inexact return 1 + x; } @@ -99,15 +97,15 @@ fn exp32(x_: f32) f32 { fn exp64(x_: f64) f64 { @setFloatMode(this, builtin.FloatMode.Strict); - const half = []const f64 { 0.5, -0.5 }; + const half = []const f64{ 0.5, -0.5 }; const ln2hi: f64 = 6.93147180369123816490e-01; const ln2lo: f64 = 1.90821492927058770002e-10; const invln2: f64 = 1.44269504088896338700e+00; - const P1: f64 = 1.66666666666666019037e-01; - const P2: f64 = -2.77777777770155933842e-03; - const P3: f64 = 6.61375632143793436117e-05; - const P4: f64 = -1.65339022054652515390e-06; - const P5: f64 = 4.13813679705723846039e-08; + const P1: f64 = 1.66666666666666019037e-01; + const P2: f64 = -2.77777777770155933842e-03; + const P3: f64 = 6.61375632143793436117e-05; + const P4: f64 = -1.65339022054652515390e-06; + const P5: f64 = 4.13813679705723846039e-08; var x = x_; var ux = @bitCast(u64, x); @@ -151,8 +149,7 @@ fn exp64(x_: f64) f64 { // |x| >= 1.5 * ln2 if (hx > 0x3FF0A2B2) { k = i32(invln2 * x + half[usize(sign)]); - } - else { + } else { k = 1 - sign - sign; } @@ -166,8 +163,7 @@ fn exp64(x_: f64) f64 { k = 0; hi = x; lo = 0; - } - else { + } else { // inexact if x != 0 // math.forceEval(0x1.0p1023 + x); return 1 + x; diff --git a/std/math/exp2.zig b/std/math/exp2.zig index 790bd1a558..62a3eb85b6 100644 --- a/std/math/exp2.zig +++ b/std/math/exp2.zig @@ -16,7 +16,7 @@ pub fn exp2(x: var) @typeOf(x) { }; } -const exp2ft = []const f64 { +const exp2ft = []const f64{ 0x1.6a09e667f3bcdp-1, 0x1.7a11473eb0187p-1, 0x1.8ace5422aa0dbp-1, @@ -92,195 +92,195 @@ fn exp2_32(x: f32) f32 { return f32(r * uk); } -const exp2dt = []f64 { +const exp2dt = []f64{ // exp2(z + eps) eps - 0x1.6a09e667f3d5dp-1, 0x1.9880p-44, - 0x1.6b052fa751744p-1, 0x1.8000p-50, + 0x1.6a09e667f3d5dp-1, 0x1.9880p-44, + 0x1.6b052fa751744p-1, 0x1.8000p-50, 0x1.6c012750bd9fep-1, -0x1.8780p-45, - 0x1.6cfdcddd476bfp-1, 0x1.ec00p-46, + 0x1.6cfdcddd476bfp-1, 0x1.ec00p-46, 0x1.6dfb23c651a29p-1, -0x1.8000p-50, 0x1.6ef9298593ae3p-1, -0x1.c000p-52, 0x1.6ff7df9519386p-1, -0x1.fd80p-45, 0x1.70f7466f42da3p-1, -0x1.c880p-45, - 0x1.71f75e8ec5fc3p-1, 0x1.3c00p-46, + 0x1.71f75e8ec5fc3p-1, 0x1.3c00p-46, 0x1.72f8286eacf05p-1, -0x1.8300p-44, 0x1.73f9a48a58152p-1, -0x1.0c00p-47, - 0x1.74fbd35d7ccfcp-1, 0x1.f880p-45, - 0x1.75feb564267f1p-1, 0x1.3e00p-47, + 0x1.74fbd35d7ccfcp-1, 0x1.f880p-45, + 0x1.75feb564267f1p-1, 0x1.3e00p-47, 0x1.77024b1ab6d48p-1, -0x1.7d00p-45, 0x1.780694fde5d38p-1, -0x1.d000p-50, - 0x1.790b938ac1d00p-1, 0x1.3000p-49, + 0x1.790b938ac1d00p-1, 0x1.3000p-49, 0x1.7a11473eb0178p-1, -0x1.d000p-49, - 0x1.7b17b0976d060p-1, 0x1.0400p-45, - 0x1.7c1ed0130c133p-1, 0x1.0000p-53, + 0x1.7b17b0976d060p-1, 0x1.0400p-45, + 0x1.7c1ed0130c133p-1, 0x1.0000p-53, 0x1.7d26a62ff8636p-1, -0x1.6900p-45, 0x1.7e2f336cf4e3bp-1, -0x1.2e00p-47, 0x1.7f3878491c3e8p-1, -0x1.4580p-45, - 0x1.80427543e1b4ep-1, 0x1.3000p-44, - 0x1.814d2add1071ap-1, 0x1.f000p-47, + 0x1.80427543e1b4ep-1, 0x1.3000p-44, + 0x1.814d2add1071ap-1, 0x1.f000p-47, 0x1.82589994ccd7ep-1, -0x1.1c00p-45, - 0x1.8364c1eb942d0p-1, 0x1.9d00p-45, - 0x1.8471a4623cab5p-1, 0x1.7100p-43, - 0x1.857f4179f5bbcp-1, 0x1.2600p-45, + 0x1.8364c1eb942d0p-1, 0x1.9d00p-45, + 0x1.8471a4623cab5p-1, 0x1.7100p-43, + 0x1.857f4179f5bbcp-1, 0x1.2600p-45, 0x1.868d99b4491afp-1, -0x1.2c40p-44, 0x1.879cad931a395p-1, -0x1.3000p-45, 0x1.88ac7d98a65b8p-1, -0x1.a800p-45, 0x1.89bd0a4785800p-1, -0x1.d000p-49, - 0x1.8ace5422aa223p-1, 0x1.3280p-44, - 0x1.8be05bad619fap-1, 0x1.2b40p-43, + 0x1.8ace5422aa223p-1, 0x1.3280p-44, + 0x1.8be05bad619fap-1, 0x1.2b40p-43, 0x1.8cf3216b54383p-1, -0x1.ed00p-45, 0x1.8e06a5e08664cp-1, -0x1.0500p-45, - 0x1.8f1ae99157807p-1, 0x1.8280p-45, + 0x1.8f1ae99157807p-1, 0x1.8280p-45, 0x1.902fed0282c0ep-1, -0x1.cb00p-46, 0x1.9145b0b91ff96p-1, -0x1.5e00p-47, - 0x1.925c353aa2ff9p-1, 0x1.5400p-48, - 0x1.93737b0cdc64ap-1, 0x1.7200p-46, + 0x1.925c353aa2ff9p-1, 0x1.5400p-48, + 0x1.93737b0cdc64ap-1, 0x1.7200p-46, 0x1.948b82b5f98aep-1, -0x1.9000p-47, - 0x1.95a44cbc852cbp-1, 0x1.5680p-45, + 0x1.95a44cbc852cbp-1, 0x1.5680p-45, 0x1.96bdd9a766f21p-1, -0x1.6d00p-44, 0x1.97d829fde4e2ap-1, -0x1.1000p-47, - 0x1.98f33e47a23a3p-1, 0x1.d000p-45, + 0x1.98f33e47a23a3p-1, 0x1.d000p-45, 0x1.9a0f170ca0604p-1, -0x1.8a40p-44, - 0x1.9b2bb4d53ff89p-1, 0x1.55c0p-44, - 0x1.9c49182a3f15bp-1, 0x1.6b80p-45, + 0x1.9b2bb4d53ff89p-1, 0x1.55c0p-44, + 0x1.9c49182a3f15bp-1, 0x1.6b80p-45, 0x1.9d674194bb8c5p-1, -0x1.c000p-49, - 0x1.9e86319e3238ep-1, 0x1.7d00p-46, - 0x1.9fa5e8d07f302p-1, 0x1.6400p-46, + 0x1.9e86319e3238ep-1, 0x1.7d00p-46, + 0x1.9fa5e8d07f302p-1, 0x1.6400p-46, 0x1.a0c667b5de54dp-1, -0x1.5000p-48, - 0x1.a1e7aed8eb8f6p-1, 0x1.9e00p-47, - 0x1.a309bec4a2e27p-1, 0x1.ad80p-45, + 0x1.a1e7aed8eb8f6p-1, 0x1.9e00p-47, + 0x1.a309bec4a2e27p-1, 0x1.ad80p-45, 0x1.a42c980460a5dp-1, -0x1.af00p-46, - 0x1.a5503b23e259bp-1, 0x1.b600p-47, - 0x1.a674a8af46213p-1, 0x1.8880p-44, - 0x1.a799e1330b3a7p-1, 0x1.1200p-46, - 0x1.a8bfe53c12e8dp-1, 0x1.6c00p-47, + 0x1.a5503b23e259bp-1, 0x1.b600p-47, + 0x1.a674a8af46213p-1, 0x1.8880p-44, + 0x1.a799e1330b3a7p-1, 0x1.1200p-46, + 0x1.a8bfe53c12e8dp-1, 0x1.6c00p-47, 0x1.a9e6b5579fcd2p-1, -0x1.9b80p-45, - 0x1.ab0e521356fb8p-1, 0x1.b700p-45, - 0x1.ac36bbfd3f381p-1, 0x1.9000p-50, - 0x1.ad5ff3a3c2780p-1, 0x1.4000p-49, + 0x1.ab0e521356fb8p-1, 0x1.b700p-45, + 0x1.ac36bbfd3f381p-1, 0x1.9000p-50, + 0x1.ad5ff3a3c2780p-1, 0x1.4000p-49, 0x1.ae89f995ad2a3p-1, -0x1.c900p-45, - 0x1.afb4ce622f367p-1, 0x1.6500p-46, - 0x1.b0e07298db790p-1, 0x1.fd40p-45, - 0x1.b20ce6c9a89a9p-1, 0x1.2700p-46, - 0x1.b33a2b84f1a4bp-1, 0x1.d470p-43, + 0x1.afb4ce622f367p-1, 0x1.6500p-46, + 0x1.b0e07298db790p-1, 0x1.fd40p-45, + 0x1.b20ce6c9a89a9p-1, 0x1.2700p-46, + 0x1.b33a2b84f1a4bp-1, 0x1.d470p-43, 0x1.b468415b747e7p-1, -0x1.8380p-44, - 0x1.b59728de5593ap-1, 0x1.8000p-54, - 0x1.b6c6e29f1c56ap-1, 0x1.ad00p-47, - 0x1.b7f76f2fb5e50p-1, 0x1.e800p-50, + 0x1.b59728de5593ap-1, 0x1.8000p-54, + 0x1.b6c6e29f1c56ap-1, 0x1.ad00p-47, + 0x1.b7f76f2fb5e50p-1, 0x1.e800p-50, 0x1.b928cf22749b2p-1, -0x1.4c00p-47, 0x1.ba5b030a10603p-1, -0x1.d700p-47, - 0x1.bb8e0b79a6f66p-1, 0x1.d900p-47, - 0x1.bcc1e904bc1ffp-1, 0x1.2a00p-47, + 0x1.bb8e0b79a6f66p-1, 0x1.d900p-47, + 0x1.bcc1e904bc1ffp-1, 0x1.2a00p-47, 0x1.bdf69c3f3a16fp-1, -0x1.f780p-46, 0x1.bf2c25bd71db8p-1, -0x1.0a00p-46, 0x1.c06286141b2e9p-1, -0x1.1400p-46, - 0x1.c199bdd8552e0p-1, 0x1.be00p-47, + 0x1.c199bdd8552e0p-1, 0x1.be00p-47, 0x1.c2d1cd9fa64eep-1, -0x1.9400p-47, 0x1.c40ab5fffd02fp-1, -0x1.ed00p-47, - 0x1.c544778fafd15p-1, 0x1.9660p-44, + 0x1.c544778fafd15p-1, 0x1.9660p-44, 0x1.c67f12e57d0cbp-1, -0x1.a100p-46, 0x1.c7ba88988c1b6p-1, -0x1.8458p-42, 0x1.c8f6d9406e733p-1, -0x1.a480p-46, - 0x1.ca3405751c4dfp-1, 0x1.b000p-51, - 0x1.cb720dcef9094p-1, 0x1.1400p-47, - 0x1.ccb0f2e6d1689p-1, 0x1.0200p-48, - 0x1.cdf0b555dc412p-1, 0x1.3600p-48, + 0x1.ca3405751c4dfp-1, 0x1.b000p-51, + 0x1.cb720dcef9094p-1, 0x1.1400p-47, + 0x1.ccb0f2e6d1689p-1, 0x1.0200p-48, + 0x1.cdf0b555dc412p-1, 0x1.3600p-48, 0x1.cf3155b5bab3bp-1, -0x1.6900p-47, - 0x1.d072d4a0789bcp-1, 0x1.9a00p-47, + 0x1.d072d4a0789bcp-1, 0x1.9a00p-47, 0x1.d1b532b08c8fap-1, -0x1.5e00p-46, - 0x1.d2f87080d8a85p-1, 0x1.d280p-46, - 0x1.d43c8eacaa203p-1, 0x1.1a00p-47, - 0x1.d5818dcfba491p-1, 0x1.f000p-50, + 0x1.d2f87080d8a85p-1, 0x1.d280p-46, + 0x1.d43c8eacaa203p-1, 0x1.1a00p-47, + 0x1.d5818dcfba491p-1, 0x1.f000p-50, 0x1.d6c76e862e6a1p-1, -0x1.3a00p-47, 0x1.d80e316c9834ep-1, -0x1.cd80p-47, - 0x1.d955d71ff6090p-1, 0x1.4c00p-48, - 0x1.da9e603db32aep-1, 0x1.f900p-48, - 0x1.dbe7cd63a8325p-1, 0x1.9800p-49, + 0x1.d955d71ff6090p-1, 0x1.4c00p-48, + 0x1.da9e603db32aep-1, 0x1.f900p-48, + 0x1.dbe7cd63a8325p-1, 0x1.9800p-49, 0x1.dd321f301b445p-1, -0x1.5200p-48, 0x1.de7d5641c05bfp-1, -0x1.d700p-46, 0x1.dfc97337b9aecp-1, -0x1.6140p-46, - 0x1.e11676b197d5ep-1, 0x1.b480p-47, - 0x1.e264614f5a3e7p-1, 0x1.0ce0p-43, - 0x1.e3b333b16ee5cp-1, 0x1.c680p-47, + 0x1.e11676b197d5ep-1, 0x1.b480p-47, + 0x1.e264614f5a3e7p-1, 0x1.0ce0p-43, + 0x1.e3b333b16ee5cp-1, 0x1.c680p-47, 0x1.e502ee78b3fb4p-1, -0x1.9300p-47, 0x1.e653924676d68p-1, -0x1.5000p-49, 0x1.e7a51fbc74c44p-1, -0x1.7f80p-47, 0x1.e8f7977cdb726p-1, -0x1.3700p-48, - 0x1.ea4afa2a490e8p-1, 0x1.5d00p-49, - 0x1.eb9f4867ccae4p-1, 0x1.61a0p-46, - 0x1.ecf482d8e680dp-1, 0x1.5500p-48, - 0x1.ee4aaa2188514p-1, 0x1.6400p-51, + 0x1.ea4afa2a490e8p-1, 0x1.5d00p-49, + 0x1.eb9f4867ccae4p-1, 0x1.61a0p-46, + 0x1.ecf482d8e680dp-1, 0x1.5500p-48, + 0x1.ee4aaa2188514p-1, 0x1.6400p-51, 0x1.efa1bee615a13p-1, -0x1.e800p-49, 0x1.f0f9c1cb64106p-1, -0x1.a880p-48, 0x1.f252b376bb963p-1, -0x1.c900p-45, - 0x1.f3ac948dd7275p-1, 0x1.a000p-53, + 0x1.f3ac948dd7275p-1, 0x1.a000p-53, 0x1.f50765b6e4524p-1, -0x1.4f00p-48, - 0x1.f6632798844fdp-1, 0x1.a800p-51, - 0x1.f7bfdad9cbe38p-1, 0x1.abc0p-48, + 0x1.f6632798844fdp-1, 0x1.a800p-51, + 0x1.f7bfdad9cbe38p-1, 0x1.abc0p-48, 0x1.f91d802243c82p-1, -0x1.4600p-50, 0x1.fa7c1819e908ep-1, -0x1.b0c0p-47, 0x1.fbdba3692d511p-1, -0x1.0e00p-51, 0x1.fd3c22b8f7194p-1, -0x1.0de8p-46, - 0x1.fe9d96b2a23eep-1, 0x1.e430p-49, - 0x1.0000000000000p+0, 0x0.0000p+0, + 0x1.fe9d96b2a23eep-1, 0x1.e430p-49, + 0x1.0000000000000p+0, 0x0.0000p+0, 0x1.00b1afa5abcbep+0, -0x1.3400p-52, 0x1.0163da9fb3303p+0, -0x1.2170p-46, - 0x1.02168143b0282p+0, 0x1.a400p-52, - 0x1.02c9a3e77806cp+0, 0x1.f980p-49, + 0x1.02168143b0282p+0, 0x1.a400p-52, + 0x1.02c9a3e77806cp+0, 0x1.f980p-49, 0x1.037d42e11bbcap+0, -0x1.7400p-51, - 0x1.04315e86e7f89p+0, 0x1.8300p-50, + 0x1.04315e86e7f89p+0, 0x1.8300p-50, 0x1.04e5f72f65467p+0, -0x1.a3f0p-46, 0x1.059b0d315855ap+0, -0x1.2840p-47, - 0x1.0650a0e3c1f95p+0, 0x1.1600p-48, - 0x1.0706b29ddf71ap+0, 0x1.5240p-46, + 0x1.0650a0e3c1f95p+0, 0x1.1600p-48, + 0x1.0706b29ddf71ap+0, 0x1.5240p-46, 0x1.07bd42b72a82dp+0, -0x1.9a00p-49, - 0x1.0874518759bd0p+0, 0x1.6400p-49, + 0x1.0874518759bd0p+0, 0x1.6400p-49, 0x1.092bdf66607c8p+0, -0x1.0780p-47, 0x1.09e3ecac6f383p+0, -0x1.8000p-54, - 0x1.0a9c79b1f3930p+0, 0x1.fa00p-48, + 0x1.0a9c79b1f3930p+0, 0x1.fa00p-48, 0x1.0b5586cf988fcp+0, -0x1.ac80p-48, - 0x1.0c0f145e46c8ap+0, 0x1.9c00p-50, - 0x1.0cc922b724816p+0, 0x1.5200p-47, + 0x1.0c0f145e46c8ap+0, 0x1.9c00p-50, + 0x1.0cc922b724816p+0, 0x1.5200p-47, 0x1.0d83b23395dd8p+0, -0x1.ad00p-48, - 0x1.0e3ec32d3d1f3p+0, 0x1.bac0p-46, + 0x1.0e3ec32d3d1f3p+0, 0x1.bac0p-46, 0x1.0efa55fdfa9a6p+0, -0x1.4e80p-47, 0x1.0fb66affed2f0p+0, -0x1.d300p-47, - 0x1.1073028d7234bp+0, 0x1.1500p-48, - 0x1.11301d0125b5bp+0, 0x1.c000p-49, - 0x1.11edbab5e2af9p+0, 0x1.6bc0p-46, - 0x1.12abdc06c31d5p+0, 0x1.8400p-49, + 0x1.1073028d7234bp+0, 0x1.1500p-48, + 0x1.11301d0125b5bp+0, 0x1.c000p-49, + 0x1.11edbab5e2af9p+0, 0x1.6bc0p-46, + 0x1.12abdc06c31d5p+0, 0x1.8400p-49, 0x1.136a814f2047dp+0, -0x1.ed00p-47, - 0x1.1429aaea92de9p+0, 0x1.8e00p-49, - 0x1.14e95934f3138p+0, 0x1.b400p-49, - 0x1.15a98c8a58e71p+0, 0x1.5300p-47, - 0x1.166a45471c3dfp+0, 0x1.3380p-47, - 0x1.172b83c7d5211p+0, 0x1.8d40p-45, + 0x1.1429aaea92de9p+0, 0x1.8e00p-49, + 0x1.14e95934f3138p+0, 0x1.b400p-49, + 0x1.15a98c8a58e71p+0, 0x1.5300p-47, + 0x1.166a45471c3dfp+0, 0x1.3380p-47, + 0x1.172b83c7d5211p+0, 0x1.8d40p-45, 0x1.17ed48695bb9fp+0, -0x1.5d00p-47, 0x1.18af9388c8d93p+0, -0x1.c880p-46, - 0x1.1972658375d66p+0, 0x1.1f00p-46, - 0x1.1a35beb6fcba7p+0, 0x1.0480p-46, + 0x1.1972658375d66p+0, 0x1.1f00p-46, + 0x1.1a35beb6fcba7p+0, 0x1.0480p-46, 0x1.1af99f81387e3p+0, -0x1.7390p-43, - 0x1.1bbe084045d54p+0, 0x1.4e40p-45, + 0x1.1bbe084045d54p+0, 0x1.4e40p-45, 0x1.1c82f95281c43p+0, -0x1.a200p-47, - 0x1.1d4873168b9b2p+0, 0x1.3800p-49, - 0x1.1e0e75eb44031p+0, 0x1.ac00p-49, - 0x1.1ed5022fcd938p+0, 0x1.1900p-47, + 0x1.1d4873168b9b2p+0, 0x1.3800p-49, + 0x1.1e0e75eb44031p+0, 0x1.ac00p-49, + 0x1.1ed5022fcd938p+0, 0x1.1900p-47, 0x1.1f9c18438cdf7p+0, -0x1.b780p-46, - 0x1.2063b88628d8fp+0, 0x1.d940p-45, - 0x1.212be3578a81ep+0, 0x1.8000p-50, - 0x1.21f49917ddd41p+0, 0x1.b340p-45, - 0x1.22bdda2791323p+0, 0x1.9f80p-46, + 0x1.2063b88628d8fp+0, 0x1.d940p-45, + 0x1.212be3578a81ep+0, 0x1.8000p-50, + 0x1.21f49917ddd41p+0, 0x1.b340p-45, + 0x1.22bdda2791323p+0, 0x1.9f80p-46, 0x1.2387a6e7561e7p+0, -0x1.9c80p-46, - 0x1.2451ffb821427p+0, 0x1.2300p-47, + 0x1.2451ffb821427p+0, 0x1.2300p-47, 0x1.251ce4fb2a602p+0, -0x1.3480p-46, - 0x1.25e85711eceb0p+0, 0x1.2700p-46, - 0x1.26b4565e27d16p+0, 0x1.1d00p-46, - 0x1.2780e341de00fp+0, 0x1.1ee0p-44, + 0x1.25e85711eceb0p+0, 0x1.2700p-46, + 0x1.26b4565e27d16p+0, 0x1.1d00p-46, + 0x1.2780e341de00fp+0, 0x1.1ee0p-44, 0x1.284dfe1f5633ep+0, -0x1.4c00p-46, 0x1.291ba7591bb30p+0, -0x1.3d80p-46, - 0x1.29e9df51fdf09p+0, 0x1.8b00p-47, + 0x1.29e9df51fdf09p+0, 0x1.8b00p-47, 0x1.2ab8a66d10e9bp+0, -0x1.27c0p-45, - 0x1.2b87fd0dada3ap+0, 0x1.a340p-45, + 0x1.2b87fd0dada3ap+0, 0x1.a340p-45, 0x1.2c57e39771af9p+0, -0x1.0800p-46, 0x1.2d285a6e402d9p+0, -0x1.ed00p-47, 0x1.2df961f641579p+0, -0x1.4200p-48, @@ -290,78 +290,78 @@ const exp2dt = []f64 { 0x1.31432edeea50bp+0, -0x1.0df8p-40, 0x1.32170fc4cd7b8p+0, -0x1.2480p-45, 0x1.32eb83ba8e9a2p+0, -0x1.5980p-45, - 0x1.33c08b2641766p+0, 0x1.ed00p-46, + 0x1.33c08b2641766p+0, 0x1.ed00p-46, 0x1.3496266e3fa27p+0, -0x1.c000p-50, 0x1.356c55f929f0fp+0, -0x1.0d80p-44, - 0x1.36431a2de88b9p+0, 0x1.2c80p-45, - 0x1.371a7373aaa39p+0, 0x1.0600p-45, + 0x1.36431a2de88b9p+0, 0x1.2c80p-45, + 0x1.371a7373aaa39p+0, 0x1.0600p-45, 0x1.37f26231e74fep+0, -0x1.6600p-46, 0x1.38cae6d05d838p+0, -0x1.ae00p-47, 0x1.39a401b713ec3p+0, -0x1.4720p-43, - 0x1.3a7db34e5a020p+0, 0x1.8200p-47, - 0x1.3b57fbfec6e95p+0, 0x1.e800p-44, - 0x1.3c32dc313a8f2p+0, 0x1.f800p-49, + 0x1.3a7db34e5a020p+0, 0x1.8200p-47, + 0x1.3b57fbfec6e95p+0, 0x1.e800p-44, + 0x1.3c32dc313a8f2p+0, 0x1.f800p-49, 0x1.3d0e544ede122p+0, -0x1.7a00p-46, - 0x1.3dea64c1234bbp+0, 0x1.6300p-45, + 0x1.3dea64c1234bbp+0, 0x1.6300p-45, 0x1.3ec70df1c4eccp+0, -0x1.8a60p-43, 0x1.3fa4504ac7e8cp+0, -0x1.cdc0p-44, - 0x1.40822c367a0bbp+0, 0x1.5b80p-45, - 0x1.4160a21f72e95p+0, 0x1.ec00p-46, + 0x1.40822c367a0bbp+0, 0x1.5b80p-45, + 0x1.4160a21f72e95p+0, 0x1.ec00p-46, 0x1.423fb27094646p+0, -0x1.3600p-46, - 0x1.431f5d950a920p+0, 0x1.3980p-45, - 0x1.43ffa3f84b9ebp+0, 0x1.a000p-48, + 0x1.431f5d950a920p+0, 0x1.3980p-45, + 0x1.43ffa3f84b9ebp+0, 0x1.a000p-48, 0x1.44e0860618919p+0, -0x1.6c00p-48, 0x1.45c2042a7d201p+0, -0x1.bc00p-47, 0x1.46a41ed1d0016p+0, -0x1.2800p-46, - 0x1.4786d668b3326p+0, 0x1.0e00p-44, + 0x1.4786d668b3326p+0, 0x1.0e00p-44, 0x1.486a2b5c13c00p+0, -0x1.d400p-45, - 0x1.494e1e192af04p+0, 0x1.c200p-47, + 0x1.494e1e192af04p+0, 0x1.c200p-47, 0x1.4a32af0d7d372p+0, -0x1.e500p-46, - 0x1.4b17dea6db801p+0, 0x1.7800p-47, + 0x1.4b17dea6db801p+0, 0x1.7800p-47, 0x1.4bfdad53629e1p+0, -0x1.3800p-46, - 0x1.4ce41b817c132p+0, 0x1.0800p-47, - 0x1.4dcb299fddddbp+0, 0x1.c700p-45, + 0x1.4ce41b817c132p+0, 0x1.0800p-47, + 0x1.4dcb299fddddbp+0, 0x1.c700p-45, 0x1.4eb2d81d8ab96p+0, -0x1.ce00p-46, - 0x1.4f9b2769d2d02p+0, 0x1.9200p-46, + 0x1.4f9b2769d2d02p+0, 0x1.9200p-46, 0x1.508417f4531c1p+0, -0x1.8c00p-47, 0x1.516daa2cf662ap+0, -0x1.a000p-48, - 0x1.5257de83f51eap+0, 0x1.a080p-43, + 0x1.5257de83f51eap+0, 0x1.a080p-43, 0x1.5342b569d4edap+0, -0x1.6d80p-45, 0x1.542e2f4f6ac1ap+0, -0x1.2440p-44, - 0x1.551a4ca5d94dbp+0, 0x1.83c0p-43, - 0x1.56070dde9116bp+0, 0x1.4b00p-45, - 0x1.56f4736b529dep+0, 0x1.15a0p-43, + 0x1.551a4ca5d94dbp+0, 0x1.83c0p-43, + 0x1.56070dde9116bp+0, 0x1.4b00p-45, + 0x1.56f4736b529dep+0, 0x1.15a0p-43, 0x1.57e27dbe2c40ep+0, -0x1.9e00p-45, 0x1.58d12d497c76fp+0, -0x1.3080p-45, - 0x1.59c0827ff0b4cp+0, 0x1.dec0p-43, + 0x1.59c0827ff0b4cp+0, 0x1.dec0p-43, 0x1.5ab07dd485427p+0, -0x1.4000p-51, - 0x1.5ba11fba87af4p+0, 0x1.0080p-44, + 0x1.5ba11fba87af4p+0, 0x1.0080p-44, 0x1.5c9268a59460bp+0, -0x1.6c80p-45, - 0x1.5d84590998e3fp+0, 0x1.69a0p-43, + 0x1.5d84590998e3fp+0, 0x1.69a0p-43, 0x1.5e76f15ad20e1p+0, -0x1.b400p-46, - 0x1.5f6a320dcebcap+0, 0x1.7700p-46, - 0x1.605e1b976dcb8p+0, 0x1.6f80p-45, - 0x1.6152ae6cdf715p+0, 0x1.1000p-47, + 0x1.5f6a320dcebcap+0, 0x1.7700p-46, + 0x1.605e1b976dcb8p+0, 0x1.6f80p-45, + 0x1.6152ae6cdf715p+0, 0x1.1000p-47, 0x1.6247eb03a5531p+0, -0x1.5d00p-46, 0x1.633dd1d1929b5p+0, -0x1.2d00p-46, 0x1.6434634ccc313p+0, -0x1.a800p-49, 0x1.652b9febc8efap+0, -0x1.8600p-45, - 0x1.6623882553397p+0, 0x1.1fe0p-40, + 0x1.6623882553397p+0, 0x1.1fe0p-40, 0x1.671c1c708328ep+0, -0x1.7200p-44, - 0x1.68155d44ca97ep+0, 0x1.6800p-49, + 0x1.68155d44ca97ep+0, 0x1.6800p-49, 0x1.690f4b19e9471p+0, -0x1.9780p-45, }; fn exp2_64(x: f64) f64 { @setFloatMode(this, @import("builtin").FloatMode.Strict); - const tblsiz = u32(exp2dt.len / 2); + const tblsiz = u32(exp2dt.len / 2); const redux: f64 = 0x1.8p52 / f64(tblsiz); - const P1: f64 = 0x1.62e42fefa39efp-1; - const P2: f64 = 0x1.ebfbdff82c575p-3; - const P3: f64 = 0x1.c6b08d704a0a6p-5; - const P4: f64 = 0x1.3b2ab88f70400p-7; - const P5: f64 = 0x1.5d88003875c74p-10; + const P1: f64 = 0x1.62e42fefa39efp-1; + const P2: f64 = 0x1.ebfbdff82c575p-3; + const P3: f64 = 0x1.c6b08d704a0a6p-5; + const P4: f64 = 0x1.3b2ab88f70400p-7; + const P5: f64 = 0x1.5d88003875c74p-10; const ux = @bitCast(u64, x); const ix = u32(ux >> 32) & 0x7FFFFFFF; diff --git a/std/math/expm1.zig b/std/math/expm1.zig index 316f0e3e71..11af1b215f 100644 --- a/std/math/expm1.zig +++ b/std/math/expm1.zig @@ -21,11 +21,11 @@ pub fn expm1(x: var) @typeOf(x) { fn expm1_32(x_: f32) f32 { @setFloatMode(this, builtin.FloatMode.Strict); const o_threshold: f32 = 8.8721679688e+01; - const ln2_hi: f32 = 6.9313812256e-01; - const ln2_lo: f32 = 9.0580006145e-06; - const invln2: f32 = 1.4426950216e+00; + const ln2_hi: f32 = 6.9313812256e-01; + const ln2_lo: f32 = 9.0580006145e-06; + const invln2: f32 = 1.4426950216e+00; const Q1: f32 = -3.3333212137e-2; - const Q2: f32 = 1.5807170421e-3; + const Q2: f32 = 1.5807170421e-3; var x = x_; const ux = @bitCast(u32, x); @@ -93,8 +93,7 @@ fn expm1_32(x_: f32) f32 { math.forceEval(x * x); } return x; - } - else { + } else { k = 0; } @@ -148,13 +147,13 @@ fn expm1_32(x_: f32) f32 { fn expm1_64(x_: f64) f64 { @setFloatMode(this, builtin.FloatMode.Strict); const o_threshold: f64 = 7.09782712893383973096e+02; - const ln2_hi: f64 = 6.93147180369123816490e-01; - const ln2_lo: f64 = 1.90821492927058770002e-10; - const invln2: f64 = 1.44269504088896338700e+00; + const ln2_hi: f64 = 6.93147180369123816490e-01; + const ln2_lo: f64 = 1.90821492927058770002e-10; + const invln2: f64 = 1.44269504088896338700e+00; const Q1: f64 = -3.33333333333331316428e-02; - const Q2: f64 = 1.58730158725481460165e-03; + const Q2: f64 = 1.58730158725481460165e-03; const Q3: f64 = -7.93650757867487942473e-05; - const Q4: f64 = 4.00821782732936239552e-06; + const Q4: f64 = 4.00821782732936239552e-06; const Q5: f64 = -2.01099218183624371326e-07; var x = x_; @@ -223,8 +222,7 @@ fn expm1_64(x_: f64) f64 { math.forceEval(f32(x)); } return x; - } - else { + } else { k = 0; } diff --git a/std/math/index.zig b/std/math/index.zig index 8fcd05d760..847e972500 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -501,7 +501,7 @@ test "math.negateCast" { if (negateCast(u32(@maxValue(i32) + 10))) |_| unreachable else |err| assert(err == error.Overflow); } -/// Cast an integer to a different integer type. If the value doesn't fit, +/// Cast an integer to a different integer type. If the value doesn't fit, /// return an error. pub fn cast(comptime T: type, x: var) (error{Overflow}!T) { comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer diff --git a/std/math/log1p.zig b/std/math/log1p.zig index 4616a2f2ba..57efdaf51c 100644 --- a/std/math/log1p.zig +++ b/std/math/log1p.zig @@ -138,8 +138,7 @@ fn log1p_64(x: f64) f64 { c = 0; f = x; } - } - else if (hx >= 0x7FF00000) { + } else if (hx >= 0x7FF00000) { return x; } diff --git a/std/math/pow.zig b/std/math/pow.zig index 51908f30ea..dfe4fc09d6 100644 --- a/std/math/pow.zig +++ b/std/math/pow.zig @@ -28,7 +28,6 @@ const assert = std.debug.assert; // This implementation is taken from the go stlib, musl is a bit more complex. pub fn pow(comptime T: type, x: T, y: T) T { - @setFloatMode(this, @import("builtin").FloatMode.Strict); if (T != f32 and T != f64) { diff --git a/std/net.zig b/std/net.zig index b1e291ab92..3af4e0b525 100644 --- a/std/net.zig +++ b/std/net.zig @@ -19,24 +19,30 @@ pub const Address = struct { os_addr: OsAddress, pub fn initIp4(ip4: u32, port: u16) Address { - return Address{ .os_addr = posix.sockaddr{ .in = posix.sockaddr_in{ - .family = posix.AF_INET, - .port = std.mem.endianSwapIfLe(u16, port), - .addr = ip4, - .zero = []u8{0} ** 8, - } } }; + return Address{ + .os_addr = posix.sockaddr{ + .in = posix.sockaddr_in{ + .family = posix.AF_INET, + .port = std.mem.endianSwapIfLe(u16, port), + .addr = ip4, + .zero = []u8{0} ** 8, + }, + }, + }; } pub fn initIp6(ip6: &const Ip6Addr, port: u16) Address { return Address{ .family = posix.AF_INET6, - .os_addr = posix.sockaddr{ .in6 = posix.sockaddr_in6{ - .family = posix.AF_INET6, - .port = std.mem.endianSwapIfLe(u16, port), - .flowinfo = 0, - .addr = ip6.addr, - .scope_id = ip6.scope_id, - } }, + .os_addr = posix.sockaddr{ + .in6 = posix.sockaddr_in6{ + .family = posix.AF_INET6, + .port = std.mem.endianSwapIfLe(u16, port), + .flowinfo = 0, + .addr = ip6.addr, + .scope_id = ip6.scope_id, + }, + }, }; } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index dc3a582707..51f1bd96e5 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -387,15 +387,12 @@ pub const ChildProcess = struct { const pid_err = posix.getErrno(pid_result); if (pid_err > 0) { return switch (pid_err) { - posix.EAGAIN, - posix.ENOMEM, - posix.ENOSYS => error.SystemResources, + posix.EAGAIN, posix.ENOMEM, posix.ENOSYS => error.SystemResources, else => os.unexpectedErrorPosix(pid_err), }; } if (pid_result == 0) { // we are the child - setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); @@ -646,8 +643,7 @@ fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ? if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?&c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) { const err = windows.GetLastError(); return switch (err) { - windows.ERROR.FILE_NOT_FOUND, - windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, + windows.ERROR.FILE_NOT_FOUND, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, windows.ERROR.INVALID_PARAMETER => unreachable, windows.ERROR.INVALID_NAME => error.InvalidName, else => os.unexpectedErrorWindows(err), @@ -745,8 +741,7 @@ fn makePipe() ![2]i32 { const err = posix.getErrno(posix.pipe(&fds)); if (err > 0) { return switch (err) { - posix.EMFILE, - posix.ENFILE => error.SystemResources, + posix.EMFILE, posix.ENFILE => error.SystemResources, else => os.unexpectedErrorPosix(err), }; } diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 45359e757d..a01755d27b 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -12,52 +12,71 @@ pub const STDERR_FILENO = 2; /// [MC2] no permissions pub const PROT_NONE = 0x00; + /// [MC2] pages can be read pub const PROT_READ = 0x01; + /// [MC2] pages can be written pub const PROT_WRITE = 0x02; + /// [MC2] pages can be executed pub const PROT_EXEC = 0x04; /// allocated from memory, swap space pub const MAP_ANONYMOUS = 0x1000; + /// map from file (default) pub const MAP_FILE = 0x0000; + /// interpret addr exactly pub const MAP_FIXED = 0x0010; + /// region may contain semaphores pub const MAP_HASSEMAPHORE = 0x0200; + /// changes are private pub const MAP_PRIVATE = 0x0002; + /// share changes pub const MAP_SHARED = 0x0001; + /// don't cache pages for this mapping pub const MAP_NOCACHE = 0x0400; + /// don't reserve needed swap area pub const MAP_NORESERVE = 0x0040; pub const MAP_FAILED = @maxValue(usize); /// [XSI] no hang in wait/no child to reap pub const WNOHANG = 0x00000001; + /// [XSI] notify on stop, untraced child pub const WUNTRACED = 0x00000002; /// take signal on signal stack pub const SA_ONSTACK = 0x0001; + /// restart system on signal return pub const SA_RESTART = 0x0002; + /// reset to SIG_DFL when taking signal pub const SA_RESETHAND = 0x0004; + /// do not generate SIGCHLD on child stop pub const SA_NOCLDSTOP = 0x0008; + /// don't mask the signal we're delivering pub const SA_NODEFER = 0x0010; + /// don't keep zombies around pub const SA_NOCLDWAIT = 0x0020; + /// signal handler with SA_SIGINFO args pub const SA_SIGINFO = 0x0040; + /// do not bounce off kernel's sigtramp pub const SA_USERTRAMP = 0x0100; + /// signal handler with SA_SIGINFO args with 64bit regs information pub const SA_64REGSET = 0x0200; @@ -71,30 +90,43 @@ pub const R_OK = 4; /// open for reading only pub const O_RDONLY = 0x0000; + /// open for writing only pub const O_WRONLY = 0x0001; + /// open for reading and writing pub const O_RDWR = 0x0002; + /// do not block on open or for data to become available pub const O_NONBLOCK = 0x0004; + /// append on each write pub const O_APPEND = 0x0008; + /// create file if it does not exist pub const O_CREAT = 0x0200; + /// truncate size to 0 pub const O_TRUNC = 0x0400; + /// error if O_CREAT and the file exists pub const O_EXCL = 0x0800; + /// atomically obtain a shared lock pub const O_SHLOCK = 0x0010; + /// atomically obtain an exclusive lock pub const O_EXLOCK = 0x0020; + /// do not follow symlinks pub const O_NOFOLLOW = 0x0100; + /// allow open of symlinks pub const O_SYMLINK = 0x200000; + /// descriptor requested for event notifications only pub const O_EVTONLY = 0x8000; + /// mark as close-on-exec pub const O_CLOEXEC = 0x1000000; @@ -126,75 +158,109 @@ pub const DT_WHT = 14; /// block specified signal set pub const SIG_BLOCK = 1; + /// unblock specified signal set pub const SIG_UNBLOCK = 2; + /// set specified signal set pub const SIG_SETMASK = 3; /// hangup pub const SIGHUP = 1; + /// interrupt pub const SIGINT = 2; + /// quit pub const SIGQUIT = 3; + /// illegal instruction (not reset when caught) pub const SIGILL = 4; + /// trace trap (not reset when caught) pub const SIGTRAP = 5; + /// abort() pub const SIGABRT = 6; + /// pollable event ([XSR] generated, not supported) pub const SIGPOLL = 7; + /// compatibility pub const SIGIOT = SIGABRT; + /// EMT instruction pub const SIGEMT = 7; + /// floating point exception pub const SIGFPE = 8; + /// kill (cannot be caught or ignored) pub const SIGKILL = 9; + /// bus error pub const SIGBUS = 10; + /// segmentation violation pub const SIGSEGV = 11; + /// bad argument to system call pub const SIGSYS = 12; + /// write on a pipe with no one to read it pub const SIGPIPE = 13; + /// alarm clock pub const SIGALRM = 14; + /// software termination signal from kill pub const SIGTERM = 15; + /// urgent condition on IO channel pub const SIGURG = 16; + /// sendable stop signal not from tty pub const SIGSTOP = 17; + /// stop signal from tty pub const SIGTSTP = 18; + /// continue a stopped process pub const SIGCONT = 19; + /// to parent on child stop or exit pub const SIGCHLD = 20; + /// to readers pgrp upon background tty read pub const SIGTTIN = 21; + /// like TTIN for output if (tp->t_local<OSTOP) pub const SIGTTOU = 22; + /// input/output possible signal pub const SIGIO = 23; + /// exceeded CPU time limit pub const SIGXCPU = 24; + /// exceeded file size limit pub const SIGXFSZ = 25; + /// virtual time alarm pub const SIGVTALRM = 26; + /// profiling time alarm pub const SIGPROF = 27; + /// window size changes pub const SIGWINCH = 28; + /// information request pub const SIGINFO = 29; + /// user defined signal 1 pub const SIGUSR1 = 30; + /// user defined signal 2 pub const SIGUSR2 = 31; diff --git a/std/os/darwin_errno.zig b/std/os/darwin_errno.zig index 6b47e5a9fa..438f3382ad 100644 --- a/std/os/darwin_errno.zig +++ b/std/os/darwin_errno.zig @@ -1,142 +1,328 @@ +/// Operation not permitted +pub const EPERM = 1; -pub const EPERM = 1; /// Operation not permitted -pub const ENOENT = 2; /// No such file or directory -pub const ESRCH = 3; /// No such process -pub const EINTR = 4; /// Interrupted system call -pub const EIO = 5; /// Input/output error -pub const ENXIO = 6; /// Device not configured -pub const E2BIG = 7; /// Argument list too long -pub const ENOEXEC = 8; /// Exec format error -pub const EBADF = 9; /// Bad file descriptor -pub const ECHILD = 10; /// No child processes -pub const EDEADLK = 11; /// Resource deadlock avoided - -pub const ENOMEM = 12; /// Cannot allocate memory -pub const EACCES = 13; /// Permission denied -pub const EFAULT = 14; /// Bad address -pub const ENOTBLK = 15; /// Block device required -pub const EBUSY = 16; /// Device / Resource busy -pub const EEXIST = 17; /// File exists -pub const EXDEV = 18; /// Cross-device link -pub const ENODEV = 19; /// Operation not supported by device -pub const ENOTDIR = 20; /// Not a directory -pub const EISDIR = 21; /// Is a directory -pub const EINVAL = 22; /// Invalid argument -pub const ENFILE = 23; /// Too many open files in system -pub const EMFILE = 24; /// Too many open files -pub const ENOTTY = 25; /// Inappropriate ioctl for device -pub const ETXTBSY = 26; /// Text file busy -pub const EFBIG = 27; /// File too large -pub const ENOSPC = 28; /// No space left on device -pub const ESPIPE = 29; /// Illegal seek -pub const EROFS = 30; /// Read-only file system -pub const EMLINK = 31; /// Too many links -pub const EPIPE = 32; /// Broken pipe +/// No such file or directory +pub const ENOENT = 2; + +/// No such process +pub const ESRCH = 3; + +/// Interrupted system call +pub const EINTR = 4; + +/// Input/output error +pub const EIO = 5; + +/// Device not configured +pub const ENXIO = 6; + +/// Argument list too long +pub const E2BIG = 7; + +/// Exec format error +pub const ENOEXEC = 8; + +/// Bad file descriptor +pub const EBADF = 9; + +/// No child processes +pub const ECHILD = 10; + +/// Resource deadlock avoided +pub const EDEADLK = 11; + +/// Cannot allocate memory +pub const ENOMEM = 12; + +/// Permission denied +pub const EACCES = 13; + +/// Bad address +pub const EFAULT = 14; + +/// Block device required +pub const ENOTBLK = 15; + +/// Device / Resource busy +pub const EBUSY = 16; + +/// File exists +pub const EEXIST = 17; + +/// Cross-device link +pub const EXDEV = 18; + +/// Operation not supported by device +pub const ENODEV = 19; + +/// Not a directory +pub const ENOTDIR = 20; + +/// Is a directory +pub const EISDIR = 21; + +/// Invalid argument +pub const EINVAL = 22; + +/// Too many open files in system +pub const ENFILE = 23; + +/// Too many open files +pub const EMFILE = 24; + +/// Inappropriate ioctl for device +pub const ENOTTY = 25; + +/// Text file busy +pub const ETXTBSY = 26; + +/// File too large +pub const EFBIG = 27; + +/// No space left on device +pub const ENOSPC = 28; + +/// Illegal seek +pub const ESPIPE = 29; + +/// Read-only file system +pub const EROFS = 30; + +/// Too many links +pub const EMLINK = 31; +/// Broken pipe // math software -pub const EDOM = 33; /// Numerical argument out of domain -pub const ERANGE = 34; /// Result too large +pub const EPIPE = 32; + +/// Numerical argument out of domain +pub const EDOM = 33; +/// Result too large // non-blocking and interrupt i/o -pub const EAGAIN = 35; /// Resource temporarily unavailable -pub const EWOULDBLOCK = EAGAIN; /// Operation would block -pub const EINPROGRESS = 36; /// Operation now in progress -pub const EALREADY = 37; /// Operation already in progress +pub const ERANGE = 34; + +/// Resource temporarily unavailable +pub const EAGAIN = 35; + +/// Operation would block +pub const EWOULDBLOCK = EAGAIN; + +/// Operation now in progress +pub const EINPROGRESS = 36; +/// Operation already in progress // ipc/network software -- argument errors -pub const ENOTSOCK = 38; /// Socket operation on non-socket -pub const EDESTADDRREQ = 39; /// Destination address required -pub const EMSGSIZE = 40; /// Message too long -pub const EPROTOTYPE = 41; /// Protocol wrong type for socket -pub const ENOPROTOOPT = 42; /// Protocol not available -pub const EPROTONOSUPPORT = 43; /// Protocol not supported +pub const EALREADY = 37; + +/// Socket operation on non-socket +pub const ENOTSOCK = 38; + +/// Destination address required +pub const EDESTADDRREQ = 39; + +/// Message too long +pub const EMSGSIZE = 40; + +/// Protocol wrong type for socket +pub const EPROTOTYPE = 41; + +/// Protocol not available +pub const ENOPROTOOPT = 42; + +/// Protocol not supported +pub const EPROTONOSUPPORT = 43; + +/// Socket type not supported +pub const ESOCKTNOSUPPORT = 44; -pub const ESOCKTNOSUPPORT = 44; /// Socket type not supported +/// Operation not supported +pub const ENOTSUP = 45; -pub const ENOTSUP = 45; /// Operation not supported +/// Protocol family not supported +pub const EPFNOSUPPORT = 46; -pub const EPFNOSUPPORT = 46; /// Protocol family not supported -pub const EAFNOSUPPORT = 47; /// Address family not supported by protocol family -pub const EADDRINUSE = 48; /// Address already in use -pub const EADDRNOTAVAIL = 49; /// Can't assign requested address +/// Address family not supported by protocol family +pub const EAFNOSUPPORT = 47; + +/// Address already in use +pub const EADDRINUSE = 48; +/// Can't assign requested address // ipc/network software -- operational errors -pub const ENETDOWN = 50; /// Network is down -pub const ENETUNREACH = 51; /// Network is unreachable -pub const ENETRESET = 52; /// Network dropped connection on reset -pub const ECONNABORTED = 53; /// Software caused connection abort -pub const ECONNRESET = 54; /// Connection reset by peer -pub const ENOBUFS = 55; /// No buffer space available -pub const EISCONN = 56; /// Socket is already connected -pub const ENOTCONN = 57; /// Socket is not connected +pub const EADDRNOTAVAIL = 49; + +/// Network is down +pub const ENETDOWN = 50; + +/// Network is unreachable +pub const ENETUNREACH = 51; + +/// Network dropped connection on reset +pub const ENETRESET = 52; + +/// Software caused connection abort +pub const ECONNABORTED = 53; + +/// Connection reset by peer +pub const ECONNRESET = 54; + +/// No buffer space available +pub const ENOBUFS = 55; + +/// Socket is already connected +pub const EISCONN = 56; + +/// Socket is not connected +pub const ENOTCONN = 57; + +/// Can't send after socket shutdown +pub const ESHUTDOWN = 58; -pub const ESHUTDOWN = 58; /// Can't send after socket shutdown -pub const ETOOMANYREFS = 59; /// Too many references: can't splice +/// Too many references: can't splice +pub const ETOOMANYREFS = 59; -pub const ETIMEDOUT = 60; /// Operation timed out -pub const ECONNREFUSED = 61; /// Connection refused +/// Operation timed out +pub const ETIMEDOUT = 60; -pub const ELOOP = 62; /// Too many levels of symbolic links -pub const ENAMETOOLONG = 63; /// File name too long +/// Connection refused +pub const ECONNREFUSED = 61; -pub const EHOSTDOWN = 64; /// Host is down -pub const EHOSTUNREACH = 65; /// No route to host -pub const ENOTEMPTY = 66; /// Directory not empty +/// Too many levels of symbolic links +pub const ELOOP = 62; + +/// File name too long +pub const ENAMETOOLONG = 63; + +/// Host is down +pub const EHOSTDOWN = 64; + +/// No route to host +pub const EHOSTUNREACH = 65; +/// Directory not empty // quotas & mush -pub const EPROCLIM = 67; /// Too many processes -pub const EUSERS = 68; /// Too many users -pub const EDQUOT = 69; /// Disc quota exceeded +pub const ENOTEMPTY = 66; + +/// Too many processes +pub const EPROCLIM = 67; + +/// Too many users +pub const EUSERS = 68; +/// Disc quota exceeded // Network File System -pub const ESTALE = 70; /// Stale NFS file handle -pub const EREMOTE = 71; /// Too many levels of remote in path -pub const EBADRPC = 72; /// RPC struct is bad -pub const ERPCMISMATCH = 73; /// RPC version wrong -pub const EPROGUNAVAIL = 74; /// RPC prog. not avail -pub const EPROGMISMATCH = 75; /// Program version wrong -pub const EPROCUNAVAIL = 76; /// Bad procedure for program +pub const EDQUOT = 69; + +/// Stale NFS file handle +pub const ESTALE = 70; + +/// Too many levels of remote in path +pub const EREMOTE = 71; + +/// RPC struct is bad +pub const EBADRPC = 72; + +/// RPC version wrong +pub const ERPCMISMATCH = 73; + +/// RPC prog. not avail +pub const EPROGUNAVAIL = 74; -pub const ENOLCK = 77; /// No locks available -pub const ENOSYS = 78; /// Function not implemented +/// Program version wrong +pub const EPROGMISMATCH = 75; -pub const EFTYPE = 79; /// Inappropriate file type or format -pub const EAUTH = 80; /// Authentication error -pub const ENEEDAUTH = 81; /// Need authenticator +/// Bad procedure for program +pub const EPROCUNAVAIL = 76; + +/// No locks available +pub const ENOLCK = 77; + +/// Function not implemented +pub const ENOSYS = 78; + +/// Inappropriate file type or format +pub const EFTYPE = 79; + +/// Authentication error +pub const EAUTH = 80; +/// Need authenticator // Intelligent device errors -pub const EPWROFF = 82; /// Device power is off -pub const EDEVERR = 83; /// Device error, e.g. paper out +pub const ENEEDAUTH = 81; + +/// Device power is off +pub const EPWROFF = 82; -pub const EOVERFLOW = 84; /// Value too large to be stored in data type +/// Device error, e.g. paper out +pub const EDEVERR = 83; +/// Value too large to be stored in data type // Program loading errors -pub const EBADEXEC = 85; /// Bad executable -pub const EBADARCH = 86; /// Bad CPU type in executable -pub const ESHLIBVERS = 87; /// Shared library version mismatch -pub const EBADMACHO = 88; /// Malformed Macho file +pub const EOVERFLOW = 84; + +/// Bad executable +pub const EBADEXEC = 85; + +/// Bad CPU type in executable +pub const EBADARCH = 86; + +/// Shared library version mismatch +pub const ESHLIBVERS = 87; + +/// Malformed Macho file +pub const EBADMACHO = 88; + +/// Operation canceled +pub const ECANCELED = 89; + +/// Identifier removed +pub const EIDRM = 90; + +/// No message of desired type +pub const ENOMSG = 91; + +/// Illegal byte sequence +pub const EILSEQ = 92; + +/// Attribute not found +pub const ENOATTR = 93; + +/// Bad message +pub const EBADMSG = 94; + +/// Reserved +pub const EMULTIHOP = 95; + +/// No message available on STREAM +pub const ENODATA = 96; + +/// Reserved +pub const ENOLINK = 97; + +/// No STREAM resources +pub const ENOSR = 98; + +/// Not a STREAM +pub const ENOSTR = 99; -pub const ECANCELED = 89; /// Operation canceled +/// Protocol error +pub const EPROTO = 100; -pub const EIDRM = 90; /// Identifier removed -pub const ENOMSG = 91; /// No message of desired type -pub const EILSEQ = 92; /// Illegal byte sequence -pub const ENOATTR = 93; /// Attribute not found +/// STREAM ioctl timeout +pub const ETIME = 101; -pub const EBADMSG = 94; /// Bad message -pub const EMULTIHOP = 95; /// Reserved -pub const ENODATA = 96; /// No message available on STREAM -pub const ENOLINK = 97; /// Reserved -pub const ENOSR = 98; /// No STREAM resources -pub const ENOSTR = 99; /// Not a STREAM -pub const EPROTO = 100; /// Protocol error -pub const ETIME = 101; /// STREAM ioctl timeout +/// No such policy registered +pub const ENOPOLICY = 103; -pub const ENOPOLICY = 103; /// No such policy registered +/// State not recoverable +pub const ENOTRECOVERABLE = 104; -pub const ENOTRECOVERABLE = 104; /// State not recoverable -pub const EOWNERDEAD = 105; /// Previous owner died +/// Previous owner died +pub const EOWNERDEAD = 105; -pub const EQFULL = 106; /// Interface output queue is full -pub const ELAST = 106; /// Must be equal largest errno +/// Interface output queue is full +pub const EQFULL = 106; +/// Must be equal largest errno +pub const ELAST = 106; diff --git a/std/os/epoch.zig b/std/os/epoch.zig index e1256c1374..fc031521a5 100644 --- a/std/os/epoch.zig +++ b/std/os/epoch.zig @@ -1,26 +1,26 @@ /// Epoch reference times in terms of their difference from /// posix epoch in seconds. -pub const posix = 0; //Jan 01, 1970 AD -pub const dos = 315532800; //Jan 01, 1980 AD -pub const ios = 978307200; //Jan 01, 2001 AD -pub const openvms = -3506716800; //Nov 17, 1858 AD -pub const zos = -2208988800; //Jan 01, 1900 AD -pub const windows = -11644473600; //Jan 01, 1601 AD -pub const amiga = 252460800; //Jan 01, 1978 AD -pub const pickos = -63244800; //Dec 31, 1967 AD -pub const gps = 315964800; //Jan 06, 1980 AD -pub const clr = -62135769600; //Jan 01, 0001 AD +pub const posix = 0; //Jan 01, 1970 AD +pub const dos = 315532800; //Jan 01, 1980 AD +pub const ios = 978307200; //Jan 01, 2001 AD +pub const openvms = -3506716800; //Nov 17, 1858 AD +pub const zos = -2208988800; //Jan 01, 1900 AD +pub const windows = -11644473600; //Jan 01, 1601 AD +pub const amiga = 252460800; //Jan 01, 1978 AD +pub const pickos = -63244800; //Dec 31, 1967 AD +pub const gps = 315964800; //Jan 06, 1980 AD +pub const clr = -62135769600; //Jan 01, 0001 AD -pub const unix = posix; -pub const android = posix; -pub const os2 = dos; -pub const bios = dos; -pub const vfat = dos; -pub const ntfs = windows; -pub const ntp = zos; -pub const jbase = pickos; -pub const aros = amiga; -pub const morphos = amiga; -pub const brew = gps; -pub const atsc = gps; -pub const go = clr; \ No newline at end of file +pub const unix = posix; +pub const android = posix; +pub const os2 = dos; +pub const bios = dos; +pub const vfat = dos; +pub const ntfs = windows; +pub const ntp = zos; +pub const jbase = pickos; +pub const aros = amiga; +pub const morphos = amiga; +pub const brew = gps; +pub const atsc = gps; +pub const go = clr; diff --git a/std/os/file.zig b/std/os/file.zig index 61fc2b1455..c07e2c5c8b 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -21,12 +21,18 @@ pub const File = struct { /// Call close to clean up. pub fn openRead(allocator: &mem.Allocator, path: []const u8) OpenError!File { if (is_posix) { - const flags = posix.O_LARGEFILE|posix.O_RDONLY; + const flags = posix.O_LARGEFILE | posix.O_RDONLY; const fd = try os.posixOpen(allocator, path, flags, 0); return openHandle(fd); } else if (is_windows) { - const handle = try os.windowsOpen(allocator, path, windows.GENERIC_READ, windows.FILE_SHARE_READ, - windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL); + const handle = try os.windowsOpen( + allocator, + path, + windows.GENERIC_READ, + windows.FILE_SHARE_READ, + windows.OPEN_EXISTING, + windows.FILE_ATTRIBUTE_NORMAL, + ); return openHandle(handle); } else { @compileError("TODO implement openRead for this OS"); @@ -36,7 +42,6 @@ pub const File = struct { /// Calls `openWriteMode` with os.default_file_mode for the mode. pub fn openWrite(allocator: &mem.Allocator, path: []const u8) OpenError!File { return openWriteMode(allocator, path, os.default_file_mode); - } /// If the path does not exist it will be created. @@ -45,18 +50,22 @@ pub const File = struct { /// Call close to clean up. pub fn openWriteMode(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { if (is_posix) { - const flags = posix.O_LARGEFILE|posix.O_WRONLY|posix.O_CREAT|posix.O_CLOEXEC|posix.O_TRUNC; + const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC; const fd = try os.posixOpen(allocator, path, flags, file_mode); return openHandle(fd); } else if (is_windows) { - const handle = try os.windowsOpen(allocator, path, windows.GENERIC_WRITE, - windows.FILE_SHARE_WRITE|windows.FILE_SHARE_READ|windows.FILE_SHARE_DELETE, - windows.CREATE_ALWAYS, windows.FILE_ATTRIBUTE_NORMAL); + const handle = try os.windowsOpen( + allocator, + path, + windows.GENERIC_WRITE, + windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, + windows.CREATE_ALWAYS, + windows.FILE_ATTRIBUTE_NORMAL, + ); return openHandle(handle); } else { @compileError("TODO implement openWriteMode for this OS"); } - } /// If the path does not exist it will be created. @@ -65,24 +74,26 @@ pub const File = struct { /// Call close to clean up. pub fn openWriteNoClobber(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { if (is_posix) { - const flags = posix.O_LARGEFILE|posix.O_WRONLY|posix.O_CREAT|posix.O_CLOEXEC|posix.O_EXCL; + const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL; const fd = try os.posixOpen(allocator, path, flags, file_mode); return openHandle(fd); } else if (is_windows) { - const handle = try os.windowsOpen(allocator, path, windows.GENERIC_WRITE, - windows.FILE_SHARE_WRITE|windows.FILE_SHARE_READ|windows.FILE_SHARE_DELETE, - windows.CREATE_NEW, windows.FILE_ATTRIBUTE_NORMAL); + const handle = try os.windowsOpen( + allocator, + path, + windows.GENERIC_WRITE, + windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, + windows.CREATE_NEW, + windows.FILE_ATTRIBUTE_NORMAL, + ); return openHandle(handle); } else { @compileError("TODO implement openWriteMode for this OS"); } - } pub fn openHandle(handle: os.FileHandle) File { - return File { - .handle = handle, - }; + return File{ .handle = handle }; } pub fn access(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool { @@ -217,7 +228,7 @@ pub const File = struct { return result; }, Os.windows => { - var pos : windows.LARGE_INTEGER = undefined; + var pos: windows.LARGE_INTEGER = undefined; if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) { const err = windows.GetLastError(); return switch (err) { @@ -268,7 +279,7 @@ pub const File = struct { } } - pub const ModeError = error { + pub const ModeError = error{ BadFd, SystemResources, Unexpected, @@ -296,7 +307,7 @@ pub const File = struct { } } - pub const ReadError = error {}; + pub const ReadError = error{}; pub fn read(self: &File, buffer: []u8) !usize { if (is_posix) { @@ -306,12 +317,12 @@ pub const File = struct { const read_err = posix.getErrno(amt_read); if (read_err > 0) { switch (read_err) { - posix.EINTR => continue, + posix.EINTR => continue, posix.EINVAL => unreachable, posix.EFAULT => unreachable, - posix.EBADF => return error.BadFd, - posix.EIO => return error.Io, - else => return os.unexpectedErrorPosix(read_err), + posix.EBADF => return error.BadFd, + posix.EIO => return error.Io, + else => return os.unexpectedErrorPosix(read_err), } } if (amt_read == 0) return index; diff --git a/std/os/get_user_id.zig b/std/os/get_user_id.zig index 11410ffa64..2a15e1d495 100644 --- a/std/os/get_user_id.zig +++ b/std/os/get_user_id.zig @@ -74,7 +74,7 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { '\n' => return error.CorruptPasswordFile, else => { const digit = switch (byte) { - '0' ... '9' => byte - '0', + '0'...'9' => byte - '0', else => return error.CorruptPasswordFile, }; if (@mulWithOverflow(u32, uid, 10, &uid)) return error.CorruptPasswordFile; @@ -83,14 +83,14 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { }, State.ReadGroupId => switch (byte) { '\n', ':' => { - return UserInfo { + return UserInfo{ .uid = uid, .gid = gid, }; }, else => { const digit = switch (byte) { - '0' ... '9' => byte - '0', + '0'...'9' => byte - '0', else => return error.CorruptPasswordFile, }; if (@mulWithOverflow(u32, gid, 10, &gid)) return error.CorruptPasswordFile; diff --git a/std/os/index.zig b/std/os/index.zig index 01e2092e1c..db182ed669 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -3,8 +3,7 @@ const builtin = @import("builtin"); const Os = builtin.Os; const is_windows = builtin.os == Os.windows; const is_posix = switch (builtin.os) { - builtin.Os.linux, - builtin.Os.macosx => true, + builtin.Os.linux, builtin.Os.macosx => true, else => false, }; const os = this; @@ -27,8 +26,7 @@ pub const linux = @import("linux/index.zig"); pub const zen = @import("zen.zig"); pub const posix = switch (builtin.os) { Os.linux => linux, - Os.macosx, - Os.ios => darwin, + Os.macosx, Os.ios => darwin, Os.zen => zen, else => @compileError("Unsupported OS"), }; @@ -112,8 +110,7 @@ pub fn getRandomBytes(buf: []u8) !void { } return; }, - Os.macosx, - Os.ios => { + Os.macosx, Os.ios => { const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); defer close(fd); @@ -175,9 +172,7 @@ pub fn abort() noreturn { c.abort(); } switch (builtin.os) { - Os.linux, - Os.macosx, - Os.ios => { + Os.linux, Os.macosx, Os.ios => { _ = posix.raise(posix.SIGABRT); _ = posix.raise(posix.SIGKILL); while (true) {} @@ -199,9 +194,7 @@ pub fn exit(status: u8) noreturn { c.exit(status); } switch (builtin.os) { - Os.linux, - Os.macosx, - Os.ios => { + Os.linux, Os.macosx, Os.ios => { posix.exit(status); }, Os.windows => { @@ -250,14 +243,12 @@ pub fn posixRead(fd: i32, buf: []u8) !void { if (err > 0) { return switch (err) { posix.EINTR => continue, - posix.EINVAL, - posix.EFAULT => unreachable, + posix.EINVAL, posix.EFAULT => unreachable, posix.EAGAIN => error.WouldBlock, posix.EBADF => error.FileClosed, posix.EIO => error.InputOutput, posix.EISDIR => error.IsDir, - posix.ENOBUFS, - posix.ENOMEM => error.SystemResources, + posix.ENOBUFS, posix.ENOMEM => error.SystemResources, else => unexpectedErrorPosix(err), }; } @@ -292,8 +283,7 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { if (write_err > 0) { return switch (write_err) { posix.EINTR => continue, - posix.EINVAL, - posix.EFAULT => unreachable, + posix.EINVAL, posix.EFAULT => unreachable, posix.EAGAIN => PosixWriteError.WouldBlock, posix.EBADF => PosixWriteError.FileClosed, posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired, @@ -349,8 +339,7 @@ pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 { posix.EFAULT => unreachable, posix.EINVAL => unreachable, posix.EACCES => return PosixOpenError.AccessDenied, - posix.EFBIG, - posix.EOVERFLOW => return PosixOpenError.FileTooBig, + posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig, posix.EISDIR => return PosixOpenError.IsDir, posix.ELOOP => return PosixOpenError.SymLinkLoop, posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded, @@ -375,8 +364,7 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void { const err = posix.getErrno(posix.dup2(old_fd, new_fd)); if (err > 0) { return switch (err) { - posix.EBUSY, - posix.EINTR => continue, + posix.EBUSY, posix.EINTR => continue, posix.EMFILE => error.ProcessFdQuotaExceeded, posix.EINVAL => unreachable, else => unexpectedErrorPosix(err), @@ -493,17 +481,10 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { assert(err > 0); return switch (err) { posix.EFAULT => unreachable, - posix.E2BIG, - posix.EMFILE, - posix.ENAMETOOLONG, - posix.ENFILE, - posix.ENOMEM => error.SystemResources, - posix.EACCES, - posix.EPERM => error.AccessDenied, - posix.EINVAL, - posix.ENOEXEC => error.InvalidExe, - posix.EIO, - posix.ELOOP => error.FileSystem, + posix.E2BIG, posix.EMFILE, posix.ENAMETOOLONG, posix.ENFILE, posix.ENOMEM => error.SystemResources, + posix.EACCES, posix.EPERM => error.AccessDenied, + posix.EINVAL, posix.ENOEXEC => error.InvalidExe, + posix.EIO, posix.ELOOP => error.FileSystem, posix.EISDIR => error.IsDir, posix.ENOENT => error.FileNotFound, posix.ENOTDIR => error.NotDir, @@ -717,10 +698,8 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr)); if (err > 0) { return switch (err) { - posix.EFAULT, - posix.EINVAL => unreachable, - posix.EACCES, - posix.EPERM => error.AccessDenied, + posix.EFAULT, posix.EINVAL => unreachable, + posix.EACCES, posix.EPERM => error.AccessDenied, posix.EDQUOT => error.DiskQuota, posix.EEXIST => error.PathAlreadyExists, posix.EIO => error.FileSystem, @@ -787,8 +766,7 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void { return switch (err) { windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, windows.ERROR.ACCESS_DENIED => error.AccessDenied, - windows.ERROR.FILENAME_EXCED_RANGE, - windows.ERROR.INVALID_PARAMETER => error.NameTooLong, + windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong, else => unexpectedErrorWindows(err), }; } @@ -804,11 +782,9 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void { const err = posix.getErrno(posix.unlink(buf.ptr)); if (err > 0) { return switch (err) { - posix.EACCES, - posix.EPERM => error.AccessDenied, + posix.EACCES, posix.EPERM => error.AccessDenied, posix.EBUSY => error.FileBusy, - posix.EFAULT, - posix.EINVAL => unreachable, + posix.EFAULT, posix.EINVAL => unreachable, posix.EIO => error.FileSystem, posix.EISDIR => error.IsDir, posix.ELOOP => error.SymLinkLoop, @@ -948,12 +924,10 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr)); if (err > 0) { return switch (err) { - posix.EACCES, - posix.EPERM => error.AccessDenied, + posix.EACCES, posix.EPERM => error.AccessDenied, posix.EBUSY => error.FileBusy, posix.EDQUOT => error.DiskQuota, - posix.EFAULT, - posix.EINVAL => unreachable, + posix.EFAULT, posix.EINVAL => unreachable, posix.EISDIR => error.IsDir, posix.ELOOP => error.SymLinkLoop, posix.EMLINK => error.LinkQuotaExceeded, @@ -962,8 +936,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) posix.ENOTDIR => error.NotDir, posix.ENOMEM => error.SystemResources, posix.ENOSPC => error.NoSpaceLeft, - posix.EEXIST, - posix.ENOTEMPTY => error.PathAlreadyExists, + posix.EEXIST, posix.ENOTEMPTY => error.PathAlreadyExists, posix.EROFS => error.ReadOnlyFileSystem, posix.EXDEV => error.RenameAcrossMountPoints, else => unexpectedErrorPosix(err), @@ -1001,8 +974,7 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void { const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755)); if (err > 0) { return switch (err) { - posix.EACCES, - posix.EPERM => error.AccessDenied, + posix.EACCES, posix.EPERM => error.AccessDenied, posix.EDQUOT => error.DiskQuota, posix.EEXIST => error.PathAlreadyExists, posix.EFAULT => unreachable, @@ -1065,18 +1037,15 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void { const err = posix.getErrno(posix.rmdir(path_buf.ptr)); if (err > 0) { return switch (err) { - posix.EACCES, - posix.EPERM => error.AccessDenied, + posix.EACCES, posix.EPERM => error.AccessDenied, posix.EBUSY => error.FileBusy, - posix.EFAULT, - posix.EINVAL => unreachable, + posix.EFAULT, posix.EINVAL => unreachable, posix.ELOOP => error.SymLinkLoop, posix.ENAMETOOLONG => error.NameTooLong, posix.ENOENT => error.FileNotFound, posix.ENOMEM => error.SystemResources, posix.ENOTDIR => error.NotDir, - posix.EEXIST, - posix.ENOTEMPTY => error.DirNotEmpty, + posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty, posix.EROFS => error.ReadOnlyFileSystem, else => unexpectedErrorPosix(err), }; @@ -1128,7 +1097,8 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! error.NotDir, error.FileSystem, error.FileBusy, - error.Unexpected => return err, + error.Unexpected, + => return err, } { var dir = Dir.open(allocator, full_path) catch |err| switch (err) { @@ -1152,7 +1122,8 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! error.SystemResources, error.NoSpaceLeft, error.PathAlreadyExists, - error.Unexpected => return err, + error.Unexpected, + => return err, }; defer dir.close(); @@ -1182,8 +1153,7 @@ pub const Dir = struct { end_index: usize, const darwin_seek_t = switch (builtin.os) { - Os.macosx, - Os.ios => i64, + Os.macosx, Os.ios => i64, else => void, }; @@ -1208,13 +1178,16 @@ pub const Dir = struct { const fd = switch (builtin.os) { Os.windows => @compileError("TODO support Dir.open for windows"), Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0), - Os.macosx, - Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, 0), + Os.macosx, Os.ios => try posixOpen( + allocator, + dir_path, + posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, + 0, + ), else => @compileError("Dir.open is not supported for this platform"), }; const darwin_seek_init = switch (builtin.os) { - Os.macosx, - Os.ios => 0, + Os.macosx, Os.ios => 0, else => {}, }; return Dir{ @@ -1237,8 +1210,7 @@ pub const Dir = struct { pub fn next(self: &Dir) !?Entry { switch (builtin.os) { Os.linux => return self.nextLinux(), - Os.macosx, - Os.ios => return self.nextDarwin(), + Os.macosx, Os.ios => return self.nextDarwin(), Os.windows => return self.nextWindows(), else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)), } @@ -1256,9 +1228,7 @@ pub const Dir = struct { const err = posix.getErrno(result); if (err > 0) { switch (err) { - posix.EBADF, - posix.EFAULT, - posix.ENOTDIR => unreachable, + posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, posix.EINVAL => { self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2); continue; @@ -1317,9 +1287,7 @@ pub const Dir = struct { const err = posix.getErrno(result); if (err > 0) { switch (err) { - posix.EBADF, - posix.EFAULT, - posix.ENOTDIR => unreachable, + posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, posix.EINVAL => { self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2); continue; @@ -1402,8 +1370,7 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 { if (err > 0) { return switch (err) { posix.EACCES => error.AccessDenied, - posix.EFAULT, - posix.EINVAL => unreachable, + posix.EFAULT, posix.EINVAL => unreachable, posix.EIO => error.FileSystem, posix.ELOOP => error.SymLinkLoop, posix.ENAMETOOLONG => error.NameTooLong, @@ -1545,8 +1512,7 @@ pub const ArgIteratorWindows = struct { const byte = self.cmd_line[self.index]; switch (byte) { 0 => return null, - ' ', - '\t' => continue, + ' ', '\t' => continue, else => break, } } @@ -1560,8 +1526,7 @@ pub const ArgIteratorWindows = struct { const byte = self.cmd_line[self.index]; switch (byte) { 0 => return false, - ' ', - '\t' => continue, + ' ', '\t' => continue, else => break, } } @@ -1580,8 +1545,7 @@ pub const ArgIteratorWindows = struct { '\\' => { backslash_count += 1; }, - ' ', - '\t' => { + ' ', '\t' => { if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) { return true; } @@ -1621,8 +1585,7 @@ pub const ArgIteratorWindows = struct { '\\' => { backslash_count += 1; }, - ' ', - '\t' => { + ' ', '\t' => { try self.emitBackslashes(&buf, backslash_count); backslash_count = 0; if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) { @@ -1840,8 +1803,7 @@ pub fn openSelfExe() !os.File { var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); return os.File.openRead(&fixed_allocator.allocator, proc_file_path); }, - Os.macosx, - Os.ios => { + Os.macosx, Os.ios => { var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined; var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); const self_exe_path = try selfExePath(&fixed_allocator.allocator); @@ -1853,9 +1815,7 @@ pub fn openSelfExe() !os.File { test "openSelfExe" { switch (builtin.os) { - Os.linux, - Os.macosx, - Os.ios => (try openSelfExe()).close(), + Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(), else => return, // Unsupported OS. } } @@ -1893,8 +1853,7 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 { try out_path.resize(new_len); } }, - Os.macosx, - Os.ios => { + Os.macosx, Os.ios => { var u32_len: u32 = 0; const ret1 = c._NSGetExecutablePath(undefined, &u32_len); assert(ret1 != 0); @@ -1922,9 +1881,7 @@ pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 { const dir = path.dirname(full_exe_path); return allocator.shrink(u8, full_exe_path, dir.len); }, - Os.windows, - Os.macosx, - Os.ios => { + Os.windows, Os.macosx, Os.ios => { const self_exe_path = try selfExePath(allocator); errdefer allocator.free(self_exe_path); const dirname = os.path.dirname(self_exe_path); @@ -1981,8 +1938,7 @@ pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded, posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded, - posix.ENOBUFS, - posix.ENOMEM => return PosixSocketError.SystemResources, + posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources, posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported, else => return unexpectedErrorPosix(err), } @@ -1990,7 +1946,7 @@ pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { pub const PosixBindError = error{ /// The address is protected, and the user is not the superuser. - /// For UNIX domain sockets: Search permission is denied on a component + /// For UNIX domain sockets: Search permission is denied on a component /// of the path prefix. AccessDenied, @@ -2151,8 +2107,7 @@ pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError! posix.EINVAL => return PosixAcceptError.InvalidSyscall, posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, - posix.ENOBUFS, - posix.ENOMEM => return PosixAcceptError.SystemResources, + posix.ENOBUFS, posix.ENOMEM => return PosixAcceptError.SystemResources, posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, posix.EPROTO => return PosixAcceptError.ProtocolFailure, @@ -2363,8 +2318,7 @@ pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConn const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); const err = posix.getErrno(rc); switch (err) { - 0, - posix.EINPROGRESS => return, + 0, posix.EINPROGRESS => return, else => return unexpectedErrorPosix(err), posix.EACCES => return PosixConnectError.PermissionDenied, @@ -2416,7 +2370,7 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { }, else => return unexpectedErrorPosix(err), posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor. - posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. + posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. posix.EINVAL => unreachable, posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. @@ -2427,11 +2381,13 @@ pub const Thread = struct { data: Data, pub const use_pthreads = is_posix and builtin.link_libc; - const Data = if (use_pthreads) struct { - handle: c.pthread_t, - stack_addr: usize, - stack_len: usize, - } else switch (builtin.os) { + const Data = if (use_pthreads) + struct { + handle: c.pthread_t, + stack_addr: usize, + stack_len: usize, + } + else switch (builtin.os) { builtin.Os.linux => struct { pid: i32, stack_addr: usize, diff --git a/std/os/linux/errno.zig b/std/os/linux/errno.zig index 39f4e37a10..5ad8777f92 100644 --- a/std/os/linux/errno.zig +++ b/std/os/linux/errno.zig @@ -1,146 +1,427 @@ -pub const EPERM = 1; /// Operation not permitted -pub const ENOENT = 2; /// No such file or directory -pub const ESRCH = 3; /// No such process -pub const EINTR = 4; /// Interrupted system call -pub const EIO = 5; /// I/O error -pub const ENXIO = 6; /// No such device or address -pub const E2BIG = 7; /// Arg list too long -pub const ENOEXEC = 8; /// Exec format error -pub const EBADF = 9; /// Bad file number -pub const ECHILD = 10; /// No child processes -pub const EAGAIN = 11; /// Try again -pub const ENOMEM = 12; /// Out of memory -pub const EACCES = 13; /// Permission denied -pub const EFAULT = 14; /// Bad address -pub const ENOTBLK = 15; /// Block device required -pub const EBUSY = 16; /// Device or resource busy -pub const EEXIST = 17; /// File exists -pub const EXDEV = 18; /// Cross-device link -pub const ENODEV = 19; /// No such device -pub const ENOTDIR = 20; /// Not a directory -pub const EISDIR = 21; /// Is a directory -pub const EINVAL = 22; /// Invalid argument -pub const ENFILE = 23; /// File table overflow -pub const EMFILE = 24; /// Too many open files -pub const ENOTTY = 25; /// Not a typewriter -pub const ETXTBSY = 26; /// Text file busy -pub const EFBIG = 27; /// File too large -pub const ENOSPC = 28; /// No space left on device -pub const ESPIPE = 29; /// Illegal seek -pub const EROFS = 30; /// Read-only file system -pub const EMLINK = 31; /// Too many links -pub const EPIPE = 32; /// Broken pipe -pub const EDOM = 33; /// Math argument out of domain of func -pub const ERANGE = 34; /// Math result not representable -pub const EDEADLK = 35; /// Resource deadlock would occur -pub const ENAMETOOLONG = 36; /// File name too long -pub const ENOLCK = 37; /// No record locks available -pub const ENOSYS = 38; /// Function not implemented -pub const ENOTEMPTY = 39; /// Directory not empty -pub const ELOOP = 40; /// Too many symbolic links encountered -pub const EWOULDBLOCK = EAGAIN; /// Operation would block -pub const ENOMSG = 42; /// No message of desired type -pub const EIDRM = 43; /// Identifier removed -pub const ECHRNG = 44; /// Channel number out of range -pub const EL2NSYNC = 45; /// Level 2 not synchronized -pub const EL3HLT = 46; /// Level 3 halted -pub const EL3RST = 47; /// Level 3 reset -pub const ELNRNG = 48; /// Link number out of range -pub const EUNATCH = 49; /// Protocol driver not attached -pub const ENOCSI = 50; /// No CSI structure available -pub const EL2HLT = 51; /// Level 2 halted -pub const EBADE = 52; /// Invalid exchange -pub const EBADR = 53; /// Invalid request descriptor -pub const EXFULL = 54; /// Exchange full -pub const ENOANO = 55; /// No anode -pub const EBADRQC = 56; /// Invalid request code -pub const EBADSLT = 57; /// Invalid slot - -pub const EBFONT = 59; /// Bad font file format -pub const ENOSTR = 60; /// Device not a stream -pub const ENODATA = 61; /// No data available -pub const ETIME = 62; /// Timer expired -pub const ENOSR = 63; /// Out of streams resources -pub const ENONET = 64; /// Machine is not on the network -pub const ENOPKG = 65; /// Package not installed -pub const EREMOTE = 66; /// Object is remote -pub const ENOLINK = 67; /// Link has been severed -pub const EADV = 68; /// Advertise error -pub const ESRMNT = 69; /// Srmount error -pub const ECOMM = 70; /// Communication error on send -pub const EPROTO = 71; /// Protocol error -pub const EMULTIHOP = 72; /// Multihop attempted -pub const EDOTDOT = 73; /// RFS specific error -pub const EBADMSG = 74; /// Not a data message -pub const EOVERFLOW = 75; /// Value too large for defined data type -pub const ENOTUNIQ = 76; /// Name not unique on network -pub const EBADFD = 77; /// File descriptor in bad state -pub const EREMCHG = 78; /// Remote address changed -pub const ELIBACC = 79; /// Can not access a needed shared library -pub const ELIBBAD = 80; /// Accessing a corrupted shared library -pub const ELIBSCN = 81; /// .lib section in a.out corrupted -pub const ELIBMAX = 82; /// Attempting to link in too many shared libraries -pub const ELIBEXEC = 83; /// Cannot exec a shared library directly -pub const EILSEQ = 84; /// Illegal byte sequence -pub const ERESTART = 85; /// Interrupted system call should be restarted -pub const ESTRPIPE = 86; /// Streams pipe error -pub const EUSERS = 87; /// Too many users -pub const ENOTSOCK = 88; /// Socket operation on non-socket -pub const EDESTADDRREQ = 89; /// Destination address required -pub const EMSGSIZE = 90; /// Message too long -pub const EPROTOTYPE = 91; /// Protocol wrong type for socket -pub const ENOPROTOOPT = 92; /// Protocol not available -pub const EPROTONOSUPPORT = 93; /// Protocol not supported -pub const ESOCKTNOSUPPORT = 94; /// Socket type not supported -pub const EOPNOTSUPP = 95; /// Operation not supported on transport endpoint -pub const EPFNOSUPPORT = 96; /// Protocol family not supported -pub const EAFNOSUPPORT = 97; /// Address family not supported by protocol -pub const EADDRINUSE = 98; /// Address already in use -pub const EADDRNOTAVAIL = 99; /// Cannot assign requested address -pub const ENETDOWN = 100; /// Network is down -pub const ENETUNREACH = 101; /// Network is unreachable -pub const ENETRESET = 102; /// Network dropped connection because of reset -pub const ECONNABORTED = 103; /// Software caused connection abort -pub const ECONNRESET = 104; /// Connection reset by peer -pub const ENOBUFS = 105; /// No buffer space available -pub const EISCONN = 106; /// Transport endpoint is already connected -pub const ENOTCONN = 107; /// Transport endpoint is not connected -pub const ESHUTDOWN = 108; /// Cannot send after transport endpoint shutdown -pub const ETOOMANYREFS = 109; /// Too many references: cannot splice -pub const ETIMEDOUT = 110; /// Connection timed out -pub const ECONNREFUSED = 111; /// Connection refused -pub const EHOSTDOWN = 112; /// Host is down -pub const EHOSTUNREACH = 113; /// No route to host -pub const EALREADY = 114; /// Operation already in progress -pub const EINPROGRESS = 115; /// Operation now in progress -pub const ESTALE = 116; /// Stale NFS file handle -pub const EUCLEAN = 117; /// Structure needs cleaning -pub const ENOTNAM = 118; /// Not a XENIX named type file -pub const ENAVAIL = 119; /// No XENIX semaphores available -pub const EISNAM = 120; /// Is a named type file -pub const EREMOTEIO = 121; /// Remote I/O error -pub const EDQUOT = 122; /// Quota exceeded - -pub const ENOMEDIUM = 123; /// No medium found -pub const EMEDIUMTYPE = 124; /// Wrong medium type +/// Operation not permitted +pub const EPERM = 1; + +/// No such file or directory +pub const ENOENT = 2; + +/// No such process +pub const ESRCH = 3; + +/// Interrupted system call +pub const EINTR = 4; + +/// I/O error +pub const EIO = 5; + +/// No such device or address +pub const ENXIO = 6; + +/// Arg list too long +pub const E2BIG = 7; + +/// Exec format error +pub const ENOEXEC = 8; + +/// Bad file number +pub const EBADF = 9; + +/// No child processes +pub const ECHILD = 10; + +/// Try again +pub const EAGAIN = 11; + +/// Out of memory +pub const ENOMEM = 12; + +/// Permission denied +pub const EACCES = 13; + +/// Bad address +pub const EFAULT = 14; + +/// Block device required +pub const ENOTBLK = 15; + +/// Device or resource busy +pub const EBUSY = 16; + +/// File exists +pub const EEXIST = 17; + +/// Cross-device link +pub const EXDEV = 18; + +/// No such device +pub const ENODEV = 19; + +/// Not a directory +pub const ENOTDIR = 20; + +/// Is a directory +pub const EISDIR = 21; + +/// Invalid argument +pub const EINVAL = 22; + +/// File table overflow +pub const ENFILE = 23; + +/// Too many open files +pub const EMFILE = 24; + +/// Not a typewriter +pub const ENOTTY = 25; + +/// Text file busy +pub const ETXTBSY = 26; + +/// File too large +pub const EFBIG = 27; + +/// No space left on device +pub const ENOSPC = 28; + +/// Illegal seek +pub const ESPIPE = 29; + +/// Read-only file system +pub const EROFS = 30; + +/// Too many links +pub const EMLINK = 31; + +/// Broken pipe +pub const EPIPE = 32; + +/// Math argument out of domain of func +pub const EDOM = 33; + +/// Math result not representable +pub const ERANGE = 34; + +/// Resource deadlock would occur +pub const EDEADLK = 35; + +/// File name too long +pub const ENAMETOOLONG = 36; + +/// No record locks available +pub const ENOLCK = 37; + +/// Function not implemented +pub const ENOSYS = 38; + +/// Directory not empty +pub const ENOTEMPTY = 39; + +/// Too many symbolic links encountered +pub const ELOOP = 40; + +/// Operation would block +pub const EWOULDBLOCK = EAGAIN; + +/// No message of desired type +pub const ENOMSG = 42; + +/// Identifier removed +pub const EIDRM = 43; + +/// Channel number out of range +pub const ECHRNG = 44; + +/// Level 2 not synchronized +pub const EL2NSYNC = 45; + +/// Level 3 halted +pub const EL3HLT = 46; + +/// Level 3 reset +pub const EL3RST = 47; + +/// Link number out of range +pub const ELNRNG = 48; + +/// Protocol driver not attached +pub const EUNATCH = 49; + +/// No CSI structure available +pub const ENOCSI = 50; + +/// Level 2 halted +pub const EL2HLT = 51; + +/// Invalid exchange +pub const EBADE = 52; + +/// Invalid request descriptor +pub const EBADR = 53; + +/// Exchange full +pub const EXFULL = 54; + +/// No anode +pub const ENOANO = 55; + +/// Invalid request code +pub const EBADRQC = 56; + +/// Invalid slot +pub const EBADSLT = 57; + +/// Bad font file format +pub const EBFONT = 59; + +/// Device not a stream +pub const ENOSTR = 60; + +/// No data available +pub const ENODATA = 61; + +/// Timer expired +pub const ETIME = 62; + +/// Out of streams resources +pub const ENOSR = 63; + +/// Machine is not on the network +pub const ENONET = 64; + +/// Package not installed +pub const ENOPKG = 65; + +/// Object is remote +pub const EREMOTE = 66; + +/// Link has been severed +pub const ENOLINK = 67; + +/// Advertise error +pub const EADV = 68; + +/// Srmount error +pub const ESRMNT = 69; + +/// Communication error on send +pub const ECOMM = 70; + +/// Protocol error +pub const EPROTO = 71; + +/// Multihop attempted +pub const EMULTIHOP = 72; + +/// RFS specific error +pub const EDOTDOT = 73; + +/// Not a data message +pub const EBADMSG = 74; + +/// Value too large for defined data type +pub const EOVERFLOW = 75; + +/// Name not unique on network +pub const ENOTUNIQ = 76; + +/// File descriptor in bad state +pub const EBADFD = 77; + +/// Remote address changed +pub const EREMCHG = 78; + +/// Can not access a needed shared library +pub const ELIBACC = 79; + +/// Accessing a corrupted shared library +pub const ELIBBAD = 80; + +/// .lib section in a.out corrupted +pub const ELIBSCN = 81; + +/// Attempting to link in too many shared libraries +pub const ELIBMAX = 82; + +/// Cannot exec a shared library directly +pub const ELIBEXEC = 83; + +/// Illegal byte sequence +pub const EILSEQ = 84; + +/// Interrupted system call should be restarted +pub const ERESTART = 85; + +/// Streams pipe error +pub const ESTRPIPE = 86; + +/// Too many users +pub const EUSERS = 87; + +/// Socket operation on non-socket +pub const ENOTSOCK = 88; + +/// Destination address required +pub const EDESTADDRREQ = 89; + +/// Message too long +pub const EMSGSIZE = 90; + +/// Protocol wrong type for socket +pub const EPROTOTYPE = 91; + +/// Protocol not available +pub const ENOPROTOOPT = 92; + +/// Protocol not supported +pub const EPROTONOSUPPORT = 93; + +/// Socket type not supported +pub const ESOCKTNOSUPPORT = 94; + +/// Operation not supported on transport endpoint +pub const EOPNOTSUPP = 95; + +/// Protocol family not supported +pub const EPFNOSUPPORT = 96; + +/// Address family not supported by protocol +pub const EAFNOSUPPORT = 97; + +/// Address already in use +pub const EADDRINUSE = 98; + +/// Cannot assign requested address +pub const EADDRNOTAVAIL = 99; + +/// Network is down +pub const ENETDOWN = 100; + +/// Network is unreachable +pub const ENETUNREACH = 101; + +/// Network dropped connection because of reset +pub const ENETRESET = 102; + +/// Software caused connection abort +pub const ECONNABORTED = 103; + +/// Connection reset by peer +pub const ECONNRESET = 104; + +/// No buffer space available +pub const ENOBUFS = 105; + +/// Transport endpoint is already connected +pub const EISCONN = 106; + +/// Transport endpoint is not connected +pub const ENOTCONN = 107; + +/// Cannot send after transport endpoint shutdown +pub const ESHUTDOWN = 108; + +/// Too many references: cannot splice +pub const ETOOMANYREFS = 109; + +/// Connection timed out +pub const ETIMEDOUT = 110; + +/// Connection refused +pub const ECONNREFUSED = 111; + +/// Host is down +pub const EHOSTDOWN = 112; + +/// No route to host +pub const EHOSTUNREACH = 113; + +/// Operation already in progress +pub const EALREADY = 114; + +/// Operation now in progress +pub const EINPROGRESS = 115; + +/// Stale NFS file handle +pub const ESTALE = 116; + +/// Structure needs cleaning +pub const EUCLEAN = 117; + +/// Not a XENIX named type file +pub const ENOTNAM = 118; + +/// No XENIX semaphores available +pub const ENAVAIL = 119; + +/// Is a named type file +pub const EISNAM = 120; + +/// Remote I/O error +pub const EREMOTEIO = 121; + +/// Quota exceeded +pub const EDQUOT = 122; + +/// No medium found +pub const ENOMEDIUM = 123; + +/// Wrong medium type +pub const EMEDIUMTYPE = 124; // nameserver query return codes -pub const ENSROK = 0; /// DNS server returned answer with no data -pub const ENSRNODATA = 160; /// DNS server returned answer with no data -pub const ENSRFORMERR = 161; /// DNS server claims query was misformatted -pub const ENSRSERVFAIL = 162; /// DNS server returned general failure -pub const ENSRNOTFOUND = 163; /// Domain name not found -pub const ENSRNOTIMP = 164; /// DNS server does not implement requested operation -pub const ENSRREFUSED = 165; /// DNS server refused query -pub const ENSRBADQUERY = 166; /// Misformatted DNS query -pub const ENSRBADNAME = 167; /// Misformatted domain name -pub const ENSRBADFAMILY = 168; /// Unsupported address family -pub const ENSRBADRESP = 169; /// Misformatted DNS reply -pub const ENSRCONNREFUSED = 170; /// Could not contact DNS servers -pub const ENSRTIMEOUT = 171; /// Timeout while contacting DNS servers -pub const ENSROF = 172; /// End of file -pub const ENSRFILE = 173; /// Error reading file -pub const ENSRNOMEM = 174; /// Out of memory -pub const ENSRDESTRUCTION = 175; /// Application terminated lookup -pub const ENSRQUERYDOMAINTOOLONG = 176; /// Domain name is too long -pub const ENSRCNAMELOOP = 177; /// Domain name is too long + +/// DNS server returned answer with no data +pub const ENSROK = 0; + +/// DNS server returned answer with no data +pub const ENSRNODATA = 160; + +/// DNS server claims query was misformatted +pub const ENSRFORMERR = 161; + +/// DNS server returned general failure +pub const ENSRSERVFAIL = 162; + +/// Domain name not found +pub const ENSRNOTFOUND = 163; + +/// DNS server does not implement requested operation +pub const ENSRNOTIMP = 164; + +/// DNS server refused query +pub const ENSRREFUSED = 165; + +/// Misformatted DNS query +pub const ENSRBADQUERY = 166; + +/// Misformatted domain name +pub const ENSRBADNAME = 167; + +/// Unsupported address family +pub const ENSRBADFAMILY = 168; + +/// Misformatted DNS reply +pub const ENSRBADRESP = 169; + +/// Could not contact DNS servers +pub const ENSRCONNREFUSED = 170; + +/// Timeout while contacting DNS servers +pub const ENSRTIMEOUT = 171; + +/// End of file +pub const ENSROF = 172; + +/// Error reading file +pub const ENSRFILE = 173; + +/// Out of memory +pub const ENSRNOMEM = 174; + +/// Application terminated lookup +pub const ENSRDESTRUCTION = 175; + +/// Domain name is too long +pub const ENSRQUERYDOMAINTOOLONG = 176; + +/// Domain name is too long +pub const ENSRCNAMELOOP = 177; diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 4e0e9ee5d5..1cf5bbd432 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -823,8 +823,7 @@ pub fn clock_gettime(clk_id: i32, tp: ×pec) usize { if (@ptrToInt(f) != 0) { const rc = f(clk_id, tp); switch (rc) { - 0, - @bitCast(usize, isize(-EINVAL)) => return rc, + 0, @bitCast(usize, isize(-EINVAL)) => return rc, else => {}, } } diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig index 18a6e5f19f..06aae1968f 100644 --- a/std/os/linux/test.zig +++ b/std/os/linux/test.zig @@ -11,22 +11,22 @@ test "timer" { const timer_fd = linux.timerfd_create(linux.CLOCK_MONOTONIC, 0); assert(linux.getErrno(timer_fd) == 0); - const time_interval = linux.timespec { + const time_interval = linux.timespec{ .tv_sec = 0, - .tv_nsec = 2000000 + .tv_nsec = 2000000, }; - const new_time = linux.itimerspec { + const new_time = linux.itimerspec{ .it_interval = time_interval, - .it_value = time_interval + .it_value = time_interval, }; err = linux.timerfd_settime(i32(timer_fd), 0, &new_time, null); assert(err == 0); - var event = linux.epoll_event { + var event = linux.epoll_event{ .events = linux.EPOLLIN | linux.EPOLLOUT | linux.EPOLLET, - .data = linux.epoll_data { .ptr = 0 }, + .data = linux.epoll_data{ .ptr = 0 }, }; err = linux.epoll_ctl(i32(epoll_fd), linux.EPOLL_CTL_ADD, i32(timer_fd), &event); diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig index f4fb513af9..8e0a285841 100644 --- a/std/os/linux/vdso.zig +++ b/std/os/linux/vdso.zig @@ -16,7 +16,10 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { var base: usize = @maxValue(usize); { var i: usize = 0; - while (i < eh.e_phnum) : ({i += 1; ph_addr += eh.e_phentsize;}) { + while (i < eh.e_phnum) : ({ + i += 1; + ph_addr += eh.e_phentsize; + }) { const this_ph = @intToPtr(&elf.Phdr, ph_addr); switch (this_ph.p_type) { elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr, @@ -54,15 +57,14 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { const hashtab = maybe_hashtab ?? return 0; if (maybe_verdef == null) maybe_versym = null; - - const OK_TYPES = (1<>4) & OK_BINDS)) continue; - if (0==syms[i].st_shndx) continue; + if (0 == (u32(1) << u5(syms[i].st_info & 0xf) & OK_TYPES)) continue; + if (0 == (u32(1) << u5(syms[i].st_info >> 4) & OK_BINDS)) continue; + if (0 == syms[i].st_shndx) continue; if (!mem.eql(u8, name, cstr.toSliceConst(&strings[syms[i].st_name]))) continue; if (maybe_versym) |versym| { if (!checkver(??maybe_verdef, versym[i], vername, strings)) @@ -78,12 +80,12 @@ fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: & var def = def_arg; const vsym = @bitCast(u32, vsym_arg) & 0x7fff; while (true) { - if (0==(def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym) + if (0 == (def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym) break; if (def.vd_next == 0) return false; def = @intToPtr(&elf.Verdef, @ptrToInt(def) + def.vd_next); } - const aux = @intToPtr(&elf.Verdaux, @ptrToInt(def ) + def.vd_aux); + const aux = @intToPtr(&elf.Verdaux, @ptrToInt(def) + def.vd_aux); return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name])); } diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index 544b2365ce..ed32d59688 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -330,26 +330,26 @@ pub const SYS_userfaultfd = 323; pub const SYS_membarrier = 324; pub const SYS_mlock2 = 325; -pub const O_CREAT = 0o100; -pub const O_EXCL = 0o200; -pub const O_NOCTTY = 0o400; -pub const O_TRUNC = 0o1000; -pub const O_APPEND = 0o2000; -pub const O_NONBLOCK = 0o4000; -pub const O_DSYNC = 0o10000; -pub const O_SYNC = 0o4010000; -pub const O_RSYNC = 0o4010000; +pub const O_CREAT = 0o100; +pub const O_EXCL = 0o200; +pub const O_NOCTTY = 0o400; +pub const O_TRUNC = 0o1000; +pub const O_APPEND = 0o2000; +pub const O_NONBLOCK = 0o4000; +pub const O_DSYNC = 0o10000; +pub const O_SYNC = 0o4010000; +pub const O_RSYNC = 0o4010000; pub const O_DIRECTORY = 0o200000; -pub const O_NOFOLLOW = 0o400000; -pub const O_CLOEXEC = 0o2000000; +pub const O_NOFOLLOW = 0o400000; +pub const O_CLOEXEC = 0o2000000; -pub const O_ASYNC = 0o20000; -pub const O_DIRECT = 0o40000; -pub const O_LARGEFILE = 0; -pub const O_NOATIME = 0o1000000; -pub const O_PATH = 0o10000000; +pub const O_ASYNC = 0o20000; +pub const O_DIRECT = 0o40000; +pub const O_LARGEFILE = 0; +pub const O_NOATIME = 0o1000000; +pub const O_PATH = 0o10000000; pub const O_TMPFILE = 0o20200000; -pub const O_NDELAY = O_NONBLOCK; +pub const O_NDELAY = O_NONBLOCK; pub const F_DUPFD = 0; pub const F_GETFD = 1; @@ -371,7 +371,6 @@ pub const F_GETOWN_EX = 16; pub const F_GETOWNER_UIDS = 17; - pub const VDSO_USEFUL = true; pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; pub const VDSO_CGT_VER = "LINUX_2.6"; @@ -382,72 +381,85 @@ pub fn syscall0(number: usize) usize { return asm volatile ("syscall" : [ret] "={rax}" (-> usize) : [number] "{rax}" (number) - : "rcx", "r11"); + : "rcx", "r11" + ); } pub fn syscall1(number: usize, arg1: usize) usize { return asm volatile ("syscall" : [ret] "={rax}" (-> usize) : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1) - : "rcx", "r11"); + [arg1] "{rdi}" (arg1) + : "rcx", "r11" + ); } pub fn syscall2(number: usize, arg1: usize, arg2: usize) usize { return asm volatile ("syscall" : [ret] "={rax}" (-> usize) : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1), - [arg2] "{rsi}" (arg2) - : "rcx", "r11"); + [arg1] "{rdi}" (arg1), + [arg2] "{rsi}" (arg2) + : "rcx", "r11" + ); } pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize { return asm volatile ("syscall" : [ret] "={rax}" (-> usize) : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1), - [arg2] "{rsi}" (arg2), - [arg3] "{rdx}" (arg3) - : "rcx", "r11"); + [arg1] "{rdi}" (arg1), + [arg2] "{rsi}" (arg2), + [arg3] "{rdx}" (arg3) + : "rcx", "r11" + ); } pub fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { return asm volatile ("syscall" : [ret] "={rax}" (-> usize) : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1), - [arg2] "{rsi}" (arg2), - [arg3] "{rdx}" (arg3), - [arg4] "{r10}" (arg4) - : "rcx", "r11"); + [arg1] "{rdi}" (arg1), + [arg2] "{rsi}" (arg2), + [arg3] "{rdx}" (arg3), + [arg4] "{r10}" (arg4) + : "rcx", "r11" + ); } pub fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { return asm volatile ("syscall" : [ret] "={rax}" (-> usize) : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1), - [arg2] "{rsi}" (arg2), - [arg3] "{rdx}" (arg3), - [arg4] "{r10}" (arg4), - [arg5] "{r8}" (arg5) - : "rcx", "r11"); + [arg1] "{rdi}" (arg1), + [arg2] "{rsi}" (arg2), + [arg3] "{rdx}" (arg3), + [arg4] "{r10}" (arg4), + [arg5] "{r8}" (arg5) + : "rcx", "r11" + ); } -pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, - arg5: usize, arg6: usize) usize -{ +pub fn syscall6( + number: usize, + arg1: usize, + arg2: usize, + arg3: usize, + arg4: usize, + arg5: usize, + arg6: usize, +) usize { return asm volatile ("syscall" : [ret] "={rax}" (-> usize) : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1), - [arg2] "{rsi}" (arg2), - [arg3] "{rdx}" (arg3), - [arg4] "{r10}" (arg4), - [arg5] "{r8}" (arg5), - [arg6] "{r9}" (arg6) - : "rcx", "r11"); + [arg1] "{rdi}" (arg1), + [arg2] "{rsi}" (arg2), + [arg3] "{rdx}" (arg3), + [arg4] "{r10}" (arg4), + [arg5] "{r8}" (arg5), + [arg6] "{r9}" (arg6) + : "rcx", "r11" + ); } /// This matches the libc clone function. @@ -457,10 +469,10 @@ pub nakedcc fn restore_rt() void { return asm volatile ("syscall" : : [number] "{rax}" (usize(SYS_rt_sigreturn)) - : "rcx", "r11"); + : "rcx", "r11" + ); } - pub const msghdr = extern struct { msg_name: &u8, msg_namelen: socklen_t, diff --git a/std/os/path.zig b/std/os/path.zig index 0ea5d5a753..5b2b031aab 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -55,9 +55,7 @@ test "os.path.join" { assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\", "a", "b\\", "c"), "c:\\a\\b\\c")); assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\", "b\\", "c"), "c:\\a\\b\\c")); - assert(mem.eql(u8, try joinWindows(debug.global_allocator, - "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig"), - "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig")); + assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig"), "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig")); assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/b", "c"), "/a/b/c")); assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/b/", "c"), "/a/b/c")); @@ -65,8 +63,7 @@ test "os.path.join" { assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/", "a", "b/", "c"), "/a/b/c")); assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/", "b/", "c"), "/a/b/c")); - assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/home/andy/dev/zig/build/lib/zig/std", "io.zig"), - "/home/andy/dev/zig/build/lib/zig/std/io.zig")); + assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/home/andy/dev/zig/build/lib/zig/std", "io.zig"), "/home/andy/dev/zig/build/lib/zig/std/io.zig")); } pub fn isAbsolute(path: []const u8) bool { @@ -151,22 +148,22 @@ pub const WindowsPath = struct { pub fn windowsParsePath(path: []const u8) WindowsPath { if (path.len >= 2 and path[1] == ':') { - return WindowsPath { + return WindowsPath{ .is_abs = isAbsoluteWindows(path), .kind = WindowsPath.Kind.Drive, .disk_designator = path[0..2], }; } if (path.len >= 1 and (path[0] == '/' or path[0] == '\\') and - (path.len == 1 or (path[1] != '/' and path[1] != '\\'))) + (path.len == 1 or (path[1] != '/' and path[1] != '\\'))) { - return WindowsPath { + return WindowsPath{ .is_abs = true, .kind = WindowsPath.Kind.None, .disk_designator = path[0..0], }; } - const relative_path = WindowsPath { + const relative_path = WindowsPath{ .kind = WindowsPath.Kind.None, .disk_designator = []u8{}, .is_abs = false, @@ -178,7 +175,7 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { // TODO when I combined these together with `inline for` the compiler crashed { const this_sep = '/'; - const two_sep = []u8{this_sep, this_sep}; + const two_sep = []u8{ this_sep, this_sep }; if (mem.startsWith(u8, path, two_sep)) { if (path[2] == this_sep) { return relative_path; @@ -187,7 +184,7 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { var it = mem.split(path, []u8{this_sep}); _ = (it.next() ?? return relative_path); _ = (it.next() ?? return relative_path); - return WindowsPath { + return WindowsPath{ .is_abs = isAbsoluteWindows(path), .kind = WindowsPath.Kind.NetworkShare, .disk_designator = path[0..it.index], @@ -196,7 +193,7 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { } { const this_sep = '\\'; - const two_sep = []u8{this_sep, this_sep}; + const two_sep = []u8{ this_sep, this_sep }; if (mem.startsWith(u8, path, two_sep)) { if (path[2] == this_sep) { return relative_path; @@ -205,7 +202,7 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { var it = mem.split(path, []u8{this_sep}); _ = (it.next() ?? return relative_path); _ = (it.next() ?? return relative_path); - return WindowsPath { + return WindowsPath{ .is_abs = isAbsoluteWindows(path), .kind = WindowsPath.Kind.NetworkShare, .disk_designator = path[0..it.index], @@ -296,7 +293,7 @@ fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8 fn asciiUpper(byte: u8) u8 { return switch (byte) { - 'a' ... 'z' => 'A' + (byte - 'a'), + 'a'...'z' => 'A' + (byte - 'a'), else => byte, }; } @@ -372,7 +369,6 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) ![]u8 { max_size += p.len + 1; } - // if we will result with a disk designator, loop again to determine // which is the last time the disk designator is absolutely specified, if any // and count up the max bytes for paths related to this disk designator @@ -386,8 +382,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) ![]u8 { const parsed = windowsParsePath(p); if (parsed.kind != WindowsPath.Kind.None) { if (parsed.kind == have_drive_kind) { - correct_disk_designator = compareDiskDesignators(have_drive_kind, - result_disk_designator, parsed.disk_designator); + correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator); } else { continue; } @@ -404,7 +399,6 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) ![]u8 { } } - // Allocate result and fill in the disk designator, calling getCwd if we have to. var result: []u8 = undefined; var result_index: usize = 0; @@ -433,7 +427,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) ![]u8 { result_index += 1; mem.copy(u8, result[result_index..], other_name); result_index += other_name.len; - + result_disk_designator = result[0..result_index]; }, WindowsPath.Kind.None => { @@ -478,8 +472,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) ![]u8 { if (parsed.kind != WindowsPath.Kind.None) { if (parsed.kind == have_drive_kind) { - correct_disk_designator = compareDiskDesignators(have_drive_kind, - result_disk_designator, parsed.disk_designator); + correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator); } else { continue; } @@ -591,7 +584,7 @@ test "os.path.resolve" { } assert(mem.eql(u8, testResolveWindows([][]const u8{"."}), cwd)); } else { - assert(mem.eql(u8, testResolvePosix([][]const u8{"a/b/c/", "../../.."}), cwd)); + assert(mem.eql(u8, testResolvePosix([][]const u8{ "a/b/c/", "../../.." }), cwd)); assert(mem.eql(u8, testResolvePosix([][]const u8{"."}), cwd)); } } @@ -601,16 +594,15 @@ test "os.path.resolveWindows" { const cwd = try os.getCwd(debug.global_allocator); const parsed_cwd = windowsParsePath(cwd); { - const result = testResolveWindows([][]const u8{"/usr/local", "lib\\zig\\std\\array_list.zig"}); - const expected = try join(debug.global_allocator, - parsed_cwd.disk_designator, "usr\\local\\lib\\zig\\std\\array_list.zig"); + const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }); + const expected = try join(debug.global_allocator, parsed_cwd.disk_designator, "usr\\local\\lib\\zig\\std\\array_list.zig"); if (parsed_cwd.kind == WindowsPath.Kind.Drive) { expected[0] = asciiUpper(parsed_cwd.disk_designator[0]); } assert(mem.eql(u8, result, expected)); } { - const result = testResolveWindows([][]const u8{"usr/local", "lib\\zig"}); + const result = testResolveWindows([][]const u8{ "usr/local", "lib\\zig" }); const expected = try join(debug.global_allocator, cwd, "usr\\local\\lib\\zig"); if (parsed_cwd.kind == WindowsPath.Kind.Drive) { expected[0] = asciiUpper(parsed_cwd.disk_designator[0]); @@ -619,33 +611,32 @@ test "os.path.resolveWindows" { } } - assert(mem.eql(u8, testResolveWindows([][]const u8{"c:\\a\\b\\c", "/hi", "ok"}), "C:\\hi\\ok")); - assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "c:../a"}), "C:\\blah\\a")); - assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "C:../a"}), "C:\\blah\\a")); - assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "d:\\a/b\\c/d", "\\e.exe"}), "D:\\e.exe")); - assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "c:/some/file"}), "C:\\some\\file")); - assert(mem.eql(u8, testResolveWindows([][]const u8{"d:/ignore", "d:some/dir//"}), "D:\\ignore\\some\\dir")); - assert(mem.eql(u8, testResolveWindows([][]const u8{"//server/share", "..", "relative\\"}), "\\\\server\\share\\relative")); - assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//"}), "C:\\")); - assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//dir"}), "C:\\dir")); - assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//server/share"}), "\\\\server\\share\\")); - assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//server//share"}), "\\\\server\\share\\")); - assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "///some//dir"}), "C:\\some\\dir")); - assert(mem.eql(u8, testResolveWindows([][]const u8{"C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js"}), - "C:\\foo\\tmp.3\\cycles\\root.js")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:\\a\\b\\c", "/hi", "ok" }), "C:\\hi\\ok")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/blah\\blah", "d:/games", "c:../a" }), "C:\\blah\\a")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/blah\\blah", "d:/games", "C:../a" }), "C:\\blah\\a")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/ignore", "d:\\a/b\\c/d", "\\e.exe" }), "D:\\e.exe")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/ignore", "c:/some/file" }), "C:\\some\\file")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "d:/ignore", "d:some/dir//" }), "D:\\ignore\\some\\dir")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "//server/share", "..", "relative\\" }), "\\\\server\\share\\relative")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/", "//" }), "C:\\")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/", "//dir" }), "C:\\dir")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/", "//server/share" }), "\\\\server\\share\\")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/", "//server//share" }), "\\\\server\\share\\")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "c:/", "///some//dir" }), "C:\\some\\dir")); + assert(mem.eql(u8, testResolveWindows([][]const u8{ "C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js" }), "C:\\foo\\tmp.3\\cycles\\root.js")); } test "os.path.resolvePosix" { - assert(mem.eql(u8, testResolvePosix([][]const u8{"/a/b", "c"}), "/a/b/c")); - assert(mem.eql(u8, testResolvePosix([][]const u8{"/a/b", "c", "//d", "e///"}), "/d/e")); - assert(mem.eql(u8, testResolvePosix([][]const u8{"/a/b/c", "..", "../"}), "/a")); - assert(mem.eql(u8, testResolvePosix([][]const u8{"/", "..", ".."}), "/")); + assert(mem.eql(u8, testResolvePosix([][]const u8{ "/a/b", "c" }), "/a/b/c")); + assert(mem.eql(u8, testResolvePosix([][]const u8{ "/a/b", "c", "//d", "e///" }), "/d/e")); + assert(mem.eql(u8, testResolvePosix([][]const u8{ "/a/b/c", "..", "../" }), "/a")); + assert(mem.eql(u8, testResolvePosix([][]const u8{ "/", "..", ".." }), "/")); assert(mem.eql(u8, testResolvePosix([][]const u8{"/a/b/c/"}), "/a/b/c")); - assert(mem.eql(u8, testResolvePosix([][]const u8{"/var/lib", "../", "file/"}), "/var/file")); - assert(mem.eql(u8, testResolvePosix([][]const u8{"/var/lib", "/../", "file/"}), "/file")); - assert(mem.eql(u8, testResolvePosix([][]const u8{"/some/dir", ".", "/absolute/"}), "/absolute")); - assert(mem.eql(u8, testResolvePosix([][]const u8{"/foo/tmp.3/", "../tmp.3/cycles/root.js"}), "/foo/tmp.3/cycles/root.js")); + assert(mem.eql(u8, testResolvePosix([][]const u8{ "/var/lib", "../", "file/" }), "/var/file")); + assert(mem.eql(u8, testResolvePosix([][]const u8{ "/var/lib", "/../", "file/" }), "/file")); + assert(mem.eql(u8, testResolvePosix([][]const u8{ "/some/dir", ".", "/absolute/" }), "/absolute")); + assert(mem.eql(u8, testResolvePosix([][]const u8{ "/foo/tmp.3/", "../tmp.3/cycles/root.js" }), "/foo/tmp.3/cycles/root.js")); } fn testResolveWindows(paths: []const []const u8) []u8 { @@ -1079,9 +1070,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) ![]u8 { mem.copy(u8, pathname_buf, pathname); pathname_buf[pathname.len] = 0; - const h_file = windows.CreateFileA(pathname_buf.ptr, - windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING, - windows.FILE_ATTRIBUTE_NORMAL, null); + const h_file = windows.CreateFileA(pathname_buf.ptr, windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null); if (h_file == windows.INVALID_HANDLE_VALUE) { const err = windows.GetLastError(); return switch (err) { @@ -1161,7 +1150,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) ![]u8 { return allocator.shrink(u8, result_buf, cstr.len(result_buf.ptr)); }, Os.linux => { - const fd = try os.posixOpen(allocator, pathname, posix.O_PATH|posix.O_NONBLOCK|posix.O_CLOEXEC, 0); + const fd = try os.posixOpen(allocator, pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0); defer os.close(fd); var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined; diff --git a/std/os/time.zig b/std/os/time.zig index 3af150ab6a..9a7c682483 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -27,7 +27,7 @@ pub fn sleep(seconds: usize, nanoseconds: usize) void { const u63 = @IntType(false, 63); pub fn posixSleep(seconds: u63, nanoseconds: u63) void { - var req = posix.timespec { + var req = posix.timespec{ .tv_sec = seconds, .tv_nsec = nanoseconds, }; @@ -71,7 +71,7 @@ fn milliTimestampWindows() u64 { var ft: i64 = undefined; windows.GetSystemTimeAsFileTime(&ft); const hns_per_ms = (ns_per_s / 100) / ms_per_s; - const epoch_adj = epoch.windows * ms_per_s; + const epoch_adj = epoch.windows * ms_per_s; return u64(@divFloor(ft, hns_per_ms) + epoch_adj); } @@ -83,7 +83,7 @@ fn milliTimestampDarwin() u64 { debug.assert(err == 0); const sec_ms = u64(tv.tv_sec) * ms_per_s; const usec_ms = @divFloor(u64(tv.tv_usec), us_per_s / ms_per_s); - return u64(sec_ms) + u64(usec_ms); + return u64(sec_ms) + u64(usec_ms); } fn milliTimestampPosix() u64 { @@ -110,17 +110,16 @@ pub const s_per_hour = s_per_min * 60; pub const s_per_day = s_per_hour * 24; pub const s_per_week = s_per_day * 7; - /// A monotonic high-performance timer. /// Timer.start() must be called to initialize the struct, which captures /// the counter frequency on windows and darwin, records the resolution, /// and gives the user an oportunity to check for the existnece of /// monotonic clocks without forcing them to check for error on each read. -/// .resolution is in nanoseconds on all platforms but .start_time's meaning -/// depends on the OS. On Windows and Darwin it is a hardware counter +/// .resolution is in nanoseconds on all platforms but .start_time's meaning +/// depends on the OS. On Windows and Darwin it is a hardware counter /// value that requires calculation to convert to a meaninful unit. pub const Timer = struct { - + //if we used resolution's value when performing the // performance counter calc on windows/darwin, it would // be less precise @@ -131,10 +130,9 @@ pub const Timer = struct { }, resolution: u64, start_time: u64, - - + //At some point we may change our minds on RAW, but for now we're - // sticking with posix standard MONOTONIC. For more information, see: + // sticking with posix standard MONOTONIC. For more information, see: // https://github.com/ziglang/zig/pull/933 // //const monotonic_clock_id = switch(builtin.os) { @@ -142,20 +140,21 @@ pub const Timer = struct { // else => posix.CLOCK_MONOTONIC, //}; const monotonic_clock_id = posix.CLOCK_MONOTONIC; - - /// Initialize the timer structure. //This gives us an oportunity to grab the counter frequency in windows. //On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000. - //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not - // supported, or if the timespec pointer is out of bounds, which should be + //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not + // supported, or if the timespec pointer is out of bounds, which should be // impossible here barring cosmic rays or other such occurances of // incredibly bad luck. //On Darwin: This cannot fail, as far as I am able to tell. - const TimerError = error{TimerUnsupported, Unexpected}; + const TimerError = error{ + TimerUnsupported, + Unexpected, + }; pub fn start() TimerError!Timer { var self: Timer = undefined; - + switch (builtin.os) { Os.windows => { var freq: i64 = undefined; @@ -163,7 +162,7 @@ pub const Timer = struct { if (err == windows.FALSE) return error.TimerUnsupported; self.frequency = u64(freq); self.resolution = @divFloor(ns_per_s, self.frequency); - + var start_time: i64 = undefined; err = windows.QueryPerformanceCounter(&start_time); debug.assert(err != windows.FALSE); @@ -171,9 +170,9 @@ pub const Timer = struct { }, Os.linux => { //On Linux, seccomp can do arbitrary things to our ability to call - // syscalls, including return any errno value it wants and + // syscalls, including return any errno value it wants and // inconsistently throwing errors. Since we can't account for - // abuses of seccomp in a reasonable way, we'll assume that if + // abuses of seccomp in a reasonable way, we'll assume that if // seccomp is going to block us it will at least do so consistently var ts: posix.timespec = undefined; var result = posix.clock_getres(monotonic_clock_id, &ts); @@ -184,7 +183,7 @@ pub const Timer = struct { else => return std.os.unexpectedErrorPosix(errno), } self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); - + result = posix.clock_gettime(monotonic_clock_id, &ts); errno = posix.getErrno(result); if (errno != 0) return std.os.unexpectedErrorPosix(errno); @@ -199,7 +198,7 @@ pub const Timer = struct { } return self; } - + /// Reads the timer value since start or the last reset in nanoseconds pub fn read(self: &Timer) u64 { var clock = clockNative() - self.start_time; @@ -210,13 +209,12 @@ pub const Timer = struct { else => @compileError("Unsupported OS"), }; } - + /// Resets the timer value to 0/now. - pub fn reset(self: &Timer) void - { + pub fn reset(self: &Timer) void { self.start_time = clockNative(); } - + /// Returns the current value of the timer in nanoseconds, then resets it pub fn lap(self: &Timer) u64 { var now = clockNative(); @@ -224,26 +222,25 @@ pub const Timer = struct { self.start_time = now; return lap_time; } - - + const clockNative = switch (builtin.os) { Os.windows => clockWindows, Os.linux => clockLinux, Os.macosx, Os.ios => clockDarwin, else => @compileError("Unsupported OS"), }; - + fn clockWindows() u64 { var result: i64 = undefined; var err = windows.QueryPerformanceCounter(&result); debug.assert(err != windows.FALSE); return u64(result); } - + fn clockDarwin() u64 { return darwin.mach_absolute_time(); } - + fn clockLinux() u64 { var ts: posix.timespec = undefined; var result = posix.clock_gettime(monotonic_clock_id, &ts); @@ -252,10 +249,6 @@ pub const Timer = struct { } }; - - - - test "os.time.sleep" { sleep(0, 1); } @@ -263,7 +256,7 @@ test "os.time.sleep" { test "os.time.timestamp" { const ns_per_ms = (ns_per_s / ms_per_s); const margin = 50; - + const time_0 = milliTimestamp(); sleep(0, ns_per_ms); const time_1 = milliTimestamp(); @@ -274,15 +267,15 @@ test "os.time.timestamp" { test "os.time.Timer" { const ns_per_ms = (ns_per_s / ms_per_s); const margin = ns_per_ms * 50; - + var timer = try Timer.start(); sleep(0, 10 * ns_per_ms); const time_0 = timer.read(); debug.assert(time_0 > 0 and time_0 < margin); - + const time_1 = timer.lap(); debug.assert(time_1 >= time_0); - + timer.reset(); debug.assert(timer.read() < time_1); } diff --git a/std/os/windows/error.zig b/std/os/windows/error.zig index 6a4087ab97..f90945d00e 100644 --- a/std/os/windows/error.zig +++ b/std/os/windows/error.zig @@ -1,2379 +1,3567 @@ /// The operation completed successfully. pub const SUCCESS = 0; + /// Incorrect function. pub const INVALID_FUNCTION = 1; + /// The system cannot find the file specified. pub const FILE_NOT_FOUND = 2; + /// The system cannot find the path specified. pub const PATH_NOT_FOUND = 3; + /// The system cannot open the file. pub const TOO_MANY_OPEN_FILES = 4; + /// Access is denied. pub const ACCESS_DENIED = 5; + /// The handle is invalid. pub const INVALID_HANDLE = 6; + /// The storage control blocks were destroyed. pub const ARENA_TRASHED = 7; + /// Not enough storage is available to process this command. pub const NOT_ENOUGH_MEMORY = 8; + /// The storage control block address is invalid. pub const INVALID_BLOCK = 9; + /// The environment is incorrect. pub const BAD_ENVIRONMENT = 10; + /// An attempt was made to load a program with an incorrect format. pub const BAD_FORMAT = 11; + /// The access code is invalid. pub const INVALID_ACCESS = 12; + /// The data is invalid. pub const INVALID_DATA = 13; + /// Not enough storage is available to complete this operation. pub const OUTOFMEMORY = 14; + /// The system cannot find the drive specified. pub const INVALID_DRIVE = 15; + /// The directory cannot be removed. pub const CURRENT_DIRECTORY = 16; + /// The system cannot move the file to a different disk drive. pub const NOT_SAME_DEVICE = 17; + /// There are no more files. pub const NO_MORE_FILES = 18; + /// The media is write protected. pub const WRITE_PROTECT = 19; + /// The system cannot find the device specified. pub const BAD_UNIT = 20; + /// The device is not ready. pub const NOT_READY = 21; + /// The device does not recognize the command. pub const BAD_COMMAND = 22; + /// Data error (cyclic redundancy check). pub const CRC = 23; + /// The program issued a command but the command length is incorrect. pub const BAD_LENGTH = 24; + /// The drive cannot locate a specific area or track on the disk. pub const SEEK = 25; + /// The specified disk or diskette cannot be accessed. pub const NOT_DOS_DISK = 26; + /// The drive cannot find the sector requested. pub const SECTOR_NOT_FOUND = 27; + /// The printer is out of paper. pub const OUT_OF_PAPER = 28; + /// The system cannot write to the specified device. pub const WRITE_FAULT = 29; + /// The system cannot read from the specified device. pub const READ_FAULT = 30; + /// A device attached to the system is not functioning. pub const GEN_FAILURE = 31; + /// The process cannot access the file because it is being used by another process. pub const SHARING_VIOLATION = 32; + /// The process cannot access the file because another process has locked a portion of the file. pub const LOCK_VIOLATION = 33; + /// The wrong diskette is in the drive. Insert %2 (Volume Serial Number: %3) into drive %1. pub const WRONG_DISK = 34; + /// Too many files opened for sharing. pub const SHARING_BUFFER_EXCEEDED = 36; + /// Reached the end of the file. pub const HANDLE_EOF = 38; + /// The disk is full. pub const HANDLE_DISK_FULL = 39; + /// The request is not supported. pub const NOT_SUPPORTED = 50; + /// Windows cannot find the network path. Verify that the network path is correct and the destination computer is not busy or turned off. If Windows still cannot find the network path, contact your network administrator. pub const REM_NOT_LIST = 51; + /// You were not connected because a duplicate name exists on the network. If joining a domain, go to System in Control Panel to change the computer name and try again. If joining a workgroup, choose another workgroup name. pub const DUP_NAME = 52; + /// The network path was not found. pub const BAD_NETPATH = 53; + /// The network is busy. pub const NETWORK_BUSY = 54; + /// The specified network resource or device is no longer available. pub const DEV_NOT_EXIST = 55; + /// The network BIOS command limit has been reached. pub const TOO_MANY_CMDS = 56; + /// A network adapter hardware error occurred. pub const ADAP_HDW_ERR = 57; + /// The specified server cannot perform the requested operation. pub const BAD_NET_RESP = 58; + /// An unexpected network error occurred. pub const UNEXP_NET_ERR = 59; + /// The remote adapter is not compatible. pub const BAD_REM_ADAP = 60; + /// The printer queue is full. pub const PRINTQ_FULL = 61; + /// Space to store the file waiting to be printed is not available on the server. pub const NO_SPOOL_SPACE = 62; + /// Your file waiting to be printed was deleted. pub const PRINT_CANCELLED = 63; + /// The specified network name is no longer available. pub const NETNAME_DELETED = 64; + /// Network access is denied. pub const NETWORK_ACCESS_DENIED = 65; + /// The network resource type is not correct. pub const BAD_DEV_TYPE = 66; + /// The network name cannot be found. pub const BAD_NET_NAME = 67; + /// The name limit for the local computer network adapter card was exceeded. pub const TOO_MANY_NAMES = 68; + /// The network BIOS session limit was exceeded. pub const TOO_MANY_SESS = 69; + /// The remote server has been paused or is in the process of being started. pub const SHARING_PAUSED = 70; + /// No more connections can be made to this remote computer at this time because there are already as many connections as the computer can accept. pub const REQ_NOT_ACCEP = 71; + /// The specified printer or disk device has been paused. pub const REDIR_PAUSED = 72; + /// The file exists. pub const FILE_EXISTS = 80; + /// The directory or file cannot be created. pub const CANNOT_MAKE = 82; + /// Fail on INT 24. pub const FAIL_I24 = 83; + /// Storage to process this request is not available. pub const OUT_OF_STRUCTURES = 84; + /// The local device name is already in use. pub const ALREADY_ASSIGNED = 85; + /// The specified network password is not correct. pub const INVALID_PASSWORD = 86; + /// The parameter is incorrect. pub const INVALID_PARAMETER = 87; + /// A write fault occurred on the network. pub const NET_WRITE_FAULT = 88; + /// The system cannot start another process at this time. pub const NO_PROC_SLOTS = 89; + /// Cannot create another system semaphore. pub const TOO_MANY_SEMAPHORES = 100; + /// The exclusive semaphore is owned by another process. pub const EXCL_SEM_ALREADY_OWNED = 101; + /// The semaphore is set and cannot be closed. pub const SEM_IS_SET = 102; + /// The semaphore cannot be set again. pub const TOO_MANY_SEM_REQUESTS = 103; + /// Cannot request exclusive semaphores at interrupt time. pub const INVALID_AT_INTERRUPT_TIME = 104; + /// The previous ownership of this semaphore has ended. pub const SEM_OWNER_DIED = 105; + /// Insert the diskette for drive %1. pub const SEM_USER_LIMIT = 106; + /// The program stopped because an alternate diskette was not inserted. pub const DISK_CHANGE = 107; + /// The disk is in use or locked by another process. pub const DRIVE_LOCKED = 108; + /// The pipe has been ended. pub const BROKEN_PIPE = 109; + /// The system cannot open the device or file specified. pub const OPEN_FAILED = 110; + /// The file name is too long. pub const BUFFER_OVERFLOW = 111; + /// There is not enough space on the disk. pub const DISK_FULL = 112; + /// No more internal file identifiers available. pub const NO_MORE_SEARCH_HANDLES = 113; + /// The target internal file identifier is incorrect. pub const INVALID_TARGET_HANDLE = 114; + /// The IOCTL call made by the application program is not correct. pub const INVALID_CATEGORY = 117; + /// The verify-on-write switch parameter value is not correct. pub const INVALID_VERIFY_SWITCH = 118; + /// The system does not support the command requested. pub const BAD_DRIVER_LEVEL = 119; + /// This function is not supported on this system. pub const CALL_NOT_IMPLEMENTED = 120; + /// The semaphore timeout period has expired. pub const SEM_TIMEOUT = 121; + /// The data area passed to a system call is too small. pub const INSUFFICIENT_BUFFER = 122; + /// The filename, directory name, or volume label syntax is incorrect. pub const INVALID_NAME = 123; + /// The system call level is not correct. pub const INVALID_LEVEL = 124; + /// The disk has no volume label. pub const NO_VOLUME_LABEL = 125; + /// The specified module could not be found. pub const MOD_NOT_FOUND = 126; + /// The specified procedure could not be found. pub const PROC_NOT_FOUND = 127; + /// There are no child processes to wait for. pub const WAIT_NO_CHILDREN = 128; + /// The %1 application cannot be run in Win32 mode. pub const CHILD_NOT_COMPLETE = 129; + /// Attempt to use a file handle to an open disk partition for an operation other than raw disk I/O. pub const DIRECT_ACCESS_HANDLE = 130; + /// An attempt was made to move the file pointer before the beginning of the file. pub const NEGATIVE_SEEK = 131; + /// The file pointer cannot be set on the specified device or file. pub const SEEK_ON_DEVICE = 132; + /// A JOIN or SUBST command cannot be used for a drive that contains previously joined drives. pub const IS_JOIN_TARGET = 133; + /// An attempt was made to use a JOIN or SUBST command on a drive that has already been joined. pub const IS_JOINED = 134; + /// An attempt was made to use a JOIN or SUBST command on a drive that has already been substituted. pub const IS_SUBSTED = 135; + /// The system tried to delete the JOIN of a drive that is not joined. pub const NOT_JOINED = 136; + /// The system tried to delete the substitution of a drive that is not substituted. pub const NOT_SUBSTED = 137; + /// The system tried to join a drive to a directory on a joined drive. pub const JOIN_TO_JOIN = 138; + /// The system tried to substitute a drive to a directory on a substituted drive. pub const SUBST_TO_SUBST = 139; + /// The system tried to join a drive to a directory on a substituted drive. pub const JOIN_TO_SUBST = 140; + /// The system tried to SUBST a drive to a directory on a joined drive. pub const SUBST_TO_JOIN = 141; + /// The system cannot perform a JOIN or SUBST at this time. pub const BUSY_DRIVE = 142; + /// The system cannot join or substitute a drive to or for a directory on the same drive. pub const SAME_DRIVE = 143; + /// The directory is not a subdirectory of the root directory. pub const DIR_NOT_ROOT = 144; + /// The directory is not empty. pub const DIR_NOT_EMPTY = 145; + /// The path specified is being used in a substitute. pub const IS_SUBST_PATH = 146; + /// Not enough resources are available to process this command. pub const IS_JOIN_PATH = 147; + /// The path specified cannot be used at this time. pub const PATH_BUSY = 148; + /// An attempt was made to join or substitute a drive for which a directory on the drive is the target of a previous substitute. pub const IS_SUBST_TARGET = 149; + /// System trace information was not specified in your CONFIG.SYS file, or tracing is disallowed. pub const SYSTEM_TRACE = 150; + /// The number of specified semaphore events for DosMuxSemWait is not correct. pub const INVALID_EVENT_COUNT = 151; + /// DosMuxSemWait did not execute; too many semaphores are already set. pub const TOO_MANY_MUXWAITERS = 152; + /// The DosMuxSemWait list is not correct. pub const INVALID_LIST_FORMAT = 153; + /// The volume label you entered exceeds the label character limit of the target file system. pub const LABEL_TOO_LONG = 154; + /// Cannot create another thread. pub const TOO_MANY_TCBS = 155; + /// The recipient process has refused the signal. pub const SIGNAL_REFUSED = 156; + /// The segment is already discarded and cannot be locked. pub const DISCARDED = 157; + /// The segment is already unlocked. pub const NOT_LOCKED = 158; + /// The address for the thread ID is not correct. pub const BAD_THREADID_ADDR = 159; + /// One or more arguments are not correct. pub const BAD_ARGUMENTS = 160; + /// The specified path is invalid. pub const BAD_PATHNAME = 161; + /// A signal is already pending. pub const SIGNAL_PENDING = 162; + /// No more threads can be created in the system. pub const MAX_THRDS_REACHED = 164; + /// Unable to lock a region of a file. pub const LOCK_FAILED = 167; + /// The requested resource is in use. pub const BUSY = 170; + /// Device's command support detection is in progress. pub const DEVICE_SUPPORT_IN_PROGRESS = 171; + /// A lock request was not outstanding for the supplied cancel region. pub const CANCEL_VIOLATION = 173; + /// The file system does not support atomic changes to the lock type. pub const ATOMIC_LOCKS_NOT_SUPPORTED = 174; + /// The system detected a segment number that was not correct. pub const INVALID_SEGMENT_NUMBER = 180; + /// The operating system cannot run %1. pub const INVALID_ORDINAL = 182; + /// Cannot create a file when that file already exists. pub const ALREADY_EXISTS = 183; + /// The flag passed is not correct. pub const INVALID_FLAG_NUMBER = 186; + /// The specified system semaphore name was not found. pub const SEM_NOT_FOUND = 187; + /// The operating system cannot run %1. pub const INVALID_STARTING_CODESEG = 188; + /// The operating system cannot run %1. pub const INVALID_STACKSEG = 189; + /// The operating system cannot run %1. pub const INVALID_MODULETYPE = 190; + /// Cannot run %1 in Win32 mode. pub const INVALID_EXE_SIGNATURE = 191; + /// The operating system cannot run %1. pub const EXE_MARKED_INVALID = 192; + /// %1 is not a valid Win32 application. pub const BAD_EXE_FORMAT = 193; + /// The operating system cannot run %1. pub const ITERATED_DATA_EXCEEDS_64k = 194; + /// The operating system cannot run %1. pub const INVALID_MINALLOCSIZE = 195; + /// The operating system cannot run this application program. pub const DYNLINK_FROM_INVALID_RING = 196; + /// The operating system is not presently configured to run this application. pub const IOPL_NOT_ENABLED = 197; + /// The operating system cannot run %1. pub const INVALID_SEGDPL = 198; + /// The operating system cannot run this application program. pub const AUTODATASEG_EXCEEDS_64k = 199; + /// The code segment cannot be greater than or equal to 64K. pub const RING2SEG_MUST_BE_MOVABLE = 200; + /// The operating system cannot run %1. pub const RELOC_CHAIN_XEEDS_SEGLIM = 201; + /// The operating system cannot run %1. pub const INFLOOP_IN_RELOC_CHAIN = 202; + /// The system could not find the environment option that was entered. pub const ENVVAR_NOT_FOUND = 203; + /// No process in the command subtree has a signal handler. pub const NO_SIGNAL_SENT = 205; + /// The filename or extension is too long. pub const FILENAME_EXCED_RANGE = 206; + /// The ring 2 stack is in use. pub const RING2_STACK_IN_USE = 207; + /// The global filename characters, * or ?, are entered incorrectly or too many global filename characters are specified. pub const META_EXPANSION_TOO_LONG = 208; + /// The signal being posted is not correct. pub const INVALID_SIGNAL_NUMBER = 209; + /// The signal handler cannot be set. pub const THREAD_1_INACTIVE = 210; + /// The segment is locked and cannot be reallocated. pub const LOCKED = 212; + /// Too many dynamic-link modules are attached to this program or dynamic-link module. pub const TOO_MANY_MODULES = 214; + /// Cannot nest calls to LoadModule. pub const NESTING_NOT_ALLOWED = 215; + /// This version of %1 is not compatible with the version of Windows you're running. Check your computer's system information and then contact the software publisher. pub const EXE_MACHINE_TYPE_MISMATCH = 216; + /// The image file %1 is signed, unable to modify. pub const EXE_CANNOT_MODIFY_SIGNED_BINARY = 217; + /// The image file %1 is strong signed, unable to modify. pub const EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY = 218; + /// This file is checked out or locked for editing by another user. pub const FILE_CHECKED_OUT = 220; + /// The file must be checked out before saving changes. pub const CHECKOUT_REQUIRED = 221; + /// The file type being saved or retrieved has been blocked. pub const BAD_FILE_TYPE = 222; + /// The file size exceeds the limit allowed and cannot be saved. pub const FILE_TOO_LARGE = 223; + /// Access Denied. Before opening files in this location, you must first add the web site to your trusted sites list, browse to the web site, and select the option to login automatically. pub const FORMS_AUTH_REQUIRED = 224; + /// Operation did not complete successfully because the file contains a virus or potentially unwanted software. pub const VIRUS_INFECTED = 225; + /// This file contains a virus or potentially unwanted software and cannot be opened. Due to the nature of this virus or potentially unwanted software, the file has been removed from this location. pub const VIRUS_DELETED = 226; + /// The pipe is local. pub const PIPE_LOCAL = 229; + /// The pipe state is invalid. pub const BAD_PIPE = 230; + /// All pipe instances are busy. pub const PIPE_BUSY = 231; + /// The pipe is being closed. pub const NO_DATA = 232; + /// No process is on the other end of the pipe. pub const PIPE_NOT_CONNECTED = 233; + /// More data is available. pub const MORE_DATA = 234; + /// The session was canceled. pub const VC_DISCONNECTED = 240; + /// The specified extended attribute name was invalid. pub const INVALID_EA_NAME = 254; + /// The extended attributes are inconsistent. pub const EA_LIST_INCONSISTENT = 255; + /// The wait operation timed out. pub const IMEOUT = 258; + /// No more data is available. pub const NO_MORE_ITEMS = 259; + /// The copy functions cannot be used. pub const CANNOT_COPY = 266; + /// The directory name is invalid. pub const DIRECTORY = 267; + /// The extended attributes did not fit in the buffer. pub const EAS_DIDNT_FIT = 275; + /// The extended attribute file on the mounted file system is corrupt. pub const EA_FILE_CORRUPT = 276; + /// The extended attribute table file is full. pub const EA_TABLE_FULL = 277; + /// The specified extended attribute handle is invalid. pub const INVALID_EA_HANDLE = 278; + /// The mounted file system does not support extended attributes. pub const EAS_NOT_SUPPORTED = 282; + /// Attempt to release mutex not owned by caller. pub const NOT_OWNER = 288; + /// Too many posts were made to a semaphore. pub const TOO_MANY_POSTS = 298; + /// Only part of a ReadProcessMemory or WriteProcessMemory request was completed. pub const PARTIAL_COPY = 299; + /// The oplock request is denied. pub const OPLOCK_NOT_GRANTED = 300; + /// An invalid oplock acknowledgment was received by the system. pub const INVALID_OPLOCK_PROTOCOL = 301; + /// The volume is too fragmented to complete this operation. pub const DISK_TOO_FRAGMENTED = 302; + /// The file cannot be opened because it is in the process of being deleted. pub const DELETE_PENDING = 303; + /// Short name settings may not be changed on this volume due to the global registry setting. pub const INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING = 304; + /// Short names are not enabled on this volume. pub const SHORT_NAMES_NOT_ENABLED_ON_VOLUME = 305; + /// The security stream for the given volume is in an inconsistent state. Please run CHKDSK on the volume. pub const SECURITY_STREAM_IS_INCONSISTENT = 306; + /// A requested file lock operation cannot be processed due to an invalid byte range. pub const INVALID_LOCK_RANGE = 307; + /// The subsystem needed to support the image type is not present. pub const IMAGE_SUBSYSTEM_NOT_PRESENT = 308; + /// The specified file already has a notification GUID associated with it. pub const NOTIFICATION_GUID_ALREADY_DEFINED = 309; + /// An invalid exception handler routine has been detected. pub const INVALID_EXCEPTION_HANDLER = 310; + /// Duplicate privileges were specified for the token. pub const DUPLICATE_PRIVILEGES = 311; + /// No ranges for the specified operation were able to be processed. pub const NO_RANGES_PROCESSED = 312; + /// Operation is not allowed on a file system internal file. pub const NOT_ALLOWED_ON_SYSTEM_FILE = 313; + /// The physical resources of this disk have been exhausted. pub const DISK_RESOURCES_EXHAUSTED = 314; + /// The token representing the data is invalid. pub const INVALID_TOKEN = 315; + /// The device does not support the command feature. pub const DEVICE_FEATURE_NOT_SUPPORTED = 316; + /// The system cannot find message text for message number 0x%1 in the message file for %2. pub const MR_MID_NOT_FOUND = 317; + /// The scope specified was not found. pub const SCOPE_NOT_FOUND = 318; + /// The Central Access Policy specified is not defined on the target machine. pub const UNDEFINED_SCOPE = 319; + /// The Central Access Policy obtained from Active Directory is invalid. pub const INVALID_CAP = 320; + /// The device is unreachable. pub const DEVICE_UNREACHABLE = 321; + /// The target device has insufficient resources to complete the operation. pub const DEVICE_NO_RESOURCES = 322; + /// A data integrity checksum error occurred. Data in the file stream is corrupt. pub const DATA_CHECKSUM_ERROR = 323; + /// An attempt was made to modify both a KERNEL and normal Extended Attribute (EA) in the same operation. pub const INTERMIXED_KERNEL_EA_OPERATION = 324; + /// Device does not support file-level TRIM. pub const FILE_LEVEL_TRIM_NOT_SUPPORTED = 326; + /// The command specified a data offset that does not align to the device's granularity/alignment. pub const OFFSET_ALIGNMENT_VIOLATION = 327; + /// The command specified an invalid field in its parameter list. pub const INVALID_FIELD_IN_PARAMETER_LIST = 328; + /// An operation is currently in progress with the device. pub const OPERATION_IN_PROGRESS = 329; + /// An attempt was made to send down the command via an invalid path to the target device. pub const BAD_DEVICE_PATH = 330; + /// The command specified a number of descriptors that exceeded the maximum supported by the device. pub const TOO_MANY_DESCRIPTORS = 331; + /// Scrub is disabled on the specified file. pub const SCRUB_DATA_DISABLED = 332; + /// The storage device does not provide redundancy. pub const NOT_REDUNDANT_STORAGE = 333; + /// An operation is not supported on a resident file. pub const RESIDENT_FILE_NOT_SUPPORTED = 334; + /// An operation is not supported on a compressed file. pub const COMPRESSED_FILE_NOT_SUPPORTED = 335; + /// An operation is not supported on a directory. pub const DIRECTORY_NOT_SUPPORTED = 336; + /// The specified copy of the requested data could not be read. pub const NOT_READ_FROM_COPY = 337; + /// No action was taken as a system reboot is required. pub const FAIL_NOACTION_REBOOT = 350; + /// The shutdown operation failed. pub const FAIL_SHUTDOWN = 351; + /// The restart operation failed. pub const FAIL_RESTART = 352; + /// The maximum number of sessions has been reached. pub const MAX_SESSIONS_REACHED = 353; + /// The thread is already in background processing mode. pub const THREAD_MODE_ALREADY_BACKGROUND = 400; + /// The thread is not in background processing mode. pub const THREAD_MODE_NOT_BACKGROUND = 401; + /// The process is already in background processing mode. pub const PROCESS_MODE_ALREADY_BACKGROUND = 402; + /// The process is not in background processing mode. pub const PROCESS_MODE_NOT_BACKGROUND = 403; + /// Attempt to access invalid address. pub const INVALID_ADDRESS = 487; + /// User profile cannot be loaded. pub const USER_PROFILE_LOAD = 500; + /// Arithmetic result exceeded 32 bits. pub const ARITHMETIC_OVERFLOW = 534; + /// There is a process on other end of the pipe. pub const PIPE_CONNECTED = 535; + /// Waiting for a process to open the other end of the pipe. pub const PIPE_LISTENING = 536; + /// Application verifier has found an error in the current process. pub const VERIFIER_STOP = 537; + /// An error occurred in the ABIOS subsystem. pub const ABIOS_ERROR = 538; + /// A warning occurred in the WX86 subsystem. pub const WX86_WARNING = 539; + /// An error occurred in the WX86 subsystem. pub const WX86_ERROR = 540; + /// An attempt was made to cancel or set a timer that has an associated APC and the subject thread is not the thread that originally set the timer with an associated APC routine. pub const TIMER_NOT_CANCELED = 541; + /// Unwind exception code. pub const UNWIND = 542; + /// An invalid or unaligned stack was encountered during an unwind operation. pub const BAD_STACK = 543; + /// An invalid unwind target was encountered during an unwind operation. pub const INVALID_UNWIND_TARGET = 544; + /// Invalid Object Attributes specified to NtCreatePort or invalid Port Attributes specified to NtConnectPort pub const INVALID_PORT_ATTRIBUTES = 545; + /// Length of message passed to NtRequestPort or NtRequestWaitReplyPort was longer than the maximum message allowed by the port. pub const PORT_MESSAGE_TOO_LONG = 546; + /// An attempt was made to lower a quota limit below the current usage. pub const INVALID_QUOTA_LOWER = 547; + /// An attempt was made to attach to a device that was already attached to another device. pub const DEVICE_ALREADY_ATTACHED = 548; + /// An attempt was made to execute an instruction at an unaligned address and the host system does not support unaligned instruction references. pub const INSTRUCTION_MISALIGNMENT = 549; + /// Profiling not started. pub const PROFILING_NOT_STARTED = 550; + /// Profiling not stopped. pub const PROFILING_NOT_STOPPED = 551; + /// The passed ACL did not contain the minimum required information. pub const COULD_NOT_INTERPRET = 552; + /// The number of active profiling objects is at the maximum and no more may be started. pub const PROFILING_AT_LIMIT = 553; + /// Used to indicate that an operation cannot continue without blocking for I/O. pub const CANT_WAIT = 554; + /// Indicates that a thread attempted to terminate itself by default (called NtTerminateThread with NULL) and it was the last thread in the current process. pub const CANT_TERMINATE_SELF = 555; + /// If an MM error is returned which is not defined in the standard FsRtl filter, it is converted to one of the following errors which is guaranteed to be in the filter. In this case information is lost, however, the filter correctly handles the exception. pub const UNEXPECTED_MM_CREATE_ERR = 556; + /// If an MM error is returned which is not defined in the standard FsRtl filter, it is converted to one of the following errors which is guaranteed to be in the filter. In this case information is lost, however, the filter correctly handles the exception. pub const UNEXPECTED_MM_MAP_ERROR = 557; + /// If an MM error is returned which is not defined in the standard FsRtl filter, it is converted to one of the following errors which is guaranteed to be in the filter. In this case information is lost, however, the filter correctly handles the exception. pub const UNEXPECTED_MM_EXTEND_ERR = 558; + /// A malformed function table was encountered during an unwind operation. pub const BAD_FUNCTION_TABLE = 559; + /// Indicates that an attempt was made to assign protection to a file system file or directory and one of the SIDs in the security descriptor could not be translated into a GUID that could be stored by the file system. This causes the protection attempt to fail, which may cause a file creation attempt to fail. pub const NO_GUID_TRANSLATION = 560; + /// Indicates that an attempt was made to grow an LDT by setting its size, or that the size was not an even number of selectors. pub const INVALID_LDT_SIZE = 561; + /// Indicates that the starting value for the LDT information was not an integral multiple of the selector size. pub const INVALID_LDT_OFFSET = 563; + /// Indicates that the user supplied an invalid descriptor when trying to set up Ldt descriptors. pub const INVALID_LDT_DESCRIPTOR = 564; + /// Indicates a process has too many threads to perform the requested action. For example, assignment of a primary token may only be performed when a process has zero or one threads. pub const TOO_MANY_THREADS = 565; + /// An attempt was made to operate on a thread within a specific process, but the thread specified is not in the process specified. pub const THREAD_NOT_IN_PROCESS = 566; + /// Page file quota was exceeded. pub const PAGEFILE_QUOTA_EXCEEDED = 567; + /// The Netlogon service cannot start because another Netlogon service running in the domain conflicts with the specified role. pub const LOGON_SERVER_CONFLICT = 568; + /// The SAM database on a Windows Server is significantly out of synchronization with the copy on the Domain Controller. A complete synchronization is required. pub const SYNCHRONIZATION_REQUIRED = 569; + /// The NtCreateFile API failed. This error should never be returned to an application, it is a place holder for the Windows Lan Manager Redirector to use in its internal error mapping routines. pub const NET_OPEN_FAILED = 570; + /// {Privilege Failed} The I/O permissions for the process could not be changed. pub const IO_PRIVILEGE_FAILED = 571; + /// {Application Exit by CTRL+C} The application terminated as a result of a CTRL+C. pub const CONTROL_C_EXIT = 572; + /// {Missing System File} The required system file %hs is bad or missing. pub const MISSING_SYSTEMFILE = 573; + /// {Application Error} The exception %s (0x%08lx) occurred in the application at location 0x%08lx. pub const UNHANDLED_EXCEPTION = 574; + /// {Application Error} The application was unable to start correctly (0x%lx). Click OK to close the application. pub const APP_INIT_FAILURE = 575; + /// {Unable to Create Paging File} The creation of the paging file %hs failed (%lx). The requested size was %ld. pub const PAGEFILE_CREATE_FAILED = 576; + /// Windows cannot verify the digital signature for this file. A recent hardware or software change might have installed a file that is signed incorrectly or damaged, or that might be malicious software from an unknown source. pub const INVALID_IMAGE_HASH = 577; + /// {No Paging File Specified} No paging file was specified in the system configuration. pub const NO_PAGEFILE = 578; + /// {EXCEPTION} A real-mode application issued a floating-point instruction and floating-point hardware is not present. pub const ILLEGAL_FLOAT_CONTEXT = 579; + /// An event pair synchronization operation was performed using the thread specific client/server event pair object, but no event pair object was associated with the thread. pub const NO_EVENT_PAIR = 580; + /// A Windows Server has an incorrect configuration. pub const DOMAIN_CTRLR_CONFIG_ERROR = 581; + /// An illegal character was encountered. For a multi-byte character set this includes a lead byte without a succeeding trail byte. For the Unicode character set this includes the characters 0xFFFF and 0xFFFE. pub const ILLEGAL_CHARACTER = 582; + /// The Unicode character is not defined in the Unicode character set installed on the system. pub const UNDEFINED_CHARACTER = 583; + /// The paging file cannot be created on a floppy diskette. pub const FLOPPY_VOLUME = 584; + /// The system BIOS failed to connect a system interrupt to the device or bus for which the device is connected. pub const BIOS_FAILED_TO_CONNECT_INTERRUPT = 585; + /// This operation is only allowed for the Primary Domain Controller of the domain. pub const BACKUP_CONTROLLER = 586; + /// An attempt was made to acquire a mutant such that its maximum count would have been exceeded. pub const MUTANT_LIMIT_EXCEEDED = 587; + /// A volume has been accessed for which a file system driver is required that has not yet been loaded. pub const FS_DRIVER_REQUIRED = 588; + /// {Registry File Failure} The registry cannot load the hive (file): %hs or its log or alternate. It is corrupt, absent, or not writable. pub const CANNOT_LOAD_REGISTRY_FILE = 589; + /// {Unexpected Failure in DebugActiveProcess} An unexpected failure occurred while processing a DebugActiveProcess API request. You may choose OK to terminate the process, or Cancel to ignore the error. pub const DEBUG_ATTACH_FAILED = 590; + /// {Fatal System Error} The %hs system process terminated unexpectedly with a status of 0x%08x (0x%08x 0x%08x). The system has been shut down. pub const SYSTEM_PROCESS_TERMINATED = 591; + /// {Data Not Accepted} The TDI client could not handle the data received during an indication. pub const DATA_NOT_ACCEPTED = 592; + /// NTVDM encountered a hard error. pub const VDM_HARD_ERROR = 593; + /// {Cancel Timeout} The driver %hs failed to complete a cancelled I/O request in the allotted time. pub const DRIVER_CANCEL_TIMEOUT = 594; + /// {Reply Message Mismatch} An attempt was made to reply to an LPC message, but the thread specified by the client ID in the message was not waiting on that message. pub const REPLY_MESSAGE_MISMATCH = 595; + /// {Delayed Write Failed} Windows was unable to save all the data for the file %hs. The data has been lost. This error may be caused by a failure of your computer hardware or network connection. Please try to save this file elsewhere. pub const LOST_WRITEBEHIND_DATA = 596; + /// The parameter(s) passed to the server in the client/server shared memory window were invalid. Too much data may have been put in the shared memory window. pub const CLIENT_SERVER_PARAMETERS_INVALID = 597; + /// The stream is not a tiny stream. pub const NOT_TINY_STREAM = 598; + /// The request must be handled by the stack overflow code. pub const STACK_OVERFLOW_READ = 599; + /// Internal OFS status codes indicating how an allocation operation is handled. Either it is retried after the containing onode is moved or the extent stream is converted to a large stream. pub const CONVERT_TO_LARGE = 600; + /// The attempt to find the object found an object matching by ID on the volume but it is out of the scope of the handle used for the operation. pub const FOUND_OUT_OF_SCOPE = 601; + /// The bucket array must be grown. Retry transaction after doing so. pub const ALLOCATE_BUCKET = 602; + /// The user/kernel marshalling buffer has overflowed. pub const MARSHALL_OVERFLOW = 603; + /// The supplied variant structure contains invalid data. pub const INVALID_VARIANT = 604; + /// The specified buffer contains ill-formed data. pub const BAD_COMPRESSION_BUFFER = 605; + /// {Audit Failed} An attempt to generate a security audit failed. pub const AUDIT_FAILED = 606; + /// The timer resolution was not previously set by the current process. pub const TIMER_RESOLUTION_NOT_SET = 607; + /// There is insufficient account information to log you on. pub const INSUFFICIENT_LOGON_INFO = 608; + /// {Invalid DLL Entrypoint} The dynamic link library %hs is not written correctly. The stack pointer has been left in an inconsistent state. The entrypoint should be declared as WINAPI or STDCALL. Select YES to fail the DLL load. Select NO to continue execution. Selecting NO may cause the application to operate incorrectly. pub const BAD_DLL_ENTRYPOINT = 609; + /// {Invalid Service Callback Entrypoint} The %hs service is not written correctly. The stack pointer has been left in an inconsistent state. The callback entrypoint should be declared as WINAPI or STDCALL. Selecting OK will cause the service to continue operation. However, the service process may operate incorrectly. pub const BAD_SERVICE_ENTRYPOINT = 610; + /// There is an IP address conflict with another system on the network. pub const IP_ADDRESS_CONFLICT1 = 611; + /// There is an IP address conflict with another system on the network. pub const IP_ADDRESS_CONFLICT2 = 612; + /// {Low On Registry Space} The system has reached the maximum size allowed for the system part of the registry. Additional storage requests will be ignored. pub const REGISTRY_QUOTA_LIMIT = 613; + /// A callback return system service cannot be executed when no callback is active. pub const NO_CALLBACK_ACTIVE = 614; + /// The password provided is too short to meet the policy of your user account. Please choose a longer password. pub const PWD_TOO_SHORT = 615; + /// The policy of your user account does not allow you to change passwords too frequently. This is done to prevent users from changing back to a familiar, but potentially discovered, password. If you feel your password has been compromised then please contact your administrator immediately to have a new one assigned. pub const PWD_TOO_RECENT = 616; + /// You have attempted to change your password to one that you have used in the past. The policy of your user account does not allow this. Please select a password that you have not previously used. pub const PWD_HISTORY_CONFLICT = 617; + /// The specified compression format is unsupported. pub const UNSUPPORTED_COMPRESSION = 618; + /// The specified hardware profile configuration is invalid. pub const INVALID_HW_PROFILE = 619; + /// The specified Plug and Play registry device path is invalid. pub const INVALID_PLUGPLAY_DEVICE_PATH = 620; + /// The specified quota list is internally inconsistent with its descriptor. pub const QUOTA_LIST_INCONSISTENT = 621; + /// {Windows Evaluation Notification} The evaluation period for this installation of Windows has expired. This system will shutdown in 1 hour. To restore access to this installation of Windows, please upgrade this installation using a licensed distribution of this product. pub const EVALUATION_EXPIRATION = 622; + /// {Illegal System DLL Relocation} The system DLL %hs was relocated in memory. The application will not run properly. The relocation occurred because the DLL %hs occupied an address range reserved for Windows system DLLs. The vendor supplying the DLL should be contacted for a new DLL. pub const ILLEGAL_DLL_RELOCATION = 623; + /// {DLL Initialization Failed} The application failed to initialize because the window station is shutting down. pub const DLL_INIT_FAILED_LOGOFF = 624; + /// The validation process needs to continue on to the next step. pub const VALIDATE_CONTINUE = 625; + /// There are no more matches for the current index enumeration. pub const NO_MORE_MATCHES = 626; + /// The range could not be added to the range list because of a conflict. pub const RANGE_LIST_CONFLICT = 627; + /// The server process is running under a SID different than that required by client. pub const SERVER_SID_MISMATCH = 628; + /// A group marked use for deny only cannot be enabled. pub const CANT_ENABLE_DENY_ONLY = 629; + /// {EXCEPTION} Multiple floating point faults. pub const FLOAT_MULTIPLE_FAULTS = 630; + /// {EXCEPTION} Multiple floating point traps. pub const FLOAT_MULTIPLE_TRAPS = 631; + /// The requested interface is not supported. pub const NOINTERFACE = 632; + /// {System Standby Failed} The driver %hs does not support standby mode. Updating this driver may allow the system to go to standby mode. pub const DRIVER_FAILED_SLEEP = 633; + /// The system file %1 has become corrupt and has been replaced. pub const CORRUPT_SYSTEM_FILE = 634; + /// {Virtual Memory Minimum Too Low} Your system is low on virtual memory. Windows is increasing the size of your virtual memory paging file. During this process, memory requests for some applications may be denied. For more information, see Help. pub const COMMITMENT_MINIMUM = 635; + /// A device was removed so enumeration must be restarted. pub const PNP_RESTART_ENUMERATION = 636; + /// {Fatal System Error} The system image %s is not properly signed. The file has been replaced with the signed file. The system has been shut down. pub const SYSTEM_IMAGE_BAD_SIGNATURE = 637; + /// Device will not start without a reboot. pub const PNP_REBOOT_REQUIRED = 638; + /// There is not enough power to complete the requested operation. pub const INSUFFICIENT_POWER = 639; + /// ERROR_MULTIPLE_FAULT_VIOLATION pub const MULTIPLE_FAULT_VIOLATION = 640; + /// The system is in the process of shutting down. pub const SYSTEM_SHUTDOWN = 641; + /// An attempt to remove a processes DebugPort was made, but a port was not already associated with the process. pub const PORT_NOT_SET = 642; + /// This version of Windows is not compatible with the behavior version of directory forest, domain or domain controller. pub const DS_VERSION_CHECK_FAILURE = 643; + /// The specified range could not be found in the range list. pub const RANGE_NOT_FOUND = 644; + /// The driver was not loaded because the system is booting into safe mode. pub const NOT_SAFE_MODE_DRIVER = 646; + /// The driver was not loaded because it failed its initialization call. pub const FAILED_DRIVER_ENTRY = 647; + /// The "%hs" encountered an error while applying power or reading the device configuration. This may be caused by a failure of your hardware or by a poor connection. pub const DEVICE_ENUMERATION_ERROR = 648; + /// The create operation failed because the name contained at least one mount point which resolves to a volume to which the specified device object is not attached. pub const MOUNT_POINT_NOT_RESOLVED = 649; + /// The device object parameter is either not a valid device object or is not attached to the volume specified by the file name. pub const INVALID_DEVICE_OBJECT_PARAMETER = 650; + /// A Machine Check Error has occurred. Please check the system eventlog for additional information. pub const MCA_OCCURED = 651; + /// There was error [%2] processing the driver database. pub const DRIVER_DATABASE_ERROR = 652; + /// System hive size has exceeded its limit. pub const SYSTEM_HIVE_TOO_LARGE = 653; + /// The driver could not be loaded because a previous version of the driver is still in memory. pub const DRIVER_FAILED_PRIOR_UNLOAD = 654; + /// {Volume Shadow Copy Service} Please wait while the Volume Shadow Copy Service prepares volume %hs for hibernation. pub const VOLSNAP_PREPARE_HIBERNATE = 655; + /// The system has failed to hibernate (The error code is %hs). Hibernation will be disabled until the system is restarted. pub const HIBERNATION_FAILURE = 656; + /// The password provided is too long to meet the policy of your user account. Please choose a shorter password. pub const PWD_TOO_LONG = 657; + /// The requested operation could not be completed due to a file system limitation. pub const FILE_SYSTEM_LIMITATION = 665; + /// An assertion failure has occurred. pub const ASSERTION_FAILURE = 668; + /// An error occurred in the ACPI subsystem. pub const ACPI_ERROR = 669; + /// WOW Assertion Error. pub const WOW_ASSERTION = 670; + /// A device is missing in the system BIOS MPS table. This device will not be used. Please contact your system vendor for system BIOS update. pub const PNP_BAD_MPS_TABLE = 671; + /// A translator failed to translate resources. pub const PNP_TRANSLATION_FAILED = 672; + /// A IRQ translator failed to translate resources. pub const PNP_IRQ_TRANSLATION_FAILED = 673; + /// Driver %2 returned invalid ID for a child device (%3). pub const PNP_INVALID_ID = 674; + /// {Kernel Debugger Awakened} the system debugger was awakened by an interrupt. pub const WAKE_SYSTEM_DEBUGGER = 675; + /// {Handles Closed} Handles to objects have been automatically closed as a result of the requested operation. pub const HANDLES_CLOSED = 676; + /// {Too Much Information} The specified access control list (ACL) contained more information than was expected. pub const EXTRANEOUS_INFORMATION = 677; + /// This warning level status indicates that the transaction state already exists for the registry sub-tree, but that a transaction commit was previously aborted. The commit has NOT been completed, but has not been rolled back either (so it may still be committed if desired). pub const RXACT_COMMIT_NECESSARY = 678; + /// {Media Changed} The media may have changed. pub const MEDIA_CHECK = 679; + /// {GUID Substitution} During the translation of a global identifier (GUID) to a Windows security ID (SID), no administratively-defined GUID prefix was found. A substitute prefix was used, which will not compromise system security. However, this may provide a more restrictive access than intended. pub const GUID_SUBSTITUTION_MADE = 680; + /// The create operation stopped after reaching a symbolic link. pub const STOPPED_ON_SYMLINK = 681; + /// A long jump has been executed. pub const LONGJUMP = 682; + /// The Plug and Play query operation was not successful. pub const PLUGPLAY_QUERY_VETOED = 683; + /// A frame consolidation has been executed. pub const UNWIND_CONSOLIDATE = 684; + /// {Registry Hive Recovered} Registry hive (file): %hs was corrupted and it has been recovered. Some data might have been lost. pub const REGISTRY_HIVE_RECOVERED = 685; + /// The application is attempting to run executable code from the module %hs. This may be insecure. An alternative, %hs, is available. Should the application use the secure module %hs? pub const DLL_MIGHT_BE_INSECURE = 686; + /// The application is loading executable code from the module %hs. This is secure, but may be incompatible with previous releases of the operating system. An alternative, %hs, is available. Should the application use the secure module %hs? pub const DLL_MIGHT_BE_INCOMPATIBLE = 687; + /// Debugger did not handle the exception. pub const DBG_EXCEPTION_NOT_HANDLED = 688; + /// Debugger will reply later. pub const DBG_REPLY_LATER = 689; + /// Debugger cannot provide handle. pub const DBG_UNABLE_TO_PROVIDE_HANDLE = 690; + /// Debugger terminated thread. pub const DBG_TERMINATE_THREAD = 691; + /// Debugger terminated process. pub const DBG_TERMINATE_PROCESS = 692; + /// Debugger got control C. pub const DBG_CONTROL_C = 693; + /// Debugger printed exception on control C. pub const DBG_PRINTEXCEPTION_C = 694; + /// Debugger received RIP exception. pub const DBG_RIPEXCEPTION = 695; + /// Debugger received control break. pub const DBG_CONTROL_BREAK = 696; + /// Debugger command communication exception. pub const DBG_COMMAND_EXCEPTION = 697; + /// {Object Exists} An attempt was made to create an object and the object name already existed. pub const OBJECT_NAME_EXISTS = 698; + /// {Thread Suspended} A thread termination occurred while the thread was suspended. The thread was resumed, and termination proceeded. pub const THREAD_WAS_SUSPENDED = 699; + /// {Image Relocated} An image file could not be mapped at the address specified in the image file. Local fixups must be performed on this image. pub const IMAGE_NOT_AT_BASE = 700; + /// This informational level status indicates that a specified registry sub-tree transaction state did not yet exist and had to be created. pub const RXACT_STATE_CREATED = 701; + /// {Segment Load} A virtual DOS machine (VDM) is loading, unloading, or moving an MS-DOS or Win16 program segment image. An exception is raised so a debugger can load, unload or track symbols and breakpoints within these 16-bit segments. pub const SEGMENT_NOTIFICATION = 702; + /// {Invalid Current Directory} The process cannot switch to the startup current directory %hs. Select OK to set current directory to %hs, or select CANCEL to exit. pub const BAD_CURRENT_DIRECTORY = 703; + /// {Redundant Read} To satisfy a read request, the NT fault-tolerant file system successfully read the requested data from a redundant copy. This was done because the file system encountered a failure on a member of the fault-tolerant volume, but was unable to reassign the failing area of the device. pub const FT_READ_RECOVERY_FROM_BACKUP = 704; + /// {Redundant Write} To satisfy a write request, the NT fault-tolerant file system successfully wrote a redundant copy of the information. This was done because the file system encountered a failure on a member of the fault-tolerant volume, but was not able to reassign the failing area of the device. pub const FT_WRITE_RECOVERY = 705; + /// {Machine Type Mismatch} The image file %hs is valid, but is for a machine type other than the current machine. Select OK to continue, or CANCEL to fail the DLL load. pub const IMAGE_MACHINE_TYPE_MISMATCH = 706; + /// {Partial Data Received} The network transport returned partial data to its client. The remaining data will be sent later. pub const RECEIVE_PARTIAL = 707; + /// {Expedited Data Received} The network transport returned data to its client that was marked as expedited by the remote system. pub const RECEIVE_EXPEDITED = 708; + /// {Partial Expedited Data Received} The network transport returned partial data to its client and this data was marked as expedited by the remote system. The remaining data will be sent later. pub const RECEIVE_PARTIAL_EXPEDITED = 709; + /// {TDI Event Done} The TDI indication has completed successfully. pub const EVENT_DONE = 710; + /// {TDI Event Pending} The TDI indication has entered the pending state. pub const EVENT_PENDING = 711; + /// Checking file system on %wZ. pub const CHECKING_FILE_SYSTEM = 712; + /// {Fatal Application Exit} %hs. pub const FATAL_APP_EXIT = 713; + /// The specified registry key is referenced by a predefined handle. pub const PREDEFINED_HANDLE = 714; + /// {Page Unlocked} The page protection of a locked page was changed to 'No Access' and the page was unlocked from memory and from the process. pub const WAS_UNLOCKED = 715; + /// %hs pub const SERVICE_NOTIFICATION = 716; + /// {Page Locked} One of the pages to lock was already locked. pub const WAS_LOCKED = 717; + /// Application popup: %1 : %2 pub const LOG_HARD_ERROR = 718; + /// ERROR_ALREADY_WIN32 pub const ALREADY_WIN32 = 719; + /// {Machine Type Mismatch} The image file %hs is valid, but is for a machine type other than the current machine. pub const IMAGE_MACHINE_TYPE_MISMATCH_EXE = 720; + /// A yield execution was performed and no thread was available to run. pub const NO_YIELD_PERFORMED = 721; + /// The resumable flag to a timer API was ignored. pub const TIMER_RESUME_IGNORED = 722; + /// The arbiter has deferred arbitration of these resources to its parent. pub const ARBITRATION_UNHANDLED = 723; + /// The inserted CardBus device cannot be started because of a configuration error on "%hs". pub const CARDBUS_NOT_SUPPORTED = 724; + /// The CPUs in this multiprocessor system are not all the same revision level. To use all processors the operating system restricts itself to the features of the least capable processor in the system. Should problems occur with this system, contact the CPU manufacturer to see if this mix of processors is supported. pub const MP_PROCESSOR_MISMATCH = 725; + /// The system was put into hibernation. pub const HIBERNATED = 726; + /// The system was resumed from hibernation. pub const RESUME_HIBERNATION = 727; + /// Windows has detected that the system firmware (BIOS) was updated [previous firmware date = %2, current firmware date %3]. pub const FIRMWARE_UPDATED = 728; + /// A device driver is leaking locked I/O pages causing system degradation. The system has automatically enabled tracking code in order to try and catch the culprit. pub const DRIVERS_LEAKING_LOCKED_PAGES = 729; + /// The system has awoken. pub const WAKE_SYSTEM = 730; + /// ERROR_WAIT_1 pub const WAIT_1 = 731; + /// ERROR_WAIT_2 pub const WAIT_2 = 732; + /// ERROR_WAIT_3 pub const WAIT_3 = 733; + /// ERROR_WAIT_63 pub const WAIT_63 = 734; + /// ERROR_ABANDONED_WAIT_0 pub const ABANDONED_WAIT_0 = 735; + /// ERROR_ABANDONED_WAIT_63 pub const ABANDONED_WAIT_63 = 736; + /// ERROR_USER_APC pub const USER_APC = 737; + /// ERROR_KERNEL_APC pub const KERNEL_APC = 738; + /// ERROR_ALERTED pub const ALERTED = 739; + /// The requested operation requires elevation. pub const ELEVATION_REQUIRED = 740; + /// A reparse should be performed by the Object Manager since the name of the file resulted in a symbolic link. pub const REPARSE = 741; + /// An open/create operation completed while an oplock break is underway. pub const OPLOCK_BREAK_IN_PROGRESS = 742; + /// A new volume has been mounted by a file system. pub const VOLUME_MOUNTED = 743; + /// This success level status indicates that the transaction state already exists for the registry sub-tree, but that a transaction commit was previously aborted. The commit has now been completed. pub const RXACT_COMMITTED = 744; + /// This indicates that a notify change request has been completed due to closing the handle which made the notify change request. pub const NOTIFY_CLEANUP = 745; + /// {Connect Failure on Primary Transport} An attempt was made to connect to the remote server %hs on the primary transport, but the connection failed. The computer WAS able to connect on a secondary transport. pub const PRIMARY_TRANSPORT_CONNECT_FAILED = 746; + /// Page fault was a transition fault. pub const PAGE_FAULT_TRANSITION = 747; + /// Page fault was a demand zero fault. pub const PAGE_FAULT_DEMAND_ZERO = 748; + /// Page fault was a demand zero fault. pub const PAGE_FAULT_COPY_ON_WRITE = 749; + /// Page fault was a demand zero fault. pub const PAGE_FAULT_GUARD_PAGE = 750; + /// Page fault was satisfied by reading from a secondary storage device. pub const PAGE_FAULT_PAGING_FILE = 751; + /// Cached page was locked during operation. pub const CACHE_PAGE_LOCKED = 752; + /// Crash dump exists in paging file. pub const CRASH_DUMP = 753; + /// Specified buffer contains all zeros. pub const BUFFER_ALL_ZEROS = 754; + /// A reparse should be performed by the Object Manager since the name of the file resulted in a symbolic link. pub const REPARSE_OBJECT = 755; + /// The device has succeeded a query-stop and its resource requirements have changed. pub const RESOURCE_REQUIREMENTS_CHANGED = 756; + /// The translator has translated these resources into the global space and no further translations should be performed. pub const TRANSLATION_COMPLETE = 757; + /// A process being terminated has no threads to terminate. pub const NOTHING_TO_TERMINATE = 758; + /// The specified process is not part of a job. pub const PROCESS_NOT_IN_JOB = 759; + /// The specified process is part of a job. pub const PROCESS_IN_JOB = 760; + /// {Volume Shadow Copy Service} The system is now ready for hibernation. pub const VOLSNAP_HIBERNATE_READY = 761; + /// A file system or file system filter driver has successfully completed an FsFilter operation. pub const FSFILTER_OP_COMPLETED_SUCCESSFULLY = 762; + /// The specified interrupt vector was already connected. pub const INTERRUPT_VECTOR_ALREADY_CONNECTED = 763; + /// The specified interrupt vector is still connected. pub const INTERRUPT_STILL_CONNECTED = 764; + /// An operation is blocked waiting for an oplock. pub const WAIT_FOR_OPLOCK = 765; + /// Debugger handled exception. pub const DBG_EXCEPTION_HANDLED = 766; + /// Debugger continued. pub const DBG_CONTINUE = 767; + /// An exception occurred in a user mode callback and the kernel callback frame should be removed. pub const CALLBACK_POP_STACK = 768; + /// Compression is disabled for this volume. pub const COMPRESSION_DISABLED = 769; + /// The data provider cannot fetch backwards through a result set. pub const CANTFETCHBACKWARDS = 770; + /// The data provider cannot scroll backwards through a result set. pub const CANTSCROLLBACKWARDS = 771; + /// The data provider requires that previously fetched data is released before asking for more data. pub const ROWSNOTRELEASED = 772; + /// The data provider was not able to interpret the flags set for a column binding in an accessor. pub const BAD_ACCESSOR_FLAGS = 773; + /// One or more errors occurred while processing the request. pub const ERRORS_ENCOUNTERED = 774; + /// The implementation is not capable of performing the request. pub const NOT_CAPABLE = 775; + /// The client of a component requested an operation which is not valid given the state of the component instance. pub const REQUEST_OUT_OF_SEQUENCE = 776; + /// A version number could not be parsed. pub const VERSION_PARSE_ERROR = 777; + /// The iterator's start position is invalid. pub const BADSTARTPOSITION = 778; + /// The hardware has reported an uncorrectable memory error. pub const MEMORY_HARDWARE = 779; + /// The attempted operation required self healing to be enabled. pub const DISK_REPAIR_DISABLED = 780; + /// The Desktop heap encountered an error while allocating session memory. There is more information in the system event log. pub const INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE = 781; + /// The system power state is transitioning from %2 to %3. pub const SYSTEM_POWERSTATE_TRANSITION = 782; + /// The system power state is transitioning from %2 to %3 but could enter %4. pub const SYSTEM_POWERSTATE_COMPLEX_TRANSITION = 783; + /// A thread is getting dispatched with MCA EXCEPTION because of MCA. pub const MCA_EXCEPTION = 784; + /// Access to %1 is monitored by policy rule %2. pub const ACCESS_AUDIT_BY_POLICY = 785; + /// Access to %1 has been restricted by your Administrator by policy rule %2. pub const ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY = 786; + /// A valid hibernation file has been invalidated and should be abandoned. pub const ABANDON_HIBERFILE = 787; + /// {Delayed Write Failed} Windows was unable to save all the data for the file %hs; the data has been lost. This error may be caused by network connectivity issues. Please try to save this file elsewhere. pub const LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED = 788; + /// {Delayed Write Failed} Windows was unable to save all the data for the file %hs; the data has been lost. This error was returned by the server on which the file exists. Please try to save this file elsewhere. pub const LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR = 789; + /// {Delayed Write Failed} Windows was unable to save all the data for the file %hs; the data has been lost. This error may be caused if the device has been removed or the media is write-protected. pub const LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR = 790; + /// The resources required for this device conflict with the MCFG table. pub const BAD_MCFG_TABLE = 791; + /// The volume repair could not be performed while it is online. Please schedule to take the volume offline so that it can be repaired. pub const DISK_REPAIR_REDIRECTED = 792; + /// The volume repair was not successful. pub const DISK_REPAIR_UNSUCCESSFUL = 793; + /// One of the volume corruption logs is full. Further corruptions that may be detected won't be logged. pub const CORRUPT_LOG_OVERFULL = 794; + /// One of the volume corruption logs is internally corrupted and needs to be recreated. The volume may contain undetected corruptions and must be scanned. pub const CORRUPT_LOG_CORRUPTED = 795; + /// One of the volume corruption logs is unavailable for being operated on. pub const CORRUPT_LOG_UNAVAILABLE = 796; + /// One of the volume corruption logs was deleted while still having corruption records in them. The volume contains detected corruptions and must be scanned. pub const CORRUPT_LOG_DELETED_FULL = 797; + /// One of the volume corruption logs was cleared by chkdsk and no longer contains real corruptions. pub const CORRUPT_LOG_CLEARED = 798; + /// Orphaned files exist on the volume but could not be recovered because no more new names could be created in the recovery directory. Files must be moved from the recovery directory. pub const ORPHAN_NAME_EXHAUSTED = 799; + /// The oplock that was associated with this handle is now associated with a different handle. pub const OPLOCK_SWITCHED_TO_NEW_HANDLE = 800; + /// An oplock of the requested level cannot be granted. An oplock of a lower level may be available. pub const CANNOT_GRANT_REQUESTED_OPLOCK = 801; + /// The operation did not complete successfully because it would cause an oplock to be broken. The caller has requested that existing oplocks not be broken. pub const CANNOT_BREAK_OPLOCK = 802; + /// The handle with which this oplock was associated has been closed. The oplock is now broken. pub const OPLOCK_HANDLE_CLOSED = 803; + /// The specified access control entry (ACE) does not contain a condition. pub const NO_ACE_CONDITION = 804; + /// The specified access control entry (ACE) contains an invalid condition. pub const INVALID_ACE_CONDITION = 805; + /// Access to the specified file handle has been revoked. pub const FILE_HANDLE_REVOKED = 806; + /// An image file was mapped at a different address from the one specified in the image file but fixups will still be automatically performed on the image. pub const IMAGE_AT_DIFFERENT_BASE = 807; + /// Access to the extended attribute was denied. pub const EA_ACCESS_DENIED = 994; + /// The I/O operation has been aborted because of either a thread exit or an application request. pub const OPERATION_ABORTED = 995; + /// Overlapped I/O event is not in a signaled state. pub const IO_INCOMPLETE = 996; + /// Overlapped I/O operation is in progress. pub const IO_PENDING = 997; + /// Invalid access to memory location. pub const NOACCESS = 998; + /// Error performing inpage operation. pub const SWAPERROR = 999; + /// Recursion too deep; the stack overflowed. pub const STACK_OVERFLOW = 1001; + /// The window cannot act on the sent message. pub const INVALID_MESSAGE = 1002; + /// Cannot complete this function. pub const CAN_NOT_COMPLETE = 1003; + /// Invalid flags. pub const INVALID_FLAGS = 1004; + /// The volume does not contain a recognized file system. Please make sure that all required file system drivers are loaded and that the volume is not corrupted. pub const UNRECOGNIZED_VOLUME = 1005; + /// The volume for a file has been externally altered so that the opened file is no longer valid. pub const FILE_INVALID = 1006; + /// The requested operation cannot be performed in full-screen mode. pub const FULLSCREEN_MODE = 1007; + /// An attempt was made to reference a token that does not exist. pub const NO_TOKEN = 1008; + /// The configuration registry database is corrupt. pub const BADDB = 1009; + /// The configuration registry key is invalid. pub const BADKEY = 1010; + /// The configuration registry key could not be opened. pub const CANTOPEN = 1011; + /// The configuration registry key could not be read. pub const CANTREAD = 1012; + /// The configuration registry key could not be written. pub const CANTWRITE = 1013; + /// One of the files in the registry database had to be recovered by use of a log or alternate copy. The recovery was successful. pub const REGISTRY_RECOVERED = 1014; + /// The registry is corrupted. The structure of one of the files containing registry data is corrupted, or the system's memory image of the file is corrupted, or the file could not be recovered because the alternate copy or log was absent or corrupted. pub const REGISTRY_CORRUPT = 1015; + /// An I/O operation initiated by the registry failed unrecoverably. The registry could not read in, or write out, or flush, one of the files that contain the system's image of the registry. pub const REGISTRY_IO_FAILED = 1016; + /// The system has attempted to load or restore a file into the registry, but the specified file is not in a registry file format. pub const NOT_REGISTRY_FILE = 1017; + /// Illegal operation attempted on a registry key that has been marked for deletion. pub const KEY_DELETED = 1018; + /// System could not allocate the required space in a registry log. pub const NO_LOG_SPACE = 1019; + /// Cannot create a symbolic link in a registry key that already has subkeys or values. pub const KEY_HAS_CHILDREN = 1020; + /// Cannot create a stable subkey under a volatile parent key. pub const CHILD_MUST_BE_VOLATILE = 1021; + /// A notify change request is being completed and the information is not being returned in the caller's buffer. The caller now needs to enumerate the files to find the changes. pub const NOTIFY_ENUM_DIR = 1022; + /// A stop control has been sent to a service that other running services are dependent on. pub const DEPENDENT_SERVICES_RUNNING = 1051; + /// The requested control is not valid for this service. pub const INVALID_SERVICE_CONTROL = 1052; + /// The service did not respond to the start or control request in a timely fashion. pub const SERVICE_REQUEST_TIMEOUT = 1053; + /// A thread could not be created for the service. pub const SERVICE_NO_THREAD = 1054; + /// The service database is locked. pub const SERVICE_DATABASE_LOCKED = 1055; + /// An instance of the service is already running. pub const SERVICE_ALREADY_RUNNING = 1056; + /// The account name is invalid or does not exist, or the password is invalid for the account name specified. pub const INVALID_SERVICE_ACCOUNT = 1057; + /// The service cannot be started, either because it is disabled or because it has no enabled devices associated with it. pub const SERVICE_DISABLED = 1058; + /// Circular service dependency was specified. pub const CIRCULAR_DEPENDENCY = 1059; + /// The specified service does not exist as an installed service. pub const SERVICE_DOES_NOT_EXIST = 1060; + /// The service cannot accept control messages at this time. pub const SERVICE_CANNOT_ACCEPT_CTRL = 1061; + /// The service has not been started. pub const SERVICE_NOT_ACTIVE = 1062; + /// The service process could not connect to the service controller. pub const FAILED_SERVICE_CONTROLLER_CONNECT = 1063; + /// An exception occurred in the service when handling the control request. pub const EXCEPTION_IN_SERVICE = 1064; + /// The database specified does not exist. pub const DATABASE_DOES_NOT_EXIST = 1065; + /// The service has returned a service-specific error code. pub const SERVICE_SPECIFIC_ERROR = 1066; + /// The process terminated unexpectedly. pub const PROCESS_ABORTED = 1067; + /// The dependency service or group failed to start. pub const SERVICE_DEPENDENCY_FAIL = 1068; + /// The service did not start due to a logon failure. pub const SERVICE_LOGON_FAILED = 1069; + /// After starting, the service hung in a start-pending state. pub const SERVICE_START_HANG = 1070; + /// The specified service database lock is invalid. pub const INVALID_SERVICE_LOCK = 1071; + /// The specified service has been marked for deletion. pub const SERVICE_MARKED_FOR_DELETE = 1072; + /// The specified service already exists. pub const SERVICE_EXISTS = 1073; + /// The system is currently running with the last-known-good configuration. pub const ALREADY_RUNNING_LKG = 1074; + /// The dependency service does not exist or has been marked for deletion. pub const SERVICE_DEPENDENCY_DELETED = 1075; + /// The current boot has already been accepted for use as the last-known-good control set. pub const BOOT_ALREADY_ACCEPTED = 1076; + /// No attempts to start the service have been made since the last boot. pub const SERVICE_NEVER_STARTED = 1077; + /// The name is already in use as either a service name or a service display name. pub const DUPLICATE_SERVICE_NAME = 1078; + /// The account specified for this service is different from the account specified for other services running in the same process. pub const DIFFERENT_SERVICE_ACCOUNT = 1079; + /// Failure actions can only be set for Win32 services, not for drivers. pub const CANNOT_DETECT_DRIVER_FAILURE = 1080; + /// This service runs in the same process as the service control manager. Therefore, the service control manager cannot take action if this service's process terminates unexpectedly. pub const CANNOT_DETECT_PROCESS_ABORT = 1081; + /// No recovery program has been configured for this service. pub const NO_RECOVERY_PROGRAM = 1082; + /// The executable program that this service is configured to run in does not implement the service. pub const SERVICE_NOT_IN_EXE = 1083; + /// This service cannot be started in Safe Mode. pub const NOT_SAFEBOOT_SERVICE = 1084; + /// The physical end of the tape has been reached. pub const END_OF_MEDIA = 1100; + /// A tape access reached a filemark. pub const FILEMARK_DETECTED = 1101; + /// The beginning of the tape or a partition was encountered. pub const BEGINNING_OF_MEDIA = 1102; + /// A tape access reached the end of a set of files. pub const SETMARK_DETECTED = 1103; + /// No more data is on the tape. pub const NO_DATA_DETECTED = 1104; + /// Tape could not be partitioned. pub const PARTITION_FAILURE = 1105; + /// When accessing a new tape of a multivolume partition, the current block size is incorrect. pub const INVALID_BLOCK_LENGTH = 1106; + /// Tape partition information could not be found when loading a tape. pub const DEVICE_NOT_PARTITIONED = 1107; + /// Unable to lock the media eject mechanism. pub const UNABLE_TO_LOCK_MEDIA = 1108; + /// Unable to unload the media. pub const UNABLE_TO_UNLOAD_MEDIA = 1109; + /// The media in the drive may have changed. pub const MEDIA_CHANGED = 1110; + /// The I/O bus was reset. pub const BUS_RESET = 1111; + /// No media in drive. pub const NO_MEDIA_IN_DRIVE = 1112; + /// No mapping for the Unicode character exists in the target multi-byte code page. pub const NO_UNICODE_TRANSLATION = 1113; + /// A dynamic link library (DLL) initialization routine failed. pub const DLL_INIT_FAILED = 1114; + /// A system shutdown is in progress. pub const SHUTDOWN_IN_PROGRESS = 1115; + /// Unable to abort the system shutdown because no shutdown was in progress. pub const NO_SHUTDOWN_IN_PROGRESS = 1116; + /// The request could not be performed because of an I/O device error. pub const IO_DEVICE = 1117; + /// No serial device was successfully initialized. The serial driver will unload. pub const SERIAL_NO_DEVICE = 1118; + /// Unable to open a device that was sharing an interrupt request (IRQ) with other devices. At least one other device that uses that IRQ was already opened. pub const IRQ_BUSY = 1119; + /// A serial I/O operation was completed by another write to the serial port. The IOCTL_SERIAL_XOFF_COUNTER reached zero.) pub const MORE_WRITES = 1120; + /// A serial I/O operation completed because the timeout period expired. The IOCTL_SERIAL_XOFF_COUNTER did not reach zero.) pub const COUNTER_TIMEOUT = 1121; + /// No ID address mark was found on the floppy disk. pub const FLOPPY_ID_MARK_NOT_FOUND = 1122; + /// Mismatch between the floppy disk sector ID field and the floppy disk controller track address. pub const FLOPPY_WRONG_CYLINDER = 1123; + /// The floppy disk controller reported an error that is not recognized by the floppy disk driver. pub const FLOPPY_UNKNOWN_ERROR = 1124; + /// The floppy disk controller returned inconsistent results in its registers. pub const FLOPPY_BAD_REGISTERS = 1125; + /// While accessing the hard disk, a recalibrate operation failed, even after retries. pub const DISK_RECALIBRATE_FAILED = 1126; + /// While accessing the hard disk, a disk operation failed even after retries. pub const DISK_OPERATION_FAILED = 1127; + /// While accessing the hard disk, a disk controller reset was needed, but even that failed. pub const DISK_RESET_FAILED = 1128; + /// Physical end of tape encountered. pub const EOM_OVERFLOW = 1129; + /// Not enough server storage is available to process this command. pub const NOT_ENOUGH_SERVER_MEMORY = 1130; + /// A potential deadlock condition has been detected. pub const POSSIBLE_DEADLOCK = 1131; + /// The base address or the file offset specified does not have the proper alignment. pub const MAPPED_ALIGNMENT = 1132; + /// An attempt to change the system power state was vetoed by another application or driver. pub const SET_POWER_STATE_VETOED = 1140; + /// The system BIOS failed an attempt to change the system power state. pub const SET_POWER_STATE_FAILED = 1141; + /// An attempt was made to create more links on a file than the file system supports. pub const TOO_MANY_LINKS = 1142; + /// The specified program requires a newer version of Windows. pub const OLD_WIN_VERSION = 1150; + /// The specified program is not a Windows or MS-DOS program. pub const APP_WRONG_OS = 1151; + /// Cannot start more than one instance of the specified program. pub const SINGLE_INSTANCE_APP = 1152; + /// The specified program was written for an earlier version of Windows. pub const RMODE_APP = 1153; + /// One of the library files needed to run this application is damaged. pub const INVALID_DLL = 1154; + /// No application is associated with the specified file for this operation. pub const NO_ASSOCIATION = 1155; + /// An error occurred in sending the command to the application. pub const DDE_FAIL = 1156; + /// One of the library files needed to run this application cannot be found. pub const DLL_NOT_FOUND = 1157; + /// The current process has used all of its system allowance of handles for Window Manager objects. pub const NO_MORE_USER_HANDLES = 1158; + /// The message can be used only with synchronous operations. pub const MESSAGE_SYNC_ONLY = 1159; + /// The indicated source element has no media. pub const SOURCE_ELEMENT_EMPTY = 1160; + /// The indicated destination element already contains media. pub const DESTINATION_ELEMENT_FULL = 1161; + /// The indicated element does not exist. pub const ILLEGAL_ELEMENT_ADDRESS = 1162; + /// The indicated element is part of a magazine that is not present. pub const MAGAZINE_NOT_PRESENT = 1163; + /// The indicated device requires reinitialization due to hardware errors. pub const DEVICE_REINITIALIZATION_NEEDED = 1164; + /// The device has indicated that cleaning is required before further operations are attempted. pub const DEVICE_REQUIRES_CLEANING = 1165; + /// The device has indicated that its door is open. pub const DEVICE_DOOR_OPEN = 1166; + /// The device is not connected. pub const DEVICE_NOT_CONNECTED = 1167; + /// Element not found. pub const NOT_FOUND = 1168; + /// There was no match for the specified key in the index. pub const NO_MATCH = 1169; + /// The property set specified does not exist on the object. pub const SET_NOT_FOUND = 1170; + /// The point passed to GetMouseMovePoints is not in the buffer. pub const POINT_NOT_FOUND = 1171; + /// The tracking (workstation) service is not running. pub const NO_TRACKING_SERVICE = 1172; + /// The Volume ID could not be found. pub const NO_VOLUME_ID = 1173; + /// Unable to remove the file to be replaced. pub const UNABLE_TO_REMOVE_REPLACED = 1175; + /// Unable to move the replacement file to the file to be replaced. The file to be replaced has retained its original name. pub const UNABLE_TO_MOVE_REPLACEMENT = 1176; + /// Unable to move the replacement file to the file to be replaced. The file to be replaced has been renamed using the backup name. pub const UNABLE_TO_MOVE_REPLACEMENT_2 = 1177; + /// The volume change journal is being deleted. pub const JOURNAL_DELETE_IN_PROGRESS = 1178; + /// The volume change journal is not active. pub const JOURNAL_NOT_ACTIVE = 1179; + /// A file was found, but it may not be the correct file. pub const POTENTIAL_FILE_FOUND = 1180; + /// The journal entry has been deleted from the journal. pub const JOURNAL_ENTRY_DELETED = 1181; + /// A system shutdown has already been scheduled. pub const SHUTDOWN_IS_SCHEDULED = 1190; + /// The system shutdown cannot be initiated because there are other users logged on to the computer. pub const SHUTDOWN_USERS_LOGGED_ON = 1191; + /// The specified device name is invalid. pub const BAD_DEVICE = 1200; + /// The device is not currently connected but it is a remembered connection. pub const CONNECTION_UNAVAIL = 1201; + /// The local device name has a remembered connection to another network resource. pub const DEVICE_ALREADY_REMEMBERED = 1202; + /// The network path was either typed incorrectly, does not exist, or the network provider is not currently available. Please try retyping the path or contact your network administrator. pub const NO_NET_OR_BAD_PATH = 1203; + /// The specified network provider name is invalid. pub const BAD_PROVIDER = 1204; + /// Unable to open the network connection profile. pub const CANNOT_OPEN_PROFILE = 1205; + /// The network connection profile is corrupted. pub const BAD_PROFILE = 1206; + /// Cannot enumerate a noncontainer. pub const NOT_CONTAINER = 1207; + /// An extended error has occurred. pub const EXTENDED_ERROR = 1208; + /// The format of the specified group name is invalid. pub const INVALID_GROUPNAME = 1209; + /// The format of the specified computer name is invalid. pub const INVALID_COMPUTERNAME = 1210; + /// The format of the specified event name is invalid. pub const INVALID_EVENTNAME = 1211; + /// The format of the specified domain name is invalid. pub const INVALID_DOMAINNAME = 1212; + /// The format of the specified service name is invalid. pub const INVALID_SERVICENAME = 1213; + /// The format of the specified network name is invalid. pub const INVALID_NETNAME = 1214; + /// The format of the specified share name is invalid. pub const INVALID_SHARENAME = 1215; + /// The format of the specified password is invalid. pub const INVALID_PASSWORDNAME = 1216; + /// The format of the specified message name is invalid. pub const INVALID_MESSAGENAME = 1217; + /// The format of the specified message destination is invalid. pub const INVALID_MESSAGEDEST = 1218; + /// Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again. pub const SESSION_CREDENTIAL_CONFLICT = 1219; + /// An attempt was made to establish a session to a network server, but there are already too many sessions established to that server. pub const REMOTE_SESSION_LIMIT_EXCEEDED = 1220; + /// The workgroup or domain name is already in use by another computer on the network. pub const DUP_DOMAINNAME = 1221; + /// The network is not present or not started. pub const NO_NETWORK = 1222; + /// The operation was canceled by the user. pub const CANCELLED = 1223; + /// The requested operation cannot be performed on a file with a user-mapped section open. pub const USER_MAPPED_FILE = 1224; + /// The remote computer refused the network connection. pub const CONNECTION_REFUSED = 1225; + /// The network connection was gracefully closed. pub const GRACEFUL_DISCONNECT = 1226; + /// The network transport endpoint already has an address associated with it. pub const ADDRESS_ALREADY_ASSOCIATED = 1227; + /// An address has not yet been associated with the network endpoint. pub const ADDRESS_NOT_ASSOCIATED = 1228; + /// An operation was attempted on a nonexistent network connection. pub const CONNECTION_INVALID = 1229; + /// An invalid operation was attempted on an active network connection. pub const CONNECTION_ACTIVE = 1230; + /// The network location cannot be reached. For information about network troubleshooting, see Windows Help. pub const NETWORK_UNREACHABLE = 1231; + /// The network location cannot be reached. For information about network troubleshooting, see Windows Help. pub const HOST_UNREACHABLE = 1232; + /// The network location cannot be reached. For information about network troubleshooting, see Windows Help. pub const PROTOCOL_UNREACHABLE = 1233; + /// No service is operating at the destination network endpoint on the remote system. pub const PORT_UNREACHABLE = 1234; + /// The request was aborted. pub const REQUEST_ABORTED = 1235; + /// The network connection was aborted by the local system. pub const CONNECTION_ABORTED = 1236; + /// The operation could not be completed. A retry should be performed. pub const RETRY = 1237; + /// A connection to the server could not be made because the limit on the number of concurrent connections for this account has been reached. pub const CONNECTION_COUNT_LIMIT = 1238; + /// Attempting to log in during an unauthorized time of day for this account. pub const LOGIN_TIME_RESTRICTION = 1239; + /// The account is not authorized to log in from this station. pub const LOGIN_WKSTA_RESTRICTION = 1240; + /// The network address could not be used for the operation requested. pub const INCORRECT_ADDRESS = 1241; + /// The service is already registered. pub const ALREADY_REGISTERED = 1242; + /// The specified service does not exist. pub const SERVICE_NOT_FOUND = 1243; + /// The operation being requested was not performed because the user has not been authenticated. pub const NOT_AUTHENTICATED = 1244; + /// The operation being requested was not performed because the user has not logged on to the network. The specified service does not exist. pub const NOT_LOGGED_ON = 1245; + /// Continue with work in progress. pub const CONTINUE = 1246; + /// An attempt was made to perform an initialization operation when initialization has already been completed. pub const ALREADY_INITIALIZED = 1247; + /// No more local devices. pub const NO_MORE_DEVICES = 1248; + /// The specified site does not exist. pub const NO_SUCH_SITE = 1249; + /// A domain controller with the specified name already exists. pub const DOMAIN_CONTROLLER_EXISTS = 1250; + /// This operation is supported only when you are connected to the server. pub const ONLY_IF_CONNECTED = 1251; + /// The group policy framework should call the extension even if there are no changes. pub const OVERRIDE_NOCHANGES = 1252; + /// The specified user does not have a valid profile. pub const BAD_USER_PROFILE = 1253; + /// This operation is not supported on a computer running Windows Server 2003 for Small Business Server. pub const NOT_SUPPORTED_ON_SBS = 1254; + /// The server machine is shutting down. pub const SERVER_SHUTDOWN_IN_PROGRESS = 1255; + /// The remote system is not available. For information about network troubleshooting, see Windows Help. pub const HOST_DOWN = 1256; + /// The security identifier provided is not from an account domain. pub const NON_ACCOUNT_SID = 1257; + /// The security identifier provided does not have a domain component. pub const NON_DOMAIN_SID = 1258; + /// AppHelp dialog canceled thus preventing the application from starting. pub const APPHELP_BLOCK = 1259; + /// This program is blocked by group policy. For more information, contact your system administrator. pub const ACCESS_DISABLED_BY_POLICY = 1260; + /// A program attempt to use an invalid register value. Normally caused by an uninitialized register. This error is Itanium specific. pub const REG_NAT_CONSUMPTION = 1261; + /// The share is currently offline or does not exist. pub const CSCSHARE_OFFLINE = 1262; + /// The Kerberos protocol encountered an error while validating the KDC certificate during smartcard logon. There is more information in the system event log. pub const PKINIT_FAILURE = 1263; + /// The Kerberos protocol encountered an error while attempting to utilize the smartcard subsystem. pub const SMARTCARD_SUBSYSTEM_FAILURE = 1264; + /// The system cannot contact a domain controller to service the authentication request. Please try again later. pub const DOWNGRADE_DETECTED = 1265; + /// The machine is locked and cannot be shut down without the force option. pub const MACHINE_LOCKED = 1271; + /// An application-defined callback gave invalid data when called. pub const CALLBACK_SUPPLIED_INVALID_DATA = 1273; + /// The group policy framework should call the extension in the synchronous foreground policy refresh. pub const SYNC_FOREGROUND_REFRESH_REQUIRED = 1274; + /// This driver has been blocked from loading. pub const DRIVER_BLOCKED = 1275; + /// A dynamic link library (DLL) referenced a module that was neither a DLL nor the process's executable image. pub const INVALID_IMPORT_OF_NON_DLL = 1276; + /// Windows cannot open this program since it has been disabled. pub const ACCESS_DISABLED_WEBBLADE = 1277; + /// Windows cannot open this program because the license enforcement system has been tampered with or become corrupted. pub const ACCESS_DISABLED_WEBBLADE_TAMPER = 1278; + /// A transaction recover failed. pub const RECOVERY_FAILURE = 1279; + /// The current thread has already been converted to a fiber. pub const ALREADY_FIBER = 1280; + /// The current thread has already been converted from a fiber. pub const ALREADY_THREAD = 1281; + /// The system detected an overrun of a stack-based buffer in this application. This overrun could potentially allow a malicious user to gain control of this application. pub const STACK_BUFFER_OVERRUN = 1282; + /// Data present in one of the parameters is more than the function can operate on. pub const PARAMETER_QUOTA_EXCEEDED = 1283; + /// An attempt to do an operation on a debug object failed because the object is in the process of being deleted. pub const DEBUGGER_INACTIVE = 1284; + /// An attempt to delay-load a .dll or get a function address in a delay-loaded .dll failed. pub const DELAY_LOAD_FAILED = 1285; + /// %1 is a 16-bit application. You do not have permissions to execute 16-bit applications. Check your permissions with your system administrator. pub const VDM_DISALLOWED = 1286; + /// Insufficient information exists to identify the cause of failure. pub const UNIDENTIFIED_ERROR = 1287; + /// The parameter passed to a C runtime function is incorrect. pub const INVALID_CRUNTIME_PARAMETER = 1288; + /// The operation occurred beyond the valid data length of the file. pub const BEYOND_VDL = 1289; + /// The service start failed since one or more services in the same process have an incompatible service SID type setting. A service with restricted service SID type can only coexist in the same process with other services with a restricted SID type. If the service SID type for this service was just configured, the hosting process must be restarted in order to start this service. /// On Windows Server 2003 and Windows XP, an unrestricted service cannot coexist in the same process with other services. The service with the unrestricted service SID type must be moved to an owned process in order to start this service. pub const INCOMPATIBLE_SERVICE_SID_TYPE = 1290; + /// The process hosting the driver for this device has been terminated. pub const DRIVER_PROCESS_TERMINATED = 1291; + /// An operation attempted to exceed an implementation-defined limit. pub const IMPLEMENTATION_LIMIT = 1292; + /// Either the target process, or the target thread's containing process, is a protected process. pub const PROCESS_IS_PROTECTED = 1293; + /// The service notification client is lagging too far behind the current state of services in the machine. pub const SERVICE_NOTIFY_CLIENT_LAGGING = 1294; + /// The requested file operation failed because the storage quota was exceeded. To free up disk space, move files to a different location or delete unnecessary files. For more information, contact your system administrator. pub const DISK_QUOTA_EXCEEDED = 1295; + /// The requested file operation failed because the storage policy blocks that type of file. For more information, contact your system administrator. pub const CONTENT_BLOCKED = 1296; + /// A privilege that the service requires to function properly does not exist in the service account configuration. You may use the Services Microsoft Management Console (MMC) snap-in (services.msc) and the Local Security Settings MMC snap-in (secpol.msc) to view the service configuration and the account configuration. pub const INCOMPATIBLE_SERVICE_PRIVILEGE = 1297; + /// A thread involved in this operation appears to be unresponsive. pub const APP_HANG = 1298; + /// Indicates a particular Security ID may not be assigned as the label of an object. pub const INVALID_LABEL = 1299; + /// Not all privileges or groups referenced are assigned to the caller. pub const NOT_ALL_ASSIGNED = 1300; + /// Some mapping between account names and security IDs was not done. pub const SOME_NOT_MAPPED = 1301; + /// No system quota limits are specifically set for this account. pub const NO_QUOTAS_FOR_ACCOUNT = 1302; + /// No encryption key is available. A well-known encryption key was returned. pub const LOCAL_USER_SESSION_KEY = 1303; + /// The password is too complex to be converted to a LAN Manager password. The LAN Manager password returned is a NULL string. pub const NULL_LM_PASSWORD = 1304; + /// The revision level is unknown. pub const UNKNOWN_REVISION = 1305; + /// Indicates two revision levels are incompatible. pub const REVISION_MISMATCH = 1306; + /// This security ID may not be assigned as the owner of this object. pub const INVALID_OWNER = 1307; + /// This security ID may not be assigned as the primary group of an object. pub const INVALID_PRIMARY_GROUP = 1308; + /// An attempt has been made to operate on an impersonation token by a thread that is not currently impersonating a client. pub const NO_IMPERSONATION_TOKEN = 1309; + /// The group may not be disabled. pub const CANT_DISABLE_MANDATORY = 1310; + /// There are currently no logon servers available to service the logon request. pub const NO_LOGON_SERVERS = 1311; + /// A specified logon session does not exist. It may already have been terminated. pub const NO_SUCH_LOGON_SESSION = 1312; + /// A specified privilege does not exist. pub const NO_SUCH_PRIVILEGE = 1313; + /// A required privilege is not held by the client. pub const PRIVILEGE_NOT_HELD = 1314; + /// The name provided is not a properly formed account name. pub const INVALID_ACCOUNT_NAME = 1315; + /// The specified account already exists. pub const USER_EXISTS = 1316; + /// The specified account does not exist. pub const NO_SUCH_USER = 1317; + /// The specified group already exists. pub const GROUP_EXISTS = 1318; + /// The specified group does not exist. pub const NO_SUCH_GROUP = 1319; + /// Either the specified user account is already a member of the specified group, or the specified group cannot be deleted because it contains a member. pub const MEMBER_IN_GROUP = 1320; + /// The specified user account is not a member of the specified group account. pub const MEMBER_NOT_IN_GROUP = 1321; + /// This operation is disallowed as it could result in an administration account being disabled, deleted or unable to log on. pub const LAST_ADMIN = 1322; + /// Unable to update the password. The value provided as the current password is incorrect. pub const WRONG_PASSWORD = 1323; + /// Unable to update the password. The value provided for the new password contains values that are not allowed in passwords. pub const ILL_FORMED_PASSWORD = 1324; + /// Unable to update the password. The value provided for the new password does not meet the length, complexity, or history requirements of the domain. pub const PASSWORD_RESTRICTION = 1325; + /// The user name or password is incorrect. pub const LOGON_FAILURE = 1326; + /// Account restrictions are preventing this user from signing in. For example: blank passwords aren't allowed, sign-in times are limited, or a policy restriction has been enforced. pub const ACCOUNT_RESTRICTION = 1327; + /// Your account has time restrictions that keep you from signing in right now. pub const INVALID_LOGON_HOURS = 1328; + /// This user isn't allowed to sign in to this computer. pub const INVALID_WORKSTATION = 1329; + /// The password for this account has expired. pub const PASSWORD_EXPIRED = 1330; + /// This user can't sign in because this account is currently disabled. pub const ACCOUNT_DISABLED = 1331; + /// No mapping between account names and security IDs was done. pub const NONE_MAPPED = 1332; + /// Too many local user identifiers (LUIDs) were requested at one time. pub const TOO_MANY_LUIDS_REQUESTED = 1333; + /// No more local user identifiers (LUIDs) are available. pub const LUIDS_EXHAUSTED = 1334; + /// The subauthority part of a security ID is invalid for this particular use. pub const INVALID_SUB_AUTHORITY = 1335; + /// The access control list (ACL) structure is invalid. pub const INVALID_ACL = 1336; + /// The security ID structure is invalid. pub const INVALID_SID = 1337; + /// The security descriptor structure is invalid. pub const INVALID_SECURITY_DESCR = 1338; + /// The inherited access control list (ACL) or access control entry (ACE) could not be built. pub const BAD_INHERITANCE_ACL = 1340; + /// The server is currently disabled. pub const SERVER_DISABLED = 1341; + /// The server is currently enabled. pub const SERVER_NOT_DISABLED = 1342; + /// The value provided was an invalid value for an identifier authority. pub const INVALID_ID_AUTHORITY = 1343; + /// No more memory is available for security information updates. pub const ALLOTTED_SPACE_EXCEEDED = 1344; + /// The specified attributes are invalid, or incompatible with the attributes for the group as a whole. pub const INVALID_GROUP_ATTRIBUTES = 1345; + /// Either a required impersonation level was not provided, or the provided impersonation level is invalid. pub const BAD_IMPERSONATION_LEVEL = 1346; + /// Cannot open an anonymous level security token. pub const CANT_OPEN_ANONYMOUS = 1347; + /// The validation information class requested was invalid. pub const BAD_VALIDATION_CLASS = 1348; + /// The type of the token is inappropriate for its attempted use. pub const BAD_TOKEN_TYPE = 1349; + /// Unable to perform a security operation on an object that has no associated security. pub const NO_SECURITY_ON_OBJECT = 1350; + /// Configuration information could not be read from the domain controller, either because the machine is unavailable, or access has been denied. pub const CANT_ACCESS_DOMAIN_INFO = 1351; + /// The security account manager (SAM) or local security authority (LSA) server was in the wrong state to perform the security operation. pub const INVALID_SERVER_STATE = 1352; + /// The domain was in the wrong state to perform the security operation. pub const INVALID_DOMAIN_STATE = 1353; + /// This operation is only allowed for the Primary Domain Controller of the domain. pub const INVALID_DOMAIN_ROLE = 1354; + /// The specified domain either does not exist or could not be contacted. pub const NO_SUCH_DOMAIN = 1355; + /// The specified domain already exists. pub const DOMAIN_EXISTS = 1356; + /// An attempt was made to exceed the limit on the number of domains per server. pub const DOMAIN_LIMIT_EXCEEDED = 1357; + /// Unable to complete the requested operation because of either a catastrophic media failure or a data structure corruption on the disk. pub const INTERNAL_DB_CORRUPTION = 1358; + /// An internal error occurred. pub const INTERNAL_ERROR = 1359; + /// Generic access types were contained in an access mask which should already be mapped to nongeneric types. pub const GENERIC_NOT_MAPPED = 1360; + /// A security descriptor is not in the right format (absolute or self-relative). pub const BAD_DESCRIPTOR_FORMAT = 1361; + /// The requested action is restricted for use by logon processes only. The calling process has not registered as a logon process. pub const NOT_LOGON_PROCESS = 1362; + /// Cannot start a new logon session with an ID that is already in use. pub const LOGON_SESSION_EXISTS = 1363; + /// A specified authentication package is unknown. pub const NO_SUCH_PACKAGE = 1364; + /// The logon session is not in a state that is consistent with the requested operation. pub const BAD_LOGON_SESSION_STATE = 1365; + /// The logon session ID is already in use. pub const LOGON_SESSION_COLLISION = 1366; + /// A logon request contained an invalid logon type value. pub const INVALID_LOGON_TYPE = 1367; + /// Unable to impersonate using a named pipe until data has been read from that pipe. pub const CANNOT_IMPERSONATE = 1368; + /// The transaction state of a registry subtree is incompatible with the requested operation. pub const RXACT_INVALID_STATE = 1369; + /// An internal security database corruption has been encountered. pub const RXACT_COMMIT_FAILURE = 1370; + /// Cannot perform this operation on built-in accounts. pub const SPECIAL_ACCOUNT = 1371; + /// Cannot perform this operation on this built-in special group. pub const SPECIAL_GROUP = 1372; + /// Cannot perform this operation on this built-in special user. pub const SPECIAL_USER = 1373; + /// The user cannot be removed from a group because the group is currently the user's primary group. pub const MEMBERS_PRIMARY_GROUP = 1374; + /// The token is already in use as a primary token. pub const TOKEN_ALREADY_IN_USE = 1375; + /// The specified local group does not exist. pub const NO_SUCH_ALIAS = 1376; + /// The specified account name is not a member of the group. pub const MEMBER_NOT_IN_ALIAS = 1377; + /// The specified account name is already a member of the group. pub const MEMBER_IN_ALIAS = 1378; + /// The specified local group already exists. pub const ALIAS_EXISTS = 1379; + /// Logon failure: the user has not been granted the requested logon type at this computer. pub const LOGON_NOT_GRANTED = 1380; + /// The maximum number of secrets that may be stored in a single system has been exceeded. pub const TOO_MANY_SECRETS = 1381; + /// The length of a secret exceeds the maximum length allowed. pub const SECRET_TOO_LONG = 1382; + /// The local security authority database contains an internal inconsistency. pub const INTERNAL_DB_ERROR = 1383; + /// During a logon attempt, the user's security context accumulated too many security IDs. pub const TOO_MANY_CONTEXT_IDS = 1384; + /// Logon failure: the user has not been granted the requested logon type at this computer. pub const LOGON_TYPE_NOT_GRANTED = 1385; + /// A cross-encrypted password is necessary to change a user password. pub const NT_CROSS_ENCRYPTION_REQUIRED = 1386; + /// A member could not be added to or removed from the local group because the member does not exist. pub const NO_SUCH_MEMBER = 1387; + /// A new member could not be added to a local group because the member has the wrong account type. pub const INVALID_MEMBER = 1388; + /// Too many security IDs have been specified. pub const TOO_MANY_SIDS = 1389; + /// A cross-encrypted password is necessary to change this user password. pub const LM_CROSS_ENCRYPTION_REQUIRED = 1390; + /// Indicates an ACL contains no inheritable components. pub const NO_INHERITANCE = 1391; + /// The file or directory is corrupted and unreadable. pub const FILE_CORRUPT = 1392; + /// The disk structure is corrupted and unreadable. pub const DISK_CORRUPT = 1393; + /// There is no user session key for the specified logon session. pub const NO_USER_SESSION_KEY = 1394; + /// The service being accessed is licensed for a particular number of connections. No more connections can be made to the service at this time because there are already as many connections as the service can accept. pub const LICENSE_QUOTA_EXCEEDED = 1395; + /// The target account name is incorrect. pub const WRONG_TARGET_NAME = 1396; + /// Mutual Authentication failed. The server's password is out of date at the domain controller. pub const MUTUAL_AUTH_FAILED = 1397; + /// There is a time and/or date difference between the client and server. pub const TIME_SKEW = 1398; + /// This operation cannot be performed on the current domain. pub const CURRENT_DOMAIN_NOT_ALLOWED = 1399; + /// Invalid window handle. pub const INVALID_WINDOW_HANDLE = 1400; + /// Invalid menu handle. pub const INVALID_MENU_HANDLE = 1401; + /// Invalid cursor handle. pub const INVALID_CURSOR_HANDLE = 1402; + /// Invalid accelerator table handle. pub const INVALID_ACCEL_HANDLE = 1403; + /// Invalid hook handle. pub const INVALID_HOOK_HANDLE = 1404; + /// Invalid handle to a multiple-window position structure. pub const INVALID_DWP_HANDLE = 1405; + /// Cannot create a top-level child window. pub const TLW_WITH_WSCHILD = 1406; + /// Cannot find window class. pub const CANNOT_FIND_WND_CLASS = 1407; + /// Invalid window; it belongs to other thread. pub const WINDOW_OF_OTHER_THREAD = 1408; + /// Hot key is already registered. pub const HOTKEY_ALREADY_REGISTERED = 1409; + /// Class already exists. pub const CLASS_ALREADY_EXISTS = 1410; + /// Class does not exist. pub const CLASS_DOES_NOT_EXIST = 1411; + /// Class still has open windows. pub const CLASS_HAS_WINDOWS = 1412; + /// Invalid index. pub const INVALID_INDEX = 1413; + /// Invalid icon handle. pub const INVALID_ICON_HANDLE = 1414; + /// Using private DIALOG window words. pub const PRIVATE_DIALOG_INDEX = 1415; + /// The list box identifier was not found. pub const LISTBOX_ID_NOT_FOUND = 1416; + /// No wildcards were found. pub const NO_WILDCARD_CHARACTERS = 1417; + /// Thread does not have a clipboard open. pub const CLIPBOARD_NOT_OPEN = 1418; + /// Hot key is not registered. pub const HOTKEY_NOT_REGISTERED = 1419; + /// The window is not a valid dialog window. pub const WINDOW_NOT_DIALOG = 1420; + /// Control ID not found. pub const CONTROL_ID_NOT_FOUND = 1421; + /// Invalid message for a combo box because it does not have an edit control. pub const INVALID_COMBOBOX_MESSAGE = 1422; + /// The window is not a combo box. pub const WINDOW_NOT_COMBOBOX = 1423; + /// Height must be less than 256. pub const INVALID_EDIT_HEIGHT = 1424; + /// Invalid device context (DC) handle. pub const DC_NOT_FOUND = 1425; + /// Invalid hook procedure type. pub const INVALID_HOOK_FILTER = 1426; + /// Invalid hook procedure. pub const INVALID_FILTER_PROC = 1427; + /// Cannot set nonlocal hook without a module handle. pub const HOOK_NEEDS_HMOD = 1428; + /// This hook procedure can only be set globally. pub const GLOBAL_ONLY_HOOK = 1429; + /// The journal hook procedure is already installed. pub const JOURNAL_HOOK_SET = 1430; + /// The hook procedure is not installed. pub const HOOK_NOT_INSTALLED = 1431; + /// Invalid message for single-selection list box. pub const INVALID_LB_MESSAGE = 1432; + /// LB_SETCOUNT sent to non-lazy list box. pub const SETCOUNT_ON_BAD_LB = 1433; + /// This list box does not support tab stops. pub const LB_WITHOUT_TABSTOPS = 1434; + /// Cannot destroy object created by another thread. pub const DESTROY_OBJECT_OF_OTHER_THREAD = 1435; + /// Child windows cannot have menus. pub const CHILD_WINDOW_MENU = 1436; + /// The window does not have a system menu. pub const NO_SYSTEM_MENU = 1437; + /// Invalid message box style. pub const INVALID_MSGBOX_STYLE = 1438; + /// Invalid system-wide (SPI_*) parameter. pub const INVALID_SPI_VALUE = 1439; + /// Screen already locked. pub const SCREEN_ALREADY_LOCKED = 1440; + /// All handles to windows in a multiple-window position structure must have the same parent. pub const HWNDS_HAVE_DIFF_PARENT = 1441; + /// The window is not a child window. pub const NOT_CHILD_WINDOW = 1442; + /// Invalid GW_* command. pub const INVALID_GW_COMMAND = 1443; + /// Invalid thread identifier. pub const INVALID_THREAD_ID = 1444; + /// Cannot process a message from a window that is not a multiple document interface (MDI) window. pub const NON_MDICHILD_WINDOW = 1445; + /// Popup menu already active. pub const POPUP_ALREADY_ACTIVE = 1446; + /// The window does not have scroll bars. pub const NO_SCROLLBARS = 1447; + /// Scroll bar range cannot be greater than MAXLONG. pub const INVALID_SCROLLBAR_RANGE = 1448; + /// Cannot show or remove the window in the way specified. pub const INVALID_SHOWWIN_COMMAND = 1449; + /// Insufficient system resources exist to complete the requested service. pub const NO_SYSTEM_RESOURCES = 1450; + /// Insufficient system resources exist to complete the requested service. pub const NONPAGED_SYSTEM_RESOURCES = 1451; + /// Insufficient system resources exist to complete the requested service. pub const PAGED_SYSTEM_RESOURCES = 1452; + /// Insufficient quota to complete the requested service. pub const WORKING_SET_QUOTA = 1453; + /// Insufficient quota to complete the requested service. pub const PAGEFILE_QUOTA = 1454; + /// The paging file is too small for this operation to complete. pub const COMMITMENT_LIMIT = 1455; + /// A menu item was not found. pub const MENU_ITEM_NOT_FOUND = 1456; + /// Invalid keyboard layout handle. pub const INVALID_KEYBOARD_HANDLE = 1457; + /// Hook type not allowed. pub const HOOK_TYPE_NOT_ALLOWED = 1458; + /// This operation requires an interactive window station. pub const REQUIRES_INTERACTIVE_WINDOWSTATION = 1459; + /// This operation returned because the timeout period expired. pub const TIMEOUT = 1460; + /// Invalid monitor handle. pub const INVALID_MONITOR_HANDLE = 1461; + /// Incorrect size argument. pub const INCORRECT_SIZE = 1462; + /// The symbolic link cannot be followed because its type is disabled. pub const SYMLINK_CLASS_DISABLED = 1463; + /// This application does not support the current operation on symbolic links. pub const SYMLINK_NOT_SUPPORTED = 1464; + /// Windows was unable to parse the requested XML data. pub const XML_PARSE_ERROR = 1465; + /// An error was encountered while processing an XML digital signature. pub const XMLDSIG_ERROR = 1466; + /// This application must be restarted. pub const RESTART_APPLICATION = 1467; + /// The caller made the connection request in the wrong routing compartment. pub const WRONG_COMPARTMENT = 1468; + /// There was an AuthIP failure when attempting to connect to the remote host. pub const AUTHIP_FAILURE = 1469; + /// Insufficient NVRAM resources exist to complete the requested service. A reboot might be required. pub const NO_NVRAM_RESOURCES = 1470; + /// Unable to finish the requested operation because the specified process is not a GUI process. pub const NOT_GUI_PROCESS = 1471; + /// The event log file is corrupted. pub const EVENTLOG_FILE_CORRUPT = 1500; + /// No event log file could be opened, so the event logging service did not start. pub const EVENTLOG_CANT_START = 1501; + /// The event log file is full. pub const LOG_FILE_FULL = 1502; + /// The event log file has changed between read operations. pub const EVENTLOG_FILE_CHANGED = 1503; + /// The specified task name is invalid. pub const INVALID_TASK_NAME = 1550; + /// The specified task index is invalid. pub const INVALID_TASK_INDEX = 1551; + /// The specified thread is already joining a task. pub const THREAD_ALREADY_IN_TASK = 1552; + /// The Windows Installer Service could not be accessed. This can occur if the Windows Installer is not correctly installed. Contact your support personnel for assistance. pub const INSTALL_SERVICE_FAILURE = 1601; + /// User cancelled installation. pub const INSTALL_USEREXIT = 1602; + /// Fatal error during installation. pub const INSTALL_FAILURE = 1603; + /// Installation suspended, incomplete. pub const INSTALL_SUSPEND = 1604; + /// This action is only valid for products that are currently installed. pub const UNKNOWN_PRODUCT = 1605; + /// Feature ID not registered. pub const UNKNOWN_FEATURE = 1606; + /// Component ID not registered. pub const UNKNOWN_COMPONENT = 1607; + /// Unknown property. pub const UNKNOWN_PROPERTY = 1608; + /// Handle is in an invalid state. pub const INVALID_HANDLE_STATE = 1609; + /// The configuration data for this product is corrupt. Contact your support personnel. pub const BAD_CONFIGURATION = 1610; + /// Component qualifier not present. pub const INDEX_ABSENT = 1611; + /// The installation source for this product is not available. Verify that the source exists and that you can access it. pub const INSTALL_SOURCE_ABSENT = 1612; + /// This installation package cannot be installed by the Windows Installer service. You must install a Windows service pack that contains a newer version of the Windows Installer service. pub const INSTALL_PACKAGE_VERSION = 1613; + /// Product is uninstalled. pub const PRODUCT_UNINSTALLED = 1614; + /// SQL query syntax invalid or unsupported. pub const BAD_QUERY_SYNTAX = 1615; + /// Record field does not exist. pub const INVALID_FIELD = 1616; + /// The device has been removed. pub const DEVICE_REMOVED = 1617; + /// Another installation is already in progress. Complete that installation before proceeding with this install. pub const INSTALL_ALREADY_RUNNING = 1618; + /// This installation package could not be opened. Verify that the package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows Installer package. pub const INSTALL_PACKAGE_OPEN_FAILED = 1619; + /// This installation package could not be opened. Contact the application vendor to verify that this is a valid Windows Installer package. pub const INSTALL_PACKAGE_INVALID = 1620; + /// There was an error starting the Windows Installer service user interface. Contact your support personnel. pub const INSTALL_UI_FAILURE = 1621; + /// Error opening installation log file. Verify that the specified log file location exists and that you can write to it. pub const INSTALL_LOG_FAILURE = 1622; + /// The language of this installation package is not supported by your system. pub const INSTALL_LANGUAGE_UNSUPPORTED = 1623; + /// Error applying transforms. Verify that the specified transform paths are valid. pub const INSTALL_TRANSFORM_FAILURE = 1624; + /// This installation is forbidden by system policy. Contact your system administrator. pub const INSTALL_PACKAGE_REJECTED = 1625; + /// Function could not be executed. pub const FUNCTION_NOT_CALLED = 1626; + /// Function failed during execution. pub const FUNCTION_FAILED = 1627; + /// Invalid or unknown table specified. pub const INVALID_TABLE = 1628; + /// Data supplied is of wrong type. pub const DATATYPE_MISMATCH = 1629; + /// Data of this type is not supported. pub const UNSUPPORTED_TYPE = 1630; + /// The Windows Installer service failed to start. Contact your support personnel. pub const CREATE_FAILED = 1631; + /// The Temp folder is on a drive that is full or is inaccessible. Free up space on the drive or verify that you have write permission on the Temp folder. pub const INSTALL_TEMP_UNWRITABLE = 1632; + /// This installation package is not supported by this processor type. Contact your product vendor. pub const INSTALL_PLATFORM_UNSUPPORTED = 1633; + /// Component not used on this computer. pub const INSTALL_NOTUSED = 1634; + /// This update package could not be opened. Verify that the update package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows Installer update package. pub const PATCH_PACKAGE_OPEN_FAILED = 1635; + /// This update package could not be opened. Contact the application vendor to verify that this is a valid Windows Installer update package. pub const PATCH_PACKAGE_INVALID = 1636; + /// This update package cannot be processed by the Windows Installer service. You must install a Windows service pack that contains a newer version of the Windows Installer service. pub const PATCH_PACKAGE_UNSUPPORTED = 1637; + /// Another version of this product is already installed. Installation of this version cannot continue. To configure or remove the existing version of this product, use Add/Remove Programs on the Control Panel. pub const PRODUCT_VERSION = 1638; + /// Invalid command line argument. Consult the Windows Installer SDK for detailed command line help. pub const INVALID_COMMAND_LINE = 1639; + /// Only administrators have permission to add, remove, or configure server software during a Terminal services remote session. If you want to install or configure software on the server, contact your network administrator. pub const INSTALL_REMOTE_DISALLOWED = 1640; + /// The requested operation completed successfully. The system will be restarted so the changes can take effect. pub const SUCCESS_REBOOT_INITIATED = 1641; + /// The upgrade cannot be installed by the Windows Installer service because the program to be upgraded may be missing, or the upgrade may update a different version of the program. Verify that the program to be upgraded exists on your computer and that you have the correct upgrade. pub const PATCH_TARGET_NOT_FOUND = 1642; + /// The update package is not permitted by software restriction policy. pub const PATCH_PACKAGE_REJECTED = 1643; + /// One or more customizations are not permitted by software restriction policy. pub const INSTALL_TRANSFORM_REJECTED = 1644; + /// The Windows Installer does not permit installation from a Remote Desktop Connection. pub const INSTALL_REMOTE_PROHIBITED = 1645; + /// Uninstallation of the update package is not supported. pub const PATCH_REMOVAL_UNSUPPORTED = 1646; + /// The update is not applied to this product. pub const UNKNOWN_PATCH = 1647; + /// No valid sequence could be found for the set of updates. pub const PATCH_NO_SEQUENCE = 1648; + /// Update removal was disallowed by policy. pub const PATCH_REMOVAL_DISALLOWED = 1649; + /// The XML update data is invalid. pub const INVALID_PATCH_XML = 1650; + /// Windows Installer does not permit updating of managed advertised products. At least one feature of the product must be installed before applying the update. pub const PATCH_MANAGED_ADVERTISED_PRODUCT = 1651; + /// The Windows Installer service is not accessible in Safe Mode. Please try again when your computer is not in Safe Mode or you can use System Restore to return your machine to a previous good state. pub const INSTALL_SERVICE_SAFEBOOT = 1652; + /// A fail fast exception occurred. Exception handlers will not be invoked and the process will be terminated immediately. pub const FAIL_FAST_EXCEPTION = 1653; + /// The app that you are trying to run is not supported on this version of Windows. pub const INSTALL_REJECTED = 1654; + /// The string binding is invalid. pub const RPC_S_INVALID_STRING_BINDING = 1700; + /// The binding handle is not the correct type. pub const RPC_S_WRONG_KIND_OF_BINDING = 1701; + /// The binding handle is invalid. pub const RPC_S_INVALID_BINDING = 1702; + /// The RPC protocol sequence is not supported. pub const RPC_S_PROTSEQ_NOT_SUPPORTED = 1703; + /// The RPC protocol sequence is invalid. pub const RPC_S_INVALID_RPC_PROTSEQ = 1704; + /// The string universal unique identifier (UUID) is invalid. pub const RPC_S_INVALID_STRING_UUID = 1705; + /// The endpoint format is invalid. pub const RPC_S_INVALID_ENDPOINT_FORMAT = 1706; + /// The network address is invalid. pub const RPC_S_INVALID_NET_ADDR = 1707; + /// No endpoint was found. pub const RPC_S_NO_ENDPOINT_FOUND = 1708; + /// The timeout value is invalid. pub const RPC_S_INVALID_TIMEOUT = 1709; + /// The object universal unique identifier (UUID) was not found. pub const RPC_S_OBJECT_NOT_FOUND = 1710; + /// The object universal unique identifier (UUID) has already been registered. pub const RPC_S_ALREADY_REGISTERED = 1711; + /// The type universal unique identifier (UUID) has already been registered. pub const RPC_S_TYPE_ALREADY_REGISTERED = 1712; + /// The RPC server is already listening. pub const RPC_S_ALREADY_LISTENING = 1713; + /// No protocol sequences have been registered. pub const RPC_S_NO_PROTSEQS_REGISTERED = 1714; + /// The RPC server is not listening. pub const RPC_S_NOT_LISTENING = 1715; + /// The manager type is unknown. pub const RPC_S_UNKNOWN_MGR_TYPE = 1716; + /// The interface is unknown. pub const RPC_S_UNKNOWN_IF = 1717; + /// There are no bindings. pub const RPC_S_NO_BINDINGS = 1718; + /// There are no protocol sequences. pub const RPC_S_NO_PROTSEQS = 1719; + /// The endpoint cannot be created. pub const RPC_S_CANT_CREATE_ENDPOINT = 1720; + /// Not enough resources are available to complete this operation. pub const RPC_S_OUT_OF_RESOURCES = 1721; + /// The RPC server is unavailable. pub const RPC_S_SERVER_UNAVAILABLE = 1722; + /// The RPC server is too busy to complete this operation. pub const RPC_S_SERVER_TOO_BUSY = 1723; + /// The network options are invalid. pub const RPC_S_INVALID_NETWORK_OPTIONS = 1724; + /// There are no remote procedure calls active on this thread. pub const RPC_S_NO_CALL_ACTIVE = 1725; + /// The remote procedure call failed. pub const RPC_S_CALL_FAILED = 1726; + /// The remote procedure call failed and did not execute. pub const RPC_S_CALL_FAILED_DNE = 1727; + /// A remote procedure call (RPC) protocol error occurred. pub const RPC_S_PROTOCOL_ERROR = 1728; + /// Access to the HTTP proxy is denied. pub const RPC_S_PROXY_ACCESS_DENIED = 1729; + /// The transfer syntax is not supported by the RPC server. pub const RPC_S_UNSUPPORTED_TRANS_SYN = 1730; + /// The universal unique identifier (UUID) type is not supported. pub const RPC_S_UNSUPPORTED_TYPE = 1732; + /// The tag is invalid. pub const RPC_S_INVALID_TAG = 1733; + /// The array bounds are invalid. pub const RPC_S_INVALID_BOUND = 1734; + /// The binding does not contain an entry name. pub const RPC_S_NO_ENTRY_NAME = 1735; + /// The name syntax is invalid. pub const RPC_S_INVALID_NAME_SYNTAX = 1736; + /// The name syntax is not supported. pub const RPC_S_UNSUPPORTED_NAME_SYNTAX = 1737; + /// No network address is available to use to construct a universal unique identifier (UUID). pub const RPC_S_UUID_NO_ADDRESS = 1739; + /// The endpoint is a duplicate. pub const RPC_S_DUPLICATE_ENDPOINT = 1740; + /// The authentication type is unknown. pub const RPC_S_UNKNOWN_AUTHN_TYPE = 1741; + /// The maximum number of calls is too small. pub const RPC_S_MAX_CALLS_TOO_SMALL = 1742; + /// The string is too long. pub const RPC_S_STRING_TOO_LONG = 1743; + /// The RPC protocol sequence was not found. pub const RPC_S_PROTSEQ_NOT_FOUND = 1744; + /// The procedure number is out of range. pub const RPC_S_PROCNUM_OUT_OF_RANGE = 1745; + /// The binding does not contain any authentication information. pub const RPC_S_BINDING_HAS_NO_AUTH = 1746; + /// The authentication service is unknown. pub const RPC_S_UNKNOWN_AUTHN_SERVICE = 1747; + /// The authentication level is unknown. pub const RPC_S_UNKNOWN_AUTHN_LEVEL = 1748; + /// The security context is invalid. pub const RPC_S_INVALID_AUTH_IDENTITY = 1749; + /// The authorization service is unknown. pub const RPC_S_UNKNOWN_AUTHZ_SERVICE = 1750; + /// The entry is invalid. pub const EPT_S_INVALID_ENTRY = 1751; + /// The server endpoint cannot perform the operation. pub const EPT_S_CANT_PERFORM_OP = 1752; + /// There are no more endpoints available from the endpoint mapper. pub const EPT_S_NOT_REGISTERED = 1753; + /// No interfaces have been exported. pub const RPC_S_NOTHING_TO_EXPORT = 1754; + /// The entry name is incomplete. pub const RPC_S_INCOMPLETE_NAME = 1755; + /// The version option is invalid. pub const RPC_S_INVALID_VERS_OPTION = 1756; + /// There are no more members. pub const RPC_S_NO_MORE_MEMBERS = 1757; + /// There is nothing to unexport. pub const RPC_S_NOT_ALL_OBJS_UNEXPORTED = 1758; + /// The interface was not found. pub const RPC_S_INTERFACE_NOT_FOUND = 1759; + /// The entry already exists. pub const RPC_S_ENTRY_ALREADY_EXISTS = 1760; + /// The entry is not found. pub const RPC_S_ENTRY_NOT_FOUND = 1761; + /// The name service is unavailable. pub const RPC_S_NAME_SERVICE_UNAVAILABLE = 1762; + /// The network address family is invalid. pub const RPC_S_INVALID_NAF_ID = 1763; + /// The requested operation is not supported. pub const RPC_S_CANNOT_SUPPORT = 1764; + /// No security context is available to allow impersonation. pub const RPC_S_NO_CONTEXT_AVAILABLE = 1765; + /// An internal error occurred in a remote procedure call (RPC). pub const RPC_S_INTERNAL_ERROR = 1766; + /// The RPC server attempted an integer division by zero. pub const RPC_S_ZERO_DIVIDE = 1767; + /// An addressing error occurred in the RPC server. pub const RPC_S_ADDRESS_ERROR = 1768; + /// A floating-point operation at the RPC server caused a division by zero. pub const RPC_S_FP_DIV_ZERO = 1769; + /// A floating-point underflow occurred at the RPC server. pub const RPC_S_FP_UNDERFLOW = 1770; + /// A floating-point overflow occurred at the RPC server. pub const RPC_S_FP_OVERFLOW = 1771; + /// The list of RPC servers available for the binding of auto handles has been exhausted. pub const RPC_X_NO_MORE_ENTRIES = 1772; + /// Unable to open the character translation table file. pub const RPC_X_SS_CHAR_TRANS_OPEN_FAIL = 1773; + /// The file containing the character translation table has fewer than 512 bytes. pub const RPC_X_SS_CHAR_TRANS_SHORT_FILE = 1774; + /// A null context handle was passed from the client to the host during a remote procedure call. pub const RPC_X_SS_IN_NULL_CONTEXT = 1775; + /// The context handle changed during a remote procedure call. pub const RPC_X_SS_CONTEXT_DAMAGED = 1777; + /// The binding handles passed to a remote procedure call do not match. pub const RPC_X_SS_HANDLES_MISMATCH = 1778; + /// The stub is unable to get the remote procedure call handle. pub const RPC_X_SS_CANNOT_GET_CALL_HANDLE = 1779; + /// A null reference pointer was passed to the stub. pub const RPC_X_NULL_REF_POINTER = 1780; + /// The enumeration value is out of range. pub const RPC_X_ENUM_VALUE_OUT_OF_RANGE = 1781; + /// The byte count is too small. pub const RPC_X_BYTE_COUNT_TOO_SMALL = 1782; + /// The stub received bad data. pub const RPC_X_BAD_STUB_DATA = 1783; + /// The supplied user buffer is not valid for the requested operation. pub const INVALID_USER_BUFFER = 1784; + /// The disk media is not recognized. It may not be formatted. pub const UNRECOGNIZED_MEDIA = 1785; + /// The workstation does not have a trust secret. pub const NO_TRUST_LSA_SECRET = 1786; + /// The security database on the server does not have a computer account for this workstation trust relationship. pub const NO_TRUST_SAM_ACCOUNT = 1787; + /// The trust relationship between the primary domain and the trusted domain failed. pub const TRUSTED_DOMAIN_FAILURE = 1788; + /// The trust relationship between this workstation and the primary domain failed. pub const TRUSTED_RELATIONSHIP_FAILURE = 1789; + /// The network logon failed. pub const TRUST_FAILURE = 1790; + /// A remote procedure call is already in progress for this thread. pub const RPC_S_CALL_IN_PROGRESS = 1791; + /// An attempt was made to logon, but the network logon service was not started. pub const NETLOGON_NOT_STARTED = 1792; + /// The user's account has expired. pub const ACCOUNT_EXPIRED = 1793; + /// The redirector is in use and cannot be unloaded. pub const REDIRECTOR_HAS_OPEN_HANDLES = 1794; + /// The specified printer driver is already installed. pub const PRINTER_DRIVER_ALREADY_INSTALLED = 1795; + /// The specified port is unknown. pub const UNKNOWN_PORT = 1796; + /// The printer driver is unknown. pub const UNKNOWN_PRINTER_DRIVER = 1797; + /// The print processor is unknown. pub const UNKNOWN_PRINTPROCESSOR = 1798; + /// The specified separator file is invalid. pub const INVALID_SEPARATOR_FILE = 1799; + /// The specified priority is invalid. pub const INVALID_PRIORITY = 1800; + /// The printer name is invalid. pub const INVALID_PRINTER_NAME = 1801; + /// The printer already exists. pub const PRINTER_ALREADY_EXISTS = 1802; + /// The printer command is invalid. pub const INVALID_PRINTER_COMMAND = 1803; + /// The specified datatype is invalid. pub const INVALID_DATATYPE = 1804; + /// The environment specified is invalid. pub const INVALID_ENVIRONMENT = 1805; + /// There are no more bindings. pub const RPC_S_NO_MORE_BINDINGS = 1806; + /// The account used is an interdomain trust account. Use your global user account or local user account to access this server. pub const NOLOGON_INTERDOMAIN_TRUST_ACCOUNT = 1807; + /// The account used is a computer account. Use your global user account or local user account to access this server. pub const NOLOGON_WORKSTATION_TRUST_ACCOUNT = 1808; + /// The account used is a server trust account. Use your global user account or local user account to access this server. pub const NOLOGON_SERVER_TRUST_ACCOUNT = 1809; + /// The name or security ID (SID) of the domain specified is inconsistent with the trust information for that domain. pub const DOMAIN_TRUST_INCONSISTENT = 1810; + /// The server is in use and cannot be unloaded. pub const SERVER_HAS_OPEN_HANDLES = 1811; + /// The specified image file did not contain a resource section. pub const RESOURCE_DATA_NOT_FOUND = 1812; + /// The specified resource type cannot be found in the image file. pub const RESOURCE_TYPE_NOT_FOUND = 1813; + /// The specified resource name cannot be found in the image file. pub const RESOURCE_NAME_NOT_FOUND = 1814; + /// The specified resource language ID cannot be found in the image file. pub const RESOURCE_LANG_NOT_FOUND = 1815; + /// Not enough quota is available to process this command. pub const NOT_ENOUGH_QUOTA = 1816; + /// No interfaces have been registered. pub const RPC_S_NO_INTERFACES = 1817; + /// The remote procedure call was cancelled. pub const RPC_S_CALL_CANCELLED = 1818; + /// The binding handle does not contain all required information. pub const RPC_S_BINDING_INCOMPLETE = 1819; + /// A communications failure occurred during a remote procedure call. pub const RPC_S_COMM_FAILURE = 1820; + /// The requested authentication level is not supported. pub const RPC_S_UNSUPPORTED_AUTHN_LEVEL = 1821; + /// No principal name registered. pub const RPC_S_NO_PRINC_NAME = 1822; + /// The error specified is not a valid Windows RPC error code. pub const RPC_S_NOT_RPC_ERROR = 1823; + /// A UUID that is valid only on this computer has been allocated. pub const RPC_S_UUID_LOCAL_ONLY = 1824; + /// A security package specific error occurred. pub const RPC_S_SEC_PKG_ERROR = 1825; + /// Thread is not canceled. pub const RPC_S_NOT_CANCELLED = 1826; + /// Invalid operation on the encoding/decoding handle. pub const RPC_X_INVALID_ES_ACTION = 1827; + /// Incompatible version of the serializing package. pub const RPC_X_WRONG_ES_VERSION = 1828; + /// Incompatible version of the RPC stub. pub const RPC_X_WRONG_STUB_VERSION = 1829; + /// The RPC pipe object is invalid or corrupted. pub const RPC_X_INVALID_PIPE_OBJECT = 1830; + /// An invalid operation was attempted on an RPC pipe object. pub const RPC_X_WRONG_PIPE_ORDER = 1831; + /// Unsupported RPC pipe version. pub const RPC_X_WRONG_PIPE_VERSION = 1832; + /// HTTP proxy server rejected the connection because the cookie authentication failed. pub const RPC_S_COOKIE_AUTH_FAILED = 1833; + /// The group member was not found. pub const RPC_S_GROUP_MEMBER_NOT_FOUND = 1898; + /// The endpoint mapper database entry could not be created. pub const EPT_S_CANT_CREATE = 1899; + /// The object universal unique identifier (UUID) is the nil UUID. pub const RPC_S_INVALID_OBJECT = 1900; + /// The specified time is invalid. pub const INVALID_TIME = 1901; + /// The specified form name is invalid. pub const INVALID_FORM_NAME = 1902; + /// The specified form size is invalid. pub const INVALID_FORM_SIZE = 1903; + /// The specified printer handle is already being waited on. pub const ALREADY_WAITING = 1904; + /// The specified printer has been deleted. pub const PRINTER_DELETED = 1905; + /// The state of the printer is invalid. pub const INVALID_PRINTER_STATE = 1906; + /// The user's password must be changed before signing in. pub const PASSWORD_MUST_CHANGE = 1907; + /// Could not find the domain controller for this domain. pub const DOMAIN_CONTROLLER_NOT_FOUND = 1908; + /// The referenced account is currently locked out and may not be logged on to. pub const ACCOUNT_LOCKED_OUT = 1909; + /// The object exporter specified was not found. pub const OR_INVALID_OXID = 1910; + /// The object specified was not found. pub const OR_INVALID_OID = 1911; + /// The object resolver set specified was not found. pub const OR_INVALID_SET = 1912; + /// Some data remains to be sent in the request buffer. pub const RPC_S_SEND_INCOMPLETE = 1913; + /// Invalid asynchronous remote procedure call handle. pub const RPC_S_INVALID_ASYNC_HANDLE = 1914; + /// Invalid asynchronous RPC call handle for this operation. pub const RPC_S_INVALID_ASYNC_CALL = 1915; + /// The RPC pipe object has already been closed. pub const RPC_X_PIPE_CLOSED = 1916; + /// The RPC call completed before all pipes were processed. pub const RPC_X_PIPE_DISCIPLINE_ERROR = 1917; + /// No more data is available from the RPC pipe. pub const RPC_X_PIPE_EMPTY = 1918; + /// No site name is available for this machine. pub const NO_SITENAME = 1919; + /// The file cannot be accessed by the system. pub const CANT_ACCESS_FILE = 1920; + /// The name of the file cannot be resolved by the system. pub const CANT_RESOLVE_FILENAME = 1921; + /// The entry is not of the expected type. pub const RPC_S_ENTRY_TYPE_MISMATCH = 1922; + /// Not all object UUIDs could be exported to the specified entry. pub const RPC_S_NOT_ALL_OBJS_EXPORTED = 1923; + /// Interface could not be exported to the specified entry. pub const RPC_S_INTERFACE_NOT_EXPORTED = 1924; + /// The specified profile entry could not be added. pub const RPC_S_PROFILE_NOT_ADDED = 1925; + /// The specified profile element could not be added. pub const RPC_S_PRF_ELT_NOT_ADDED = 1926; + /// The specified profile element could not be removed. pub const RPC_S_PRF_ELT_NOT_REMOVED = 1927; + /// The group element could not be added. pub const RPC_S_GRP_ELT_NOT_ADDED = 1928; + /// The group element could not be removed. pub const RPC_S_GRP_ELT_NOT_REMOVED = 1929; + /// The printer driver is not compatible with a policy enabled on your computer that blocks NT 4.0 drivers. pub const KM_DRIVER_BLOCKED = 1930; + /// The context has expired and can no longer be used. pub const CONTEXT_EXPIRED = 1931; + /// The current user's delegated trust creation quota has been exceeded. pub const PER_USER_TRUST_QUOTA_EXCEEDED = 1932; + /// The total delegated trust creation quota has been exceeded. pub const ALL_USER_TRUST_QUOTA_EXCEEDED = 1933; + /// The current user's delegated trust deletion quota has been exceeded. pub const USER_DELETE_TRUST_QUOTA_EXCEEDED = 1934; + /// The computer you are signing into is protected by an authentication firewall. The specified account is not allowed to authenticate to the computer. pub const AUTHENTICATION_FIREWALL_FAILED = 1935; + /// Remote connections to the Print Spooler are blocked by a policy set on your machine. pub const REMOTE_PRINT_CONNECTIONS_BLOCKED = 1936; + /// Authentication failed because NTLM authentication has been disabled. pub const NTLM_BLOCKED = 1937; + /// Logon Failure: EAS policy requires that the user change their password before this operation can be performed. pub const PASSWORD_CHANGE_REQUIRED = 1938; + /// The pixel format is invalid. pub const INVALID_PIXEL_FORMAT = 2000; + /// The specified driver is invalid. pub const BAD_DRIVER = 2001; + /// The window style or class attribute is invalid for this operation. pub const INVALID_WINDOW_STYLE = 2002; + /// The requested metafile operation is not supported. pub const METAFILE_NOT_SUPPORTED = 2003; + /// The requested transformation operation is not supported. pub const TRANSFORM_NOT_SUPPORTED = 2004; + /// The requested clipping operation is not supported. pub const CLIPPING_NOT_SUPPORTED = 2005; + /// The specified color management module is invalid. pub const INVALID_CMM = 2010; + /// The specified color profile is invalid. pub const INVALID_PROFILE = 2011; + /// The specified tag was not found. pub const TAG_NOT_FOUND = 2012; + /// A required tag is not present. pub const TAG_NOT_PRESENT = 2013; + /// The specified tag is already present. pub const DUPLICATE_TAG = 2014; + /// The specified color profile is not associated with the specified device. pub const PROFILE_NOT_ASSOCIATED_WITH_DEVICE = 2015; + /// The specified color profile was not found. pub const PROFILE_NOT_FOUND = 2016; + /// The specified color space is invalid. pub const INVALID_COLORSPACE = 2017; + /// Image Color Management is not enabled. pub const ICM_NOT_ENABLED = 2018; + /// There was an error while deleting the color transform. pub const DELETING_ICM_XFORM = 2019; + /// The specified color transform is invalid. pub const INVALID_TRANSFORM = 2020; + /// The specified transform does not match the bitmap's color space. pub const COLORSPACE_MISMATCH = 2021; + /// The specified named color index is not present in the profile. pub const INVALID_COLORINDEX = 2022; + /// The specified profile is intended for a device of a different type than the specified device. pub const PROFILE_DOES_NOT_MATCH_DEVICE = 2023; + /// The network connection was made successfully, but the user had to be prompted for a password other than the one originally specified. pub const CONNECTED_OTHER_PASSWORD = 2108; + /// The network connection was made successfully using default credentials. pub const CONNECTED_OTHER_PASSWORD_DEFAULT = 2109; + /// The specified username is invalid. pub const BAD_USERNAME = 2202; + /// This network connection does not exist. pub const NOT_CONNECTED = 2250; + /// This network connection has files open or requests pending. pub const OPEN_FILES = 2401; + /// Active connections still exist. pub const ACTIVE_CONNECTIONS = 2402; + /// The device is in use by an active process and cannot be disconnected. pub const DEVICE_IN_USE = 2404; + /// The specified print monitor is unknown. pub const UNKNOWN_PRINT_MONITOR = 3000; + /// The specified printer driver is currently in use. pub const PRINTER_DRIVER_IN_USE = 3001; + /// The spool file was not found. pub const SPOOL_FILE_NOT_FOUND = 3002; + /// A StartDocPrinter call was not issued. pub const SPL_NO_STARTDOC = 3003; + /// An AddJob call was not issued. pub const SPL_NO_ADDJOB = 3004; + /// The specified print processor has already been installed. pub const PRINT_PROCESSOR_ALREADY_INSTALLED = 3005; + /// The specified print monitor has already been installed. pub const PRINT_MONITOR_ALREADY_INSTALLED = 3006; + /// The specified print monitor does not have the required functions. pub const INVALID_PRINT_MONITOR = 3007; + /// The specified print monitor is currently in use. pub const PRINT_MONITOR_IN_USE = 3008; + /// The requested operation is not allowed when there are jobs queued to the printer. pub const PRINTER_HAS_JOBS_QUEUED = 3009; + /// The requested operation is successful. Changes will not be effective until the system is rebooted. pub const SUCCESS_REBOOT_REQUIRED = 3010; + /// The requested operation is successful. Changes will not be effective until the service is restarted. pub const SUCCESS_RESTART_REQUIRED = 3011; + /// No printers were found. pub const PRINTER_NOT_FOUND = 3012; + /// The printer driver is known to be unreliable. pub const PRINTER_DRIVER_WARNED = 3013; + /// The printer driver is known to harm the system. pub const PRINTER_DRIVER_BLOCKED = 3014; + /// The specified printer driver package is currently in use. pub const PRINTER_DRIVER_PACKAGE_IN_USE = 3015; + /// Unable to find a core driver package that is required by the printer driver package. pub const CORE_DRIVER_PACKAGE_NOT_FOUND = 3016; + /// The requested operation failed. A system reboot is required to roll back changes made. pub const FAIL_REBOOT_REQUIRED = 3017; + /// The requested operation failed. A system reboot has been initiated to roll back changes made. pub const FAIL_REBOOT_INITIATED = 3018; + /// The specified printer driver was not found on the system and needs to be downloaded. pub const PRINTER_DRIVER_DOWNLOAD_NEEDED = 3019; + /// The requested print job has failed to print. A print system update requires the job to be resubmitted. pub const PRINT_JOB_RESTART_REQUIRED = 3020; + /// The printer driver does not contain a valid manifest, or contains too many manifests. pub const INVALID_PRINTER_DRIVER_MANIFEST = 3021; + /// The specified printer cannot be shared. pub const PRINTER_NOT_SHAREABLE = 3022; + /// The operation was paused. pub const REQUEST_PAUSED = 3050; + /// Reissue the given operation as a cached IO operation. pub const IO_REISSUE_AS_CACHED = 3950; diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index e13ed0f131..426514f7d7 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -1,33 +1,59 @@ pub const ERROR = @import("error.zig"); -pub extern "advapi32" stdcallcc fn CryptAcquireContextA(phProv: &HCRYPTPROV, pszContainer: ?LPCSTR, - pszProvider: ?LPCSTR, dwProvType: DWORD, dwFlags: DWORD) BOOL; +pub extern "advapi32" stdcallcc fn CryptAcquireContextA( + phProv: &HCRYPTPROV, + pszContainer: ?LPCSTR, + pszProvider: ?LPCSTR, + dwProvType: DWORD, + dwFlags: DWORD, +) BOOL; pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL; pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: &BYTE) BOOL; - pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: LPCSTR, - lpSecurityAttributes: ?&SECURITY_ATTRIBUTES) BOOL; - -pub extern "kernel32" stdcallcc fn CreateFileA(lpFileName: LPCSTR, dwDesiredAccess: DWORD, - dwShareMode: DWORD, lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD, - dwFlagsAndAttributes: DWORD, hTemplateFile: ?HANDLE) HANDLE; - -pub extern "kernel32" stdcallcc fn CreatePipe(hReadPipe: &HANDLE, hWritePipe: &HANDLE, - lpPipeAttributes: &const SECURITY_ATTRIBUTES, nSize: DWORD) BOOL; - -pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lpCommandLine: LPSTR, - lpProcessAttributes: ?&SECURITY_ATTRIBUTES, lpThreadAttributes: ?&SECURITY_ATTRIBUTES, bInheritHandles: BOOL, - dwCreationFlags: DWORD, lpEnvironment: ?&c_void, lpCurrentDirectory: ?LPCSTR, lpStartupInfo: &STARTUPINFOA, - lpProcessInformation: &PROCESS_INFORMATION) BOOL; - -pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR, - dwFlags: DWORD) BOOLEAN; - +pub extern "kernel32" stdcallcc fn CreateDirectoryA( + lpPathName: LPCSTR, + lpSecurityAttributes: ?&SECURITY_ATTRIBUTES, +) BOOL; + +pub extern "kernel32" stdcallcc fn CreateFileA( + lpFileName: LPCSTR, + dwDesiredAccess: DWORD, + dwShareMode: DWORD, + lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, + dwCreationDisposition: DWORD, + dwFlagsAndAttributes: DWORD, + hTemplateFile: ?HANDLE, +) HANDLE; + +pub extern "kernel32" stdcallcc fn CreatePipe( + hReadPipe: &HANDLE, + hWritePipe: &HANDLE, + lpPipeAttributes: &const SECURITY_ATTRIBUTES, + nSize: DWORD, +) BOOL; + +pub extern "kernel32" stdcallcc fn CreateProcessA( + lpApplicationName: ?LPCSTR, + lpCommandLine: LPSTR, + lpProcessAttributes: ?&SECURITY_ATTRIBUTES, + lpThreadAttributes: ?&SECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: ?&c_void, + lpCurrentDirectory: ?LPCSTR, + lpStartupInfo: &STARTUPINFOA, + lpProcessInformation: &PROCESS_INFORMATION, +) BOOL; + +pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( + lpSymlinkFileName: LPCSTR, + lpTargetFileName: LPCSTR, + dwFlags: DWORD, +) BOOLEAN; pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; @@ -55,12 +81,19 @@ pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilen pub extern "kernel32" stdcallcc fn GetLastError() DWORD; -pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE, - in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, out_lpFileInformation: &c_void, - in_dwBufferSize: DWORD) BOOL; - -pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpszFilePath: LPSTR, - cchFilePath: DWORD, dwFlags: DWORD) DWORD; +pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( + in_hFile: HANDLE, + in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, + out_lpFileInformation: &c_void, + in_dwBufferSize: DWORD, +) BOOL; + +pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( + hFile: HANDLE, + lpszFilePath: LPSTR, + cchFilePath: DWORD, + dwFlags: DWORD, +) DWORD; pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; @@ -80,21 +113,32 @@ pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBy pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void) BOOL; -pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR, - dwFlags: DWORD) BOOL; - +pub extern "kernel32" stdcallcc fn MoveFileExA( + lpExistingFileName: LPCSTR, + lpNewFileName: LPCSTR, + dwFlags: DWORD, +) BOOL; + pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: &LARGE_INTEGER) BOOL; pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: &LARGE_INTEGER) BOOL; pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL; -pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void, - in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD, - in_out_lpOverlapped: ?&OVERLAPPED) BOOL; - -pub extern "kernel32" stdcallcc fn SetFilePointerEx(in_fFile: HANDLE, in_liDistanceToMove: LARGE_INTEGER, - out_opt_ldNewFilePointer: ?&LARGE_INTEGER, in_dwMoveMethod: DWORD) BOOL; +pub extern "kernel32" stdcallcc fn ReadFile( + in_hFile: HANDLE, + out_lpBuffer: &c_void, + in_nNumberOfBytesToRead: DWORD, + out_lpNumberOfBytesRead: &DWORD, + in_out_lpOverlapped: ?&OVERLAPPED, +) BOOL; + +pub extern "kernel32" stdcallcc fn SetFilePointerEx( + in_fFile: HANDLE, + in_liDistanceToMove: LARGE_INTEGER, + out_opt_ldNewFilePointer: ?&LARGE_INTEGER, + in_dwMoveMethod: DWORD, +) BOOL; pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL; @@ -104,14 +148,18 @@ pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD; -pub extern "kernel32" stdcallcc fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &const c_void, - in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD, - in_out_lpOverlapped: ?&OVERLAPPED) BOOL; +pub extern "kernel32" stdcallcc fn WriteFile( + in_hFile: HANDLE, + in_lpBuffer: &const c_void, + in_nNumberOfBytesToWrite: DWORD, + out_lpNumberOfBytesWritten: ?&DWORD, + in_out_lpOverlapped: ?&OVERLAPPED, +) BOOL; //TODO: call unicode versions instead of relying on ANSI code page pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE; -pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; +pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; @@ -176,49 +224,51 @@ pub const MAX_PATH = 260; // TODO issue #305 pub const FILE_INFO_BY_HANDLE_CLASS = u32; -pub const FileBasicInfo = 0; -pub const FileStandardInfo = 1; -pub const FileNameInfo = 2; -pub const FileRenameInfo = 3; -pub const FileDispositionInfo = 4; -pub const FileAllocationInfo = 5; -pub const FileEndOfFileInfo = 6; -pub const FileStreamInfo = 7; -pub const FileCompressionInfo = 8; -pub const FileAttributeTagInfo = 9; -pub const FileIdBothDirectoryInfo = 10; -pub const FileIdBothDirectoryRestartInfo = 11; -pub const FileIoPriorityHintInfo = 12; -pub const FileRemoteProtocolInfo = 13; -pub const FileFullDirectoryInfo = 14; -pub const FileFullDirectoryRestartInfo = 15; -pub const FileStorageInfo = 16; -pub const FileAlignmentInfo = 17; -pub const FileIdInfo = 18; -pub const FileIdExtdDirectoryInfo = 19; -pub const FileIdExtdDirectoryRestartInfo = 20; +pub const FileBasicInfo = 0; +pub const FileStandardInfo = 1; +pub const FileNameInfo = 2; +pub const FileRenameInfo = 3; +pub const FileDispositionInfo = 4; +pub const FileAllocationInfo = 5; +pub const FileEndOfFileInfo = 6; +pub const FileStreamInfo = 7; +pub const FileCompressionInfo = 8; +pub const FileAttributeTagInfo = 9; +pub const FileIdBothDirectoryInfo = 10; +pub const FileIdBothDirectoryRestartInfo = 11; +pub const FileIoPriorityHintInfo = 12; +pub const FileRemoteProtocolInfo = 13; +pub const FileFullDirectoryInfo = 14; +pub const FileFullDirectoryRestartInfo = 15; +pub const FileStorageInfo = 16; +pub const FileAlignmentInfo = 17; +pub const FileIdInfo = 18; +pub const FileIdExtdDirectoryInfo = 19; +pub const FileIdExtdDirectoryRestartInfo = 20; pub const FILE_NAME_INFO = extern struct { FileNameLength: DWORD, FileName: [1]WCHAR, }; - /// Return the normalized drive name. This is the default. pub const FILE_NAME_NORMALIZED = 0x0; + /// Return the opened file name (not normalized). pub const FILE_NAME_OPENED = 0x8; /// Return the path with the drive letter. This is the default. pub const VOLUME_NAME_DOS = 0x0; + /// Return the path with a volume GUID path instead of the drive name. pub const VOLUME_NAME_GUID = 0x1; + /// Return the path with no drive information. pub const VOLUME_NAME_NONE = 0x4; + /// Return the path with the volume device path. pub const VOLUME_NAME_NT = 0x2; - pub const SECURITY_ATTRIBUTES = extern struct { nLength: DWORD, lpSecurityDescriptor: ?&c_void, @@ -227,7 +277,6 @@ pub const SECURITY_ATTRIBUTES = extern struct { pub const PSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES; pub const LPSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES; - pub const GENERIC_READ = 0x80000000; pub const GENERIC_WRITE = 0x40000000; pub const GENERIC_EXECUTE = 0x20000000; @@ -243,7 +292,6 @@ pub const OPEN_ALWAYS = 4; pub const OPEN_EXISTING = 3; pub const TRUNCATE_EXISTING = 5; - pub const FILE_ATTRIBUTE_ARCHIVE = 0x20; pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000; pub const FILE_ATTRIBUTE_HIDDEN = 0x2; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 5af318b7b0..7b7fdfae08 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -7,7 +7,7 @@ const mem = std.mem; const BufMap = std.BufMap; const cstr = std.cstr; -pub const WaitError = error { +pub const WaitError = error{ WaitAbandoned, WaitTimeOut, Unexpected, @@ -33,7 +33,7 @@ pub fn windowsClose(handle: windows.HANDLE) void { assert(windows.CloseHandle(handle) != 0); } -pub const WriteError = error { +pub const WriteError = error{ SystemResources, OperationAborted, IoPending, @@ -68,20 +68,18 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { const size = @sizeOf(windows.FILE_NAME_INFO); var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = []u8{0} ** (size + windows.MAX_PATH); - if (windows.GetFileInformationByHandleEx(handle, windows.FileNameInfo, - @ptrCast(&c_void, &name_info_bytes[0]), u32(name_info_bytes.len)) == 0) - { + if (windows.GetFileInformationByHandleEx(handle, windows.FileNameInfo, @ptrCast(&c_void, &name_info_bytes[0]), u32(name_info_bytes.len)) == 0) { return true; } const name_info = @ptrCast(&const windows.FILE_NAME_INFO, &name_info_bytes[0]); const name_bytes = name_info_bytes[size..size + usize(name_info.FileNameLength)]; - const name_wide = ([]u16)(name_bytes); - return mem.indexOf(u16, name_wide, []u16{'m','s','y','s','-'}) != null or - mem.indexOf(u16, name_wide, []u16{'-','p','t','y'}) != null; + const name_wide = ([]u16)(name_bytes); + return mem.indexOf(u16, name_wide, []u16{ 'm', 's', 'y', 's', '-' }) != null or + mem.indexOf(u16, name_wide, []u16{ '-', 'p', 't', 'y' }) != null; } -pub const OpenError = error { +pub const OpenError = error{ SharingViolation, PathAlreadyExists, FileNotFound, @@ -92,15 +90,18 @@ pub const OpenError = error { }; /// `file_path` needs to be copied in memory to add a null terminating byte, hence the allocator. -pub fn windowsOpen(allocator: &mem.Allocator, file_path: []const u8, desired_access: windows.DWORD, share_mode: windows.DWORD, - creation_disposition: windows.DWORD, flags_and_attrs: windows.DWORD) - OpenError!windows.HANDLE -{ +pub fn windowsOpen( + allocator: &mem.Allocator, + file_path: []const u8, + desired_access: windows.DWORD, + share_mode: windows.DWORD, + creation_disposition: windows.DWORD, + flags_and_attrs: windows.DWORD, +) OpenError!windows.HANDLE { const path_with_null = try cstr.addNullByte(allocator, file_path); defer allocator.free(path_with_null); - const result = windows.CreateFileA(path_with_null.ptr, desired_access, share_mode, null, creation_disposition, - flags_and_attrs, null); + const result = windows.CreateFileA(path_with_null.ptr, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); if (result == windows.INVALID_HANDLE_VALUE) { const err = windows.GetLastError(); @@ -156,18 +157,16 @@ pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) !windows. } pub fn windowsUnloadDll(hModule: windows.HMODULE) void { - assert(windows.FreeLibrary(hModule)!= 0); + assert(windows.FreeLibrary(hModule) != 0); } - test "InvalidDll" { if (builtin.os != builtin.Os.windows) return; const DllName = "asdf.dll"; const allocator = std.debug.global_allocator; - const handle = os.windowsLoadDll(allocator, DllName) catch |err| { + const handle = os.windowsLoadDll(allocator, DllName) catch |err| { assert(err == error.DllNotFound); return; }; } - diff --git a/std/os/zen.zig b/std/os/zen.zig index 51528ca391..7517cc0d69 100644 --- a/std/os/zen.zig +++ b/std/os/zen.zig @@ -3,35 +3,35 @@ ////////////////////////// pub const Message = struct { - sender: MailboxId, + sender: MailboxId, receiver: MailboxId, - type: usize, - payload: usize, + type: usize, + payload: usize, pub fn from(mailbox_id: &const MailboxId) Message { - return Message { - .sender = MailboxId.Undefined, + return Message{ + .sender = MailboxId.Undefined, .receiver = *mailbox_id, - .type = 0, - .payload = 0, + .type = 0, + .payload = 0, }; } pub fn to(mailbox_id: &const MailboxId, msg_type: usize) Message { - return Message { - .sender = MailboxId.This, + return Message{ + .sender = MailboxId.This, .receiver = *mailbox_id, - .type = msg_type, - .payload = 0, + .type = msg_type, + .payload = 0, }; } pub fn withData(mailbox_id: &const MailboxId, msg_type: usize, payload: usize) Message { - return Message { - .sender = MailboxId.This, + return Message{ + .sender = MailboxId.This, .receiver = *mailbox_id, - .type = msg_type, - .payload = payload, + .type = msg_type, + .payload = payload, }; } }; @@ -40,27 +40,25 @@ pub const MailboxId = union(enum) { Undefined, This, Kernel, - Port: u16, + Port: u16, Thread: u16, }; - ////////////////////////////////////// //// Ports reserved for servers //// ////////////////////////////////////// pub const Server = struct { - pub const Keyboard = MailboxId { .Port = 0 }; - pub const Terminal = MailboxId { .Port = 1 }; + pub const Keyboard = MailboxId{ .Port = 0 }; + pub const Terminal = MailboxId{ .Port = 1 }; }; - //////////////////////// //// POSIX things //// //////////////////////// // Standard streams. -pub const STDIN_FILENO = 0; +pub const STDIN_FILENO = 0; pub const STDOUT_FILENO = 1; pub const STDERR_FILENO = 2; @@ -101,26 +99,24 @@ pub fn write(fd: i32, buf: &const u8, count: usize) usize { return count; } - /////////////////////////// //// Syscall numbers //// /////////////////////////// pub const Syscall = enum(usize) { - exit = 0, - createPort = 1, - send = 2, - receive = 3, - subscribeIRQ = 4, - inb = 5, - map = 6, - createThread = 7, + exit = 0, + createPort = 1, + send = 2, + receive = 3, + subscribeIRQ = 4, + inb = 5, + map = 6, + createThread = 7, createProcess = 8, - wait = 9, - portReady = 10, + wait = 9, + portReady = 10, }; - //////////////////// //// Syscalls //// //////////////////// @@ -157,7 +153,7 @@ pub fn map(v_addr: usize, p_addr: usize, size: usize, writable: bool) bool { return syscall4(Syscall.map, v_addr, p_addr, size, usize(writable)) != 0; } -pub fn createThread(function: fn()void) u16 { +pub fn createThread(function: fn() void) u16 { return u16(syscall1(Syscall.createThread, @ptrToInt(function))); } @@ -180,66 +176,84 @@ pub fn portReady(port: u16) bool { inline fn syscall0(number: Syscall) usize { return asm volatile ("int $0x80" : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number)); + : [number] "{eax}" (number) + ); } inline fn syscall1(number: Syscall, arg1: usize) usize { return asm volatile ("int $0x80" : [ret] "={eax}" (-> usize) : [number] "{eax}" (number), - [arg1] "{ecx}" (arg1)); + [arg1] "{ecx}" (arg1) + ); } inline fn syscall2(number: Syscall, arg1: usize, arg2: usize) usize { return asm volatile ("int $0x80" : [ret] "={eax}" (-> usize) : [number] "{eax}" (number), - [arg1] "{ecx}" (arg1), - [arg2] "{edx}" (arg2)); + [arg1] "{ecx}" (arg1), + [arg2] "{edx}" (arg2) + ); } inline fn syscall3(number: Syscall, arg1: usize, arg2: usize, arg3: usize) usize { return asm volatile ("int $0x80" : [ret] "={eax}" (-> usize) : [number] "{eax}" (number), - [arg1] "{ecx}" (arg1), - [arg2] "{edx}" (arg2), - [arg3] "{ebx}" (arg3)); + [arg1] "{ecx}" (arg1), + [arg2] "{edx}" (arg2), + [arg3] "{ebx}" (arg3) + ); } inline fn syscall4(number: Syscall, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { return asm volatile ("int $0x80" : [ret] "={eax}" (-> usize) : [number] "{eax}" (number), - [arg1] "{ecx}" (arg1), - [arg2] "{edx}" (arg2), - [arg3] "{ebx}" (arg3), - [arg4] "{esi}" (arg4)); + [arg1] "{ecx}" (arg1), + [arg2] "{edx}" (arg2), + [arg3] "{ebx}" (arg3), + [arg4] "{esi}" (arg4) + ); } -inline fn syscall5(number: Syscall, arg1: usize, arg2: usize, arg3: usize, - arg4: usize, arg5: usize) usize -{ +inline fn syscall5( + number: Syscall, + arg1: usize, + arg2: usize, + arg3: usize, + arg4: usize, + arg5: usize, +) usize { return asm volatile ("int $0x80" : [ret] "={eax}" (-> usize) : [number] "{eax}" (number), - [arg1] "{ecx}" (arg1), - [arg2] "{edx}" (arg2), - [arg3] "{ebx}" (arg3), - [arg4] "{esi}" (arg4), - [arg5] "{edi}" (arg5)); + [arg1] "{ecx}" (arg1), + [arg2] "{edx}" (arg2), + [arg3] "{ebx}" (arg3), + [arg4] "{esi}" (arg4), + [arg5] "{edi}" (arg5) + ); } -inline fn syscall6(number: Syscall, arg1: usize, arg2: usize, arg3: usize, - arg4: usize, arg5: usize, arg6: usize) usize -{ +inline fn syscall6( + number: Syscall, + arg1: usize, + arg2: usize, + arg3: usize, + arg4: usize, + arg5: usize, + arg6: usize, +) usize { return asm volatile ("int $0x80" : [ret] "={eax}" (-> usize) : [number] "{eax}" (number), - [arg1] "{ecx}" (arg1), - [arg2] "{edx}" (arg2), - [arg3] "{ebx}" (arg3), - [arg4] "{esi}" (arg4), - [arg5] "{edi}" (arg5), - [arg6] "{ebp}" (arg6)); + [arg1] "{ecx}" (arg1), + [arg2] "{edx}" (arg2), + [arg3] "{ebx}" (arg3), + [arg4] "{esi}" (arg4), + [arg5] "{edi}" (arg5), + [arg6] "{ebp}" (arg6) + ); } diff --git a/std/rand/index.zig b/std/rand/index.zig index bd6209009e..68e6d3cb4d 100644 --- a/std/rand/index.zig +++ b/std/rand/index.zig @@ -69,7 +69,7 @@ pub const Random = struct { break :x start; } else x: { // Can't overflow because the range is over signed ints - break :x math.negateCast(value - end_uint) catch unreachable; + break :x math.negateCast(value - end_uint) catch unreachable; }; return result; } else { @@ -156,7 +156,7 @@ const SplitMix64 = struct { s: u64, pub fn init(seed: u64) SplitMix64 { - return SplitMix64 { .s = seed }; + return SplitMix64{ .s = seed }; } pub fn next(self: &SplitMix64) u64 { @@ -172,7 +172,7 @@ const SplitMix64 = struct { test "splitmix64 sequence" { var r = SplitMix64.init(0xaeecf86f7878dd75); - const seq = []const u64 { + const seq = []const u64{ 0x5dbd39db0178eb44, 0xa9900fb66b397da3, 0x5c1a28b1aeebcf5c, @@ -198,8 +198,8 @@ pub const Pcg = struct { i: u64, pub fn init(init_s: u64) Pcg { - var pcg = Pcg { - .random = Random { .fillFn = fill }, + var pcg = Pcg{ + .random = Random{ .fillFn = fill }, .s = undefined, .i = undefined, }; @@ -265,7 +265,7 @@ test "pcg sequence" { const s1: u64 = 0x84e9c579ef59bbf7; r.seedTwo(s0, s1); - const seq = []const u32 { + const seq = []const u32{ 2881561918, 3063928540, 1199791034, @@ -288,8 +288,8 @@ pub const Xoroshiro128 = struct { s: [2]u64, pub fn init(init_s: u64) Xoroshiro128 { - var x = Xoroshiro128 { - .random = Random { .fillFn = fill }, + var x = Xoroshiro128{ + .random = Random{ .fillFn = fill }, .s = undefined, }; @@ -314,9 +314,9 @@ pub const Xoroshiro128 = struct { var s0: u64 = 0; var s1: u64 = 0; - const table = []const u64 { + const table = []const u64{ 0xbeac0467eba5facb, - 0xd86b048b86aa9922 + 0xd86b048b86aa9922, }; inline for (table) |entry| { @@ -374,7 +374,7 @@ test "xoroshiro sequence" { r.s[0] = 0xaeecf86f7878dd75; r.s[1] = 0x01cd153642e72622; - const seq1 = []const u64 { + const seq1 = []const u64{ 0xb0ba0da5bb600397, 0x18a08afde614dccc, 0xa2635b956a31b929, @@ -387,10 +387,9 @@ test "xoroshiro sequence" { std.debug.assert(s == r.next()); } - r.jump(); - const seq2 = []const u64 { + const seq2 = []const u64{ 0x95344a13556d3e22, 0xb4fb32dafa4d00df, 0xb2011d9ccdcfe2dd, @@ -421,8 +420,8 @@ pub const Isaac64 = struct { i: usize, pub fn init(init_s: u64) Isaac64 { - var isaac = Isaac64 { - .random = Random { .fillFn = fill }, + var isaac = Isaac64{ + .random = Random{ .fillFn = fill }, .r = undefined, .m = undefined, .a = undefined, @@ -456,20 +455,20 @@ pub const Isaac64 = struct { { var i: usize = 0; while (i < midpoint) : (i += 4) { - self.step( ~(self.a ^ (self.a << 21)), i + 0, 0, midpoint); - self.step( self.a ^ (self.a >> 5) , i + 1, 0, midpoint); - self.step( self.a ^ (self.a << 12) , i + 2, 0, midpoint); - self.step( self.a ^ (self.a >> 33) , i + 3, 0, midpoint); + self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint); + self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint); + self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint); + self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint); } } { var i: usize = 0; while (i < midpoint) : (i += 4) { - self.step( ~(self.a ^ (self.a << 21)), i + 0, midpoint, 0); - self.step( self.a ^ (self.a >> 5) , i + 1, midpoint, 0); - self.step( self.a ^ (self.a << 12) , i + 2, midpoint, 0); - self.step( self.a ^ (self.a >> 33) , i + 3, midpoint, 0); + self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0); + self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0); + self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0); + self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0); } } @@ -493,7 +492,7 @@ pub const Isaac64 = struct { self.m[0] = init_s; // prescrambled golden ratio constants - var a = []const u64 { + var a = []const u64{ 0x647c4677a2884b7c, 0xb9f8b322c73ac862, 0x8c0ea5053d4712a0, @@ -513,14 +512,30 @@ pub const Isaac64 = struct { a[x1] +%= self.m[j + x1]; } - a[0] -%= a[4]; a[5] ^= a[7] >> 9; a[7] +%= a[0]; - a[1] -%= a[5]; a[6] ^= a[0] << 9; a[0] +%= a[1]; - a[2] -%= a[6]; a[7] ^= a[1] >> 23; a[1] +%= a[2]; - a[3] -%= a[7]; a[0] ^= a[2] << 15; a[2] +%= a[3]; - a[4] -%= a[0]; a[1] ^= a[3] >> 14; a[3] +%= a[4]; - a[5] -%= a[1]; a[2] ^= a[4] << 20; a[4] +%= a[5]; - a[6] -%= a[2]; a[3] ^= a[5] >> 17; a[5] +%= a[6]; - a[7] -%= a[3]; a[4] ^= a[6] << 14; a[6] +%= a[7]; + a[0] -%= a[4]; + a[5] ^= a[7] >> 9; + a[7] +%= a[0]; + a[1] -%= a[5]; + a[6] ^= a[0] << 9; + a[0] +%= a[1]; + a[2] -%= a[6]; + a[7] ^= a[1] >> 23; + a[1] +%= a[2]; + a[3] -%= a[7]; + a[0] ^= a[2] << 15; + a[2] +%= a[3]; + a[4] -%= a[0]; + a[1] ^= a[3] >> 14; + a[3] +%= a[4]; + a[5] -%= a[1]; + a[2] ^= a[4] << 20; + a[4] +%= a[5]; + a[6] -%= a[2]; + a[3] ^= a[5] >> 17; + a[5] +%= a[6]; + a[7] -%= a[3]; + a[4] ^= a[6] << 14; + a[6] +%= a[7]; comptime var x2: usize = 0; inline while (x2 < 8) : (x2 += 1) { @@ -533,7 +548,7 @@ pub const Isaac64 = struct { self.a = 0; self.b = 0; self.c = 0; - self.i = self.r.len; // trigger refill on first value + self.i = self.r.len; // trigger refill on first value } fn fill(r: &Random, buf: []u8) void { @@ -567,7 +582,7 @@ test "isaac64 sequence" { var r = Isaac64.init(0); // from reference implementation - const seq = []const u64 { + const seq = []const u64{ 0xf67dfba498e4937c, 0x84a5066a9204f380, 0xfee34bd5f5514dbb, @@ -609,7 +624,7 @@ test "Random float" { test "Random scalar" { var prng = DefaultPrng.init(0); - const s = prng .random.scalar(u64); + const s = prng.random.scalar(u64); } test "Random bytes" { @@ -621,8 +636,8 @@ test "Random bytes" { test "Random shuffle" { var prng = DefaultPrng.init(0); - var seq = []const u8 { 0, 1, 2, 3, 4 }; - var seen = []bool {false} ** 5; + var seq = []const u8{ 0, 1, 2, 3, 4 }; + var seen = []bool{false} ** 5; var i: usize = 0; while (i < 1000) : (i += 1) { @@ -639,7 +654,8 @@ test "Random shuffle" { fn sumArray(s: []const u8) u32 { var r: u32 = 0; - for (s) |e| r += e; + for (s) |e| + r += e; return r; } diff --git a/std/rand/ziggurat.zig b/std/rand/ziggurat.zig index 7790b71d26..404687ad0c 100644 --- a/std/rand/ziggurat.zig +++ b/std/rand/ziggurat.zig @@ -64,8 +64,14 @@ pub const ZigTable = struct { }; // zigNorInit -fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64, - comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable { +fn ZigTableGen( + comptime is_symmetric: bool, + comptime r: f64, + comptime v: f64, + comptime f: fn(f64) f64, + comptime f_inv: fn(f64) f64, + comptime zero_case: fn(&Random, f64) f64, +) ZigTable { var tables: ZigTable = undefined; tables.is_symmetric = is_symmetric; @@ -98,8 +104,12 @@ pub const NormDist = blk: { const norm_r = 3.6541528853610088; const norm_v = 0.00492867323399; -fn norm_f(x: f64) f64 { return math.exp(-x * x / 2.0); } -fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); } +fn norm_f(x: f64) f64 { + return math.exp(-x * x / 2.0); +} +fn norm_f_inv(y: f64) f64 { + return math.sqrt(-2.0 * math.ln(y)); +} fn norm_zero_case(random: &Random, u: f64) f64 { var x: f64 = 1; var y: f64 = 0; @@ -133,9 +143,15 @@ pub const ExpDist = blk: { const exp_r = 7.69711747013104972; const exp_v = 0.0039496598225815571993; -fn exp_f(x: f64) f64 { return math.exp(-x); } -fn exp_f_inv(y: f64) f64 { return -math.ln(y); } -fn exp_zero_case(random: &Random, _: f64) f64 { return exp_r - math.ln(random.float(f64)); } +fn exp_f(x: f64) f64 { + return math.exp(-x); +} +fn exp_f_inv(y: f64) f64 { + return -math.ln(y); +} +fn exp_zero_case(random: &Random, _: f64) f64 { + return exp_r - math.ln(random.float(f64)); +} test "ziggurant exp dist sanity" { var prng = std.rand.DefaultPrng.init(0); diff --git a/std/segmented_list.zig b/std/segmented_list.zig index 098378de4e..d755135fe8 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -5,7 +5,7 @@ const Allocator = std.mem.Allocator; // Imagine that `fn at(self: &Self, index: usize) &T` is a customer asking for a box // from a warehouse, based on a flat array, boxes ordered from 0 to N - 1. // But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes. -// So when the customer requests a box index, we have to translate it to shelf index +// So when the customer requests a box index, we have to translate it to shelf index // and box index within that shelf. Illustration: // // customer indexes: @@ -37,14 +37,14 @@ const Allocator = std.mem.Allocator; // Now we complicate it a little bit further by adding a preallocated shelf, which must be // a power of 2: // prealloc=4 -// +// // customer indexes: // prealloc: 0 1 2 3 // shelf 0: 4 5 6 7 8 9 10 11 // shelf 1: 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 // shelf 2: 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 // ... -// +// // warehouse indexes: // prealloc: 0 1 2 3 // shelf 0: 0 1 2 3 4 5 6 7 diff --git a/std/sort.zig b/std/sort.zig index 4cc7ad503a..5596c9063d 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -317,7 +317,6 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons // 6. merge each A block with any B values that follow, using the cache or the second internal buffer // 7. sort the second internal buffer if it exists // 8. redistribute the two internal buffers back into the items - var block_size: usize = math.sqrt(iterator.length()); var buffer_size = iterator.length() / block_size + 1; diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 056ab35003..c10f4aa806 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -27,10 +27,14 @@ extern fn zen_start() noreturn { nakedcc fn _start() noreturn { switch (builtin.arch) { builtin.Arch.x86_64 => { - argc_ptr = asm ("lea (%%rsp), %[argc]" : [argc] "=r" (-> &usize)); + argc_ptr = asm ("lea (%%rsp), %[argc]" + : [argc] "=r" (-> &usize) + ); }, builtin.Arch.i386 => { - argc_ptr = asm ("lea (%%esp), %[argc]" : [argc] "=r" (-> &usize)); + argc_ptr = asm ("lea (%%esp), %[argc]" + : [argc] "=r" (-> &usize) + ); }, else => @compileError("unsupported arch"), } diff --git a/std/special/bootstrap_lib.zig b/std/special/bootstrap_lib.zig index f55aaed96a..f029495cb0 100644 --- a/std/special/bootstrap_lib.zig +++ b/std/special/bootstrap_lib.zig @@ -7,8 +7,10 @@ comptime { @export("_DllMainCRTStartup", _DllMainCRTStartup, builtin.GlobalLinkage.Strong); } -stdcallcc fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD, - lpReserved: std.os.windows.LPVOID) std.os.windows.BOOL -{ +stdcallcc fn _DllMainCRTStartup( + hinstDLL: std.os.windows.HINSTANCE, + fdwReason: std.os.windows.DWORD, + lpReserved: std.os.windows.LPVOID, +) std.os.windows.BOOL { return std.os.windows.TRUE; } diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index e1a35f6648..cf8f19d49e 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -24,7 +24,6 @@ pub fn main() !void { const allocator = &arena.allocator; - // skip my own exe name _ = arg_it.skip(); @@ -175,8 +174,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: var) !void { try out_stream.print(" (none)\n"); } else { for (builder.available_options_list.toSliceConst()) |option| { - const name = try fmt.allocPrint(allocator, - " -D{}=[{}]", option.name, Builder.typeIdName(option.type_id)); + const name = try fmt.allocPrint(allocator, " -D{}=[{}]", option.name, Builder.typeIdName(option.type_id)); defer allocator.free(name); try out_stream.print("{s24} {}\n", name, option.description); } @@ -202,7 +200,7 @@ fn usageAndErr(builder: &Builder, already_ran_build: bool, out_stream: var) erro return error.InvalidArgs; } -const UnwrapArgError = error {OutOfMemory}; +const UnwrapArgError = error{OutOfMemory}; fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 { return arg catch |err| { diff --git a/std/special/builtin.zig b/std/special/builtin.zig index a5126bc4f3..63149d5161 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -56,7 +56,8 @@ export fn memmove(dest: ?&u8, src: ?&const u8, n: usize) ?&u8 { comptime { if (builtin.mode != builtin.Mode.ReleaseFast and builtin.mode != builtin.Mode.ReleaseSmall and - builtin.os != builtin.Os.windows) { + builtin.os != builtin.Os.windows) + { @export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong); } if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) { @@ -101,15 +102,27 @@ nakedcc fn clone() void { const math = @import("../math/index.zig"); -export fn fmodf(x: f32, y: f32) f32 { return generic_fmod(f32, x, y); } -export fn fmod(x: f64, y: f64) f64 { return generic_fmod(f64, x, y); } +export fn fmodf(x: f32, y: f32) f32 { + return generic_fmod(f32, x, y); +} +export fn fmod(x: f64, y: f64) f64 { + return generic_fmod(f64, x, y); +} // TODO add intrinsics for these (and probably the double version too) // and have the math stuff use the intrinsic. same as @mod and @rem -export fn floorf(x: f32) f32 { return math.floor(x); } -export fn ceilf(x: f32) f32 { return math.ceil(x); } -export fn floor(x: f64) f64 { return math.floor(x); } -export fn ceil(x: f64) f64 { return math.ceil(x); } +export fn floorf(x: f32) f32 { + return math.floor(x); +} +export fn ceilf(x: f32) f32 { + return math.ceil(x); +} +export fn floor(x: f64) f64 { + return math.floor(x); +} +export fn ceil(x: f64) f64 { + return math.ceil(x); +} fn generic_fmod(comptime T: type, x: T, y: T) T { @setRuntimeSafety(false); @@ -139,7 +152,10 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { // normalize x and y if (ex == 0) { i = ux << exp_bits; - while (i >> bits_minus_1 == 0) : (b: {ex -= 1; break :b i <<= 1;}) {} + while (i >> bits_minus_1 == 0) : (b: { + ex -= 1; + i <<= 1; + }) {} ux <<= log2uint(@bitCast(u32, -ex + 1)); } else { ux &= @maxValue(uint) >> exp_bits; @@ -147,7 +163,10 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { } if (ey == 0) { i = uy << exp_bits; - while (i >> bits_minus_1 == 0) : (b: {ey -= 1; break :b i <<= 1;}) {} + while (i >> bits_minus_1 == 0) : (b: { + ey -= 1; + i <<= 1; + }) {} uy <<= log2uint(@bitCast(u32, -ey + 1)); } else { uy &= @maxValue(uint) >> exp_bits; @@ -170,7 +189,10 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { return 0 * x; ux = i; } - while (ux >> digits == 0) : (b: {ux <<= 1; break :b ex -= 1;}) {} + while (ux >> digits == 0) : (b: { + ux <<= 1; + ex -= 1; + }) {} // scale result up if (ex > 0) { @@ -298,7 +320,7 @@ export fn sqrt(x: f64) f64 { // rounding direction if (ix0 | ix1 != 0) { - var z = 1.0 - tiny; // raise inexact + var z = 1.0 - tiny; // raise inexact if (z >= 1.0) { z = 1.0 + tiny; if (q1 == 0xFFFFFFFF) { @@ -336,13 +358,13 @@ export fn sqrtf(x: f32) f32 { var ix: i32 = @bitCast(i32, x); if ((ix & 0x7F800000) == 0x7F800000) { - return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan + return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan } // zero if (ix <= 0) { if (ix & ~sign == 0) { - return x; // sqrt (+-0) = +-0 + return x; // sqrt (+-0) = +-0 } if (ix < 0) { return math.snan(f32); @@ -360,20 +382,20 @@ export fn sqrtf(x: f32) f32 { m -= i - 1; } - m -= 127; // unbias exponent + m -= 127; // unbias exponent ix = (ix & 0x007FFFFF) | 0x00800000; - if (m & 1 != 0) { // odd m, double x to even + if (m & 1 != 0) { // odd m, double x to even ix += ix; } - m >>= 1; // m = [m / 2] + m >>= 1; // m = [m / 2] // sqrt(x) bit by bit ix += ix; - var q: i32 = 0; // q = sqrt(x) + var q: i32 = 0; // q = sqrt(x) var s: i32 = 0; - var r: i32 = 0x01000000; // r = moving bit right -> left + var r: i32 = 0x01000000; // r = moving bit right -> left while (r != 0) { const t = s + r; @@ -388,7 +410,7 @@ export fn sqrtf(x: f32) f32 { // floating add to find rounding direction if (ix != 0) { - var z = 1.0 - tiny; // inexact + var z = 1.0 - tiny; // inexact if (z >= 1.0) { z = 1.0 + tiny; if (z > 1.0) { diff --git a/std/special/compiler_rt/comparetf2.zig b/std/special/compiler_rt/comparetf2.zig index 760c3689c0..d63b7a7c92 100644 --- a/std/special/compiler_rt/comparetf2.zig +++ b/std/special/compiler_rt/comparetf2.zig @@ -38,25 +38,22 @@ pub extern fn __letf2(a: f128, b: f128) c_int { // If at least one of a and b is positive, we get the same result comparing // a and b as signed integers as we would with a floating-point compare. - return if ((aInt & bInt) >= 0) - if (aInt < bInt) - LE_LESS - else if (aInt == bInt) - LE_EQUAL - else - LE_GREATER + return if ((aInt & bInt) >= 0) if (aInt < bInt) + LE_LESS + else if (aInt == bInt) + LE_EQUAL else - // Otherwise, both are negative, so we need to flip the sense of the - // comparison to get the correct result. (This assumes a twos- or ones- - // complement integer representation; if integers are represented in a - // sign-magnitude representation, then this flip is incorrect). - if (aInt > bInt) - LE_LESS - else if (aInt == bInt) - LE_EQUAL - else - LE_GREATER - ; + LE_GREATER else + // Otherwise, both are negative, so we need to flip the sense of the + // comparison to get the correct result. (This assumes a twos- or ones- + // complement integer representation; if integers are represented in a + // sign-magnitude representation, then this flip is incorrect). + if (aInt > bInt) + LE_LESS + else if (aInt == bInt) + LE_EQUAL + else + LE_GREATER; } // TODO https://github.com/ziglang/zig/issues/305 @@ -76,21 +73,17 @@ pub extern fn __getf2(a: f128, b: f128) c_int { if (aAbs > infRep or bAbs > infRep) return GE_UNORDERED; if ((aAbs | bAbs) == 0) return GE_EQUAL; - return if ((aInt & bInt) >= 0) - if (aInt < bInt) - GE_LESS - else if (aInt == bInt) - GE_EQUAL - else - GE_GREATER + return if ((aInt & bInt) >= 0) if (aInt < bInt) + GE_LESS + else if (aInt == bInt) + GE_EQUAL + else + GE_GREATER else if (aInt > bInt) + GE_LESS + else if (aInt == bInt) + GE_EQUAL else - if (aInt > bInt) - GE_LESS - else if (aInt == bInt) - GE_EQUAL - else - GE_GREATER - ; + GE_GREATER; } pub extern fn __unordtf2(a: f128, b: f128) c_int { diff --git a/std/special/compiler_rt/fixunsdfti_test.zig b/std/special/compiler_rt/fixunsdfti_test.zig index 7283b35c0e..7f7b083d19 100644 --- a/std/special/compiler_rt/fixunsdfti_test.zig +++ b/std/special/compiler_rt/fixunsdfti_test.zig @@ -44,4 +44,3 @@ test "fixunsdfti" { test__fixunsdfti(-0x1.FFFFFFFFFFFFFp+62, 0); test__fixunsdfti(-0x1.FFFFFFFFFFFFEp+62, 0); } - diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index b051ccfc9d..3e014d4d16 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -92,10 +92,10 @@ pub fn setXmm0(comptime T: type, value: T) void { const aligned_value: T align(16) = value; asm volatile ( \\movaps (%[ptr]), %%xmm0 - - : + : : [ptr] "r" (&aligned_value) - : "xmm0"); + : "xmm0" + ); } extern fn __udivdi3(a: u64, b: u64) u64 { @@ -159,7 +159,8 @@ fn isArmArch() bool { builtin.Arch.armebv6t2, builtin.Arch.armebv5, builtin.Arch.armebv5te, - builtin.Arch.armebv4t => true, + builtin.Arch.armebv4t, + => true, else => false, }; } @@ -174,7 +175,10 @@ nakedcc fn __aeabi_uidivmod() void { \\ ldr r1, [sp] \\ add sp, sp, #4 \\ pop { pc } - ::: "r2", "r1"); + : + : + : "r2", "r1" + ); } // _chkstk (_alloca) routine - probe stack between %esp and (%esp-%eax) in 4k increments, diff --git a/std/unicode.zig b/std/unicode.zig index 300e129647..8bcc2705dd 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -58,6 +58,7 @@ pub fn utf8Encode(c: u32, out: []u8) !u3 { } const Utf8DecodeError = Utf8Decode2Error || Utf8Decode3Error || Utf8Decode4Error; + /// Decodes the UTF-8 codepoint encoded in the given slice of bytes. /// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable. /// If you already know the length at comptime, you can call one of @@ -150,7 +151,9 @@ pub fn utf8ValidateSlice(s: []const u8) bool { return false; } - if (utf8Decode(s[i..i+cp_len])) |_| {} else |_| { return false; } + if (utf8Decode(s[i..i + cp_len])) |_| {} else |_| { + return false; + } i += cp_len; } else |err| { return false; @@ -179,9 +182,7 @@ pub const Utf8View = struct { } pub fn initUnchecked(s: []const u8) Utf8View { - return Utf8View { - .bytes = s, - }; + return Utf8View{ .bytes = s }; } pub fn initComptime(comptime s: []const u8) Utf8View { @@ -191,12 +192,12 @@ pub const Utf8View = struct { error.InvalidUtf8 => { @compileError("invalid utf8"); unreachable; - } + }, } } pub fn iterator(s: &const Utf8View) Utf8Iterator { - return Utf8Iterator { + return Utf8Iterator{ .bytes = s.bytes, .i = 0, }; @@ -215,7 +216,7 @@ const Utf8Iterator = struct { const cp_len = utf8ByteSequenceLength(it.bytes[it.i]) catch unreachable; it.i += cp_len; - return it.bytes[it.i-cp_len..it.i]; + return it.bytes[it.i - cp_len..it.i]; } pub fn nextCodepoint(it: &Utf8Iterator) ?u32 { @@ -304,9 +305,12 @@ test "utf8 view bad" { fn testUtf8ViewBad() void { // Compile-time error. // const s3 = Utf8View.initComptime("\xfe\xf2"); - const s = Utf8View.init("hel\xadlo"); - if (s) |_| { unreachable; } else |err| { debug.assert(err == error.InvalidUtf8); } + if (s) |_| { + unreachable; + } else |err| { + debug.assert(err == error.InvalidUtf8); + } } test "utf8 view ok" { diff --git a/std/zig/ast.zig b/std/zig/ast.zig index e86c40e310..3e1b4fe16a 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -388,7 +388,8 @@ pub const Node = struct { Id.SwitchElse, Id.FieldInitializer, Id.DocComment, - Id.TestDecl => return false, + Id.TestDecl, + => return false, Id.While => { const while_node = @fieldParentPtr(While, "base", n); if (while_node.@"else") |@"else"| { @@ -608,8 +609,7 @@ pub const Node = struct { if (i < 1) return t; i -= 1; }, - InitArg.None, - InitArg.Enum => {}, + InitArg.None, InitArg.Enum => {}, } if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i).*; @@ -1475,7 +1475,8 @@ pub const Node = struct { Op.Range, Op.Sub, Op.SubWrap, - Op.UnwrapMaybe => {}, + Op.UnwrapMaybe, + => {}, } if (i < 1) return self.rhs; diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 1bc64c3ddb..05554f5d34 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -81,10 +81,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try root_node.decls.push(&test_node.base); try stack.append(State{ .Block = block }); - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.LBrace, - .ptr = &block.lbrace, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.LBrace, + .ptr = &block.lbrace, + }, + }); try stack.append(State{ .StringLiteral = OptionalCtx{ .Required = &test_node.name } }); continue; }, @@ -95,13 +97,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, Token.Id.Keyword_pub => { stack.append(State.TopLevel) catch unreachable; - try stack.append(State{ .TopLevelExtern = TopLevelDeclCtx{ - .decls = &root_node.decls, - .visib_token = token_index, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } }); + try stack.append(State{ + .TopLevelExtern = TopLevelDeclCtx{ + .decls = &root_node.decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + }, + }); continue; }, Token.Id.Keyword_comptime => { @@ -122,22 +126,26 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { stack.append(State.TopLevel) catch unreachable; try stack.append(State{ .Block = block }); - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.LBrace, - .ptr = &block.lbrace, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.LBrace, + .ptr = &block.lbrace, + }, + }); continue; }, else => { prevToken(&tok_it, &tree); stack.append(State.TopLevel) catch unreachable; - try stack.append(State{ .TopLevelExtern = TopLevelDeclCtx{ - .decls = &root_node.decls, - .visib_token = null, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } }); + try stack.append(State{ + .TopLevelExtern = TopLevelDeclCtx{ + .decls = &root_node.decls, + .visib_token = null, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + }, + }); continue; }, } @@ -147,31 +155,34 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; switch (token_ptr.id) { - Token.Id.Keyword_export, - Token.Id.Keyword_inline => { - stack.append(State{ .TopLevelDecl = TopLevelDeclCtx{ - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = AnnotatedToken{ - .index = token_index, - .ptr = token_ptr, + Token.Id.Keyword_export, Token.Id.Keyword_inline => { + stack.append(State{ + .TopLevelDecl = TopLevelDeclCtx{ + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = AnnotatedToken{ + .index = token_index, + .ptr = token_ptr, + }, + .lib_name = null, + .comments = ctx.comments, }, - .lib_name = null, - .comments = ctx.comments, - } }) catch unreachable; + }) catch unreachable; continue; }, Token.Id.Keyword_extern => { - stack.append(State{ .TopLevelLibname = TopLevelDeclCtx{ - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = AnnotatedToken{ - .index = token_index, - .ptr = token_ptr, + stack.append(State{ + .TopLevelLibname = TopLevelDeclCtx{ + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = AnnotatedToken{ + .index = token_index, + .ptr = token_ptr, + }, + .lib_name = null, + .comments = ctx.comments, }, - .lib_name = null, - .comments = ctx.comments, - } }) catch unreachable; + }) catch unreachable; continue; }, else => { @@ -192,13 +203,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }; }; - stack.append(State{ .TopLevelDecl = TopLevelDeclCtx{ - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = ctx.extern_export_inline_token, - .lib_name = lib_name, - .comments = ctx.comments, - } }) catch unreachable; + stack.append(State{ + .TopLevelDecl = TopLevelDeclCtx{ + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = ctx.extern_export_inline_token, + .lib_name = lib_name, + .comments = ctx.comments, + }, + }) catch unreachable; continue; }, State.TopLevelDecl => |ctx| { @@ -222,15 +235,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try ctx.decls.push(&node.base); - stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.Semicolon, - .ptr = &node.semicolon_token, - } }) catch unreachable; + stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Semicolon, + .ptr = &node.semicolon_token, + }, + }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); continue; }, - Token.Id.Keyword_var, - Token.Id.Keyword_const => { + Token.Id.Keyword_var, Token.Id.Keyword_const => { if (ctx.extern_export_inline_token) |annotated_token| { if (annotated_token.ptr.id == Token.Id.Keyword_inline) { ((try tree.errors.addOne())).* = Error{ .InvalidToken = Error.InvalidToken{ .token = annotated_token.index } }; @@ -238,21 +252,20 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } } - try stack.append(State{ .VarDecl = VarDeclCtx{ - .comments = ctx.comments, - .visib_token = ctx.visib_token, - .lib_name = ctx.lib_name, - .comptime_token = null, - .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null, - .mut_token = token_index, - .list = ctx.decls, - } }); + try stack.append(State{ + .VarDecl = VarDeclCtx{ + .comments = ctx.comments, + .visib_token = ctx.visib_token, + .lib_name = ctx.lib_name, + .comptime_token = null, + .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null, + .mut_token = token_index, + .list = ctx.decls, + }, + }); continue; }, - Token.Id.Keyword_fn, - Token.Id.Keyword_nakedcc, - Token.Id.Keyword_stdcallcc, - Token.Id.Keyword_async => { + Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { const fn_proto = try arena.construct(ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = ctx.comments, @@ -274,13 +287,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.append(State{ .FnProto = fn_proto }); switch (token_ptr.id) { - Token.Id.Keyword_nakedcc, - Token.Id.Keyword_stdcallcc => { + Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { fn_proto.cc_token = token_index; - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + }, + }); continue; }, Token.Id.Keyword_async => { @@ -292,10 +306,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); fn_proto.async_attr = async_node; - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + }, + }); try stack.append(State{ .AsyncAllocator = async_node }); continue; }, @@ -331,13 +347,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } stack.append(State{ .ContainerDecl = ctx.container_decl }) catch unreachable; - try stack.append(State{ .TopLevelExtern = TopLevelDeclCtx{ - .decls = &ctx.container_decl.fields_and_decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = null, - .lib_name = null, - .comments = ctx.comments, - } }); + try stack.append(State{ + .TopLevelExtern = TopLevelDeclCtx{ + .decls = &ctx.container_decl.fields_and_decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = null, + .lib_name = null, + .comments = ctx.comments, + }, + }); continue; }, @@ -361,9 +379,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .base = ast.Node{ .id = ast.Node.Id.ContainerDecl }, .layout_token = ctx.layout_token, .kind_token = switch (token_ptr.id) { - Token.Id.Keyword_struct, - Token.Id.Keyword_union, - Token.Id.Keyword_enum => token_index, + Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => token_index, else => { ((try tree.errors.addOne())).* = Error{ .ExpectedAggregateKw = Error.ExpectedAggregateKw{ .token = token_index } }; return tree; @@ -377,10 +393,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { ctx.opt_ctx.store(&node.base); stack.append(State{ .ContainerDecl = node }) catch unreachable; - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.LBrace, - .ptr = &node.lbrace_token, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.LBrace, + .ptr = &node.lbrace_token, + }, + }); try stack.append(State{ .ContainerInitArgStart = node }); continue; }, @@ -481,35 +499,41 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Keyword_pub => { switch (tree.tokens.at(container_decl.kind_token).id) { Token.Id.Keyword_struct => { - try stack.append(State{ .TopLevelExternOrField = TopLevelExternOrFieldCtx{ - .visib_token = token_index, - .container_decl = container_decl, - .comments = comments, - } }); + try stack.append(State{ + .TopLevelExternOrField = TopLevelExternOrFieldCtx{ + .visib_token = token_index, + .container_decl = container_decl, + .comments = comments, + }, + }); continue; }, else => { stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State{ .TopLevelExtern = TopLevelDeclCtx{ - .decls = &container_decl.fields_and_decls, - .visib_token = token_index, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } }); + try stack.append(State{ + .TopLevelExtern = TopLevelDeclCtx{ + .decls = &container_decl.fields_and_decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + }, + }); continue; }, } }, Token.Id.Keyword_export => { stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State{ .TopLevelExtern = TopLevelDeclCtx{ - .decls = &container_decl.fields_and_decls, - .visib_token = token_index, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } }); + try stack.append(State{ + .TopLevelExtern = TopLevelDeclCtx{ + .decls = &container_decl.fields_and_decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + }, + }); continue; }, Token.Id.RBrace => { @@ -523,13 +547,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { else => { prevToken(&tok_it, &tree); stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State{ .TopLevelExtern = TopLevelDeclCtx{ - .decls = &container_decl.fields_and_decls, - .visib_token = null, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } }); + try stack.append(State{ + .TopLevelExtern = TopLevelDeclCtx{ + .decls = &container_decl.fields_and_decls, + .visib_token = null, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + }, + }); continue; }, } @@ -557,10 +583,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.append(State{ .VarDeclAlign = var_decl }); try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &var_decl.type_node } }); try stack.append(State{ .IfToken = Token.Id.Colon }); - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.Identifier, - .ptr = &var_decl.name_token, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Identifier, + .ptr = &var_decl.name_token, + }, + }); continue; }, State.VarDeclAlign => |var_decl| { @@ -605,10 +633,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const semicolon_token = nextToken(&tok_it, &tree); if (semicolon_token.ptr.id != Token.Id.Semicolon) { - ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ - .token = semicolon_token.index, - .expected_id = Token.Id.Semicolon, - } }; + ((try tree.errors.addOne())).* = Error{ + .ExpectedToken = Error.ExpectedToken{ + .token = semicolon_token.index, + .expected_id = Token.Id.Semicolon, + }, + }; return tree; } @@ -713,10 +743,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try fn_proto.params.push(¶m_decl.base); - stack.append(State{ .ParamDeclEnd = ParamDeclEndCtx{ - .param_decl = param_decl, - .fn_proto = fn_proto, - } }) catch unreachable; + stack.append(State{ + .ParamDeclEnd = ParamDeclEndCtx{ + .param_decl = param_decl, + .fn_proto = fn_proto, + }, + }) catch unreachable; try stack.append(State{ .ParamDeclName = param_decl }); try stack.append(State{ .ParamDeclAliasOrComptime = param_decl }); continue; @@ -769,10 +801,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { State.MaybeLabeledExpression => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Colon)) |_| { - stack.append(State{ .LabeledExpression = LabelCtx{ - .label = ctx.label, - .opt_ctx = ctx.opt_ctx, - } }) catch unreachable; + stack.append(State{ + .LabeledExpression = LabelCtx{ + .label = ctx.label, + .opt_ctx = ctx.opt_ctx, + }, + }) catch unreachable; continue; } @@ -797,21 +831,25 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_while => { - stack.append(State{ .While = LoopCtx{ - .label = ctx.label, - .inline_token = null, - .loop_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } }) catch unreachable; + stack.append(State{ + .While = LoopCtx{ + .label = ctx.label, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + }, + }) catch unreachable; continue; }, Token.Id.Keyword_for => { - stack.append(State{ .For = LoopCtx{ - .label = ctx.label, - .inline_token = null, - .loop_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } }) catch unreachable; + stack.append(State{ + .For = LoopCtx{ + .label = ctx.label, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + }, + }) catch unreachable; continue; }, Token.Id.Keyword_suspend => { @@ -828,11 +866,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_inline => { - stack.append(State{ .Inline = InlineCtx{ - .label = ctx.label, - .inline_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } }) catch unreachable; + stack.append(State{ + .Inline = InlineCtx{ + .label = ctx.label, + .inline_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + }, + }) catch unreachable; continue; }, else => { @@ -852,21 +892,25 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_while => { - stack.append(State{ .While = LoopCtx{ - .inline_token = ctx.inline_token, - .label = ctx.label, - .loop_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } }) catch unreachable; + stack.append(State{ + .While = LoopCtx{ + .inline_token = ctx.inline_token, + .label = ctx.label, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + }, + }) catch unreachable; continue; }, Token.Id.Keyword_for => { - stack.append(State{ .For = LoopCtx{ - .inline_token = ctx.inline_token, - .label = ctx.label, - .loop_token = token_index, - .opt_ctx = ctx.opt_ctx.toRequired(), - } }) catch unreachable; + stack.append(State{ + .For = LoopCtx{ + .inline_token = ctx.inline_token, + .label = ctx.label, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + }, + }) catch unreachable; continue; }, else => { @@ -971,27 +1015,29 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_comptime => { - stack.append(State{ .ComptimeStatement = ComptimeStatementCtx{ - .comptime_token = token_index, - .block = block, - } }) catch unreachable; - continue; - }, - Token.Id.Keyword_var, - Token.Id.Keyword_const => { - stack.append(State{ .VarDecl = VarDeclCtx{ - .comments = null, - .visib_token = null, - .comptime_token = null, - .extern_export_token = null, - .lib_name = null, - .mut_token = token_index, - .list = &block.statements, - } }) catch unreachable; + stack.append(State{ + .ComptimeStatement = ComptimeStatementCtx{ + .comptime_token = token_index, + .block = block, + }, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_var, Token.Id.Keyword_const => { + stack.append(State{ + .VarDecl = VarDeclCtx{ + .comments = null, + .visib_token = null, + .comptime_token = null, + .extern_export_token = null, + .lib_name = null, + .mut_token = token_index, + .list = &block.statements, + }, + }) catch unreachable; continue; }, - Token.Id.Keyword_defer, - Token.Id.Keyword_errdefer => { + Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { const node = try arena.construct(ast.Node.Defer{ .base = ast.Node{ .id = ast.Node.Id.Defer }, .defer_token = token_index, @@ -1036,17 +1082,18 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; switch (token_ptr.id) { - Token.Id.Keyword_var, - Token.Id.Keyword_const => { - stack.append(State{ .VarDecl = VarDeclCtx{ - .comments = null, - .visib_token = null, - .comptime_token = ctx.comptime_token, - .extern_export_token = null, - .lib_name = null, - .mut_token = token_index, - .list = &ctx.block.statements, - } }) catch unreachable; + Token.Id.Keyword_var, Token.Id.Keyword_const => { + stack.append(State{ + .VarDecl = VarDeclCtx{ + .comments = null, + .visib_token = null, + .comptime_token = ctx.comptime_token, + .extern_export_token = null, + .lib_name = null, + .mut_token = token_index, + .list = &ctx.block.statements, + }, + }) catch unreachable; continue; }, else => { @@ -1089,10 +1136,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { stack.append(State{ .AsmOutputItems = items }) catch unreachable; try stack.append(State{ .IfToken = Token.Id.Comma }); - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.RParen, - .ptr = &node.rparen, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.RParen, + .ptr = &node.rparen, + }, + }); try stack.append(State{ .AsmOutputReturnOrType = node }); try stack.append(State{ .ExpectToken = Token.Id.LParen }); try stack.append(State{ .StringLiteral = OptionalCtx{ .Required = &node.constraint } }); @@ -1141,10 +1190,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { stack.append(State{ .AsmInputItems = items }) catch unreachable; try stack.append(State{ .IfToken = Token.Id.Comma }); - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.RParen, - .ptr = &node.rparen, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.RParen, + .ptr = &node.rparen, + }, + }); try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); try stack.append(State{ .ExpectToken = Token.Id.LParen }); try stack.append(State{ .StringLiteral = OptionalCtx{ .Required = &node.constraint } }); @@ -1203,14 +1254,18 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { stack.append(State{ .FieldInitListCommaOrEnd = list_state }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); try stack.append(State{ .ExpectToken = Token.Id.Equal }); - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.Identifier, - .ptr = &node.name_token, - } }); - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.Period, - .ptr = &node.period_token, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Identifier, + .ptr = &node.name_token, + }, + }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Period, + .ptr = &node.period_token, + }, + }); continue; }, State.FieldInitListCommaOrEnd => |list_state| { @@ -1320,10 +1375,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); try switch_case.items.push(&else_node.base); - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.EqualAngleBracketRight, - .ptr = &switch_case.arrow_token, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.EqualAngleBracketRight, + .ptr = &switch_case.arrow_token, + }, + }); continue; } else { prevToken(&tok_it, &tree); @@ -1374,10 +1431,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } async_node.rangle_bracket = TokenIndex(0); - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.AngleBracketRight, - .ptr = &??async_node.rangle_bracket, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.AngleBracketRight, + .ptr = &??async_node.rangle_bracket, + }, + }); try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &async_node.allocator_type } }); continue; }, @@ -1430,10 +1489,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; } - stack.append(State{ .ContainerKind = ContainerKindCtx{ - .opt_ctx = ctx.opt_ctx, - .layout_token = ctx.extern_token, - } }) catch unreachable; + stack.append(State{ + .ContainerKind = ContainerKindCtx{ + .opt_ctx = ctx.opt_ctx, + .layout_token = ctx.extern_token, + }, + }) catch unreachable; continue; }, State.SliceOrArrayAccess => |node| { @@ -1443,15 +1504,19 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { switch (token_ptr.id) { Token.Id.Ellipsis2 => { const start = node.op.ArrayAccess; - node.op = ast.Node.SuffixOp.Op{ .Slice = ast.Node.SuffixOp.Op.Slice{ - .start = start, - .end = null, - } }; + node.op = ast.Node.SuffixOp.Op{ + .Slice = ast.Node.SuffixOp.Op.Slice{ + .start = start, + .end = null, + }, + }; - stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.RBracket, - .ptr = &node.rtoken, - } }) catch unreachable; + stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.RBracket, + .ptr = &node.rtoken, + }, + }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Optional = &node.op.Slice.end } }); continue; }, @@ -1467,11 +1532,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }, State.SliceOrArrayType => |node| { if (eatToken(&tok_it, &tree, Token.Id.RBracket)) |_| { - node.op = ast.Node.PrefixOp.Op{ .SliceType = ast.Node.PrefixOp.AddrOfInfo{ - .align_info = null, - .const_token = null, - .volatile_token = null, - } }; + node.op = ast.Node.PrefixOp.Op{ + .SliceType = ast.Node.PrefixOp.AddrOfInfo{ + .align_info = null, + .const_token = null, + .volatile_token = null, + }, + }; stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; try stack.append(State{ .AddrOfModifiers = &node.op.SliceType }); continue; @@ -1495,7 +1562,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { ((try tree.errors.addOne())).* = Error{ .ExtraAlignQualifier = Error.ExtraAlignQualifier{ .token = token_index } }; return tree; } - addr_of_info.align_info = ast.Node.PrefixOp.AddrOfInfo.Align { + addr_of_info.align_info = ast.Node.PrefixOp.AddrOfInfo.Align{ .node = undefined, .bit_range = null, }; @@ -1548,9 +1615,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { Token.Id.RParen => continue, else => { (try tree.errors.addOne()).* = Error{ - .ExpectedColonOrRParen = Error.ExpectedColonOrRParen{ - .token = token.index, - } + .ExpectedColonOrRParen = Error.ExpectedColonOrRParen{ .token = token.index }, }; return tree; }, @@ -1563,10 +1628,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; if (token_ptr.id != Token.Id.Pipe) { if (opt_ctx != OptionalCtx.Optional) { - ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ - .token = token_index, - .expected_id = Token.Id.Pipe, - } }; + ((try tree.errors.addOne())).* = Error{ + .ExpectedToken = Error.ExpectedToken{ + .token = token_index, + .expected_id = Token.Id.Pipe, + }, + }; return tree; } @@ -1582,10 +1649,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); opt_ctx.store(&node.base); - stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } }) catch unreachable; + stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + }, + }) catch unreachable; try stack.append(State{ .Identifier = OptionalCtx{ .Required = &node.error_symbol } }); continue; }, @@ -1595,10 +1664,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; if (token_ptr.id != Token.Id.Pipe) { if (opt_ctx != OptionalCtx.Optional) { - ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ - .token = token_index, - .expected_id = Token.Id.Pipe, - } }; + ((try tree.errors.addOne())).* = Error{ + .ExpectedToken = Error.ExpectedToken{ + .token = token_index, + .expected_id = Token.Id.Pipe, + }, + }; return tree; } @@ -1615,15 +1686,19 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); opt_ctx.store(&node.base); - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + }, + }); try stack.append(State{ .Identifier = OptionalCtx{ .Required = &node.value_symbol } }); - try stack.append(State{ .OptionalTokenSave = OptionalTokenSave{ - .id = Token.Id.Asterisk, - .ptr = &node.ptr_token, - } }); + try stack.append(State{ + .OptionalTokenSave = OptionalTokenSave{ + .id = Token.Id.Asterisk, + .ptr = &node.ptr_token, + }, + }); continue; }, State.PointerIndexPayload => |opt_ctx| { @@ -1632,10 +1707,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; if (token_ptr.id != Token.Id.Pipe) { if (opt_ctx != OptionalCtx.Optional) { - ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ - .token = token_index, - .expected_id = Token.Id.Pipe, - } }; + ((try tree.errors.addOne())).* = Error{ + .ExpectedToken = Error.ExpectedToken{ + .token = token_index, + .expected_id = Token.Id.Pipe, + }, + }; return tree; } @@ -1653,17 +1730,21 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); opt_ctx.store(&node.base); - stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } }) catch unreachable; + stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + }, + }) catch unreachable; try stack.append(State{ .Identifier = OptionalCtx{ .RequiredNull = &node.index_symbol } }); try stack.append(State{ .IfToken = Token.Id.Comma }); try stack.append(State{ .Identifier = OptionalCtx{ .Required = &node.value_symbol } }); - try stack.append(State{ .OptionalTokenSave = OptionalTokenSave{ - .id = Token.Id.Asterisk, - .ptr = &node.ptr_token, - } }); + try stack.append(State{ + .OptionalTokenSave = OptionalTokenSave{ + .id = Token.Id.Asterisk, + .ptr = &node.ptr_token, + }, + }); continue; }, @@ -1672,9 +1753,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; switch (token_ptr.id) { - Token.Id.Keyword_return, - Token.Id.Keyword_break, - Token.Id.Keyword_continue => { + Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { const node = try arena.construct(ast.Node.ControlFlowExpression{ .base = ast.Node{ .id = ast.Node.Id.ControlFlowExpression }, .ltoken = token_index, @@ -1703,9 +1782,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } continue; }, - Token.Id.Keyword_try, - Token.Id.Keyword_cancel, - Token.Id.Keyword_resume => { + Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { const node = try arena.construct(ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, @@ -2078,10 +2155,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { stack.append(State{ .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .IfToken = Token.Id.LBrace }); - try stack.append(State{ .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)){ - .list = &node.op.StructInitializer, - .ptr = &node.rtoken, - } }); + try stack.append(State{ + .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)){ + .list = &node.op.StructInitializer, + .ptr = &node.rtoken, + }, + }); continue; } @@ -2094,11 +2173,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { opt_ctx.store(&node.base); stack.append(State{ .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .IfToken = Token.Id.LBrace }); - try stack.append(State{ .ExprListItemOrEnd = ExprListCtx{ - .list = &node.op.ArrayInitializer, - .end = Token.Id.RBrace, - .ptr = &node.rtoken, - } }); + try stack.append(State{ + .ExprListItemOrEnd = ExprListCtx{ + .list = &node.op.ArrayInitializer, + .end = Token.Id.RBrace, + .ptr = &node.rtoken, + }, + }); continue; }, @@ -2171,10 +2252,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { .allocator_type = null, .rangle_bracket = null, }); - stack.append(State{ .AsyncEnd = AsyncEndCtx{ - .ctx = opt_ctx, - .attribute = async_node, - } }) catch unreachable; + stack.append(State{ + .AsyncEnd = AsyncEndCtx{ + .ctx = opt_ctx, + .attribute = async_node, + }, + }) catch unreachable; try stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }); try stack.append(State{ .PrimaryExpression = opt_ctx.toRequired() }); try stack.append(State{ .AsyncAllocator = async_node }); @@ -2197,20 +2280,24 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const node = try arena.construct(ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, - .op = ast.Node.SuffixOp.Op{ .Call = ast.Node.SuffixOp.Op.Call{ - .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena), - .async_attr = null, - } }, + .op = ast.Node.SuffixOp.Op{ + .Call = ast.Node.SuffixOp.Op.Call{ + .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena), + .async_attr = null, + }, + }, .rtoken = undefined, }); opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State{ .ExprListItemOrEnd = ExprListCtx{ - .list = &node.op.Call.params, - .end = Token.Id.RParen, - .ptr = &node.rtoken, - } }); + try stack.append(State{ + .ExprListItemOrEnd = ExprListCtx{ + .list = &node.op.Call.params, + .end = Token.Id.RParen, + .ptr = &node.rtoken, + }, + }); continue; }, Token.Id.LBracket => { @@ -2278,8 +2365,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token.index); continue; }, - Token.Id.Keyword_true, - Token.Id.Keyword_false => { + Token.Id.Keyword_true, Token.Id.Keyword_false => { _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token.index); continue; }, @@ -2321,8 +2407,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.append(State{ .Expression = OptionalCtx{ .Required = return_type_ptr } }); continue; }, - Token.Id.StringLiteral, - Token.Id.MultilineStringLiteralLine => { + Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) ?? unreachable); continue; }, @@ -2335,10 +2420,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); opt_ctx.store(&node.base); - stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.RParen, - .ptr = &node.rparen, - } }) catch unreachable; + stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.RParen, + .ptr = &node.rparen, + }, + }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); continue; }, @@ -2351,11 +2438,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); opt_ctx.store(&node.base); - stack.append(State{ .ExprListItemOrEnd = ExprListCtx{ - .list = &node.params, - .end = Token.Id.RParen, - .ptr = &node.rparen_token, - } }) catch unreachable; + stack.append(State{ + .ExprListItemOrEnd = ExprListCtx{ + .list = &node.params, + .end = Token.Id.RParen, + .ptr = &node.rparen_token, + }, + }) catch unreachable; try stack.append(State{ .ExpectToken = Token.Id.LParen }); continue; }, @@ -2372,42 +2461,50 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_error => { - stack.append(State{ .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx{ - .error_token = token.index, - .opt_ctx = opt_ctx, - } }) catch unreachable; + stack.append(State{ + .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx{ + .error_token = token.index, + .opt_ctx = opt_ctx, + }, + }) catch unreachable; continue; }, Token.Id.Keyword_packed => { - stack.append(State{ .ContainerKind = ContainerKindCtx{ - .opt_ctx = opt_ctx, - .layout_token = token.index, - } }) catch unreachable; + stack.append(State{ + .ContainerKind = ContainerKindCtx{ + .opt_ctx = opt_ctx, + .layout_token = token.index, + }, + }) catch unreachable; continue; }, Token.Id.Keyword_extern => { - stack.append(State{ .ExternType = ExternTypeCtx{ - .opt_ctx = opt_ctx, - .extern_token = token.index, - .comments = null, - } }) catch unreachable; + stack.append(State{ + .ExternType = ExternTypeCtx{ + .opt_ctx = opt_ctx, + .extern_token = token.index, + .comments = null, + }, + }) catch unreachable; continue; }, - Token.Id.Keyword_struct, - Token.Id.Keyword_union, - Token.Id.Keyword_enum => { + Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => { prevToken(&tok_it, &tree); - stack.append(State{ .ContainerKind = ContainerKindCtx{ - .opt_ctx = opt_ctx, - .layout_token = null, - } }) catch unreachable; + stack.append(State{ + .ContainerKind = ContainerKindCtx{ + .opt_ctx = opt_ctx, + .layout_token = null, + }, + }) catch unreachable; continue; }, Token.Id.Identifier => { - stack.append(State{ .MaybeLabeledExpression = MaybeLabeledExpressionCtx{ - .label = token.index, - .opt_ctx = opt_ctx, - } }) catch unreachable; + stack.append(State{ + .MaybeLabeledExpression = MaybeLabeledExpressionCtx{ + .label = token.index, + .opt_ctx = opt_ctx, + }, + }) catch unreachable; continue; }, Token.Id.Keyword_fn => { @@ -2431,8 +2528,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { stack.append(State{ .FnProto = fn_proto }) catch unreachable; continue; }, - Token.Id.Keyword_nakedcc, - Token.Id.Keyword_stdcallcc => { + Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { const fn_proto = try arena.construct(ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = null, @@ -2451,10 +2547,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; - try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } }); + try stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + }, + }); continue; }, Token.Id.Keyword_asm => { @@ -2470,10 +2568,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); opt_ctx.store(&node.base); - stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ - .id = Token.Id.RParen, - .ptr = &node.rparen, - } }) catch unreachable; + stack.append(State{ + .ExpectTokenSave = ExpectTokenSave{ + .id = Token.Id.RParen, + .ptr = &node.rparen, + }, + }) catch unreachable; try stack.append(State{ .AsmClobberItems = &node.clobbers }); try stack.append(State{ .IfToken = Token.Id.Colon }); try stack.append(State{ .AsmInputItems = &node.inputs }); @@ -2482,17 +2582,21 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { try stack.append(State{ .IfToken = Token.Id.Colon }); try stack.append(State{ .StringLiteral = OptionalCtx{ .Required = &node.template } }); try stack.append(State{ .ExpectToken = Token.Id.LParen }); - try stack.append(State{ .OptionalTokenSave = OptionalTokenSave{ - .id = Token.Id.Keyword_volatile, - .ptr = &node.volatile_token, - } }); + try stack.append(State{ + .OptionalTokenSave = OptionalTokenSave{ + .id = Token.Id.Keyword_volatile, + .ptr = &node.volatile_token, + }, + }); }, Token.Id.Keyword_inline => { - stack.append(State{ .Inline = InlineCtx{ - .label = null, - .inline_token = token.index, - .opt_ctx = opt_ctx, - } }) catch unreachable; + stack.append(State{ + .Inline = InlineCtx{ + .label = null, + .inline_token = token.index, + .opt_ctx = opt_ctx, + }, + }) catch unreachable; continue; }, else => { @@ -2522,10 +2626,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { }); ctx.opt_ctx.store(&node.base); - stack.append(State{ .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)){ - .list = &node.decls, - .ptr = &node.rbrace_token, - } }) catch unreachable; + stack.append(State{ + .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)){ + .list = &node.decls, + .ptr = &node.rbrace_token, + }, + }) catch unreachable; continue; }, State.StringLiteral => |opt_ctx| { @@ -2553,10 +2659,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; - ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ - .token = token_index, - .expected_id = Token.Id.Identifier, - } }; + ((try tree.errors.addOne())).* = Error{ + .ExpectedToken = Error.ExpectedToken{ + .token = token_index, + .expected_id = Token.Id.Identifier, + }, + }; return tree; } }, @@ -2567,10 +2675,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const ident_token_index = ident_token.index; const ident_token_ptr = ident_token.ptr; if (ident_token_ptr.id != Token.Id.Identifier) { - ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ - .token = ident_token_index, - .expected_id = Token.Id.Identifier, - } }; + ((try tree.errors.addOne())).* = Error{ + .ExpectedToken = Error.ExpectedToken{ + .token = ident_token_index, + .expected_id = Token.Id.Identifier, + }, + }; return tree; } @@ -2588,10 +2698,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (token_ptr.id != token_id) { - ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ - .token = token_index, - .expected_id = token_id, - } }; + ((try tree.errors.addOne())).* = Error{ + .ExpectedToken = Error.ExpectedToken{ + .token = token_index, + .expected_id = token_id, + }, + }; return tree; } continue; @@ -2601,10 +2713,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (token_ptr.id != expect_token_save.id) { - ((try tree.errors.addOne())).* = Error{ .ExpectedToken = Error.ExpectedToken{ - .token = token_index, - .expected_id = expect_token_save.id, - } }; + ((try tree.errors.addOne())).* = Error{ + .ExpectedToken = Error.ExpectedToken{ + .token = token_index, + .expected_id = expect_token_save.id, + }, + }; return tree; } expect_token_save.ptr.* = token_index; @@ -2997,21 +3111,25 @@ fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &con return true; }, Token.Id.Keyword_while => { - stack.append(State{ .While = LoopCtx{ - .label = null, - .inline_token = null, - .loop_token = token_index, - .opt_ctx = ctx.*, - } }) catch unreachable; + stack.append(State{ + .While = LoopCtx{ + .label = null, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.*, + }, + }) catch unreachable; return true; }, Token.Id.Keyword_for => { - stack.append(State{ .For = LoopCtx{ - .label = null, - .inline_token = null, - .loop_token = token_index, - .opt_ctx = ctx.*, - } }) catch unreachable; + stack.append(State{ + .For = LoopCtx{ + .label = null, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.*, + }, + }) catch unreachable; return true; }, Token.Id.Keyword_switch => { @@ -3024,10 +3142,12 @@ fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &con }); ctx.store(&node.base); - stack.append(State{ .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)){ - .list = &node.cases, - .ptr = &node.rbrace, - } }) catch unreachable; + stack.append(State{ + .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)){ + .list = &node.cases, + .ptr = &node.rbrace, + }, + }) catch unreachable; try stack.append(State{ .ExpectToken = Token.Id.LBrace }); try stack.append(State{ .ExpectToken = Token.Id.RParen }); try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); @@ -3080,10 +3200,14 @@ fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, end: return ExpectCommaOrEndResult{ .end_token = token_index }; } - return ExpectCommaOrEndResult{ .parse_error = Error{ .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd{ - .token = token_index, - .end_id = end, - } } }; + return ExpectCommaOrEndResult{ + .parse_error = Error{ + .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd{ + .token = token_index, + .end_id = end, + }, + }, + }; }, } } @@ -3167,13 +3291,14 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { Token.Id.Tilde => ast.Node.PrefixOp.Op{ .BitNot = void{} }, Token.Id.Minus => ast.Node.PrefixOp.Op{ .Negation = void{} }, Token.Id.MinusPercent => ast.Node.PrefixOp.Op{ .NegationWrap = void{} }, - Token.Id.Asterisk, - Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op{ .PointerType = void{} }, - Token.Id.Ampersand => ast.Node.PrefixOp.Op{ .AddrOf = ast.Node.PrefixOp.AddrOfInfo{ - .align_info = null, - .const_token = null, - .volatile_token = null, - } }, + Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op{ .PointerType = void{} }, + Token.Id.Ampersand => ast.Node.PrefixOp.Op{ + .AddrOf = ast.Node.PrefixOp.AddrOfInfo{ + .align_info = null, + .const_token = null, + .volatile_token = null, + }, + }, Token.Id.QuestionMark => ast.Node.PrefixOp.Op{ .MaybeType = void{} }, Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op{ .UnwrapMaybe = void{} }, Token.Id.Keyword_await => ast.Node.PrefixOp.Op{ .Await = void{} }, diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 1f60fae270..75ba9e61d7 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1021,7 +1021,7 @@ test "zig fmt: extern declaration" { } test "zig fmt: alignment" { - try testCanonical( + try testCanonical( \\var foo: c_int align(1); \\ ); @@ -1070,7 +1070,7 @@ test "zig fmt: slice attributes" { } test "zig fmt: test declaration" { - try testCanonical( + try testCanonical( \\test "test name" { \\ const a = 1; \\ var b = 1; @@ -1312,7 +1312,7 @@ test "zig fmt: struct declaration" { } test "zig fmt: enum declaration" { - try testCanonical( + try testCanonical( \\const E = enum { \\ Ok, \\ SomethingElse = 0, @@ -1340,7 +1340,7 @@ test "zig fmt: enum declaration" { } test "zig fmt: union declaration" { - try testCanonical( + try testCanonical( \\const U = union { \\ Int: u8, \\ Float: f32, @@ -1860,10 +1860,15 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { } else |err| switch (err) { error.OutOfMemory => { if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) { - warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n", - fail_index, needed_alloc_count, - failing_allocator.allocated_bytes, failing_allocator.freed_bytes, - failing_allocator.index, failing_allocator.deallocations); + warn( + "\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n", + fail_index, + needed_alloc_count, + failing_allocator.allocated_bytes, + failing_allocator.freed_bytes, + failing_allocator.index, + failing_allocator.deallocations, + ); return error.MemoryLeakDetected; } }, @@ -1876,4 +1881,3 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { fn testCanonical(source: []const u8) !void { return testTransform(source, source); } - diff --git a/std/zig/render.zig b/std/zig/render.zig index dce659f1ef..e3bf5fe38e 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -161,7 +161,15 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i } } -fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, start_col: &usize, base: &ast.Node, space: Space,) (@typeOf(stream).Child.Error || Error)!void { +fn renderExpression( + allocator: &mem.Allocator, + stream: var, + tree: &ast.Tree, + indent: usize, + start_col: &usize, + base: &ast.Node, + space: Space, +) (@typeOf(stream).Child.Error || Error)!void { switch (base.id) { ast.Node.Id.Identifier => { const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); @@ -259,8 +267,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.lhs, op_space); const after_op_space = blk: { - const loc = tree.tokenLocation(tree.tokens.at(infix_op_node.op_token).end, - tree.nextToken(infix_op_node.op_token)); + const loc = tree.tokenLocation(tree.tokens.at(infix_op_node.op_token).end, tree.nextToken(infix_op_node.op_token)); break :blk if (loc.line == 0) op_space else Space.Newline; }; @@ -367,14 +374,16 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind ast.Node.PrefixOp.Op.NegationWrap, ast.Node.PrefixOp.Op.UnwrapMaybe, ast.Node.PrefixOp.Op.MaybeType, - ast.Node.PrefixOp.Op.PointerType => { + ast.Node.PrefixOp.Op.PointerType, + => { try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); }, ast.Node.PrefixOp.Op.Try, ast.Node.PrefixOp.Op.Await, ast.Node.PrefixOp.Op.Cancel, - ast.Node.PrefixOp.Op.Resume => { + ast.Node.PrefixOp.Op.Resume, + => { try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space); }, } @@ -1568,13 +1577,19 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind ast.Node.Id.VarDecl, ast.Node.Id.Use, ast.Node.Id.TestDecl, - ast.Node.Id.ParamDecl => unreachable, + ast.Node.Id.ParamDecl, + => unreachable, } } -fn renderVarDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, start_col: &usize, - var_decl: &ast.Node.VarDecl,) (@typeOf(stream).Child.Error || Error)!void -{ +fn renderVarDecl( + allocator: &mem.Allocator, + stream: var, + tree: &ast.Tree, + indent: usize, + start_col: &usize, + var_decl: &ast.Node.VarDecl, +) (@typeOf(stream).Child.Error || Error)!void { if (var_decl.visib_token) |visib_token| { try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub } @@ -1623,7 +1638,15 @@ fn renderVarDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent try renderToken(tree, stream, var_decl.semicolon_token, indent, start_col, Space.Newline); } -fn renderParamDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, start_col: &usize, base: &ast.Node, space: Space,) (@typeOf(stream).Child.Error || Error)!void { +fn renderParamDecl( + allocator: &mem.Allocator, + stream: var, + tree: &ast.Tree, + indent: usize, + start_col: &usize, + base: &ast.Node, + space: Space, +) (@typeOf(stream).Child.Error || Error)!void { const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); if (param_decl.comptime_token) |comptime_token| { @@ -1643,7 +1666,14 @@ fn renderParamDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, inde } } -fn renderStatement(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, start_col: &usize, base: &ast.Node,) (@typeOf(stream).Child.Error || Error)!void { +fn renderStatement( + allocator: &mem.Allocator, + stream: var, + tree: &ast.Tree, + indent: usize, + start_col: &usize, + base: &ast.Node, +) (@typeOf(stream).Child.Error || Error)!void { switch (base.id) { ast.Node.Id.VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); @@ -1840,7 +1870,13 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent } } -fn renderDocComments(tree: &ast.Tree, stream: var, node: var, indent: usize, start_col: &usize,) (@typeOf(stream).Child.Error || Error)!void { +fn renderDocComments( + tree: &ast.Tree, + stream: var, + node: var, + indent: usize, + start_col: &usize, +) (@typeOf(stream).Child.Error || Error)!void { const comment = node.doc_comments ?? return; var it = comment.lines.iterator(0); const first_token = node.firstToken(); diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index b90a40108f..4881e952b7 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -11,55 +11,55 @@ pub const Token = struct { id: Id, }; - pub const keywords = []Keyword { - Keyword{.bytes="align", .id = Id.Keyword_align}, - Keyword{.bytes="and", .id = Id.Keyword_and}, - Keyword{.bytes="asm", .id = Id.Keyword_asm}, - Keyword{.bytes="async", .id = Id.Keyword_async}, - Keyword{.bytes="await", .id = Id.Keyword_await}, - Keyword{.bytes="break", .id = Id.Keyword_break}, - Keyword{.bytes="catch", .id = Id.Keyword_catch}, - Keyword{.bytes="cancel", .id = Id.Keyword_cancel}, - Keyword{.bytes="comptime", .id = Id.Keyword_comptime}, - Keyword{.bytes="const", .id = Id.Keyword_const}, - Keyword{.bytes="continue", .id = Id.Keyword_continue}, - Keyword{.bytes="defer", .id = Id.Keyword_defer}, - Keyword{.bytes="else", .id = Id.Keyword_else}, - Keyword{.bytes="enum", .id = Id.Keyword_enum}, - Keyword{.bytes="errdefer", .id = Id.Keyword_errdefer}, - Keyword{.bytes="error", .id = Id.Keyword_error}, - Keyword{.bytes="export", .id = Id.Keyword_export}, - Keyword{.bytes="extern", .id = Id.Keyword_extern}, - Keyword{.bytes="false", .id = Id.Keyword_false}, - Keyword{.bytes="fn", .id = Id.Keyword_fn}, - Keyword{.bytes="for", .id = Id.Keyword_for}, - Keyword{.bytes="if", .id = Id.Keyword_if}, - Keyword{.bytes="inline", .id = Id.Keyword_inline}, - Keyword{.bytes="nakedcc", .id = Id.Keyword_nakedcc}, - Keyword{.bytes="noalias", .id = Id.Keyword_noalias}, - Keyword{.bytes="null", .id = Id.Keyword_null}, - Keyword{.bytes="or", .id = Id.Keyword_or}, - Keyword{.bytes="packed", .id = Id.Keyword_packed}, - Keyword{.bytes="promise", .id = Id.Keyword_promise}, - Keyword{.bytes="pub", .id = Id.Keyword_pub}, - Keyword{.bytes="resume", .id = Id.Keyword_resume}, - Keyword{.bytes="return", .id = Id.Keyword_return}, - Keyword{.bytes="section", .id = Id.Keyword_section}, - Keyword{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc}, - Keyword{.bytes="struct", .id = Id.Keyword_struct}, - Keyword{.bytes="suspend", .id = Id.Keyword_suspend}, - Keyword{.bytes="switch", .id = Id.Keyword_switch}, - Keyword{.bytes="test", .id = Id.Keyword_test}, - Keyword{.bytes="this", .id = Id.Keyword_this}, - Keyword{.bytes="true", .id = Id.Keyword_true}, - Keyword{.bytes="try", .id = Id.Keyword_try}, - Keyword{.bytes="undefined", .id = Id.Keyword_undefined}, - Keyword{.bytes="union", .id = Id.Keyword_union}, - Keyword{.bytes="unreachable", .id = Id.Keyword_unreachable}, - Keyword{.bytes="use", .id = Id.Keyword_use}, - Keyword{.bytes="var", .id = Id.Keyword_var}, - Keyword{.bytes="volatile", .id = Id.Keyword_volatile}, - Keyword{.bytes="while", .id = Id.Keyword_while}, + pub const keywords = []Keyword{ + Keyword{ .bytes = "align", .id = Id.Keyword_align }, + Keyword{ .bytes = "and", .id = Id.Keyword_and }, + Keyword{ .bytes = "asm", .id = Id.Keyword_asm }, + Keyword{ .bytes = "async", .id = Id.Keyword_async }, + Keyword{ .bytes = "await", .id = Id.Keyword_await }, + Keyword{ .bytes = "break", .id = Id.Keyword_break }, + Keyword{ .bytes = "catch", .id = Id.Keyword_catch }, + Keyword{ .bytes = "cancel", .id = Id.Keyword_cancel }, + Keyword{ .bytes = "comptime", .id = Id.Keyword_comptime }, + Keyword{ .bytes = "const", .id = Id.Keyword_const }, + Keyword{ .bytes = "continue", .id = Id.Keyword_continue }, + Keyword{ .bytes = "defer", .id = Id.Keyword_defer }, + Keyword{ .bytes = "else", .id = Id.Keyword_else }, + Keyword{ .bytes = "enum", .id = Id.Keyword_enum }, + Keyword{ .bytes = "errdefer", .id = Id.Keyword_errdefer }, + Keyword{ .bytes = "error", .id = Id.Keyword_error }, + Keyword{ .bytes = "export", .id = Id.Keyword_export }, + Keyword{ .bytes = "extern", .id = Id.Keyword_extern }, + Keyword{ .bytes = "false", .id = Id.Keyword_false }, + Keyword{ .bytes = "fn", .id = Id.Keyword_fn }, + Keyword{ .bytes = "for", .id = Id.Keyword_for }, + Keyword{ .bytes = "if", .id = Id.Keyword_if }, + Keyword{ .bytes = "inline", .id = Id.Keyword_inline }, + Keyword{ .bytes = "nakedcc", .id = Id.Keyword_nakedcc }, + Keyword{ .bytes = "noalias", .id = Id.Keyword_noalias }, + Keyword{ .bytes = "null", .id = Id.Keyword_null }, + Keyword{ .bytes = "or", .id = Id.Keyword_or }, + Keyword{ .bytes = "packed", .id = Id.Keyword_packed }, + Keyword{ .bytes = "promise", .id = Id.Keyword_promise }, + Keyword{ .bytes = "pub", .id = Id.Keyword_pub }, + Keyword{ .bytes = "resume", .id = Id.Keyword_resume }, + Keyword{ .bytes = "return", .id = Id.Keyword_return }, + Keyword{ .bytes = "section", .id = Id.Keyword_section }, + Keyword{ .bytes = "stdcallcc", .id = Id.Keyword_stdcallcc }, + Keyword{ .bytes = "struct", .id = Id.Keyword_struct }, + Keyword{ .bytes = "suspend", .id = Id.Keyword_suspend }, + Keyword{ .bytes = "switch", .id = Id.Keyword_switch }, + Keyword{ .bytes = "test", .id = Id.Keyword_test }, + Keyword{ .bytes = "this", .id = Id.Keyword_this }, + Keyword{ .bytes = "true", .id = Id.Keyword_true }, + Keyword{ .bytes = "try", .id = Id.Keyword_try }, + Keyword{ .bytes = "undefined", .id = Id.Keyword_undefined }, + Keyword{ .bytes = "union", .id = Id.Keyword_union }, + Keyword{ .bytes = "unreachable", .id = Id.Keyword_unreachable }, + Keyword{ .bytes = "use", .id = Id.Keyword_use }, + Keyword{ .bytes = "var", .id = Id.Keyword_var }, + Keyword{ .bytes = "volatile", .id = Id.Keyword_volatile }, + Keyword{ .bytes = "while", .id = Id.Keyword_while }, }; // TODO perfect hash at comptime @@ -72,7 +72,10 @@ pub const Token = struct { return null; } - const StrLitKind = enum {Normal, C}; + const StrLitKind = enum { + Normal, + C, + }; pub const Id = union(enum) { Invalid, @@ -202,7 +205,7 @@ pub const Tokenizer = struct { } pub fn init(buffer: []const u8) Tokenizer { - return Tokenizer { + return Tokenizer{ .buffer = buffer, .index = 0, .pending_invalid_token = null, @@ -269,7 +272,7 @@ pub const Tokenizer = struct { } const start_index = self.index; var state = State.Start; - var result = Token { + var result = Token{ .id = Token.Id.Eof, .start = self.index, .end = undefined, @@ -290,7 +293,7 @@ pub const Tokenizer = struct { }, '"' => { state = State.StringLiteral; - result.id = Token.Id { .StringLiteral = Token.StrLitKind.Normal }; + result.id = Token.Id{ .StringLiteral = Token.StrLitKind.Normal }; }, '\'' => { state = State.CharLiteral; @@ -369,7 +372,7 @@ pub const Tokenizer = struct { }, '\\' => { state = State.Backslash; - result.id = Token.Id { .MultilineStringLiteralLine = Token.StrLitKind.Normal }; + result.id = Token.Id{ .MultilineStringLiteralLine = Token.StrLitKind.Normal }; }, '{' => { result.id = Token.Id.LBrace; @@ -455,7 +458,7 @@ pub const Tokenizer = struct { else => { result.id = Token.Id.Asterisk; break; - } + }, }, State.AsteriskPercent => switch (c) { @@ -467,7 +470,7 @@ pub const Tokenizer = struct { else => { result.id = Token.Id.AsteriskPercent; break; - } + }, }, State.QuestionMark => switch (c) { @@ -535,7 +538,7 @@ pub const Tokenizer = struct { else => { result.id = Token.Id.Caret; break; - } + }, }, State.Identifier => switch (c) { @@ -560,11 +563,11 @@ pub const Tokenizer = struct { State.C => switch (c) { '\\' => { state = State.Backslash; - result.id = Token.Id { .MultilineStringLiteralLine = Token.StrLitKind.C }; + result.id = Token.Id{ .MultilineStringLiteralLine = Token.StrLitKind.C }; }, '"' => { state = State.StringLiteral; - result.id = Token.Id { .StringLiteral = Token.StrLitKind.C }; + result.id = Token.Id{ .StringLiteral = Token.StrLitKind.C }; }, 'a'...'z', 'A'...'Z', '_', '0'...'9' => { state = State.Identifier; @@ -605,7 +608,7 @@ pub const Tokenizer = struct { } state = State.CharLiteralEnd; - } + }, }, State.CharLiteralBackslash => switch (c) { @@ -736,7 +739,7 @@ pub const Tokenizer = struct { else => { result.id = Token.Id.MinusPercent; break; - } + }, }, State.AngleBracketLeft => switch (c) { @@ -944,7 +947,7 @@ pub const Tokenizer = struct { // reinterpret as a normal exponent number self.index -= 1; state = State.FloatExponentNumber; - } + }, }, State.FloatExponentUnsignedHex => switch (c) { '+', '-' => { @@ -954,7 +957,7 @@ pub const Tokenizer = struct { // reinterpret as a normal exponent number self.index -= 1; state = State.FloatExponentNumberHex; - } + }, }, State.FloatExponentNumber => switch (c) { '0'...'9' => {}, @@ -978,15 +981,15 @@ pub const Tokenizer = struct { State.FloatExponentNumberHex, State.StringLiteral, // find this error later State.MultilineStringLiteralLine, - State.Builtin => {}, + State.Builtin, + => {}, State.Identifier => { if (Token.getKeyword(self.buffer[result.start..self.index])) |id| { result.id = id; } }, - State.LineCommentStart, - State.LineComment => { + State.LineCommentStart, State.LineComment => { result.id = Token.Id.LineComment; }, State.DocComment, State.DocCommentStart => { @@ -1004,7 +1007,8 @@ pub const Tokenizer = struct { State.CharLiteralEscape1, State.CharLiteralEscape2, State.CharLiteralEnd, - State.StringLiteralBackslash => { + State.StringLiteralBackslash, + => { result.id = Token.Id.Invalid; }, @@ -1089,7 +1093,7 @@ pub const Tokenizer = struct { if (self.pending_invalid_token != null) return; const invalid_length = self.getInvalidCharacterLength(); if (invalid_length == 0) return; - self.pending_invalid_token = Token { + self.pending_invalid_token = Token{ .id = Token.Id.Invalid, .start = self.index, .end = self.index + invalid_length, @@ -1134,23 +1138,18 @@ pub const Tokenizer = struct { } }; - - test "tokenizer" { - testTokenize("test", []Token.Id { - Token.Id.Keyword_test, - }); + testTokenize("test", []Token.Id{Token.Id.Keyword_test}); } test "tokenizer - char literal with hex escape" { - testTokenize( \\'\x1b' - , []Token.Id { - Token.Id.CharLiteral, - }); + testTokenize( + \\'\x1b' + , []Token.Id{Token.Id.CharLiteral}); } test "tokenizer - float literal e exponent" { - testTokenize("a = 4.94065645841246544177e-324;\n", []Token.Id { + testTokenize("a = 4.94065645841246544177e-324;\n", []Token.Id{ Token.Id.Identifier, Token.Id.Equal, Token.Id.FloatLiteral, @@ -1159,7 +1158,7 @@ test "tokenizer - float literal e exponent" { } test "tokenizer - float literal p exponent" { - testTokenize("a = 0x1.a827999fcef32p+1022;\n", []Token.Id { + testTokenize("a = 0x1.a827999fcef32p+1022;\n", []Token.Id{ Token.Id.Identifier, Token.Id.Equal, Token.Id.FloatLiteral, @@ -1168,31 +1167,31 @@ test "tokenizer - float literal p exponent" { } test "tokenizer - chars" { - testTokenize("'c'", []Token.Id {Token.Id.CharLiteral}); + testTokenize("'c'", []Token.Id{Token.Id.CharLiteral}); } test "tokenizer - invalid token characters" { testTokenize("#", []Token.Id{Token.Id.Invalid}); testTokenize("`", []Token.Id{Token.Id.Invalid}); - testTokenize("'c", []Token.Id {Token.Id.Invalid}); - testTokenize("'", []Token.Id {Token.Id.Invalid}); - testTokenize("''", []Token.Id {Token.Id.Invalid, Token.Id.Invalid}); + testTokenize("'c", []Token.Id{Token.Id.Invalid}); + testTokenize("'", []Token.Id{Token.Id.Invalid}); + testTokenize("''", []Token.Id{ Token.Id.Invalid, Token.Id.Invalid }); } test "tokenizer - invalid literal/comment characters" { - testTokenize("\"\x00\"", []Token.Id { - Token.Id { .StringLiteral = Token.StrLitKind.Normal }, + testTokenize("\"\x00\"", []Token.Id{ + Token.Id{ .StringLiteral = Token.StrLitKind.Normal }, Token.Id.Invalid, }); - testTokenize("//\x00", []Token.Id { + testTokenize("//\x00", []Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\x1f", []Token.Id { + testTokenize("//\x1f", []Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); - testTokenize("//\x7f", []Token.Id { + testTokenize("//\x7f", []Token.Id{ Token.Id.LineComment, Token.Id.Invalid, }); @@ -1261,18 +1260,16 @@ test "tokenizer - illegal unicode codepoints" { test "tokenizer - string identifier and builtin fns" { testTokenize( \\const @"if" = @import("std"); - , - []Token.Id{ - Token.Id.Keyword_const, - Token.Id.Identifier, - Token.Id.Equal, - Token.Id.Builtin, - Token.Id.LParen, - Token.Id {.StringLiteral = Token.StrLitKind.Normal}, - Token.Id.RParen, - Token.Id.Semicolon, - } - ); + , []Token.Id{ + Token.Id.Keyword_const, + Token.Id.Identifier, + Token.Id.Equal, + Token.Id.Builtin, + Token.Id.LParen, + Token.Id{ .StringLiteral = Token.StrLitKind.Normal }, + Token.Id.RParen, + Token.Id.Semicolon, + }); } test "tokenizer - pipe and then invalid" { @@ -1314,7 +1311,10 @@ fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { } switch (expected_token_id) { Token.Id.StringLiteral => |expected_kind| { - std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable }); + std.debug.assert(expected_kind == switch (token.id) { + Token.Id.StringLiteral => |kind| kind, + else => unreachable, + }); }, else => {}, } diff --git a/test/cases/align.zig b/test/cases/align.zig index a1259e96bf..f82aa6cfc4 100644 --- a/test/cases/align.zig +++ b/test/cases/align.zig @@ -70,7 +70,7 @@ test "specifying alignment allows pointer cast" { testBytesAlign(0x33); } fn testBytesAlign(b: u8) void { - var bytes align(4) = []u8 { + var bytes align(4) = []u8{ b, b, b, @@ -84,7 +84,7 @@ test "specifying alignment allows slice cast" { testBytesAlignSlice(0x33); } fn testBytesAlignSlice(b: u8) void { - var bytes align(4) = []u8 { + var bytes align(4) = []u8{ b, b, b, @@ -107,7 +107,7 @@ fn expects4(x: &align(4) u32) void { } test "@alignCast slices" { - var array align(4) = []u32 { + var array align(4) = []u32{ 1, 1, }; @@ -169,7 +169,7 @@ test "@ptrCast preserves alignment of bigger source" { test "compile-time known array index has best alignment possible" { // take full advantage of over-alignment - var array align(4) = []u8 { + var array align(4) = []u8{ 1, 2, 3, @@ -181,7 +181,7 @@ test "compile-time known array index has best alignment possible" { assert(@typeOf(&array[3]) == &u8); // because align is too small but we still figure out to use 2 - var bigger align(2) = []u64 { + var bigger align(2) = []u64{ 1, 2, 3, @@ -193,7 +193,7 @@ test "compile-time known array index has best alignment possible" { assert(@typeOf(&bigger[3]) == &align(2) u64); // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 - var smaller align(2) = []u32 { + var smaller align(2) = []u32{ 1, 2, 3, diff --git a/test/cases/array.zig b/test/cases/array.zig index 0fb61b2a9f..9a405216d8 100644 --- a/test/cases/array.zig +++ b/test/cases/array.zig @@ -34,7 +34,7 @@ test "void arrays" { } test "array literal" { - const hex_mult = []u16 { + const hex_mult = []u16{ 4096, 256, 16, @@ -54,7 +54,7 @@ test "array dot len const expr" { const ArrayDotLenConstExpr = struct { y: [some_array.len]u8, }; -const some_array = []u8 { +const some_array = []u8{ 0, 1, 2, @@ -62,7 +62,7 @@ const some_array = []u8 { }; test "nested arrays" { - const array_of_strings = [][]const u8 { + const array_of_strings = [][]const u8{ "hello", "this", "is", @@ -86,9 +86,7 @@ const Str = struct { a: []Sub, }; test "set global var array via slice embedded in struct" { - var s = Str { - .a = s_array[0..], - }; + var s = Str{ .a = s_array[0..] }; s.a[0].b = 1; s.a[1].b = 2; @@ -100,7 +98,7 @@ test "set global var array via slice embedded in struct" { } test "array literal with specified size" { - var array = [2]u8 { + var array = [2]u8{ 1, 2, }; diff --git a/test/cases/bugs/394.zig b/test/cases/bugs/394.zig index a99bd18b28..b0afec2357 100644 --- a/test/cases/bugs/394.zig +++ b/test/cases/bugs/394.zig @@ -10,11 +10,9 @@ const S = struct { const assert = @import("std").debug.assert; test "bug 394 fixed" { - const x = S { + const x = S{ .x = 3, - .y = E { - .B = 1, - }, + .y = E{ .B = 1 }, }; assert(x.x == 3); } diff --git a/test/cases/bugs/656.zig b/test/cases/bugs/656.zig index 24a28bf411..a6035d51bb 100644 --- a/test/cases/bugs/656.zig +++ b/test/cases/bugs/656.zig @@ -14,10 +14,8 @@ test "nullable if after an if in a switch prong of a switch with 2 prongs in an } fn foo(a: bool, b: bool) void { - var prefix_op = PrefixOp { - .AddrOf = Value { - .align_expr = 1234, - }, + var prefix_op = PrefixOp{ + .AddrOf = Value{ .align_expr = 1234 }, }; if (a) {} else { switch (prefix_op) { diff --git a/test/cases/bugs/828.zig b/test/cases/bugs/828.zig index 8f329e4f82..10d7370b90 100644 --- a/test/cases/bugs/828.zig +++ b/test/cases/bugs/828.zig @@ -1,14 +1,10 @@ const CountBy = struct { a: usize, - const One = CountBy { - .a = 1, - }; + const One = CountBy{ .a = 1 }; pub fn counter(self: &const CountBy) Counter { - return Counter { - .i = 0, - }; + return Counter{ .i = 0 }; } }; diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 8b6afb4310..e37451ea93 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -33,27 +33,21 @@ fn funcWithConstPtrPtr(x: &const &i32) void { } test "implicitly cast a container to a const pointer of it" { - const z = Struct(void) { - .x = void{}, - }; + const z = Struct(void){ .x = void{} }; assert(0 == @sizeOf(@typeOf(z))); assert(void{} == Struct(void).pointer(z).x); assert(void{} == Struct(void).pointer(&z).x); assert(void{} == Struct(void).maybePointer(z).x); assert(void{} == Struct(void).maybePointer(&z).x); assert(void{} == Struct(void).maybePointer(null).x); - const s = Struct(u8) { - .x = 42, - }; + const s = Struct(u8){ .x = 42 }; assert(0 != @sizeOf(@typeOf(s))); assert(42 == Struct(u8).pointer(s).x); assert(42 == Struct(u8).pointer(&s).x); assert(42 == Struct(u8).maybePointer(s).x); assert(42 == Struct(u8).maybePointer(&s).x); assert(0 == Struct(u8).maybePointer(null).x); - const u = Union { - .x = 42, - }; + const u = Union{ .x = 42 }; assert(42 == Union.pointer(u).x); assert(42 == Union.pointer(&u).x); assert(42 == Union.maybePointer(u).x); @@ -77,9 +71,7 @@ fn Struct(comptime T: type) type { } fn maybePointer(self: ?&const Self) Self { - const none = Self { - .x = if (T == void) void{} else 0, - }; + const none = Self{ .x = if (T == void) void{} else 0 }; return (self ?? &none).*; } }; @@ -93,9 +85,7 @@ const Union = union { } fn maybePointer(self: ?&const Union) Union { - const none = Union { - .x = 0, - }; + const none = Union{ .x = 0 }; return (self ?? &none).*; } }; @@ -130,9 +120,7 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" { return ((??p).*.*).x; } }; - const s = S { - .x = 42, - }; + const s = S{ .x = 42 }; const p = &s; const q = &p; const r = &q; @@ -202,9 +190,7 @@ fn castToMaybeTypeError(z: i32) void { const f = z; const g: error!?i32 = f; - const a = A { - .a = z, - }; + const a = A{ .a = z }; const b: error!?A = a; assert((??(b catch unreachable)).a == 1); } @@ -343,7 +329,6 @@ test "peer type resolution: error and [N]T" { // TODO: implicit error!T to error!U where T can implicitly cast to U //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); //comptime assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); comptime assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); } @@ -387,7 +372,7 @@ fn cast128Float(x: u128) f128 { } test "const slice widen cast" { - const bytes align(4) = []u8 { + const bytes align(4) = []u8{ 0x12, 0x12, 0x12, diff --git a/test/cases/const_slice_child.zig b/test/cases/const_slice_child.zig index 456b115234..a92c589186 100644 --- a/test/cases/const_slice_child.zig +++ b/test/cases/const_slice_child.zig @@ -4,7 +4,7 @@ const assert = debug.assert; var argv: &const &const u8 = undefined; test "const slice child" { - const strs = ([]&const u8) { + const strs = ([]&const u8){ c"one", c"two", c"three", diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index e983947a4c..8a0218aeb7 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -10,7 +10,6 @@ test "create a coroutine and cancel it" { cancel p; assert(x == 2); } - async fn simpleAsyncFn() void { x += 1; suspend; @@ -28,7 +27,6 @@ test "coroutine suspend, resume, cancel" { assert(std.mem.eql(u8, points, "abcdefg")); } - async fn testAsyncSeq() void { defer seq('e'); @@ -36,7 +34,7 @@ async fn testAsyncSeq() void { suspend; seq('d'); } -var points = []u8 {0} ** "abcdefg".len; +var points = []u8{0} ** "abcdefg".len; var index: usize = 0; fn seq(c: u8) void { @@ -54,7 +52,6 @@ test "coroutine suspend with block" { var a_promise: promise = undefined; var result = false; - async fn testSuspendBlock() void { suspend |p| { comptime assert(@typeOf(p) == promise->void); @@ -75,7 +72,6 @@ test "coroutine await" { assert(await_final_result == 1234); assert(std.mem.eql(u8, await_points, "abcdefghi")); } - async fn await_amain() void { await_seq('b'); const p = async await_another() catch unreachable; @@ -83,7 +79,6 @@ async fn await_amain() void { await_final_result = await p; await_seq('h'); } - async fn await_another() i32 { await_seq('c'); suspend |p| { @@ -94,7 +89,7 @@ async fn await_another() i32 { return 1234; } -var await_points = []u8 {0} ** "abcdefghi".len; +var await_points = []u8{0} ** "abcdefghi".len; var await_seq_index: usize = 0; fn await_seq(c: u8) void { @@ -111,7 +106,6 @@ test "coroutine await early return" { assert(early_final_result == 1234); assert(std.mem.eql(u8, early_points, "abcdef")); } - async fn early_amain() void { early_seq('b'); const p = async early_another() catch unreachable; @@ -119,13 +113,12 @@ async fn early_amain() void { early_final_result = await p; early_seq('e'); } - async fn early_another() i32 { early_seq('c'); return 1234; } -var early_points = []u8 {0} ** "abcdef".len; +var early_points = []u8{0} ** "abcdef".len; var early_seq_index: usize = 0; fn early_seq(c: u8) void { @@ -141,7 +134,6 @@ test "coro allocation failure" { error.OutOfMemory => {}, } } - async fn asyncFuncThatNeverGetsRun() void { @panic("coro frame allocation should fail"); } @@ -164,15 +156,12 @@ test "async fn pointer in a struct field" { const Foo = struct { bar: async<&std.mem.Allocator> fn(&i32) void, }; - var foo = Foo { - .bar = simpleAsyncFn2, - }; + var foo = Foo{ .bar = simpleAsyncFn2 }; const p = (async foo.bar(&data)) catch unreachable; assert(data == 2); cancel p; assert(data == 4); } - async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void { defer y.* += 2; y.* += 1; @@ -184,7 +173,6 @@ test "async fn with inferred error set" { resume p; cancel p; } - async fn failing() !void { suspend; return error.Fail; @@ -208,12 +196,10 @@ test "error return trace across suspend points - async return" { fn nonFailing() (promise->error!void) { return async suspendThenFail() catch unreachable; } - async fn suspendThenFail() error!void { suspend; return error.Fail; } - async fn printTrace(p: promise->error!void) void { (await p) catch |e| { std.debug.assert(e == error.Fail); @@ -234,7 +220,6 @@ test "break from suspend" { cancel p; std.debug.assert(my_result == 2); } - async fn testBreakFromSuspend(my_result: &i32) void { s: suspend |p| { break :s; diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 1c46a3d9e0..cbcbc5e306 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -2,11 +2,9 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; test "enum type" { - const foo1 = Foo { - .One = 13, - }; - const foo2 = Foo { - .Two = Point { + const foo1 = Foo{ .One = 13 }; + const foo2 = Foo{ + .Two = Point{ .x = 1234, .y = 5678, }, @@ -48,18 +46,12 @@ const Bar = enum { }; fn returnAnInt(x: i32) Foo { - return Foo { - .One = x, - }; + return Foo{ .One = x }; } test "constant enum with payload" { - var empty = AnEnumWithPayload { - .Empty = {}, - }; - var full = AnEnumWithPayload { - .Full = 13, - }; + var empty = AnEnumWithPayload{ .Empty = {} }; + var full = AnEnumWithPayload{ .Full = 13 }; shouldBeEmpty(empty); shouldBeNotEmpty(full); } @@ -737,7 +729,7 @@ const BitFieldOfEnums = packed struct { c: C, }; -const bit_field_1 = BitFieldOfEnums { +const bit_field_1 = BitFieldOfEnums{ .a = A.Two, .b = B.Three3, .c = C.Four4, diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index 9e3e031f92..8fafa70b02 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -15,12 +15,8 @@ const ET = union(enum) { }; test "enum with members" { - const a = ET { - .SINT = -42, - }; - const b = ET { - .UINT = 42, - }; + const a = ET{ .SINT = -42 }; + const b = ET{ .UINT = 42 }; var buf: [20]u8 = undefined; assert((a.print(buf[0..]) catch unreachable) == 3); diff --git a/test/cases/error.zig b/test/cases/error.zig index 70d96e4d01..92b2a012bd 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -92,7 +92,7 @@ test "error set type " { comptime testErrorSetType(); } -const MyErrSet = error { +const MyErrSet = error{ OutOfMemory, FileNotFound, }; @@ -114,11 +114,11 @@ test "explicit error set cast" { comptime testExplicitErrorSetCast(Set1.A); } -const Set1 = error { +const Set1 = error{ A, B, }; -const Set2 = error { +const Set2 = error{ A, C, }; @@ -134,8 +134,7 @@ test "comptime test error for empty error set" { comptime testComptimeTestErrorEmptySet(1234); } -const EmptyErrorSet = error { -}; +const EmptyErrorSet = error{}; fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { if (x) |v| assert(v == 1234) else |err| @compileError("bad"); @@ -151,9 +150,10 @@ test "comptime err to int of error set with only 1 possible value" { testErrToIntWithOnePossibleValue(error.A, u32(error.A)); comptime testErrToIntWithOnePossibleValue(error.A, u32(error.A)); } -fn testErrToIntWithOnePossibleValue(x: error { - A, -}, comptime value: u32) void { +fn testErrToIntWithOnePossibleValue( + x: error{A}, + comptime value: u32, +) void { if (u32(x) != value) { @compileError("bad"); } @@ -197,16 +197,14 @@ fn foo2(f: fn() error!void) void { const x = f(); } -fn bar2() (error { -}!void) {} +fn bar2() (error{}!void) {} test "error: Zero sized error set returned with value payload crash" { _ = foo3(0); _ = comptime foo3(0); } -const Error = error { -}; +const Error = error{}; fn foo3(b: usize) Error!usize { return b; } diff --git a/test/cases/eval.zig b/test/cases/eval.zig index d7ad68b74e..3a1c67445a 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -72,12 +72,12 @@ const Point = struct { x: i32, y: i32, }; -const static_point_list = []Point { +const static_point_list = []Point{ makePoint(1, 2), makePoint(3, 4), }; fn makePoint(x: i32, y: i32) Point { - return Point { + return Point{ .x = x, .y = y, }; @@ -92,13 +92,11 @@ pub const Vec3 = struct { data: [3]f32, }; pub fn vec3(x: f32, y: f32, z: f32) Vec3 { - return Vec3 { - .data = []f32 { - x, - y, - z, - }, - }; + return Vec3{ .data = []f32{ + x, + y, + z, + } }; } test "constant expressions" { @@ -117,22 +115,22 @@ const Vertex = struct { g: f32, b: f32, }; -const vertices = []Vertex { - Vertex { +const vertices = []Vertex{ + Vertex{ .x = -0.6, .y = -0.4, .r = 1.0, .g = 0.0, .b = 0.0, }, - Vertex { + Vertex{ .x = 0.6, .y = -0.4, .r = 0.0, .g = 1.0, .b = 0.0, }, - Vertex { + Vertex{ .x = 0.0, .y = 0.6, .r = 0.0, @@ -149,7 +147,7 @@ const StInitStrFoo = struct { x: i32, y: bool, }; -var st_init_str_foo = StInitStrFoo { +var st_init_str_foo = StInitStrFoo{ .x = 13, .y = true, }; @@ -158,7 +156,7 @@ test "statically initalized array literal" { const y: [4]u8 = st_init_arr_lit_x; assert(y[3] == 4); } -const st_init_arr_lit_x = []u8 { +const st_init_arr_lit_x = []u8{ 1, 2, 3, @@ -220,16 +218,16 @@ const CmdFn = struct { func: fn(i32) i32, }; -const cmd_fns = []CmdFn { - CmdFn { +const cmd_fns = []CmdFn{ + CmdFn{ .name = "one", .func = one, }, - CmdFn { + CmdFn{ .name = "two", .func = two, }, - CmdFn { + CmdFn{ .name = "three", .func = three, }, @@ -289,9 +287,7 @@ const SimpleStruct = struct { } }; -var simple_struct = SimpleStruct { - .field = 1234, -}; +var simple_struct = SimpleStruct{ .field = 1234 }; const bound_fn = simple_struct.method; @@ -341,9 +337,7 @@ const Foo = struct { name: []const u8, }; -var foo_contents = Foo { - .name = "a", -}; +var foo_contents = Foo{ .name = "a" }; const foo_ref = &foo_contents; test "create global array with for loop" { @@ -529,9 +523,7 @@ const SingleFieldStruct = struct { }; test "const ptr to comptime mutable data is not memoized" { comptime { - var foo = SingleFieldStruct { - .x = 1, - }; + var foo = SingleFieldStruct{ .x = 1 }; assert(foo.read_x() == 1); foo.x = 2; assert(foo.read_x() == 2); @@ -574,9 +566,7 @@ pub const Info = struct { version: u8, }; -pub const diamond_info = Info { - .version = 0, -}; +pub const diamond_info = Info{ .version = 0 }; test "comptime modification of const struct field" { comptime { diff --git a/test/cases/field_parent_ptr.zig b/test/cases/field_parent_ptr.zig index 2e519098cc..1a7de9ce35 100644 --- a/test/cases/field_parent_ptr.zig +++ b/test/cases/field_parent_ptr.zig @@ -17,7 +17,7 @@ const Foo = struct { d: i32, }; -const foo = Foo { +const foo = Foo{ .a = true, .b = 0.123, .c = 1234, diff --git a/test/cases/fn.zig b/test/cases/fn.zig index 6d47dafad4..a0691fbffc 100644 --- a/test/cases/fn.zig +++ b/test/cases/fn.zig @@ -73,7 +73,7 @@ fn fnWithUnreachable() noreturn { } test "function pointers" { - const fns = []@typeOf(fn1) { + const fns = []@typeOf(fn1){ fn1, fn2, fn3, diff --git a/test/cases/fn_in_struct_in_comptime.zig b/test/cases/fn_in_struct_in_comptime.zig index 4f181d7ffb..51e494036b 100644 --- a/test/cases/fn_in_struct_in_comptime.zig +++ b/test/cases/fn_in_struct_in_comptime.zig @@ -1,6 +1,6 @@ const assert = @import("std").debug.assert; -fn get_foo() fn(&u8)usize { +fn get_foo() fn(&u8) usize { comptime { return struct { fn func(ptr: &u8) usize { diff --git a/test/cases/for.zig b/test/cases/for.zig index f13e6ec6e5..c624035708 100644 --- a/test/cases/for.zig +++ b/test/cases/for.zig @@ -3,7 +3,7 @@ const assert = std.debug.assert; const mem = std.mem; test "continue in for loop" { - const array = []i32 { + const array = []i32{ 1, 2, 3, @@ -35,7 +35,7 @@ fn mangleString(s: []u8) void { } test "basic for loop" { - const expected_result = []u8 { + const expected_result = []u8{ 9, 8, 7, @@ -57,7 +57,7 @@ test "basic for loop" { var buffer: [expected_result.len]u8 = undefined; var buf_index: usize = 0; - const array = []u8 { + const array = []u8{ 9, 8, 7, diff --git a/test/cases/generics.zig b/test/cases/generics.zig index 3fb33d0495..93fecc7295 100644 --- a/test/cases/generics.zig +++ b/test/cases/generics.zig @@ -81,11 +81,11 @@ test "function with return type type" { } test "generic struct" { - var a1 = GenNode(i32) { + var a1 = GenNode(i32){ .value = 13, .next = null, }; - var b1 = GenNode(bool) { + var b1 = GenNode(bool){ .value = true, .next = null, }; @@ -120,8 +120,8 @@ fn aGenericFn(comptime T: type, comptime a: T, b: T) T { } test "generic fn with implicit cast" { - assert(getFirstByte(u8, []u8 {13}) == 13); - assert(getFirstByte(u16, []u16 { + assert(getFirstByte(u8, []u8{13}) == 13); + assert(getFirstByte(u16, []u16{ 0, 13, }) == 0); @@ -133,7 +133,7 @@ fn getFirstByte(comptime T: type, mem: []const T) u8 { return getByte(@ptrCast(&const u8, &mem[0])); } -const foos = []fn(var) bool { +const foos = []fn(var) bool{ foo1, foo2, }; diff --git a/test/cases/incomplete_struct_param_tld.zig b/test/cases/incomplete_struct_param_tld.zig index a907ca748a..a2f57743d0 100644 --- a/test/cases/incomplete_struct_param_tld.zig +++ b/test/cases/incomplete_struct_param_tld.zig @@ -21,11 +21,9 @@ fn foo(a: &const A) i32 { } test "incomplete struct param top level declaration" { - const a = A { - .b = B { - .c = C { - .x = 13, - }, + const a = A{ + .b = B{ + .c = C{ .x = 13 }, }, }; assert(foo(a) == 13); diff --git a/test/cases/math.zig b/test/cases/math.zig index 3c6156a3ea..0b4622702f 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -197,7 +197,7 @@ fn test_u64_div() void { assert(result.remainder == 100663296); } fn divWithResult(a: u64, b: u64) DivResult { - return DivResult { + return DivResult{ .quotient = a / b, .remainder = a % b, }; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index deeeca8c3a..42de163ea5 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -232,7 +232,7 @@ test "string escapes" { } test "multiline string" { - const s1 = + const s1 = \\one \\two) \\three @@ -242,7 +242,7 @@ test "multiline string" { } test "multiline C string" { - const s1 = + const s1 = c\\one c\\two) c\\three @@ -350,15 +350,13 @@ const Test3Point = struct { x: i32, y: i32, }; -const test3_foo = Test3Foo { - .Three = Test3Point { +const test3_foo = Test3Foo{ + .Three = Test3Point{ .x = 3, .y = 4, }, }; -const test3_bar = Test3Foo { - .Two = 13, -}; +const test3_bar = Test3Foo{ .Two = 13 }; fn test3_1(f: &const Test3Foo) void { switch (f.*) { Test3Foo.Three => |pt| { @@ -417,7 +415,7 @@ test "C string concatenation" { test "cast slice to u8 slice" { assert(@sizeOf(i32) == 4); - var big_thing_array = []i32 { + var big_thing_array = []i32{ 1, 2, 3, @@ -458,9 +456,9 @@ test "non const ptr to aliased type" { } test "array 2D const double ptr" { - const rect_2d_vertexes = [][1]f32 { - []f32 {1.0}, - []f32 {2.0}, + const rect_2d_vertexes = [][1]f32{ + []f32{1.0}, + []f32{2.0}, }; testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]); } @@ -565,7 +563,7 @@ test "volatile load and store" { test "slice string literal has type []const u8" { comptime { assert(@typeOf("aoeu"[0..]) == []const u8); - const array = []i32 { + const array = []i32{ 1, 2, 3, @@ -581,13 +579,9 @@ test "global variable initialized to global variable array element" { const GDTEntry = struct { field: i32, }; -var gdt = []GDTEntry { - GDTEntry { - .field = 1, - }, - GDTEntry { - .field = 2, - }, +var gdt = []GDTEntry{ + GDTEntry{ .field = 1 }, + GDTEntry{ .field = 2 }, }; var global_ptr = &gdt[0]; @@ -648,9 +642,7 @@ fn testStructInFn() void { kind: BlockKind, }; - var block = Block { - .kind = 1234, - }; + var block = Block{ .kind = 1234 }; block.kind += 1; @@ -694,12 +686,10 @@ const PackedEnum = packed enum { }; test "packed struct, enum, union parameters in extern function" { - testPackedStuff(PackedStruct { + testPackedStuff(PackedStruct{ .a = 1, .b = 2, - }, PackedUnion { - .a = 1, - }, PackedEnum.A); + }, PackedUnion{ .a = 1 }, PackedEnum.A); } export fn testPackedStuff(a: &const PackedStruct, b: &const PackedUnion, c: PackedEnum) void {} diff --git a/test/cases/null.zig b/test/cases/null.zig index 96a62ab1ed..936e5fafbd 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -58,7 +58,7 @@ fn foo(x: ?i32) ?bool { } test "if var maybe pointer" { - assert(shouldBeAPlus1(Particle { + assert(shouldBeAPlus1(Particle{ .a = 14, .b = 1, .c = 1, @@ -92,9 +92,7 @@ test "null literal outside function" { const SillyStruct = struct { context: ?i32, }; -const here_is_a_null_literal = SillyStruct { - .context = null, -}; +const here_is_a_null_literal = SillyStruct{ .context = null }; test "test null runtime" { testTestNullRuntime(null); diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index f9b64c80eb..b82ce6340f 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -59,7 +59,7 @@ test "reflection: enum member types and names" { } test "reflection: @field" { - var f = Foo { + var f = Foo{ .one = 42, .two = true, .three = void{}, diff --git a/test/cases/slice.zig b/test/cases/slice.zig index 4ca194672c..eae6fa895e 100644 --- a/test/cases/slice.zig +++ b/test/cases/slice.zig @@ -18,7 +18,7 @@ test "slice child property" { } test "runtime safety lets us slice from len..len" { - var an_array = []u8 { + var an_array = []u8{ 1, 2, 3, diff --git a/test/cases/struct.zig b/test/cases/struct.zig index c474d99f2b..37b0e497f0 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -27,7 +27,7 @@ test "invake static method in global scope" { } test "void struct fields" { - const foo = VoidStructFieldsFoo { + const foo = VoidStructFieldsFoo{ .a = void{}, .b = 1, .c = void{}, @@ -96,16 +96,12 @@ test "struct byval assign" { } fn structInitializer() void { - const val = Val { - .x = 42, - }; + const val = Val{ .x = 42 }; assert(val.x == 42); } test "fn call of struct field" { - assert(callStructField(Foo { - .ptr = aFunc, - }) == 13); + assert(callStructField(Foo{ .ptr = aFunc }) == 13); } const Foo = struct { @@ -121,9 +117,7 @@ fn callStructField(foo: &const Foo) i32 { } test "store member function in variable" { - const instance = MemberFnTestFoo { - .x = 1234, - }; + const instance = MemberFnTestFoo{ .x = 1234 }; const memberFn = MemberFnTestFoo.member; const result = memberFn(instance); assert(result == 1234); @@ -136,17 +130,13 @@ const MemberFnTestFoo = struct { }; test "call member function directly" { - const instance = MemberFnTestFoo { - .x = 1234, - }; + const instance = MemberFnTestFoo{ .x = 1234 }; const result = MemberFnTestFoo.member(instance); assert(result == 1234); } test "member functions" { - const r = MemberFnRand { - .seed = 1234, - }; + const r = MemberFnRand{ .seed = 1234 }; assert(r.getSeed() == 1234); } const MemberFnRand = struct { @@ -165,7 +155,7 @@ const Bar = struct { y: i32, }; fn makeBar(x: i32, y: i32) Bar { - return Bar { + return Bar{ .x = x, .y = y, }; @@ -190,7 +180,7 @@ fn testReturnEmptyStructFromFn() EmptyStruct2 { } test "pass slice of empty struct to fn" { - assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2 {EmptyStruct2{}}) == 1); + assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); } fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { return slice.len; @@ -202,7 +192,7 @@ const APackedStruct = packed struct { }; test "packed struct" { - var foo = APackedStruct { + var foo = APackedStruct{ .x = 1, .y = 2, }; @@ -217,7 +207,7 @@ const BitField1 = packed struct { c: u2, }; -const bit_field_1 = BitField1 { +const bit_field_1 = BitField1{ .a = 1, .b = 2, .c = 3, @@ -267,7 +257,7 @@ test "packed struct 24bits" { assert(@sizeOf(Foo96Bits) == 12); } - var value = Foo96Bits { + var value = Foo96Bits{ .a = 0, .b = 0, .c = 0, @@ -310,7 +300,7 @@ test "packed array 24bits" { assert(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); } - var bytes = []u8 {0} ** (@sizeOf(FooArray24Bits) + 1); + var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); bytes[bytes.len - 1] = 0xaa; const ptr = &([]FooArray24Bits)(bytes[0..bytes.len - 1])[0]; assert(ptr.a == 0); @@ -360,7 +350,7 @@ test "aligned array of packed struct" { assert(@sizeOf(FooArrayOfAligned) == 2 * 2); } - var bytes = []u8 {0xbb} ** @sizeOf(FooArrayOfAligned); + var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); const ptr = &([]FooArrayOfAligned)(bytes[0..bytes.len])[0]; assert(ptr.a[0].a == 0xbb); @@ -370,11 +360,11 @@ test "aligned array of packed struct" { } test "runtime struct initialization of bitfield" { - const s1 = Nibbles { + const s1 = Nibbles{ .x = x1, .y = x1, }; - const s2 = Nibbles { + const s2 = Nibbles{ .x = u4(x2), .y = u4(x2), }; diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/cases/struct_contains_slice_of_itself.zig index ee34c16baf..07987ae32b 100644 --- a/test/cases/struct_contains_slice_of_itself.zig +++ b/test/cases/struct_contains_slice_of_itself.zig @@ -6,31 +6,31 @@ const Node = struct { }; test "struct contains slice of itself" { - var other_nodes = []Node { - Node { + var other_nodes = []Node{ + Node{ .payload = 31, .children = []Node{}, }, - Node { + Node{ .payload = 32, .children = []Node{}, }, }; - var nodes = []Node { - Node { + var nodes = []Node{ + Node{ .payload = 1, .children = []Node{}, }, - Node { + Node{ .payload = 2, .children = []Node{}, }, - Node { + Node{ .payload = 3, .children = other_nodes[0..], }, }; - const root = Node { + const root = Node{ .payload = 1234, .children = nodes[0..], }; diff --git a/test/cases/switch.zig b/test/cases/switch.zig index b870297f18..495fa9f3ed 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -6,10 +6,7 @@ test "switch with numbers" { fn testSwitchWithNumbers(x: u32) void { const result = switch (x) { - 1, - 2, - 3, - 4 ... 8 => false, + 1, 2, 3, 4...8 => false, 13 => true, else => false, }; @@ -25,9 +22,9 @@ test "switch with all ranges" { fn testSwitchWithAllRanges(x: u32, y: u32) u32 { return switch (x) { - 0 ... 100 => 1, - 101 ... 200 => 2, - 201 ... 300 => 3, + 0...100 => 1, + 101...200 => 2, + 201...300 => 3, else => y, }; } @@ -37,10 +34,8 @@ test "implicit comptime switch" { const result = switch (x) { 3 => 10, 4 => 11, - 5, - 6 => 12, - 7, - 8 => 13, + 5, 6 => 12, + 7, 8 => 13, else => 14, }; @@ -86,15 +81,9 @@ const SwitchStatmentFoo = enum { }; test "switch prong with variable" { - switchProngWithVarFn(SwitchProngWithVarEnum { - .One = 13, - }); - switchProngWithVarFn(SwitchProngWithVarEnum { - .Two = 13.0, - }); - switchProngWithVarFn(SwitchProngWithVarEnum { - .Meh = {}, - }); + switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 }); + switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 }); + switchProngWithVarFn(SwitchProngWithVarEnum{ .Meh = {} }); } const SwitchProngWithVarEnum = union(enum) { One: i32, @@ -121,9 +110,7 @@ test "switch on enum using pointer capture" { } fn testSwitchEnumPtrCapture() void { - var value = SwitchProngWithVarEnum { - .One = 1234, - }; + var value = SwitchProngWithVarEnum{ .One = 1234 }; switch (value) { SwitchProngWithVarEnum.One => |*x| x.* += 1, else => unreachable, @@ -136,12 +123,8 @@ fn testSwitchEnumPtrCapture() void { test "switch with multiple expressions" { const x = switch (returnsFive()) { - 1, - 2, - 3 => 1, - 4, - 5, - 6 => 2, + 1, 2, 3 => 1, + 4, 5, 6 => 2, else => i32(3), }; assert(x == 2); @@ -156,9 +139,7 @@ const Number = union(enum) { Three: f32, }; -const number = Number { - .Three = 1.23, -}; +const number = Number{ .Three = 1.23 }; fn returnsFalse() bool { switch (number) { @@ -212,12 +193,11 @@ fn testSwitchHandleAllCasesExhaustive(x: u2) u2 { fn testSwitchHandleAllCasesRange(x: u8) u8 { return switch (x) { - 0 ... 100 => u8(0), - 101 ... 200 => 1, - 201, - 203 => 2, + 0...100 => u8(0), + 101...200 => 1, + 201, 203 => 2, 202 => 4, - 204 ... 255 => 3, + 204...255 => 3, }; } diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig index 2d28d2f4c7..f060ac2c57 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/cases/switch_prong_err_enum.zig @@ -14,9 +14,7 @@ const FormValue = union(enum) { fn doThing(form_id: u64) error!FormValue { return switch (form_id) { - 17 => FormValue { - .Address = try readOnce(), - }, + 17 => FormValue{ .Address = try readOnce() }, else => error.InvalidDebugInfo, }; } diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/cases/switch_prong_implicit_cast.zig index 3d80f3fdb2..56d37e290f 100644 --- a/test/cases/switch_prong_implicit_cast.zig +++ b/test/cases/switch_prong_implicit_cast.zig @@ -7,12 +7,8 @@ const FormValue = union(enum) { fn foo(id: u64) !FormValue { return switch (id) { - 2 => FormValue { - .Two = true, - }, - 1 => FormValue { - .One = {}, - }, + 2 => FormValue{ .Two = true }, + 1 => FormValue{ .One = {} }, else => return error.Whatever, }; } diff --git a/test/cases/this.zig b/test/cases/this.zig index 8ed5e1ae1a..5e433b5037 100644 --- a/test/cases/this.zig +++ b/test/cases/this.zig @@ -29,7 +29,7 @@ test "this refer to module call private fn" { } test "this refer to container" { - var pt = Point(i32) { + var pt = Point(i32){ .x = 12, .y = 34, }; diff --git a/test/cases/try.zig b/test/cases/try.zig index 483bf6a915..cf5fa5862a 100644 --- a/test/cases/try.zig +++ b/test/cases/try.zig @@ -7,8 +7,7 @@ test "try on error union" { fn tryOnErrorUnionImpl() void { const x = if (returnsTen()) |val| val + 1 else |err| switch (err) { - error.ItBroke, - error.NoMem => 1, + error.ItBroke, error.NoMem => 1, error.CrappedOut => i32(2), else => unreachable, }; diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index 7bf1e68180..eee5d1f2ca 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -103,7 +103,7 @@ test "type info: error set, error union info" { } fn testErrorSet() void { - const TestErrorSet = error { + const TestErrorSet = error{ First, Second, Third, @@ -196,7 +196,7 @@ fn testStruct() void { assert(!struct_info.Struct.defs[0].data.Fn.is_extern); assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); assert(struct_info.Struct.defs[0].data.Fn.return_type == void); - assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn(&const TestStruct)void); + assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn(&const TestStruct) void); } const TestStruct = packed struct { diff --git a/test/cases/union.zig b/test/cases/union.zig index 93b5f740be..005ad08e6a 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -50,10 +50,10 @@ test "basic unions" { test "comptime union field access" { comptime { - var foo = Foo { .int = 0 }; + var foo = Foo{ .int = 0 }; assert(foo.int == 0); - foo = Foo { .float = 42.42 }; + foo = Foo{ .float = 42.42 }; assert(foo.float == 42.42); } } @@ -286,7 +286,6 @@ const PartialInstWithPayload = union(enum) { Compiled: i32, }; - test "access a member of tagged union with conflicting enum tag name" { const Bar = union(enum) { A: A, diff --git a/test/cases/var_args.zig b/test/cases/var_args.zig index 81f800568c..ec4d2059f3 100644 --- a/test/cases/var_args.zig +++ b/test/cases/var_args.zig @@ -58,7 +58,7 @@ fn extraFn(extra: u32, args: ...) usize { return args.len; } -const foos = []fn(...) bool { +const foos = []fn(...) bool{ foo1, foo2, }; diff --git a/test/cases/void.zig b/test/cases/void.zig index f4d72209e4..ef91690878 100644 --- a/test/cases/void.zig +++ b/test/cases/void.zig @@ -8,7 +8,7 @@ const Foo = struct { test "compare void with void compile time known" { comptime { - const foo = Foo { + const foo = Foo{ .a = {}, .b = 1, .c = {}, diff --git a/test/cases/while.zig b/test/cases/while.zig index 574a7b7e76..a95481668d 100644 --- a/test/cases/while.zig +++ b/test/cases/while.zig @@ -151,7 +151,7 @@ test "while on nullable with else result follow break prong" { test "while on error union with else result follow else prong" { const result = while (returnError()) |value| { break value; - } else|err| + } else |err| i32(2); assert(result == 2); } @@ -159,7 +159,7 @@ test "while on error union with else result follow else prong" { test "while on error union with else result follow break prong" { const result = while (returnSuccess(10)) |value| { break value; - } else|err| + } else |err| i32(2); assert(result == 10); } diff --git a/test/compare_output.zig b/test/compare_output.zig index 905ffd37a9..0170477b8b 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -475,7 +475,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ ); - tc.setCommandLineArgs([][]const u8 { + tc.setCommandLineArgs([][]const u8{ "first arg", "'a' 'b' \\", "bare", @@ -516,7 +516,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ ); - tc.setCommandLineArgs([][]const u8 { + tc.setCommandLineArgs([][]const u8{ "first arg", "'a' 'b' \\", "bare", diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 904ba6d9d8..5215953d0a 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,7 +1,8 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { - cases.add("invalid deref on switch target", + cases.add( + "invalid deref on switch target", \\comptime { \\ var tile = Tile.Empty; \\ switch (tile.*) { @@ -14,15 +15,19 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ Filled, \\}; , - ".tmp_source.zig:3:17: error: invalid deref on switch target"); + ".tmp_source.zig:3:17: error: invalid deref on switch target", + ); - cases.add("invalid field access in comptime", + cases.add( + "invalid field access in comptime", \\comptime { var x = doesnt_exist.whatever; } , - ".tmp_source.zig:1:20: error: use of undeclared identifier 'doesnt_exist'"); + ".tmp_source.zig:1:20: error: use of undeclared identifier 'doesnt_exist'", + ); - cases.add("suspend inside suspend block", - \\const std = @import("std"); + cases.add( + "suspend inside suspend block", + \\const std = @import("std",); \\ \\export fn entry() void { \\ var buf: [500]u8 = undefined; @@ -39,27 +44,32 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:12:9: error: cannot suspend inside suspend block", - ".tmp_source.zig:11:5: note: other suspend block here"); + ".tmp_source.zig:11:5: note: other suspend block here", + ); - cases.add("assign inline fn to non-comptime var", + cases.add( + "assign inline fn to non-comptime var", \\export fn entry() void { \\ var a = b; \\} \\inline fn b() void { } , ".tmp_source.zig:2:5: error: functions marked inline must be stored in const or comptime var", - ".tmp_source.zig:4:8: note: declared here"); + ".tmp_source.zig:4:8: note: declared here", + ); - cases.add("wrong type passed to @panic", + cases.add( + "wrong type passed to @panic", \\export fn entry() void { \\ var e = error.Foo; \\ @panic(e); \\} , - ".tmp_source.zig:3:12: error: expected type '[]const u8', found 'error{Foo}'"); + ".tmp_source.zig:3:12: error: expected type '[]const u8', found 'error{Foo}'", + ); - - cases.add("@tagName used on union with no associated enum tag", + cases.add( + "@tagName used on union with no associated enum tag", \\const FloatInt = extern union { \\ Float: f32, \\ Int: i32, @@ -70,10 +80,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:7:19: error: union has no associated enum", - ".tmp_source.zig:1:18: note: declared here"); + ".tmp_source.zig:1:18: note: declared here", + ); - cases.add("returning error from void async function", - \\const std = @import("std"); + cases.add( + "returning error from void async function", + \\const std = @import("std",); \\export fn entry() void { \\ const p = async amain() catch unreachable; \\} @@ -81,31 +93,39 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ return error.ShouldBeCompileError; \\} , - ".tmp_source.zig:6:17: error: expected type 'void', found 'error{ShouldBeCompileError}'"); + ".tmp_source.zig:6:17: error: expected type 'void', found 'error{ShouldBeCompileError}'", + ); - cases.add("var not allowed in structs", + cases.add( + "var not allowed in structs", \\export fn entry() void { \\ var s = (struct{v: var}){.v=i32(10)}; \\} , - ".tmp_source.zig:2:23: error: invalid token: 'var'"); + ".tmp_source.zig:2:23: error: invalid token: 'var'", + ); - cases.add("@ptrCast discards const qualifier", + cases.add( + "@ptrCast discards const qualifier", \\export fn entry() void { \\ const x: i32 = 1234; \\ const y = @ptrCast(&i32, &x); \\} , - ".tmp_source.zig:3:15: error: cast discards const qualifier"); + ".tmp_source.zig:3:15: error: cast discards const qualifier", + ); - cases.add("comptime slice of undefined pointer non-zero len", + cases.add( + "comptime slice of undefined pointer non-zero len", \\export fn entry() void { \\ const slice = (&i32)(undefined)[0..1]; \\} , - ".tmp_source.zig:2:36: error: non-zero length slice of undefined pointer"); + ".tmp_source.zig:2:36: error: non-zero length slice of undefined pointer", + ); - cases.add("type checking function pointers", + cases.add( + "type checking function pointers", \\fn a(b: fn (&const u8) void) void { \\ b('a'); \\} @@ -116,9 +136,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ a(c); \\} , - ".tmp_source.zig:8:7: error: expected type 'fn(&const u8) void', found 'fn(u8) void'"); + ".tmp_source.zig:8:7: error: expected type 'fn(&const u8) void', found 'fn(u8) void'", + ); - cases.add("no else prong on switch on global error set", + cases.add( + "no else prong on switch on global error set", \\export fn entry() void { \\ foo(error.A); \\} @@ -128,18 +150,22 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ } \\} , - ".tmp_source.zig:5:5: error: else prong required when switching on type 'error'"); + ".tmp_source.zig:5:5: error: else prong required when switching on type 'error'", + ); - cases.add("inferred error set with no returned error", + cases.add( + "inferred error set with no returned error", \\export fn entry() void { \\ foo() catch unreachable; \\} \\fn foo() !void { \\} , - ".tmp_source.zig:4:11: error: function with inferred error set must return at least one possible error"); + ".tmp_source.zig:4:11: error: function with inferred error set must return at least one possible error", + ); - cases.add("error not handled in switch", + cases.add( + "error not handled in switch", \\export fn entry() void { \\ foo(452) catch |err| switch (err) { \\ error.Foo => {}, @@ -155,9 +181,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:2:26: error: error.Baz not handled in switch", - ".tmp_source.zig:2:26: error: error.Bar not handled in switch"); + ".tmp_source.zig:2:26: error: error.Bar not handled in switch", + ); - cases.add("duplicate error in switch", + cases.add( + "duplicate error in switch", \\export fn entry() void { \\ foo(452) catch |err| switch (err) { \\ error.Foo => {}, @@ -175,9 +203,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:5:14: error: duplicate switch value: '@typeOf(foo).ReturnType.ErrorSet.Foo'", - ".tmp_source.zig:3:14: note: other value is here"); + ".tmp_source.zig:3:14: note: other value is here", + ); - cases.add("range operator in switch used on error set", + cases.add( + "range operator in switch used on error set", \\export fn entry() void { \\ try foo(452) catch |err| switch (err) { \\ error.A ... error.B => {}, @@ -192,31 +222,39 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ } \\} , - ".tmp_source.zig:3:17: error: operator not allowed for errors"); + ".tmp_source.zig:3:17: error: operator not allowed for errors", + ); - cases.add("inferring error set of function pointer", + cases.add( + "inferring error set of function pointer", \\comptime { \\ const z: ?fn()!void = null; \\} , - ".tmp_source.zig:2:15: error: inferring error set of return type valid only for function definitions"); + ".tmp_source.zig:2:15: error: inferring error set of return type valid only for function definitions", + ); - cases.add("access non-existent member of error set", + cases.add( + "access non-existent member of error set", \\const Foo = error{A}; \\comptime { \\ const z = Foo.Bar; \\} , - ".tmp_source.zig:3:18: error: no error named 'Bar' in 'Foo'"); + ".tmp_source.zig:3:18: error: no error named 'Bar' in 'Foo'", + ); - cases.add("error union operator with non error set LHS", + cases.add( + "error union operator with non error set LHS", \\comptime { \\ const z = i32!i32; \\} , - ".tmp_source.zig:2:15: error: expected error set type, found type 'i32'"); + ".tmp_source.zig:2:15: error: expected error set type, found type 'i32'", + ); - cases.add("error equality but sets have no common members", + cases.add( + "error equality but sets have no common members", \\const Set1 = error{A, C}; \\const Set2 = error{B, D}; \\export fn entry() void { @@ -228,16 +266,20 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ } \\} , - ".tmp_source.zig:7:11: error: error sets 'Set1' and 'Set2' have no common errors"); + ".tmp_source.zig:7:11: error: error sets 'Set1' and 'Set2' have no common errors", + ); - cases.add("only equality binary operator allowed for error sets", + cases.add( + "only equality binary operator allowed for error sets", \\comptime { \\ const z = error.A > error.B; \\} , - ".tmp_source.zig:2:23: error: operator not allowed for errors"); + ".tmp_source.zig:2:23: error: operator not allowed for errors", + ); - cases.add("explicit error set cast known at comptime violates error sets", + cases.add( + "explicit error set cast known at comptime violates error sets", \\const Set1 = error {A, B}; \\const Set2 = error {A, C}; \\comptime { @@ -245,9 +287,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var y = Set2(x); \\} , - ".tmp_source.zig:5:17: error: error.B not a member of error set 'Set2'"); + ".tmp_source.zig:5:17: error: error.B not a member of error set 'Set2'", + ); - cases.add("cast error union of global error set to error union of smaller error set", + cases.add( + "cast error union of global error set to error union of smaller error set", \\const SmallErrorSet = error{A}; \\export fn entry() void { \\ var x: SmallErrorSet!i32 = foo(); @@ -257,9 +301,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:3:35: error: expected 'SmallErrorSet!i32', found 'error!i32'", - ".tmp_source.zig:3:35: note: unable to cast global error set into smaller set"); + ".tmp_source.zig:3:35: note: unable to cast global error set into smaller set", + ); - cases.add("cast global error set to error set", + cases.add( + "cast global error set to error set", \\const SmallErrorSet = error{A}; \\export fn entry() void { \\ var x: SmallErrorSet = foo(); @@ -269,9 +315,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:3:31: error: expected 'SmallErrorSet', found 'error'", - ".tmp_source.zig:3:31: note: unable to cast global error set into smaller set"); + ".tmp_source.zig:3:31: note: unable to cast global error set into smaller set", + ); - cases.add("recursive inferred error set", + cases.add( + "recursive inferred error set", \\export fn entry() void { \\ foo() catch unreachable; \\} @@ -279,9 +327,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ try foo(); \\} , - ".tmp_source.zig:5:5: error: cannot resolve inferred error set '@typeOf(foo).ReturnType.ErrorSet': function 'foo' not fully analyzed yet"); + ".tmp_source.zig:5:5: error: cannot resolve inferred error set '@typeOf(foo).ReturnType.ErrorSet': function 'foo' not fully analyzed yet", + ); - cases.add("implicit cast of error set not a subset", + cases.add( + "implicit cast of error set not a subset", \\const Set1 = error{A, B}; \\const Set2 = error{A, C}; \\export fn entry() void { @@ -292,18 +342,22 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:7:19: error: expected 'Set2', found 'Set1'", - ".tmp_source.zig:1:23: note: 'error.B' not a member of destination error set"); + ".tmp_source.zig:1:23: note: 'error.B' not a member of destination error set", + ); - cases.add("int to err global invalid number", + cases.add( + "int to err global invalid number", \\const Set1 = error{A, B}; \\comptime { \\ var x: usize = 3; \\ var y = error(x); \\} , - ".tmp_source.zig:4:18: error: integer value 3 represents no error"); + ".tmp_source.zig:4:18: error: integer value 3 represents no error", + ); - cases.add("int to err non global invalid number", + cases.add( + "int to err non global invalid number", \\const Set1 = error{A, B}; \\const Set2 = error{A, C}; \\comptime { @@ -311,16 +365,20 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var y = Set2(x); \\} , - ".tmp_source.zig:5:17: error: integer value 2 represents no error in 'Set2'"); + ".tmp_source.zig:5:17: error: integer value 2 represents no error in 'Set2'", + ); - cases.add("@memberCount of error", + cases.add( + "@memberCount of error", \\comptime { \\ _ = @memberCount(error); \\} , - ".tmp_source.zig:2:9: error: global error set member count not available at comptime"); + ".tmp_source.zig:2:9: error: global error set member count not available at comptime", + ); - cases.add("duplicate error value in error set", + cases.add( + "duplicate error value in error set", \\const Foo = error { \\ Bar, \\ Bar, @@ -330,22 +388,30 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:3:5: error: duplicate error: 'Bar'", - ".tmp_source.zig:2:5: note: other error here"); + ".tmp_source.zig:2:5: note: other error here", + ); - cases.add("cast negative integer literal to usize", + cases.add( + "cast negative integer literal to usize", \\export fn entry() void { \\ const x = usize(-10); \\} - , ".tmp_source.zig:2:21: error: cannot cast negative value -10 to unsigned integer type 'usize'"); + , + ".tmp_source.zig:2:21: error: cannot cast negative value -10 to unsigned integer type 'usize'", + ); - cases.add("use invalid number literal as array index", + cases.add( + "use invalid number literal as array index", \\var v = 25; \\export fn entry() void { \\ var arr: [v]u8 = undefined; \\} - , ".tmp_source.zig:1:1: error: unable to infer variable type"); + , + ".tmp_source.zig:1:1: error: unable to infer variable type", + ); - cases.add("duplicate struct field", + cases.add( + "duplicate struct field", \\const Foo = struct { \\ Bar: i32, \\ Bar: usize, @@ -355,9 +421,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:3:5: error: duplicate struct field: 'Bar'", - ".tmp_source.zig:2:5: note: other field here"); + ".tmp_source.zig:2:5: note: other field here", + ); - cases.add("duplicate union field", + cases.add( + "duplicate union field", \\const Foo = union { \\ Bar: i32, \\ Bar: usize, @@ -367,9 +435,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:3:5: error: duplicate union field: 'Bar'", - ".tmp_source.zig:2:5: note: other field here"); + ".tmp_source.zig:2:5: note: other field here", + ); - cases.add("duplicate enum field", + cases.add( + "duplicate enum field", \\const Foo = enum { \\ Bar, \\ Bar, @@ -380,77 +450,108 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:3:5: error: duplicate enum field: 'Bar'", - ".tmp_source.zig:2:5: note: other field here"); + ".tmp_source.zig:2:5: note: other field here", + ); - cases.add("calling function with naked calling convention", + cases.add( + "calling function with naked calling convention", \\export fn entry() void { \\ foo(); \\} \\nakedcc fn foo() void { } , ".tmp_source.zig:2:5: error: unable to call function with naked calling convention", - ".tmp_source.zig:4:9: note: declared here"); + ".tmp_source.zig:4:9: note: declared here", + ); - cases.add("function with invalid return type", + cases.add( + "function with invalid return type", \\export fn foo() boid {} - , ".tmp_source.zig:1:17: error: use of undeclared identifier 'boid'"); + , + ".tmp_source.zig:1:17: error: use of undeclared identifier 'boid'", + ); - cases.add("function with non-extern non-packed enum parameter", + cases.add( + "function with non-extern non-packed enum parameter", \\const Foo = enum { A, B, C }; \\export fn entry(foo: Foo) void { } - , ".tmp_source.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'"); + , + ".tmp_source.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'", + ); - cases.add("function with non-extern non-packed struct parameter", + cases.add( + "function with non-extern non-packed struct parameter", \\const Foo = struct { \\ A: i32, \\ B: f32, \\ C: bool, \\}; \\export fn entry(foo: Foo) void { } - , ".tmp_source.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'"); + , + ".tmp_source.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'", + ); - cases.add("function with non-extern non-packed union parameter", + cases.add( + "function with non-extern non-packed union parameter", \\const Foo = union { \\ A: i32, \\ B: f32, \\ C: bool, \\}; \\export fn entry(foo: Foo) void { } - , ".tmp_source.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'"); + , + ".tmp_source.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'", + ); - cases.add("switch on enum with 1 field with no prongs", + cases.add( + "switch on enum with 1 field with no prongs", \\const Foo = enum { M }; \\ \\export fn entry() void { \\ var f = Foo.M; \\ switch (f) {} \\} - , ".tmp_source.zig:5:5: error: enumeration value 'Foo.M' not handled in switch"); + , + ".tmp_source.zig:5:5: error: enumeration value 'Foo.M' not handled in switch", + ); - cases.add("shift by negative comptime integer", + cases.add( + "shift by negative comptime integer", \\comptime { \\ var a = 1 >> -1; \\} - , ".tmp_source.zig:2:18: error: shift by negative value -1"); + , + ".tmp_source.zig:2:18: error: shift by negative value -1", + ); - cases.add("@panic called at compile time", + cases.add( + "@panic called at compile time", \\export fn entry() void { \\ comptime { - \\ @panic("aoeu"); + \\ @panic("aoeu",); \\ } \\} - , ".tmp_source.zig:3:9: error: encountered @panic at compile-time"); + , + ".tmp_source.zig:3:9: error: encountered @panic at compile-time", + ); - cases.add("wrong return type for main", + cases.add( + "wrong return type for main", \\pub fn main() f32 { } - , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"); + , + "error: expected return type of main to be 'u8', 'noreturn', 'void', or '!void'", + ); - cases.add("double ?? on main return value", + cases.add( + "double ?? on main return value", \\pub fn main() ??void { \\} - , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"); + , + "error: expected return type of main to be 'u8', 'noreturn', 'void', or '!void'", + ); - cases.add("bad identifier in function with struct defined inside function which references local const", + cases.add( + "bad identifier in function with struct defined inside function which references local const", \\export fn entry() void { \\ const BlockKind = u32; \\ @@ -460,9 +561,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\ bogus; \\} - , ".tmp_source.zig:8:5: error: use of undeclared identifier 'bogus'"); + , + ".tmp_source.zig:8:5: error: use of undeclared identifier 'bogus'", + ); - cases.add("labeled break not found", + cases.add( + "labeled break not found", \\export fn entry() void { \\ blah: while (true) { \\ while (true) { @@ -470,9 +574,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ } \\ } \\} - , ".tmp_source.zig:4:13: error: label not found: 'outer'"); + , + ".tmp_source.zig:4:13: error: label not found: 'outer'", + ); - cases.add("labeled continue not found", + cases.add( + "labeled continue not found", \\export fn entry() void { \\ var i: usize = 0; \\ blah: while (i < 10) : (i += 1) { @@ -481,9 +588,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ } \\ } \\} - , ".tmp_source.zig:5:13: error: labeled loop not found: 'outer'"); + , + ".tmp_source.zig:5:13: error: labeled loop not found: 'outer'", + ); - cases.add("attempt to use 0 bit type in extern fn", + cases.add( + "attempt to use 0 bit type in extern fn", \\extern fn foo(ptr: extern fn(&void) void) void; \\ \\export fn entry() void { @@ -491,390 +601,541 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\extern fn bar(x: &void) void { } - , ".tmp_source.zig:7:18: error: parameter of type '&void' has 0 bits; not allowed in function with calling convention 'ccc'"); + , + ".tmp_source.zig:7:18: error: parameter of type '&void' has 0 bits; not allowed in function with calling convention 'ccc'", + ); - cases.add("implicit semicolon - block statement", + cases.add( + "implicit semicolon - block statement", \\export fn entry() void { \\ {} \\ var good = {}; \\ ({}) \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - block expr", + cases.add( + "implicit semicolon - block expr", \\export fn entry() void { \\ _ = {}; \\ var good = {}; \\ _ = {} \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - comptime statement", + cases.add( + "implicit semicolon - comptime statement", \\export fn entry() void { \\ comptime {} \\ var good = {}; \\ comptime ({}) \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - comptime expression", + cases.add( + "implicit semicolon - comptime expression", \\export fn entry() void { \\ _ = comptime {}; \\ var good = {}; \\ _ = comptime {} \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - defer", + cases.add( + "implicit semicolon - defer", \\export fn entry() void { \\ defer {} \\ var good = {}; \\ defer ({}) \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - if statement", + cases.add( + "implicit semicolon - if statement", \\export fn entry() void { \\ if(true) {} \\ var good = {}; \\ if(true) ({}) \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - if expression", + cases.add( + "implicit semicolon - if expression", \\export fn entry() void { \\ _ = if(true) {}; \\ var good = {}; \\ _ = if(true) {} \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - if-else statement", + cases.add( + "implicit semicolon - if-else statement", \\export fn entry() void { \\ if(true) {} else {} \\ var good = {}; \\ if(true) ({}) else ({}) \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - if-else expression", + cases.add( + "implicit semicolon - if-else expression", \\export fn entry() void { \\ _ = if(true) {} else {}; \\ var good = {}; \\ _ = if(true) {} else {} \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - if-else-if statement", + cases.add( + "implicit semicolon - if-else-if statement", \\export fn entry() void { \\ if(true) {} else if(true) {} \\ var good = {}; \\ if(true) ({}) else if(true) ({}) \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - if-else-if expression", + cases.add( + "implicit semicolon - if-else-if expression", \\export fn entry() void { \\ _ = if(true) {} else if(true) {}; \\ var good = {}; \\ _ = if(true) {} else if(true) {} \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - if-else-if-else statement", + cases.add( + "implicit semicolon - if-else-if-else statement", \\export fn entry() void { \\ if(true) {} else if(true) {} else {} \\ var good = {}; \\ if(true) ({}) else if(true) ({}) else ({}) \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - if-else-if-else expression", + cases.add( + "implicit semicolon - if-else-if-else expression", \\export fn entry() void { \\ _ = if(true) {} else if(true) {} else {}; \\ var good = {}; \\ _ = if(true) {} else if(true) {} else {} \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - test statement", + cases.add( + "implicit semicolon - test statement", \\export fn entry() void { \\ if (foo()) |_| {} \\ var good = {}; \\ if (foo()) |_| ({}) \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - test expression", + cases.add( + "implicit semicolon - test expression", \\export fn entry() void { \\ _ = if (foo()) |_| {}; \\ var good = {}; \\ _ = if (foo()) |_| {} \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - while statement", + cases.add( + "implicit semicolon - while statement", \\export fn entry() void { \\ while(true) {} \\ var good = {}; \\ while(true) ({}) \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - while expression", + cases.add( + "implicit semicolon - while expression", \\export fn entry() void { \\ _ = while(true) {}; \\ var good = {}; \\ _ = while(true) {} \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - while-continue statement", + cases.add( + "implicit semicolon - while-continue statement", \\export fn entry() void { \\ while(true):({}) {} \\ var good = {}; \\ while(true):({}) ({}) \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - while-continue expression", + cases.add( + "implicit semicolon - while-continue expression", \\export fn entry() void { \\ _ = while(true):({}) {}; \\ var good = {}; \\ _ = while(true):({}) {} \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - for statement", + cases.add( + "implicit semicolon - for statement", \\export fn entry() void { \\ for(foo()) {} \\ var good = {}; \\ for(foo()) ({}) \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("implicit semicolon - for expression", + cases.add( + "implicit semicolon - for expression", \\export fn entry() void { \\ _ = for(foo()) {}; \\ var good = {}; \\ _ = for(foo()) {} \\ var bad = {}; \\} - , ".tmp_source.zig:5:5: error: expected token ';', found 'var'"); + , + ".tmp_source.zig:5:5: error: expected token ';', found 'var'", + ); - cases.add("multiple function definitions", + cases.add( + "multiple function definitions", \\fn a() void {} \\fn a() void {} \\export fn entry() void { a(); } - , ".tmp_source.zig:2:1: error: redefinition of 'a'"); + , + ".tmp_source.zig:2:1: error: redefinition of 'a'", + ); - cases.add("unreachable with return", + cases.add( + "unreachable with return", \\fn a() noreturn {return;} \\export fn entry() void { a(); } - , ".tmp_source.zig:1:18: error: expected type 'noreturn', found 'void'"); + , + ".tmp_source.zig:1:18: error: expected type 'noreturn', found 'void'", + ); - cases.add("control reaches end of non-void function", + cases.add( + "control reaches end of non-void function", \\fn a() i32 {} \\export fn entry() void { _ = a(); } - , ".tmp_source.zig:1:12: error: expected type 'i32', found 'void'"); + , + ".tmp_source.zig:1:12: error: expected type 'i32', found 'void'", + ); - cases.add("undefined function call", + cases.add( + "undefined function call", \\export fn a() void { \\ b(); \\} - , ".tmp_source.zig:2:5: error: use of undeclared identifier 'b'"); + , + ".tmp_source.zig:2:5: error: use of undeclared identifier 'b'", + ); - cases.add("wrong number of arguments", + cases.add( + "wrong number of arguments", \\export fn a() void { \\ b(1); \\} \\fn b(a: i32, b: i32, c: i32) void { } - , ".tmp_source.zig:2:6: error: expected 3 arguments, found 1"); + , + ".tmp_source.zig:2:6: error: expected 3 arguments, found 1", + ); - cases.add("invalid type", + cases.add( + "invalid type", \\fn a() bogus {} \\export fn entry() void { _ = a(); } - , ".tmp_source.zig:1:8: error: use of undeclared identifier 'bogus'"); + , + ".tmp_source.zig:1:8: error: use of undeclared identifier 'bogus'", + ); - cases.add("pointer to noreturn", + cases.add( + "pointer to noreturn", \\fn a() &noreturn {} \\export fn entry() void { _ = a(); } - , ".tmp_source.zig:1:9: error: pointer to noreturn not allowed"); + , + ".tmp_source.zig:1:9: error: pointer to noreturn not allowed", + ); - cases.add("unreachable code", + cases.add( + "unreachable code", \\export fn a() void { \\ return; \\ b(); \\} \\ \\fn b() void {} - , ".tmp_source.zig:3:5: error: unreachable code"); + , + ".tmp_source.zig:3:5: error: unreachable code", + ); - cases.add("bad import", - \\const bogus = @import("bogus-does-not-exist.zig"); + cases.add( + "bad import", + \\const bogus = @import("bogus-does-not-exist.zig",); \\export fn entry() void { bogus.bogo(); } - , ".tmp_source.zig:1:15: error: unable to find 'bogus-does-not-exist.zig'"); + , + ".tmp_source.zig:1:15: error: unable to find 'bogus-does-not-exist.zig'", + ); - cases.add("undeclared identifier", + cases.add( + "undeclared identifier", \\export fn a() void { \\ return \\ b + \\ c; \\} , - ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'", - ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'"); + ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'", + ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'", + ); - cases.add("parameter redeclaration", + cases.add( + "parameter redeclaration", \\fn f(a : i32, a : i32) void { \\} \\export fn entry() void { f(1, 2); } - , ".tmp_source.zig:1:15: error: redeclaration of variable 'a'"); + , + ".tmp_source.zig:1:15: error: redeclaration of variable 'a'", + ); - cases.add("local variable redeclaration", + cases.add( + "local variable redeclaration", \\export fn f() void { \\ const a : i32 = 0; \\ const a = 0; \\} - , ".tmp_source.zig:3:5: error: redeclaration of variable 'a'"); + , + ".tmp_source.zig:3:5: error: redeclaration of variable 'a'", + ); - cases.add("local variable redeclares parameter", + cases.add( + "local variable redeclares parameter", \\fn f(a : i32) void { \\ const a = 0; \\} \\export fn entry() void { f(1); } - , ".tmp_source.zig:2:5: error: redeclaration of variable 'a'"); + , + ".tmp_source.zig:2:5: error: redeclaration of variable 'a'", + ); - cases.add("variable has wrong type", + cases.add( + "variable has wrong type", \\export fn f() i32 { \\ const a = c"a"; \\ return a; \\} - , ".tmp_source.zig:3:12: error: expected type 'i32', found '&const u8'"); + , + ".tmp_source.zig:3:12: error: expected type 'i32', found '&const u8'", + ); - cases.add("if condition is bool, not int", + cases.add( + "if condition is bool, not int", \\export fn f() void { \\ if (0) {} \\} - , ".tmp_source.zig:2:9: error: integer value 0 cannot be implicitly casted to type 'bool'"); + , + ".tmp_source.zig:2:9: error: integer value 0 cannot be implicitly casted to type 'bool'", + ); - cases.add("assign unreachable", + cases.add( + "assign unreachable", \\export fn f() void { \\ const a = return; \\} - , ".tmp_source.zig:2:5: error: unreachable code"); + , + ".tmp_source.zig:2:5: error: unreachable code", + ); - cases.add("unreachable variable", + cases.add( + "unreachable variable", \\export fn f() void { \\ const a: noreturn = {}; \\} - , ".tmp_source.zig:2:14: error: variable of type 'noreturn' not allowed"); + , + ".tmp_source.zig:2:14: error: variable of type 'noreturn' not allowed", + ); - cases.add("unreachable parameter", + cases.add( + "unreachable parameter", \\fn f(a: noreturn) void {} \\export fn entry() void { f(); } - , ".tmp_source.zig:1:9: error: parameter of type 'noreturn' not allowed"); + , + ".tmp_source.zig:1:9: error: parameter of type 'noreturn' not allowed", + ); - cases.add("bad assignment target", + cases.add( + "bad assignment target", \\export fn f() void { \\ 3 = 3; \\} - , ".tmp_source.zig:2:7: error: cannot assign to constant"); + , + ".tmp_source.zig:2:7: error: cannot assign to constant", + ); - cases.add("assign to constant variable", + cases.add( + "assign to constant variable", \\export fn f() void { \\ const a = 3; \\ a = 4; \\} - , ".tmp_source.zig:3:7: error: cannot assign to constant"); + , + ".tmp_source.zig:3:7: error: cannot assign to constant", + ); - cases.add("use of undeclared identifier", + cases.add( + "use of undeclared identifier", \\export fn f() void { \\ b = 3; \\} - , ".tmp_source.zig:2:5: error: use of undeclared identifier 'b'"); + , + ".tmp_source.zig:2:5: error: use of undeclared identifier 'b'", + ); - cases.add("const is a statement, not an expression", + cases.add( + "const is a statement, not an expression", \\export fn f() void { \\ (const a = 0); \\} - , ".tmp_source.zig:2:6: error: invalid token: 'const'"); + , + ".tmp_source.zig:2:6: error: invalid token: 'const'", + ); - cases.add("array access of undeclared identifier", + cases.add( + "array access of undeclared identifier", \\export fn f() void { \\ i[i] = i[i]; \\} - , ".tmp_source.zig:2:5: error: use of undeclared identifier 'i'", - ".tmp_source.zig:2:12: error: use of undeclared identifier 'i'"); + , + ".tmp_source.zig:2:5: error: use of undeclared identifier 'i'", + ".tmp_source.zig:2:12: error: use of undeclared identifier 'i'", + ); - cases.add("array access of non array", + cases.add( + "array access of non array", \\export fn f() void { \\ var bad : bool = undefined; \\ bad[bad] = bad[bad]; \\} - , ".tmp_source.zig:3:8: error: array access of non-array type 'bool'", - ".tmp_source.zig:3:19: error: array access of non-array type 'bool'"); + , + ".tmp_source.zig:3:8: error: array access of non-array type 'bool'", + ".tmp_source.zig:3:19: error: array access of non-array type 'bool'", + ); - cases.add("array access with non integer index", + cases.add( + "array access with non integer index", \\export fn f() void { \\ var array = "aoeu"; \\ var bad = false; \\ array[bad] = array[bad]; \\} - , ".tmp_source.zig:4:11: error: expected type 'usize', found 'bool'", - ".tmp_source.zig:4:24: error: expected type 'usize', found 'bool'"); + , + ".tmp_source.zig:4:11: error: expected type 'usize', found 'bool'", + ".tmp_source.zig:4:24: error: expected type 'usize', found 'bool'", + ); - cases.add("write to const global variable", + cases.add( + "write to const global variable", \\const x : i32 = 99; \\fn f() void { \\ x = 1; \\} \\export fn entry() void { f(); } - , ".tmp_source.zig:3:7: error: cannot assign to constant"); - + , + ".tmp_source.zig:3:7: error: cannot assign to constant", + ); - cases.add("missing else clause", + cases.add( + "missing else clause", \\fn f(b: bool) void { \\ const x : i32 = if (b) h: { break :h 1; }; \\ const y = if (b) h: { break :h i32(1); }; \\} \\export fn entry() void { f(true); } - , ".tmp_source.zig:2:42: error: integer value 1 cannot be implicitly casted to type 'void'", - ".tmp_source.zig:3:15: error: incompatible types: 'i32' and 'void'"); + , + ".tmp_source.zig:2:42: error: integer value 1 cannot be implicitly casted to type 'void'", + ".tmp_source.zig:3:15: error: incompatible types: 'i32' and 'void'", + ); - cases.add("direct struct loop", + cases.add( + "direct struct loop", \\const A = struct { a : A, }; \\export fn entry() usize { return @sizeOf(A); } - , ".tmp_source.zig:1:11: error: struct 'A' contains itself"); + , + ".tmp_source.zig:1:11: error: struct 'A' contains itself", + ); - cases.add("indirect struct loop", + cases.add( + "indirect struct loop", \\const A = struct { b : B, }; \\const B = struct { c : C, }; \\const C = struct { a : A, }; \\export fn entry() usize { return @sizeOf(A); } - , ".tmp_source.zig:1:11: error: struct 'A' contains itself"); + , + ".tmp_source.zig:1:11: error: struct 'A' contains itself", + ); - cases.add("invalid struct field", + cases.add( + "invalid struct field", \\const A = struct { x : i32, }; \\export fn f() void { \\ var a : A = undefined; @@ -882,27 +1143,37 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ const y = a.bar; \\} , - ".tmp_source.zig:4:6: error: no member named 'foo' in struct 'A'", - ".tmp_source.zig:5:16: error: no member named 'bar' in struct 'A'"); + ".tmp_source.zig:4:6: error: no member named 'foo' in struct 'A'", + ".tmp_source.zig:5:16: error: no member named 'bar' in struct 'A'", + ); - cases.add("redefinition of struct", + cases.add( + "redefinition of struct", \\const A = struct { x : i32, }; \\const A = struct { y : i32, }; - , ".tmp_source.zig:2:1: error: redefinition of 'A'"); + , + ".tmp_source.zig:2:1: error: redefinition of 'A'", + ); - cases.add("redefinition of enums", + cases.add( + "redefinition of enums", \\const A = enum {}; \\const A = enum {}; - , ".tmp_source.zig:2:1: error: redefinition of 'A'"); + , + ".tmp_source.zig:2:1: error: redefinition of 'A'", + ); - cases.add("redefinition of global variables", + cases.add( + "redefinition of global variables", \\var a : i32 = 1; \\var a : i32 = 2; , - ".tmp_source.zig:2:1: error: redefinition of 'a'", - ".tmp_source.zig:1:1: note: previous definition is here"); + ".tmp_source.zig:2:1: error: redefinition of 'a'", + ".tmp_source.zig:1:1: note: previous definition is here", + ); - cases.add("duplicate field in struct value expression", + cases.add( + "duplicate field in struct value expression", \\const A = struct { \\ x : i32, \\ y : i32, @@ -916,9 +1187,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ .z = 4, \\ }; \\} - , ".tmp_source.zig:11:9: error: duplicate field"); + , + ".tmp_source.zig:11:9: error: duplicate field", + ); - cases.add("missing field in struct value expression", + cases.add( + "missing field in struct value expression", \\const A = struct { \\ x : i32, \\ y : i32, @@ -932,9 +1206,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ .y = 2, \\ }; \\} - , ".tmp_source.zig:9:17: error: missing field: 'x'"); + , + ".tmp_source.zig:9:17: error: missing field: 'x'", + ); - cases.add("invalid field in struct value expression", + cases.add( + "invalid field in struct value expression", \\const A = struct { \\ x : i32, \\ y : i32, @@ -947,66 +1224,95 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ .foo = 42, \\ }; \\} - , ".tmp_source.zig:10:9: error: no member named 'foo' in struct 'A'"); + , + ".tmp_source.zig:10:9: error: no member named 'foo' in struct 'A'", + ); - cases.add("invalid break expression", + cases.add( + "invalid break expression", \\export fn f() void { \\ break; \\} - , ".tmp_source.zig:2:5: error: break expression outside loop"); + , + ".tmp_source.zig:2:5: error: break expression outside loop", + ); - cases.add("invalid continue expression", + cases.add( + "invalid continue expression", \\export fn f() void { \\ continue; \\} - , ".tmp_source.zig:2:5: error: continue expression outside loop"); + , + ".tmp_source.zig:2:5: error: continue expression outside loop", + ); - cases.add("invalid maybe type", + cases.add( + "invalid maybe type", \\export fn f() void { \\ if (true) |x| { } \\} - , ".tmp_source.zig:2:9: error: expected nullable type, found 'bool'"); + , + ".tmp_source.zig:2:9: error: expected nullable type, found 'bool'", + ); - cases.add("cast unreachable", + cases.add( + "cast unreachable", \\fn f() i32 { \\ return i32(return 1); \\} \\export fn entry() void { _ = f(); } - , ".tmp_source.zig:2:15: error: unreachable code"); + , + ".tmp_source.zig:2:15: error: unreachable code", + ); - cases.add("invalid builtin fn", + cases.add( + "invalid builtin fn", \\fn f() @bogus(foo) { \\} \\export fn entry() void { _ = f(); } - , ".tmp_source.zig:1:8: error: invalid builtin function: 'bogus'"); + , + ".tmp_source.zig:1:8: error: invalid builtin function: 'bogus'", + ); - cases.add("top level decl dependency loop", + cases.add( + "top level decl dependency loop", \\const a : @typeOf(b) = 0; \\const b : @typeOf(a) = 0; \\export fn entry() void { \\ const c = a + b; \\} - , ".tmp_source.zig:1:1: error: 'a' depends on itself"); + , + ".tmp_source.zig:1:1: error: 'a' depends on itself", + ); - cases.add("noalias on non pointer param", + cases.add( + "noalias on non pointer param", \\fn f(noalias x: i32) void {} \\export fn entry() void { f(1234); } - , ".tmp_source.zig:1:6: error: noalias on non-pointer parameter"); + , + ".tmp_source.zig:1:6: error: noalias on non-pointer parameter", + ); - cases.add("struct init syntax for array", + cases.add( + "struct init syntax for array", \\const foo = []u16{.x = 1024,}; \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , ".tmp_source.zig:1:18: error: type '[]u16' does not support struct initialization syntax"); + , + ".tmp_source.zig:1:18: error: type '[]u16' does not support struct initialization syntax", + ); - cases.add("type variables must be constant", + cases.add( + "type variables must be constant", \\var foo = u8; \\export fn entry() foo { \\ return 1; \\} - , ".tmp_source.zig:1:1: error: variable of type 'type' must be constant"); - + , + ".tmp_source.zig:1:1: error: variable of type 'type' must be constant", + ); - cases.add("variables shadowing types", + cases.add( + "variables shadowing types", \\const Foo = struct {}; \\const Bar = struct {}; \\ @@ -1018,12 +1324,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ f(1234); \\} , - ".tmp_source.zig:4:6: error: redefinition of 'Foo'", - ".tmp_source.zig:1:1: note: previous definition is here", - ".tmp_source.zig:5:5: error: redefinition of 'Bar'", - ".tmp_source.zig:2:1: note: previous definition is here"); + ".tmp_source.zig:4:6: error: redefinition of 'Foo'", + ".tmp_source.zig:1:1: note: previous definition is here", + ".tmp_source.zig:5:5: error: redefinition of 'Bar'", + ".tmp_source.zig:2:1: note: previous definition is here", + ); - cases.add("switch expression - missing enumeration prong", + cases.add( + "switch expression - missing enumeration prong", \\const Number = enum { \\ One, \\ Two, @@ -1039,9 +1347,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , ".tmp_source.zig:8:5: error: enumeration value 'Number.Four' not handled in switch"); + , + ".tmp_source.zig:8:5: error: enumeration value 'Number.Four' not handled in switch", + ); - cases.add("switch expression - duplicate enumeration prong", + cases.add( + "switch expression - duplicate enumeration prong", \\const Number = enum { \\ One, \\ Two, @@ -1059,10 +1370,13 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , ".tmp_source.zig:13:15: error: duplicate switch value", - ".tmp_source.zig:10:15: note: other value is here"); + , + ".tmp_source.zig:13:15: error: duplicate switch value", + ".tmp_source.zig:10:15: note: other value is here", + ); - cases.add("switch expression - duplicate enumeration prong when else present", + cases.add( + "switch expression - duplicate enumeration prong when else present", \\const Number = enum { \\ One, \\ Two, @@ -1081,10 +1395,13 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , ".tmp_source.zig:13:15: error: duplicate switch value", - ".tmp_source.zig:10:15: note: other value is here"); + , + ".tmp_source.zig:13:15: error: duplicate switch value", + ".tmp_source.zig:10:15: note: other value is here", + ); - cases.add("switch expression - multiple else prongs", + cases.add( + "switch expression - multiple else prongs", \\fn f(x: u32) void { \\ const value: bool = switch (x) { \\ 1234 => false, @@ -1095,9 +1412,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\export fn entry() void { \\ f(1234); \\} - , ".tmp_source.zig:5:9: error: multiple else prongs in switch expression"); + , + ".tmp_source.zig:5:9: error: multiple else prongs in switch expression", + ); - cases.add("switch expression - non exhaustive integer prongs", + cases.add( + "switch expression - non exhaustive integer prongs", \\fn foo(x: u8) void { \\ switch (x) { \\ 0 => {}, @@ -1105,9 +1425,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:2:5: error: switch must handle all possibilities"); + ".tmp_source.zig:2:5: error: switch must handle all possibilities", + ); - cases.add("switch expression - duplicate or overlapping integer value", + cases.add( + "switch expression - duplicate or overlapping integer value", \\fn foo(x: u8) u8 { \\ return switch (x) { \\ 0 ... 100 => u8(0), @@ -1119,9 +1441,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , ".tmp_source.zig:6:9: error: duplicate switch value", - ".tmp_source.zig:5:14: note: previous value is here"); + ".tmp_source.zig:5:14: note: previous value is here", + ); - cases.add("switch expression - switch on pointer type with no else", + cases.add( + "switch expression - switch on pointer type with no else", \\fn foo(x: &u8) void { \\ switch (x) { \\ &y => {}, @@ -1130,54 +1454,77 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const y: u8 = 100; \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:2:5: error: else prong required when switching on type '&u8'"); + ".tmp_source.zig:2:5: error: else prong required when switching on type '&u8'", + ); - cases.add("global variable initializer must be constant expression", + cases.add( + "global variable initializer must be constant expression", \\extern fn foo() i32; \\const x = foo(); \\export fn entry() i32 { return x; } - , ".tmp_source.zig:2:11: error: unable to evaluate constant expression"); + , + ".tmp_source.zig:2:11: error: unable to evaluate constant expression", + ); - cases.add("array concatenation with wrong type", + cases.add( + "array concatenation with wrong type", \\const src = "aoeu"; \\const derp = usize(1234); \\const a = derp ++ "foo"; \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } - , ".tmp_source.zig:3:11: error: expected array or C string literal, found 'usize'"); + , + ".tmp_source.zig:3:11: error: expected array or C string literal, found 'usize'", + ); - cases.add("non compile time array concatenation", + cases.add( + "non compile time array concatenation", \\fn f() []u8 { \\ return s ++ "foo"; \\} \\var s: [10]u8 = undefined; \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , ".tmp_source.zig:2:12: error: unable to evaluate constant expression"); + , + ".tmp_source.zig:2:12: error: unable to evaluate constant expression", + ); - cases.add("@cImport with bogus include", + cases.add( + "@cImport with bogus include", \\const c = @cImport(@cInclude("bogus.h")); \\export fn entry() usize { return @sizeOf(@typeOf(c.bogo)); } - , ".tmp_source.zig:1:11: error: C import failed", - ".h:1:10: note: 'bogus.h' file not found"); + , + ".tmp_source.zig:1:11: error: C import failed", + ".h:1:10: note: 'bogus.h' file not found", + ); - cases.add("address of number literal", + cases.add( + "address of number literal", \\const x = 3; \\const y = &x; \\fn foo() &const i32 { return y; } \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , ".tmp_source.zig:3:30: error: expected type '&const i32', found '&const (integer literal)'"); + , + ".tmp_source.zig:3:30: error: expected type '&const i32', found '&const (integer literal)'", + ); - cases.add("integer overflow error", + cases.add( + "integer overflow error", \\const x : u8 = 300; \\export fn entry() usize { return @sizeOf(@typeOf(x)); } - , ".tmp_source.zig:1:16: error: integer value 300 cannot be implicitly casted to type 'u8'"); + , + ".tmp_source.zig:1:16: error: integer value 300 cannot be implicitly casted to type 'u8'", + ); - cases.add("incompatible number literals", + cases.add( + "incompatible number literals", \\const x = 2 == 2.0; \\export fn entry() usize { return @sizeOf(@typeOf(x)); } - , ".tmp_source.zig:1:11: error: integer value 2 cannot be implicitly casted to type '(float literal)'"); + , + ".tmp_source.zig:1:11: error: integer value 2 cannot be implicitly casted to type '(float literal)'", + ); - cases.add("missing function call param", + cases.add( + "missing function call param", \\const Foo = struct { \\ a: i32, \\ b: i32, @@ -1201,58 +1548,73 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , ".tmp_source.zig:20:34: error: expected 1 arguments, found 0"); + , + ".tmp_source.zig:20:34: error: expected 1 arguments, found 0", + ); - cases.add("missing function name and param name", + cases.add( + "missing function name and param name", \\fn () void {} \\fn f(i32) void {} \\export fn entry() usize { return @sizeOf(@typeOf(f)); } , - ".tmp_source.zig:1:1: error: missing function name", - ".tmp_source.zig:2:6: error: missing parameter name"); + ".tmp_source.zig:1:1: error: missing function name", + ".tmp_source.zig:2:6: error: missing parameter name", + ); - cases.add("wrong function type", + cases.add( + "wrong function type", \\const fns = []fn() void { a, b, c }; \\fn a() i32 {return 0;} \\fn b() i32 {return 1;} \\fn c() i32 {return 2;} \\export fn entry() usize { return @sizeOf(@typeOf(fns)); } - , ".tmp_source.zig:1:27: error: expected type 'fn() void', found 'fn() i32'"); + , + ".tmp_source.zig:1:27: error: expected type 'fn() void', found 'fn() i32'", + ); - cases.add("extern function pointer mismatch", + cases.add( + "extern function pointer mismatch", \\const fns = [](fn(i32)i32) { a, b, c }; \\pub fn a(x: i32) i32 {return x + 0;} \\pub fn b(x: i32) i32 {return x + 1;} \\export fn c(x: i32) i32 {return x + 2;} \\ \\export fn entry() usize { return @sizeOf(@typeOf(fns)); } - , ".tmp_source.zig:1:36: error: expected type 'fn(i32) i32', found 'extern fn(i32) i32'"); - + , + ".tmp_source.zig:1:36: error: expected type 'fn(i32) i32', found 'extern fn(i32) i32'", + ); - cases.add("implicit cast from f64 to f32", + cases.add( + "implicit cast from f64 to f32", \\const x : f64 = 1.0; \\const y : f32 = x; \\ \\export fn entry() usize { return @sizeOf(@typeOf(y)); } - , ".tmp_source.zig:2:17: error: expected type 'f32', found 'f64'"); - + , + ".tmp_source.zig:2:17: error: expected type 'f32', found 'f64'", + ); - cases.add("colliding invalid top level functions", + cases.add( + "colliding invalid top level functions", \\fn func() bogus {} \\fn func() bogus {} \\export fn entry() usize { return @sizeOf(@typeOf(func)); } , - ".tmp_source.zig:2:1: error: redefinition of 'func'", - ".tmp_source.zig:1:11: error: use of undeclared identifier 'bogus'"); + ".tmp_source.zig:2:1: error: redefinition of 'func'", + ".tmp_source.zig:1:11: error: use of undeclared identifier 'bogus'", + ); - - cases.add("bogus compile var", + cases.add( + "bogus compile var", \\const x = @import("builtin").bogus; \\export fn entry() usize { return @sizeOf(@typeOf(x)); } - , ".tmp_source.zig:1:29: error: no member named 'bogus' in '"); - + , + ".tmp_source.zig:1:29: error: no member named 'bogus' in '", + ); - cases.add("non constant expression in array size outside function", + cases.add( + "non constant expression in array size outside function", \\const Foo = struct { \\ y: [get()]u8, \\}; @@ -1261,22 +1623,25 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(Foo)); } , - ".tmp_source.zig:5:25: error: unable to evaluate constant expression", - ".tmp_source.zig:2:12: note: called from here", - ".tmp_source.zig:2:8: note: called from here"); - + ".tmp_source.zig:5:25: error: unable to evaluate constant expression", + ".tmp_source.zig:2:12: note: called from here", + ".tmp_source.zig:2:8: note: called from here", + ); - cases.add("addition with non numbers", + cases.add( + "addition with non numbers", \\const Foo = struct { \\ field: i32, \\}; \\const x = Foo {.field = 1} + Foo {.field = 2}; \\ \\export fn entry() usize { return @sizeOf(@typeOf(x)); } - , ".tmp_source.zig:4:28: error: invalid operands to binary expression: 'Foo' and 'Foo'"); - + , + ".tmp_source.zig:4:28: error: invalid operands to binary expression: 'Foo' and 'Foo'", + ); - cases.add("division by zero", + cases.add( + "division by zero", \\const lit_int_x = 1 / 0; \\const lit_float_x = 1.0 / 0.0; \\const int_x = u32(1) / u32(0); @@ -1287,49 +1652,65 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\export fn entry3() usize { return @sizeOf(@typeOf(int_x)); } \\export fn entry4() usize { return @sizeOf(@typeOf(float_x)); } , - ".tmp_source.zig:1:21: error: division by zero", - ".tmp_source.zig:2:25: error: division by zero", - ".tmp_source.zig:3:22: error: division by zero", - ".tmp_source.zig:4:26: error: division by zero"); - + ".tmp_source.zig:1:21: error: division by zero", + ".tmp_source.zig:2:25: error: division by zero", + ".tmp_source.zig:3:22: error: division by zero", + ".tmp_source.zig:4:26: error: division by zero", + ); - cases.add("normal string with newline", + cases.add( + "normal string with newline", \\const foo = "a \\b"; \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , ".tmp_source.zig:1:13: error: newline not allowed in string literal"); + , + ".tmp_source.zig:1:13: error: newline not allowed in string literal", + ); - cases.add("invalid comparison for function pointers", + cases.add( + "invalid comparison for function pointers", \\fn foo() void {} \\const invalid = foo > foo; \\ \\export fn entry() usize { return @sizeOf(@typeOf(invalid)); } - , ".tmp_source.zig:2:21: error: operator not allowed for type 'fn() void'"); + , + ".tmp_source.zig:2:21: error: operator not allowed for type 'fn() void'", + ); - cases.add("generic function instance with non-constant expression", + cases.add( + "generic function instance with non-constant expression", \\fn foo(comptime x: i32, y: i32) i32 { return x + y; } \\fn test1(a: i32, b: i32) i32 { \\ return foo(a, b); \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(test1)); } - , ".tmp_source.zig:3:16: error: unable to evaluate constant expression"); + , + ".tmp_source.zig:3:16: error: unable to evaluate constant expression", + ); - cases.add("assign null to non-nullable pointer", + cases.add( + "assign null to non-nullable pointer", \\const a: &u8 = null; \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } - , ".tmp_source.zig:1:16: error: expected type '&u8', found '(null)'"); + , + ".tmp_source.zig:1:16: error: expected type '&u8', found '(null)'", + ); - cases.add("indexing an array of size zero", + cases.add( + "indexing an array of size zero", \\const array = []u8{}; \\export fn foo() void { \\ const pointer = &array[0]; \\} - , ".tmp_source.zig:3:27: error: index 0 outside array of size 0"); + , + ".tmp_source.zig:3:27: error: index 0 outside array of size 0", + ); - cases.add("compile time division by zero", + cases.add( + "compile time division by zero", \\const y = foo(0); \\fn foo(x: u32) u32 { \\ return 1 / x; @@ -1337,17 +1718,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(y)); } , - ".tmp_source.zig:3:14: error: division by zero", - ".tmp_source.zig:1:14: note: called from here"); + ".tmp_source.zig:3:14: error: division by zero", + ".tmp_source.zig:1:14: note: called from here", + ); - cases.add("branch on undefined value", + cases.add( + "branch on undefined value", \\const x = if (undefined) true else false; \\ \\export fn entry() usize { return @sizeOf(@typeOf(x)); } - , ".tmp_source.zig:1:15: error: use of undefined value"); - + , + ".tmp_source.zig:1:15: error: use of undefined value", + ); - cases.add("endless loop in function evaluation", + cases.add( + "endless loop in function evaluation", \\const seventh_fib_number = fibbonaci(7); \\fn fibbonaci(x: i32) i32 { \\ return fibbonaci(x - 1) + fibbonaci(x - 2); @@ -1355,16 +1740,22 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(seventh_fib_number)); } , - ".tmp_source.zig:3:21: error: evaluation exceeded 1000 backwards branches", - ".tmp_source.zig:3:21: note: called from here"); + ".tmp_source.zig:3:21: error: evaluation exceeded 1000 backwards branches", + ".tmp_source.zig:3:21: note: called from here", + ); - cases.add("@embedFile with bogus file", - \\const resource = @embedFile("bogus.txt"); + cases.add( + "@embedFile with bogus file", + \\const resource = @embedFile("bogus.txt",); \\ \\export fn entry() usize { return @sizeOf(@typeOf(resource)); } - , ".tmp_source.zig:1:29: error: unable to find '", "bogus.txt'"); + , + ".tmp_source.zig:1:29: error: unable to find '", + "bogus.txt'", + ); - cases.add("non-const expression in struct literal outside function", + cases.add( + "non-const expression in struct literal outside function", \\const Foo = struct { \\ x: i32, \\}; @@ -1372,9 +1763,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\extern fn get_it() i32; \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } - , ".tmp_source.zig:4:21: error: unable to evaluate constant expression"); + , + ".tmp_source.zig:4:21: error: unable to evaluate constant expression", + ); - cases.add("non-const expression function call with struct return value outside function", + cases.add( + "non-const expression function call with struct return value outside function", \\const Foo = struct { \\ x: i32, \\}; @@ -1387,19 +1781,24 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } , - ".tmp_source.zig:6:24: error: unable to evaluate constant expression", - ".tmp_source.zig:4:17: note: called from here"); + ".tmp_source.zig:6:24: error: unable to evaluate constant expression", + ".tmp_source.zig:4:17: note: called from here", + ); - cases.add("undeclared identifier error should mark fn as impure", + cases.add( + "undeclared identifier error should mark fn as impure", \\export fn foo() void { \\ test_a_thing(); \\} \\fn test_a_thing() void { \\ bad_fn_call(); \\} - , ".tmp_source.zig:5:5: error: use of undeclared identifier 'bad_fn_call'"); + , + ".tmp_source.zig:5:5: error: use of undeclared identifier 'bad_fn_call'", + ); - cases.add("illegal comparison of types", + cases.add( + "illegal comparison of types", \\fn bad_eql_1(a: []u8, b: []u8) bool { \\ return a == b; \\} @@ -1414,10 +1813,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\export fn entry1() usize { return @sizeOf(@typeOf(bad_eql_1)); } \\export fn entry2() usize { return @sizeOf(@typeOf(bad_eql_2)); } , - ".tmp_source.zig:2:14: error: operator not allowed for type '[]u8'", - ".tmp_source.zig:9:16: error: operator not allowed for type 'EnumWithData'"); + ".tmp_source.zig:2:14: error: operator not allowed for type '[]u8'", + ".tmp_source.zig:9:16: error: operator not allowed for type 'EnumWithData'", + ); - cases.add("non-const switch number literal", + cases.add( + "non-const switch number literal", \\export fn foo() void { \\ const x = switch (bar()) { \\ 1, 2 => 1, @@ -1428,25 +1829,34 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\fn bar() i32 { \\ return 2; \\} - , ".tmp_source.zig:2:15: error: unable to infer expression type"); + , + ".tmp_source.zig:2:15: error: unable to infer expression type", + ); - cases.add("atomic orderings of cmpxchg - failure stricter than success", + cases.add( + "atomic orderings of cmpxchg - failure stricter than success", \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn f() void { \\ var x: i32 = 1234; \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {} \\} - , ".tmp_source.zig:4:81: error: failure atomic ordering must be no stricter than success"); + , + ".tmp_source.zig:4:81: error: failure atomic ordering must be no stricter than success", + ); - cases.add("atomic orderings of cmpxchg - success Monotonic or stricter", + cases.add( + "atomic orderings of cmpxchg - success Monotonic or stricter", \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn f() void { \\ var x: i32 = 1234; \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {} \\} - , ".tmp_source.zig:4:58: error: success atomic ordering must be Monotonic or stricter"); + , + ".tmp_source.zig:4:58: error: success atomic ordering must be Monotonic or stricter", + ); - cases.add("negation overflow in function evaluation", + cases.add( + "negation overflow in function evaluation", \\const y = neg(-128); \\fn neg(x: i8) i8 { \\ return -x; @@ -1454,10 +1864,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(y)); } , - ".tmp_source.zig:3:12: error: negation caused overflow", - ".tmp_source.zig:1:14: note: called from here"); + ".tmp_source.zig:3:12: error: negation caused overflow", + ".tmp_source.zig:1:14: note: called from here", + ); - cases.add("add overflow in function evaluation", + cases.add( + "add overflow in function evaluation", \\const y = add(65530, 10); \\fn add(a: u16, b: u16) u16 { \\ return a + b; @@ -1465,11 +1877,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(y)); } , - ".tmp_source.zig:3:14: error: operation caused overflow", - ".tmp_source.zig:1:14: note: called from here"); - + ".tmp_source.zig:3:14: error: operation caused overflow", + ".tmp_source.zig:1:14: note: called from here", + ); - cases.add("sub overflow in function evaluation", + cases.add( + "sub overflow in function evaluation", \\const y = sub(10, 20); \\fn sub(a: u16, b: u16) u16 { \\ return a - b; @@ -1477,10 +1890,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(y)); } , - ".tmp_source.zig:3:14: error: operation caused overflow", - ".tmp_source.zig:1:14: note: called from here"); + ".tmp_source.zig:3:14: error: operation caused overflow", + ".tmp_source.zig:1:14: note: called from here", + ); - cases.add("mul overflow in function evaluation", + cases.add( + "mul overflow in function evaluation", \\const y = mul(300, 6000); \\fn mul(a: u16, b: u16) u16 { \\ return a * b; @@ -1488,27 +1903,34 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(y)); } , - ".tmp_source.zig:3:14: error: operation caused overflow", - ".tmp_source.zig:1:14: note: called from here"); + ".tmp_source.zig:3:14: error: operation caused overflow", + ".tmp_source.zig:1:14: note: called from here", + ); - cases.add("truncate sign mismatch", + cases.add( + "truncate sign mismatch", \\fn f() i8 { \\ const x: u32 = 10; \\ return @truncate(i8, x); \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , ".tmp_source.zig:3:26: error: expected signed integer type, found 'u32'"); + , + ".tmp_source.zig:3:26: error: expected signed integer type, found 'u32'", + ); - cases.add("try in function with non error return type", + cases.add( + "try in function with non error return type", \\export fn f() void { \\ try something(); \\} \\fn something() error!void { } , - ".tmp_source.zig:2:5: error: expected type 'void', found 'error'"); + ".tmp_source.zig:2:5: error: expected type 'void', found 'error'", + ); - cases.add("invalid pointer for var type", + cases.add( + "invalid pointer for var type", \\extern fn ext() usize; \\var bytes: [ext()]u8 = undefined; \\export fn f() void { @@ -1516,30 +1938,42 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ b.* = u8(i); \\ } \\} - , ".tmp_source.zig:2:13: error: unable to evaluate constant expression"); + , + ".tmp_source.zig:2:13: error: unable to evaluate constant expression", + ); - cases.add("export function with comptime parameter", + cases.add( + "export function with comptime parameter", \\export fn foo(comptime x: i32, y: i32) i32{ \\ return x + y; \\} - , ".tmp_source.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'"); + , + ".tmp_source.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'", + ); - cases.add("extern function with comptime parameter", + cases.add( + "extern function with comptime parameter", \\extern fn foo(comptime x: i32, y: i32) i32; \\fn f() i32 { \\ return foo(1, 2); \\} \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , ".tmp_source.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'"); + , + ".tmp_source.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'", + ); - cases.add("convert fixed size array to slice with invalid size", + cases.add( + "convert fixed size array to slice with invalid size", \\export fn f() void { \\ var array: [5]u8 = undefined; \\ var foo = ([]const u32)(array)[0]; \\} - , ".tmp_source.zig:3:28: error: unable to convert [5]u8 to []const u32: size mismatch"); + , + ".tmp_source.zig:3:28: error: unable to convert [5]u8 to []const u32: size mismatch", + ); - cases.add("non-pure function returns type", + cases.add( + "non-pure function returns type", \\var a: u32 = 0; \\pub fn List(comptime T: type) type { \\ a += 1; @@ -1558,18 +1992,24 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var list: List(i32) = undefined; \\ list.length = 10; \\} - , ".tmp_source.zig:3:7: error: unable to evaluate constant expression", - ".tmp_source.zig:16:19: note: called from here"); + , + ".tmp_source.zig:3:7: error: unable to evaluate constant expression", + ".tmp_source.zig:16:19: note: called from here", + ); - cases.add("bogus method call on slice", + cases.add( + "bogus method call on slice", \\var self = "aoeu"; \\fn f(m: []const u8) void { \\ m.copy(u8, self[0..], m); \\} \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , ".tmp_source.zig:3:6: error: no member named 'copy' in '[]const u8'"); + , + ".tmp_source.zig:3:6: error: no member named 'copy' in '[]const u8'", + ); - cases.add("wrong number of arguments for method fn call", + cases.add( + "wrong number of arguments for method fn call", \\const Foo = struct { \\ fn method(self: &const Foo, a: i32) void {} \\}; @@ -1578,34 +2018,49 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ foo.method(1, 2); \\} \\export fn entry() usize { return @sizeOf(@typeOf(f)); } - , ".tmp_source.zig:6:15: error: expected 2 arguments, found 3"); + , + ".tmp_source.zig:6:15: error: expected 2 arguments, found 3", + ); - cases.add("assign through constant pointer", + cases.add( + "assign through constant pointer", \\export fn f() void { \\ var cstr = c"Hat"; \\ cstr[0] = 'W'; \\} - , ".tmp_source.zig:3:11: error: cannot assign to constant"); + , + ".tmp_source.zig:3:11: error: cannot assign to constant", + ); - cases.add("assign through constant slice", + cases.add( + "assign through constant slice", \\export fn f() void { \\ var cstr: []const u8 = "Hat"; \\ cstr[0] = 'W'; \\} - , ".tmp_source.zig:3:11: error: cannot assign to constant"); + , + ".tmp_source.zig:3:11: error: cannot assign to constant", + ); - cases.add("main function with bogus args type", + cases.add( + "main function with bogus args type", \\pub fn main(args: [][]bogus) !void {} - , ".tmp_source.zig:1:23: error: use of undeclared identifier 'bogus'"); + , + ".tmp_source.zig:1:23: error: use of undeclared identifier 'bogus'", + ); - cases.add("for loop missing element param", + cases.add( + "for loop missing element param", \\fn foo(blah: []u8) void { \\ for (blah) { } \\} \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , ".tmp_source.zig:2:5: error: for loop expression missing element parameter"); + , + ".tmp_source.zig:2:5: error: for loop expression missing element parameter", + ); - cases.add("misspelled type with pointer only reference", + cases.add( + "misspelled type with pointer only reference", \\const JasonHM = u8; \\const JasonList = &JsonNode; \\ @@ -1636,9 +2091,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , ".tmp_source.zig:5:16: error: use of undeclared identifier 'JsonList'"); + , + ".tmp_source.zig:5:16: error: use of undeclared identifier 'JsonList'", + ); - cases.add("method call with first arg type primitive", + cases.add( + "method call with first arg type primitive", \\const Foo = struct { \\ x: i32, \\ @@ -1654,9 +2112,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\ derp.init(); \\} - , ".tmp_source.zig:14:5: error: expected type 'i32', found '&const Foo'"); + , + ".tmp_source.zig:14:5: error: expected type 'i32', found '&const Foo'", + ); - cases.add("method call with first arg type wrong container", + cases.add( + "method call with first arg type wrong container", \\pub const List = struct { \\ len: usize, \\ allocator: &Allocator, @@ -1681,26 +2142,33 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var x = List.init(&global_allocator); \\ x.init(); \\} - , ".tmp_source.zig:23:5: error: expected type '&Allocator', found '&List'"); + , + ".tmp_source.zig:23:5: error: expected type '&Allocator', found '&List'", + ); - cases.add("binary not on number literal", + cases.add( + "binary not on number literal", \\const TINY_QUANTUM_SHIFT = 4; \\const TINY_QUANTUM_SIZE = 1 << TINY_QUANTUM_SHIFT; \\var block_aligned_stuff: usize = (4 + TINY_QUANTUM_SIZE) & ~(TINY_QUANTUM_SIZE - 1); \\ \\export fn entry() usize { return @sizeOf(@typeOf(block_aligned_stuff)); } - , ".tmp_source.zig:3:60: error: unable to perform binary not operation on type '(integer literal)'"); + , + ".tmp_source.zig:3:60: error: unable to perform binary not operation on type '(integer literal)'", + ); cases.addCase(x: { - const tc = cases.create("multiple files with private function error", - \\const foo = @import("foo.zig"); + const tc = cases.create( + "multiple files with private function error", + \\const foo = @import("foo.zig",); \\ \\export fn callPrivFunction() void { \\ foo.privateFunction(); \\} , ".tmp_source.zig:4:8: error: 'privateFunction' is private", - "foo.zig:1:1: note: declared here"); + "foo.zig:1:1: note: declared here", + ); tc.addSourceFile("foo.zig", \\fn privateFunction() void { } @@ -1709,14 +2177,18 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { break :x tc; }); - cases.add("container init with non-type", + cases.add( + "container init with non-type", \\const zero: i32 = 0; \\const a = zero{1}; \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } - , ".tmp_source.zig:2:11: error: expected type, found 'i32'"); + , + ".tmp_source.zig:2:11: error: expected type, found 'i32'", + ); - cases.add("assign to constant field", + cases.add( + "assign to constant field", \\const Foo = struct { \\ field: i32, \\}; @@ -1724,9 +2196,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ const f = Foo {.field = 1234,}; \\ f.field = 0; \\} - , ".tmp_source.zig:6:13: error: cannot assign to constant"); + , + ".tmp_source.zig:6:13: error: cannot assign to constant", + ); - cases.add("return from defer expression", + cases.add( + "return from defer expression", \\pub fn testTrickyDefer() !void { \\ defer canFail() catch {}; \\ @@ -1742,9 +2217,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(testTrickyDefer)); } - , ".tmp_source.zig:4:11: error: cannot return from defer expression"); + , + ".tmp_source.zig:4:11: error: cannot return from defer expression", + ); - cases.add("attempt to access var args out of bounds", + cases.add( + "attempt to access var args out of bounds", \\fn add(args: ...) i32 { \\ return args[0] + args[1]; \\} @@ -1755,10 +2233,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:2:26: error: index 1 outside argument list of size 1", - ".tmp_source.zig:6:15: note: called from here"); + ".tmp_source.zig:2:26: error: index 1 outside argument list of size 1", + ".tmp_source.zig:6:15: note: called from here", + ); - cases.add("pass integer literal to var args", + cases.add( + "pass integer literal to var args", \\fn add(args: ...) i32 { \\ var sum = i32(0); \\ {comptime var i: usize = 0; inline while (i < args.len) : (i += 1) { @@ -1772,32 +2252,44 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(bar)); } - , ".tmp_source.zig:10:16: error: compiler bug: integer and float literals in var args function must be casted"); + , + ".tmp_source.zig:10:16: error: compiler bug: integer and float literals in var args function must be casted", + ); - cases.add("assign too big number to u16", + cases.add( + "assign too big number to u16", \\export fn foo() void { \\ var vga_mem: u16 = 0xB8000; \\} - , ".tmp_source.zig:2:24: error: integer value 753664 cannot be implicitly casted to type 'u16'"); + , + ".tmp_source.zig:2:24: error: integer value 753664 cannot be implicitly casted to type 'u16'", + ); - cases.add("global variable alignment non power of 2", + cases.add( + "global variable alignment non power of 2", \\const some_data: [100]u8 align(3) = undefined; \\export fn entry() usize { return @sizeOf(@typeOf(some_data)); } - , ".tmp_source.zig:1:32: error: alignment value 3 is not a power of 2"); + , + ".tmp_source.zig:1:32: error: alignment value 3 is not a power of 2", + ); - cases.add("function alignment non power of 2", + cases.add( + "function alignment non power of 2", \\extern fn foo() align(3) void; \\export fn entry() void { return foo(); } - , ".tmp_source.zig:1:23: error: alignment value 3 is not a power of 2"); + , + ".tmp_source.zig:1:23: error: alignment value 3 is not a power of 2", + ); - cases.add("compile log", + cases.add( + "compile log", \\export fn foo() void { - \\ comptime bar(12, "hi"); + \\ comptime bar(12, "hi",); \\} \\fn bar(a: i32, b: []const u8) void { - \\ @compileLog("begin"); + \\ @compileLog("begin",); \\ @compileLog("a", a, "b", b); - \\ @compileLog("end"); + \\ @compileLog("end",); \\} , ".tmp_source.zig:5:5: error: found compile log statement", @@ -1805,9 +2297,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { ".tmp_source.zig:6:5: error: found compile log statement", ".tmp_source.zig:2:17: note: called from here", ".tmp_source.zig:7:5: error: found compile log statement", - ".tmp_source.zig:2:17: note: called from here"); + ".tmp_source.zig:2:17: note: called from here", + ); - cases.add("casting bit offset pointer to regular pointer", + cases.add( + "casting bit offset pointer to regular pointer", \\const BitField = packed struct { \\ a: u3, \\ b: u3, @@ -1823,9 +2317,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , ".tmp_source.zig:8:26: error: expected type '&const u3', found '&align(1:3:6) const u3'"); + , + ".tmp_source.zig:8:26: error: expected type '&const u3', found '&align(1:3:6) const u3'", + ); - cases.add("referring to a struct that is invalid", + cases.add( + "referring to a struct that is invalid", \\const UsbDeviceRequest = struct { \\ Type: u8, \\}; @@ -1838,10 +2335,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ if (!ok) unreachable; \\} , - ".tmp_source.zig:10:14: error: unable to evaluate constant expression", - ".tmp_source.zig:6:20: note: called from here"); + ".tmp_source.zig:10:14: error: unable to evaluate constant expression", + ".tmp_source.zig:6:20: note: called from here", + ); - cases.add("control flow uses comptime var at runtime", + cases.add( + "control flow uses comptime var at runtime", \\export fn foo() void { \\ comptime var i = 0; \\ while (i < 5) : (i += 1) { @@ -1851,55 +2350,78 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\fn bar() void { } , - ".tmp_source.zig:3:5: error: control flow attempts to use compile-time variable at runtime", - ".tmp_source.zig:3:24: note: compile-time variable assigned here"); + ".tmp_source.zig:3:5: error: control flow attempts to use compile-time variable at runtime", + ".tmp_source.zig:3:24: note: compile-time variable assigned here", + ); - cases.add("ignored return value", + cases.add( + "ignored return value", \\export fn foo() void { \\ bar(); \\} \\fn bar() i32 { return 0; } - , ".tmp_source.zig:2:8: error: expression value is ignored"); + , + ".tmp_source.zig:2:8: error: expression value is ignored", + ); - cases.add("ignored assert-err-ok return value", + cases.add( + "ignored assert-err-ok return value", \\export fn foo() void { \\ bar() catch unreachable; \\} \\fn bar() error!i32 { return 0; } - , ".tmp_source.zig:2:11: error: expression value is ignored"); + , + ".tmp_source.zig:2:11: error: expression value is ignored", + ); - cases.add("ignored statement value", + cases.add( + "ignored statement value", \\export fn foo() void { \\ 1; \\} - , ".tmp_source.zig:2:5: error: expression value is ignored"); + , + ".tmp_source.zig:2:5: error: expression value is ignored", + ); - cases.add("ignored comptime statement value", + cases.add( + "ignored comptime statement value", \\export fn foo() void { \\ comptime {1;} \\} - , ".tmp_source.zig:2:15: error: expression value is ignored"); + , + ".tmp_source.zig:2:15: error: expression value is ignored", + ); - cases.add("ignored comptime value", + cases.add( + "ignored comptime value", \\export fn foo() void { \\ comptime 1; \\} - , ".tmp_source.zig:2:5: error: expression value is ignored"); + , + ".tmp_source.zig:2:5: error: expression value is ignored", + ); - cases.add("ignored defered statement value", + cases.add( + "ignored defered statement value", \\export fn foo() void { \\ defer {1;} \\} - , ".tmp_source.zig:2:12: error: expression value is ignored"); + , + ".tmp_source.zig:2:12: error: expression value is ignored", + ); - cases.add("ignored defered function call", + cases.add( + "ignored defered function call", \\export fn foo() void { \\ defer bar(); \\} \\fn bar() error!i32 { return 0; } - , ".tmp_source.zig:2:14: error: expression value is ignored"); + , + ".tmp_source.zig:2:14: error: expression value is ignored", + ); - cases.add("dereference an array", + cases.add( + "dereference an array", \\var s_buffer: [10]u8 = undefined; \\pub fn pass(in: []u8) []u8 { \\ var out = &s_buffer; @@ -1908,11 +2430,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(pass)); } - , ".tmp_source.zig:4:11: error: attempt to dereference non pointer type '[10]u8'"); + , + ".tmp_source.zig:4:11: error: attempt to dereference non pointer type '[10]u8'", + ); - cases.add("pass const ptr to mutable ptr fn", + cases.add( + "pass const ptr to mutable ptr fn", \\fn foo() bool { - \\ const a = ([]const u8)("a"); + \\ const a = ([]const u8)("a",); \\ const b = &a; \\ return ptrEql(b, b); \\} @@ -1921,18 +2446,22 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , ".tmp_source.zig:4:19: error: expected type '&[]const u8', found '&const []const u8'"); + , + ".tmp_source.zig:4:19: error: expected type '&[]const u8', found '&const []const u8'", + ); cases.addCase(x: { - const tc = cases.create("export collision", - \\const foo = @import("foo.zig"); + const tc = cases.create( + "export collision", + \\const foo = @import("foo.zig",); \\ \\export fn bar() usize { \\ return foo.baz; \\} , "foo.zig:1:8: error: exported symbol collision: 'bar'", - ".tmp_source.zig:3:8: note: other symbol here"); + ".tmp_source.zig:3:8: note: other symbol here", + ); tc.addSourceFile("foo.zig", \\export fn bar() void {} @@ -1942,35 +2471,48 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { break :x tc; }); - cases.add("pass non-copyable type by value to function", + cases.add( + "pass non-copyable type by value to function", \\const Point = struct { x: i32, y: i32, }; \\fn foo(p: Point) void { } \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , ".tmp_source.zig:2:11: error: type 'Point' is not copyable; cannot pass by value"); + , + ".tmp_source.zig:2:11: error: type 'Point' is not copyable; cannot pass by value", + ); - cases.add("implicit cast from array to mutable slice", + cases.add( + "implicit cast from array to mutable slice", \\var global_array: [10]i32 = undefined; \\fn foo(param: []i32) void {} \\export fn entry() void { \\ foo(global_array); \\} - , ".tmp_source.zig:4:9: error: expected type '[]i32', found '[10]i32'"); + , + ".tmp_source.zig:4:9: error: expected type '[]i32', found '[10]i32'", + ); - cases.add("ptrcast to non-pointer", + cases.add( + "ptrcast to non-pointer", \\export fn entry(a: &i32) usize { \\ return @ptrCast(usize, a); \\} - , ".tmp_source.zig:2:21: error: expected pointer, found 'usize'"); + , + ".tmp_source.zig:2:21: error: expected pointer, found 'usize'", + ); - cases.add("too many error values to cast to small integer", + cases.add( + "too many error values to cast to small integer", \\const Error = error { A, B, C, D, E, F, G, H }; \\fn foo(e: Error) u2 { \\ return u2(e); \\} \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , ".tmp_source.zig:3:14: error: too many error values to fit in 'u2'"); + , + ".tmp_source.zig:3:14: error: too many error values to fit in 'u2'", + ); - cases.add("asm at compile time", + cases.add( + "asm at compile time", \\comptime { \\ doSomeAsm(); \\} @@ -1982,48 +2524,66 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\.set aoeu, derp; \\ ); \\} - , ".tmp_source.zig:6:5: error: unable to evaluate constant expression"); + , + ".tmp_source.zig:6:5: error: unable to evaluate constant expression", + ); - cases.add("invalid member of builtin enum", - \\const builtin = @import("builtin"); + cases.add( + "invalid member of builtin enum", + \\const builtin = @import("builtin",); \\export fn entry() void { \\ const foo = builtin.Arch.x86; \\} - , ".tmp_source.zig:3:29: error: container 'Arch' has no member called 'x86'"); + , + ".tmp_source.zig:3:29: error: container 'Arch' has no member called 'x86'", + ); - cases.add("int to ptr of 0 bits", + cases.add( + "int to ptr of 0 bits", \\export fn foo() void { \\ var x: usize = 0x1000; \\ var y: &void = @intToPtr(&void, x); \\} - , ".tmp_source.zig:3:31: error: type '&void' has 0 bits and cannot store information"); + , + ".tmp_source.zig:3:31: error: type '&void' has 0 bits and cannot store information", + ); - cases.add("@fieldParentPtr - non struct", + cases.add( + "@fieldParentPtr - non struct", \\const Foo = i32; \\export fn foo(a: &i32) &Foo { \\ return @fieldParentPtr(Foo, "a", a); \\} - , ".tmp_source.zig:3:28: error: expected struct type, found 'i32'"); + , + ".tmp_source.zig:3:28: error: expected struct type, found 'i32'", + ); - cases.add("@fieldParentPtr - bad field name", + cases.add( + "@fieldParentPtr - bad field name", \\const Foo = extern struct { \\ derp: i32, \\}; \\export fn foo(a: &i32) &Foo { \\ return @fieldParentPtr(Foo, "a", a); \\} - , ".tmp_source.zig:5:33: error: struct 'Foo' has no field 'a'"); + , + ".tmp_source.zig:5:33: error: struct 'Foo' has no field 'a'", + ); - cases.add("@fieldParentPtr - field pointer is not pointer", + cases.add( + "@fieldParentPtr - field pointer is not pointer", \\const Foo = extern struct { \\ a: i32, \\}; \\export fn foo(a: i32) &Foo { \\ return @fieldParentPtr(Foo, "a", a); \\} - , ".tmp_source.zig:5:38: error: expected pointer, found 'i32'"); + , + ".tmp_source.zig:5:38: error: expected pointer, found 'i32'", + ); - cases.add("@fieldParentPtr - comptime field ptr not based on struct", + cases.add( + "@fieldParentPtr - comptime field ptr not based on struct", \\const Foo = struct { \\ a: i32, \\ b: i32, @@ -2034,9 +2594,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ const field_ptr = @intToPtr(&i32, 0x1234); \\ const another_foo_ptr = @fieldParentPtr(Foo, "b", field_ptr); \\} - , ".tmp_source.zig:9:55: error: pointer value not based on parent struct"); + , + ".tmp_source.zig:9:55: error: pointer value not based on parent struct", + ); - cases.add("@fieldParentPtr - comptime wrong field index", + cases.add( + "@fieldParentPtr - comptime wrong field index", \\const Foo = struct { \\ a: i32, \\ b: i32, @@ -2046,76 +2609,100 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\comptime { \\ const another_foo_ptr = @fieldParentPtr(Foo, "b", &foo.a); \\} - , ".tmp_source.zig:8:29: error: field 'b' has index 1 but pointer value is index 0 of struct 'Foo'"); + , + ".tmp_source.zig:8:29: error: field 'b' has index 1 but pointer value is index 0 of struct 'Foo'", + ); - cases.add("@offsetOf - non struct", + cases.add( + "@offsetOf - non struct", \\const Foo = i32; \\export fn foo() usize { - \\ return @offsetOf(Foo, "a"); + \\ return @offsetOf(Foo, "a",); \\} - , ".tmp_source.zig:3:22: error: expected struct type, found 'i32'"); + , + ".tmp_source.zig:3:22: error: expected struct type, found 'i32'", + ); - cases.add("@offsetOf - bad field name", + cases.add( + "@offsetOf - bad field name", \\const Foo = struct { \\ derp: i32, \\}; \\export fn foo() usize { - \\ return @offsetOf(Foo, "a"); + \\ return @offsetOf(Foo, "a",); \\} - , ".tmp_source.zig:5:27: error: struct 'Foo' has no field 'a'"); + , + ".tmp_source.zig:5:27: error: struct 'Foo' has no field 'a'", + ); - cases.addExe("missing main fn in executable", + cases.addExe( + "missing main fn in executable", \\ - , "error: no member named 'main' in '"); + , + "error: no member named 'main' in '", + ); - cases.addExe("private main fn", + cases.addExe( + "private main fn", \\fn main() void {} , "error: 'main' is private", - ".tmp_source.zig:1:1: note: declared here"); + ".tmp_source.zig:1:1: note: declared here", + ); - cases.add("setting a section on an extern variable", + cases.add( + "setting a section on an extern variable", \\extern var foo: i32 section(".text2"); \\export fn entry() i32 { \\ return foo; \\} , - ".tmp_source.zig:1:29: error: cannot set section of external variable 'foo'"); + ".tmp_source.zig:1:29: error: cannot set section of external variable 'foo'", + ); - cases.add("setting a section on a local variable", + cases.add( + "setting a section on a local variable", \\export fn entry() i32 { \\ var foo: i32 section(".text2") = 1234; \\ return foo; \\} , - ".tmp_source.zig:2:26: error: cannot set section of local variable 'foo'"); + ".tmp_source.zig:2:26: error: cannot set section of local variable 'foo'", + ); - cases.add("setting a section on an extern fn", + cases.add( + "setting a section on an extern fn", \\extern fn foo() section(".text2") void; \\export fn entry() void { \\ foo(); \\} , - ".tmp_source.zig:1:25: error: cannot set section of external function 'foo'"); + ".tmp_source.zig:1:25: error: cannot set section of external function 'foo'", + ); - cases.add("returning address of local variable - simple", + cases.add( + "returning address of local variable - simple", \\export fn foo() &i32 { \\ var a: i32 = undefined; \\ return &a; \\} , - ".tmp_source.zig:3:13: error: function returns address of local variable"); + ".tmp_source.zig:3:13: error: function returns address of local variable", + ); - cases.add("returning address of local variable - phi", + cases.add( + "returning address of local variable - phi", \\export fn foo(c: bool) &i32 { \\ var a: i32 = undefined; \\ var b: i32 = undefined; \\ return if (c) &a else &b; \\} , - ".tmp_source.zig:4:12: error: function returns address of local variable"); + ".tmp_source.zig:4:12: error: function returns address of local variable", + ); - cases.add("inner struct member shadowing outer struct member", + cases.add( + "inner struct member shadowing outer struct member", \\fn A() type { \\ return struct { \\ b: B(), @@ -2137,57 +2724,71 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:9:17: error: redefinition of 'Self'", - ".tmp_source.zig:5:9: note: previous definition is here"); + ".tmp_source.zig:5:9: note: previous definition is here", + ); - cases.add("while expected bool, got nullable", + cases.add( + "while expected bool, got nullable", \\export fn foo() void { \\ while (bar()) {} \\} \\fn bar() ?i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected type 'bool', found '?i32'"); + ".tmp_source.zig:2:15: error: expected type 'bool', found '?i32'", + ); - cases.add("while expected bool, got error union", + cases.add( + "while expected bool, got error union", \\export fn foo() void { \\ while (bar()) {} \\} \\fn bar() error!i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected type 'bool', found 'error!i32'"); + ".tmp_source.zig:2:15: error: expected type 'bool', found 'error!i32'", + ); - cases.add("while expected nullable, got bool", + cases.add( + "while expected nullable, got bool", \\export fn foo() void { \\ while (bar()) |x| {} \\} \\fn bar() bool { return true; } , - ".tmp_source.zig:2:15: error: expected nullable type, found 'bool'"); + ".tmp_source.zig:2:15: error: expected nullable type, found 'bool'", + ); - cases.add("while expected nullable, got error union", + cases.add( + "while expected nullable, got error union", \\export fn foo() void { \\ while (bar()) |x| {} \\} \\fn bar() error!i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected nullable type, found 'error!i32'"); + ".tmp_source.zig:2:15: error: expected nullable type, found 'error!i32'", + ); - cases.add("while expected error union, got bool", + cases.add( + "while expected error union, got bool", \\export fn foo() void { \\ while (bar()) |x| {} else |err| {} \\} \\fn bar() bool { return true; } , - ".tmp_source.zig:2:15: error: expected error union type, found 'bool'"); + ".tmp_source.zig:2:15: error: expected error union type, found 'bool'", + ); - cases.add("while expected error union, got nullable", + cases.add( + "while expected error union, got nullable", \\export fn foo() void { \\ while (bar()) |x| {} else |err| {} \\} \\fn bar() ?i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected error union type, found '?i32'"); + ".tmp_source.zig:2:15: error: expected error union type, found '?i32'", + ); - cases.add("inline fn calls itself indirectly", + cases.add( + "inline fn calls itself indirectly", \\export fn foo() void { \\ bar(); \\} @@ -2201,91 +2802,113 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\extern fn quux() void; , - ".tmp_source.zig:4:8: error: unable to inline function"); + ".tmp_source.zig:4:8: error: unable to inline function", + ); - cases.add("save reference to inline function", + cases.add( + "save reference to inline function", \\export fn foo() void { \\ quux(@ptrToInt(bar)); \\} \\inline fn bar() void { } \\extern fn quux(usize) void; , - ".tmp_source.zig:4:8: error: unable to inline function"); + ".tmp_source.zig:4:8: error: unable to inline function", + ); - cases.add("signed integer division", + cases.add( + "signed integer division", \\export fn foo(a: i32, b: i32) i32 { \\ return a / b; \\} , - ".tmp_source.zig:2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact"); + ".tmp_source.zig:2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact", + ); - cases.add("signed integer remainder division", + cases.add( + "signed integer remainder division", \\export fn foo(a: i32, b: i32) i32 { \\ return a % b; \\} , - ".tmp_source.zig:2:14: error: remainder division with 'i32' and 'i32': signed integers and floats must use @rem or @mod"); + ".tmp_source.zig:2:14: error: remainder division with 'i32' and 'i32': signed integers and floats must use @rem or @mod", + ); - cases.add("cast negative value to unsigned integer", + cases.add( + "cast negative value to unsigned integer", \\comptime { \\ const value: i32 = -1; \\ const unsigned = u32(value); \\} , - ".tmp_source.zig:3:25: error: attempt to cast negative value to unsigned integer"); + ".tmp_source.zig:3:25: error: attempt to cast negative value to unsigned integer", + ); - cases.add("compile-time division by zero", + cases.add( + "compile-time division by zero", \\comptime { \\ const a: i32 = 1; \\ const b: i32 = 0; \\ const c = a / b; \\} , - ".tmp_source.zig:4:17: error: division by zero"); + ".tmp_source.zig:4:17: error: division by zero", + ); - cases.add("compile-time remainder division by zero", + cases.add( + "compile-time remainder division by zero", \\comptime { \\ const a: i32 = 1; \\ const b: i32 = 0; \\ const c = a % b; \\} , - ".tmp_source.zig:4:17: error: division by zero"); + ".tmp_source.zig:4:17: error: division by zero", + ); - cases.add("compile-time integer cast truncates bits", + cases.add( + "compile-time integer cast truncates bits", \\comptime { \\ const spartan_count: u16 = 300; \\ const byte = u8(spartan_count); \\} , - ".tmp_source.zig:3:20: error: cast from 'u16' to 'u8' truncates bits"); + ".tmp_source.zig:3:20: error: cast from 'u16' to 'u8' truncates bits", + ); - cases.add("@setRuntimeSafety twice for same scope", + cases.add( + "@setRuntimeSafety twice for same scope", \\export fn foo() void { \\ @setRuntimeSafety(false); \\ @setRuntimeSafety(false); \\} , ".tmp_source.zig:3:5: error: runtime safety set twice for same scope", - ".tmp_source.zig:2:5: note: first set here"); + ".tmp_source.zig:2:5: note: first set here", + ); - cases.add("@setFloatMode twice for same scope", + cases.add( + "@setFloatMode twice for same scope", \\export fn foo() void { \\ @setFloatMode(this, @import("builtin").FloatMode.Optimized); \\ @setFloatMode(this, @import("builtin").FloatMode.Optimized); \\} , ".tmp_source.zig:3:5: error: float mode set twice for same scope", - ".tmp_source.zig:2:5: note: first set here"); + ".tmp_source.zig:2:5: note: first set here", + ); - cases.add("array access of type", + cases.add( + "array access of type", \\export fn foo() void { \\ var b: u8[40] = undefined; \\} , - ".tmp_source.zig:2:14: error: array access of non-array type 'type'"); + ".tmp_source.zig:2:14: error: array access of non-array type 'type'", + ); - cases.add("cannot break out of defer expression", + cases.add( + "cannot break out of defer expression", \\export fn foo() void { \\ while (true) { \\ defer { @@ -2294,9 +2917,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ } \\} , - ".tmp_source.zig:4:13: error: cannot break out of defer expression"); + ".tmp_source.zig:4:13: error: cannot break out of defer expression", + ); - cases.add("cannot continue out of defer expression", + cases.add( + "cannot continue out of defer expression", \\export fn foo() void { \\ while (true) { \\ defer { @@ -2305,9 +2930,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ } \\} , - ".tmp_source.zig:4:13: error: cannot continue out of defer expression"); + ".tmp_source.zig:4:13: error: cannot continue out of defer expression", + ); - cases.add("calling a var args function only known at runtime", + cases.add( + "calling a var args function only known at runtime", \\var foos = []fn(...) void { foo1, foo2 }; \\ \\fn foo1(args: ...) void {} @@ -2317,9 +2944,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ foos[0](); \\} , - ".tmp_source.zig:7:9: error: calling a generic function requires compile-time known function value"); + ".tmp_source.zig:7:9: error: calling a generic function requires compile-time known function value", + ); - cases.add("calling a generic function only known at runtime", + cases.add( + "calling a generic function only known at runtime", \\var foos = []fn(var) void { foo1, foo2 }; \\ \\fn foo1(arg: var) void {} @@ -2329,10 +2958,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ foos[0](true); \\} , - ".tmp_source.zig:7:9: error: calling a generic function requires compile-time known function value"); + ".tmp_source.zig:7:9: error: calling a generic function requires compile-time known function value", + ); - cases.add("@compileError shows traceback of references that caused it", - \\const foo = @compileError("aoeu"); + cases.add( + "@compileError shows traceback of references that caused it", + \\const foo = @compileError("aoeu",); \\ \\const bar = baz + foo; \\const baz = 1; @@ -2343,9 +2974,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { , ".tmp_source.zig:1:13: error: aoeu", ".tmp_source.zig:3:19: note: referenced here", - ".tmp_source.zig:7:12: note: referenced here"); + ".tmp_source.zig:7:12: note: referenced here", + ); - cases.add("instantiating an undefined value for an invalid struct that contains itself", + cases.add( + "instantiating an undefined value for an invalid struct that contains itself", \\const Foo = struct { \\ x: Foo, \\}; @@ -2356,73 +2989,93 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ return @sizeOf(@typeOf(foo.x)); \\} , - ".tmp_source.zig:1:13: error: struct 'Foo' contains itself"); + ".tmp_source.zig:1:13: error: struct 'Foo' contains itself", + ); - cases.add("float literal too large error", + cases.add( + "float literal too large error", \\comptime { \\ const a = 0x1.0p16384; \\} , - ".tmp_source.zig:2:15: error: float literal out of range of any type"); + ".tmp_source.zig:2:15: error: float literal out of range of any type", + ); - cases.add("float literal too small error (denormal)", + cases.add( + "float literal too small error (denormal)", \\comptime { \\ const a = 0x1.0p-16384; \\} , - ".tmp_source.zig:2:15: error: float literal out of range of any type"); + ".tmp_source.zig:2:15: error: float literal out of range of any type", + ); - cases.add("explicit cast float literal to integer when there is a fraction component", + cases.add( + "explicit cast float literal to integer when there is a fraction component", \\export fn entry() i32 { \\ return i32(12.34); \\} , - ".tmp_source.zig:2:16: error: fractional component prevents float value 12.340000 from being casted to type 'i32'"); + ".tmp_source.zig:2:16: error: fractional component prevents float value 12.340000 from being casted to type 'i32'", + ); - cases.add("non pointer given to @ptrToInt", + cases.add( + "non pointer given to @ptrToInt", \\export fn entry(x: i32) usize { \\ return @ptrToInt(x); \\} , - ".tmp_source.zig:2:22: error: expected pointer, found 'i32'"); + ".tmp_source.zig:2:22: error: expected pointer, found 'i32'", + ); - cases.add("@shlExact shifts out 1 bits", + cases.add( + "@shlExact shifts out 1 bits", \\comptime { \\ const x = @shlExact(u8(0b01010101), 2); \\} , - ".tmp_source.zig:2:15: error: operation caused overflow"); + ".tmp_source.zig:2:15: error: operation caused overflow", + ); - cases.add("@shrExact shifts out 1 bits", + cases.add( + "@shrExact shifts out 1 bits", \\comptime { \\ const x = @shrExact(u8(0b10101010), 2); \\} , - ".tmp_source.zig:2:15: error: exact shift shifted out 1 bits"); + ".tmp_source.zig:2:15: error: exact shift shifted out 1 bits", + ); - cases.add("shifting without int type or comptime known", + cases.add( + "shifting without int type or comptime known", \\export fn entry(x: u8) u8 { \\ return 0x11 << x; \\} , - ".tmp_source.zig:2:17: error: LHS of shift must be an integer type, or RHS must be compile-time known"); + ".tmp_source.zig:2:17: error: LHS of shift must be an integer type, or RHS must be compile-time known", + ); - cases.add("shifting RHS is log2 of LHS int bit width", + cases.add( + "shifting RHS is log2 of LHS int bit width", \\export fn entry(x: u8, y: u8) u8 { \\ return x << y; \\} , - ".tmp_source.zig:2:17: error: expected type 'u3', found 'u8'"); + ".tmp_source.zig:2:17: error: expected type 'u3', found 'u8'", + ); - cases.add("globally shadowing a primitive type", + cases.add( + "globally shadowing a primitive type", \\const u16 = @intType(false, 8); \\export fn entry() void { \\ const a: u16 = 300; \\} , - ".tmp_source.zig:1:1: error: declaration shadows type 'u16'"); + ".tmp_source.zig:1:1: error: declaration shadows type 'u16'", + ); - cases.add("implicitly increasing pointer alignment", + cases.add( + "implicitly increasing pointer alignment", \\const Foo = packed struct { \\ a: u8, \\ b: u32, @@ -2437,9 +3090,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ x.* += 1; \\} , - ".tmp_source.zig:8:13: error: expected type '&u32', found '&align(1) u32'"); + ".tmp_source.zig:8:13: error: expected type '&u32', found '&align(1) u32'", + ); - cases.add("implicitly increasing slice alignment", + cases.add( + "implicitly increasing slice alignment", \\const Foo = packed struct { \\ a: u8, \\ b: u32, @@ -2455,9 +3110,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ x[0] += 1; \\} , - ".tmp_source.zig:9:17: error: expected type '[]u32', found '[]align(1) u32'"); + ".tmp_source.zig:9:17: error: expected type '[]u32', found '[]align(1) u32'", + ); - cases.add("increase pointer alignment in @ptrCast", + cases.add( + "increase pointer alignment in @ptrCast", \\export fn entry() u32 { \\ var bytes: [4]u8 = []u8{0x01, 0x02, 0x03, 0x04}; \\ const ptr = @ptrCast(&u32, &bytes[0]); @@ -2466,9 +3123,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { , ".tmp_source.zig:3:17: error: cast increases pointer alignment", ".tmp_source.zig:3:38: note: '&u8' has alignment 1", - ".tmp_source.zig:3:27: note: '&u32' has alignment 4"); + ".tmp_source.zig:3:27: note: '&u32' has alignment 4", + ); - cases.add("increase pointer alignment in slice resize", + cases.add( + "increase pointer alignment in slice resize", \\export fn entry() u32 { \\ var bytes = []u8{0x01, 0x02, 0x03, 0x04}; \\ return ([]u32)(bytes[0..])[0]; @@ -2476,16 +3135,20 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { , ".tmp_source.zig:3:19: error: cast increases pointer alignment", ".tmp_source.zig:3:19: note: '[]u8' has alignment 1", - ".tmp_source.zig:3:19: note: '[]u32' has alignment 4"); + ".tmp_source.zig:3:19: note: '[]u32' has alignment 4", + ); - cases.add("@alignCast expects pointer or slice", + cases.add( + "@alignCast expects pointer or slice", \\export fn entry() void { \\ @alignCast(4, u32(3)); \\} , - ".tmp_source.zig:2:22: error: expected pointer or slice, found 'u32'"); + ".tmp_source.zig:2:22: error: expected pointer or slice, found 'u32'", + ); - cases.add("passing an under-aligned function pointer", + cases.add( + "passing an under-aligned function pointer", \\export fn entry() void { \\ testImplicitlyDecreaseFnAlign(alignedSmall, 1234); \\} @@ -2494,9 +3157,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\fn alignedSmall() align(4) i32 { return 1234; } , - ".tmp_source.zig:2:35: error: expected type 'fn() align(8) i32', found 'fn() align(4) i32'"); + ".tmp_source.zig:2:35: error: expected type 'fn() align(8) i32', found 'fn() align(4) i32'", + ); - cases.add("passing a not-aligned-enough pointer to cmpxchg", + cases.add( + "passing a not-aligned-enough pointer to cmpxchg", \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn entry() bool { \\ var x: i32 align(1) = 1234; @@ -2504,16 +3169,20 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ return x == 5678; \\} , - ".tmp_source.zig:4:32: error: expected type '&i32', found '&align(1) i32'"); + ".tmp_source.zig:4:32: error: expected type '&i32', found '&align(1) i32'", + ); - cases.add("wrong size to an array literal", + cases.add( + "wrong size to an array literal", \\comptime { \\ const array = [2]u8{1, 2, 3}; \\} , - ".tmp_source.zig:2:24: error: expected [2]u8 literal, found [3]u8 literal"); + ".tmp_source.zig:2:24: error: expected [2]u8 literal, found [3]u8 literal", + ); - cases.add("@setEvalBranchQuota in non-root comptime execution context", + cases.add( + "@setEvalBranchQuota in non-root comptime execution context", \\comptime { \\ foo(); \\} @@ -2523,9 +3192,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { , ".tmp_source.zig:5:5: error: @setEvalBranchQuota must be called from the top of the comptime stack", ".tmp_source.zig:2:8: note: called from here", - ".tmp_source.zig:1:10: note: called from here"); + ".tmp_source.zig:1:10: note: called from here", + ); - cases.add("wrong pointer implicitly casted to pointer to @OpaqueType()", + cases.add( + "wrong pointer implicitly casted to pointer to @OpaqueType()", \\const Derp = @OpaqueType(); \\extern fn bar(d: &Derp) void; \\export fn foo() void { @@ -2533,9 +3204,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ bar(@ptrCast(&c_void, &x)); \\} , - ".tmp_source.zig:5:9: error: expected type '&Derp', found '&c_void'"); + ".tmp_source.zig:5:9: error: expected type '&Derp', found '&c_void'", + ); - cases.add("non-const variables of things that require const variables", + cases.add( + "non-const variables of things that require const variables", \\const Opaque = @OpaqueType(); \\ \\export fn entry(opaque: &Opaque) void { @@ -2549,7 +3222,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var e = null; \\ var f = opaque.*; \\ var g = i32; - \\ var h = @import("std"); + \\ var h = @import("std",); \\ var i = (Foo {}).bar; \\ \\ var z: noreturn = return; @@ -2569,26 +3242,32 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { ".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime", ".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime", ".tmp_source.zig:15:4: error: variable of type '(bound fn(&const Foo) void)' must be const or comptime", - ".tmp_source.zig:17:4: error: unreachable code"); + ".tmp_source.zig:17:4: error: unreachable code", + ); - cases.add("wrong types given to atomic order args in cmpxchg", + cases.add( + "wrong types given to atomic order args in cmpxchg", \\export fn entry() void { \\ var x: i32 = 1234; \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, u32(1234), u32(1234))) {} \\} , - ".tmp_source.zig:3:50: error: expected type 'AtomicOrder', found 'u32'"); + ".tmp_source.zig:3:50: error: expected type 'AtomicOrder', found 'u32'", + ); - cases.add("wrong types given to @export", + cases.add( + "wrong types given to @export", \\extern fn entry() void { } \\comptime { \\ @export("entry", entry, u32(1234)); \\} , - ".tmp_source.zig:3:32: error: expected type 'GlobalLinkage', found 'u32'"); + ".tmp_source.zig:3:32: error: expected type 'GlobalLinkage', found 'u32'", + ); - cases.add("struct with invalid field", - \\const std = @import("std"); + cases.add( + "struct with invalid field", + \\const std = @import("std",); \\const Allocator = std.mem.Allocator; \\const ArrayList = std.ArrayList; \\ @@ -2612,23 +3291,29 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ }; \\} , - ".tmp_source.zig:14:17: error: use of undeclared identifier 'HeaderValue'"); + ".tmp_source.zig:14:17: error: use of undeclared identifier 'HeaderValue'", + ); - cases.add("@setAlignStack outside function", + cases.add( + "@setAlignStack outside function", \\comptime { \\ @setAlignStack(16); \\} , - ".tmp_source.zig:2:5: error: @setAlignStack outside function"); + ".tmp_source.zig:2:5: error: @setAlignStack outside function", + ); - cases.add("@setAlignStack in naked function", + cases.add( + "@setAlignStack in naked function", \\export nakedcc fn entry() void { \\ @setAlignStack(16); \\} , - ".tmp_source.zig:2:5: error: @setAlignStack in naked function"); + ".tmp_source.zig:2:5: error: @setAlignStack in naked function", + ); - cases.add("@setAlignStack in inline function", + cases.add( + "@setAlignStack in inline function", \\export fn entry() void { \\ foo(); \\} @@ -2636,25 +3321,31 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ @setAlignStack(16); \\} , - ".tmp_source.zig:5:5: error: @setAlignStack in inline function"); + ".tmp_source.zig:5:5: error: @setAlignStack in inline function", + ); - cases.add("@setAlignStack set twice", + cases.add( + "@setAlignStack set twice", \\export fn entry() void { \\ @setAlignStack(16); \\ @setAlignStack(16); \\} , ".tmp_source.zig:3:5: error: alignstack set twice", - ".tmp_source.zig:2:5: note: first set here"); + ".tmp_source.zig:2:5: note: first set here", + ); - cases.add("@setAlignStack too big", + cases.add( + "@setAlignStack too big", \\export fn entry() void { \\ @setAlignStack(511 + 1); \\} , - ".tmp_source.zig:2:5: error: attempt to @setAlignStack(512); maximum is 256"); + ".tmp_source.zig:2:5: error: attempt to @setAlignStack(512); maximum is 256", + ); - cases.add("storing runtime value in compile time variable then using it", + cases.add( + "storing runtime value in compile time variable then using it", \\const Mode = @import("builtin").Mode; \\ \\fn Free(comptime filename: []const u8) TestCase { @@ -2697,9 +3388,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ } \\} , - ".tmp_source.zig:37:16: error: cannot store runtime value in compile time variable"); + ".tmp_source.zig:37:16: error: cannot store runtime value in compile time variable", + ); - cases.add("field access of opaque type", + cases.add( + "field access of opaque type", \\const MyType = @OpaqueType(); \\ \\export fn entry() bool { @@ -2711,120 +3404,148 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ return x.blah; \\} , - ".tmp_source.zig:9:13: error: type '&MyType' does not support field access"); + ".tmp_source.zig:9:13: error: type '&MyType' does not support field access", + ); - cases.add("carriage return special case", + cases.add( + "carriage return special case", "fn test() bool {\r\n" ++ - " true\r\n" ++ - "}\r\n" - , - ".tmp_source.zig:1:17: error: invalid carriage return, only '\\n' line endings are supported"); - - cases.add("non-printable invalid character", - "\xff\xfe" ++ - \\fn test() bool {\r - \\ true\r - \\} - , - ".tmp_source.zig:1:1: error: invalid character: '\\xff'"); + " true\r\n" ++ + "}\r\n", + ".tmp_source.zig:1:17: error: invalid carriage return, only '\\n' line endings are supported", + ); + + cases.add( + "non-printable invalid character", + "\xff\xfe" ++ + \\fn test() bool {\r + \\ true\r + \\} + , + ".tmp_source.zig:1:1: error: invalid character: '\\xff'", + ); - cases.add("non-printable invalid character with escape alternative", + cases.add( + "non-printable invalid character with escape alternative", "fn test() bool {\n" ++ - "\ttrue\n" ++ - "}\n" - , - ".tmp_source.zig:2:1: error: invalid character: '\\t'"); + "\ttrue\n" ++ + "}\n", + ".tmp_source.zig:2:1: error: invalid character: '\\t'", + ); - cases.add("@ArgType given non function parameter", + cases.add( + "@ArgType given non function parameter", \\comptime { \\ _ = @ArgType(i32, 3); \\} , - ".tmp_source.zig:2:18: error: expected function, found 'i32'"); + ".tmp_source.zig:2:18: error: expected function, found 'i32'", + ); - cases.add("@ArgType arg index out of bounds", + cases.add( + "@ArgType arg index out of bounds", \\comptime { \\ _ = @ArgType(@typeOf(add), 2); \\} \\fn add(a: i32, b: i32) i32 { return a + b; } , - ".tmp_source.zig:2:32: error: arg index 2 out of bounds; 'fn(i32, i32) i32' has 2 arguments"); + ".tmp_source.zig:2:32: error: arg index 2 out of bounds; 'fn(i32, i32) i32' has 2 arguments", + ); - cases.add("@memberType on unsupported type", + cases.add( + "@memberType on unsupported type", \\comptime { \\ _ = @memberType(i32, 0); \\} , - ".tmp_source.zig:2:21: error: type 'i32' does not support @memberType"); + ".tmp_source.zig:2:21: error: type 'i32' does not support @memberType", + ); - cases.add("@memberType on enum", + cases.add( + "@memberType on enum", \\comptime { \\ _ = @memberType(Foo, 0); \\} \\const Foo = enum {A,}; , - ".tmp_source.zig:2:21: error: type 'Foo' does not support @memberType"); + ".tmp_source.zig:2:21: error: type 'Foo' does not support @memberType", + ); - cases.add("@memberType struct out of bounds", + cases.add( + "@memberType struct out of bounds", \\comptime { \\ _ = @memberType(Foo, 0); \\} \\const Foo = struct {}; , - ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); + ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members", + ); - cases.add("@memberType union out of bounds", + cases.add( + "@memberType union out of bounds", \\comptime { \\ _ = @memberType(Foo, 1); \\} \\const Foo = union {A: void,}; , - ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members"); + ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members", + ); - cases.add("@memberName on unsupported type", + cases.add( + "@memberName on unsupported type", \\comptime { \\ _ = @memberName(i32, 0); \\} , - ".tmp_source.zig:2:21: error: type 'i32' does not support @memberName"); + ".tmp_source.zig:2:21: error: type 'i32' does not support @memberName", + ); - cases.add("@memberName struct out of bounds", + cases.add( + "@memberName struct out of bounds", \\comptime { \\ _ = @memberName(Foo, 0); \\} \\const Foo = struct {}; , - ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); + ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members", + ); - cases.add("@memberName enum out of bounds", + cases.add( + "@memberName enum out of bounds", \\comptime { \\ _ = @memberName(Foo, 1); \\} \\const Foo = enum {A,}; , - ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members"); + ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members", + ); - cases.add("@memberName union out of bounds", + cases.add( + "@memberName union out of bounds", \\comptime { \\ _ = @memberName(Foo, 1); \\} \\const Foo = union {A:i32,}; , - ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members"); + ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members", + ); - cases.add("calling var args extern function, passing array instead of pointer", + cases.add( + "calling var args extern function, passing array instead of pointer", \\export fn entry() void { - \\ foo("hello"); + \\ foo("hello",); \\} \\pub extern fn foo(format: &const u8, ...) void; , - ".tmp_source.zig:2:9: error: expected type '&const u8', found '[5]u8'"); + ".tmp_source.zig:2:9: error: expected type '&const u8', found '[5]u8'", + ); - cases.add("constant inside comptime function has compile error", + cases.add( + "constant inside comptime function has compile error", \\const ContextAllocator = MemoryPool(usize); \\ \\pub fn MemoryPool(comptime T: type) type { - \\ const free_list_t = @compileError("aoeu"); + \\ const free_list_t = @compileError("aoeu",); \\ \\ return struct { \\ free_list: free_list_t, @@ -2837,9 +3558,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { , ".tmp_source.zig:4:25: error: aoeu", ".tmp_source.zig:1:36: note: called from here", - ".tmp_source.zig:12:20: note: referenced here"); + ".tmp_source.zig:12:20: note: referenced here", + ); - cases.add("specify enum tag type that is too small", + cases.add( + "specify enum tag type that is too small", \\const Small = enum (u2) { \\ One, \\ Two, @@ -2852,9 +3575,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var x = Small.One; \\} , - ".tmp_source.zig:1:20: error: 'u2' too small to hold all bits; must be at least 'u3'"); + ".tmp_source.zig:1:20: error: 'u2' too small to hold all bits; must be at least 'u3'", + ); - cases.add("specify non-integer enum tag type", + cases.add( + "specify non-integer enum tag type", \\const Small = enum (f32) { \\ One, \\ Two, @@ -2865,9 +3590,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var x = Small.One; \\} , - ".tmp_source.zig:1:20: error: expected integer, found 'f32'"); + ".tmp_source.zig:1:20: error: expected integer, found 'f32'", + ); - cases.add("implicitly casting enum to tag type", + cases.add( + "implicitly casting enum to tag type", \\const Small = enum(u2) { \\ One, \\ Two, @@ -2879,9 +3606,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var x: u2 = Small.Two; \\} , - ".tmp_source.zig:9:22: error: expected type 'u2', found 'Small'"); + ".tmp_source.zig:9:22: error: expected type 'u2', found 'Small'", + ); - cases.add("explicitly casting enum to non tag type", + cases.add( + "explicitly casting enum to non tag type", \\const Small = enum(u2) { \\ One, \\ Two, @@ -2893,9 +3622,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var x = u3(Small.Two); \\} , - ".tmp_source.zig:9:15: error: enum to integer cast to 'u3' instead of its tag type, 'u2'"); + ".tmp_source.zig:9:15: error: enum to integer cast to 'u3' instead of its tag type, 'u2'", + ); - cases.add("explicitly casting non tag type to enum", + cases.add( + "explicitly casting non tag type to enum", \\const Small = enum(u2) { \\ One, \\ Two, @@ -2908,9 +3639,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var x = Small(y); \\} , - ".tmp_source.zig:10:18: error: integer to enum cast from 'u3' instead of its tag type, 'u2'"); + ".tmp_source.zig:10:18: error: integer to enum cast from 'u3' instead of its tag type, 'u2'", + ); - cases.add("non unsigned integer enum tag type", + cases.add( + "non unsigned integer enum tag type", \\const Small = enum(i2) { \\ One, \\ Two, @@ -2922,9 +3655,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var y = Small.Two; \\} , - ".tmp_source.zig:1:19: error: expected unsigned integer, found 'i2'"); + ".tmp_source.zig:1:19: error: expected unsigned integer, found 'i2'", + ); - cases.add("struct fields with value assignments", + cases.add( + "struct fields with value assignments", \\const MultipleChoice = struct { \\ A: i32 = 20, \\}; @@ -2932,9 +3667,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var x: MultipleChoice = undefined; \\} , - ".tmp_source.zig:2:14: error: enums, not structs, support field assignment"); + ".tmp_source.zig:2:14: error: enums, not structs, support field assignment", + ); - cases.add("union fields with value assignments", + cases.add( + "union fields with value assignments", \\const MultipleChoice = union { \\ A: i32 = 20, \\}; @@ -2943,25 +3680,31 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:2:14: error: non-enum union field assignment", - ".tmp_source.zig:1:24: note: consider 'union(enum)' here"); + ".tmp_source.zig:1:24: note: consider 'union(enum)' here", + ); - cases.add("enum with 0 fields", + cases.add( + "enum with 0 fields", \\const Foo = enum {}; \\export fn entry() usize { \\ return @sizeOf(Foo); \\} , - ".tmp_source.zig:1:13: error: enums must have 1 or more fields"); + ".tmp_source.zig:1:13: error: enums must have 1 or more fields", + ); - cases.add("union with 0 fields", + cases.add( + "union with 0 fields", \\const Foo = union {}; \\export fn entry() usize { \\ return @sizeOf(Foo); \\} , - ".tmp_source.zig:1:13: error: unions must have 1 or more fields"); + ".tmp_source.zig:1:13: error: unions must have 1 or more fields", + ); - cases.add("enum value already taken", + cases.add( + "enum value already taken", \\const MultipleChoice = enum(u32) { \\ A = 20, \\ B = 40, @@ -2974,9 +3717,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:6:9: error: enum tag value 60 already taken", - ".tmp_source.zig:4:9: note: other occurrence here"); + ".tmp_source.zig:4:9: note: other occurrence here", + ); - cases.add("union with specified enum omits field", + cases.add( + "union with specified enum omits field", \\const Letter = enum { \\ A, \\ B, @@ -2991,9 +3736,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:6:17: error: enum field missing: 'C'", - ".tmp_source.zig:4:5: note: declared here"); + ".tmp_source.zig:4:5: note: declared here", + ); - cases.add("@TagType when union has no attached enum", + cases.add( + "@TagType when union has no attached enum", \\const Foo = union { \\ A: i32, \\}; @@ -3002,9 +3749,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:5:24: error: union 'Foo' has no tag", - ".tmp_source.zig:1:13: note: consider 'union(enum)' here"); + ".tmp_source.zig:1:13: note: consider 'union(enum)' here", + ); - cases.add("non-integer tag type to automatic union enum", + cases.add( + "non-integer tag type to automatic union enum", \\const Foo = union(enum(f32)) { \\ A: i32, \\}; @@ -3012,9 +3761,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ const x = @TagType(Foo); \\} , - ".tmp_source.zig:1:23: error: expected integer tag type, found 'f32'"); + ".tmp_source.zig:1:23: error: expected integer tag type, found 'f32'", + ); - cases.add("non-enum tag type passed to union", + cases.add( + "non-enum tag type passed to union", \\const Foo = union(u32) { \\ A: i32, \\}; @@ -3022,9 +3773,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ const x = @TagType(Foo); \\} , - ".tmp_source.zig:1:18: error: expected enum tag type, found 'u32'"); + ".tmp_source.zig:1:18: error: expected enum tag type, found 'u32'", + ); - cases.add("union auto-enum value already taken", + cases.add( + "union auto-enum value already taken", \\const MultipleChoice = union(enum(u32)) { \\ A = 20, \\ B = 40, @@ -3037,9 +3790,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:6:9: error: enum tag value 60 already taken", - ".tmp_source.zig:4:9: note: other occurrence here"); + ".tmp_source.zig:4:9: note: other occurrence here", + ); - cases.add("union enum field does not match enum", + cases.add( + "union enum field does not match enum", \\const Letter = enum { \\ A, \\ B, @@ -3056,9 +3811,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:10:5: error: enum field not found: 'D'", - ".tmp_source.zig:1:16: note: enum declared here"); + ".tmp_source.zig:1:16: note: enum declared here", + ); - cases.add("field type supplied in an enum", + cases.add( + "field type supplied in an enum", \\const Letter = enum { \\ A: void, \\ B, @@ -3069,9 +3826,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:2:8: error: structs and unions, not enums, support field types", - ".tmp_source.zig:1:16: note: consider 'union(enum)' here"); + ".tmp_source.zig:1:16: note: consider 'union(enum)' here", + ); - cases.add("struct field missing type", + cases.add( + "struct field missing type", \\const Letter = struct { \\ A, \\}; @@ -3079,9 +3838,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var a = Letter { .A = {} }; \\} , - ".tmp_source.zig:2:5: error: struct field missing type"); + ".tmp_source.zig:2:5: error: struct field missing type", + ); - cases.add("extern union field missing type", + cases.add( + "extern union field missing type", \\const Letter = extern union { \\ A, \\}; @@ -3089,9 +3850,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var a = Letter { .A = {} }; \\} , - ".tmp_source.zig:2:5: error: union field missing type"); + ".tmp_source.zig:2:5: error: union field missing type", + ); - cases.add("extern union given enum tag type", + cases.add( + "extern union given enum tag type", \\const Letter = enum { \\ A, \\ B, @@ -3106,9 +3869,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var a = Payload { .A = 1234 }; \\} , - ".tmp_source.zig:6:29: error: extern union does not support enum tag type"); + ".tmp_source.zig:6:29: error: extern union does not support enum tag type", + ); - cases.add("packed union given enum tag type", + cases.add( + "packed union given enum tag type", \\const Letter = enum { \\ A, \\ B, @@ -3123,9 +3888,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var a = Payload { .A = 1234 }; \\} , - ".tmp_source.zig:6:29: error: packed union does not support enum tag type"); + ".tmp_source.zig:6:29: error: packed union does not support enum tag type", + ); - cases.add("switch on union with no attached enum", + cases.add( + "switch on union with no attached enum", \\const Payload = union { \\ A: i32, \\ B: f64, @@ -3143,9 +3910,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:11:14: error: switch on union which has no attached enum", - ".tmp_source.zig:1:17: note: consider 'union(enum)' here"); + ".tmp_source.zig:1:17: note: consider 'union(enum)' here", + ); - cases.add("enum in field count range but not matching tag", + cases.add( + "enum in field count range but not matching tag", \\const Foo = enum(u32) { \\ A = 10, \\ B = 11, @@ -3155,9 +3924,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:6:16: error: enum 'Foo' has no tag matching integer value 0", - ".tmp_source.zig:1:13: note: 'Foo' declared here"); + ".tmp_source.zig:1:13: note: 'Foo' declared here", + ); - cases.add("comptime cast enum to union but field has payload", + cases.add( + "comptime cast enum to union but field has payload", \\const Letter = enum { A, B, C }; \\const Value = union(Letter) { \\ A: i32, @@ -3169,9 +3940,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A'", - ".tmp_source.zig:3:5: note: field 'A' declared here"); + ".tmp_source.zig:3:5: note: field 'A' declared here", + ); - cases.add("runtime cast to union which has non-void fields", + cases.add( + "runtime cast to union which has non-void fields", \\const Letter = enum { A, B, C }; \\const Value = union(Letter) { \\ A: i32, @@ -3186,9 +3959,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:11:20: error: runtime cast to union 'Value' which has non-void fields", - ".tmp_source.zig:3:5: note: field 'A' has type 'i32'"); + ".tmp_source.zig:3:5: note: field 'A' has type 'i32'", + ); - cases.add("self-referencing function pointer field", + cases.add( + "self-referencing function pointer field", \\const S = struct { \\ f: fn(_: S) void, \\}; @@ -3198,19 +3973,23 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ var _ = S { .f = f }; \\} , - ".tmp_source.zig:4:9: error: type 'S' is not copyable; cannot pass by value"); + ".tmp_source.zig:4:9: error: type 'S' is not copyable; cannot pass by value", + ); - cases.add("taking offset of void field in struct", + cases.add( + "taking offset of void field in struct", \\const Empty = struct { \\ val: void, \\}; \\export fn foo() void { - \\ const fieldOffset = @offsetOf(Empty, "val"); + \\ const fieldOffset = @offsetOf(Empty, "val",); \\} , - ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset"); + ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset", + ); - cases.add("invalid union field access in comptime", + cases.add( + "invalid union field access in comptime", \\const Foo = union { \\ Bar: u8, \\ Baz: void, @@ -3220,21 +3999,26 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ const bar_val = foo.Bar; \\} , - ".tmp_source.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set"); + ".tmp_source.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set", + ); - cases.add("getting return type of generic function", + cases.add( + "getting return type of generic function", \\fn generic(a: var) void {} \\comptime { \\ _ = @typeOf(generic).ReturnType; \\} , - ".tmp_source.zig:3:25: error: ReturnType has not been resolved because 'fn(var)var' is generic"); + ".tmp_source.zig:3:25: error: ReturnType has not been resolved because 'fn(var)var' is generic", + ); - cases.add("getting @ArgType of generic function", + cases.add( + "getting @ArgType of generic function", \\fn generic(a: var) void {} \\comptime { \\ _ = @ArgType(@typeOf(generic), 0); \\} , - ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic"); + ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", + ); } diff --git a/test/gen_h.zig b/test/gen_h.zig index 30d168cf2c..2def39bed7 100644 --- a/test/gen_h.zig +++ b/test/gen_h.zig @@ -76,5 +76,4 @@ pub fn addCases(cases: &tests.GenHContext) void { \\TEST_EXPORT void entry(struct Foo foo, uint8_t bar[]); \\ ); - } diff --git a/test/standalone/brace_expansion/main.zig b/test/standalone/brace_expansion/main.zig index 93089ae1ff..c96cc2cbb9 100644 --- a/test/standalone/brace_expansion/main.zig +++ b/test/standalone/brace_expansion/main.zig @@ -29,8 +29,7 @@ fn tokenize(input: []const u8) !ArrayList(Token) { for (input) |b, i| { switch (state) { State.Start => switch (b) { - 'a' ... 'z', - 'A' ... 'Z' => { + 'a'...'z', 'A'...'Z' => { state = State.Word; tok_begin = i; }, @@ -40,11 +39,8 @@ fn tokenize(input: []const u8) !ArrayList(Token) { else => return error.InvalidInput, }, State.Word => switch (b) { - 'a' ... 'z', - 'A' ... 'Z' => {}, - '{', - '}', - ',' => { + 'a'...'z', 'A'...'Z' => {}, + '{', '}', ',' => { try token_list.append(Token{ .Word = input[tok_begin..i] }); switch (b) { '{' => try token_list.append(Token.OpenBrace), @@ -103,8 +99,7 @@ fn parse(tokens: &const ArrayList(Token), token_index: &usize) ParseError!Node { }; switch (tokens.items[token_index.*]) { - Token.Word, - Token.OpenBrace => { + Token.Word, Token.OpenBrace => { const pair = try global_allocator.alloc(Node, 2); pair[0] = result_node; pair[1] = try parse(tokens, token_index); diff --git a/test/standalone/issue_339/test.zig b/test/standalone/issue_339/test.zig index f65b9f734e..da0747b8e6 100644 --- a/test/standalone/issue_339/test.zig +++ b/test/standalone/issue_339/test.zig @@ -1,5 +1,8 @@ const StackTrace = @import("builtin").StackTrace; -pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) noreturn { @breakpoint(); while (true) {} } +pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) noreturn { + @breakpoint(); + while (true) {} +} fn bar() error!void {} diff --git a/test/standalone/pkg_import/pkg.zig b/test/standalone/pkg_import/pkg.zig index abb977a2ef..19ab525b81 100644 --- a/test/standalone/pkg_import/pkg.zig +++ b/test/standalone/pkg_import/pkg.zig @@ -1 +1,3 @@ -pub fn add(a: i32, b: i32) i32 { return a + b; } +pub fn add(a: i32, b: i32) i32 { + return a + b; +} diff --git a/test/standalone/use_alias/main.zig b/test/standalone/use_alias/main.zig index 40cab9ad8a..873393cef7 100644 --- a/test/standalone/use_alias/main.zig +++ b/test/standalone/use_alias/main.zig @@ -2,7 +2,7 @@ const c = @import("c.zig"); const assert = @import("std").debug.assert; test "symbol exists" { - var foo = c.Foo { + var foo = c.Foo{ .a = 1, .b = 1, }; diff --git a/test/translate_c.zig b/test/translate_c.zig index 2054cfa246..4cf1e047fa 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -638,7 +638,6 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\} ); - cases.addC("c style cast", \\int float_to_int(float a) { \\ return (int)a; @@ -1289,29 +1288,29 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ } \\} , - \\pub fn switch_fn(i: c_int) c_int { - \\ var res: c_int = 0; - \\ __switch: { - \\ __case_2: { - \\ __default: { - \\ __case_1: { - \\ __case_0: { - \\ switch (i) { - \\ 0 => break :__case_0, - \\ 1 => break :__case_1, - \\ else => break :__default, - \\ 2 => break :__case_2, - \\ } - \\ } - \\ res = 1; - \\ } - \\ res = 2; - \\ } - \\ res = (3 * i); - \\ break :__switch; - \\ } - \\ res = 5; - \\ } - \\} + \\pub fn switch_fn(i: c_int) c_int { + \\ var res: c_int = 0; + \\ __switch: { + \\ __case_2: { + \\ __default: { + \\ __case_1: { + \\ __case_0: { + \\ switch (i) { + \\ 0 => break :__case_0, + \\ 1 => break :__case_1, + \\ else => break :__default, + \\ 2 => break :__case_2, + \\ } + \\ } + \\ res = 1; + \\ } + \\ res = 2; + \\ } + \\ res = (3 * i); + \\ break :__switch; + \\ } + \\ res = 5; + \\ } + \\} ); } -- cgit v1.2.3 From d172e3f3bbb61956d8d2202fd008b61f07eb3121 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 May 2018 17:38:50 -0400 Subject: fix AtomicFile for relative paths closes #1017 --- std/os/index.zig | 14 ++++++++++---- std/os/path.zig | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index db182ed669..645d993256 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -855,14 +855,20 @@ pub const AtomicFile = struct { const dirname = os.path.dirname(dest_path); var rand_buf: [12]u8 = undefined; - const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len)); + + const dirname_component_len = if (dirname.len == 0) 0 else dirname.len + 1; + const tmp_path = try allocator.alloc(u8, dirname_component_len + + base64.Base64Encoder.calcSize(rand_buf.len)); errdefer allocator.free(tmp_path); - mem.copy(u8, tmp_path[0..], dirname); - tmp_path[dirname.len] = os.path.sep; + + if (dirname.len != 0) { + mem.copy(u8, tmp_path[0..], dirname); + tmp_path[dirname.len] = os.path.sep; + } while (true) { try getRandomBytes(rand_buf[0..]); - b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf); + b64_fs_encoder.encode(tmp_path[dirname_component_len..], rand_buf); const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) { error.PathAlreadyExists => continue, diff --git a/std/os/path.zig b/std/os/path.zig index 5b2b031aab..f2b3bb9b0a 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -647,6 +647,8 @@ fn testResolvePosix(paths: []const []const u8) []u8 { return resolvePosix(debug.global_allocator, paths) catch unreachable; } +/// If the path is a file in the current directory (no directory component) +/// then the returned slice has .len = 0. pub fn dirname(path: []const u8) []const u8 { if (is_windows) { return dirnameWindows(path); -- cgit v1.2.3 From b0eebfa560b7c05859a535bf46abd8b9cf9306b3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 May 2018 18:10:36 -0400 Subject: fix syntax of std/json_test.zig See #663 --- std/json_test.zig | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/std/json_test.zig b/std/json_test.zig index 60a5d1288c..cb054d8e4e 100644 --- a/std/json_test.zig +++ b/std/json_test.zig @@ -81,9 +81,7 @@ test "y_array_with_several_null" { } test "y_array_with_trailing_space" { - ok( - "[2] " - ); + ok("[2] "); } test "y_number_0e+1" { @@ -579,9 +577,7 @@ test "y_structure_true_in_array" { } test "y_structure_whitespace_array" { - ok( - " [] " - ); + ok(" [] "); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -696,7 +692,6 @@ test "n_array_newlines_unclosed" { ); } - test "n_array_number_and_comma" { err( \\[1,] @@ -971,7 +966,6 @@ test "n_number_invalid-utf-8-in-int" { ); } - test "n_number_++" { err( \\[++1234] @@ -1228,7 +1222,7 @@ test "n_object_unterminated-value" { err( \\{"a":"a ); - } +} test "n_object_with_single_string" { err( @@ -1243,9 +1237,7 @@ test "n_object_with_trailing_garbage" { } test "n_single_space" { - err( - " " - ); + err(" "); } test "n_string_1_surrogate_then_escape" { @@ -1279,9 +1271,7 @@ test "n_string_accentuated_char_no_quotes" { } test "n_string_backslash_00" { - err( - \\["\"] - ); + err("[\"\x00\"]"); } test "n_string_escaped_backslash_bad" { @@ -1291,9 +1281,7 @@ test "n_string_escaped_backslash_bad" { } test "n_string_escaped_ctrl_char_tab" { - err( - \\["\ "] - ); + err("\x5b\x22\x5c\x09\x22\x5d"); } test "n_string_escaped_emoji" { @@ -1416,9 +1404,7 @@ test "n_string_with_trailing_garbage" { } test "n_structure_100000_opening_arrays" { - err( - "[" ** 100000 - ); + err("[" ** 100000); } test "n_structure_angle_bracket_." { @@ -1558,9 +1544,7 @@ test "n_structure_open_array_comma" { } test "n_structure_open_array_object" { - err( - "[{\"\":" ** 50000 - ); + err("[{\"\":" ** 50000); } test "n_structure_open_array_open_object" { @@ -1900,9 +1884,7 @@ test "i_string_UTF8_surrogate_U+D800" { } test "i_structure_500_nested_arrays" { - any( - ("[" ** 500) ++ ("]" ** 500) - ); + any(("[" ** 500) ++ ("]" ** 500)); } test "i_structure_UTF-8_BOM_empty_object" { -- cgit v1.2.3 From 2b3af4ef6b79b8fe178656b069f837eff82ae8c3 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Wed, 30 May 2018 10:30:09 +0200 Subject: fixed #1009 ir_make_type_info_defs already calls resolve_top_level_decl on all Tld when building the def array. This means, that there is no reason that analyze_fn_body is nessesary, as the fn type should have already been resolved completly. The only thing analyze_fn_body does here, is cause problems with generic functions. --- src/ir.cpp | 4 ---- test/cases/type_info.zig | 7 +++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 440063d58d..5d182fe9b0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15982,10 +15982,6 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; assert(!fn_entry->is_test); - analyze_fn_body(ira->codegen, fn_entry); - if (fn_entry->anal_state == FnAnalStateInvalid) - return; - AstNodeFnProto *fn_node = (AstNodeFnProto *)(fn_entry->proto_node); ConstExprValue *fn_def_val = create_const_vals(1); diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index eee5d1f2ca..05266feb9c 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -233,3 +233,10 @@ fn testFunction() void { fn foo(comptime a: usize, b: bool, args: ...) usize { return 0; } + +test "typeInfo with comptime parameter in struct fn def" { + const S = struct { + pub fn func(comptime x: f32) void {} + }; + comptime var info = @typeInfo(S); +} -- cgit v1.2.3 From 1b3aaacba260e4c8d89ac98ab856ff9b3c77dac4 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Wed, 30 May 2018 10:34:20 +0200 Subject: Removed copy-pasted resolve_inferred_error_set both ir.cpp and analyze.cpp have a function resolve_inferred_error_set, which is a nearly exact copy-paste. This commit removes the one in ir.cpp and exposes then one in analyze.cpp. This also allows us to make analyze_fn_body local to analyze.cpp, as it is not used anywhere in ir.cpp after this change --- src/analyze.cpp | 7 ++++--- src/analyze.hpp | 2 +- src/ir.cpp | 62 +++++++++++++++++++-------------------------------------- 3 files changed, 25 insertions(+), 46 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index c59fde8ef6..b00e18a9a1 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -25,6 +25,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type); static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type); static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type); static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type); +static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry); ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { if (node->owner->c_import_node != nullptr) { @@ -3880,7 +3881,7 @@ static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entr } } -static bool analyze_resolve_inferred_error_set(CodeGen *g, TypeTableEntry *err_set_type, AstNode *source_node) { +bool resolve_inferred_error_set(CodeGen *g, TypeTableEntry *err_set_type, AstNode *source_node) { FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn; if (infer_fn != nullptr) { if (infer_fn->anal_state == FnAnalStateInvalid) { @@ -3932,7 +3933,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ } if (inferred_err_set_type->data.error_set.infer_fn != nullptr) { - if (!analyze_resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) { + if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) { fn_table_entry->anal_state = FnAnalStateInvalid; return; } @@ -3962,7 +3963,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ fn_table_entry->anal_state = FnAnalStateComplete; } -void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { +static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { assert(fn_table_entry->anal_state != FnAnalStateProbing); if (fn_table_entry->anal_state != FnAnalStateReady) return; diff --git a/src/analyze.hpp b/src/analyze.hpp index 56ca21a93f..d538f042ce 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -191,7 +191,7 @@ void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, G ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name); TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g); -void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry); +bool resolve_inferred_error_set(CodeGen *g, TypeTableEntry *err_set_type, AstNode *source_node); TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry); diff --git a/src/ir.cpp b/src/ir.cpp index 5d182fe9b0..8d32a81e25 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7633,38 +7633,16 @@ static bool slice_is_const(TypeTableEntry *type) { return type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; } -static bool resolve_inferred_error_set(IrAnalyze *ira, TypeTableEntry *err_set_type, AstNode *source_node) { - assert(err_set_type->id == TypeTableEntryIdErrorSet); - FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn; - if (infer_fn != nullptr) { - if (infer_fn->anal_state == FnAnalStateInvalid) { - return false; - } else if (infer_fn->anal_state == FnAnalStateReady) { - analyze_fn_body(ira->codegen, infer_fn); - if (err_set_type->data.error_set.infer_fn != nullptr) { - assert(ira->codegen->errors.length != 0); - return false; - } - } else { - ir_add_error_node(ira, source_node, - buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet", - buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name))); - return false; - } - } - return true; -} - static TypeTableEntry *get_error_set_intersection(IrAnalyze *ira, TypeTableEntry *set1, TypeTableEntry *set2, AstNode *source_node) { assert(set1->id == TypeTableEntryIdErrorSet); assert(set2->id == TypeTableEntryIdErrorSet); - if (!resolve_inferred_error_set(ira, set1, source_node)) { + if (!resolve_inferred_error_set(ira->codegen, set1, source_node)) { return ira->codegen->builtin_types.entry_invalid; } - if (!resolve_inferred_error_set(ira, set2, source_node)) { + if (!resolve_inferred_error_set(ira->codegen, set2, source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(set1)) { @@ -7803,7 +7781,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry return result; } - if (!resolve_inferred_error_set(ira, contained_set, source_node)) { + if (!resolve_inferred_error_set(ira->codegen, contained_set, source_node)) { result.id = ConstCastResultIdUnresolvedInferredErrSet; return result; } @@ -8192,7 +8170,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod err_set_type = ira->codegen->builtin_types.entry_global_error_set; } else { err_set_type = prev_inst->value.type; - if (!resolve_inferred_error_set(ira, err_set_type, prev_inst->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, err_set_type, prev_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } update_errors_helper(ira->codegen, &errors, &errors_count); @@ -8231,7 +8209,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (type_is_global_error_set(err_set_type)) { continue; } - if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(cur_type)) { @@ -8297,7 +8275,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type; - if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(cur_err_set_type)) { @@ -8360,7 +8338,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) { continue; } - if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } @@ -8417,11 +8395,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod TypeTableEntry *prev_err_set_type = (err_set_type == nullptr) ? prev_type->data.error_union.err_set_type : err_set_type; TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type; - if (!resolve_inferred_error_set(ira, prev_err_set_type, cur_inst->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, prev_err_set_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } - if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } @@ -8531,7 +8509,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod { if (err_set_type != nullptr) { TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type; - if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(cur_err_set_type) || type_is_global_error_set(err_set_type)) { @@ -9213,7 +9191,7 @@ static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *sou if (!val) return ira->codegen->invalid_instruction; - if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; } if (!type_is_global_error_set(wanted_type)) { @@ -9654,7 +9632,7 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; } @@ -9752,7 +9730,7 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc zig_unreachable(); } if (!type_is_global_error_set(err_set_type)) { - if (!resolve_inferred_error_set(ira, err_set_type, source_instr->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, err_set_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; } if (err_set_type->data.error_set.err_count == 0) { @@ -10647,7 +10625,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp return ira->codegen->builtin_types.entry_invalid; } - if (!resolve_inferred_error_set(ira, intersect_type, source_node)) { + if (!resolve_inferred_error_set(ira->codegen, intersect_type, source_node)) { return ira->codegen->builtin_types.entry_invalid; } @@ -11503,11 +11481,11 @@ static TypeTableEntry *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstruction return ira->codegen->builtin_types.entry_type; } - if (!resolve_inferred_error_set(ira, op1_type, instruction->op1->other->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, op1_type, instruction->op1->other->source_node)) { return ira->codegen->builtin_types.entry_invalid; } - if (!resolve_inferred_error_set(ira, op2_type, instruction->op2->other->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, op2_type, instruction->op2->other->source_node)) { return ira->codegen->builtin_types.entry_invalid; } @@ -13851,7 +13829,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru } err_set_type = err_entry->set_with_only_this_in_it; } else { - if (!resolve_inferred_error_set(ira, child_type, field_ptr_instruction->base.source_node)) { + if (!resolve_inferred_error_set(ira->codegen, child_type, field_ptr_instruction->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } err_entry = find_err_table_entry(child_type, field_name); @@ -17559,7 +17537,7 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns } else if (container_type->id == TypeTableEntryIdUnion) { result = container_type->data.unionation.src_field_count; } else if (container_type->id == TypeTableEntryIdErrorSet) { - if (!resolve_inferred_error_set(ira, container_type, instruction->base.source_node)) { + if (!resolve_inferred_error_set(ira->codegen, container_type, instruction->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (type_is_global_error_set(container_type)) { @@ -17863,7 +17841,7 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc } TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type; - if (!resolve_inferred_error_set(ira, err_set_type, instruction->base.source_node)) { + if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } if (!type_is_global_error_set(err_set_type) && @@ -18131,7 +18109,7 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira } } } else if (switch_type->id == TypeTableEntryIdErrorSet) { - if (!resolve_inferred_error_set(ira, switch_type, target_value->source_node)) { + if (!resolve_inferred_error_set(ira->codegen, switch_type, target_value->source_node)) { return ira->codegen->builtin_types.entry_invalid; } -- cgit v1.2.3 From 15302e84a45a04cfe94a8842318f02a608055962 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Wed, 30 May 2018 11:51:46 +0200 Subject: Adding workaround for when the user tries to unwrap 'type' closes #1011 --- src/ir.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 8d32a81e25..6e944a8976 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -17914,6 +17914,15 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *ptr_type = value->value.type; + // Because we don't have Pointer Reform yet, we can't have a pointer to a 'type'. + // Therefor, we have to check for type 'type' here, so we can output a correct error + // without asserting the assert below. + if (ptr_type->id == TypeTableEntryIdMetaType) { + ir_add_error(ira, value, + buf_sprintf("expected error union type, found '%s'", buf_ptr(&ptr_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == TypeTableEntryIdPointer); -- cgit v1.2.3 From 8fc52a94f46295ee821708c44a165803207e85a6 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 30 May 2018 10:18:11 -0500 Subject: Added custom formatter support, refactored fmt.format --- std/fmt/index.zig | 404 +++++++++++++++++++++++++++--------------------------- 1 file changed, 201 insertions(+), 203 deletions(-) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 71ac764b0b..011b5d7c25 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -16,27 +16,12 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), Start, OpenBrace, CloseBrace, - Integer, - IntegerWidth, - Float, - FloatWidth, - FloatScientific, - FloatScientificWidth, - Character, - Buf, - BufWidth, - Bytes, - BytesBase, - BytesWidth, + FormatString, }; comptime var start_index = 0; comptime var state = State.Start; comptime var next_arg = 0; - comptime var radix = 0; - comptime var uppercase = false; - comptime var width = 0; - comptime var width_start = 0; inline for (fmt) |c, i| { switch (state) { @@ -45,8 +30,10 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), if (start_index < i) { try output(context, fmt[start_index..i]); } + start_index = i; state = State.OpenBrace; }, + '}' => { if (start_index < i) { try output(context, fmt[start_index..i]); @@ -61,57 +48,14 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), start_index = i; }, '}' => { - try formatValue(args[next_arg], context, Errors, output); + try formatType(args[next_arg], fmt[0..0], context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; }, - 'd' => { - radix = 10; - uppercase = false; - width = 0; - state = State.Integer; - }, - 'x' => { - radix = 16; - uppercase = false; - width = 0; - state = State.Integer; - }, - 'X' => { - radix = 16; - uppercase = true; - width = 0; - state = State.Integer; - }, - 'c' => { - state = State.Character; - }, - 's' => { - state = State.Buf; - }, - 'e' => { - state = State.FloatScientific; + else => { + state = State.FormatString; }, - '.' => { - state = State.Float; - }, - 'B' => { - width = 0; - radix = 1000; - state = State.Bytes; - }, - else => @compileError("Unknown format character: " ++ []u8{c}), - }, - State.Buf => switch (c) { - '}' => { - return output(context, args[next_arg]); - }, - '0'...'9' => { - width_start = i; - state = State.BufWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), }, State.CloseBrace => switch (c) { '}' => { @@ -120,139 +64,16 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), }, else => @compileError("Single '}' encountered in format string"), }, - State.Integer => switch (c) { + State.FormatString => switch(c) { '}' => { - try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output); + const s = start_index + 1; + try formatType(args[next_arg], fmt[s..i], context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; }, - '0'...'9' => { - width_start = i; - state = State.IntegerWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.IntegerWidth => switch (c) { - '}' => { - width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => {}, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.FloatScientific => switch (c) { - '}' => { - try formatFloatScientific(args[next_arg], null, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => { - width_start = i; - state = State.FloatScientificWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.FloatScientificWidth => switch (c) { - '}' => { - width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatFloatScientific(args[next_arg], width, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => {}, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.Float => switch (c) { - '}' => { - try formatFloatDecimal(args[next_arg], null, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => { - width_start = i; - state = State.FloatWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.FloatWidth => switch (c) { - '}' => { - width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatFloatDecimal(args[next_arg], width, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => {}, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.BufWidth => switch (c) { - '}' => { - width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatBuf(args[next_arg], width, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => {}, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.Character => switch (c) { - '}' => { - try formatAsciiChar(args[next_arg], context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.Bytes => switch (c) { - '}' => { - try formatBytes(args[next_arg], 0, radix, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - 'i' => { - radix = 1024; - state = State.BytesBase; - }, - '0'...'9' => { - width_start = i; - state = State.BytesWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.BytesBase => switch (c) { - '}' => { - try formatBytes(args[next_arg], 0, radix, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => { - width_start = i; - state = State.BytesWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.BytesWidth => switch (c) { - '}' => { - width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatBytes(args[next_arg], width, radix, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0'...'9' => {}, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, + else => {}, + } } } comptime { @@ -268,14 +89,14 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), } } -pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatType(value: var, comptime fmt: []const u8, context: var, comptime Errors: type, + output: fn(@typeOf(context), []const u8)Errors!void) Errors!void +{ const T = @typeOf(value); switch (@typeId(T)) { - builtin.TypeId.Int => { - return formatInt(value, 10, false, 0, context, Errors, output); - }, + builtin.TypeId.Int, builtin.TypeId.Float => { - return formatFloatScientific(value, null, context, Errors, output); + return formatValue(value, fmt, context, Errors, output); }, builtin.TypeId.Void => { return output(context, "void"); @@ -285,16 +106,16 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ }, builtin.TypeId.Nullable => { if (value) |payload| { - return formatValue(payload, context, Errors, output); + return formatType(payload, fmt, context, Errors, output); } else { return output(context, "null"); } }, builtin.TypeId.ErrorUnion => { if (value) |payload| { - return formatValue(payload, context, Errors, output); + return formatType(payload, fmt, context, Errors, output); } else |err| { - return formatValue(err, context, Errors, output); + return formatType(err, fmt, context, Errors, output); } }, builtin.TypeId.ErrorSet => { @@ -302,10 +123,60 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ return output(context, @errorName(value)); }, builtin.TypeId.Pointer => { - if (@typeId(T.Child) == builtin.TypeId.Array and T.Child.Child == u8) { - return output(context, (value.*)[0..]); - } else { - return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); + switch(@typeId(T.Child)) { + builtin.TypeId.Array => { + if(T.Child.Child == u8) { + return formatText(value, fmt, context, Errors, output); + } + }, + builtin.TypeId.Enum, + builtin.TypeId.Union, + builtin.TypeId.Struct => { + const has_cust_fmt = comptime cf: { + const info = @typeInfo(T.Child); + const defs = switch (info) { + builtin.TypeId.Struct => |s| s.defs, + builtin.TypeId.Union => |u| u.defs, + builtin.TypeId.Enum => |e| e.defs, + else => unreachable, + }; + + for (defs) |def| { + if (mem.eql(u8, def.name, "format") and def.is_pub) { + const data = def.data; + switch (data) { + builtin.TypeInfo.Definition.Data.Type, + builtin.TypeInfo.Definition.Data.Var => continue, + builtin.TypeInfo.Definition.Data.Fn => |*fn_def| { + //const FmtType = fn(@typeOf(context), []const u8)Errors!void; + //// for some reason, fn_type sees the arg `comptime []const u8` as `var` + //const TargetType = fn(T, var, var, type, FmtType) Errors!void; + + // This hack is because fn_def.fn_type != TargetType + // for reasons I have yet to determine. + + const fn_type_name = @typeName(@typeOf(value.format)); + const value_type_name = @typeName(@typeOf(value)); + const target_type_name = "(bound fn(" + ++ value_type_name ++ ",var,var,var,var)var)"; + if (mem.eql(u8, fn_type_name, target_type_name)) + { + break :cf true; + } + + }, + } + } + } + break :cf false; + }; + + if (has_cust_fmt) return value.format(fmt, context, Errors, output); + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), + @ptrToInt(value)); + }, + else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), + @ptrToInt(value)), } }, else => if (@canImplicitCast([]const u8, value)) { @@ -317,11 +188,106 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ } } +fn formatValue(value: var, comptime fmt: []const u8, context: var, comptime Errors: type, + output: fn(@typeOf(context), []const u8)Errors!void) Errors!void +{ + if (fmt.len > 0) { + if (fmt[0] == 'B') { + comptime var width: ?usize = null; + if (fmt.len > 1) { + if (fmt[1] == 'i') { + if (fmt.len > 2) width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable); + return formatBytes(value, width, 1024, context, Errors, output); + } + width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + } + return formatBytes(value, width, 1000, context, Errors, output); + } + } + + comptime var T = @typeOf(value); + switch (@typeId(T)) { + builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output), + builtin.TypeId.Int => return formatIntValue(value, fmt, context, Errors, output), + else => unreachable, + } +} + +pub fn formatIntValue(value: var, comptime fmt: []const u8, context: var, comptime Errors: type, + output: fn(@typeOf(context), []const u8)Errors!void) Errors!void +{ + comptime var radix = 10; + comptime var uppercase = false; + comptime var width = 0; + if (fmt.len > 0) { + switch (fmt[0]) { + 'c' => { + if(@typeOf(value) == u8) { + if(fmt.len > 1) @compileError("Unknown format character: " ++ []u8{fmt[1]}); + return formatAsciiChar(fmt[0], context, Errors, output); + } + }, + 'd' => { + radix = 10; + uppercase = false; + width = 0; + }, + 'x' => { + radix = 16; + uppercase = false; + width = 0; + }, + 'X' => { + radix = 16; + uppercase = true; + width = 0; + }, + else => @compileError("Unknown format character: " ++ []u8{fmt[0]}), + } + if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + } + return formatInt(value, radix, uppercase, width, context, Errors, output); +} + +fn formatFloatValue(value: var, comptime fmt: []const u8, context: var, comptime Errors: type, + output: fn(@typeOf(context), []const u8)Errors!void) Errors!void +{ + comptime var width: ?usize = null; + comptime var float_fmt = 'e'; + if (fmt.len > 0) { + float_fmt = fmt[0]; + if(fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + } + + switch (float_fmt) { + 'e' => try formatFloatScientific(value, width, context, Errors, output), + '.' => try formatFloatDecimal(value, width, context, Errors, output), + else => @compileError("Unknown format character: " ++ []u8{float_fmt}), + } + +} + +pub fn formatText(bytes: []const u8, comptime fmt: []const u8, context: var, + comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void +{ + if (fmt.len > 0) { + if (fmt[0] == 's') { + comptime var width = 0; + if(fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + return formatBuf(bytes, width, context, Errors, output); + } + else @compileError("Unknown format character: " ++ []u8{fmt[0]}); + } + return output(context, bytes); +} + pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { return output(context, (&c)[0..1]); } -pub fn formatBuf(buf: []const u8, width: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatBuf(buf: []const u8, width: usize, context: var, + comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void +{ try output(context, buf); var leftover_padding = if (width > buf.len) (width - buf.len) else return; @@ -1048,6 +1014,38 @@ test "fmt.format" { const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); } + //custom type format + { + const Vec2 = struct { + const SelfType = this; + x: f32, + y: f32, + + pub fn format(self: &SelfType, comptime fmt: []const u8, context: var, + comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) + Errors!void + { + if (fmt.len > 0) { + if (fmt.len > 1) unreachable; + switch (fmt[0]) { + //point format + 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), + //dimension format + 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y), + else => unreachable, + } + } + return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y); + } + }; + + var buf1: [32]u8 = undefined; + var value = Vec2{.x = 10.2, .y = 2.22,}; + const point_result = try bufPrint(buf1[0..], "point: {}\n", &value); + assert(mem.eql(u8, point_result, "point: (10.200,2.220)\n")); + const dim_result = try bufPrint(buf1[0..], "dim: {d}\n", &value); + assert(mem.eql(u8, dim_result, "dim: 10.200x2.220\n")); + } } fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { -- cgit v1.2.3 From 4e1d0a59faea8ca9a9c28a876bc448f1d2f46a17 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 30 May 2018 10:24:27 -0500 Subject: Minor typo --- std/fmt/index.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 011b5d7c25..eb52cead90 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -64,7 +64,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), }, else => @compileError("Single '}' encountered in format string"), }, - State.FormatString => switch(c) { + State.FormatString => switch (c) { '}' => { const s = start_index + 1; try formatType(args[next_arg], fmt[s..i], context, Errors, output); -- cgit v1.2.3 From 8938c16f38866a7149c32463dea44f884b265643 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 30 May 2018 10:41:48 -0500 Subject: Formatting --- std/fmt/index.zig | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index eb52cead90..0eed6613a6 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -11,7 +11,10 @@ const max_int_digits = 65; /// Renders fmt string with args, calling output with slices of bytes. /// If `output` returns an error, the error is returned from `format` and /// `output` is not called again. -pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void { +pub fn format(context: var, comptime Errors: type, + output: fn(@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, + args: ...) Errors!void +{ const State = enum { Start, OpenBrace, @@ -281,7 +284,9 @@ pub fn formatText(bytes: []const u8, comptime fmt: []const u8, context: var, return output(context, bytes); } -pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, + output: fn(@typeOf(context), []const u8) Errors!void) Errors!void +{ return output(context, (&c)[0..1]); } @@ -300,7 +305,9 @@ pub fn formatBuf(buf: []const u8, width: usize, context: var, // Print a float in scientific notation to the specified precision. Null uses full precision. // It should be the case that every full precision, printed value can be re-parsed back to the // same type unambiguously. -pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, + comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void +{ var x = f64(value); // Errol doesn't handle these special cases. @@ -389,7 +396,9 @@ pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, // Print a float of the format x.yyyyy where the number of y is specified by the precision argument. // By default floats are printed at full precision (no rounding). -pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, + output: fn(@typeOf(context), []const u8) Errors!void) Errors!void +{ var x = f64(value); // Errol doesn't handle these special cases. @@ -579,7 +588,9 @@ pub fn formatInt( } } -fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, context: var, + comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void +{ const uint = @IntType(false, @typeOf(value).bit_count); if (value < 0) { const minus_sign: u8 = '-'; @@ -598,7 +609,9 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, context: } } -fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, context: var, + comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void +{ // max_int_digits accounts for the minus sign. when printing an unsigned // number we don't need to do that. var buf: [max_int_digits - 1]u8 = undefined; -- cgit v1.2.3 From fb001f5e903ddf3774a673af0923841a57197a17 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 30 May 2018 12:18:24 -0500 Subject: Fixed character handling --- std/fmt/index.zig | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 0eed6613a6..0f663c88bd 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -227,7 +227,7 @@ pub fn formatIntValue(value: var, comptime fmt: []const u8, context: var, compti 'c' => { if(@typeOf(value) == u8) { if(fmt.len > 1) @compileError("Unknown format character: " ++ []u8{fmt[1]}); - return formatAsciiChar(fmt[0], context, Errors, output); + return formatAsciiChar(value, context, Errors, output); } }, 'd' => { @@ -810,6 +810,10 @@ test "fmt.format" { const value: u3 = 0b101; try testFmt("u3: 5\n", "u3: {}\n", value); } + { + const value: u8 = 'a'; + try testFmt("u8: a\n", "u8: {c}\n", value); + } try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024)); { @@ -1054,10 +1058,8 @@ test "fmt.format" { var buf1: [32]u8 = undefined; var value = Vec2{.x = 10.2, .y = 2.22,}; - const point_result = try bufPrint(buf1[0..], "point: {}\n", &value); - assert(mem.eql(u8, point_result, "point: (10.200,2.220)\n")); - const dim_result = try bufPrint(buf1[0..], "dim: {d}\n", &value); - assert(mem.eql(u8, dim_result, "dim: 10.200x2.220\n")); + try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value); } } -- cgit v1.2.3 From 940a8544486d24e69162b9ed1dcf1495ba0c4bd3 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 30 May 2018 13:38:41 -0500 Subject: Fix MacOS CI Timer test failing...? --- std/os/time.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/os/time.zig b/std/os/time.zig index 9a7c682483..6b062f3f45 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -266,7 +266,7 @@ test "os.time.timestamp" { test "os.time.Timer" { const ns_per_ms = (ns_per_s / ms_per_s); - const margin = ns_per_ms * 50; + const margin = ns_per_ms * 150; var timer = try Timer.start(); sleep(0, 10 * ns_per_ms); -- cgit v1.2.3 From 2c96f19fd3ed312e5cb0ac8006b73e73abf4a98d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 May 2018 14:55:59 -0400 Subject: std.zig.render returns bool of whether anything changed zig fmt only renames files and prints to stdout for files which changed --- src-self-hosted/main.zig | 12 +++++++---- src-self-hosted/module.zig | 2 +- std/zig/parser_test.zig | 10 +++++---- std/zig/render.zig | 54 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index f54bdbdaf0..02e5bbf921 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -719,6 +719,9 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { }; defer tree.deinit(); + var old_digest: [256]u8 = undefined; + std.crypto.Sha256.hash(source_code, old_digest[0..]); + var error_it = tree.errors.iterator(0); while (error_it.next()) |parse_error| { const token = tree.tokens.at(parse_error.loc()); @@ -745,13 +748,14 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { continue; } - try stderr.print("{}\n", file_path); - const baf = try io.BufferedAtomicFile.create(allocator, file_path); defer baf.destroy(); - try std.zig.render(allocator, baf.stream(), &tree); - try baf.finish(); + const anything_changed = try std.zig.render(allocator, baf.stream(), &tree); + if (anything_changed) { + try stderr.print("{}\n", file_path); + try baf.finish(); + } } } diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 375e2e840b..848ce95309 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -255,7 +255,7 @@ pub const Module = struct { const out_stream = &stderr_file_out_stream.stream; warn("====fmt:====\n"); - try std.zig.render(self.allocator, out_stream, &tree); + _ = try std.zig.render(self.allocator, out_stream, &tree); warn("====ir:====\n"); warn("TODO\n\n"); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 75ba9e61d7..eee6b12767 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1792,7 +1792,7 @@ const io = std.io; var fixed_buffer_mem: [100 * 1024]u8 = undefined; -fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { +fn testParse(source: []const u8, allocator: &mem.Allocator, changes_expected: bool) ![]u8 { var stderr_file = try io.getStdErr(); var stderr = &io.FileOutStream.init(&stderr_file).stream; @@ -1829,16 +1829,18 @@ fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { errdefer buffer.deinit(); var buffer_out_stream = io.BufferOutStream.init(&buffer); - try std.zig.render(allocator, &buffer_out_stream.stream, &tree); + const anything_changed = try std.zig.render(allocator, &buffer_out_stream.stream, &tree); + std.debug.assert(anything_changed == changes_expected); return buffer.toOwnedSlice(); } fn testTransform(source: []const u8, expected_source: []const u8) !void { + const changes_expected = source.ptr != expected_source.ptr; const needed_alloc_count = x: { // Try it once with unlimited memory, make sure it works var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize)); - const result_source = try testParse(source, &failing_allocator.allocator); + const result_source = try testParse(source, &failing_allocator.allocator, changes_expected); if (!mem.eql(u8, result_source, expected_source)) { warn("\n====== expected this output: =========\n"); warn("{}", expected_source); @@ -1855,7 +1857,7 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { while (fail_index < needed_alloc_count) : (fail_index += 1) { var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index); - if (testParse(source, &failing_allocator.allocator)) |_| { + if (testParse(source, &failing_allocator.allocator, changes_expected)) |_| { return error.NondeterministicMemoryUsage; } else |err| switch (err) { error.OutOfMemory => { diff --git a/std/zig/render.zig b/std/zig/render.zig index e3bf5fe38e..657fcae8bb 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -12,9 +12,61 @@ pub const Error = error{ OutOfMemory, }; -pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf(stream).Child.Error || Error)!void { +/// Returns whether anything changed +pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf(stream).Child.Error || Error)!bool { comptime assert(@typeId(@typeOf(stream)) == builtin.TypeId.Pointer); + var anything_changed: bool = false; + + // make a passthrough stream that checks whether something changed + const MyStream = struct { + const MyStream = this; + const StreamError = @typeOf(stream).Child.Error; + const Stream = std.io.OutStream(StreamError); + + anything_changed_ptr: &bool, + child_stream: @typeOf(stream), + stream: Stream, + source_index: usize, + source: []const u8, + + fn write(iface_stream: &Stream, bytes: []const u8) StreamError!void { + const self = @fieldParentPtr(MyStream, "stream", iface_stream); + + if (!self.anything_changed_ptr.*) { + const end = self.source_index + bytes.len; + if (end > self.source.len) { + self.anything_changed_ptr.* = true; + } else { + const src_slice = self.source[self.source_index..end]; + self.source_index += bytes.len; + if (!mem.eql(u8, bytes, src_slice)) { + self.anything_changed_ptr.* = true; + } + } + } + + try self.child_stream.write(bytes); + } + }; + var my_stream = MyStream{ + .stream = MyStream.Stream{ .writeFn = MyStream.write }, + .child_stream = stream, + .anything_changed_ptr = &anything_changed, + .source_index = 0, + .source = tree.source, + }; + + try renderRoot(allocator, &my_stream.stream, tree); + + return anything_changed; +} + +fn renderRoot( + allocator: &mem.Allocator, + stream: var, + tree: &ast.Tree, +) (@typeOf(stream).Child.Error || Error)!void { // render all the line comments at the beginning of the file var tok_it = tree.tokens.iterator(0); while (tok_it.next()) |token| { -- cgit v1.2.3 From 93b51b0e4023c05f1ef40371b8e72004cb55f046 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 May 2018 15:33:58 -0400 Subject: spaces around slice operator if operands are infix See #1003 --- std/zig/parser_test.zig | 9 +++++++++ std/zig/render.zig | 8 ++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index eee6b12767..52bbaa8b4d 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,12 @@ +test "zig fmt: spaces around slice operator" { + try testCanonical( + \\var a = b[c..d]; + \\var a = b[c + 1 .. d]; + \\var a = b[c .. d + 1]; + \\ + ); +} + test "zig fmt: async call in if condition" { try testCanonical( \\comptime { diff --git a/std/zig/render.zig b/std/zig/render.zig index 657fcae8bb..bd20346bec 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -530,9 +530,13 @@ fn renderExpression( const lbracket = tree.prevToken(range.start.firstToken()); const dotdot = tree.nextToken(range.start.lastToken()); + const spaces_around_op = range.start.id == ast.Node.Id.InfixOp or + (if (range.end) |end| end.id == ast.Node.Id.InfixOp else false); + const op_space = if (spaces_around_op) Space.Space else Space.None; + try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [ - try renderExpression(allocator, stream, tree, indent, start_col, range.start, Space.None); - try renderToken(tree, stream, dotdot, indent, start_col, Space.None); // .. + try renderExpression(allocator, stream, tree, indent, start_col, range.start, op_space); + try renderToken(tree, stream, dotdot, indent, start_col, op_space); // .. if (range.end) |end| { try renderExpression(allocator, stream, tree, indent, start_col, end, Space.None); } -- cgit v1.2.3 From 84b1842026053197cdd8afe0c436ded55f352949 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 May 2018 15:50:01 -0400 Subject: zig fmt: space after fn in fn prototypes See #1003 --- std/zig/parser_test.zig | 28 +++++++++++++++++----------- std/zig/render.zig | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 52bbaa8b4d..bd5b3291e0 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -805,7 +805,7 @@ test "zig fmt: doc comments before struct field" { \\pub const Allocator = struct { \\ /// Allocate byte_count bytes and return them in a slice, with the \\ /// slice's pointer aligned at least to alignment bytes. - \\ allocFn: fn() void, + \\ allocFn: fn () void, \\}; \\ ); @@ -1710,10 +1710,10 @@ test "zig fmt: fn type" { \\ return i + 1; \\} \\ - \\const a: fn(u8) u8 = undefined; - \\const b: extern fn(u8) u8 = undefined; - \\const c: nakedcc fn(u8) u8 = undefined; - \\const ap: fn(u8) u8 = a; + \\const a: fn (u8) u8 = undefined; + \\const b: extern fn (u8) u8 = undefined; + \\const c: nakedcc fn (u8) u8 = undefined; + \\const ap: fn (u8) u8 = a; \\ ); } @@ -1801,7 +1801,7 @@ const io = std.io; var fixed_buffer_mem: [100 * 1024]u8 = undefined; -fn testParse(source: []const u8, allocator: &mem.Allocator, changes_expected: bool) ![]u8 { +fn testParse(source: []const u8, allocator: &mem.Allocator, anything_changed: &bool) ![]u8 { var stderr_file = try io.getStdErr(); var stderr = &io.FileOutStream.init(&stderr_file).stream; @@ -1838,18 +1838,17 @@ fn testParse(source: []const u8, allocator: &mem.Allocator, changes_expected: bo errdefer buffer.deinit(); var buffer_out_stream = io.BufferOutStream.init(&buffer); - const anything_changed = try std.zig.render(allocator, &buffer_out_stream.stream, &tree); - std.debug.assert(anything_changed == changes_expected); + anything_changed.* = try std.zig.render(allocator, &buffer_out_stream.stream, &tree); return buffer.toOwnedSlice(); } fn testTransform(source: []const u8, expected_source: []const u8) !void { - const changes_expected = source.ptr != expected_source.ptr; const needed_alloc_count = x: { // Try it once with unlimited memory, make sure it works var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize)); - const result_source = try testParse(source, &failing_allocator.allocator, changes_expected); + var anything_changed: bool = undefined; + const result_source = try testParse(source, &failing_allocator.allocator, &anything_changed); if (!mem.eql(u8, result_source, expected_source)) { warn("\n====== expected this output: =========\n"); warn("{}", expected_source); @@ -1858,6 +1857,12 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { warn("\n======================================\n"); return error.TestFailed; } + const changes_expected = source.ptr != expected_source.ptr; + if (anything_changed != changes_expected) { + warn("std.zig.render returned {} instead of {}\n", anything_changed, changes_expected); + return error.TestFailed; + } + std.debug.assert(anything_changed == changes_expected); failing_allocator.allocator.free(result_source); break :x failing_allocator.index; }; @@ -1866,7 +1871,8 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { while (fail_index < needed_alloc_count) : (fail_index += 1) { var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index); - if (testParse(source, &failing_allocator.allocator, changes_expected)) |_| { + var anything_changed: bool = undefined; + if (testParse(source, &failing_allocator.allocator, &anything_changed)) |_| { return error.NondeterministicMemoryUsage; } else |err| switch (err) { error.OutOfMemory => { diff --git a/std/zig/render.zig b/std/zig/render.zig index bd20346bec..50ef4b2a16 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -1058,7 +1058,7 @@ fn renderExpression( try renderToken(tree, stream, name_token, indent, start_col, Space.None); // name break :blk tree.nextToken(name_token); } else blk: { - try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.None); // fn + try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.Space); // fn break :blk tree.nextToken(fn_proto.fn_token); }; -- cgit v1.2.3 From b082cd4580890190218f876bf28816b4878ae85c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 May 2018 16:08:40 -0400 Subject: zig fmt: field access does not cause spaces for slicing See #1003 --- std/zig/parser_test.zig | 2 ++ std/zig/render.zig | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index bd5b3291e0..69903bc3fd 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -2,7 +2,9 @@ test "zig fmt: spaces around slice operator" { try testCanonical( \\var a = b[c..d]; \\var a = b[c + 1 .. d]; + \\var a = b[c + 1 ..]; \\var a = b[c .. d + 1]; + \\var a = b[c.a..d.e]; \\ ); } diff --git a/std/zig/render.zig b/std/zig/render.zig index 50ef4b2a16..ac07917ff1 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -530,13 +530,14 @@ fn renderExpression( const lbracket = tree.prevToken(range.start.firstToken()); const dotdot = tree.nextToken(range.start.lastToken()); - const spaces_around_op = range.start.id == ast.Node.Id.InfixOp or - (if (range.end) |end| end.id == ast.Node.Id.InfixOp else false); - const op_space = if (spaces_around_op) Space.Space else Space.None; + const after_start_space_bool = nodeCausesSliceOpSpace(range.start) or + (if (range.end) |end| nodeCausesSliceOpSpace(end) else false); + const after_start_space = if (after_start_space_bool) Space.Space else Space.None; + const after_op_space = if (range.end != null) after_start_space else Space.None; try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [ - try renderExpression(allocator, stream, tree, indent, start_col, range.start, op_space); - try renderToken(tree, stream, dotdot, indent, start_col, op_space); // .. + try renderExpression(allocator, stream, tree, indent, start_col, range.start, after_start_space); + try renderToken(tree, stream, dotdot, indent, start_col, after_op_space); // .. if (range.end) |end| { try renderExpression(allocator, stream, tree, indent, start_col, end, Space.None); } @@ -1959,3 +1960,11 @@ fn nodeIsBlock(base: &const ast.Node) bool { else => false, }; } + +fn nodeCausesSliceOpSpace(base: &ast.Node) bool { + const infix_op = base.cast(ast.Node.InfixOp) ?? return false; + return switch (infix_op.op) { + ast.Node.InfixOp.Op.Period => false, + else => true, + }; +} -- cgit v1.2.3 From ea58f4a5a95662637d2142727a0452542bcdb196 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 May 2018 16:09:11 -0400 Subject: run zig fmt on the codebase --- src-self-hosted/main.zig | 4 ++-- std/array_list.zig | 6 ++--- std/base64.zig | 2 +- std/build.zig | 4 ++-- std/c/darwin.zig | 2 +- std/c/index.zig | 2 +- std/crypto/blake2.zig | 16 ++++++------- std/crypto/md5.zig | 4 ++-- std/crypto/sha1.zig | 4 ++-- std/crypto/sha2.zig | 12 +++++----- std/crypto/sha3.zig | 8 +++---- std/crypto/test.zig | 2 +- std/event.zig | 4 ++-- std/fmt/errol/index.zig | 4 ++-- std/fmt/index.zig | 26 ++++++++++---------- std/hash/crc.zig | 2 +- std/hash/siphash.zig | 2 +- std/hash_map.zig | 2 +- std/io.zig | 6 ++--- std/io_test.zig | 4 ++-- std/json.zig | 2 +- std/mem.zig | 12 +++++----- std/os/darwin.zig | 6 ++--- std/os/index.zig | 16 ++++++------- std/os/linux/index.zig | 14 +++++------ std/os/linux/x86_64.zig | 2 +- std/os/path.zig | 6 ++--- std/os/windows/index.zig | 2 +- std/os/windows/util.zig | 2 +- std/os/zen.zig | 2 +- std/rand/index.zig | 2 +- std/rand/ziggurat.zig | 10 ++++---- std/sort.zig | 42 ++++++++++++++++----------------- std/special/build_runner.zig | 2 +- std/unicode.zig | 4 ++-- std/zig/tokenizer.zig | 2 +- test/cases/align.zig | 10 ++++---- test/cases/bugs/920.zig | 6 ++--- test/cases/coroutines.zig | 2 +- test/cases/error.zig | 2 +- test/cases/eval.zig | 2 +- test/cases/fn.zig | 2 +- test/cases/fn_in_struct_in_comptime.zig | 2 +- test/cases/generics.zig | 2 +- test/cases/misc.zig | 2 +- test/cases/struct.zig | 4 ++-- test/cases/type_info.zig | 2 +- test/cases/var_args.zig | 2 +- 48 files changed, 140 insertions(+), 140 deletions(-) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 02e5bbf921..9ad766dda2 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -41,7 +41,7 @@ const usage = const Command = struct { name: []const u8, - exec: fn(&Allocator, []const []const u8) error!void, + exec: fn (&Allocator, []const []const u8) error!void, }; pub fn main() !void { @@ -862,7 +862,7 @@ fn cmdRun(allocator: &Allocator, args: []const []const u8) !void { for (args) |argv, i| { if (mem.eql(u8, argv, "--")) { compile_args = args[0..i]; - runtime_args = args[i + 1..]; + runtime_args = args[i + 1 ..]; break; } } diff --git a/std/array_list.zig b/std/array_list.zig index 679f7d73b8..b315194c33 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -71,7 +71,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { try l.ensureCapacity(l.len + 1); l.len += 1; - mem.copy(T, l.items[n + 1..l.len], l.items[n..l.len - 1]); + mem.copy(T, l.items[n + 1 .. l.len], l.items[n .. l.len - 1]); l.items[n] = item.*; } @@ -79,8 +79,8 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { try l.ensureCapacity(l.len + items.len); l.len += items.len; - mem.copy(T, l.items[n + items.len..l.len], l.items[n..l.len - items.len]); - mem.copy(T, l.items[n..n + items.len], items); + mem.copy(T, l.items[n + items.len .. l.len], l.items[n .. l.len - items.len]); + mem.copy(T, l.items[n .. n + items.len], items); } pub fn append(l: &Self, item: &const T) !void { diff --git a/std/base64.zig b/std/base64.zig index b714250992..204628a405 100644 --- a/std/base64.zig +++ b/std/base64.zig @@ -450,7 +450,7 @@ fn testError(encoded: []const u8, expected_err: error) !void { fn testOutputTooSmallError(encoded: []const u8) !void { const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " "); var buffer: [0x100]u8 = undefined; - var decoded = buffer[0..calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1]; + var decoded = buffer[0 .. calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1]; if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| { return error.ExpectedError; } else |err| if (err != error.OutputTooSmall) return err; diff --git a/std/build.zig b/std/build.zig index f86c3d394f..9a6e17f728 100644 --- a/std/build.zig +++ b/std/build.zig @@ -1912,12 +1912,12 @@ pub const RemoveDirStep = struct { pub const Step = struct { name: []const u8, - makeFn: fn(self: &Step) error!void, + makeFn: fn (self: &Step) error!void, dependencies: ArrayList(&Step), loop_flag: bool, done_flag: bool, - pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn(&Step) error!void) Step { + pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn (&Step) error!void) Step { return Step{ .name = name, .makeFn = makeFn, diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 24be832d01..6a33c994bf 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -60,7 +60,7 @@ pub const sigset_t = u32; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name. pub const Sigaction = extern struct { - handler: extern fn(c_int) void, + handler: extern fn (c_int) void, sa_mask: sigset_t, sa_flags: c_int, }; diff --git a/std/c/index.zig b/std/c/index.zig index b5e6b48751..f9704f4738 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -52,7 +52,7 @@ pub extern "c" fn realloc(&c_void, usize) ?&c_void; pub extern "c" fn free(&c_void) void; pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int; -pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t, noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void, noalias arg: ?&c_void) c_int; +pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t, noalias attr: ?&const pthread_attr_t, start_routine: extern fn (?&c_void) ?&c_void, noalias arg: ?&c_void) c_int; pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int; pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int; pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int; diff --git a/std/crypto/blake2.zig b/std/crypto/blake2.zig index b48e2fb4ba..bf3193b5d9 100644 --- a/std/crypto/blake2.zig +++ b/std/crypto/blake2.zig @@ -105,7 +105,7 @@ fn Blake2s(comptime out_len: usize) type { // Full middle blocks. while (off + 64 <= b.len) : (off += 64) { d.t += 64; - d.round(b[off..off + 64], false); + d.round(b[off .. off + 64], false); } // Copy any remainder for next pass. @@ -120,10 +120,10 @@ fn Blake2s(comptime out_len: usize) type { d.t += d.buf_len; d.round(d.buf[0..], true); - const rr = d.h[0..out_len / 32]; + const rr = d.h[0 .. out_len / 32]; for (rr) |s, j| { - mem.writeInt(out[4 * j..4 * j + 4], s, builtin.Endian.Little); + mem.writeInt(out[4 * j .. 4 * j + 4], s, builtin.Endian.Little); } } @@ -134,7 +134,7 @@ fn Blake2s(comptime out_len: usize) type { var v: [16]u32 = undefined; for (m) |*r, i| { - r.* = mem.readIntLE(u32, b[4 * i..4 * i + 4]); + r.* = mem.readIntLE(u32, b[4 * i .. 4 * i + 4]); } var k: usize = 0; @@ -340,7 +340,7 @@ fn Blake2b(comptime out_len: usize) type { // Full middle blocks. while (off + 128 <= b.len) : (off += 128) { d.t += 128; - d.round(b[off..off + 128], false); + d.round(b[off .. off + 128], false); } // Copy any remainder for next pass. @@ -353,10 +353,10 @@ fn Blake2b(comptime out_len: usize) type { d.t += d.buf_len; d.round(d.buf[0..], true); - const rr = d.h[0..out_len / 64]; + const rr = d.h[0 .. out_len / 64]; for (rr) |s, j| { - mem.writeInt(out[8 * j..8 * j + 8], s, builtin.Endian.Little); + mem.writeInt(out[8 * j .. 8 * j + 8], s, builtin.Endian.Little); } } @@ -367,7 +367,7 @@ fn Blake2b(comptime out_len: usize) type { var v: [16]u64 = undefined; for (m) |*r, i| { - r.* = mem.readIntLE(u64, b[8 * i..8 * i + 8]); + r.* = mem.readIntLE(u64, b[8 * i .. 8 * i + 8]); } var k: usize = 0; diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig index e0473884cd..3d05597273 100644 --- a/std/crypto/md5.zig +++ b/std/crypto/md5.zig @@ -73,7 +73,7 @@ pub const Md5 = struct { // Full middle blocks. while (off + 64 <= b.len) : (off += 64) { - d.round(b[off..off + 64]); + d.round(b[off .. off + 64]); } // Copy any remainder for next pass. @@ -112,7 +112,7 @@ pub const Md5 = struct { d.round(d.buf[0..]); for (d.s) |s, j| { - mem.writeInt(out[4 * j..4 * j + 4], s, builtin.Endian.Little); + mem.writeInt(out[4 * j .. 4 * j + 4], s, builtin.Endian.Little); } } diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig index 77324b482f..e9d8e3e132 100644 --- a/std/crypto/sha1.zig +++ b/std/crypto/sha1.zig @@ -73,7 +73,7 @@ pub const Sha1 = struct { // Full middle blocks. while (off + 64 <= b.len) : (off += 64) { - d.round(b[off..off + 64]); + d.round(b[off .. off + 64]); } // Copy any remainder for next pass. @@ -111,7 +111,7 @@ pub const Sha1 = struct { d.round(d.buf[0..]); for (d.s) |s, j| { - mem.writeInt(out[4 * j..4 * j + 4], s, builtin.Endian.Big); + mem.writeInt(out[4 * j .. 4 * j + 4], s, builtin.Endian.Big); } } diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig index a55182aacd..aedc820f44 100644 --- a/std/crypto/sha2.zig +++ b/std/crypto/sha2.zig @@ -126,7 +126,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { // Full middle blocks. while (off + 64 <= b.len) : (off += 64) { - d.round(b[off..off + 64]); + d.round(b[off .. off + 64]); } // Copy any remainder for next pass. @@ -164,10 +164,10 @@ fn Sha2_32(comptime params: Sha2Params32) type { d.round(d.buf[0..]); // May truncate for possible 224 output - const rr = d.s[0..params.out_len / 32]; + const rr = d.s[0 .. params.out_len / 32]; for (rr) |s, j| { - mem.writeInt(out[4 * j..4 * j + 4], s, builtin.Endian.Big); + mem.writeInt(out[4 * j .. 4 * j + 4], s, builtin.Endian.Big); } } @@ -467,7 +467,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { // Full middle blocks. while (off + 128 <= b.len) : (off += 128) { - d.round(b[off..off + 128]); + d.round(b[off .. off + 128]); } // Copy any remainder for next pass. @@ -505,10 +505,10 @@ fn Sha2_64(comptime params: Sha2Params64) type { d.round(d.buf[0..]); // May truncate for possible 384 output - const rr = d.s[0..params.out_len / 64]; + const rr = d.s[0 .. params.out_len / 64]; for (rr) |s, j| { - mem.writeInt(out[8 * j..8 * j + 8], s, builtin.Endian.Big); + mem.writeInt(out[8 * j .. 8 * j + 8], s, builtin.Endian.Big); } } diff --git a/std/crypto/sha3.zig b/std/crypto/sha3.zig index 73b6415e1d..75bec57a87 100644 --- a/std/crypto/sha3.zig +++ b/std/crypto/sha3.zig @@ -46,7 +46,7 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { // absorb while (len >= rate) { - for (d.s[offset..offset + rate]) |*r, i| + for (d.s[offset .. offset + rate]) |*r, i| r.* ^= b[ip..][i]; keccak_f(1600, d.s[0..]); @@ -57,7 +57,7 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { offset = 0; } - for (d.s[offset..offset + len]) |*r, i| + for (d.s[offset .. offset + len]) |*r, i| r.* ^= b[ip..][i]; d.offset = offset + len; @@ -193,7 +193,7 @@ fn keccak_f(comptime F: usize, d: []u8) void { var c = []const u64{0} ** 5; for (s) |*r, i| { - r.* = mem.readIntLE(u64, d[8 * i..8 * i + 8]); + r.* = mem.readIntLE(u64, d[8 * i .. 8 * i + 8]); } comptime var x: usize = 0; @@ -240,7 +240,7 @@ fn keccak_f(comptime F: usize, d: []u8) void { } for (s) |r, i| { - mem.writeInt(d[8 * i..8 * i + 8], r, builtin.Endian.Little); + mem.writeInt(d[8 * i .. 8 * i + 8], r, builtin.Endian.Little); } } diff --git a/std/crypto/test.zig b/std/crypto/test.zig index c0a96a98de..3fa24272e5 100644 --- a/std/crypto/test.zig +++ b/std/crypto/test.zig @@ -14,7 +14,7 @@ pub fn assertEqualHash(comptime Hasher: var, comptime expected: []const u8, inpu pub fn assertEqual(comptime expected: []const u8, input: []const u8) void { var expected_bytes: [expected.len / 2]u8 = undefined; for (expected_bytes) |*r, i| { - r.* = fmt.parseInt(u8, expected[2 * i..2 * i + 2], 16) catch unreachable; + r.* = fmt.parseInt(u8, expected[2 * i .. 2 * i + 2], 16) catch unreachable; } debug.assert(mem.eql(u8, expected_bytes, input)); diff --git a/std/event.zig b/std/event.zig index 6ee8ab35f1..4604eb8d02 100644 --- a/std/event.zig +++ b/std/event.zig @@ -6,7 +6,7 @@ const mem = std.mem; const posix = std.os.posix; pub const TcpServer = struct { - handleRequestFn: async<&mem.Allocator> fn(&TcpServer, &const std.net.Address, &const std.os.File) void, + handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void, loop: &Loop, sockfd: i32, @@ -32,7 +32,7 @@ pub const TcpServer = struct { }; } - pub fn listen(self: &TcpServer, address: &const std.net.Address, handleRequestFn: async<&mem.Allocator> fn(&TcpServer, &const std.net.Address, &const std.os.File) void) !void { + pub fn listen(self: &TcpServer, address: &const std.net.Address, handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void) !void { self.handleRequestFn = handleRequestFn; try std.os.posixBind(self.sockfd, &address.os_addr); diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index 9c8fe343b5..65e8d448a8 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -60,7 +60,7 @@ pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: Ro // Re-size the buffer to use the reserved leading byte. const one_before = @intToPtr(&u8, @ptrToInt(&float_decimal.digits[0]) - 1); - float_decimal.digits = one_before[0..float_decimal.digits.len + 1]; + float_decimal.digits = one_before[0 .. float_decimal.digits.len + 1]; float_decimal.digits[0] = '1'; return; } @@ -84,7 +84,7 @@ pub fn errol3(value: f64, buffer: []u8) FloatDecimal { const i = tableLowerBound(bits); if (i < enum3.len and enum3[i] == bits) { const data = enum3_data[i]; - const digits = buffer[1..data.str.len + 1]; + const digits = buffer[1 .. data.str.len + 1]; mem.copy(u8, digits, data.str); return FloatDecimal{ .digits = digits, diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 71ac764b0b..0ffbc59895 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -11,7 +11,7 @@ const max_int_digits = 65; /// Renders fmt string with args, calling output with slices of bytes. /// If `output` returns an error, the error is returned from `format` and /// `output` is not called again. -pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void { +pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void { const State = enum { Start, OpenBrace, @@ -268,7 +268,7 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), } } -pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { const T = @typeOf(value); switch (@typeId(T)) { builtin.TypeId.Int => { @@ -317,11 +317,11 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ } } -pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { return output(context, (&c)[0..1]); } -pub fn formatBuf(buf: []const u8, width: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatBuf(buf: []const u8, width: usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { try output(context, buf); var leftover_padding = if (width > buf.len) (width - buf.len) else return; @@ -334,7 +334,7 @@ pub fn formatBuf(buf: []const u8, width: usize, context: var, comptime Errors: t // Print a float in scientific notation to the specified precision. Null uses full precision. // It should be the case that every full precision, printed value can be re-parsed back to the // same type unambiguously. -pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { var x = f64(value); // Errol doesn't handle these special cases. @@ -423,7 +423,7 @@ pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, // Print a float of the format x.yyyyy where the number of y is specified by the precision argument. // By default floats are printed at full precision (no rounding). -pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { var x = f64(value); // Errol doesn't handle these special cases. @@ -512,7 +512,7 @@ pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, com // Remaining fractional portion, zero-padding if insufficient. debug.assert(precision >= printed); if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { - try output(context, float_decimal.digits[num_digits_whole_no_pad..num_digits_whole_no_pad + precision - printed]); + try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); return; } else { try output(context, float_decimal.digits[num_digits_whole_no_pad..]); @@ -568,7 +568,7 @@ pub fn formatBytes( comptime radix: usize, context: var, comptime Errors: type, - output: fn(@typeOf(context), []const u8) Errors!void, + output: fn (@typeOf(context), []const u8) Errors!void, ) Errors!void { if (value == 0) { return output(context, "0B"); @@ -604,7 +604,7 @@ pub fn formatInt( width: usize, context: var, comptime Errors: type, - output: fn(@typeOf(context), []const u8) Errors!void, + output: fn (@typeOf(context), []const u8) Errors!void, ) Errors!void { if (@typeOf(value).is_signed) { return formatIntSigned(value, base, uppercase, width, context, Errors, output); @@ -613,7 +613,7 @@ pub fn formatInt( } } -fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { const uint = @IntType(false, @typeOf(value).bit_count); if (value < 0) { const minus_sign: u8 = '-'; @@ -632,7 +632,7 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, context: } } -fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void) Errors!void { +fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void) Errors!void { // max_int_digits accounts for the minus sign. when printing an unsigned // number we don't need to do that. var buf: [max_int_digits - 1]u8 = undefined; @@ -661,7 +661,7 @@ fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, contex mem.set(u8, buf[0..index], '0'); return output(context, buf); } else { - const padded_buf = buf[index - padding..]; + const padded_buf = buf[index - padding ..]; mem.set(u8, padded_buf[0..padding], '0'); return output(context, padded_buf); } @@ -760,7 +760,7 @@ fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) !void { pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { var context = BufPrintContext{ .remaining = buf }; try format(&context, error{BufferTooSmall}, bufPrintWrite, fmt, args); - return buf[0..buf.len - context.remaining.len]; + return buf[0 .. buf.len - context.remaining.len]; } pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { diff --git a/std/hash/crc.zig b/std/hash/crc.zig index 72c4e467c2..45bcb70e8b 100644 --- a/std/hash/crc.zig +++ b/std/hash/crc.zig @@ -61,7 +61,7 @@ pub fn Crc32WithPoly(comptime poly: u32) type { pub fn update(self: &Self, input: []const u8) void { var i: usize = 0; while (i + 8 <= input.len) : (i += 8) { - const p = input[i..i + 8]; + const p = input[i .. i + 8]; // Unrolling this way gives ~50Mb/s increase self.crc ^= (u32(p[0]) << 0); diff --git a/std/hash/siphash.zig b/std/hash/siphash.zig index b75866a403..750e23d4c8 100644 --- a/std/hash/siphash.zig +++ b/std/hash/siphash.zig @@ -76,7 +76,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) // Full middle blocks. while (off + 8 <= b.len) : (off += 8) { - d.round(b[off..off + 8]); + d.round(b[off .. off + 8]); } // Remainder for next pass. diff --git a/std/hash_map.zig b/std/hash_map.zig index e3b86f8a3b..f51b9c66ba 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -9,7 +9,7 @@ const builtin = @import("builtin"); const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast; const debug_u32 = if (want_modification_safety) u32 else void; -pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn(key: K) u32, comptime eql: fn(a: K, b: K) bool) type { +pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type { return struct { entries: []Entry, size: usize, diff --git a/std/io.zig b/std/io.zig index 7c997fdf42..39d319159e 100644 --- a/std/io.zig +++ b/std/io.zig @@ -82,7 +82,7 @@ pub fn InStream(comptime ReadError: type) type { /// Return the number of bytes read. If the number read is smaller than buf.len, it /// means the stream reached the end. Reaching the end of a stream is not an error /// condition. - readFn: fn(self: &Self, buffer: []u8) Error!usize, + readFn: fn (self: &Self, buffer: []u8) Error!usize, /// Replaces `buffer` contents by reading from the stream until it is finished. /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and @@ -208,7 +208,7 @@ pub fn OutStream(comptime WriteError: type) type { const Self = this; pub const Error = WriteError; - writeFn: fn(self: &Self, bytes: []const u8) Error!void, + writeFn: fn (self: &Self, bytes: []const u8) Error!void, pub fn print(self: &Self, comptime format: []const u8, args: ...) !void { return std.fmt.format(self, Error, self.writeFn, format, args); @@ -369,7 +369,7 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr while (src_index < bytes.len) { const dest_space_left = self.buffer.len - self.index; const copy_amt = math.min(dest_space_left, bytes.len - src_index); - mem.copy(u8, self.buffer[self.index..], bytes[src_index..src_index + copy_amt]); + mem.copy(u8, self.buffer[self.index..], bytes[src_index .. src_index + copy_amt]); self.index += copy_amt; assert(self.index <= self.buffer.len); if (self.index == self.buffer.len) { diff --git a/std/io_test.zig b/std/io_test.zig index ca4eeb3aaa..301a9a4cd0 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -41,8 +41,8 @@ test "write a file, read it, then delete it" { defer allocator.free(contents); assert(mem.eql(u8, contents[0.."begin".len], "begin")); - assert(mem.eql(u8, contents["begin".len..contents.len - "end".len], data)); - assert(mem.eql(u8, contents[contents.len - "end".len..], "end")); + assert(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data)); + assert(mem.eql(u8, contents[contents.len - "end".len ..], "end")); } try os.deleteFile(allocator, tmp_file_name); } diff --git a/std/json.zig b/std/json.zig index c88ce59139..9de8f0b53e 100644 --- a/std/json.zig +++ b/std/json.zig @@ -77,7 +77,7 @@ pub const Token = struct { // Slice into the underlying input string. pub fn slice(self: &const Token, input: []const u8, i: usize) []const u8 { - return input[i + self.offset - self.count..i + self.offset]; + return input[i + self.offset - self.count .. i + self.offset]; } }; diff --git a/std/mem.zig b/std/mem.zig index 70f2fe22ac..f4696cff9f 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -13,7 +13,7 @@ pub const Allocator = struct { /// The returned newly allocated memory is undefined. /// `alignment` is guaranteed to be >= 1 /// `alignment` is guaranteed to be a power of 2 - allocFn: fn(self: &Allocator, byte_count: usize, alignment: u29) Error![]u8, + allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8, /// If `new_byte_count > old_mem.len`: /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn. @@ -26,10 +26,10 @@ pub const Allocator = struct { /// The returned newly allocated memory is undefined. /// `alignment` is guaranteed to be >= 1 /// `alignment` is guaranteed to be a power of 2 - reallocFn: fn(self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, + reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` - freeFn: fn(self: &Allocator, old_mem: []u8) void, + freeFn: fn (self: &Allocator, old_mem: []u8) void, fn create(self: &Allocator, comptime T: type) !&T { if (@sizeOf(T) == 0) return &{}; @@ -282,7 +282,7 @@ pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?us var i: usize = haystack.len - needle.len; while (true) : (i -= 1) { - if (mem.eql(T, haystack[i..i + needle.len], needle)) return i; + if (mem.eql(T, haystack[i .. i + needle.len], needle)) return i; if (i == 0) return null; } } @@ -294,7 +294,7 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee var i: usize = start_index; const end = haystack.len - needle.len; while (i <= end) : (i += 1) { - if (eql(T, haystack[i..i + needle.len], needle)) return i; + if (eql(T, haystack[i .. i + needle.len], needle)) return i; } return null; } @@ -444,7 +444,7 @@ test "mem.startsWith" { } pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool { - return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len..], needle); + return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle); } test "mem.endsWith" { diff --git a/std/os/darwin.zig b/std/os/darwin.zig index a01755d27b..a3fc230ac5 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -437,7 +437,7 @@ pub fn sigaction(sig: u5, noalias act: &const Sigaction, noalias oact: ?&Sigacti assert(sig != SIGKILL); assert(sig != SIGSTOP); var cact = c.Sigaction{ - .handler = @ptrCast(extern fn(c_int) void, act.handler), + .handler = @ptrCast(extern fn (c_int) void, act.handler), .sa_flags = @bitCast(c_int, act.flags), .sa_mask = act.mask, }; @@ -448,7 +448,7 @@ pub fn sigaction(sig: u5, noalias act: &const Sigaction, noalias oact: ?&Sigacti } if (oact) |old| { old.* = Sigaction{ - .handler = @ptrCast(extern fn(i32) void, coact.handler), + .handler = @ptrCast(extern fn (i32) void, coact.handler), .flags = @bitCast(u32, coact.sa_flags), .mask = coact.sa_mask, }; @@ -468,7 +468,7 @@ pub const sockaddr = c.sockaddr; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = struct { - handler: extern fn(i32) void, + handler: extern fn (i32) void, mask: sigset_t, flags: u32, }; diff --git a/std/os/index.zig b/std/os/index.zig index 645d993256..70e654bcd9 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -399,7 +399,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void { for (envp_buf) |env| { - const env_buf = if (env) |ptr| ptr[0..cstr.len(ptr) + 1] else break; + const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; allocator.free(env_buf); } allocator.free(envp_buf); @@ -449,7 +449,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, allocator: while (it.next()) |search_path| { mem.copy(u8, path_buf, search_path); path_buf[search_path.len] = '/'; - mem.copy(u8, path_buf[search_path.len + 1..], exe_path); + mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); path_buf[search_path.len + exe_path.len + 1] = 0; err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); assert(err > 0); @@ -532,7 +532,7 @@ pub fn getEnvMap(allocator: &Allocator) !BufMap { var end_i: usize = line_i; while (ptr[end_i] != 0) : (end_i += 1) {} - const value = ptr[line_i + 1..end_i]; + const value = ptr[line_i + 1 .. end_i]; try result.set(key, value); } @@ -549,7 +549,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 { var end_i: usize = line_i; while (ptr[end_i] != 0) : (end_i += 1) {} - const this_value = ptr[line_i + 1..end_i]; + const this_value = ptr[line_i + 1 .. end_i]; return this_value; } @@ -691,7 +691,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: mem.copy(u8, existing_buf, existing_path); existing_buf[existing_path.len] = 0; - const new_buf = full_buf[existing_path.len + 1..]; + const new_buf = full_buf[existing_path.len + 1 ..]; mem.copy(u8, new_buf, new_path); new_buf[new_path.len] = 0; @@ -735,7 +735,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: tmp_path[dirname.len] = os.path.sep; while (true) { try getRandomBytes(rand_buf[0..]); - b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf); + b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); if (symLink(allocator, existing_path, tmp_path)) { return rename(allocator, tmp_path, new_path); @@ -914,7 +914,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) mem.copy(u8, old_buf, old_path); old_buf[old_path.len] = 0; - const new_buf = full_buf[old_path.len + 1..]; + const new_buf = full_buf[old_path.len + 1 ..]; mem.copy(u8, new_buf, new_path); new_buf[new_path.len] = 0; @@ -1141,7 +1141,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! const full_entry_path = full_entry_buf.toSlice(); mem.copy(u8, full_entry_path, full_path); full_entry_path[full_path.len] = '/'; - mem.copy(u8, full_entry_path[full_path.len + 1..], entry.name); + mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name); try deleteTree(allocator, full_entry_path); } diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 1cf5bbd432..5186ff32d3 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -939,7 +939,7 @@ pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigacti .handler = act.handler, .flags = act.flags | SA_RESTORER, .mask = undefined, - .restorer = @ptrCast(extern fn() void, restore_rt), + .restorer = @ptrCast(extern fn () void, restore_rt), }; var ksa_old: k_sigaction = undefined; @memcpy(@ptrCast(&u8, &ksa.mask), @ptrCast(&const u8, &act.mask), 8); @@ -962,22 +962,22 @@ const all_mask = []usize{@maxValue(usize)}; const app_mask = []usize{0xfffffffc7fffffff}; const k_sigaction = extern struct { - handler: extern fn(i32) void, + handler: extern fn (i32) void, flags: usize, - restorer: extern fn() void, + restorer: extern fn () void, mask: [2]u32, }; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = struct { - handler: extern fn(i32) void, + handler: extern fn (i32) void, mask: sigset_t, flags: u32, }; -pub const SIG_ERR = @intToPtr(extern fn(i32) void, @maxValue(usize)); -pub const SIG_DFL = @intToPtr(extern fn(i32) void, 0); -pub const SIG_IGN = @intToPtr(extern fn(i32) void, 1); +pub const SIG_ERR = @intToPtr(extern fn (i32) void, @maxValue(usize)); +pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); +pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); pub const empty_sigset = []usize{0} ** sigset_t.len; pub fn raise(sig: i32) usize { diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index ed32d59688..b43a642038 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -463,7 +463,7 @@ pub fn syscall6( } /// This matches the libc clone function. -pub extern fn clone(func: extern fn(arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: &i32, tls: usize, ctid: &i32) usize; +pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: &i32, tls: usize, ctid: &i32) usize; pub nakedcc fn restore_rt() void { return asm volatile ("syscall" diff --git a/std/os/path.zig b/std/os/path.zig index f2b3bb9b0a..162faffc42 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -793,7 +793,7 @@ pub fn basenamePosix(path: []const u8) []const u8 { start_index -= 1; } - return path[start_index + 1..end_index]; + return path[start_index + 1 .. end_index]; } pub fn basenameWindows(path: []const u8) []const u8 { @@ -825,7 +825,7 @@ pub fn basenameWindows(path: []const u8) []const u8 { start_index -= 1; } - return path[start_index + 1..end_index]; + return path[start_index + 1 .. end_index]; } test "os.path.basename" { @@ -999,7 +999,7 @@ pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) ![ } if (to_rest.len == 0) { // shave off the trailing slash - return result[0..result_index - 1]; + return result[0 .. result_index - 1]; } mem.copy(u8, result[result_index..], to_rest); diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 426514f7d7..264ea391c4 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -369,7 +369,7 @@ pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; pub const HEAP_NO_SERIALIZE = 0x00000001; -pub const PTHREAD_START_ROUTINE = extern fn(LPVOID) DWORD; +pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; test "import" { diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 7b7fdfae08..2bd8a157e4 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -73,7 +73,7 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { } const name_info = @ptrCast(&const windows.FILE_NAME_INFO, &name_info_bytes[0]); - const name_bytes = name_info_bytes[size..size + usize(name_info.FileNameLength)]; + const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)]; const name_wide = ([]u16)(name_bytes); return mem.indexOf(u16, name_wide, []u16{ 'm', 's', 'y', 's', '-' }) != null or mem.indexOf(u16, name_wide, []u16{ '-', 'p', 't', 'y' }) != null; diff --git a/std/os/zen.zig b/std/os/zen.zig index 7517cc0d69..2411c5363e 100644 --- a/std/os/zen.zig +++ b/std/os/zen.zig @@ -153,7 +153,7 @@ pub fn map(v_addr: usize, p_addr: usize, size: usize, writable: bool) bool { return syscall4(Syscall.map, v_addr, p_addr, size, usize(writable)) != 0; } -pub fn createThread(function: fn() void) u16 { +pub fn createThread(function: fn () void) u16 { return u16(syscall1(Syscall.createThread, @ptrToInt(function))); } diff --git a/std/rand/index.zig b/std/rand/index.zig index 68e6d3cb4d..c32309a0fd 100644 --- a/std/rand/index.zig +++ b/std/rand/index.zig @@ -28,7 +28,7 @@ pub const DefaultPrng = Xoroshiro128; pub const DefaultCsprng = Isaac64; pub const Random = struct { - fillFn: fn(r: &Random, buf: []u8) void, + fillFn: fn (r: &Random, buf: []u8) void, /// Read random bytes into the specified buffer until fill. pub fn bytes(r: &Random, buf: []u8) void { diff --git a/std/rand/ziggurat.zig b/std/rand/ziggurat.zig index 404687ad0c..7daeb59165 100644 --- a/std/rand/ziggurat.zig +++ b/std/rand/ziggurat.zig @@ -56,11 +56,11 @@ pub const ZigTable = struct { f: [257]f64, // probability density function used as a fallback - pdf: fn(f64) f64, + pdf: fn (f64) f64, // whether the distribution is symmetric is_symmetric: bool, // fallback calculation in the case we are in the 0 block - zero_case: fn(&Random, f64) f64, + zero_case: fn (&Random, f64) f64, }; // zigNorInit @@ -68,9 +68,9 @@ fn ZigTableGen( comptime is_symmetric: bool, comptime r: f64, comptime v: f64, - comptime f: fn(f64) f64, - comptime f_inv: fn(f64) f64, - comptime zero_case: fn(&Random, f64) f64, + comptime f: fn (f64) f64, + comptime f_inv: fn (f64) f64, + comptime zero_case: fn (&Random, f64) f64, ) ZigTable { var tables: ZigTable = undefined; diff --git a/std/sort.zig b/std/sort.zig index 5596c9063d..4e17718241 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -5,7 +5,7 @@ const math = std.math; const builtin = @import("builtin"); /// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required). -pub fn insertionSort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T) bool) void { +pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool) void { { var i: usize = 1; while (i < items.len) : (i += 1) { @@ -108,7 +108,7 @@ const Pull = struct { /// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required). /// Currently implemented as block sort. -pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T) bool) void { +pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool) void { // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c var cache: [512]T = undefined; @@ -257,7 +257,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons // merge A2 and B2 into the cache if (lessThan(items[B2.end - 1], items[A2.start])) { // the two ranges are in reverse order, so copy them in reverse order into the cache - mem.copy(T, cache[A1.length() + B2.length()..], items[A2.start..A2.end]); + mem.copy(T, cache[A1.length() + B2.length() ..], items[A2.start..A2.end]); mem.copy(T, cache[A1.length()..], items[B2.start..B2.end]); } else if (lessThan(items[B2.start], items[A2.end - 1])) { // these two ranges weren't already in order, so merge them into the cache @@ -265,7 +265,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons } else { // copy A2 and B2 into the cache in the same order mem.copy(T, cache[A1.length()..], items[A2.start..A2.end]); - mem.copy(T, cache[A1.length() + A2.length()..], items[B2.start..B2.end]); + mem.copy(T, cache[A1.length() + A2.length() ..], items[B2.start..B2.end]); } A2 = Range.init(A2.start, B2.end); @@ -275,7 +275,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons if (lessThan(cache[B3.end - 1], cache[A3.start])) { // the two ranges are in reverse order, so copy them in reverse order into the items - mem.copy(T, items[A1.start + A2.length()..], cache[A3.start..A3.end]); + mem.copy(T, items[A1.start + A2.length() ..], cache[A3.start..A3.end]); mem.copy(T, items[A1.start..], cache[B3.start..B3.end]); } else if (lessThan(cache[B3.start], cache[A3.end - 1])) { // these two ranges weren't already in order, so merge them back into the items @@ -283,7 +283,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons } else { // copy A3 and B3 into the items in the same order mem.copy(T, items[A1.start..], cache[A3.start..A3.end]); - mem.copy(T, items[A1.start + A1.length()..], cache[B3.start..B3.end]); + mem.copy(T, items[A1.start + A1.length() ..], cache[B3.start..B3.end]); } } @@ -640,7 +640,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons if (buffer2.length() > 0 or block_size <= cache.len) { // copy the previous A block into the cache or buffer2, since that's where we need it to be when we go to merge it anyway if (block_size <= cache.len) { - mem.copy(T, cache[0..], items[blockA.start..blockA.start + block_size]); + mem.copy(T, cache[0..], items[blockA.start .. blockA.start + block_size]); } else { blockSwap(T, items, blockA.start, buffer2.start, block_size); } @@ -651,7 +651,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons blockSwap(T, items, B_split, blockA.start + block_size - B_remaining, B_remaining); } else { // we are unable to use the 'buffer2' trick to speed up the rotation operation since buffer2 doesn't exist, so perform a normal rotation - mem.rotate(T, items[B_split..blockA.start + block_size], blockA.start - B_split); + mem.rotate(T, items[B_split .. blockA.start + block_size], blockA.start - B_split); } // update the range for the remaining A blocks, and the range remaining from the B block after it was split @@ -741,7 +741,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons } // merge operation without a buffer -fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const Range, lessThan: fn(&const T, &const T) bool) void { +fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const Range, lessThan: fn (&const T, &const T) bool) void { if (A_arg.length() == 0 or B_arg.length() == 0) return; // this just repeatedly binary searches into B and rotates A into position. @@ -783,7 +783,7 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const } // merge operation using an internal buffer -fn mergeInternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn(&const T, &const T) bool, buffer: &const Range) void { +fn mergeInternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn (&const T, &const T) bool, buffer: &const Range) void { // whenever we find a value to add to the final array, swap it with the value that's already in that spot // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order var A_count: usize = 0; @@ -819,7 +819,7 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s // combine a linear search with a binary search to reduce the number of comparisons in situations // where have some idea as to how many unique values there are and where the next value might be -fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T, &const T) bool, unique: usize) usize { +fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -833,7 +833,7 @@ fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan); } -fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T, &const T) bool, unique: usize) usize { +fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -847,7 +847,7 @@ fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &cons return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan); } -fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T, &const T) bool, unique: usize) usize { +fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -861,7 +861,7 @@ fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const return binaryLast(T, items, value, Range.init(index - skip, index), lessThan); } -fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T, &const T) bool, unique: usize) usize { +fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -875,7 +875,7 @@ fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const return binaryLast(T, items, value, Range.init(index, index + skip), lessThan); } -fn binaryFirst(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T, &const T) bool) usize { +fn binaryFirst(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool) usize { var start = range.start; var end = range.end - 1; if (range.start >= range.end) return range.end; @@ -893,7 +893,7 @@ fn binaryFirst(comptime T: type, items: []T, value: &const T, range: &const Rang return start; } -fn binaryLast(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T, &const T) bool) usize { +fn binaryLast(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool) usize { var start = range.start; var end = range.end - 1; if (range.start >= range.end) return range.end; @@ -911,7 +911,7 @@ fn binaryLast(comptime T: type, items: []T, value: &const T, range: &const Range return start; } -fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, lessThan: fn(&const T, &const T) bool, into: []T) void { +fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, lessThan: fn (&const T, &const T) bool, into: []T) void { var A_index: usize = A.start; var B_index: usize = B.start; const A_last = A.end; @@ -941,7 +941,7 @@ fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, less } } -fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn(&const T, &const T) bool, cache: []T) void { +fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn (&const T, &const T) bool, cache: []T) void { // A fits into the cache, so use that instead of the internal buffer var A_index: usize = 0; var B_index: usize = B.start; @@ -969,7 +969,7 @@ fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range, mem.copy(T, items[insert_index..], cache[A_index..A_last]); } -fn swap(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T) bool, order: &[8]u8, x: usize, y: usize) void { +fn swap(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool, order: &[8]u8, x: usize, y: usize) void { if (lessThan(items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(items[x], items[y]))) { mem.swap(T, &items[x], &items[y]); mem.swap(u8, &(order.*)[x], &(order.*)[y]); @@ -1345,7 +1345,7 @@ fn fuzzTest(rng: &std.rand.Random) void { } } -pub fn min(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T) bool) T { +pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool) T { var i: usize = 0; var smallest = items[0]; for (items[1..]) |item| { @@ -1356,7 +1356,7 @@ pub fn min(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const return smallest; } -pub fn max(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T) bool) T { +pub fn max(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool) T { var i: usize = 0; var biggest = items[0]; for (items[1..]) |item| { diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index cf8f19d49e..3ff11bbee4 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -71,7 +71,7 @@ pub fn main() !void { } if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { const option_name = option_contents[0..name_end]; - const option_value = option_contents[name_end + 1..]; + const option_value = option_contents[name_end + 1 ..]; if (builder.addUserInputOption(option_name, option_value)) return usageAndErr(&builder, false, try stderr_stream); } else { diff --git a/std/unicode.zig b/std/unicode.zig index 8bcc2705dd..36f04778f4 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -151,7 +151,7 @@ pub fn utf8ValidateSlice(s: []const u8) bool { return false; } - if (utf8Decode(s[i..i + cp_len])) |_| {} else |_| { + if (utf8Decode(s[i .. i + cp_len])) |_| {} else |_| { return false; } i += cp_len; @@ -216,7 +216,7 @@ const Utf8Iterator = struct { const cp_len = utf8ByteSequenceLength(it.bytes[it.i]) catch unreachable; it.i += cp_len; - return it.bytes[it.i - cp_len..it.i]; + return it.bytes[it.i - cp_len .. it.i]; } pub fn nextCodepoint(it: &Utf8Iterator) ?u32 { diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 4881e952b7..7c3b3210fb 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -1116,7 +1116,7 @@ pub const Tokenizer = struct { if (self.index + length > self.buffer.len) { return u3(self.buffer.len - self.index); } - const bytes = self.buffer[self.index..self.index + length]; + const bytes = self.buffer[self.index .. self.index + length]; switch (length) { 2 => { const value = std.unicode.utf8Decode2(bytes) catch return length; diff --git a/test/cases/align.zig b/test/cases/align.zig index f82aa6cfc4..582063766f 100644 --- a/test/cases/align.zig +++ b/test/cases/align.zig @@ -18,8 +18,8 @@ fn noop4() align(4) void {} test "function alignment" { assert(derp() == 1234); - assert(@typeOf(noop1) == fn() align(1) void); - assert(@typeOf(noop4) == fn() align(4) void); + assert(@typeOf(noop1) == fn () align(1) void); + assert(@typeOf(noop4) == fn () align(4) void); noop1(); noop4(); } @@ -127,7 +127,7 @@ test "implicitly decreasing fn alignment" { testImplicitlyDecreaseFnAlign(alignedBig, 5678); } -fn testImplicitlyDecreaseFnAlign(ptr: fn() align(1) i32, answer: i32) void { +fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) void { assert(ptr() == answer); } @@ -141,10 +141,10 @@ fn alignedBig() align(16) i32 { test "@alignCast functions" { assert(fnExpectsOnly1(simple4) == 0x19); } -fn fnExpectsOnly1(ptr: fn() align(1) i32) i32 { +fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 { return fnExpects4(@alignCast(4, ptr)); } -fn fnExpects4(ptr: fn() align(4) i32) i32 { +fn fnExpects4(ptr: fn () align(4) i32) i32 { return ptr(); } fn simple4() align(4) i32 { diff --git a/test/cases/bugs/920.zig b/test/cases/bugs/920.zig index c2b6816e94..c315206072 100644 --- a/test/cases/bugs/920.zig +++ b/test/cases/bugs/920.zig @@ -7,12 +7,12 @@ const ZigTable = struct { x: [257]f64, f: [257]f64, - pdf: fn(f64) f64, + pdf: fn (f64) f64, is_symmetric: bool, - zero_case: fn(&Random, f64) f64, + zero_case: fn (&Random, f64) f64, }; -fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64, comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable { +fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, comptime zero_case: fn (&Random, f64) f64) ZigTable { var tables: ZigTable = undefined; tables.is_symmetric = is_symmetric; diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 8a0218aeb7..8a071c6aad 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -154,7 +154,7 @@ test "async function with dot syntax" { test "async fn pointer in a struct field" { var data: i32 = 1; const Foo = struct { - bar: async<&std.mem.Allocator> fn(&i32) void, + bar: async<&std.mem.Allocator> fn (&i32) void, }; var foo = Foo{ .bar = simpleAsyncFn2 }; const p = (async foo.bar(&data)) catch unreachable; diff --git a/test/cases/error.zig b/test/cases/error.zig index 92b2a012bd..ced49419d5 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -193,7 +193,7 @@ fn entry() void { foo2(bar2); } -fn foo2(f: fn() error!void) void { +fn foo2(f: fn () error!void) void { const x = f(); } diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 3a1c67445a..8a6dc25bd8 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -215,7 +215,7 @@ test "inlined block and runtime block phi" { const CmdFn = struct { name: []const u8, - func: fn(i32) i32, + func: fn (i32) i32, }; const cmd_fns = []CmdFn{ diff --git a/test/cases/fn.zig b/test/cases/fn.zig index a0691fbffc..dfb254c6aa 100644 --- a/test/cases/fn.zig +++ b/test/cases/fn.zig @@ -66,7 +66,7 @@ test "implicit cast function unreachable return" { wantsFnWithVoid(fnWithUnreachable); } -fn wantsFnWithVoid(f: fn() void) void {} +fn wantsFnWithVoid(f: fn () void) void {} fn fnWithUnreachable() noreturn { unreachable; diff --git a/test/cases/fn_in_struct_in_comptime.zig b/test/cases/fn_in_struct_in_comptime.zig index 51e494036b..c22da71940 100644 --- a/test/cases/fn_in_struct_in_comptime.zig +++ b/test/cases/fn_in_struct_in_comptime.zig @@ -1,6 +1,6 @@ const assert = @import("std").debug.assert; -fn get_foo() fn(&u8) usize { +fn get_foo() fn (&u8) usize { comptime { return struct { fn func(ptr: &u8) usize { diff --git a/test/cases/generics.zig b/test/cases/generics.zig index 93fecc7295..37cd1b89e4 100644 --- a/test/cases/generics.zig +++ b/test/cases/generics.zig @@ -133,7 +133,7 @@ fn getFirstByte(comptime T: type, mem: []const T) u8 { return getByte(@ptrCast(&const u8, &mem[0])); } -const foos = []fn(var) bool{ +const foos = []fn (var) bool{ foo1, foo2, }; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 42de163ea5..b6b2da8de5 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -511,7 +511,7 @@ test "@typeId" { assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); assert(@typeId(AUnionEnum) == Tid.Union); assert(@typeId(AUnion) == Tid.Union); - assert(@typeId(fn() void) == Tid.Fn); + assert(@typeId(fn () void) == Tid.Fn); assert(@typeId(@typeOf(builtin)) == Tid.Namespace); assert(@typeId(@typeOf(x: { break :x this; diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 37b0e497f0..d4a1c7fbe3 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -105,7 +105,7 @@ test "fn call of struct field" { } const Foo = struct { - ptr: fn() i32, + ptr: fn () i32, }; fn aFunc() i32 { @@ -302,7 +302,7 @@ test "packed array 24bits" { var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); bytes[bytes.len - 1] = 0xaa; - const ptr = &([]FooArray24Bits)(bytes[0..bytes.len - 1])[0]; + const ptr = &([]FooArray24Bits)(bytes[0 .. bytes.len - 1])[0]; assert(ptr.a == 0); assert(ptr.b[0].field == 0); assert(ptr.b[1].field == 0); diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index 05266feb9c..2561d70865 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -196,7 +196,7 @@ fn testStruct() void { assert(!struct_info.Struct.defs[0].data.Fn.is_extern); assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); assert(struct_info.Struct.defs[0].data.Fn.return_type == void); - assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn(&const TestStruct) void); + assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn (&const TestStruct) void); } const TestStruct = packed struct { diff --git a/test/cases/var_args.zig b/test/cases/var_args.zig index ec4d2059f3..5ef41f52ba 100644 --- a/test/cases/var_args.zig +++ b/test/cases/var_args.zig @@ -58,7 +58,7 @@ fn extraFn(extra: u32, args: ...) usize { return args.len; } -const foos = []fn(...) bool{ +const foos = []fn (...) bool{ foo1, foo2, }; -- cgit v1.2.3 From d8699ae57ed1e69aac209d674b26685c6c569525 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 May 2018 16:45:32 -0400 Subject: zig fmt: don't compute a sha-256 for no reason I forgot to delete this code before pushing 2c96f19fd3e --- src-self-hosted/main.zig | 3 --- 1 file changed, 3 deletions(-) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 9ad766dda2..e8a0020e5a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -719,9 +719,6 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { }; defer tree.deinit(); - var old_digest: [256]u8 = undefined; - std.crypto.Sha256.hash(source_code, old_digest[0..]); - var error_it = tree.errors.iterator(0); while (error_it.next()) |parse_error| { const token = tree.tokens.at(parse_error.loc()); -- cgit v1.2.3 From a05acaf9fd8ea1b42ec300ce4ba948ac00b89d76 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 May 2018 18:26:09 -0400 Subject: Add --color CLI option to zig fmt It doesn't actually do terminal color yet because we need to add cross platform terminal color abstractions. But it toggles between the single line error reporting and the multiline error reporting. See #1026 --- src-self-hosted/errmsg.zig | 87 ++++++++++++++++++++++++++++++++++++++++++++++ src-self-hosted/main.zig | 71 ++++++++++++++++++++----------------- src-self-hosted/module.zig | 11 ++---- std/zig/ast.zig | 16 ++++----- 4 files changed, 138 insertions(+), 47 deletions(-) create mode 100644 src-self-hosted/errmsg.zig diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig new file mode 100644 index 0000000000..9905b8e3a6 --- /dev/null +++ b/src-self-hosted/errmsg.zig @@ -0,0 +1,87 @@ +const std = @import("std"); +const mem = std.mem; +const os = std.os; +const Token = std.zig.Token; +const ast = std.zig.ast; +const TokenIndex = std.zig.ast.TokenIndex; + +pub const Color = enum { + Auto, + Off, + On, +}; + +pub const Msg = struct { + path: []const u8, + text: []u8, + first_token: TokenIndex, + last_token: TokenIndex, + tree: &ast.Tree, +}; + +/// `path` must outlive the returned Msg +/// `tree` must outlive the returned Msg +/// Caller owns returned Msg and must free with `allocator` +pub fn createFromParseError( + allocator: &mem.Allocator, + parse_error: &const ast.Error, + tree: &ast.Tree, + path: []const u8, +) !&Msg { + const loc_token = parse_error.loc(); + var text_buf = try std.Buffer.initSize(allocator, 0); + defer text_buf.deinit(); + + var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; + try parse_error.render(&tree.tokens, out_stream); + + const msg = try allocator.construct(Msg{ + .tree = tree, + .path = path, + .text = text_buf.toOwnedSlice(), + .first_token = loc_token, + .last_token = loc_token, + }); + errdefer allocator.destroy(msg); + + return msg; +} + +pub fn printToStream(stream: var, msg: &const Msg, color_on: bool) !void { + const first_token = msg.tree.tokens.at(msg.first_token); + const last_token = msg.tree.tokens.at(msg.last_token); + const start_loc = msg.tree.tokenLocationPtr(0, first_token); + const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token); + if (!color_on) { + try stream.print( + "{}:{}:{}: error: {}\n", + msg.path, + start_loc.line + 1, + start_loc.column + 1, + msg.text, + ); + return; + } + + try stream.print( + "{}:{}:{}: error: {}\n{}\n", + msg.path, + start_loc.line + 1, + start_loc.column + 1, + msg.text, + msg.tree.source[start_loc.line_start..start_loc.line_end], + ); + try stream.writeByteNTimes(' ', start_loc.column); + try stream.writeByteNTimes('~', last_token.end - first_token.start); + try stream.write("\n"); +} + +pub fn printToFile(file: &os.File, msg: &const Msg, color: Color) !void { + const color_on = switch (color) { + Color.Auto => file.isTty(), + Color.On => true, + Color.Off => false, + }; + var stream = &std.io.FileOutStream.init(file).stream; + return printToStream(stream, msg, color_on); +} diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index e8a0020e5a..734c3911ae 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -15,7 +15,9 @@ const Args = arg.Args; const Flag = arg.Flag; const Module = @import("module.zig").Module; const Target = @import("target.zig").Target; +const errmsg = @import("errmsg.zig"); +var stderr_file: os.File = undefined; var stderr: &io.OutStream(io.FileOutStream.Error) = undefined; var stdout: &io.OutStream(io.FileOutStream.Error) = undefined; @@ -51,7 +53,7 @@ pub fn main() !void { var stdout_out_stream = std.io.FileOutStream.init(&stdout_file); stdout = &stdout_out_stream.stream; - var stderr_file = try std.io.getStdErr(); + stderr_file = try std.io.getStdErr(); var stderr_out_stream = std.io.FileOutStream.init(&stderr_file); stderr = &stderr_out_stream.stream; @@ -440,18 +442,19 @@ fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Mo build_mode = builtin.Mode.ReleaseSafe; } - var color = Module.ErrColor.Auto; - if (flags.single("color")) |color_flag| { - if (mem.eql(u8, color_flag, "auto")) { - color = Module.ErrColor.Auto; - } else if (mem.eql(u8, color_flag, "on")) { - color = Module.ErrColor.On; - } else if (mem.eql(u8, color_flag, "off")) { - color = Module.ErrColor.Off; + const color = blk: { + if (flags.single("color")) |color_flag| { + if (mem.eql(u8, color_flag, "auto")) { + break :blk errmsg.Color.Auto; + } else if (mem.eql(u8, color_flag, "on")) { + break :blk errmsg.Color.On; + } else if (mem.eql(u8, color_flag, "off")) { + break :blk errmsg.Color.Off; + } else unreachable; } else { - unreachable; + break :blk errmsg.Color.Auto; } - } + }; var emit_type = Module.Emit.Binary; if (flags.single("emit")) |emit_flag| { @@ -687,7 +690,14 @@ const usage_fmt = \\ ; -const args_fmt_spec = []Flag{Flag.Bool("--help")}; +const args_fmt_spec = []Flag{ + Flag.Bool("--help"), + Flag.Option("--color", []const []const u8{ + "auto", + "off", + "on", + }), +}; fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_fmt_spec, args); @@ -703,6 +713,20 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { os.exit(1); } + const color = blk: { + if (flags.single("color")) |color_flag| { + if (mem.eql(u8, color_flag, "auto")) { + break :blk errmsg.Color.Auto; + } else if (mem.eql(u8, color_flag, "on")) { + break :blk errmsg.Color.On; + } else if (mem.eql(u8, color_flag, "off")) { + break :blk errmsg.Color.Off; + } else unreachable; + } else { + break :blk errmsg.Color.Auto; + } + }; + for (flags.positionals.toSliceConst()) |file_path| { var file = try os.File.openRead(allocator, file_path); defer file.close(); @@ -721,25 +745,10 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { var error_it = tree.errors.iterator(0); while (error_it.next()) |parse_error| { - const token = tree.tokens.at(parse_error.loc()); - const loc = tree.tokenLocation(0, parse_error.loc()); - try stderr.print("{}:{}:{}: error: ", file_path, loc.line + 1, loc.column + 1); - try tree.renderError(parse_error, stderr); - try stderr.print("\n{}\n", source_code[loc.line_start..loc.line_end]); - { - var i: usize = 0; - while (i < loc.column) : (i += 1) { - try stderr.write(" "); - } - } - { - const caret_count = token.end - token.start; - var i: usize = 0; - while (i < caret_count) : (i += 1) { - try stderr.write("~"); - } - } - try stderr.write("\n"); + const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path); + defer allocator.destroy(msg); + + try errmsg.printToFile(&stderr_file, msg, color); } if (tree.errors.len != 0) { continue; diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 848ce95309..61834eab66 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -10,6 +10,7 @@ const Target = @import("target.zig").Target; const warn = std.debug.warn; const Token = std.zig.Token; const ArrayList = std.ArrayList; +const errmsg = @import("errmsg.zig"); pub const Module = struct { allocator: &mem.Allocator, @@ -55,7 +56,7 @@ pub const Module = struct { link_libs_list: ArrayList(&LinkLib), libc_link_lib: ?&LinkLib, - err_color: ErrColor, + err_color: errmsg.Color, verbose_tokenize: bool, verbose_ast_tree: bool, @@ -87,12 +88,6 @@ pub const Module = struct { Obj, }; - pub const ErrColor = enum { - Auto, - Off, - On, - }; - pub const LinkLib = struct { name: []const u8, path: ?[]const u8, @@ -195,7 +190,7 @@ pub const Module = struct { .windows_subsystem_console = false, .link_libs_list = ArrayList(&LinkLib).init(allocator), .libc_link_lib = null, - .err_color = ErrColor.Auto, + .err_color = errmsg.Color.Auto, .darwin_frameworks = [][]const u8{}, .darwin_version_min = DarwinVersionMin.None, .test_filters = [][]const u8{}, diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 3e1b4fe16a..56d4f9c393 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -120,7 +120,7 @@ pub const Error = union(enum) { ExpectedToken: ExpectedToken, ExpectedCommaOrEnd: ExpectedCommaOrEnd, - pub fn render(self: &Error, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: &const Error, tokens: &Tree.TokenList, stream: var) !void { switch (self.*) { // TODO https://github.com/ziglang/zig/issues/683 @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream), @@ -145,7 +145,7 @@ pub const Error = union(enum) { } } - pub fn loc(self: &Error) TokenIndex { + pub fn loc(self: &const Error) TokenIndex { switch (self.*) { // TODO https://github.com/ziglang/zig/issues/683 @TagType(Error).InvalidToken => |x| return x.token, @@ -190,7 +190,7 @@ pub const Error = union(enum) { pub const ExpectedCall = struct { node: &Node, - pub fn render(self: &ExpectedCall, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: &const ExpectedCall, tokens: &Tree.TokenList, stream: var) !void { return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", @tagName(self.node.id)); } }; @@ -198,7 +198,7 @@ pub const Error = union(enum) { pub const ExpectedCallOrFnProto = struct { node: &Node, - pub fn render(self: &ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: &const ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void { return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id)); } }; @@ -207,7 +207,7 @@ pub const Error = union(enum) { token: TokenIndex, expected_id: @TagType(Token.Id), - pub fn render(self: &ExpectedToken, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: &const ExpectedToken, tokens: &Tree.TokenList, stream: var) !void { const token_name = @tagName(tokens.at(self.token).id); return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name); } @@ -217,7 +217,7 @@ pub const Error = union(enum) { token: TokenIndex, end_id: @TagType(Token.Id), - pub fn render(self: &ExpectedCommaOrEnd, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: &const ExpectedCommaOrEnd, tokens: &Tree.TokenList, stream: var) !void { const token_name = @tagName(tokens.at(self.token).id); return stream.print("expected ',' or {}, found {}", @tagName(self.end_id), token_name); } @@ -229,7 +229,7 @@ pub const Error = union(enum) { token: TokenIndex, - pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: &const ThisError, tokens: &Tree.TokenList, stream: var) !void { const token_name = @tagName(tokens.at(self.token).id); return stream.print(msg, token_name); } @@ -242,7 +242,7 @@ pub const Error = union(enum) { token: TokenIndex, - pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: &const ThisError, tokens: &Tree.TokenList, stream: var) !void { return stream.write(msg); } }; -- cgit v1.2.3 From 717ac85a5acb5e6ae063c4d0eb3b8f1bd260776a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 May 2018 18:37:12 -0400 Subject: zig fmt: add --color option to CLI help text --- src-self-hosted/main.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 734c3911ae..71838503b7 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -686,6 +686,7 @@ const usage_fmt = \\ \\Options: \\ --help Print this help and exit + \\ --color [auto|off|on] Enable or disable colored error messages \\ \\ ; -- cgit v1.2.3 From fcbb7426faac5e693ef195defe2d8d2a2eddadb1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 31 May 2018 10:56:59 -0400 Subject: use * for pointer type instead of & See #770 To help automatically translate code, see the zig-fmt-pointer-reform-2 branch. This will convert all & into *. Due to the syntax ambiguity (which is why we are making this change), even address-of & will turn into *, so you'll have to manually fix thes instances. You will be guaranteed to get compile errors for them - expected 'type', found 'foo' --- build.zig | 14 +- doc/docgen.zig | 22 +- doc/langref.html.in | 212 ++++----- example/cat/main.zig | 2 +- example/hello_world/hello_libc.zig | 2 +- example/mix_o_files/base64.zig | 2 +- example/mix_o_files/build.zig | 2 +- example/shared_library/build.zig | 2 +- src-self-hosted/arg.zig | 12 +- src-self-hosted/errmsg.zig | 14 +- src-self-hosted/introspect.zig | 6 +- src-self-hosted/ir.zig | 2 +- src-self-hosted/main.zig | 36 +- src-self-hosted/module.zig | 30 +- src-self-hosted/scope.zig | 2 +- src-self-hosted/target.zig | 10 +- src/all_types.hpp | 31 +- src/analyze.cpp | 8 +- src/ast_render.cpp | 31 +- src/codegen.cpp | 2 +- src/ir.cpp | 89 ++-- src/ir_print.cpp | 6 +- src/parser.cpp | 41 +- src/translate_c.cpp | 33 +- std/array_list.zig | 46 +- std/atomic/queue.zig | 32 +- std/atomic/stack.zig | 36 +- std/base64.zig | 12 +- std/buf_map.zig | 18 +- std/buf_set.zig | 18 +- std/buffer.zig | 40 +- std/build.zig | 278 ++++++------ std/c/darwin.zig | 8 +- std/c/index.zig | 72 ++-- std/c/linux.zig | 4 +- std/c/windows.zig | 2 +- std/crypto/blake2.zig | 16 +- std/crypto/md5.zig | 8 +- std/crypto/sha1.zig | 8 +- std/crypto/sha2.zig | 16 +- std/crypto/sha3.zig | 6 +- std/crypto/throughput_test.zig | 4 +- std/cstr.zig | 26 +- std/debug/failing_allocator.zig | 10 +- std/debug/index.zig | 106 ++--- std/elf.zig | 18 +- std/event.zig | 34 +- std/fmt/errol/index.zig | 14 +- std/fmt/index.zig | 8 +- std/hash/adler.zig | 4 +- std/hash/crc.zig | 8 +- std/hash/fnv.zig | 4 +- std/hash/siphash.zig | 8 +- std/hash_map.zig | 36 +- std/heap.zig | 82 ++-- std/io.zig | 80 ++-- std/json.zig | 36 +- std/linked_list.zig | 32 +- std/macho.zig | 16 +- std/math/complex/atan.zig | 4 +- std/math/complex/cosh.zig | 4 +- std/math/complex/exp.zig | 4 +- std/math/complex/index.zig | 14 +- std/math/complex/ldexp.zig | 8 +- std/math/complex/pow.zig | 2 +- std/math/complex/sinh.zig | 4 +- std/math/complex/sqrt.zig | 4 +- std/math/complex/tanh.zig | 4 +- std/math/hypot.zig | 2 +- std/math/index.zig | 4 +- std/mem.zig | 48 +-- std/net.zig | 8 +- std/os/child_process.zig | 62 +-- std/os/darwin.zig | 64 +-- std/os/file.zig | 32 +- std/os/get_user_id.zig | 8 +- std/os/index.zig | 164 +++---- std/os/linux/index.zig | 174 ++++---- std/os/linux/vdso.zig | 36 +- std/os/linux/x86_64.zig | 8 +- std/os/path.zig | 22 +- std/os/test.zig | 2 +- std/os/time.zig | 6 +- std/os/windows/index.zig | 96 ++--- std/os/windows/util.zig | 12 +- std/os/zen.zig | 20 +- std/rand/index.zig | 46 +- std/rand/ziggurat.zig | 10 +- std/segmented_list.zig | 54 +-- std/sort.zig | 54 +-- std/special/bootstrap.zig | 20 +- std/special/build_file_template.zig | 4 +- std/special/build_runner.zig | 6 +- std/special/builtin.zig | 8 +- std/special/compiler_rt/index.zig | 4 +- std/special/compiler_rt/udivmod.zig | 18 +- std/special/compiler_rt/udivmoddi4.zig | 2 +- std/special/compiler_rt/udivmodti4.zig | 4 +- std/special/compiler_rt/udivti3.zig | 2 +- std/special/compiler_rt/umodti3.zig | 2 +- std/special/panic.zig | 2 +- std/unicode.zig | 6 +- std/zig/ast.zig | 570 ++++++++++++------------- std/zig/bench.zig | 6 +- std/zig/parse.zig | 156 +++---- std/zig/parser_test.zig | 2 +- std/zig/render.zig | 56 +-- std/zig/tokenizer.zig | 8 +- test/assemble_and_link.zig | 2 +- test/build_examples.zig | 2 +- test/cases/align.zig | 56 +-- test/cases/atomics.zig | 12 +- test/cases/bugs/655.zig | 4 +- test/cases/bugs/828.zig | 6 +- test/cases/bugs/920.zig | 6 +- test/cases/cast.zig | 42 +- test/cases/const_slice_child.zig | 6 +- test/cases/coroutines.zig | 6 +- test/cases/enum.zig | 10 +- test/cases/enum_with_members.zig | 2 +- test/cases/eval.zig | 12 +- test/cases/field_parent_ptr.zig | 4 +- test/cases/fn_in_struct_in_comptime.zig | 6 +- test/cases/generics.zig | 8 +- test/cases/incomplete_struct_param_tld.zig | 4 +- test/cases/math.zig | 18 +- test/cases/misc.zig | 48 +-- test/cases/null.zig | 2 +- test/cases/reflection.zig | 2 +- test/cases/slice.zig | 2 +- test/cases/struct.zig | 28 +- test/cases/struct_contains_null_ptr_itself.zig | 4 +- test/cases/switch.zig | 2 +- test/cases/this.zig | 2 +- test/cases/type_info.zig | 16 +- test/cases/undefined.zig | 4 +- test/cases/union.zig | 16 +- test/compare_output.zig | 20 +- test/compile_errors.zig | 122 +++--- test/gen_h.zig | 2 +- test/runtime_safety.zig | 2 +- test/standalone/brace_expansion/build.zig | 2 +- test/standalone/brace_expansion/main.zig | 8 +- test/standalone/issue_339/build.zig | 2 +- test/standalone/issue_339/test.zig | 2 +- test/standalone/issue_794/build.zig | 2 +- test/standalone/pkg_import/build.zig | 2 +- test/standalone/use_alias/build.zig | 2 +- test/tests.zig | 136 +++--- test/translate_c.zig | 58 +-- 150 files changed, 2162 insertions(+), 2143 deletions(-) diff --git a/build.zig b/build.zig index a4e3dbcdfa..109a799ac9 100644 --- a/build.zig +++ b/build.zig @@ -10,7 +10,7 @@ const ArrayList = std.ArrayList; const Buffer = std.Buffer; const io = std.io; -pub fn build(b: &Builder) !void { +pub fn build(b: *Builder) !void { const mode = b.standardReleaseOptions(); var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); @@ -132,7 +132,7 @@ pub fn build(b: &Builder) !void { test_step.dependOn(tests.addGenHTests(b, test_filter)); } -fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) void { +fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) void { for (dep.libdirs.toSliceConst()) |lib_dir| { lib_exe_obj.addLibPath(lib_dir); } @@ -147,7 +147,7 @@ fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) vo } } -fn addCppLib(b: &Builder, lib_exe_obj: &std.build.LibExeObjStep, cmake_binary_dir: []const u8, lib_name: []const u8) void { +fn addCppLib(b: *Builder, lib_exe_obj: *std.build.LibExeObjStep, cmake_binary_dir: []const u8, lib_name: []const u8) void { const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib"; lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable); } @@ -159,7 +159,7 @@ const LibraryDep = struct { includes: ArrayList([]const u8), }; -fn findLLVM(b: &Builder, llvm_config_exe: []const u8) !LibraryDep { +fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { const libs_output = try b.exec([][]const u8{ llvm_config_exe, "--libs", @@ -217,7 +217,7 @@ fn findLLVM(b: &Builder, llvm_config_exe: []const u8) !LibraryDep { return result; } -pub fn installStdLib(b: &Builder, stdlib_files: []const u8) void { +pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void { var it = mem.split(stdlib_files, ";"); while (it.next()) |stdlib_file| { const src_path = os.path.join(b.allocator, "std", stdlib_file) catch unreachable; @@ -226,7 +226,7 @@ pub fn installStdLib(b: &Builder, stdlib_files: []const u8) void { } } -pub fn installCHeaders(b: &Builder, c_header_files: []const u8) void { +pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void { var it = mem.split(c_header_files, ";"); while (it.next()) |c_header_file| { const src_path = os.path.join(b.allocator, "c_headers", c_header_file) catch unreachable; @@ -235,7 +235,7 @@ pub fn installCHeaders(b: &Builder, c_header_files: []const u8) void { } } -fn nextValue(index: &usize, build_info: []const u8) []const u8 { +fn nextValue(index: *usize, build_info: []const u8) []const u8 { const start = index.*; while (true) : (index.* += 1) { switch (build_info[index.*]) { diff --git a/doc/docgen.zig b/doc/docgen.zig index 7dc444f127..fed4bb8eba 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -104,7 +104,7 @@ const Tokenizer = struct { }; } - fn next(self: &Tokenizer) Token { + fn next(self: *Tokenizer) Token { var result = Token{ .id = Token.Id.Eof, .start = self.index, @@ -196,7 +196,7 @@ const Tokenizer = struct { line_end: usize, }; - fn getTokenLocation(self: &Tokenizer, token: &const Token) Location { + fn getTokenLocation(self: *Tokenizer, token: *const Token) Location { var loc = Location{ .line = 0, .column = 0, @@ -221,7 +221,7 @@ const Tokenizer = struct { } }; -fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const u8, args: ...) error { +fn parseError(tokenizer: *Tokenizer, token: *const Token, comptime fmt: []const u8, args: ...) error { const loc = tokenizer.getTokenLocation(token); warn("{}:{}:{}: error: " ++ fmt ++ "\n", tokenizer.source_file_name, loc.line + 1, loc.column + 1, args); if (loc.line_start <= loc.line_end) { @@ -244,13 +244,13 @@ fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const return error.ParseError; } -fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) !void { +fn assertToken(tokenizer: *Tokenizer, token: *const Token, id: Token.Id) !void { if (token.id != id) { return parseError(tokenizer, token, "expected {}, found {}", @tagName(id), @tagName(token.id)); } } -fn eatToken(tokenizer: &Tokenizer, id: Token.Id) !Token { +fn eatToken(tokenizer: *Tokenizer, id: Token.Id) !Token { const token = tokenizer.next(); try assertToken(tokenizer, token, id); return token; @@ -317,7 +317,7 @@ const Action = enum { Close, }; -fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc { +fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { var urls = std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator); errdefer urls.deinit(); @@ -546,7 +546,7 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc { }; } -fn urlize(allocator: &mem.Allocator, input: []const u8) ![]u8 { +fn urlize(allocator: *mem.Allocator, input: []const u8) ![]u8 { var buf = try std.Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -566,7 +566,7 @@ fn urlize(allocator: &mem.Allocator, input: []const u8) ![]u8 { return buf.toOwnedSlice(); } -fn escapeHtml(allocator: &mem.Allocator, input: []const u8) ![]u8 { +fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 { var buf = try std.Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -608,7 +608,7 @@ test "term color" { assert(mem.eql(u8, result, "AgreenB")); } -fn termColor(allocator: &mem.Allocator, input: []const u8) ![]u8 { +fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 { var buf = try std.Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -688,7 +688,7 @@ fn termColor(allocator: &mem.Allocator, input: []const u8) ![]u8 { return buf.toOwnedSlice(); } -fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var, zig_exe: []const u8) !void { +fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void { var code_progress_index: usize = 0; for (toc.nodes) |node| { switch (node) { @@ -1036,7 +1036,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var } } -fn exec(allocator: &mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult { +fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult { const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size); switch (result.term) { os.ChildProcess.Term.Exited => |exit_code| { diff --git a/doc/langref.html.in b/doc/langref.html.in index d63c38d0fe..3bd1124e00 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -458,7 +458,7 @@ test "string literals" { // A C string literal is a null terminated pointer. const null_terminated_bytes = c"hello"; - assert(@typeOf(null_terminated_bytes) == &const u8); + assert(@typeOf(null_terminated_bytes) == *const u8); assert(null_terminated_bytes[5] == 0); } {#code_end#} @@ -547,7 +547,7 @@ const c_string_literal = ; {#code_end#}

- In this example the variable c_string_literal has type &const char and + In this example the variable c_string_literal has type *const char and has a terminating null byte.

{#see_also|@embedFile#} @@ -1403,12 +1403,12 @@ test "address of syntax" { assert(x_ptr.* == 1234); // When you get the address of a const variable, you get a const pointer. - assert(@typeOf(x_ptr) == &const i32); + assert(@typeOf(x_ptr) == *const i32); // If you want to mutate the value, you'd need an address of a mutable variable: var y: i32 = 5678; const y_ptr = &y; - assert(@typeOf(y_ptr) == &i32); + assert(@typeOf(y_ptr) == *i32); y_ptr.* += 1; assert(y_ptr.* == 5679); } @@ -1455,7 +1455,7 @@ comptime { test "@ptrToInt and @intToPtr" { // To convert an integer address into a pointer, use @intToPtr: - const ptr = @intToPtr(&i32, 0xdeadbeef); + const ptr = @intToPtr(*i32, 0xdeadbeef); // To convert a pointer to an integer, use @ptrToInt: const addr = @ptrToInt(ptr); @@ -1467,7 +1467,7 @@ test "@ptrToInt and @intToPtr" { comptime { // Zig is able to do this at compile-time, as long as // ptr is never dereferenced. - const ptr = @intToPtr(&i32, 0xdeadbeef); + const ptr = @intToPtr(*i32, 0xdeadbeef); const addr = @ptrToInt(ptr); assert(@typeOf(addr) == usize); assert(addr == 0xdeadbeef); @@ -1477,17 +1477,17 @@ test "volatile" { // In Zig, loads and stores are assumed to not have side effects. // If a given load or store should have side effects, such as // Memory Mapped Input/Output (MMIO), use `volatile`: - const mmio_ptr = @intToPtr(&volatile u8, 0x12345678); + const mmio_ptr = @intToPtr(*volatile u8, 0x12345678); // Now loads and stores with mmio_ptr are guaranteed to all happen // and in the same order as in source code. - assert(@typeOf(mmio_ptr) == &volatile u8); + assert(@typeOf(mmio_ptr) == *volatile u8); } test "nullable pointers" { // Pointers cannot be null. If you want a null pointer, use the nullable // prefix `?` to make the pointer type nullable. - var ptr: ?&i32 = null; + var ptr: ?*i32 = null; var x: i32 = 1; ptr = &x; @@ -1496,7 +1496,7 @@ test "nullable pointers" { // Nullable pointers are the same size as normal pointers, because pointer // value 0 is used as the null value. - assert(@sizeOf(?&i32) == @sizeOf(&i32)); + assert(@sizeOf(?*i32) == @sizeOf(*i32)); } test "pointer casting" { @@ -1504,7 +1504,7 @@ test "pointer casting" { // operation that Zig cannot protect you against. Use @ptrCast only when other // conversions are not possible. const bytes align(@alignOf(u32)) = []u8{0x12, 0x12, 0x12, 0x12}; - const u32_ptr = @ptrCast(&const u32, &bytes[0]); + const u32_ptr = @ptrCast(*const u32, &bytes[0]); assert(u32_ptr.* == 0x12121212); // Even this example is contrived - there are better ways to do the above than @@ -1518,7 +1518,7 @@ test "pointer casting" { test "pointer child type" { // pointer types have a `child` field which tells you the type they point to. - assert((&u32).Child == u32); + assert((*u32).Child == u32); } {#code_end#} {#header_open|Alignment#} @@ -1543,15 +1543,15 @@ const builtin = @import("builtin"); test "variable alignment" { var x: i32 = 1234; const align_of_i32 = @alignOf(@typeOf(x)); - assert(@typeOf(&x) == &i32); - assert(&i32 == &align(align_of_i32) i32); + assert(@typeOf(&x) == *i32); + assert(*i32 == *align(align_of_i32) i32); if (builtin.arch == builtin.Arch.x86_64) { - assert((&i32).alignment == 4); + assert((*i32).alignment == 4); } } {#code_end#} -

In the same way that a &i32 can be implicitly cast to a - &const i32, a pointer with a larger alignment can be implicitly +

In the same way that a *i32 can be implicitly cast to a + *const i32, a pointer with a larger alignment can be implicitly cast to a pointer with a smaller alignment, but not vice versa.

@@ -1565,7 +1565,7 @@ var foo: u8 align(4) = 100; test "global variable alignment" { assert(@typeOf(&foo).alignment == 4); - assert(@typeOf(&foo) == &align(4) u8); + assert(@typeOf(&foo) == *align(4) u8); const slice = (&foo)[0..1]; assert(@typeOf(slice) == []align(4) u8); } @@ -1610,7 +1610,7 @@ fn foo(bytes: []u8) u32 { u8 can alias any memory.

As an example, this code produces undefined behavior:

-
@ptrCast(&u32, f32(12.34)).*
+
@ptrCast(*u32, f32(12.34)).*

Instead, use {#link|@bitCast#}:

@bitCast(u32, f32(12.34))

As an added benefit, the @bitcast version works at compile-time.

@@ -1736,7 +1736,7 @@ const Vec3 = struct { }; } - pub fn dot(self: &const Vec3, other: &const Vec3) f32 { + pub fn dot(self: *const Vec3, other: *const Vec3) f32 { return self.x * other.x + self.y * other.y + self.z * other.z; } }; @@ -1768,7 +1768,7 @@ test "struct namespaced variable" { // struct field order is determined by the compiler for optimal performance. // however, you can still calculate a struct base pointer given a field pointer: -fn setYBasedOnX(x: &f32, y: f32) void { +fn setYBasedOnX(x: *f32, y: f32) void { const point = @fieldParentPtr(Point, "x", x); point.y = y; } @@ -1786,13 +1786,13 @@ test "field parent pointer" { fn LinkedList(comptime T: type) type { return struct { pub const Node = struct { - prev: ?&Node, - next: ?&Node, + prev: ?*Node, + next: ?*Node, data: T, }; - first: ?&Node, - last: ?&Node, + first: ?*Node, + last: ?*Node, len: usize, }; } @@ -2039,7 +2039,7 @@ const Variant = union(enum) { Int: i32, Bool: bool, - fn truthy(self: &const Variant) bool { + fn truthy(self: *const Variant) bool { return switch (self.*) { Variant.Int => |x_int| x_int != 0, Variant.Bool => |x_bool| x_bool, @@ -2786,7 +2786,7 @@ test "pass aggregate type by value to function" { } {#code_end#}

- Instead, one must use &const. Zig allows implicitly casting something + Instead, one must use *const. Zig allows implicitly casting something to a const pointer to it:

{#code_begin|test#} @@ -2794,7 +2794,7 @@ const Foo = struct { x: i32, }; -fn bar(foo: &const Foo) void {} +fn bar(foo: *const Foo) void {} test "implicitly cast to const pointer" { bar(Foo {.x = 12,}); @@ -3208,16 +3208,16 @@ struct Foo *do_a_thing(void) {

Zig code

{#code_begin|syntax#} // malloc prototype included for reference -extern fn malloc(size: size_t) ?&u8; +extern fn malloc(size: size_t) ?*u8; -fn doAThing() ?&Foo { +fn doAThing() ?*Foo { const ptr = malloc(1234) ?? return null; // ... } {#code_end#}

Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr" - is &u8 not ?&u8. The ?? operator + is *u8 not ?*u8. The ?? operator unwrapped the nullable type and therefore ptr is guaranteed to be non-null everywhere it is used in the function.

@@ -3237,7 +3237,7 @@ fn doAThing() ?&Foo { In Zig you can accomplish the same thing:

{#code_begin|syntax#} -fn doAThing(nullable_foo: ?&Foo) void { +fn doAThing(nullable_foo: ?*Foo) void { // do some stuff if (nullable_foo) |foo| { @@ -3713,7 +3713,7 @@ fn List(comptime T: type) type {

{#code_begin|syntax#} const Node = struct { - next: &Node, + next: *Node, name: []u8, }; {#code_end#} @@ -3745,7 +3745,7 @@ pub fn main() void { {#code_begin|syntax#} /// Calls print and then flushes the buffer. -pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) error!void { +pub fn printf(self: *OutStream, comptime format: []const u8, args: ...) error!void { const State = enum { Start, OpenBrace, @@ -3817,7 +3817,7 @@ pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) error!vo and emits a function that actually looks like this:

{#code_begin|syntax#} -pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) !void { +pub fn printf(self: *OutStream, arg0: i32, arg1: []const u8) !void { try self.write("here is a string: '"); try self.printValue(arg0); try self.write("' here is a number: "); @@ -3831,7 +3831,7 @@ pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) !void { on the type:

{#code_begin|syntax#} -pub fn printValue(self: &OutStream, value: var) !void { +pub fn printValue(self: *OutStream, value: var) !void { const T = @typeOf(value); if (@isInteger(T)) { return self.printInt(T, value); @@ -3911,7 +3911,7 @@ pub fn main() void { at compile time.

{#header_open|@addWithOverflow#} -
@addWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool
+
@addWithOverflow(comptime T: type, a: T, b: T, result: *T) bool

Performs result.* = a + b. If overflow or underflow occurs, stores the overflowed bits in result and returns true. @@ -3919,7 +3919,7 @@ pub fn main() void {

{#header_close#} {#header_open|@ArgType#} -
@ArgType(comptime T: type, comptime n: usize) -> type
+
@ArgType(comptime T: type, comptime n: usize) type

This builtin function takes a function type and returns the type of the parameter at index n.

@@ -3931,7 +3931,7 @@ pub fn main() void {

{#header_close#} {#header_open|@atomicLoad#} -
@atomicLoad(comptime T: type, ptr: &const T, comptime ordering: builtin.AtomicOrder) -> T
+
@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T

This builtin function atomically dereferences a pointer and returns the value.

@@ -3950,7 +3950,7 @@ pub fn main() void {

{#header_close#} {#header_open|@atomicRmw#} -
@atomicRmw(comptime T: type, ptr: &T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) -> T
+
@atomicRmw(comptime T: type, ptr: *T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) T

This builtin function atomically modifies memory and then returns the previous value.

@@ -3969,7 +3969,7 @@ pub fn main() void {

{#header_close#} {#header_open|@bitCast#} -
@bitCast(comptime DestType: type, value: var) -> DestType
+
@bitCast(comptime DestType: type, value: var) DestType

Converts a value of one type to another type.

@@ -4002,9 +4002,9 @@ pub fn main() void { {#header_close#} {#header_open|@alignCast#} -
@alignCast(comptime alignment: u29, ptr: var) -> var
+
@alignCast(comptime alignment: u29, ptr: var) var

- ptr can be &T, fn(), ?&T, + ptr can be *T, fn(), ?*T, ?fn(), or []T. It returns the same type as ptr except with the alignment adjusted to the new value.

@@ -4013,7 +4013,7 @@ pub fn main() void { {#header_close#} {#header_open|@alignOf#} -
@alignOf(comptime T: type) -> (number literal)
+
@alignOf(comptime T: type) (number literal)

This function returns the number of bytes that this type should be aligned to for the current target to match the C ABI. When the child type of a pointer has @@ -4021,7 +4021,7 @@ pub fn main() void {

const assert = @import("std").debug.assert;
 comptime {
-    assert(&u32 == &align(@alignOf(u32)) u32);
+    assert(*u32 == *align(@alignOf(u32)) u32);
 }

The result is a target-specific compile time constant. It is guaranteed to be @@ -4049,7 +4049,7 @@ comptime { {#see_also|Import from C Header File|@cInclude|@cImport|@cUndef|void#} {#header_close#} {#header_open|@cImport#} -

@cImport(expression) -> (namespace)
+
@cImport(expression) (namespace)

This function parses C code and imports the functions, types, variables, and compatible macro definitions into the result namespace. @@ -4095,13 +4095,13 @@ comptime { {#see_also|Import from C Header File|@cImport|@cDefine|@cInclude#} {#header_close#} {#header_open|@canImplicitCast#} -

@canImplicitCast(comptime T: type, value) -> bool
+
@canImplicitCast(comptime T: type, value) bool

Returns whether a value can be implicitly casted to a given type.

{#header_close#} {#header_open|@clz#} -
@clz(x: T) -> U
+
@clz(x: T) U

This function counts the number of leading zeroes in x which is an integer type T. @@ -4116,13 +4116,13 @@ comptime { {#header_close#} {#header_open|@cmpxchgStrong#} -

@cmpxchgStrong(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> ?T
+
@cmpxchgStrong(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T

This function performs a strong atomic compare exchange operation. It's the equivalent of this code, except atomic:

{#code_begin|syntax#} -fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T { +fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T { const old_value = ptr.*; if (old_value == expected_value) { ptr.* = new_value; @@ -4143,13 +4143,13 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_v {#see_also|Compile Variables|cmpxchgWeak#} {#header_close#} {#header_open|@cmpxchgWeak#} -
@cmpxchgWeak(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> ?T
+
@cmpxchgWeak(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T

This function performs a weak atomic compare exchange operation. It's the equivalent of this code, except atomic:

{#code_begin|syntax#} -fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T { +fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T { const old_value = ptr.*; if (old_value == expected_value and usuallyTrueButSometimesFalse()) { ptr.* = new_value; @@ -4237,7 +4237,7 @@ test "main" { {#code_end#} {#header_close#} {#header_open|@ctz#} -
@ctz(x: T) -> U
+
@ctz(x: T) U

This function counts the number of trailing zeroes in x which is an integer type T. @@ -4251,7 +4251,7 @@ test "main" {

{#header_close#} {#header_open|@divExact#} -
@divExact(numerator: T, denominator: T) -> T
+
@divExact(numerator: T, denominator: T) T

Exact division. Caller guarantees denominator != 0 and @divTrunc(numerator, denominator) * denominator == numerator. @@ -4264,7 +4264,7 @@ test "main" { {#see_also|@divTrunc|@divFloor#} {#header_close#} {#header_open|@divFloor#} -

@divFloor(numerator: T, denominator: T) -> T
+
@divFloor(numerator: T, denominator: T) T

Floored division. Rounds toward negative infinity. For unsigned integers it is the same as numerator / denominator. Caller guarantees denominator != 0 and @@ -4278,7 +4278,7 @@ test "main" { {#see_also|@divTrunc|@divExact#} {#header_close#} {#header_open|@divTrunc#} -

@divTrunc(numerator: T, denominator: T) -> T
+
@divTrunc(numerator: T, denominator: T) T

Truncated division. Rounds toward zero. For unsigned integers it is the same as numerator / denominator. Caller guarantees denominator != 0 and @@ -4292,7 +4292,7 @@ test "main" { {#see_also|@divFloor|@divExact#} {#header_close#} {#header_open|@embedFile#} -

@embedFile(comptime path: []const u8) -> [X]u8
+
@embedFile(comptime path: []const u8) [X]u8

This function returns a compile time constant fixed-size array with length equal to the byte count of the file given by path. The contents of the array @@ -4304,19 +4304,19 @@ test "main" { {#see_also|@import#} {#header_close#} {#header_open|@export#} -

@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) -> []const u8
+
@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8

Creates a symbol in the output object file.

{#header_close#} {#header_open|@tagName#} -
@tagName(value: var) -> []const u8
+
@tagName(value: var) []const u8

Converts an enum value or union value to a slice of bytes representing the name.

{#header_close#} {#header_open|@TagType#} -
@TagType(T: type) -> type
+
@TagType(T: type) type

For an enum, returns the integer type that is used to store the enumeration value.

@@ -4325,7 +4325,7 @@ test "main" {

{#header_close#} {#header_open|@errorName#} -
@errorName(err: error) -> []u8
+
@errorName(err: error) []u8

This function returns the string representation of an error. If an error declaration is: @@ -4341,7 +4341,7 @@ test "main" {

{#header_close#} {#header_open|@errorReturnTrace#} -
@errorReturnTrace() -> ?&builtin.StackTrace
+
@errorReturnTrace() ?*builtin.StackTrace

If the binary is built with error return tracing, and this function is invoked in a function that calls a function with an error or error union return type, returns a @@ -4360,7 +4360,7 @@ test "main" { {#header_close#} {#header_open|@fieldParentPtr#}

@fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8,
-    field_ptr: &T) -> &ParentType
+ field_ptr: *T) *ParentType

Given a pointer to a field, returns the base pointer of a struct.

@@ -4380,7 +4380,7 @@ test "main" {

{#header_close#} {#header_open|@import#} -
@import(comptime path: []u8) -> (namespace)
+
@import(comptime path: []u8) (namespace)

This function finds a zig file corresponding to path and imports all the public top level declarations into the resulting namespace. @@ -4400,7 +4400,7 @@ test "main" { {#see_also|Compile Variables|@embedFile#} {#header_close#} {#header_open|@inlineCall#} -

@inlineCall(function: X, args: ...) -> Y
+
@inlineCall(function: X, args: ...) Y

This calls a function, in the same way that invoking an expression with parentheses does:

@@ -4420,19 +4420,19 @@ fn add(a: i32, b: i32) i32 { return a + b; } {#see_also|@noInlineCall#} {#header_close#} {#header_open|@intToPtr#} -
@intToPtr(comptime DestType: type, int: usize) -> DestType
+
@intToPtr(comptime DestType: type, int: usize) DestType

Converts an integer to a pointer. To convert the other way, use {#link|@ptrToInt#}.

{#header_close#} {#header_open|@IntType#} -
@IntType(comptime is_signed: bool, comptime bit_count: u8) -> type
+
@IntType(comptime is_signed: bool, comptime bit_count: u8) type

This function returns an integer type with the given signness and bit count.

{#header_close#} {#header_open|@maxValue#} -
@maxValue(comptime T: type) -> (number literal)
+
@maxValue(comptime T: type) (number literal)

This function returns the maximum value of the integer type T.

@@ -4441,7 +4441,7 @@ fn add(a: i32, b: i32) i32 { return a + b; }

{#header_close#} {#header_open|@memberCount#} -
@memberCount(comptime T: type) -> (number literal)
+
@memberCount(comptime T: type) (number literal)

This function returns the number of members in a struct, enum, or union type.

@@ -4453,7 +4453,7 @@ fn add(a: i32, b: i32) i32 { return a + b; }

{#header_close#} {#header_open|@memberName#} -
@memberName(comptime T: type, comptime index: usize) -> [N]u8
+
@memberName(comptime T: type, comptime index: usize) [N]u8

Returns the field name of a struct, union, or enum.

The result is a compile time constant. @@ -4463,15 +4463,15 @@ fn add(a: i32, b: i32) i32 { return a + b; }

{#header_close#} {#header_open|@field#} -
@field(lhs: var, comptime field_name: []const u8) -> (field)
+
@field(lhs: var, comptime field_name: []const u8) (field)

Preforms field access equivalent to lhs.->field_name-<.

{#header_close#} {#header_open|@memberType#} -
@memberType(comptime T: type, comptime index: usize) -> type
+
@memberType(comptime T: type, comptime index: usize) type

Returns the field type of a struct or union.

{#header_close#} {#header_open|@memcpy#} -
@memcpy(noalias dest: &u8, noalias source: &const u8, byte_count: usize)
+
@memcpy(noalias dest: *u8, noalias source: *const u8, byte_count: usize)

This function copies bytes from one region of memory to another. dest and source are both pointers and must not overlap. @@ -4489,7 +4489,7 @@ fn add(a: i32, b: i32) i32 { return a + b; } mem.copy(u8, dest[0...byte_count], source[0...byte_count]);

{#header_close#} {#header_open|@memset#} -
@memset(dest: &u8, c: u8, byte_count: usize)
+
@memset(dest: *u8, c: u8, byte_count: usize)

This function sets a region of memory to c. dest is a pointer.

@@ -4506,7 +4506,7 @@ mem.copy(u8, dest[0...byte_count], source[0...byte_count]); mem.set(u8, dest, c); {#header_close#} {#header_open|@minValue#} -
@minValue(comptime T: type) -> (number literal)
+
@minValue(comptime T: type) (number literal)

This function returns the minimum value of the integer type T.

@@ -4515,7 +4515,7 @@ mem.set(u8, dest, c);

{#header_close#} {#header_open|@mod#} -
@mod(numerator: T, denominator: T) -> T
+
@mod(numerator: T, denominator: T) T

Modulus division. For unsigned integers this is the same as numerator % denominator. Caller guarantees denominator > 0. @@ -4528,7 +4528,7 @@ mem.set(u8, dest, c); {#see_also|@rem#} {#header_close#} {#header_open|@mulWithOverflow#} -

@mulWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool
+
@mulWithOverflow(comptime T: type, a: T, b: T, result: *T) bool

Performs result.* = a * b. If overflow or underflow occurs, stores the overflowed bits in result and returns true. @@ -4536,7 +4536,7 @@ mem.set(u8, dest, c);

{#header_close#} {#header_open|@newStackCall#} -
@newStackCall(new_stack: []u8, function: var, args: ...) -> var
+
@newStackCall(new_stack: []u8, function: var, args: ...) var

This calls a function, in the same way that invoking an expression with parentheses does. However, instead of using the same stack as the caller, the function uses the stack provided in the new_stack @@ -4572,7 +4572,7 @@ fn targetFunction(x: i32) usize { {#code_end#} {#header_close#} {#header_open|@noInlineCall#} -

@noInlineCall(function: var, args: ...) -> var
+
@noInlineCall(function: var, args: ...) var

This calls a function, in the same way that invoking an expression with parentheses does:

@@ -4594,13 +4594,13 @@ fn add(a: i32, b: i32) i32 { {#see_also|@inlineCall#} {#header_close#} {#header_open|@offsetOf#} -
@offsetOf(comptime T: type, comptime field_name: [] const u8) -> (number literal)
+
@offsetOf(comptime T: type, comptime field_name: [] const u8) (number literal)

This function returns the byte offset of a field relative to its containing struct.

{#header_close#} {#header_open|@OpaqueType#} -
@OpaqueType() -> type
+
@OpaqueType() type

Creates a new type with an unknown size and alignment.

@@ -4608,12 +4608,12 @@ fn add(a: i32, b: i32) i32 { This is typically used for type safety when interacting with C code that does not expose struct details. Example:

- {#code_begin|test_err|expected type '&Derp', found '&Wat'#} + {#code_begin|test_err|expected type '*Derp', found '*Wat'#} const Derp = @OpaqueType(); const Wat = @OpaqueType(); -extern fn bar(d: &Derp) void; -export fn foo(w: &Wat) void { +extern fn bar(d: *Derp) void; +export fn foo(w: *Wat) void { bar(w); } @@ -4623,7 +4623,7 @@ test "call foo" { {#code_end#} {#header_close#} {#header_open|@panic#} -
@panic(message: []const u8) -> noreturn
+
@panic(message: []const u8) noreturn

Invokes the panic handler function. By default the panic handler function calls the public panic function exposed in the root source file, or @@ -4639,19 +4639,19 @@ test "call foo" { {#see_also|Root Source File#} {#header_close#} {#header_open|@ptrCast#} -

@ptrCast(comptime DestType: type, value: var) -> DestType
+
@ptrCast(comptime DestType: type, value: var) DestType

Converts a pointer of one type to a pointer of another type.

{#header_close#} {#header_open|@ptrToInt#} -
@ptrToInt(value: var) -> usize
+
@ptrToInt(value: var) usize

Converts value to a usize which is the address of the pointer. value can be one of these types:

    -
  • &T
  • -
  • ?&T
  • +
  • *T
  • +
  • ?*T
  • fn()
  • ?fn()
@@ -4659,7 +4659,7 @@ test "call foo" { {#header_close#} {#header_open|@rem#} -
@rem(numerator: T, denominator: T) -> T
+
@rem(numerator: T, denominator: T) T

Remainder division. For unsigned integers this is the same as numerator % denominator. Caller guarantees denominator > 0. @@ -4776,13 +4776,13 @@ pub const FloatMode = enum { {#see_also|Compile Variables#} {#header_close#} {#header_open|@setGlobalSection#} -

@setGlobalSection(global_variable_name, comptime section_name: []const u8) -> bool
+
@setGlobalSection(global_variable_name, comptime section_name: []const u8) bool

Puts the global variable in the specified section.

{#header_close#} {#header_open|@shlExact#} -
@shlExact(value: T, shift_amt: Log2T) -> T
+
@shlExact(value: T, shift_amt: Log2T) T

Performs the left shift operation (<<). Caller guarantees that the shift will not shift any 1 bits out. @@ -4794,7 +4794,7 @@ pub const FloatMode = enum { {#see_also|@shrExact|@shlWithOverflow#} {#header_close#} {#header_open|@shlWithOverflow#} -

@shlWithOverflow(comptime T: type, a: T, shift_amt: Log2T, result: &T) -> bool
+
@shlWithOverflow(comptime T: type, a: T, shift_amt: Log2T, result: *T) bool

Performs result.* = a << b. If overflow or underflow occurs, stores the overflowed bits in result and returns true. @@ -4807,7 +4807,7 @@ pub const FloatMode = enum { {#see_also|@shlExact|@shrExact#} {#header_close#} {#header_open|@shrExact#} -

@shrExact(value: T, shift_amt: Log2T) -> T
+
@shrExact(value: T, shift_amt: Log2T) T

Performs the right shift operation (>>). Caller guarantees that the shift will not shift any 1 bits out. @@ -4819,7 +4819,7 @@ pub const FloatMode = enum { {#see_also|@shlExact|@shlWithOverflow#} {#header_close#} {#header_open|@sizeOf#} -

@sizeOf(comptime T: type) -> (number literal)
+
@sizeOf(comptime T: type) (number literal)

This function returns the number of bytes it takes to store T in memory.

@@ -4828,7 +4828,7 @@ pub const FloatMode = enum {

{#header_close#} {#header_open|@sqrt#} -
@sqrt(comptime T: type, value: T) -> T
+
@sqrt(comptime T: type, value: T) T

Performs the square root of a floating point number. Uses a dedicated hardware instruction when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO. @@ -4838,7 +4838,7 @@ pub const FloatMode = enum {

{#header_close#} {#header_open|@subWithOverflow#} -
@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool
+
@subWithOverflow(comptime T: type, a: T, b: T, result: *T) bool

Performs result.* = a - b. If overflow or underflow occurs, stores the overflowed bits in result and returns true. @@ -4846,7 +4846,7 @@ pub const FloatMode = enum {

{#header_close#} {#header_open|@truncate#} -
@truncate(comptime T: type, integer) -> T
+
@truncate(comptime T: type, integer) T

This function truncates bits from an integer type, resulting in a smaller integer type. @@ -4870,7 +4870,7 @@ const b: u8 = @truncate(u8, a); {#header_close#} {#header_open|@typeId#} -

@typeId(comptime T: type) -> @import("builtin").TypeId
+
@typeId(comptime T: type) @import("builtin").TypeId

Returns which kind of type something is. Possible values:

@@ -4904,7 +4904,7 @@ pub const TypeId = enum { {#code_end#} {#header_close#} {#header_open|@typeInfo#} -
@typeInfo(comptime T: type) -> @import("builtin").TypeInfo
+
@typeInfo(comptime T: type) @import("builtin").TypeInfo

Returns information on the type. Returns a value of the following union:

@@ -5080,14 +5080,14 @@ pub const TypeInfo = union(TypeId) { {#code_end#} {#header_close#} {#header_open|@typeName#} -
@typeName(T: type) -> []u8
+
@typeName(T: type) []u8

This function returns the string representation of a type.

{#header_close#} {#header_open|@typeOf#} -
@typeOf(expression) -> type
+
@typeOf(expression) type

This function returns a compile-time constant, which is the type of the expression passed as an argument. The expression is evaluated. @@ -5937,7 +5937,7 @@ pub const __zig_test_fn_slice = {}; // overwritten later {#header_open|C String Literals#} {#code_begin|exe#} {#link_libc#} -extern fn puts(&const u8) void; +extern fn puts(*const u8) void; pub fn main() void { puts(c"this has a null terminator"); @@ -5996,8 +5996,8 @@ const c = @cImport({ {#code_begin|syntax#} const base64 = @import("std").base64; -export fn decode_base_64(dest_ptr: &u8, dest_len: usize, - source_ptr: &const u8, source_len: usize) usize +export fn decode_base_64(dest_ptr: *u8, dest_len: usize, + source_ptr: *const u8, source_len: usize) usize { const src = source_ptr[0..source_len]; const dest = dest_ptr[0..dest_len]; @@ -6028,7 +6028,7 @@ int main(int argc, char **argv) { {#code_begin|syntax#} const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const obj = b.addObject("base64", "base64.zig"); const exe = b.addCExecutable("test"); diff --git a/example/cat/main.zig b/example/cat/main.zig index de0d323bed..1b34cb22eb 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -41,7 +41,7 @@ fn usage(exe: []const u8) !void { return error.Invalid; } -fn cat_file(stdout: &os.File, file: &os.File) !void { +fn cat_file(stdout: *os.File, file: *os.File) !void { var buf: [1024 * 4]u8 = undefined; while (true) { diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig index 1df8f04ce4..f64beda40f 100644 --- a/example/hello_world/hello_libc.zig +++ b/example/hello_world/hello_libc.zig @@ -7,7 +7,7 @@ const c = @cImport({ const msg = c"Hello, world!\n"; -export fn main(argc: c_int, argv: &&u8) c_int { +export fn main(argc: c_int, argv: **u8) c_int { if (c.printf(msg) != c_int(c.strlen(msg))) return -1; return 0; diff --git a/example/mix_o_files/base64.zig b/example/mix_o_files/base64.zig index e682a97055..35b090825b 100644 --- a/example/mix_o_files/base64.zig +++ b/example/mix_o_files/base64.zig @@ -1,6 +1,6 @@ const base64 = @import("std").base64; -export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8, source_len: usize) usize { +export fn decode_base_64(dest_ptr: *u8, dest_len: usize, source_ptr: *const u8, source_len: usize) usize { const src = source_ptr[0..source_len]; const dest = dest_ptr[0..dest_len]; const base64_decoder = base64.standard_decoder_unsafe; diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig index e5d2e6a446..a4e7fbbf8f 100644 --- a/example/mix_o_files/build.zig +++ b/example/mix_o_files/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const obj = b.addObject("base64", "base64.zig"); const exe = b.addCExecutable("test"); diff --git a/example/shared_library/build.zig b/example/shared_library/build.zig index 30c714c6c6..05648cf9eb 100644 --- a/example/shared_library/build.zig +++ b/example/shared_library/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); const exe = b.addCExecutable("test"); diff --git a/src-self-hosted/arg.zig b/src-self-hosted/arg.zig index fa2166e3a5..df2c04ef1f 100644 --- a/src-self-hosted/arg.zig +++ b/src-self-hosted/arg.zig @@ -30,7 +30,7 @@ fn argInAllowedSet(maybe_set: ?[]const []const u8, arg: []const u8) bool { } // Modifies the current argument index during iteration -fn readFlagArguments(allocator: &Allocator, args: []const []const u8, required: usize, allowed_set: ?[]const []const u8, index: &usize) !FlagArg { +fn readFlagArguments(allocator: *Allocator, args: []const []const u8, required: usize, allowed_set: ?[]const []const u8, index: *usize) !FlagArg { switch (required) { 0 => return FlagArg{ .None = undefined }, // TODO: Required to force non-tag but value? 1 => { @@ -79,7 +79,7 @@ pub const Args = struct { flags: HashMapFlags, positionals: ArrayList([]const u8), - pub fn parse(allocator: &Allocator, comptime spec: []const Flag, args: []const []const u8) !Args { + pub fn parse(allocator: *Allocator, comptime spec: []const Flag, args: []const []const u8) !Args { var parsed = Args{ .flags = HashMapFlags.init(allocator), .positionals = ArrayList([]const u8).init(allocator), @@ -143,18 +143,18 @@ pub const Args = struct { return parsed; } - pub fn deinit(self: &Args) void { + pub fn deinit(self: *Args) void { self.flags.deinit(); self.positionals.deinit(); } // e.g. --help - pub fn present(self: &Args, name: []const u8) bool { + pub fn present(self: *Args, name: []const u8) bool { return self.flags.contains(name); } // e.g. --name value - pub fn single(self: &Args, name: []const u8) ?[]const u8 { + pub fn single(self: *Args, name: []const u8) ?[]const u8 { if (self.flags.get(name)) |entry| { switch (entry.value) { FlagArg.Single => |inner| { @@ -168,7 +168,7 @@ pub const Args = struct { } // e.g. --names value1 value2 value3 - pub fn many(self: &Args, name: []const u8) ?[]const []const u8 { + pub fn many(self: *Args, name: []const u8) ?[]const []const u8 { if (self.flags.get(name)) |entry| { switch (entry.value) { FlagArg.Many => |inner| { diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index 9905b8e3a6..32d2450aac 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -16,18 +16,18 @@ pub const Msg = struct { text: []u8, first_token: TokenIndex, last_token: TokenIndex, - tree: &ast.Tree, + tree: *ast.Tree, }; /// `path` must outlive the returned Msg /// `tree` must outlive the returned Msg /// Caller owns returned Msg and must free with `allocator` pub fn createFromParseError( - allocator: &mem.Allocator, - parse_error: &const ast.Error, - tree: &ast.Tree, + allocator: *mem.Allocator, + parse_error: *const ast.Error, + tree: *ast.Tree, path: []const u8, -) !&Msg { +) !*Msg { const loc_token = parse_error.loc(); var text_buf = try std.Buffer.initSize(allocator, 0); defer text_buf.deinit(); @@ -47,7 +47,7 @@ pub fn createFromParseError( return msg; } -pub fn printToStream(stream: var, msg: &const Msg, color_on: bool) !void { +pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void { const first_token = msg.tree.tokens.at(msg.first_token); const last_token = msg.tree.tokens.at(msg.last_token); const start_loc = msg.tree.tokenLocationPtr(0, first_token); @@ -76,7 +76,7 @@ pub fn printToStream(stream: var, msg: &const Msg, color_on: bool) !void { try stream.write("\n"); } -pub fn printToFile(file: &os.File, msg: &const Msg, color: Color) !void { +pub fn printToFile(file: *os.File, msg: *const Msg, color: Color) !void { const color_on = switch (color) { Color.Auto => file.isTty(), Color.On => true, diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig index adab00286b..56b56c0c78 100644 --- a/src-self-hosted/introspect.zig +++ b/src-self-hosted/introspect.zig @@ -7,7 +7,7 @@ const os = std.os; const warn = std.debug.warn; /// Caller must free result -pub fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 { +pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 { const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig"); errdefer allocator.free(test_zig_dir); @@ -21,7 +21,7 @@ pub fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![ } /// Caller must free result -pub fn findZigLibDir(allocator: &mem.Allocator) ![]u8 { +pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 { const self_exe_path = try os.selfExeDirPath(allocator); defer allocator.free(self_exe_path); @@ -42,7 +42,7 @@ pub fn findZigLibDir(allocator: &mem.Allocator) ![]u8 { return error.FileNotFound; } -pub fn resolveZigLibDir(allocator: &mem.Allocator) ![]u8 { +pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 { return findZigLibDir(allocator) catch |err| { warn( \\Unable to find zig lib directory: {}. diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index c4550b5179..3334d9511b 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -2,7 +2,7 @@ const Scope = @import("scope.zig").Scope; pub const Instruction = struct { id: Id, - scope: &Scope, + scope: *Scope, pub const Id = enum { Br, diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 71838503b7..80b1c3889a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -18,8 +18,8 @@ const Target = @import("target.zig").Target; const errmsg = @import("errmsg.zig"); var stderr_file: os.File = undefined; -var stderr: &io.OutStream(io.FileOutStream.Error) = undefined; -var stdout: &io.OutStream(io.FileOutStream.Error) = undefined; +var stderr: *io.OutStream(io.FileOutStream.Error) = undefined; +var stdout: *io.OutStream(io.FileOutStream.Error) = undefined; const usage = \\usage: zig [command] [options] @@ -43,7 +43,7 @@ const usage = const Command = struct { name: []const u8, - exec: fn (&Allocator, []const []const u8) error!void, + exec: fn (*Allocator, []const []const u8) error!void, }; pub fn main() !void { @@ -191,7 +191,7 @@ const missing_build_file = \\ ; -fn cmdBuild(allocator: &Allocator, args: []const []const u8) !void { +fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_build_spec, args); defer flags.deinit(); @@ -426,7 +426,7 @@ const args_build_generic = []Flag{ Flag.Arg1("--ver-patch"), }; -fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Module.Kind) !void { +fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Module.Kind) !void { var flags = try Args.parse(allocator, args_build_generic, args); defer flags.deinit(); @@ -661,19 +661,19 @@ fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Mo try stderr.print("building {}: {}\n", @tagName(out_type), in_file); } -fn cmdBuildExe(allocator: &Allocator, args: []const []const u8) !void { +fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void { try buildOutputType(allocator, args, Module.Kind.Exe); } // cmd:build-lib /////////////////////////////////////////////////////////////////////////////////// -fn cmdBuildLib(allocator: &Allocator, args: []const []const u8) !void { +fn cmdBuildLib(allocator: *Allocator, args: []const []const u8) !void { try buildOutputType(allocator, args, Module.Kind.Lib); } // cmd:build-obj /////////////////////////////////////////////////////////////////////////////////// -fn cmdBuildObj(allocator: &Allocator, args: []const []const u8) !void { +fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void { try buildOutputType(allocator, args, Module.Kind.Obj); } @@ -700,7 +700,7 @@ const args_fmt_spec = []Flag{ }), }; -fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { +fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_fmt_spec, args); defer flags.deinit(); @@ -768,7 +768,7 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { // cmd:targets ///////////////////////////////////////////////////////////////////////////////////// -fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void { +fn cmdTargets(allocator: *Allocator, args: []const []const u8) !void { try stdout.write("Architectures:\n"); { comptime var i: usize = 0; @@ -810,7 +810,7 @@ fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void { // cmd:version ///////////////////////////////////////////////////////////////////////////////////// -fn cmdVersion(allocator: &Allocator, args: []const []const u8) !void { +fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void { try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING)); } @@ -827,7 +827,7 @@ const usage_test = const args_test_spec = []Flag{Flag.Bool("--help")}; -fn cmdTest(allocator: &Allocator, args: []const []const u8) !void { +fn cmdTest(allocator: *Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_build_spec, args); defer flags.deinit(); @@ -862,7 +862,7 @@ const usage_run = const args_run_spec = []Flag{Flag.Bool("--help")}; -fn cmdRun(allocator: &Allocator, args: []const []const u8) !void { +fn cmdRun(allocator: *Allocator, args: []const []const u8) !void { var compile_args = args; var runtime_args: []const []const u8 = []const []const u8{}; @@ -912,7 +912,7 @@ const args_translate_c_spec = []Flag{ Flag.Arg1("--output"), }; -fn cmdTranslateC(allocator: &Allocator, args: []const []const u8) !void { +fn cmdTranslateC(allocator: *Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_translate_c_spec, args); defer flags.deinit(); @@ -958,7 +958,7 @@ fn cmdTranslateC(allocator: &Allocator, args: []const []const u8) !void { // cmd:help //////////////////////////////////////////////////////////////////////////////////////// -fn cmdHelp(allocator: &Allocator, args: []const []const u8) !void { +fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void { try stderr.write(usage); } @@ -981,7 +981,7 @@ const info_zen = \\ ; -fn cmdZen(allocator: &Allocator, args: []const []const u8) !void { +fn cmdZen(allocator: *Allocator, args: []const []const u8) !void { try stdout.write(info_zen); } @@ -996,7 +996,7 @@ const usage_internal = \\ ; -fn cmdInternal(allocator: &Allocator, args: []const []const u8) !void { +fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void { if (args.len == 0) { try stderr.write(usage_internal); os.exit(1); @@ -1018,7 +1018,7 @@ fn cmdInternal(allocator: &Allocator, args: []const []const u8) !void { try stderr.write(usage_internal); } -fn cmdInternalBuildInfo(allocator: &Allocator, args: []const []const u8) !void { +fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void { try stdout.print( \\ZIG_CMAKE_BINARY_DIR {} \\ZIG_CXX_COMPILER {} diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 61834eab66..a7ddf3f9e9 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -13,7 +13,7 @@ const ArrayList = std.ArrayList; const errmsg = @import("errmsg.zig"); pub const Module = struct { - allocator: &mem.Allocator, + allocator: *mem.Allocator, name: Buffer, root_src_path: ?[]const u8, module: llvm.ModuleRef, @@ -53,8 +53,8 @@ pub const Module = struct { windows_subsystem_windows: bool, windows_subsystem_console: bool, - link_libs_list: ArrayList(&LinkLib), - libc_link_lib: ?&LinkLib, + link_libs_list: ArrayList(*LinkLib), + libc_link_lib: ?*LinkLib, err_color: errmsg.Color, @@ -106,19 +106,19 @@ pub const Module = struct { pub const CliPkg = struct { name: []const u8, path: []const u8, - children: ArrayList(&CliPkg), - parent: ?&CliPkg, + children: ArrayList(*CliPkg), + parent: ?*CliPkg, - pub fn init(allocator: &mem.Allocator, name: []const u8, path: []const u8, parent: ?&CliPkg) !&CliPkg { + pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg { var pkg = try allocator.create(CliPkg); pkg.name = name; pkg.path = path; - pkg.children = ArrayList(&CliPkg).init(allocator); + pkg.children = ArrayList(*CliPkg).init(allocator); pkg.parent = parent; return pkg; } - pub fn deinit(self: &CliPkg) void { + pub fn deinit(self: *CliPkg) void { for (self.children.toSliceConst()) |child| { child.deinit(); } @@ -126,7 +126,7 @@ pub const Module = struct { } }; - pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target, kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module { + pub fn create(allocator: *mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: *const Target, kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !*Module { var name_buffer = try Buffer.init(allocator, name); errdefer name_buffer.deinit(); @@ -188,7 +188,7 @@ pub const Module = struct { .link_objects = [][]const u8{}, .windows_subsystem_windows = false, .windows_subsystem_console = false, - .link_libs_list = ArrayList(&LinkLib).init(allocator), + .link_libs_list = ArrayList(*LinkLib).init(allocator), .libc_link_lib = null, .err_color = errmsg.Color.Auto, .darwin_frameworks = [][]const u8{}, @@ -200,11 +200,11 @@ pub const Module = struct { return module_ptr; } - fn dump(self: &Module) void { + fn dump(self: *Module) void { c.LLVMDumpModule(self.module); } - pub fn destroy(self: &Module) void { + pub fn destroy(self: *Module) void { c.LLVMDisposeBuilder(self.builder); c.LLVMDisposeModule(self.module); c.LLVMContextDispose(self.context); @@ -213,7 +213,7 @@ pub const Module = struct { self.allocator.destroy(self); } - pub fn build(self: &Module) !void { + pub fn build(self: *Module) !void { if (self.llvm_argv.len != 0) { var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.allocator, [][]const []const u8{ [][]const u8{"zig (LLVM option parsing)"}, @@ -259,12 +259,12 @@ pub const Module = struct { self.dump(); } - pub fn link(self: &Module, out_file: ?[]const u8) !void { + pub fn link(self: *Module, out_file: ?[]const u8) !void { warn("TODO link"); return error.Todo; } - pub fn addLinkLib(self: &Module, name: []const u8, provided_explicitly: bool) !&LinkLib { + pub fn addLinkLib(self: *Module, name: []const u8, provided_explicitly: bool) !*LinkLib { const is_libc = mem.eql(u8, name, "c"); if (is_libc) { diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 05e586daae..b73dcb4ed3 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -1,6 +1,6 @@ pub const Scope = struct { id: Id, - parent: &Scope, + parent: *Scope, pub const Id = enum { Decls, diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index 7983a3ddec..724d99ea23 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -11,7 +11,7 @@ pub const Target = union(enum) { Native, Cross: CrossTarget, - pub fn oFileExt(self: &const Target) []const u8 { + pub fn oFileExt(self: *const Target) []const u8 { const environ = switch (self.*) { Target.Native => builtin.environ, Target.Cross => |t| t.environ, @@ -22,28 +22,28 @@ pub const Target = union(enum) { }; } - pub fn exeFileExt(self: &const Target) []const u8 { + pub fn exeFileExt(self: *const Target) []const u8 { return switch (self.getOs()) { builtin.Os.windows => ".exe", else => "", }; } - pub fn getOs(self: &const Target) builtin.Os { + pub fn getOs(self: *const Target) builtin.Os { return switch (self.*) { Target.Native => builtin.os, Target.Cross => |t| t.os, }; } - pub fn isDarwin(self: &const Target) bool { + pub fn isDarwin(self: *const Target) bool { return switch (self.getOs()) { builtin.Os.ios, builtin.Os.macosx => true, else => false, }; } - pub fn isWindows(self: &const Target) bool { + pub fn isWindows(self: *const Target) bool { return switch (self.getOs()) { builtin.Os.windows => true, else => false, diff --git a/src/all_types.hpp b/src/all_types.hpp index 9c156fb58b..b9199c2757 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -374,7 +374,7 @@ enum NodeType { NodeTypeCharLiteral, NodeTypeSymbol, NodeTypePrefixOpExpr, - NodeTypeAddrOfExpr, + NodeTypePointerType, NodeTypeFnCallExpr, NodeTypeArrayAccessExpr, NodeTypeSliceExpr, @@ -616,6 +616,7 @@ enum PrefixOp { PrefixOpNegationWrap, PrefixOpMaybe, PrefixOpUnwrapMaybe, + PrefixOpAddrOf, }; struct AstNodePrefixOpExpr { @@ -623,7 +624,7 @@ struct AstNodePrefixOpExpr { AstNode *primary_expr; }; -struct AstNodeAddrOfExpr { +struct AstNodePointerType { AstNode *align_expr; BigInt *bit_offset_start; BigInt *bit_offset_end; @@ -899,7 +900,7 @@ struct AstNode { AstNodeBinOpExpr bin_op_expr; AstNodeCatchExpr unwrap_err_expr; AstNodePrefixOpExpr prefix_op_expr; - AstNodeAddrOfExpr addr_of_expr; + AstNodePointerType pointer_type; AstNodeFnCallExpr fn_call_expr; AstNodeArrayAccessExpr array_access_expr; AstNodeSliceExpr slice_expr; @@ -2053,7 +2054,7 @@ enum IrInstructionId { IrInstructionIdTypeInfo, IrInstructionIdTypeId, IrInstructionIdSetEvalBranchQuota, - IrInstructionIdPtrTypeOf, + IrInstructionIdPtrType, IrInstructionIdAlignCast, IrInstructionIdOpaqueType, IrInstructionIdSetAlignStack, @@ -2412,6 +2413,17 @@ struct IrInstructionArrayType { IrInstruction *child_type; }; +struct IrInstructionPtrType { + IrInstruction base; + + IrInstruction *align_value; + IrInstruction *child_type; + uint32_t bit_offset_start; + uint32_t bit_offset_end; + bool is_const; + bool is_volatile; +}; + struct IrInstructionPromiseType { IrInstruction base; @@ -2891,17 +2903,6 @@ struct IrInstructionSetEvalBranchQuota { IrInstruction *new_quota; }; -struct IrInstructionPtrTypeOf { - IrInstruction base; - - IrInstruction *align_value; - IrInstruction *child_type; - uint32_t bit_offset_start; - uint32_t bit_offset_end; - bool is_const; - bool is_volatile; -}; - struct IrInstructionAlignCast { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index b00e18a9a1..a5011035c5 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -418,12 +418,12 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type const char *volatile_str = is_volatile ? "volatile " : ""; buf_resize(&entry->name, 0); if (unaligned_bit_count == 0 && byte_alignment == abi_alignment) { - buf_appendf(&entry->name, "&%s%s%s", const_str, volatile_str, buf_ptr(&child_type->name)); + buf_appendf(&entry->name, "*%s%s%s", const_str, volatile_str, buf_ptr(&child_type->name)); } else if (unaligned_bit_count == 0) { - buf_appendf(&entry->name, "&align(%" PRIu32 ") %s%s%s", byte_alignment, + buf_appendf(&entry->name, "*align(%" PRIu32 ") %s%s%s", byte_alignment, const_str, volatile_str, buf_ptr(&child_type->name)); } else { - buf_appendf(&entry->name, "&align(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", byte_alignment, + buf_appendf(&entry->name, "*align(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", byte_alignment, bit_offset, bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name)); } @@ -3270,7 +3270,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeThisLiteral: case NodeTypeSymbol: case NodeTypePrefixOpExpr: - case NodeTypeAddrOfExpr: + case NodeTypePointerType: case NodeTypeIfBoolExpr: case NodeTypeWhileExpr: case NodeTypeForExpr: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 5a1e81b36d..f356f406b0 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -68,6 +68,7 @@ static const char *prefix_op_str(PrefixOp prefix_op) { case PrefixOpBinNot: return "~"; case PrefixOpMaybe: return "?"; case PrefixOpUnwrapMaybe: return "??"; + case PrefixOpAddrOf: return "&"; } zig_unreachable(); } @@ -185,8 +186,6 @@ static const char *node_type_str(NodeType node_type) { return "Symbol"; case NodeTypePrefixOpExpr: return "PrefixOpExpr"; - case NodeTypeAddrOfExpr: - return "AddrOfExpr"; case NodeTypeUse: return "Use"; case NodeTypeBoolLiteral: @@ -251,6 +250,8 @@ static const char *node_type_str(NodeType node_type) { return "Suspend"; case NodeTypePromiseType: return "PromiseType"; + case NodeTypePointerType: + return "PointerType"; } zig_unreachable(); } @@ -616,41 +617,41 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, "%s", prefix_op_str(op)); AstNode *child_node = node->data.prefix_op_expr.primary_expr; - bool new_grouped = child_node->type == NodeTypePrefixOpExpr || child_node->type == NodeTypeAddrOfExpr; + bool new_grouped = child_node->type == NodeTypePrefixOpExpr || child_node->type == NodeTypePointerType; render_node_extra(ar, child_node, new_grouped); if (!grouped) fprintf(ar->f, ")"); break; } - case NodeTypeAddrOfExpr: + case NodeTypePointerType: { if (!grouped) fprintf(ar->f, "("); - fprintf(ar->f, "&"); - if (node->data.addr_of_expr.align_expr != nullptr) { + fprintf(ar->f, "*"); + if (node->data.pointer_type.align_expr != nullptr) { fprintf(ar->f, "align("); - render_node_grouped(ar, node->data.addr_of_expr.align_expr); - if (node->data.addr_of_expr.bit_offset_start != nullptr) { - assert(node->data.addr_of_expr.bit_offset_end != nullptr); + render_node_grouped(ar, node->data.pointer_type.align_expr); + if (node->data.pointer_type.bit_offset_start != nullptr) { + assert(node->data.pointer_type.bit_offset_end != nullptr); Buf offset_start_buf = BUF_INIT; buf_resize(&offset_start_buf, 0); - bigint_append_buf(&offset_start_buf, node->data.addr_of_expr.bit_offset_start, 10); + bigint_append_buf(&offset_start_buf, node->data.pointer_type.bit_offset_start, 10); Buf offset_end_buf = BUF_INIT; buf_resize(&offset_end_buf, 0); - bigint_append_buf(&offset_end_buf, node->data.addr_of_expr.bit_offset_end, 10); + bigint_append_buf(&offset_end_buf, node->data.pointer_type.bit_offset_end, 10); fprintf(ar->f, ":%s:%s ", buf_ptr(&offset_start_buf), buf_ptr(&offset_end_buf)); } fprintf(ar->f, ") "); } - if (node->data.addr_of_expr.is_const) { + if (node->data.pointer_type.is_const) { fprintf(ar->f, "const "); } - if (node->data.addr_of_expr.is_volatile) { + if (node->data.pointer_type.is_volatile) { fprintf(ar->f, "volatile "); } - render_node_ungrouped(ar, node->data.addr_of_expr.op_expr); + render_node_ungrouped(ar, node->data.pointer_type.op_expr); if (!grouped) fprintf(ar->f, ")"); break; } @@ -669,7 +670,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, " "); } AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; - bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypeAddrOfExpr); + bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypePointerType); render_node_extra(ar, fn_ref_node, grouped); fprintf(ar->f, "("); for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 69542b3e67..d07d427729 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4600,7 +4600,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdTypeInfo: case IrInstructionIdTypeId: case IrInstructionIdSetEvalBranchQuota: - case IrInstructionIdPtrTypeOf: + case IrInstructionIdPtrType: case IrInstructionIdOpaqueType: case IrInstructionIdSetAlignStack: case IrInstructionIdArgType: diff --git a/src/ir.cpp b/src/ir.cpp index 6e944a8976..b1fac9f485 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -41,10 +41,6 @@ struct IrAnalyze { static const LVal LVAL_NONE = { false, false, false }; static const LVal LVAL_PTR = { true, false, false }; -static LVal make_lval_addr(bool is_const, bool is_volatile) { - return { true, is_const, is_volatile }; -} - enum ConstCastResultId { ConstCastResultIdOk, ConstCastResultIdErrSet, @@ -629,8 +625,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetEvalBranchQuo return IrInstructionIdSetEvalBranchQuota; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeOf *) { - return IrInstructionIdPtrTypeOf; +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrType *) { + return IrInstructionIdPtrType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignCast *) { @@ -1196,11 +1192,11 @@ static IrInstruction *ir_build_br_from(IrBuilder *irb, IrInstruction *old_instru return new_instruction; } -static IrInstruction *ir_build_ptr_type_of(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value, uint32_t bit_offset_start, uint32_t bit_offset_end) { - IrInstructionPtrTypeOf *ptr_type_of_instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionPtrType *ptr_type_of_instruction = ir_build_instruction(irb, scope, source_node); ptr_type_of_instruction->align_value = align_value; ptr_type_of_instruction->child_type = child_type; ptr_type_of_instruction->is_const = is_const; @@ -4609,14 +4605,8 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode } static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) { - AstNode *expr_node; - if (node->type == NodeTypePrefixOpExpr) { - expr_node = node->data.prefix_op_expr.primary_expr; - } else if (node->type == NodeTypePtrDeref) { - expr_node = node->data.ptr_deref_expr.target; - } else { - zig_unreachable(); - } + assert(node->type == NodeTypePrefixOpExpr); + AstNode *expr_node = node->data.prefix_op_expr.primary_expr; IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); if (value == irb->codegen->invalid_instruction) @@ -4640,16 +4630,12 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction * return ir_build_ref(irb, scope, value->source_node, value, lval.is_const, lval.is_volatile); } -static IrInstruction *ir_gen_address_of(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeAddrOfExpr); - bool is_const = node->data.addr_of_expr.is_const; - bool is_volatile = node->data.addr_of_expr.is_volatile; - AstNode *expr_node = node->data.addr_of_expr.op_expr; - AstNode *align_expr = node->data.addr_of_expr.align_expr; - - if (align_expr == nullptr && !is_const && !is_volatile) { - return ir_gen_node_extra(irb, expr_node, scope, make_lval_addr(is_const, is_volatile)); - } +static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypePointerType); + bool is_const = node->data.pointer_type.is_const; + bool is_volatile = node->data.pointer_type.is_volatile; + AstNode *expr_node = node->data.pointer_type.op_expr; + AstNode *align_expr = node->data.pointer_type.align_expr; IrInstruction *align_value; if (align_expr != nullptr) { @@ -4665,27 +4651,27 @@ static IrInstruction *ir_gen_address_of(IrBuilder *irb, Scope *scope, AstNode *n return child_type; uint32_t bit_offset_start = 0; - if (node->data.addr_of_expr.bit_offset_start != nullptr) { - if (!bigint_fits_in_bits(node->data.addr_of_expr.bit_offset_start, 32, false)) { + if (node->data.pointer_type.bit_offset_start != nullptr) { + if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, node->data.addr_of_expr.bit_offset_start, 10); + bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10); exec_add_error_node(irb->codegen, irb->exec, node, buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); return irb->codegen->invalid_instruction; } - bit_offset_start = bigint_as_unsigned(node->data.addr_of_expr.bit_offset_start); + bit_offset_start = bigint_as_unsigned(node->data.pointer_type.bit_offset_start); } uint32_t bit_offset_end = 0; - if (node->data.addr_of_expr.bit_offset_end != nullptr) { - if (!bigint_fits_in_bits(node->data.addr_of_expr.bit_offset_end, 32, false)) { + if (node->data.pointer_type.bit_offset_end != nullptr) { + if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_end, 32, false)) { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, node->data.addr_of_expr.bit_offset_end, 10); + bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_end, 10); exec_add_error_node(irb->codegen, irb->exec, node, buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); return irb->codegen->invalid_instruction; } - bit_offset_end = bigint_as_unsigned(node->data.addr_of_expr.bit_offset_end); + bit_offset_end = bigint_as_unsigned(node->data.pointer_type.bit_offset_end); } if ((bit_offset_start != 0 || bit_offset_end != 0) && bit_offset_start >= bit_offset_end) { @@ -4694,7 +4680,7 @@ static IrInstruction *ir_gen_address_of(IrBuilder *irb, Scope *scope, AstNode *n return irb->codegen->invalid_instruction; } - return ir_build_ptr_type_of(irb, scope, node, child_type, is_const, is_volatile, + return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile, align_value, bit_offset_start, bit_offset_end); } @@ -4761,6 +4747,10 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe), lval); case PrefixOpUnwrapMaybe: return ir_gen_maybe_assert_ok(irb, scope, node, lval); + case PrefixOpAddrOf: { + AstNode *expr_node = node->data.prefix_op_expr.primary_expr; + return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR), lval); + } } zig_unreachable(); } @@ -6568,8 +6558,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval); case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, scope, node, lval); - case NodeTypeAddrOfExpr: - return ir_lval_wrap(irb, scope, ir_gen_address_of(irb, scope, node), lval); case NodeTypeContainerInitExpr: return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node), lval); case NodeTypeVariableDeclaration: @@ -6592,14 +6580,23 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_build_load_ptr(irb, scope, node, ptr_instruction); } - case NodeTypePtrDeref: - return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval); + case NodeTypePtrDeref: { + assert(node->type == NodeTypePtrDeref); + AstNode *expr_node = node->data.ptr_deref_expr.target; + IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); + if (value == irb->codegen->invalid_instruction) + return value; + + return ir_build_un_op(irb, scope, node, IrUnOpDereference, value); + } case NodeTypeThisLiteral: return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval); case NodeTypeBoolLiteral: return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval); case NodeTypeArrayType: return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval); + case NodeTypePointerType: + return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval); case NodeTypePromiseType: return ir_lval_wrap(irb, scope, ir_gen_promise_type(irb, scope, node), lval); case NodeTypeStringLiteral: @@ -8961,6 +8958,7 @@ static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instructio ConstExprValue *pointee, TypeTableEntry *pointee_type, ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) { + // TODO remove this special case for types if (pointee_type->id == TypeTableEntryIdMetaType) { TypeTableEntry *type_entry = pointee->data.x_type; if (type_entry->id == TypeTableEntryIdUnreachable) { @@ -18778,11 +18776,16 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr return usize; } -static TypeTableEntry *ir_analyze_instruction_ptr_type_of(IrAnalyze *ira, IrInstructionPtrTypeOf *instruction) { +static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtrType *instruction) { TypeTableEntry *child_type = ir_resolve_type(ira, instruction->child_type->other); if (type_is_invalid(child_type)) return ira->codegen->builtin_types.entry_invalid; + if (child_type->id == TypeTableEntryIdUnreachable) { + ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed")); + return ira->codegen->builtin_types.entry_invalid; + } + uint32_t align_bytes; if (instruction->align_value != nullptr) { if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes)) @@ -19606,8 +19609,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction); case IrInstructionIdSetEvalBranchQuota: return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstructionSetEvalBranchQuota *)instruction); - case IrInstructionIdPtrTypeOf: - return ir_analyze_instruction_ptr_type_of(ira, (IrInstructionPtrTypeOf *)instruction); + case IrInstructionIdPtrType: + return ir_analyze_instruction_ptr_type(ira, (IrInstructionPtrType *)instruction); case IrInstructionIdAlignCast: return ir_analyze_instruction_align_cast(ira, (IrInstructionAlignCast *)instruction); case IrInstructionIdOpaqueType: @@ -19783,7 +19786,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCheckStatementIsVoid: case IrInstructionIdPanic: case IrInstructionIdSetEvalBranchQuota: - case IrInstructionIdPtrTypeOf: + case IrInstructionIdPtrType: case IrInstructionIdSetAlignStack: case IrInstructionIdExport: case IrInstructionIdCancel: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 9678120f1d..3c177a8bbf 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -921,7 +921,7 @@ static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCas fprintf(irp->f, ")"); } -static void ir_print_ptr_type_of(IrPrint *irp, IrInstructionPtrTypeOf *instruction) { +static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) { fprintf(irp->f, "&"); if (instruction->align_value != nullptr) { fprintf(irp->f, "align("); @@ -1527,8 +1527,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdCanImplicitCast: ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction); break; - case IrInstructionIdPtrTypeOf: - ir_print_ptr_type_of(irp, (IrInstructionPtrTypeOf *)instruction); + case IrInstructionIdPtrType: + ir_print_ptr_type(irp, (IrInstructionPtrType *)instruction); break; case IrInstructionIdDeclRef: ir_print_decl_ref(irp, (IrInstructionDeclRef *)instruction); diff --git a/src/parser.cpp b/src/parser.cpp index 4763d3b987..ef390a3a2e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1167,20 +1167,19 @@ static PrefixOp tok_to_prefix_op(Token *token) { case TokenIdTilde: return PrefixOpBinNot; case TokenIdMaybe: return PrefixOpMaybe; case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe; + case TokenIdAmpersand: return PrefixOpAddrOf; default: return PrefixOpInvalid; } } -static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) { - Token *ampersand_tok = ast_eat_token(pc, token_index, TokenIdAmpersand); - - AstNode *node = ast_create_node(pc, NodeTypeAddrOfExpr, ampersand_tok); +static AstNode *ast_parse_pointer_type(ParseContext *pc, size_t *token_index, Token *star_tok) { + AstNode *node = ast_create_node(pc, NodeTypePointerType, star_tok); Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdKeywordAlign) { *token_index += 1; ast_eat_token(pc, token_index, TokenIdLParen); - node->data.addr_of_expr.align_expr = ast_parse_expression(pc, token_index, true); + node->data.pointer_type.align_expr = ast_parse_expression(pc, token_index, true); token = &pc->tokens->at(*token_index); if (token->id == TokenIdColon) { @@ -1189,24 +1188,24 @@ static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) { ast_eat_token(pc, token_index, TokenIdColon); Token *bit_offset_end_tok = ast_eat_token(pc, token_index, TokenIdIntLiteral); - node->data.addr_of_expr.bit_offset_start = token_bigint(bit_offset_start_tok); - node->data.addr_of_expr.bit_offset_end = token_bigint(bit_offset_end_tok); + node->data.pointer_type.bit_offset_start = token_bigint(bit_offset_start_tok); + node->data.pointer_type.bit_offset_end = token_bigint(bit_offset_end_tok); } ast_eat_token(pc, token_index, TokenIdRParen); token = &pc->tokens->at(*token_index); } if (token->id == TokenIdKeywordConst) { *token_index += 1; - node->data.addr_of_expr.is_const = true; + node->data.pointer_type.is_const = true; token = &pc->tokens->at(*token_index); } if (token->id == TokenIdKeywordVolatile) { *token_index += 1; - node->data.addr_of_expr.is_volatile = true; + node->data.pointer_type.is_volatile = true; } - node->data.addr_of_expr.op_expr = ast_parse_prefix_op_expr(pc, token_index, true); + node->data.pointer_type.op_expr = ast_parse_prefix_op_expr(pc, token_index, true); return node; } @@ -1216,8 +1215,17 @@ PrefixOp = "!" | "-" | "~" | ("*" option("align" "(" Expression option(":" Integ */ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdAmpersand) { - return ast_parse_addr_of(pc, token_index); + if (token->id == TokenIdStar) { + *token_index += 1; + return ast_parse_pointer_type(pc, token_index, token); + } + if (token->id == TokenIdStarStar) { + *token_index += 1; + AstNode *child_node = ast_parse_pointer_type(pc, token_index, token); + child_node->column += 1; + AstNode *parent_node = ast_create_node(pc, NodeTypePointerType, token); + parent_node->data.pointer_type.op_expr = child_node; + return parent_node; } if (token->id == TokenIdKeywordTry) { return ast_parse_try_expr(pc, token_index); @@ -1234,13 +1242,12 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token); - AstNode *parent_node = node; AstNode *prefix_op_expr = ast_parse_error_set_expr(pc, token_index, true); node->data.prefix_op_expr.primary_expr = prefix_op_expr; node->data.prefix_op_expr.prefix_op = prefix_op; - return parent_node; + return node; } @@ -3121,9 +3128,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeErrorType: // none break; - case NodeTypeAddrOfExpr: - visit_field(&node->data.addr_of_expr.align_expr, visit, context); - visit_field(&node->data.addr_of_expr.op_expr, visit, context); + case NodeTypePointerType: + visit_field(&node->data.pointer_type.align_expr, visit, context); + visit_field(&node->data.pointer_type.op_expr, visit, context); break; case NodeTypeErrorSetDecl: visit_node_list(&node->data.err_set_decl.decls, visit, context); diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 50ff073008..db541d34f3 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -276,11 +276,18 @@ static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNod node); } -static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_volatile, AstNode *child_node) { - AstNode *node = trans_create_node(c, NodeTypeAddrOfExpr); - node->data.addr_of_expr.is_const = is_const; - node->data.addr_of_expr.is_volatile = is_volatile; - node->data.addr_of_expr.op_expr = child_node; +static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node) { + AstNode *node = trans_create_node(c, NodeTypePointerType); + node->data.pointer_type.is_const = is_const; + node->data.pointer_type.is_volatile = is_volatile; + node->data.pointer_type.op_expr = child_node; + return node; +} + +static AstNode *trans_create_node_addr_of(Context *c, AstNode *child_node) { + AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr); + node->data.prefix_op_expr.prefix_op = PrefixOpAddrOf; + node->data.prefix_op_expr.primary_expr = child_node; return node; } @@ -848,7 +855,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return trans_create_node_prefix_op(c, PrefixOpMaybe, child_node); } - AstNode *pointer_node = trans_create_node_addr_of(c, child_qt.isConstQualified(), + AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), child_qt.isVolatileQualified(), child_node); return trans_create_node_prefix_op(c, PrefixOpMaybe, pointer_node); } @@ -1033,7 +1040,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou emit_warning(c, source_loc, "unresolved array element type"); return nullptr; } - AstNode *pointer_node = trans_create_node_addr_of(c, child_qt.isConstQualified(), + AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), child_qt.isVolatileQualified(), child_type_node); return pointer_node; } @@ -1402,7 +1409,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result // const _ref = &lhs; AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue); if (lhs == nullptr) return nullptr; - AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs); + AstNode *addr_of_lhs = trans_create_node_addr_of(c, lhs); // TODO: avoid name collisions with generated variable names Buf* tmp_var_name = buf_create_from_str("_ref"); AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs); @@ -1476,7 +1483,7 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, // const _ref = &lhs; AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue); if (lhs == nullptr) return nullptr; - AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs); + AstNode *addr_of_lhs = trans_create_node_addr_of(c, lhs); // TODO: avoid name collisions with generated variable names Buf* tmp_var_name = buf_create_from_str("_ref"); AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs); @@ -1813,7 +1820,7 @@ static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, Tr // const _ref = &expr; AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue); if (expr == nullptr) return nullptr; - AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr); + AstNode *addr_of_expr = trans_create_node_addr_of(c, expr); // TODO: avoid name collisions with generated variable names Buf* ref_var_name = buf_create_from_str("_ref"); AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr); @@ -1868,7 +1875,7 @@ static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, Tra // const _ref = &expr; AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue); if (expr == nullptr) return nullptr; - AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr); + AstNode *addr_of_expr = trans_create_node_addr_of(c, expr); // TODO: avoid name collisions with generated variable names Buf* ref_var_name = buf_create_from_str("_ref"); AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr); @@ -1917,7 +1924,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransLValue); if (value_node == nullptr) return value_node; - return trans_create_node_addr_of(c, false, false, value_node); + return trans_create_node_addr_of(c, value_node); } case UO_Deref: { @@ -4441,7 +4448,7 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t } else if (first_tok->id == CTokIdAsterisk) { *tok_i += 1; - node = trans_create_node_addr_of(c, false, false, node); + node = trans_create_node_ptr_type(c, false, false, node); } else { return node; } diff --git a/std/array_list.zig b/std/array_list.zig index b315194c33..07a1db6451 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -17,10 +17,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { /// you uninitialized memory. items: []align(A) T, len: usize, - allocator: &Allocator, + allocator: *Allocator, /// Deinitialize with `deinit` or use `toOwnedSlice`. - pub fn init(allocator: &Allocator) Self { + pub fn init(allocator: *Allocator) Self { return Self{ .items = []align(A) T{}, .len = 0, @@ -28,30 +28,30 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { }; } - pub fn deinit(l: &const Self) void { + pub fn deinit(l: *const Self) void { l.allocator.free(l.items); } - pub fn toSlice(l: &const Self) []align(A) T { + pub fn toSlice(l: *const Self) []align(A) T { return l.items[0..l.len]; } - pub fn toSliceConst(l: &const Self) []align(A) const T { + pub fn toSliceConst(l: *const Self) []align(A) const T { return l.items[0..l.len]; } - pub fn at(l: &const Self, n: usize) T { + pub fn at(l: *const Self, n: usize) T { return l.toSliceConst()[n]; } - pub fn count(self: &const Self) usize { + pub fn count(self: *const Self) usize { return self.len; } /// ArrayList takes ownership of the passed in slice. The slice must have been /// allocated with `allocator`. /// Deinitialize with `deinit` or use `toOwnedSlice`. - pub fn fromOwnedSlice(allocator: &Allocator, slice: []align(A) T) Self { + pub fn fromOwnedSlice(allocator: *Allocator, slice: []align(A) T) Self { return Self{ .items = slice, .len = slice.len, @@ -60,14 +60,14 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { } /// The caller owns the returned memory. ArrayList becomes empty. - pub fn toOwnedSlice(self: &Self) []align(A) T { + pub fn toOwnedSlice(self: *Self) []align(A) T { const allocator = self.allocator; const result = allocator.alignedShrink(T, A, self.items, self.len); self.* = init(allocator); return result; } - pub fn insert(l: &Self, n: usize, item: &const T) !void { + pub fn insert(l: *Self, n: usize, item: *const T) !void { try l.ensureCapacity(l.len + 1); l.len += 1; @@ -75,7 +75,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { l.items[n] = item.*; } - pub fn insertSlice(l: &Self, n: usize, items: []align(A) const T) !void { + pub fn insertSlice(l: *Self, n: usize, items: []align(A) const T) !void { try l.ensureCapacity(l.len + items.len); l.len += items.len; @@ -83,28 +83,28 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { mem.copy(T, l.items[n .. n + items.len], items); } - pub fn append(l: &Self, item: &const T) !void { + pub fn append(l: *Self, item: *const T) !void { const new_item_ptr = try l.addOne(); new_item_ptr.* = item.*; } - pub fn appendSlice(l: &Self, items: []align(A) const T) !void { + pub fn appendSlice(l: *Self, items: []align(A) const T) !void { try l.ensureCapacity(l.len + items.len); mem.copy(T, l.items[l.len..], items); l.len += items.len; } - pub fn resize(l: &Self, new_len: usize) !void { + pub fn resize(l: *Self, new_len: usize) !void { try l.ensureCapacity(new_len); l.len = new_len; } - pub fn shrink(l: &Self, new_len: usize) void { + pub fn shrink(l: *Self, new_len: usize) void { assert(new_len <= l.len); l.len = new_len; } - pub fn ensureCapacity(l: &Self, new_capacity: usize) !void { + pub fn ensureCapacity(l: *Self, new_capacity: usize) !void { var better_capacity = l.items.len; if (better_capacity >= new_capacity) return; while (true) { @@ -114,7 +114,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { l.items = try l.allocator.alignedRealloc(T, A, l.items, better_capacity); } - pub fn addOne(l: &Self) !&T { + pub fn addOne(l: *Self) !*T { const new_length = l.len + 1; try l.ensureCapacity(new_length); const result = &l.items[l.len]; @@ -122,34 +122,34 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return result; } - pub fn pop(self: &Self) T { + pub fn pop(self: *Self) T { self.len -= 1; return self.items[self.len]; } - pub fn popOrNull(self: &Self) ?T { + pub fn popOrNull(self: *Self) ?T { if (self.len == 0) return null; return self.pop(); } pub const Iterator = struct { - list: &const Self, + list: *const Self, // how many items have we returned count: usize, - pub fn next(it: &Iterator) ?T { + pub fn next(it: *Iterator) ?T { if (it.count >= it.list.len) return null; const val = it.list.at(it.count); it.count += 1; return val; } - pub fn reset(it: &Iterator) void { + pub fn reset(it: *Iterator) void { it.count = 0; } }; - pub fn iterator(self: &const Self) Iterator { + pub fn iterator(self: *const Self) Iterator { return Iterator{ .list = self, .count = 0, diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 35180da8d1..142c958173 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -5,36 +5,36 @@ const AtomicRmwOp = builtin.AtomicRmwOp; /// Many reader, many writer, non-allocating, thread-safe, lock-free pub fn Queue(comptime T: type) type { return struct { - head: &Node, - tail: &Node, + head: *Node, + tail: *Node, root: Node, pub const Self = this; pub const Node = struct { - next: ?&Node, + next: ?*Node, data: T, }; // TODO: well defined copy elision: https://github.com/ziglang/zig/issues/287 - pub fn init(self: &Self) void { + pub fn init(self: *Self) void { self.root.next = null; self.head = &self.root; self.tail = &self.root; } - pub fn put(self: &Self, node: &Node) void { + pub fn put(self: *Self, node: *Node) void { node.next = null; - const tail = @atomicRmw(&Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); - _ = @atomicRmw(?&Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); + const tail = @atomicRmw(*Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); + _ = @atomicRmw(?*Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); } - pub fn get(self: &Self) ?&Node { - var head = @atomicLoad(&Node, &self.head, AtomicOrder.SeqCst); + pub fn get(self: *Self) ?*Node { + var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst); while (true) { const node = head.next ?? return null; - head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return node; + head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return node; } } }; @@ -42,8 +42,8 @@ pub fn Queue(comptime T: type) type { const std = @import("std"); const Context = struct { - allocator: &std.mem.Allocator, - queue: &Queue(i32), + allocator: *std.mem.Allocator, + queue: *Queue(i32), put_sum: isize, get_sum: isize, get_count: usize, @@ -79,11 +79,11 @@ test "std.atomic.queue" { .get_count = 0, }; - var putters: [put_thread_count]&std.os.Thread = undefined; + var putters: [put_thread_count]*std.os.Thread = undefined; for (putters) |*t| { t.* = try std.os.spawnThread(&context, startPuts); } - var getters: [put_thread_count]&std.os.Thread = undefined; + var getters: [put_thread_count]*std.os.Thread = undefined; for (getters) |*t| { t.* = try std.os.spawnThread(&context, startGets); } @@ -98,7 +98,7 @@ test "std.atomic.queue" { std.debug.assert(context.get_count == puts_per_thread * put_thread_count); } -fn startPuts(ctx: &Context) u8 { +fn startPuts(ctx: *Context) u8 { var put_count: usize = puts_per_thread; var r = std.rand.DefaultPrng.init(0xdeadbeef); while (put_count != 0) : (put_count -= 1) { @@ -112,7 +112,7 @@ fn startPuts(ctx: &Context) u8 { return 0; } -fn startGets(ctx: &Context) u8 { +fn startGets(ctx: *Context) u8 { while (true) { while (ctx.queue.get()) |node| { std.os.time.sleep(0, 1); // let the os scheduler be our fuzz diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 400a1a3c4f..15611188d2 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -4,12 +4,12 @@ const AtomicOrder = builtin.AtomicOrder; /// Many reader, many writer, non-allocating, thread-safe, lock-free pub fn Stack(comptime T: type) type { return struct { - root: ?&Node, + root: ?*Node, pub const Self = this; pub const Node = struct { - next: ?&Node, + next: ?*Node, data: T, }; @@ -19,36 +19,36 @@ pub fn Stack(comptime T: type) type { /// push operation, but only if you are the first item in the stack. if you did not succeed in /// being the first item in the stack, returns the other item that was there. - pub fn pushFirst(self: &Self, node: &Node) ?&Node { + pub fn pushFirst(self: *Self, node: *Node) ?*Node { node.next = null; - return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst); + return @cmpxchgStrong(?*Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst); } - pub fn push(self: &Self, node: &Node) void { - var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst); + pub fn push(self: *Self, node: *Node) void { + var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst); while (true) { node.next = root; - root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break; + root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break; } } - pub fn pop(self: &Self) ?&Node { - var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst); + pub fn pop(self: *Self) ?*Node { + var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst); while (true) { - root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root; + root = @cmpxchgWeak(?*Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root; } } - pub fn isEmpty(self: &Self) bool { - return @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst) == null; + pub fn isEmpty(self: *Self) bool { + return @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst) == null; } }; } const std = @import("std"); const Context = struct { - allocator: &std.mem.Allocator, - stack: &Stack(i32), + allocator: *std.mem.Allocator, + stack: *Stack(i32), put_sum: isize, get_sum: isize, get_count: usize, @@ -82,11 +82,11 @@ test "std.atomic.stack" { .get_count = 0, }; - var putters: [put_thread_count]&std.os.Thread = undefined; + var putters: [put_thread_count]*std.os.Thread = undefined; for (putters) |*t| { t.* = try std.os.spawnThread(&context, startPuts); } - var getters: [put_thread_count]&std.os.Thread = undefined; + var getters: [put_thread_count]*std.os.Thread = undefined; for (getters) |*t| { t.* = try std.os.spawnThread(&context, startGets); } @@ -101,7 +101,7 @@ test "std.atomic.stack" { std.debug.assert(context.get_count == puts_per_thread * put_thread_count); } -fn startPuts(ctx: &Context) u8 { +fn startPuts(ctx: *Context) u8 { var put_count: usize = puts_per_thread; var r = std.rand.DefaultPrng.init(0xdeadbeef); while (put_count != 0) : (put_count -= 1) { @@ -115,7 +115,7 @@ fn startPuts(ctx: &Context) u8 { return 0; } -fn startGets(ctx: &Context) u8 { +fn startGets(ctx: *Context) u8 { while (true) { while (ctx.stack.pop()) |node| { std.os.time.sleep(0, 1); // let the os scheduler be our fuzz diff --git a/std/base64.zig b/std/base64.zig index 204628a405..d27bcbd201 100644 --- a/std/base64.zig +++ b/std/base64.zig @@ -32,7 +32,7 @@ pub const Base64Encoder = struct { } /// dest.len must be what you get from ::calcSize. - pub fn encode(encoder: &const Base64Encoder, dest: []u8, source: []const u8) void { + pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) void { assert(dest.len == Base64Encoder.calcSize(source.len)); var i: usize = 0; @@ -107,7 +107,7 @@ pub const Base64Decoder = struct { } /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding. - pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) !usize { + pub fn calcSize(decoder: *const Base64Decoder, source: []const u8) !usize { if (source.len % 4 != 0) return error.InvalidPadding; return calcDecodedSizeExactUnsafe(source, decoder.pad_char); } @@ -115,7 +115,7 @@ pub const Base64Decoder = struct { /// dest.len must be what you get from ::calcSize. /// invalid characters result in error.InvalidCharacter. /// invalid padding results in error.InvalidPadding. - pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) !void { + pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) !void { assert(dest.len == (decoder.calcSize(source) catch unreachable)); assert(source.len % 4 == 0); @@ -181,7 +181,7 @@ pub const Base64DecoderWithIgnore = struct { /// Invalid padding results in error.InvalidPadding. /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound. /// Returns the number of bytes writen to dest. - pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) !usize { + pub fn decode(decoder_with_ignore: *const Base64DecoderWithIgnore, dest: []u8, source: []const u8) !usize { const decoder = &decoder_with_ignore.decoder; var src_cursor: usize = 0; @@ -290,13 +290,13 @@ pub const Base64DecoderUnsafe = struct { } /// The source buffer must be valid. - pub fn calcSize(decoder: &const Base64DecoderUnsafe, source: []const u8) usize { + pub fn calcSize(decoder: *const Base64DecoderUnsafe, source: []const u8) usize { return calcDecodedSizeExactUnsafe(source, decoder.pad_char); } /// dest.len must be what you get from ::calcDecodedSizeExactUnsafe. /// invalid characters or padding will result in undefined values. - pub fn decode(decoder: &const Base64DecoderUnsafe, dest: []u8, source: []const u8) void { + pub fn decode(decoder: *const Base64DecoderUnsafe, dest: []u8, source: []const u8) void { assert(dest.len == decoder.calcSize(source)); var src_index: usize = 0; diff --git a/std/buf_map.zig b/std/buf_map.zig index 930fc36a78..22d821ae7b 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -11,12 +11,12 @@ pub const BufMap = struct { const BufMapHashMap = HashMap([]const u8, []const u8, mem.hash_slice_u8, mem.eql_slice_u8); - pub fn init(allocator: &Allocator) BufMap { + pub fn init(allocator: *Allocator) BufMap { var self = BufMap{ .hash_map = BufMapHashMap.init(allocator) }; return self; } - pub fn deinit(self: &const BufMap) void { + pub fn deinit(self: *const BufMap) void { var it = self.hash_map.iterator(); while (true) { const entry = it.next() ?? break; @@ -27,7 +27,7 @@ pub const BufMap = struct { self.hash_map.deinit(); } - pub fn set(self: &BufMap, key: []const u8, value: []const u8) !void { + pub fn set(self: *BufMap, key: []const u8, value: []const u8) !void { self.delete(key); const key_copy = try self.copy(key); errdefer self.free(key_copy); @@ -36,30 +36,30 @@ pub const BufMap = struct { _ = try self.hash_map.put(key_copy, value_copy); } - pub fn get(self: &const BufMap, key: []const u8) ?[]const u8 { + pub fn get(self: *const BufMap, key: []const u8) ?[]const u8 { const entry = self.hash_map.get(key) ?? return null; return entry.value; } - pub fn delete(self: &BufMap, key: []const u8) void { + pub fn delete(self: *BufMap, key: []const u8) void { const entry = self.hash_map.remove(key) ?? return; self.free(entry.key); self.free(entry.value); } - pub fn count(self: &const BufMap) usize { + pub fn count(self: *const BufMap) usize { return self.hash_map.count(); } - pub fn iterator(self: &const BufMap) BufMapHashMap.Iterator { + pub fn iterator(self: *const BufMap) BufMapHashMap.Iterator { return self.hash_map.iterator(); } - fn free(self: &const BufMap, value: []const u8) void { + fn free(self: *const BufMap, value: []const u8) void { self.hash_map.allocator.free(value); } - fn copy(self: &const BufMap, value: []const u8) ![]const u8 { + fn copy(self: *const BufMap, value: []const u8) ![]const u8 { return mem.dupe(self.hash_map.allocator, u8, value); } }; diff --git a/std/buf_set.zig b/std/buf_set.zig index c5a80e16fb..03a050ed8b 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -9,12 +9,12 @@ pub const BufSet = struct { const BufSetHashMap = HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); - pub fn init(a: &Allocator) BufSet { + pub fn init(a: *Allocator) BufSet { var self = BufSet{ .hash_map = BufSetHashMap.init(a) }; return self; } - pub fn deinit(self: &const BufSet) void { + pub fn deinit(self: *const BufSet) void { var it = self.hash_map.iterator(); while (true) { const entry = it.next() ?? break; @@ -24,7 +24,7 @@ pub const BufSet = struct { self.hash_map.deinit(); } - pub fn put(self: &BufSet, key: []const u8) !void { + pub fn put(self: *BufSet, key: []const u8) !void { if (self.hash_map.get(key) == null) { const key_copy = try self.copy(key); errdefer self.free(key_copy); @@ -32,28 +32,28 @@ pub const BufSet = struct { } } - pub fn delete(self: &BufSet, key: []const u8) void { + pub fn delete(self: *BufSet, key: []const u8) void { const entry = self.hash_map.remove(key) ?? return; self.free(entry.key); } - pub fn count(self: &const BufSet) usize { + pub fn count(self: *const BufSet) usize { return self.hash_map.count(); } - pub fn iterator(self: &const BufSet) BufSetHashMap.Iterator { + pub fn iterator(self: *const BufSet) BufSetHashMap.Iterator { return self.hash_map.iterator(); } - pub fn allocator(self: &const BufSet) &Allocator { + pub fn allocator(self: *const BufSet) *Allocator { return self.hash_map.allocator; } - fn free(self: &const BufSet, value: []const u8) void { + fn free(self: *const BufSet, value: []const u8) void { self.hash_map.allocator.free(value); } - fn copy(self: &const BufSet, value: []const u8) ![]const u8 { + fn copy(self: *const BufSet, value: []const u8) ![]const u8 { const result = try self.hash_map.allocator.alloc(u8, value.len); mem.copy(u8, result, value); return result; diff --git a/std/buffer.zig b/std/buffer.zig index 90d63719e3..305746e183 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -12,14 +12,14 @@ pub const Buffer = struct { list: ArrayList(u8), /// Must deinitialize with deinit. - pub fn init(allocator: &Allocator, m: []const u8) !Buffer { + pub fn init(allocator: *Allocator, m: []const u8) !Buffer { var self = try initSize(allocator, m.len); mem.copy(u8, self.list.items, m); return self; } /// Must deinitialize with deinit. - pub fn initSize(allocator: &Allocator, size: usize) !Buffer { + pub fn initSize(allocator: *Allocator, size: usize) !Buffer { var self = initNull(allocator); try self.resize(size); return self; @@ -30,19 +30,19 @@ pub const Buffer = struct { /// * ::replaceContents /// * ::replaceContentsBuffer /// * ::resize - pub fn initNull(allocator: &Allocator) Buffer { + pub fn initNull(allocator: *Allocator) Buffer { return Buffer{ .list = ArrayList(u8).init(allocator) }; } /// Must deinitialize with deinit. - pub fn initFromBuffer(buffer: &const Buffer) !Buffer { + pub fn initFromBuffer(buffer: *const Buffer) !Buffer { return Buffer.init(buffer.list.allocator, buffer.toSliceConst()); } /// Buffer takes ownership of the passed in slice. The slice must have been /// allocated with `allocator`. /// Must deinitialize with deinit. - pub fn fromOwnedSlice(allocator: &Allocator, slice: []u8) Buffer { + pub fn fromOwnedSlice(allocator: *Allocator, slice: []u8) Buffer { var self = Buffer{ .list = ArrayList(u8).fromOwnedSlice(allocator, slice) }; self.list.append(0); return self; @@ -50,79 +50,79 @@ pub const Buffer = struct { /// The caller owns the returned memory. The Buffer becomes null and /// is safe to `deinit`. - pub fn toOwnedSlice(self: &Buffer) []u8 { + pub fn toOwnedSlice(self: *Buffer) []u8 { const allocator = self.list.allocator; const result = allocator.shrink(u8, self.list.items, self.len()); self.* = initNull(allocator); return result; } - pub fn deinit(self: &Buffer) void { + pub fn deinit(self: *Buffer) void { self.list.deinit(); } - pub fn toSlice(self: &const Buffer) []u8 { + pub fn toSlice(self: *const Buffer) []u8 { return self.list.toSlice()[0..self.len()]; } - pub fn toSliceConst(self: &const Buffer) []const u8 { + pub fn toSliceConst(self: *const Buffer) []const u8 { return self.list.toSliceConst()[0..self.len()]; } - pub fn shrink(self: &Buffer, new_len: usize) void { + pub fn shrink(self: *Buffer, new_len: usize) void { assert(new_len <= self.len()); self.list.shrink(new_len + 1); self.list.items[self.len()] = 0; } - pub fn resize(self: &Buffer, new_len: usize) !void { + pub fn resize(self: *Buffer, new_len: usize) !void { try self.list.resize(new_len + 1); self.list.items[self.len()] = 0; } - pub fn isNull(self: &const Buffer) bool { + pub fn isNull(self: *const Buffer) bool { return self.list.len == 0; } - pub fn len(self: &const Buffer) usize { + pub fn len(self: *const Buffer) usize { return self.list.len - 1; } - pub fn append(self: &Buffer, m: []const u8) !void { + pub fn append(self: *Buffer, m: []const u8) !void { const old_len = self.len(); try self.resize(old_len + m.len); mem.copy(u8, self.list.toSlice()[old_len..], m); } - pub fn appendByte(self: &Buffer, byte: u8) !void { + pub fn appendByte(self: *Buffer, byte: u8) !void { const old_len = self.len(); try self.resize(old_len + 1); self.list.toSlice()[old_len] = byte; } - pub fn eql(self: &const Buffer, m: []const u8) bool { + pub fn eql(self: *const Buffer, m: []const u8) bool { return mem.eql(u8, self.toSliceConst(), m); } - pub fn startsWith(self: &const Buffer, m: []const u8) bool { + pub fn startsWith(self: *const Buffer, m: []const u8) bool { if (self.len() < m.len) return false; return mem.eql(u8, self.list.items[0..m.len], m); } - pub fn endsWith(self: &const Buffer, m: []const u8) bool { + pub fn endsWith(self: *const Buffer, m: []const u8) bool { const l = self.len(); if (l < m.len) return false; const start = l - m.len; return mem.eql(u8, self.list.items[start..l], m); } - pub fn replaceContents(self: &const Buffer, m: []const u8) !void { + pub fn replaceContents(self: *const Buffer, m: []const u8) !void { try self.resize(m.len); mem.copy(u8, self.list.toSlice(), m); } /// For passing to C functions. - pub fn ptr(self: &const Buffer) &u8 { + pub fn ptr(self: *const Buffer) *u8 { return self.list.items.ptr; } }; diff --git a/std/build.zig b/std/build.zig index 9a6e17f728..fed02e0815 100644 --- a/std/build.zig +++ b/std/build.zig @@ -20,7 +20,7 @@ pub const Builder = struct { install_tls: TopLevelStep, have_uninstall_step: bool, have_install_step: bool, - allocator: &Allocator, + allocator: *Allocator, lib_paths: ArrayList([]const u8), include_paths: ArrayList([]const u8), rpaths: ArrayList([]const u8), @@ -36,9 +36,9 @@ pub const Builder = struct { verbose_cimport: bool, invalid_user_input: bool, zig_exe: []const u8, - default_step: &Step, + default_step: *Step, env_map: BufMap, - top_level_steps: ArrayList(&TopLevelStep), + top_level_steps: ArrayList(*TopLevelStep), prefix: []const u8, search_prefixes: ArrayList([]const u8), lib_dir: []const u8, @@ -82,7 +82,7 @@ pub const Builder = struct { description: []const u8, }; - pub fn init(allocator: &Allocator, zig_exe: []const u8, build_root: []const u8, cache_root: []const u8) Builder { + pub fn init(allocator: *Allocator, zig_exe: []const u8, build_root: []const u8, cache_root: []const u8) Builder { var self = Builder{ .zig_exe = zig_exe, .build_root = build_root, @@ -102,7 +102,7 @@ pub const Builder = struct { .user_input_options = UserInputOptionsMap.init(allocator), .available_options_map = AvailableOptionsMap.init(allocator), .available_options_list = ArrayList(AvailableOption).init(allocator), - .top_level_steps = ArrayList(&TopLevelStep).init(allocator), + .top_level_steps = ArrayList(*TopLevelStep).init(allocator), .default_step = undefined, .env_map = os.getEnvMap(allocator) catch unreachable, .prefix = undefined, @@ -127,7 +127,7 @@ pub const Builder = struct { return self; } - pub fn deinit(self: &Builder) void { + pub fn deinit(self: *Builder) void { self.lib_paths.deinit(); self.include_paths.deinit(); self.rpaths.deinit(); @@ -135,81 +135,81 @@ pub const Builder = struct { self.top_level_steps.deinit(); } - pub fn setInstallPrefix(self: &Builder, maybe_prefix: ?[]const u8) void { + pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void { self.prefix = maybe_prefix ?? "/usr/local"; // TODO better default self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable; self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable; } - pub fn addExecutable(self: &Builder, name: []const u8, root_src: ?[]const u8) &LibExeObjStep { + pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { return LibExeObjStep.createExecutable(self, name, root_src); } - pub fn addObject(self: &Builder, name: []const u8, root_src: []const u8) &LibExeObjStep { + pub fn addObject(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { return LibExeObjStep.createObject(self, name, root_src); } - pub fn addSharedLibrary(self: &Builder, name: []const u8, root_src: ?[]const u8, ver: &const Version) &LibExeObjStep { + pub fn addSharedLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8, ver: *const Version) *LibExeObjStep { return LibExeObjStep.createSharedLibrary(self, name, root_src, ver); } - pub fn addStaticLibrary(self: &Builder, name: []const u8, root_src: ?[]const u8) &LibExeObjStep { + pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { return LibExeObjStep.createStaticLibrary(self, name, root_src); } - pub fn addTest(self: &Builder, root_src: []const u8) &TestStep { + pub fn addTest(self: *Builder, root_src: []const u8) *TestStep { const test_step = self.allocator.create(TestStep) catch unreachable; test_step.* = TestStep.init(self, root_src); return test_step; } - pub fn addAssemble(self: &Builder, name: []const u8, src: []const u8) &LibExeObjStep { + pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { const obj_step = LibExeObjStep.createObject(self, name, null); obj_step.addAssemblyFile(src); return obj_step; } - pub fn addCStaticLibrary(self: &Builder, name: []const u8) &LibExeObjStep { + pub fn addCStaticLibrary(self: *Builder, name: []const u8) *LibExeObjStep { return LibExeObjStep.createCStaticLibrary(self, name); } - pub fn addCSharedLibrary(self: &Builder, name: []const u8, ver: &const Version) &LibExeObjStep { + pub fn addCSharedLibrary(self: *Builder, name: []const u8, ver: *const Version) *LibExeObjStep { return LibExeObjStep.createCSharedLibrary(self, name, ver); } - pub fn addCExecutable(self: &Builder, name: []const u8) &LibExeObjStep { + pub fn addCExecutable(self: *Builder, name: []const u8) *LibExeObjStep { return LibExeObjStep.createCExecutable(self, name); } - pub fn addCObject(self: &Builder, name: []const u8, src: []const u8) &LibExeObjStep { + pub fn addCObject(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { return LibExeObjStep.createCObject(self, name, src); } /// ::argv is copied. - pub fn addCommand(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap, argv: []const []const u8) &CommandStep { + pub fn addCommand(self: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) *CommandStep { return CommandStep.create(self, cwd, env_map, argv); } - pub fn addWriteFile(self: &Builder, file_path: []const u8, data: []const u8) &WriteFileStep { + pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep { const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; write_file_step.* = WriteFileStep.init(self, file_path, data); return write_file_step; } - pub fn addLog(self: &Builder, comptime format: []const u8, args: ...) &LogStep { + pub fn addLog(self: *Builder, comptime format: []const u8, args: ...) *LogStep { const data = self.fmt(format, args); const log_step = self.allocator.create(LogStep) catch unreachable; log_step.* = LogStep.init(self, data); return log_step; } - pub fn addRemoveDirTree(self: &Builder, dir_path: []const u8) &RemoveDirStep { + pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep { const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable; remove_dir_step.* = RemoveDirStep.init(self, dir_path); return remove_dir_step; } - pub fn version(self: &const Builder, major: u32, minor: u32, patch: u32) Version { + pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) Version { return Version{ .major = major, .minor = minor, @@ -217,20 +217,20 @@ pub const Builder = struct { }; } - pub fn addCIncludePath(self: &Builder, path: []const u8) void { + pub fn addCIncludePath(self: *Builder, path: []const u8) void { self.include_paths.append(path) catch unreachable; } - pub fn addRPath(self: &Builder, path: []const u8) void { + pub fn addRPath(self: *Builder, path: []const u8) void { self.rpaths.append(path) catch unreachable; } - pub fn addLibPath(self: &Builder, path: []const u8) void { + pub fn addLibPath(self: *Builder, path: []const u8) void { self.lib_paths.append(path) catch unreachable; } - pub fn make(self: &Builder, step_names: []const []const u8) !void { - var wanted_steps = ArrayList(&Step).init(self.allocator); + pub fn make(self: *Builder, step_names: []const []const u8) !void { + var wanted_steps = ArrayList(*Step).init(self.allocator); defer wanted_steps.deinit(); if (step_names.len == 0) { @@ -247,7 +247,7 @@ pub const Builder = struct { } } - pub fn getInstallStep(self: &Builder) &Step { + pub fn getInstallStep(self: *Builder) *Step { if (self.have_install_step) return &self.install_tls.step; self.top_level_steps.append(&self.install_tls) catch unreachable; @@ -255,7 +255,7 @@ pub const Builder = struct { return &self.install_tls.step; } - pub fn getUninstallStep(self: &Builder) &Step { + pub fn getUninstallStep(self: *Builder) *Step { if (self.have_uninstall_step) return &self.uninstall_tls.step; self.top_level_steps.append(&self.uninstall_tls) catch unreachable; @@ -263,7 +263,7 @@ pub const Builder = struct { return &self.uninstall_tls.step; } - fn makeUninstall(uninstall_step: &Step) error!void { + fn makeUninstall(uninstall_step: *Step) error!void { const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls); @@ -277,7 +277,7 @@ pub const Builder = struct { // TODO remove empty directories } - fn makeOneStep(self: &Builder, s: &Step) error!void { + fn makeOneStep(self: *Builder, s: *Step) error!void { if (s.loop_flag) { warn("Dependency loop detected:\n {}\n", s.name); return error.DependencyLoopDetected; @@ -298,7 +298,7 @@ pub const Builder = struct { try s.make(); } - fn getTopLevelStepByName(self: &Builder, name: []const u8) !&Step { + fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step { for (self.top_level_steps.toSliceConst()) |top_level_step| { if (mem.eql(u8, top_level_step.step.name, name)) { return &top_level_step.step; @@ -308,7 +308,7 @@ pub const Builder = struct { return error.InvalidStepName; } - fn processNixOSEnvVars(self: &Builder) void { + fn processNixOSEnvVars(self: *Builder) void { if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { var it = mem.split(nix_cflags_compile, " "); while (true) { @@ -350,7 +350,7 @@ pub const Builder = struct { } } - pub fn option(self: &Builder, comptime T: type, name: []const u8, description: []const u8) ?T { + pub fn option(self: *Builder, comptime T: type, name: []const u8, description: []const u8) ?T { const type_id = comptime typeToEnum(T); const available_option = AvailableOption{ .name = name, @@ -403,7 +403,7 @@ pub const Builder = struct { } } - pub fn step(self: &Builder, name: []const u8, description: []const u8) &Step { + pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step { const step_info = self.allocator.create(TopLevelStep) catch unreachable; step_info.* = TopLevelStep{ .step = Step.initNoOp(name, self.allocator), @@ -413,7 +413,7 @@ pub const Builder = struct { return &step_info.step; } - pub fn standardReleaseOptions(self: &Builder) builtin.Mode { + pub fn standardReleaseOptions(self: *Builder) builtin.Mode { if (self.release_mode) |mode| return mode; const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false; @@ -429,7 +429,7 @@ pub const Builder = struct { return mode; } - pub fn addUserInputOption(self: &Builder, name: []const u8, value: []const u8) bool { + pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) bool { if (self.user_input_options.put(name, UserInputOption{ .name = name, .value = UserValue{ .Scalar = value }, @@ -466,7 +466,7 @@ pub const Builder = struct { return false; } - pub fn addUserInputFlag(self: &Builder, name: []const u8) bool { + pub fn addUserInputFlag(self: *Builder, name: []const u8) bool { if (self.user_input_options.put(name, UserInputOption{ .name = name, .value = UserValue{ .Flag = {} }, @@ -500,7 +500,7 @@ pub const Builder = struct { }; } - fn markInvalidUserInput(self: &Builder) void { + fn markInvalidUserInput(self: *Builder) void { self.invalid_user_input = true; } @@ -514,7 +514,7 @@ pub const Builder = struct { }; } - pub fn validateUserInputDidItFail(self: &Builder) bool { + pub fn validateUserInputDidItFail(self: *Builder) bool { // make sure all args are used var it = self.user_input_options.iterator(); while (true) { @@ -528,7 +528,7 @@ pub const Builder = struct { return self.invalid_user_input; } - fn spawnChild(self: &Builder, argv: []const []const u8) !void { + fn spawnChild(self: *Builder, argv: []const []const u8) !void { return self.spawnChildEnvMap(null, &self.env_map, argv); } @@ -540,7 +540,7 @@ pub const Builder = struct { warn("\n"); } - fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap, argv: []const []const u8) !void { + fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) !void { if (self.verbose) { printCmd(cwd, argv); } @@ -573,28 +573,28 @@ pub const Builder = struct { } } - pub fn makePath(self: &Builder, path: []const u8) !void { + pub fn makePath(self: *Builder, path: []const u8) !void { os.makePath(self.allocator, self.pathFromRoot(path)) catch |err| { warn("Unable to create path {}: {}\n", path, @errorName(err)); return err; }; } - pub fn installArtifact(self: &Builder, artifact: &LibExeObjStep) void { + pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void { self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); } - pub fn addInstallArtifact(self: &Builder, artifact: &LibExeObjStep) &InstallArtifactStep { + pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep { return InstallArtifactStep.create(self, artifact); } ///::dest_rel_path is relative to prefix path or it can be an absolute path - pub fn installFile(self: &Builder, src_path: []const u8, dest_rel_path: []const u8) void { + pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { self.getInstallStep().dependOn(&self.addInstallFile(src_path, dest_rel_path).step); } ///::dest_rel_path is relative to prefix path or it can be an absolute path - pub fn addInstallFile(self: &Builder, src_path: []const u8, dest_rel_path: []const u8) &InstallFileStep { + pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { const full_dest_path = os.path.resolve(self.allocator, self.prefix, dest_rel_path) catch unreachable; self.pushInstalledFile(full_dest_path); @@ -603,16 +603,16 @@ pub const Builder = struct { return install_step; } - pub fn pushInstalledFile(self: &Builder, full_path: []const u8) void { + pub fn pushInstalledFile(self: *Builder, full_path: []const u8) void { _ = self.getUninstallStep(); self.installed_files.append(full_path) catch unreachable; } - fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) !void { + fn copyFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { return self.copyFileMode(source_path, dest_path, os.default_file_mode); } - fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: os.FileMode) !void { + fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: os.FileMode) !void { if (self.verbose) { warn("cp {} {}\n", source_path, dest_path); } @@ -629,15 +629,15 @@ pub const Builder = struct { }; } - fn pathFromRoot(self: &Builder, rel_path: []const u8) []u8 { + fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { return os.path.resolve(self.allocator, self.build_root, rel_path) catch unreachable; } - pub fn fmt(self: &Builder, comptime format: []const u8, args: ...) []u8 { + pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 { return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable; } - fn getCCExe(self: &Builder) []const u8 { + fn getCCExe(self: *Builder) []const u8 { if (builtin.environ == builtin.Environ.msvc) { return "cl.exe"; } else { @@ -645,7 +645,7 @@ pub const Builder = struct { } } - pub fn findProgram(self: &Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { + pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { // TODO report error for ambiguous situations const exe_extension = (Target{ .Native = {} }).exeFileExt(); for (self.search_prefixes.toSliceConst()) |search_prefix| { @@ -693,7 +693,7 @@ pub const Builder = struct { return error.FileNotFound; } - pub fn exec(self: &Builder, argv: []const []const u8) ![]u8 { + pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 { const max_output_size = 100 * 1024; const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size); switch (result.term) { @@ -715,7 +715,7 @@ pub const Builder = struct { } } - pub fn addSearchPrefix(self: &Builder, search_prefix: []const u8) void { + pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void { self.search_prefixes.append(search_prefix) catch unreachable; } }; @@ -736,7 +736,7 @@ pub const Target = union(enum) { Native: void, Cross: CrossTarget, - pub fn oFileExt(self: &const Target) []const u8 { + pub fn oFileExt(self: *const Target) []const u8 { const environ = switch (self.*) { Target.Native => builtin.environ, Target.Cross => |t| t.environ, @@ -747,49 +747,49 @@ pub const Target = union(enum) { }; } - pub fn exeFileExt(self: &const Target) []const u8 { + pub fn exeFileExt(self: *const Target) []const u8 { return switch (self.getOs()) { builtin.Os.windows => ".exe", else => "", }; } - pub fn libFileExt(self: &const Target) []const u8 { + pub fn libFileExt(self: *const Target) []const u8 { return switch (self.getOs()) { builtin.Os.windows => ".lib", else => ".a", }; } - pub fn getOs(self: &const Target) builtin.Os { + pub fn getOs(self: *const Target) builtin.Os { return switch (self.*) { Target.Native => builtin.os, Target.Cross => |t| t.os, }; } - pub fn isDarwin(self: &const Target) bool { + pub fn isDarwin(self: *const Target) bool { return switch (self.getOs()) { builtin.Os.ios, builtin.Os.macosx => true, else => false, }; } - pub fn isWindows(self: &const Target) bool { + pub fn isWindows(self: *const Target) bool { return switch (self.getOs()) { builtin.Os.windows => true, else => false, }; } - pub fn wantSharedLibSymLinks(self: &const Target) bool { + pub fn wantSharedLibSymLinks(self: *const Target) bool { return !self.isWindows(); } }; pub const LibExeObjStep = struct { step: Step, - builder: &Builder, + builder: *Builder, name: []const u8, target: Target, link_libs: BufSet, @@ -836,56 +836,56 @@ pub const LibExeObjStep = struct { Obj, }; - pub fn createSharedLibrary(builder: &Builder, name: []const u8, root_src: ?[]const u8, ver: &const Version) &LibExeObjStep { + pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8, ver: *const Version) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, ver); return self; } - pub fn createCSharedLibrary(builder: &Builder, name: []const u8, version: &const Version) &LibExeObjStep { + pub fn createCSharedLibrary(builder: *Builder, name: []const u8, version: *const Version) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initC(builder, name, Kind.Lib, version, false); return self; } - pub fn createStaticLibrary(builder: &Builder, name: []const u8, root_src: ?[]const u8) &LibExeObjStep { + pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0)); return self; } - pub fn createCStaticLibrary(builder: &Builder, name: []const u8) &LibExeObjStep { + pub fn createCStaticLibrary(builder: *Builder, name: []const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true); return self; } - pub fn createObject(builder: &Builder, name: []const u8, root_src: []const u8) &LibExeObjStep { + pub fn createObject(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); return self; } - pub fn createCObject(builder: &Builder, name: []const u8, src: []const u8) &LibExeObjStep { + pub fn createCObject(builder: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false); self.object_src = src; return self; } - pub fn createExecutable(builder: &Builder, name: []const u8, root_src: ?[]const u8) &LibExeObjStep { + pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Exe, false, builder.version(0, 0, 0)); return self; } - pub fn createCExecutable(builder: &Builder, name: []const u8) &LibExeObjStep { + pub fn createCExecutable(builder: *Builder, name: []const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false); return self; } - fn initExtraArgs(builder: &Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: &const Version) LibExeObjStep { + fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: *const Version) LibExeObjStep { var self = LibExeObjStep{ .strip = false, .builder = builder, @@ -924,7 +924,7 @@ pub const LibExeObjStep = struct { return self; } - fn initC(builder: &Builder, name: []const u8, kind: Kind, version: &const Version, static: bool) LibExeObjStep { + fn initC(builder: *Builder, name: []const u8, kind: Kind, version: *const Version, static: bool) LibExeObjStep { var self = LibExeObjStep{ .builder = builder, .name = name, @@ -964,7 +964,7 @@ pub const LibExeObjStep = struct { return self; } - fn computeOutFileNames(self: &LibExeObjStep) void { + fn computeOutFileNames(self: *LibExeObjStep) void { switch (self.kind) { Kind.Obj => { self.out_filename = self.builder.fmt("{}{}", self.name, self.target.oFileExt()); @@ -996,7 +996,7 @@ pub const LibExeObjStep = struct { } } - pub fn setTarget(self: &LibExeObjStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { + pub fn setTarget(self: *LibExeObjStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { self.target = Target{ .Cross = CrossTarget{ .arch = target_arch, @@ -1008,16 +1008,16 @@ pub const LibExeObjStep = struct { } // TODO respect this in the C args - pub fn setLinkerScriptPath(self: &LibExeObjStep, path: []const u8) void { + pub fn setLinkerScriptPath(self: *LibExeObjStep, path: []const u8) void { self.linker_script = path; } - pub fn linkFramework(self: &LibExeObjStep, framework_name: []const u8) void { + pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void { assert(self.target.isDarwin()); self.frameworks.put(framework_name) catch unreachable; } - pub fn linkLibrary(self: &LibExeObjStep, lib: &LibExeObjStep) void { + pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void { assert(self.kind != Kind.Obj); assert(lib.kind == Kind.Lib); @@ -1038,26 +1038,26 @@ pub const LibExeObjStep = struct { } } - pub fn linkSystemLibrary(self: &LibExeObjStep, name: []const u8) void { + pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { assert(self.kind != Kind.Obj); self.link_libs.put(name) catch unreachable; } - pub fn addSourceFile(self: &LibExeObjStep, file: []const u8) void { + pub fn addSourceFile(self: *LibExeObjStep, file: []const u8) void { assert(self.kind != Kind.Obj); assert(!self.is_zig); self.source_files.append(file) catch unreachable; } - pub fn setVerboseLink(self: &LibExeObjStep, value: bool) void { + pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void { self.verbose_link = value; } - pub fn setBuildMode(self: &LibExeObjStep, mode: builtin.Mode) void { + pub fn setBuildMode(self: *LibExeObjStep, mode: builtin.Mode) void { self.build_mode = mode; } - pub fn setOutputPath(self: &LibExeObjStep, file_path: []const u8) void { + pub fn setOutputPath(self: *LibExeObjStep, file_path: []const u8) void { self.output_path = file_path; // catch a common mistake @@ -1066,11 +1066,11 @@ pub const LibExeObjStep = struct { } } - pub fn getOutputPath(self: &LibExeObjStep) []const u8 { + pub fn getOutputPath(self: *LibExeObjStep) []const u8 { return if (self.output_path) |output_path| output_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename) catch unreachable; } - pub fn setOutputHPath(self: &LibExeObjStep, file_path: []const u8) void { + pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void { self.output_h_path = file_path; // catch a common mistake @@ -1079,21 +1079,21 @@ pub const LibExeObjStep = struct { } } - pub fn getOutputHPath(self: &LibExeObjStep) []const u8 { + pub fn getOutputHPath(self: *LibExeObjStep) []const u8 { return if (self.output_h_path) |output_h_path| output_h_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_h_filename) catch unreachable; } - pub fn addAssemblyFile(self: &LibExeObjStep, path: []const u8) void { + pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { self.assembly_files.append(path) catch unreachable; } - pub fn addObjectFile(self: &LibExeObjStep, path: []const u8) void { + pub fn addObjectFile(self: *LibExeObjStep, path: []const u8) void { assert(self.kind != Kind.Obj); self.object_files.append(path) catch unreachable; } - pub fn addObject(self: &LibExeObjStep, obj: &LibExeObjStep) void { + pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void { assert(obj.kind == Kind.Obj); assert(self.kind != Kind.Obj); @@ -1110,15 +1110,15 @@ pub const LibExeObjStep = struct { self.include_dirs.append(self.builder.cache_root) catch unreachable; } - pub fn addIncludeDir(self: &LibExeObjStep, path: []const u8) void { + pub fn addIncludeDir(self: *LibExeObjStep, path: []const u8) void { self.include_dirs.append(path) catch unreachable; } - pub fn addLibPath(self: &LibExeObjStep, path: []const u8) void { + pub fn addLibPath(self: *LibExeObjStep, path: []const u8) void { self.lib_paths.append(path) catch unreachable; } - pub fn addPackagePath(self: &LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { + pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { assert(self.is_zig); self.packages.append(Pkg{ @@ -1127,23 +1127,23 @@ pub const LibExeObjStep = struct { }) catch unreachable; } - pub fn addCompileFlags(self: &LibExeObjStep, flags: []const []const u8) void { + pub fn addCompileFlags(self: *LibExeObjStep, flags: []const []const u8) void { for (flags) |flag| { self.cflags.append(flag) catch unreachable; } } - pub fn setNoStdLib(self: &LibExeObjStep, disable: bool) void { + pub fn setNoStdLib(self: *LibExeObjStep, disable: bool) void { assert(!self.is_zig); self.disable_libc = disable; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(LibExeObjStep, "step", step); return if (self.is_zig) self.makeZig() else self.makeC(); } - fn makeZig(self: &LibExeObjStep) !void { + fn makeZig(self: *LibExeObjStep) !void { const builder = self.builder; assert(self.is_zig); @@ -1309,7 +1309,7 @@ pub const LibExeObjStep = struct { } } - fn appendCompileFlags(self: &LibExeObjStep, args: &ArrayList([]const u8)) void { + fn appendCompileFlags(self: *LibExeObjStep, args: *ArrayList([]const u8)) void { if (!self.strip) { args.append("-g") catch unreachable; } @@ -1354,7 +1354,7 @@ pub const LibExeObjStep = struct { } } - fn makeC(self: &LibExeObjStep) !void { + fn makeC(self: *LibExeObjStep) !void { const builder = self.builder; const cc = builder.getCCExe(); @@ -1580,7 +1580,7 @@ pub const LibExeObjStep = struct { pub const TestStep = struct { step: Step, - builder: &Builder, + builder: *Builder, root_src: []const u8, build_mode: builtin.Mode, verbose: bool, @@ -1591,7 +1591,7 @@ pub const TestStep = struct { exec_cmd_args: ?[]const ?[]const u8, include_dirs: ArrayList([]const u8), - pub fn init(builder: &Builder, root_src: []const u8) TestStep { + pub fn init(builder: *Builder, root_src: []const u8) TestStep { const step_name = builder.fmt("test {}", root_src); return TestStep{ .step = Step.init(step_name, builder.allocator, make), @@ -1608,31 +1608,31 @@ pub const TestStep = struct { }; } - pub fn setVerbose(self: &TestStep, value: bool) void { + pub fn setVerbose(self: *TestStep, value: bool) void { self.verbose = value; } - pub fn addIncludeDir(self: &TestStep, path: []const u8) void { + pub fn addIncludeDir(self: *TestStep, path: []const u8) void { self.include_dirs.append(path) catch unreachable; } - pub fn setBuildMode(self: &TestStep, mode: builtin.Mode) void { + pub fn setBuildMode(self: *TestStep, mode: builtin.Mode) void { self.build_mode = mode; } - pub fn linkSystemLibrary(self: &TestStep, name: []const u8) void { + pub fn linkSystemLibrary(self: *TestStep, name: []const u8) void { self.link_libs.put(name) catch unreachable; } - pub fn setNamePrefix(self: &TestStep, text: []const u8) void { + pub fn setNamePrefix(self: *TestStep, text: []const u8) void { self.name_prefix = text; } - pub fn setFilter(self: &TestStep, text: ?[]const u8) void { + pub fn setFilter(self: *TestStep, text: ?[]const u8) void { self.filter = text; } - pub fn setTarget(self: &TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { + pub fn setTarget(self: *TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { self.target = Target{ .Cross = CrossTarget{ .arch = target_arch, @@ -1642,11 +1642,11 @@ pub const TestStep = struct { }; } - pub fn setExecCmd(self: &TestStep, args: []const ?[]const u8) void { + pub fn setExecCmd(self: *TestStep, args: []const ?[]const u8) void { self.exec_cmd_args = args; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(TestStep, "step", step); const builder = self.builder; @@ -1739,13 +1739,13 @@ pub const TestStep = struct { pub const CommandStep = struct { step: Step, - builder: &Builder, + builder: *Builder, argv: [][]const u8, cwd: ?[]const u8, - env_map: &const BufMap, + env_map: *const BufMap, /// ::argv is copied. - pub fn create(builder: &Builder, cwd: ?[]const u8, env_map: &const BufMap, argv: []const []const u8) &CommandStep { + pub fn create(builder: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) *CommandStep { const self = builder.allocator.create(CommandStep) catch unreachable; self.* = CommandStep{ .builder = builder, @@ -1759,7 +1759,7 @@ pub const CommandStep = struct { return self; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(CommandStep, "step", step); const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; @@ -1769,13 +1769,13 @@ pub const CommandStep = struct { const InstallArtifactStep = struct { step: Step, - builder: &Builder, - artifact: &LibExeObjStep, + builder: *Builder, + artifact: *LibExeObjStep, dest_file: []const u8, const Self = this; - pub fn create(builder: &Builder, artifact: &LibExeObjStep) &Self { + pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self { const self = builder.allocator.create(Self) catch unreachable; const dest_dir = switch (artifact.kind) { LibExeObjStep.Kind.Obj => unreachable, @@ -1797,7 +1797,7 @@ const InstallArtifactStep = struct { return self; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(Self, "step", step); const builder = self.builder; @@ -1818,11 +1818,11 @@ const InstallArtifactStep = struct { pub const InstallFileStep = struct { step: Step, - builder: &Builder, + builder: *Builder, src_path: []const u8, dest_path: []const u8, - pub fn init(builder: &Builder, src_path: []const u8, dest_path: []const u8) InstallFileStep { + pub fn init(builder: *Builder, src_path: []const u8, dest_path: []const u8) InstallFileStep { return InstallFileStep{ .builder = builder, .step = Step.init(builder.fmt("install {}", src_path), builder.allocator, make), @@ -1831,7 +1831,7 @@ pub const InstallFileStep = struct { }; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(InstallFileStep, "step", step); try self.builder.copyFile(self.src_path, self.dest_path); } @@ -1839,11 +1839,11 @@ pub const InstallFileStep = struct { pub const WriteFileStep = struct { step: Step, - builder: &Builder, + builder: *Builder, file_path: []const u8, data: []const u8, - pub fn init(builder: &Builder, file_path: []const u8, data: []const u8) WriteFileStep { + pub fn init(builder: *Builder, file_path: []const u8, data: []const u8) WriteFileStep { return WriteFileStep{ .builder = builder, .step = Step.init(builder.fmt("writefile {}", file_path), builder.allocator, make), @@ -1852,7 +1852,7 @@ pub const WriteFileStep = struct { }; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(WriteFileStep, "step", step); const full_path = self.builder.pathFromRoot(self.file_path); const full_path_dir = os.path.dirname(full_path); @@ -1869,10 +1869,10 @@ pub const WriteFileStep = struct { pub const LogStep = struct { step: Step, - builder: &Builder, + builder: *Builder, data: []const u8, - pub fn init(builder: &Builder, data: []const u8) LogStep { + pub fn init(builder: *Builder, data: []const u8) LogStep { return LogStep{ .builder = builder, .step = Step.init(builder.fmt("log {}", data), builder.allocator, make), @@ -1880,7 +1880,7 @@ pub const LogStep = struct { }; } - fn make(step: &Step) error!void { + fn make(step: *Step) error!void { const self = @fieldParentPtr(LogStep, "step", step); warn("{}", self.data); } @@ -1888,10 +1888,10 @@ pub const LogStep = struct { pub const RemoveDirStep = struct { step: Step, - builder: &Builder, + builder: *Builder, dir_path: []const u8, - pub fn init(builder: &Builder, dir_path: []const u8) RemoveDirStep { + pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep { return RemoveDirStep{ .builder = builder, .step = Step.init(builder.fmt("RemoveDir {}", dir_path), builder.allocator, make), @@ -1899,7 +1899,7 @@ pub const RemoveDirStep = struct { }; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(RemoveDirStep, "step", step); const full_path = self.builder.pathFromRoot(self.dir_path); @@ -1912,39 +1912,39 @@ pub const RemoveDirStep = struct { pub const Step = struct { name: []const u8, - makeFn: fn (self: &Step) error!void, - dependencies: ArrayList(&Step), + makeFn: fn (self: *Step) error!void, + dependencies: ArrayList(*Step), loop_flag: bool, done_flag: bool, - pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn (&Step) error!void) Step { + pub fn init(name: []const u8, allocator: *Allocator, makeFn: fn (*Step) error!void) Step { return Step{ .name = name, .makeFn = makeFn, - .dependencies = ArrayList(&Step).init(allocator), + .dependencies = ArrayList(*Step).init(allocator), .loop_flag = false, .done_flag = false, }; } - pub fn initNoOp(name: []const u8, allocator: &Allocator) Step { + pub fn initNoOp(name: []const u8, allocator: *Allocator) Step { return init(name, allocator, makeNoOp); } - pub fn make(self: &Step) !void { + pub fn make(self: *Step) !void { if (self.done_flag) return; try self.makeFn(self); self.done_flag = true; } - pub fn dependOn(self: &Step, other: &Step) void { + pub fn dependOn(self: *Step, other: *Step) void { self.dependencies.append(other) catch unreachable; } - fn makeNoOp(self: &Step) error!void {} + fn makeNoOp(self: *Step) error!void {} }; -fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { +fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { const out_dir = os.path.dirname(output_path); const out_basename = os.path.basename(output_path); // sym link for libfoo.so.1 to libfoo.so.1.2.3 diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 6a33c994bf..69395e6b27 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -1,10 +1,10 @@ -extern "c" fn __error() &c_int; -pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int; +extern "c" fn __error() *c_int; +pub extern "c" fn _NSGetExecutablePath(buf: *u8, bufsize: *u32) c_int; -pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize; +pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: *u8, buf_len: usize, basep: *i64) usize; pub extern "c" fn mach_absolute_time() u64; -pub extern "c" fn mach_timebase_info(tinfo: ?&mach_timebase_info_data) void; +pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void; pub use @import("../os/darwin_errno.zig"); diff --git a/std/c/index.zig b/std/c/index.zig index f9704f4738..114b79cdae 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -13,49 +13,49 @@ pub extern "c" fn abort() noreturn; pub extern "c" fn exit(code: c_int) noreturn; pub extern "c" fn isatty(fd: c_int) c_int; pub extern "c" fn close(fd: c_int) c_int; -pub extern "c" fn fstat(fd: c_int, buf: &Stat) c_int; -pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: &Stat) c_int; +pub extern "c" fn fstat(fd: c_int, buf: *Stat) c_int; +pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int; pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; -pub extern "c" fn open(path: &const u8, oflag: c_int, ...) c_int; +pub extern "c" fn open(path: *const u8, oflag: c_int, ...) c_int; pub extern "c" fn raise(sig: c_int) c_int; -pub extern "c" fn read(fd: c_int, buf: &c_void, nbyte: usize) isize; -pub extern "c" fn stat(noalias path: &const u8, noalias buf: &Stat) c_int; -pub extern "c" fn write(fd: c_int, buf: &const c_void, nbyte: usize) isize; -pub extern "c" fn mmap(addr: ?&c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?&c_void; -pub extern "c" fn munmap(addr: &c_void, len: usize) c_int; -pub extern "c" fn unlink(path: &const u8) c_int; -pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8; -pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int; +pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; +pub extern "c" fn stat(noalias path: *const u8, noalias buf: *Stat) c_int; +pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; +pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; +pub extern "c" fn munmap(addr: *c_void, len: usize) c_int; +pub extern "c" fn unlink(path: *const u8) c_int; +pub extern "c" fn getcwd(buf: *u8, size: usize) ?*u8; +pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; pub extern "c" fn fork() c_int; -pub extern "c" fn access(path: &const u8, mode: c_uint) c_int; -pub extern "c" fn pipe(fds: &c_int) c_int; -pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int; -pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int; -pub extern "c" fn rename(old: &const u8, new: &const u8) c_int; -pub extern "c" fn chdir(path: &const u8) c_int; -pub extern "c" fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) c_int; +pub extern "c" fn access(path: *const u8, mode: c_uint) c_int; +pub extern "c" fn pipe(fds: *c_int) c_int; +pub extern "c" fn mkdir(path: *const u8, mode: c_uint) c_int; +pub extern "c" fn symlink(existing: *const u8, new: *const u8) c_int; +pub extern "c" fn rename(old: *const u8, new: *const u8) c_int; +pub extern "c" fn chdir(path: *const u8) c_int; +pub extern "c" fn execve(path: *const u8, argv: *const ?*const u8, envp: *const ?*const u8) c_int; pub extern "c" fn dup(fd: c_int) c_int; pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; -pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize; -pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8; -pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int; -pub extern "c" fn gettimeofday(tv: ?&timeval, tz: ?&timezone) c_int; -pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int; -pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?×pec) c_int; +pub extern "c" fn readlink(noalias path: *const u8, noalias buf: *u8, bufsize: usize) isize; +pub extern "c" fn realpath(noalias file_name: *const u8, noalias resolved_name: *u8) ?*u8; +pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int; +pub extern "c" fn gettimeofday(tv: ?*timeval, tz: ?*timezone) c_int; +pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; +pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; -pub extern "c" fn rmdir(path: &const u8) c_int; +pub extern "c" fn rmdir(path: *const u8) c_int; -pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?&c_void; -pub extern "c" fn malloc(usize) ?&c_void; -pub extern "c" fn realloc(&c_void, usize) ?&c_void; -pub extern "c" fn free(&c_void) void; -pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int; +pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; +pub extern "c" fn malloc(usize) ?*c_void; +pub extern "c" fn realloc(*c_void, usize) ?*c_void; +pub extern "c" fn free(*c_void) void; +pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; -pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t, noalias attr: ?&const pthread_attr_t, start_routine: extern fn (?&c_void) ?&c_void, noalias arg: ?&c_void) c_int; -pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int; -pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int; -pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int; -pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int; +pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int; +pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; +pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; +pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; +pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; -pub const pthread_t = &@OpaqueType(); +pub const pthread_t = *@OpaqueType(); diff --git a/std/c/linux.zig b/std/c/linux.zig index 7810fec130..0ab043533e 100644 --- a/std/c/linux.zig +++ b/std/c/linux.zig @@ -1,7 +1,7 @@ pub use @import("../os/linux/errno.zig"); -pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int; -extern "c" fn __errno_location() &c_int; +pub extern "c" fn getrandom(buf_ptr: *u8, buf_len: usize, flags: c_uint) c_int; +extern "c" fn __errno_location() *c_int; pub const _errno = __errno_location; pub const pthread_attr_t = extern struct { diff --git a/std/c/windows.zig b/std/c/windows.zig index 6e8b17eda8..35ca217131 100644 --- a/std/c/windows.zig +++ b/std/c/windows.zig @@ -1 +1 @@ -pub extern "c" fn _errno() &c_int; +pub extern "c" fn _errno() *c_int; diff --git a/std/crypto/blake2.zig b/std/crypto/blake2.zig index bf3193b5d9..f0a9766c00 100644 --- a/std/crypto/blake2.zig +++ b/std/crypto/blake2.zig @@ -75,7 +75,7 @@ fn Blake2s(comptime out_len: usize) type { return s; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { mem.copy(u32, d.h[0..], iv[0..]); // No key plus default parameters @@ -90,7 +90,7 @@ fn Blake2s(comptime out_len: usize) type { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial buffer exists from previous update. Copy into buffer then hash. @@ -113,7 +113,7 @@ fn Blake2s(comptime out_len: usize) type { d.buf_len += u8(b[off..].len); } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { debug.assert(out.len >= out_len / 8); mem.set(u8, d.buf[d.buf_len..], 0); @@ -127,7 +127,7 @@ fn Blake2s(comptime out_len: usize) type { } } - fn round(d: &Self, b: []const u8, last: bool) void { + fn round(d: *Self, b: []const u8, last: bool) void { debug.assert(b.len == 64); var m: [16]u32 = undefined; @@ -310,7 +310,7 @@ fn Blake2b(comptime out_len: usize) type { return s; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { mem.copy(u64, d.h[0..], iv[0..]); // No key plus default parameters @@ -325,7 +325,7 @@ fn Blake2b(comptime out_len: usize) type { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial buffer exists from previous update. Copy into buffer then hash. @@ -348,7 +348,7 @@ fn Blake2b(comptime out_len: usize) type { d.buf_len += u8(b[off..].len); } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { mem.set(u8, d.buf[d.buf_len..], 0); d.t += d.buf_len; d.round(d.buf[0..], true); @@ -360,7 +360,7 @@ fn Blake2b(comptime out_len: usize) type { } } - fn round(d: &Self, b: []const u8, last: bool) void { + fn round(d: *Self, b: []const u8, last: bool) void { debug.assert(b.len == 128); var m: [16]u64 = undefined; diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig index 3d05597273..c0d1732d37 100644 --- a/std/crypto/md5.zig +++ b/std/crypto/md5.zig @@ -44,7 +44,7 @@ pub const Md5 = struct { return d; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { d.s[0] = 0x67452301; d.s[1] = 0xEFCDAB89; d.s[2] = 0x98BADCFE; @@ -59,7 +59,7 @@ pub const Md5 = struct { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial buffer exists from previous update. Copy into buffer then hash. @@ -84,7 +84,7 @@ pub const Md5 = struct { d.total_len +%= b.len; } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { debug.assert(out.len >= 16); // The buffer here will never be completely full. @@ -116,7 +116,7 @@ pub const Md5 = struct { } } - fn round(d: &Self, b: []const u8) void { + fn round(d: *Self, b: []const u8) void { debug.assert(b.len == 64); var s: [16]u32 = undefined; diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig index e9d8e3e132..9e46fc9239 100644 --- a/std/crypto/sha1.zig +++ b/std/crypto/sha1.zig @@ -43,7 +43,7 @@ pub const Sha1 = struct { return d; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { d.s[0] = 0x67452301; d.s[1] = 0xEFCDAB89; d.s[2] = 0x98BADCFE; @@ -59,7 +59,7 @@ pub const Sha1 = struct { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial buffer exists from previous update. Copy into buffer then hash. @@ -83,7 +83,7 @@ pub const Sha1 = struct { d.total_len += b.len; } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { debug.assert(out.len >= 20); // The buffer here will never be completely full. @@ -115,7 +115,7 @@ pub const Sha1 = struct { } } - fn round(d: &Self, b: []const u8) void { + fn round(d: *Self, b: []const u8) void { debug.assert(b.len == 64); var s: [16]u32 = undefined; diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig index aedc820f44..d1375d73e8 100644 --- a/std/crypto/sha2.zig +++ b/std/crypto/sha2.zig @@ -93,7 +93,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { return d; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { d.s[0] = params.iv0; d.s[1] = params.iv1; d.s[2] = params.iv2; @@ -112,7 +112,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial buffer exists from previous update. Copy into buffer then hash. @@ -136,7 +136,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { d.total_len += b.len; } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { debug.assert(out.len >= params.out_len / 8); // The buffer here will never be completely full. @@ -171,7 +171,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { } } - fn round(d: &Self, b: []const u8) void { + fn round(d: *Self, b: []const u8) void { debug.assert(b.len == 64); var s: [64]u32 = undefined; @@ -434,7 +434,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { return d; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { d.s[0] = params.iv0; d.s[1] = params.iv1; d.s[2] = params.iv2; @@ -453,7 +453,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial buffer exists from previous update. Copy into buffer then hash. @@ -477,7 +477,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { d.total_len += b.len; } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { debug.assert(out.len >= params.out_len / 8); // The buffer here will never be completely full. @@ -512,7 +512,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { } } - fn round(d: &Self, b: []const u8) void { + fn round(d: *Self, b: []const u8) void { debug.assert(b.len == 128); var s: [80]u64 = undefined; diff --git a/std/crypto/sha3.zig b/std/crypto/sha3.zig index 75bec57a87..ae02d7a482 100644 --- a/std/crypto/sha3.zig +++ b/std/crypto/sha3.zig @@ -26,7 +26,7 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { return d; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { mem.set(u8, d.s[0..], 0); d.offset = 0; d.rate = 200 - (bits / 4); @@ -38,7 +38,7 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var ip: usize = 0; var len = b.len; var rate = d.rate - d.offset; @@ -63,7 +63,7 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { d.offset = offset + len; } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { // padding d.s[d.offset] ^= delim; d.s[d.rate - 1] ^= 0x80; diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig index c5c4f9fe10..0ad6845d1a 100644 --- a/std/crypto/throughput_test.zig +++ b/std/crypto/throughput_test.zig @@ -15,8 +15,8 @@ const BytesToHash = 1024 * MiB; pub fn main() !void { var stdout_file = try std.io.getStdOut(); - var stdout_out_stream = std.io.FileOutStream.init(&stdout_file); - const stdout = &stdout_out_stream.stream; + var stdout_out_stream = std.io.FileOutStream.init(*stdout_file); + const stdout = *stdout_out_stream.stream; var block: [HashFunction.block_size]u8 = undefined; std.mem.set(u8, block[0..], 0); diff --git a/std/cstr.zig b/std/cstr.zig index c9f3026064..dfbfb8047f 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -9,13 +9,13 @@ pub const line_sep = switch (builtin.os) { else => "\n", }; -pub fn len(ptr: &const u8) usize { +pub fn len(ptr: *const u8) usize { var count: usize = 0; while (ptr[count] != 0) : (count += 1) {} return count; } -pub fn cmp(a: &const u8, b: &const u8) i8 { +pub fn cmp(a: *const u8, b: *const u8) i8 { var index: usize = 0; while (a[index] == b[index] and a[index] != 0) : (index += 1) {} if (a[index] > b[index]) { @@ -27,11 +27,11 @@ pub fn cmp(a: &const u8, b: &const u8) i8 { } } -pub fn toSliceConst(str: &const u8) []const u8 { +pub fn toSliceConst(str: *const u8) []const u8 { return str[0..len(str)]; } -pub fn toSlice(str: &u8) []u8 { +pub fn toSlice(str: *u8) []u8 { return str[0..len(str)]; } @@ -47,7 +47,7 @@ fn testCStrFnsImpl() void { /// Returns a mutable slice with 1 more byte of length which is a null byte. /// Caller owns the returned memory. -pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) ![]u8 { +pub fn addNullByte(allocator: *mem.Allocator, slice: []const u8) ![]u8 { const result = try allocator.alloc(u8, slice.len + 1); mem.copy(u8, result, slice); result[slice.len] = 0; @@ -55,13 +55,13 @@ pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) ![]u8 { } pub const NullTerminated2DArray = struct { - allocator: &mem.Allocator, + allocator: *mem.Allocator, byte_count: usize, - ptr: ?&?&u8, + ptr: ?*?*u8, /// Takes N lists of strings, concatenates the lists together, and adds a null terminator /// Caller must deinit result - pub fn fromSlices(allocator: &mem.Allocator, slices: []const []const []const u8) !NullTerminated2DArray { + pub fn fromSlices(allocator: *mem.Allocator, slices: []const []const []const u8) !NullTerminated2DArray { var new_len: usize = 1; // 1 for the list null var byte_count: usize = 0; for (slices) |slice| { @@ -75,11 +75,11 @@ pub const NullTerminated2DArray = struct { const index_size = @sizeOf(usize) * new_len; // size of the ptrs byte_count += index_size; - const buf = try allocator.alignedAlloc(u8, @alignOf(?&u8), byte_count); + const buf = try allocator.alignedAlloc(u8, @alignOf(?*u8), byte_count); errdefer allocator.free(buf); var write_index = index_size; - const index_buf = ([]?&u8)(buf); + const index_buf = ([]?*u8)(buf); var i: usize = 0; for (slices) |slice| { @@ -97,12 +97,12 @@ pub const NullTerminated2DArray = struct { return NullTerminated2DArray{ .allocator = allocator, .byte_count = byte_count, - .ptr = @ptrCast(?&?&u8, buf.ptr), + .ptr = @ptrCast(?*?*u8, buf.ptr), }; } - pub fn deinit(self: &NullTerminated2DArray) void { - const buf = @ptrCast(&u8, self.ptr); + pub fn deinit(self: *NullTerminated2DArray) void { + const buf = @ptrCast(*u8, self.ptr); self.allocator.free(buf[0..self.byte_count]); } }; diff --git a/std/debug/failing_allocator.zig b/std/debug/failing_allocator.zig index 6b5edff5bf..e16dd21db4 100644 --- a/std/debug/failing_allocator.zig +++ b/std/debug/failing_allocator.zig @@ -7,12 +7,12 @@ pub const FailingAllocator = struct { allocator: mem.Allocator, index: usize, fail_index: usize, - internal_allocator: &mem.Allocator, + internal_allocator: *mem.Allocator, allocated_bytes: usize, freed_bytes: usize, deallocations: usize, - pub fn init(allocator: &mem.Allocator, fail_index: usize) FailingAllocator { + pub fn init(allocator: *mem.Allocator, fail_index: usize) FailingAllocator { return FailingAllocator{ .internal_allocator = allocator, .fail_index = fail_index, @@ -28,7 +28,7 @@ pub const FailingAllocator = struct { }; } - fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) ![]u8 { + fn alloc(allocator: *mem.Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); if (self.index == self.fail_index) { return error.OutOfMemory; @@ -39,7 +39,7 @@ pub const FailingAllocator = struct { return result; } - fn realloc(allocator: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + fn realloc(allocator: *mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); if (new_size <= old_mem.len) { self.freed_bytes += old_mem.len - new_size; @@ -55,7 +55,7 @@ pub const FailingAllocator = struct { return result; } - fn free(allocator: &mem.Allocator, bytes: []u8) void { + fn free(allocator: *mem.Allocator, bytes: []u8) void { const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); self.freed_bytes += bytes.len; self.deallocations += 1; diff --git a/std/debug/index.zig b/std/debug/index.zig index 92e565b391..00d9bef121 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -16,12 +16,12 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; /// TODO atomic/multithread support var stderr_file: os.File = undefined; var stderr_file_out_stream: io.FileOutStream = undefined; -var stderr_stream: ?&io.OutStream(io.FileOutStream.Error) = null; +var stderr_stream: ?*io.OutStream(io.FileOutStream.Error) = null; pub fn warn(comptime fmt: []const u8, args: ...) void { const stderr = getStderrStream() catch return; stderr.print(fmt, args) catch return; } -fn getStderrStream() !&io.OutStream(io.FileOutStream.Error) { +fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) { if (stderr_stream) |st| { return st; } else { @@ -33,8 +33,8 @@ fn getStderrStream() !&io.OutStream(io.FileOutStream.Error) { } } -var self_debug_info: ?&ElfStackTrace = null; -pub fn getSelfDebugInfo() !&ElfStackTrace { +var self_debug_info: ?*ElfStackTrace = null; +pub fn getSelfDebugInfo() !*ElfStackTrace { if (self_debug_info) |info| { return info; } else { @@ -58,7 +58,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { } /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. -pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void { +pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void { const stderr = getStderrStream() catch return; const debug_info = getSelfDebugInfo() catch |err| { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; @@ -104,7 +104,7 @@ pub fn panic(comptime format: []const u8, args: ...) noreturn { var panicking: u8 = 0; // TODO make this a bool -pub fn panicExtra(trace: ?&const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn { +pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn { @setCold(true); if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { @@ -130,7 +130,7 @@ const WHITE = "\x1b[37;1m"; const DIM = "\x1b[2m"; const RESET = "\x1b[0m"; -pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, allocator: &mem.Allocator, debug_info: &ElfStackTrace, tty_color: bool) !void { +pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool) !void { var frame_index: usize = undefined; var frames_left: usize = undefined; if (stack_trace.index < stack_trace.instruction_addresses.len) { @@ -150,7 +150,7 @@ pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, } } -pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, debug_info: &ElfStackTrace, tty_color: bool, start_addr: ?usize) !void { +pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void { const AddressState = union(enum) { NotLookingForStartAddress, LookingForStartAddress: usize, @@ -166,8 +166,8 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, debug_ } var fp = @ptrToInt(@frameAddress()); - while (fp != 0) : (fp = @intToPtr(&const usize, fp).*) { - const return_address = @intToPtr(&const usize, fp + @sizeOf(usize)).*; + while (fp != 0) : (fp = @intToPtr(*const usize, fp).*) { + const return_address = @intToPtr(*const usize, fp + @sizeOf(usize)).*; switch (addr_state) { AddressState.NotLookingForStartAddress => {}, @@ -183,7 +183,7 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, debug_ } } -fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void { +fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize) !void { const ptr_hex = "0x{x}"; switch (builtin.os) { @@ -236,7 +236,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: us } } -pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { +pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace { switch (builtin.object_format) { builtin.ObjectFormat.elf => { const st = try allocator.create(ElfStackTrace); @@ -289,7 +289,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { } } -fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &const LineInfo) !void { +fn printLineFromFile(allocator: *mem.Allocator, out_stream: var, line_info: *const LineInfo) !void { var f = try os.File.openRead(allocator, line_info.file_name); defer f.close(); // TODO fstat and make sure that the file has the correct size @@ -325,32 +325,32 @@ pub const ElfStackTrace = switch (builtin.os) { builtin.Os.macosx => struct { symbol_table: macho.SymbolTable, - pub fn close(self: &ElfStackTrace) void { + pub fn close(self: *ElfStackTrace) void { self.symbol_table.deinit(); } }, else => struct { self_exe_file: os.File, elf: elf.Elf, - debug_info: &elf.SectionHeader, - debug_abbrev: &elf.SectionHeader, - debug_str: &elf.SectionHeader, - debug_line: &elf.SectionHeader, - debug_ranges: ?&elf.SectionHeader, + debug_info: *elf.SectionHeader, + debug_abbrev: *elf.SectionHeader, + debug_str: *elf.SectionHeader, + debug_line: *elf.SectionHeader, + debug_ranges: ?*elf.SectionHeader, abbrev_table_list: ArrayList(AbbrevTableHeader), compile_unit_list: ArrayList(CompileUnit), - pub fn allocator(self: &const ElfStackTrace) &mem.Allocator { + pub fn allocator(self: *const ElfStackTrace) *mem.Allocator { return self.abbrev_table_list.allocator; } - pub fn readString(self: &ElfStackTrace) ![]u8 { + pub fn readString(self: *ElfStackTrace) ![]u8 { var in_file_stream = io.FileInStream.init(&self.self_exe_file); const in_stream = &in_file_stream.stream; return readStringRaw(self.allocator(), in_stream); } - pub fn close(self: &ElfStackTrace) void { + pub fn close(self: *ElfStackTrace) void { self.self_exe_file.close(); self.elf.close(); } @@ -365,7 +365,7 @@ const PcRange = struct { const CompileUnit = struct { version: u16, is_64: bool, - die: &Die, + die: *Die, index: usize, pc_range: ?PcRange, }; @@ -408,7 +408,7 @@ const Constant = struct { payload: []u8, signed: bool, - fn asUnsignedLe(self: &const Constant) !u64 { + fn asUnsignedLe(self: *const Constant) !u64 { if (self.payload.len > @sizeOf(u64)) return error.InvalidDebugInfo; if (self.signed) return error.InvalidDebugInfo; return mem.readInt(self.payload, u64, builtin.Endian.Little); @@ -425,14 +425,14 @@ const Die = struct { value: FormValue, }; - fn getAttr(self: &const Die, id: u64) ?&const FormValue { + fn getAttr(self: *const Die, id: u64) ?*const FormValue { for (self.attrs.toSliceConst()) |*attr| { if (attr.id == id) return &attr.value; } return null; } - fn getAttrAddr(self: &const Die, id: u64) !u64 { + fn getAttrAddr(self: *const Die, id: u64) !u64 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; return switch (form_value.*) { FormValue.Address => |value| value, @@ -440,7 +440,7 @@ const Die = struct { }; } - fn getAttrSecOffset(self: &const Die, id: u64) !u64 { + fn getAttrSecOffset(self: *const Die, id: u64) !u64 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; return switch (form_value.*) { FormValue.Const => |value| value.asUnsignedLe(), @@ -449,7 +449,7 @@ const Die = struct { }; } - fn getAttrUnsignedLe(self: &const Die, id: u64) !u64 { + fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; return switch (form_value.*) { FormValue.Const => |value| value.asUnsignedLe(), @@ -457,7 +457,7 @@ const Die = struct { }; } - fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) ![]u8 { + fn getAttrString(self: *const Die, st: *ElfStackTrace, id: u64) ![]u8 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; return switch (form_value.*) { FormValue.String => |value| value, @@ -478,9 +478,9 @@ const LineInfo = struct { line: usize, column: usize, file_name: []u8, - allocator: &mem.Allocator, + allocator: *mem.Allocator, - fn deinit(self: &const LineInfo) void { + fn deinit(self: *const LineInfo) void { self.allocator.free(self.file_name); } }; @@ -496,7 +496,7 @@ const LineNumberProgram = struct { target_address: usize, include_dirs: []const []const u8, - file_entries: &ArrayList(FileEntry), + file_entries: *ArrayList(FileEntry), prev_address: usize, prev_file: usize, @@ -506,7 +506,7 @@ const LineNumberProgram = struct { prev_basic_block: bool, prev_end_sequence: bool, - pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: &ArrayList(FileEntry), target_address: usize) LineNumberProgram { + pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram { return LineNumberProgram{ .address = 0, .file = 1, @@ -528,7 +528,7 @@ const LineNumberProgram = struct { }; } - pub fn checkLineMatch(self: &LineNumberProgram) !?LineInfo { + pub fn checkLineMatch(self: *LineNumberProgram) !?LineInfo { if (self.target_address >= self.prev_address and self.target_address < self.address) { const file_entry = if (self.prev_file == 0) { return error.MissingDebugInfo; @@ -562,7 +562,7 @@ const LineNumberProgram = struct { } }; -fn readStringRaw(allocator: &mem.Allocator, in_stream: var) ![]u8 { +fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { var buf = ArrayList(u8).init(allocator); while (true) { const byte = try in_stream.readByte(); @@ -572,30 +572,30 @@ fn readStringRaw(allocator: &mem.Allocator, in_stream: var) ![]u8 { return buf.toSlice(); } -fn getString(st: &ElfStackTrace, offset: u64) ![]u8 { +fn getString(st: *ElfStackTrace, offset: u64) ![]u8 { const pos = st.debug_str.offset + offset; try st.self_exe_file.seekTo(pos); return st.readString(); } -fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 { +fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { const buf = try allocator.alloc(u8, size); errdefer allocator.free(buf); if ((try in_stream.read(buf)) < size) return error.EndOfFile; return buf; } -fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { +fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { const buf = try readAllocBytes(allocator, in_stream, size); return FormValue{ .Block = buf }; } -fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { +fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { const block_len = try in_stream.readVarInt(builtin.Endian.Little, usize, size); return parseFormValueBlockLen(allocator, in_stream, block_len); } -fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue { +fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue { return FormValue{ .Const = Constant{ .signed = signed, @@ -612,12 +612,12 @@ fn parseFormValueTargetAddrSize(in_stream: var) !u64 { return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64) else unreachable; } -fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { +fn parseFormValueRefLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { const buf = try readAllocBytes(allocator, in_stream, size); return FormValue{ .Ref = buf }; } -fn parseFormValueRef(allocator: &mem.Allocator, in_stream: var, comptime T: type) !FormValue { +fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type) !FormValue { const block_len = try in_stream.readIntLe(T); return parseFormValueRefLen(allocator, in_stream, block_len); } @@ -632,7 +632,7 @@ const ParseFormValueError = error{ OutOfMemory, }; -fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { +fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { return switch (form_id) { DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) }, DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), @@ -682,7 +682,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64 }; } -fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable { +fn parseAbbrevTable(st: *ElfStackTrace) !AbbrevTable { const in_file = &st.self_exe_file; var in_file_stream = io.FileInStream.init(in_file); const in_stream = &in_file_stream.stream; @@ -712,7 +712,7 @@ fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable { /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, /// seeks in the stream and parses it. -fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) !&const AbbrevTable { +fn getAbbrevTable(st: *ElfStackTrace, abbrev_offset: u64) !*const AbbrevTable { for (st.abbrev_table_list.toSlice()) |*header| { if (header.offset == abbrev_offset) { return &header.table; @@ -726,14 +726,14 @@ fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) !&const AbbrevTable { return &st.abbrev_table_list.items[st.abbrev_table_list.len - 1].table; } -fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) ?&const AbbrevTableEntry { +fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { for (abbrev_table.toSliceConst()) |*table_entry| { if (table_entry.abbrev_code == abbrev_code) return table_entry; } return null; } -fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) !Die { +fn parseDie(st: *ElfStackTrace, abbrev_table: *const AbbrevTable, is_64: bool) !Die { const in_file = &st.self_exe_file; var in_file_stream = io.FileInStream.init(in_file); const in_stream = &in_file_stream.stream; @@ -755,7 +755,7 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) ! return result; } -fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, target_address: usize) !LineInfo { +fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, target_address: usize) !LineInfo { const compile_unit_cwd = try compile_unit.die.getAttrString(st, DW.AT_comp_dir); const in_file = &st.self_exe_file; @@ -934,7 +934,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe return error.MissingDebugInfo; } -fn scanAllCompileUnits(st: &ElfStackTrace) !void { +fn scanAllCompileUnits(st: *ElfStackTrace) !void { const debug_info_end = st.debug_info.offset + st.debug_info.size; var this_unit_offset = st.debug_info.offset; var cu_index: usize = 0; @@ -1005,7 +1005,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void { } } -fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit { +fn findCompileUnit(st: *ElfStackTrace, target_address: u64) !*const CompileUnit { var in_file_stream = io.FileInStream.init(&st.self_exe_file); const in_stream = &in_file_stream.stream; for (st.compile_unit_list.toSlice()) |*compile_unit| { @@ -1039,7 +1039,7 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit return error.MissingDebugInfo; } -fn readInitialLength(comptime E: type, in_stream: &io.InStream(E), is_64: &bool) !u64 { +fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { const first_32_bits = try in_stream.readIntLe(u32); is_64.* = (first_32_bits == 0xffffffff); if (is_64.*) { @@ -1096,10 +1096,10 @@ var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator var global_allocator_mem: [100 * 1024]u8 = undefined; // TODO make thread safe -var debug_info_allocator: ?&mem.Allocator = null; +var debug_info_allocator: ?*mem.Allocator = null; var debug_info_direct_allocator: std.heap.DirectAllocator = undefined; var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined; -fn getDebugInfoAllocator() &mem.Allocator { +fn getDebugInfoAllocator() *mem.Allocator { if (debug_info_allocator) |a| return a; debug_info_direct_allocator = std.heap.DirectAllocator.init(); diff --git a/std/elf.zig b/std/elf.zig index 29b9473f98..50e97ab271 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -338,7 +338,7 @@ pub const SectionHeader = struct { }; pub const Elf = struct { - in_file: &os.File, + in_file: *os.File, auto_close_stream: bool, is_64: bool, endian: builtin.Endian, @@ -348,20 +348,20 @@ pub const Elf = struct { program_header_offset: u64, section_header_offset: u64, string_section_index: u64, - string_section: &SectionHeader, + string_section: *SectionHeader, section_headers: []SectionHeader, - allocator: &mem.Allocator, + allocator: *mem.Allocator, prealloc_file: os.File, /// Call close when done. - pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) !void { + pub fn openPath(elf: *Elf, allocator: *mem.Allocator, path: []const u8) !void { try elf.prealloc_file.open(path); - try elf.openFile(allocator, &elf.prealloc_file); + try elf.openFile(allocator, *elf.prealloc_file); elf.auto_close_stream = true; } /// Call close when done. - pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &os.File) !void { + pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: *os.File) !void { elf.allocator = allocator; elf.in_file = file; elf.auto_close_stream = false; @@ -503,13 +503,13 @@ pub const Elf = struct { } } - pub fn close(elf: &Elf) void { + pub fn close(elf: *Elf) void { elf.allocator.free(elf.section_headers); if (elf.auto_close_stream) elf.in_file.close(); } - pub fn findSection(elf: &Elf, name: []const u8) !?&SectionHeader { + pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader { var file_stream = io.FileInStream.init(elf.in_file); const in = &file_stream.stream; @@ -533,7 +533,7 @@ pub const Elf = struct { return null; } - pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) !void { + pub fn seekToSection(elf: *Elf, elf_section: *SectionHeader) !void { try elf.in_file.seekTo(elf_section.offset); } }; diff --git a/std/event.zig b/std/event.zig index 4604eb8d02..89ab816bb6 100644 --- a/std/event.zig +++ b/std/event.zig @@ -6,9 +6,9 @@ const mem = std.mem; const posix = std.os.posix; pub const TcpServer = struct { - handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void, + handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void, - loop: &Loop, + loop: *Loop, sockfd: i32, accept_coro: ?promise, listen_address: std.net.Address, @@ -17,7 +17,7 @@ pub const TcpServer = struct { const PromiseNode = std.LinkedList(promise).Node; - pub fn init(loop: &Loop) !TcpServer { + pub fn init(loop: *Loop) !TcpServer { const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); errdefer std.os.close(sockfd); @@ -32,7 +32,7 @@ pub const TcpServer = struct { }; } - pub fn listen(self: &TcpServer, address: &const std.net.Address, handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void) !void { + pub fn listen(self: *TcpServer, address: *const std.net.Address, handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void) !void { self.handleRequestFn = handleRequestFn; try std.os.posixBind(self.sockfd, &address.os_addr); @@ -46,13 +46,13 @@ pub const TcpServer = struct { errdefer self.loop.removeFd(self.sockfd); } - pub fn deinit(self: &TcpServer) void { + pub fn deinit(self: *TcpServer) void { self.loop.removeFd(self.sockfd); if (self.accept_coro) |accept_coro| cancel accept_coro; std.os.close(self.sockfd); } - pub async fn handler(self: &TcpServer) void { + pub async fn handler(self: *TcpServer) void { while (true) { var accepted_addr: std.net.Address = undefined; if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr, posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| { @@ -92,11 +92,11 @@ pub const TcpServer = struct { }; pub const Loop = struct { - allocator: &mem.Allocator, + allocator: *mem.Allocator, epollfd: i32, keep_running: bool, - fn init(allocator: &mem.Allocator) !Loop { + fn init(allocator: *mem.Allocator) !Loop { const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC); return Loop{ .keep_running = true, @@ -105,7 +105,7 @@ pub const Loop = struct { }; } - pub fn addFd(self: &Loop, fd: i32, prom: promise) !void { + pub fn addFd(self: *Loop, fd: i32, prom: promise) !void { var ev = std.os.linux.epoll_event{ .events = std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(prom) }, @@ -113,23 +113,23 @@ pub const Loop = struct { try std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev); } - pub fn removeFd(self: &Loop, fd: i32) void { + pub fn removeFd(self: *Loop, fd: i32) void { std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; } - async fn waitFd(self: &Loop, fd: i32) !void { + async fn waitFd(self: *Loop, fd: i32) !void { defer self.removeFd(fd); suspend |p| { try self.addFd(fd, p); } } - pub fn stop(self: &Loop) void { + pub fn stop(self: *Loop) void { // TODO make atomic self.keep_running = false; // TODO activate an fd in the epoll set } - pub fn run(self: &Loop) void { + pub fn run(self: *Loop) void { while (self.keep_running) { var events: [16]std.os.linux.epoll_event = undefined; const count = std.os.linuxEpollWait(self.epollfd, events[0..], -1); @@ -141,7 +141,7 @@ pub const Loop = struct { } }; -pub async fn connect(loop: &Loop, _address: &const std.net.Address) !std.os.File { +pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File { var address = _address.*; // TODO https://github.com/ziglang/zig/issues/733 const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); @@ -163,7 +163,7 @@ test "listen on a port, send bytes, receive bytes" { tcp_server: TcpServer, const Self = this; - async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address, _socket: &const std.os.File) void { + async<*mem.Allocator> fn handler(tcp_server: *TcpServer, _addr: *const std.net.Address, _socket: *const std.os.File) void { const self = @fieldParentPtr(Self, "tcp_server", tcp_server); var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 defer socket.close(); @@ -177,7 +177,7 @@ test "listen on a port, send bytes, receive bytes" { cancel p; } } - async fn errorableHandler(self: &Self, _addr: &const std.net.Address, _socket: &const std.os.File) !void { + async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void { const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733 var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 @@ -199,7 +199,7 @@ test "listen on a port, send bytes, receive bytes" { defer cancel p; loop.run(); } -async fn doAsyncTest(loop: &Loop, address: &const std.net.Address) void { +async fn doAsyncTest(loop: *Loop, address: *const std.net.Address) void { errdefer @panic("test failure"); var socket_file = try await try async event.connect(loop, address); diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index 65e8d448a8..933958ac18 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -21,7 +21,7 @@ pub const RoundMode = enum { /// Round a FloatDecimal as returned by errol3 to the specified fractional precision. /// All digits after the specified precision should be considered invalid. -pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: RoundMode) void { +pub fn roundToPrecision(float_decimal: *FloatDecimal, precision: usize, mode: RoundMode) void { // The round digit refers to the index which we should look at to determine // whether we need to round to match the specified precision. var round_digit: usize = 0; @@ -59,7 +59,7 @@ pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: Ro float_decimal.exp += 1; // Re-size the buffer to use the reserved leading byte. - const one_before = @intToPtr(&u8, @ptrToInt(&float_decimal.digits[0]) - 1); + const one_before = @intToPtr(*u8, @ptrToInt(&float_decimal.digits[0]) - 1); float_decimal.digits = one_before[0 .. float_decimal.digits.len + 1]; float_decimal.digits[0] = '1'; return; @@ -217,7 +217,7 @@ fn tableLowerBound(k: u64) usize { /// @in: The HP number. /// @val: The double. /// &returns: The HP number. -fn hpProd(in: &const HP, val: f64) HP { +fn hpProd(in: *const HP, val: f64) HP { var hi: f64 = undefined; var lo: f64 = undefined; split(in.val, &hi, &lo); @@ -239,7 +239,7 @@ fn hpProd(in: &const HP, val: f64) HP { /// @val: The double. /// @hi: The high bits. /// @lo: The low bits. -fn split(val: f64, hi: &f64, lo: &f64) void { +fn split(val: f64, hi: *f64, lo: *f64) void { hi.* = gethi(val); lo.* = val - hi.*; } @@ -252,7 +252,7 @@ fn gethi(in: f64) f64 { /// Normalize the number by factoring in the error. /// @hp: The float pair. -fn hpNormalize(hp: &HP) void { +fn hpNormalize(hp: *HP) void { // Required to avoid segfaults causing buffer overrun during errol3 digit output termination. @setFloatMode(this, @import("builtin").FloatMode.Strict); @@ -264,7 +264,7 @@ fn hpNormalize(hp: &HP) void { /// Divide the high-precision number by ten. /// @hp: The high-precision number -fn hpDiv10(hp: &HP) void { +fn hpDiv10(hp: *HP) void { var val = hp.val; hp.val /= 10.0; @@ -280,7 +280,7 @@ fn hpDiv10(hp: &HP) void { /// Multiply the high-precision number by ten. /// @hp: The high-precision number -fn hpMul10(hp: &HP) void { +fn hpMul10(hp: *HP) void { const val = hp.val; hp.val *= 10.0; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 0ffbc59895..b522d9d37d 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -679,7 +679,7 @@ const FormatIntBuf = struct { out_buf: []u8, index: usize, }; -fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) (error{}!void) { +fn formatIntCallback(context: *FormatIntBuf, bytes: []const u8) (error{}!void) { mem.copy(u8, context.out_buf[context.index..], bytes); context.index += bytes.len; } @@ -751,7 +751,7 @@ const BufPrintContext = struct { remaining: []u8, }; -fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) !void { +fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { if (context.remaining.len < bytes.len) return error.BufferTooSmall; mem.copy(u8, context.remaining, bytes); context.remaining = context.remaining[bytes.len..]; @@ -763,14 +763,14 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { return buf[0 .. buf.len - context.remaining.len]; } -pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { +pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { var size: usize = 0; format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; const buf = try allocator.alloc(u8, size); return bufPrint(buf, fmt, args); } -fn countSize(size: &usize, bytes: []const u8) (error{}!void) { +fn countSize(size: *usize, bytes: []const u8) (error{}!void) { size.* += bytes.len; } diff --git a/std/hash/adler.zig b/std/hash/adler.zig index 12dab1457c..9c5966f89b 100644 --- a/std/hash/adler.zig +++ b/std/hash/adler.zig @@ -18,7 +18,7 @@ pub const Adler32 = struct { // This fast variant is taken from zlib. It reduces the required modulos and unrolls longer // buffer inputs and should be much quicker. - pub fn update(self: &Adler32, input: []const u8) void { + pub fn update(self: *Adler32, input: []const u8) void { var s1 = self.adler & 0xffff; var s2 = (self.adler >> 16) & 0xffff; @@ -77,7 +77,7 @@ pub const Adler32 = struct { self.adler = s1 | (s2 << 16); } - pub fn final(self: &Adler32) u32 { + pub fn final(self: *Adler32) u32 { return self.adler; } diff --git a/std/hash/crc.zig b/std/hash/crc.zig index 45bcb70e8b..ec831cdc2e 100644 --- a/std/hash/crc.zig +++ b/std/hash/crc.zig @@ -58,7 +58,7 @@ pub fn Crc32WithPoly(comptime poly: u32) type { return Self{ .crc = 0xffffffff }; } - pub fn update(self: &Self, input: []const u8) void { + pub fn update(self: *Self, input: []const u8) void { var i: usize = 0; while (i + 8 <= input.len) : (i += 8) { const p = input[i .. i + 8]; @@ -86,7 +86,7 @@ pub fn Crc32WithPoly(comptime poly: u32) type { } } - pub fn final(self: &Self) u32 { + pub fn final(self: *Self) u32 { return ~self.crc; } @@ -143,14 +143,14 @@ pub fn Crc32SmallWithPoly(comptime poly: u32) type { return Self{ .crc = 0xffffffff }; } - pub fn update(self: &Self, input: []const u8) void { + pub fn update(self: *Self, input: []const u8) void { for (input) |b| { self.crc = lookup_table[@truncate(u4, self.crc ^ (b >> 0))] ^ (self.crc >> 4); self.crc = lookup_table[@truncate(u4, self.crc ^ (b >> 4))] ^ (self.crc >> 4); } } - pub fn final(self: &Self) u32 { + pub fn final(self: *Self) u32 { return ~self.crc; } diff --git a/std/hash/fnv.zig b/std/hash/fnv.zig index c2439e0ebc..447c996772 100644 --- a/std/hash/fnv.zig +++ b/std/hash/fnv.zig @@ -21,14 +21,14 @@ fn Fnv1a(comptime T: type, comptime prime: T, comptime offset: T) type { return Self{ .value = offset }; } - pub fn update(self: &Self, input: []const u8) void { + pub fn update(self: *Self, input: []const u8) void { for (input) |b| { self.value ^= b; self.value *%= prime; } } - pub fn final(self: &Self) T { + pub fn final(self: *Self) T { return self.value; } diff --git a/std/hash/siphash.zig b/std/hash/siphash.zig index 750e23d4c8..8a90308a46 100644 --- a/std/hash/siphash.zig +++ b/std/hash/siphash.zig @@ -63,7 +63,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) return d; } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial from previous. @@ -85,7 +85,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) d.msg_len +%= @truncate(u8, b.len); } - pub fn final(d: &Self) T { + pub fn final(d: *Self) T { // Padding mem.set(u8, d.buf[d.buf_len..], 0); d.buf[7] = d.msg_len; @@ -118,7 +118,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) return (u128(b2) << 64) | b1; } - fn round(d: &Self, b: []const u8) void { + fn round(d: *Self, b: []const u8) void { debug.assert(b.len == 8); const m = mem.readInt(b[0..], u64, Endian.Little); @@ -132,7 +132,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) d.v0 ^= m; } - fn sipRound(d: &Self) void { + fn sipRound(d: *Self) void { d.v0 +%= d.v1; d.v1 = math.rotl(u64, d.v1, u64(13)); d.v1 ^= d.v0; diff --git a/std/hash_map.zig b/std/hash_map.zig index f51b9c66ba..a323cdc197 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -14,7 +14,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 entries: []Entry, size: usize, max_distance_from_start_index: usize, - allocator: &Allocator, + allocator: *Allocator, // this is used to detect bugs where a hashtable is edited while an iterator is running. modification_count: debug_u32, @@ -28,7 +28,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 }; pub const Iterator = struct { - hm: &const Self, + hm: *const Self, // how many items have we returned count: usize, // iterator through the entry array @@ -36,7 +36,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 // used to detect concurrent modification initial_modification_count: debug_u32, - pub fn next(it: &Iterator) ?&Entry { + pub fn next(it: *Iterator) ?*Entry { if (want_modification_safety) { assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification } @@ -53,7 +53,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 } // Reset the iterator to the initial index - pub fn reset(it: &Iterator) void { + pub fn reset(it: *Iterator) void { it.count = 0; it.index = 0; // Resetting the modification count too @@ -61,7 +61,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 } }; - pub fn init(allocator: &Allocator) Self { + pub fn init(allocator: *Allocator) Self { return Self{ .entries = []Entry{}, .allocator = allocator, @@ -71,11 +71,11 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 }; } - pub fn deinit(hm: &const Self) void { + pub fn deinit(hm: *const Self) void { hm.allocator.free(hm.entries); } - pub fn clear(hm: &Self) void { + pub fn clear(hm: *Self) void { for (hm.entries) |*entry| { entry.used = false; } @@ -84,12 +84,12 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 hm.incrementModificationCount(); } - pub fn count(hm: &const Self) usize { + pub fn count(hm: *const Self) usize { return hm.size; } /// Returns the value that was already there. - pub fn put(hm: &Self, key: K, value: &const V) !?V { + pub fn put(hm: *Self, key: K, value: *const V) !?V { if (hm.entries.len == 0) { try hm.initCapacity(16); } @@ -111,18 +111,18 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 return hm.internalPut(key, value); } - pub fn get(hm: &const Self, key: K) ?&Entry { + pub fn get(hm: *const Self, key: K) ?*Entry { if (hm.entries.len == 0) { return null; } return hm.internalGet(key); } - pub fn contains(hm: &const Self, key: K) bool { + pub fn contains(hm: *const Self, key: K) bool { return hm.get(key) != null; } - pub fn remove(hm: &Self, key: K) ?&Entry { + pub fn remove(hm: *Self, key: K) ?*Entry { if (hm.entries.len == 0) return null; hm.incrementModificationCount(); const start_index = hm.keyToIndex(key); @@ -154,7 +154,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 return null; } - pub fn iterator(hm: &const Self) Iterator { + pub fn iterator(hm: *const Self) Iterator { return Iterator{ .hm = hm, .count = 0, @@ -163,7 +163,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 }; } - fn initCapacity(hm: &Self, capacity: usize) !void { + fn initCapacity(hm: *Self, capacity: usize) !void { hm.entries = try hm.allocator.alloc(Entry, capacity); hm.size = 0; hm.max_distance_from_start_index = 0; @@ -172,14 +172,14 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 } } - fn incrementModificationCount(hm: &Self) void { + fn incrementModificationCount(hm: *Self) void { if (want_modification_safety) { hm.modification_count +%= 1; } } /// Returns the value that was already there. - fn internalPut(hm: &Self, orig_key: K, orig_value: &const V) ?V { + fn internalPut(hm: *Self, orig_key: K, orig_value: *const V) ?V { var key = orig_key; var value = orig_value.*; const start_index = hm.keyToIndex(key); @@ -231,7 +231,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 unreachable; // put into a full map } - fn internalGet(hm: &const Self, key: K) ?&Entry { + fn internalGet(hm: *const Self, key: K) ?*Entry { const start_index = hm.keyToIndex(key); { var roll_over: usize = 0; @@ -246,7 +246,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 return null; } - fn keyToIndex(hm: &const Self, key: K) usize { + fn keyToIndex(hm: *const Self, key: K) usize { return usize(hash(key)) % hm.entries.len; } }; diff --git a/std/heap.zig b/std/heap.zig index 8d4938a7c3..81d6f25282 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -16,15 +16,15 @@ var c_allocator_state = Allocator{ .freeFn = cFree, }; -fn cAlloc(self: &Allocator, n: usize, alignment: u29) ![]u8 { +fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 { assert(alignment <= @alignOf(c_longdouble)); - return if (c.malloc(n)) |buf| @ptrCast(&u8, buf)[0..n] else error.OutOfMemory; + return if (c.malloc(n)) |buf| @ptrCast(*u8, buf)[0..n] else error.OutOfMemory; } -fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { - const old_ptr = @ptrCast(&c_void, old_mem.ptr); +fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + const old_ptr = @ptrCast(*c_void, old_mem.ptr); if (c.realloc(old_ptr, new_size)) |buf| { - return @ptrCast(&u8, buf)[0..new_size]; + return @ptrCast(*u8, buf)[0..new_size]; } else if (new_size <= old_mem.len) { return old_mem[0..new_size]; } else { @@ -32,8 +32,8 @@ fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![ } } -fn cFree(self: &Allocator, old_mem: []u8) void { - const old_ptr = @ptrCast(&c_void, old_mem.ptr); +fn cFree(self: *Allocator, old_mem: []u8) void { + const old_ptr = @ptrCast(*c_void, old_mem.ptr); c.free(old_ptr); } @@ -55,7 +55,7 @@ pub const DirectAllocator = struct { }; } - pub fn deinit(self: &DirectAllocator) void { + pub fn deinit(self: *DirectAllocator) void { switch (builtin.os) { Os.windows => if (self.heap_handle) |heap_handle| { _ = os.windows.HeapDestroy(heap_handle); @@ -64,7 +64,7 @@ pub const DirectAllocator = struct { } } - fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { + fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { @@ -74,7 +74,7 @@ pub const DirectAllocator = struct { const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0); if (addr == p.MAP_FAILED) return error.OutOfMemory; - if (alloc_size == n) return @intToPtr(&u8, addr)[0..n]; + if (alloc_size == n) return @intToPtr(*u8, addr)[0..n]; var aligned_addr = addr & ~usize(alignment - 1); aligned_addr += alignment; @@ -93,7 +93,7 @@ pub const DirectAllocator = struct { //It is impossible that there is an unoccupied page at the top of our // mmap. - return @intToPtr(&u8, aligned_addr)[0..n]; + return @intToPtr(*u8, aligned_addr)[0..n]; }, Os.windows => { const amt = n + alignment + @sizeOf(usize); @@ -108,14 +108,14 @@ pub const DirectAllocator = struct { const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); const adjusted_addr = root_addr + march_forward_bytes; const record_addr = adjusted_addr + n; - @intToPtr(&align(1) usize, record_addr).* = root_addr; - return @intToPtr(&u8, adjusted_addr)[0..n]; + @intToPtr(*align(1) usize, record_addr).* = root_addr; + return @intToPtr(*u8, adjusted_addr)[0..n]; }, else => @compileError("Unsupported OS"), } } - fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { @@ -139,13 +139,13 @@ pub const DirectAllocator = struct { Os.windows => { const old_adjusted_addr = @ptrToInt(old_mem.ptr); const old_record_addr = old_adjusted_addr + old_mem.len; - const root_addr = @intToPtr(&align(1) usize, old_record_addr).*; + const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; const old_ptr = @intToPtr(os.windows.LPVOID, root_addr); const amt = new_size + alignment + @sizeOf(usize); const new_ptr = os.windows.HeapReAlloc(??self.heap_handle, 0, old_ptr, amt) ?? blk: { if (new_size > old_mem.len) return error.OutOfMemory; const new_record_addr = old_record_addr - new_size + old_mem.len; - @intToPtr(&align(1) usize, new_record_addr).* = root_addr; + @intToPtr(*align(1) usize, new_record_addr).* = root_addr; return old_mem[0..new_size]; }; const offset = old_adjusted_addr - root_addr; @@ -153,14 +153,14 @@ pub const DirectAllocator = struct { const new_adjusted_addr = new_root_addr + offset; assert(new_adjusted_addr % alignment == 0); const new_record_addr = new_adjusted_addr + new_size; - @intToPtr(&align(1) usize, new_record_addr).* = new_root_addr; - return @intToPtr(&u8, new_adjusted_addr)[0..new_size]; + @intToPtr(*align(1) usize, new_record_addr).* = new_root_addr; + return @intToPtr(*u8, new_adjusted_addr)[0..new_size]; }, else => @compileError("Unsupported OS"), } } - fn free(allocator: &Allocator, bytes: []u8) void { + fn free(allocator: *Allocator, bytes: []u8) void { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { @@ -169,7 +169,7 @@ pub const DirectAllocator = struct { }, Os.windows => { const record_addr = @ptrToInt(bytes.ptr) + bytes.len; - const root_addr = @intToPtr(&align(1) usize, record_addr).*; + const root_addr = @intToPtr(*align(1) usize, record_addr).*; const ptr = @intToPtr(os.windows.LPVOID, root_addr); _ = os.windows.HeapFree(??self.heap_handle, 0, ptr); }, @@ -183,13 +183,13 @@ pub const DirectAllocator = struct { pub const ArenaAllocator = struct { pub allocator: Allocator, - child_allocator: &Allocator, + child_allocator: *Allocator, buffer_list: std.LinkedList([]u8), end_index: usize, const BufNode = std.LinkedList([]u8).Node; - pub fn init(child_allocator: &Allocator) ArenaAllocator { + pub fn init(child_allocator: *Allocator) ArenaAllocator { return ArenaAllocator{ .allocator = Allocator{ .allocFn = alloc, @@ -202,7 +202,7 @@ pub const ArenaAllocator = struct { }; } - pub fn deinit(self: &ArenaAllocator) void { + pub fn deinit(self: *ArenaAllocator) void { var it = self.buffer_list.first; while (it) |node| { // this has to occur before the free because the free frees node @@ -212,7 +212,7 @@ pub const ArenaAllocator = struct { } } - fn createNode(self: &ArenaAllocator, prev_len: usize, minimum_size: usize) !&BufNode { + fn createNode(self: *ArenaAllocator, prev_len: usize, minimum_size: usize) !*BufNode { const actual_min_size = minimum_size + @sizeOf(BufNode); var len = prev_len; while (true) { @@ -233,7 +233,7 @@ pub const ArenaAllocator = struct { return buf_node; } - fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { + fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator); var cur_node = if (self.buffer_list.last) |last_node| last_node else try self.createNode(0, n + alignment); @@ -254,7 +254,7 @@ pub const ArenaAllocator = struct { } } - fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { if (new_size <= old_mem.len) { return old_mem[0..new_size]; } else { @@ -264,7 +264,7 @@ pub const ArenaAllocator = struct { } } - fn free(allocator: &Allocator, bytes: []u8) void {} + fn free(allocator: *Allocator, bytes: []u8) void {} }; pub const FixedBufferAllocator = struct { @@ -284,7 +284,7 @@ pub const FixedBufferAllocator = struct { }; } - fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { + fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); const addr = @ptrToInt(self.buffer.ptr) + self.end_index; const rem = @rem(addr, alignment); @@ -300,7 +300,7 @@ pub const FixedBufferAllocator = struct { return result; } - fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { if (new_size <= old_mem.len) { return old_mem[0..new_size]; } else { @@ -310,7 +310,7 @@ pub const FixedBufferAllocator = struct { } } - fn free(allocator: &Allocator, bytes: []u8) void {} + fn free(allocator: *Allocator, bytes: []u8) void {} }; /// lock free @@ -331,7 +331,7 @@ pub const ThreadSafeFixedBufferAllocator = struct { }; } - fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { + fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator); var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst); while (true) { @@ -343,11 +343,11 @@ pub const ThreadSafeFixedBufferAllocator = struct { if (new_end_index > self.buffer.len) { return error.OutOfMemory; } - end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index..new_end_index]; + end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst,) ?? return self.buffer[adjusted_index..new_end_index]; } } - fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { if (new_size <= old_mem.len) { return old_mem[0..new_size]; } else { @@ -357,7 +357,7 @@ pub const ThreadSafeFixedBufferAllocator = struct { } } - fn free(allocator: &Allocator, bytes: []u8) void {} + fn free(allocator: *Allocator, bytes: []u8) void {} }; test "c_allocator" { @@ -403,8 +403,8 @@ test "ThreadSafeFixedBufferAllocator" { try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); } -fn testAllocator(allocator: &mem.Allocator) !void { - var slice = try allocator.alloc(&i32, 100); +fn testAllocator(allocator: *mem.Allocator) !void { + var slice = try allocator.alloc(*i32, 100); for (slice) |*item, i| { item.* = try allocator.create(i32); @@ -415,15 +415,15 @@ fn testAllocator(allocator: &mem.Allocator) !void { allocator.destroy(item); } - slice = try allocator.realloc(&i32, slice, 20000); - slice = try allocator.realloc(&i32, slice, 50); - slice = try allocator.realloc(&i32, slice, 25); - slice = try allocator.realloc(&i32, slice, 10); + slice = try allocator.realloc(*i32, slice, 20000); + slice = try allocator.realloc(*i32, slice, 50); + slice = try allocator.realloc(*i32, slice, 25); + slice = try allocator.realloc(*i32, slice, 10); allocator.free(slice); } -fn testAllocatorLargeAlignment(allocator: &mem.Allocator) mem.Allocator.Error!void { +fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void { //Maybe a platform's page_size is actually the same as or // very near usize? if (os.page_size << 2 > @maxValue(usize)) return; diff --git a/std/io.zig b/std/io.zig index 39d319159e..e20a284e4e 100644 --- a/std/io.zig +++ b/std/io.zig @@ -34,20 +34,20 @@ pub fn getStdIn() GetStdIoErrs!File { /// Implementation of InStream trait for File pub const FileInStream = struct { - file: &File, + file: *File, stream: Stream, pub const Error = @typeOf(File.read).ReturnType.ErrorSet; pub const Stream = InStream(Error); - pub fn init(file: &File) FileInStream { + pub fn init(file: *File) FileInStream { return FileInStream{ .file = file, .stream = Stream{ .readFn = readFn }, }; } - fn readFn(in_stream: &Stream, buffer: []u8) Error!usize { + fn readFn(in_stream: *Stream, buffer: []u8) Error!usize { const self = @fieldParentPtr(FileInStream, "stream", in_stream); return self.file.read(buffer); } @@ -55,20 +55,20 @@ pub const FileInStream = struct { /// Implementation of OutStream trait for File pub const FileOutStream = struct { - file: &File, + file: *File, stream: Stream, pub const Error = File.WriteError; pub const Stream = OutStream(Error); - pub fn init(file: &File) FileOutStream { + pub fn init(file: *File) FileOutStream { return FileOutStream{ .file = file, .stream = Stream{ .writeFn = writeFn }, }; } - fn writeFn(out_stream: &Stream, bytes: []const u8) !void { + fn writeFn(out_stream: *Stream, bytes: []const u8) !void { const self = @fieldParentPtr(FileOutStream, "stream", out_stream); return self.file.write(bytes); } @@ -82,12 +82,12 @@ pub fn InStream(comptime ReadError: type) type { /// Return the number of bytes read. If the number read is smaller than buf.len, it /// means the stream reached the end. Reaching the end of a stream is not an error /// condition. - readFn: fn (self: &Self, buffer: []u8) Error!usize, + readFn: fn (self: *Self, buffer: []u8) Error!usize, /// Replaces `buffer` contents by reading from the stream until it is finished. /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and /// the contents read from the stream are lost. - pub fn readAllBuffer(self: &Self, buffer: &Buffer, max_size: usize) !void { + pub fn readAllBuffer(self: *Self, buffer: *Buffer, max_size: usize) !void { try buffer.resize(0); var actual_buf_len: usize = 0; @@ -111,7 +111,7 @@ pub fn InStream(comptime ReadError: type) type { /// memory would be greater than `max_size`, returns `error.StreamTooLong`. /// Caller owns returned memory. /// If this function returns an error, the contents from the stream read so far are lost. - pub fn readAllAlloc(self: &Self, allocator: &mem.Allocator, max_size: usize) ![]u8 { + pub fn readAllAlloc(self: *Self, allocator: *mem.Allocator, max_size: usize) ![]u8 { var buf = Buffer.initNull(allocator); defer buf.deinit(); @@ -123,7 +123,7 @@ pub fn InStream(comptime ReadError: type) type { /// Does not include the delimiter in the result. /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents /// read from the stream so far are lost. - pub fn readUntilDelimiterBuffer(self: &Self, buffer: &Buffer, delimiter: u8, max_size: usize) !void { + pub fn readUntilDelimiterBuffer(self: *Self, buffer: *Buffer, delimiter: u8, max_size: usize) !void { try buffer.resize(0); while (true) { @@ -145,7 +145,7 @@ pub fn InStream(comptime ReadError: type) type { /// memory would be greater than `max_size`, returns `error.StreamTooLong`. /// Caller owns returned memory. /// If this function returns an error, the contents from the stream read so far are lost. - pub fn readUntilDelimiterAlloc(self: &Self, allocator: &mem.Allocator, delimiter: u8, max_size: usize) ![]u8 { + pub fn readUntilDelimiterAlloc(self: *Self, allocator: *mem.Allocator, delimiter: u8, max_size: usize) ![]u8 { var buf = Buffer.initNull(allocator); defer buf.deinit(); @@ -156,43 +156,43 @@ pub fn InStream(comptime ReadError: type) type { /// Returns the number of bytes read. If the number read is smaller than buf.len, it /// means the stream reached the end. Reaching the end of a stream is not an error /// condition. - pub fn read(self: &Self, buffer: []u8) !usize { + pub fn read(self: *Self, buffer: []u8) !usize { return self.readFn(self, buffer); } /// Same as `read` but end of stream returns `error.EndOfStream`. - pub fn readNoEof(self: &Self, buf: []u8) !void { + pub fn readNoEof(self: *Self, buf: []u8) !void { const amt_read = try self.read(buf); if (amt_read < buf.len) return error.EndOfStream; } /// Reads 1 byte from the stream or returns `error.EndOfStream`. - pub fn readByte(self: &Self) !u8 { + pub fn readByte(self: *Self) !u8 { var result: [1]u8 = undefined; try self.readNoEof(result[0..]); return result[0]; } /// Same as `readByte` except the returned byte is signed. - pub fn readByteSigned(self: &Self) !i8 { + pub fn readByteSigned(self: *Self) !i8 { return @bitCast(i8, try self.readByte()); } - pub fn readIntLe(self: &Self, comptime T: type) !T { + pub fn readIntLe(self: *Self, comptime T: type) !T { return self.readInt(builtin.Endian.Little, T); } - pub fn readIntBe(self: &Self, comptime T: type) !T { + pub fn readIntBe(self: *Self, comptime T: type) !T { return self.readInt(builtin.Endian.Big, T); } - pub fn readInt(self: &Self, endian: builtin.Endian, comptime T: type) !T { + pub fn readInt(self: *Self, endian: builtin.Endian, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); return mem.readInt(bytes, T, endian); } - pub fn readVarInt(self: &Self, endian: builtin.Endian, comptime T: type, size: usize) !T { + pub fn readVarInt(self: *Self, endian: builtin.Endian, comptime T: type, size: usize) !T { assert(size <= @sizeOf(T)); assert(size <= 8); var input_buf: [8]u8 = undefined; @@ -208,22 +208,22 @@ pub fn OutStream(comptime WriteError: type) type { const Self = this; pub const Error = WriteError; - writeFn: fn (self: &Self, bytes: []const u8) Error!void, + writeFn: fn (self: *Self, bytes: []const u8) Error!void, - pub fn print(self: &Self, comptime format: []const u8, args: ...) !void { + pub fn print(self: *Self, comptime format: []const u8, args: ...) !void { return std.fmt.format(self, Error, self.writeFn, format, args); } - pub fn write(self: &Self, bytes: []const u8) !void { + pub fn write(self: *Self, bytes: []const u8) !void { return self.writeFn(self, bytes); } - pub fn writeByte(self: &Self, byte: u8) !void { + pub fn writeByte(self: *Self, byte: u8) !void { const slice = (&byte)[0..1]; return self.writeFn(self, slice); } - pub fn writeByteNTimes(self: &Self, byte: u8, n: usize) !void { + pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) !void { const slice = (&byte)[0..1]; var i: usize = 0; while (i < n) : (i += 1) { @@ -234,14 +234,14 @@ pub fn OutStream(comptime WriteError: type) type { } /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. -pub fn writeFile(allocator: &mem.Allocator, path: []const u8, data: []const u8) !void { +pub fn writeFile(allocator: *mem.Allocator, path: []const u8, data: []const u8) !void { var file = try File.openWrite(allocator, path); defer file.close(); try file.write(data); } /// On success, caller owns returned buffer. -pub fn readFileAlloc(allocator: &mem.Allocator, path: []const u8) ![]u8 { +pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { var file = try File.openRead(allocator, path); defer file.close(); @@ -265,13 +265,13 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) pub stream: Stream, - unbuffered_in_stream: &Stream, + unbuffered_in_stream: *Stream, buffer: [buffer_size]u8, start_index: usize, end_index: usize, - pub fn init(unbuffered_in_stream: &Stream) Self { + pub fn init(unbuffered_in_stream: *Stream) Self { return Self{ .unbuffered_in_stream = unbuffered_in_stream, .buffer = undefined, @@ -287,7 +287,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) }; } - fn readFn(in_stream: &Stream, dest: []u8) !usize { + fn readFn(in_stream: *Stream, dest: []u8) !usize { const self = @fieldParentPtr(Self, "stream", in_stream); var dest_index: usize = 0; @@ -338,12 +338,12 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr pub stream: Stream, - unbuffered_out_stream: &Stream, + unbuffered_out_stream: *Stream, buffer: [buffer_size]u8, index: usize, - pub fn init(unbuffered_out_stream: &Stream) Self { + pub fn init(unbuffered_out_stream: *Stream) Self { return Self{ .unbuffered_out_stream = unbuffered_out_stream, .buffer = undefined, @@ -352,12 +352,12 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr }; } - pub fn flush(self: &Self) !void { + pub fn flush(self: *Self) !void { try self.unbuffered_out_stream.write(self.buffer[0..self.index]); self.index = 0; } - fn writeFn(out_stream: &Stream, bytes: []const u8) !void { + fn writeFn(out_stream: *Stream, bytes: []const u8) !void { const self = @fieldParentPtr(Self, "stream", out_stream); if (bytes.len >= self.buffer.len) { @@ -383,20 +383,20 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr /// Implementation of OutStream trait for Buffer pub const BufferOutStream = struct { - buffer: &Buffer, + buffer: *Buffer, stream: Stream, pub const Error = error{OutOfMemory}; pub const Stream = OutStream(Error); - pub fn init(buffer: &Buffer) BufferOutStream { + pub fn init(buffer: *Buffer) BufferOutStream { return BufferOutStream{ .buffer = buffer, .stream = Stream{ .writeFn = writeFn }, }; } - fn writeFn(out_stream: &Stream, bytes: []const u8) !void { + fn writeFn(out_stream: *Stream, bytes: []const u8) !void { const self = @fieldParentPtr(BufferOutStream, "stream", out_stream); return self.buffer.append(bytes); } @@ -407,7 +407,7 @@ pub const BufferedAtomicFile = struct { file_stream: FileOutStream, buffered_stream: BufferedOutStream(FileOutStream.Error), - pub fn create(allocator: &mem.Allocator, dest_path: []const u8) !&BufferedAtomicFile { + pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile { // TODO with well defined copy elision we don't need this allocation var self = try allocator.create(BufferedAtomicFile); errdefer allocator.destroy(self); @@ -427,18 +427,18 @@ pub const BufferedAtomicFile = struct { } /// always call destroy, even after successful finish() - pub fn destroy(self: &BufferedAtomicFile) void { + pub fn destroy(self: *BufferedAtomicFile) void { const allocator = self.atomic_file.allocator; self.atomic_file.deinit(); allocator.destroy(self); } - pub fn finish(self: &BufferedAtomicFile) !void { + pub fn finish(self: *BufferedAtomicFile) !void { try self.buffered_stream.flush(); try self.atomic_file.finish(); } - pub fn stream(self: &BufferedAtomicFile) &OutStream(FileOutStream.Error) { + pub fn stream(self: *BufferedAtomicFile) *OutStream(FileOutStream.Error) { return &self.buffered_stream.stream; } }; diff --git a/std/json.zig b/std/json.zig index 9de8f0b53e..c8aef7688b 100644 --- a/std/json.zig +++ b/std/json.zig @@ -76,7 +76,7 @@ pub const Token = struct { } // Slice into the underlying input string. - pub fn slice(self: &const Token, input: []const u8, i: usize) []const u8 { + pub fn slice(self: *const Token, input: []const u8, i: usize) []const u8 { return input[i + self.offset - self.count .. i + self.offset]; } }; @@ -115,7 +115,7 @@ pub const StreamingJsonParser = struct { return p; } - pub fn reset(p: &StreamingJsonParser) void { + pub fn reset(p: *StreamingJsonParser) void { p.state = State.TopLevelBegin; p.count = 0; // Set before ever read in main transition function @@ -205,7 +205,7 @@ pub const StreamingJsonParser = struct { // tokens. token2 is always null if token1 is null. // // There is currently no error recovery on a bad stream. - pub fn feed(p: &StreamingJsonParser, c: u8, token1: &?Token, token2: &?Token) Error!void { + pub fn feed(p: *StreamingJsonParser, c: u8, token1: *?Token, token2: *?Token) Error!void { token1.* = null; token2.* = null; p.count += 1; @@ -217,7 +217,7 @@ pub const StreamingJsonParser = struct { } // Perform a single transition on the state machine and return any possible token. - fn transition(p: &StreamingJsonParser, c: u8, token: &?Token) Error!bool { + fn transition(p: *StreamingJsonParser, c: u8, token: *?Token) Error!bool { switch (p.state) { State.TopLevelBegin => switch (c) { '{' => { @@ -861,7 +861,7 @@ pub fn validate(s: []const u8) bool { var token1: ?Token = undefined; var token2: ?Token = undefined; - p.feed(c, &token1, &token2) catch |err| { + p.feed(c, *token1, *token2) catch |err| { return false; }; } @@ -878,7 +878,7 @@ pub const ValueTree = struct { arena: ArenaAllocator, root: Value, - pub fn deinit(self: &ValueTree) void { + pub fn deinit(self: *ValueTree) void { self.arena.deinit(); } }; @@ -894,7 +894,7 @@ pub const Value = union(enum) { Array: ArrayList(Value), Object: ObjectMap, - pub fn dump(self: &const Value) void { + pub fn dump(self: *const Value) void { switch (self.*) { Value.Null => { std.debug.warn("null"); @@ -941,7 +941,7 @@ pub const Value = union(enum) { } } - pub fn dumpIndent(self: &const Value, indent: usize) void { + pub fn dumpIndent(self: *const Value, indent: usize) void { if (indent == 0) { self.dump(); } else { @@ -949,7 +949,7 @@ pub const Value = union(enum) { } } - fn dumpIndentLevel(self: &const Value, indent: usize, level: usize) void { + fn dumpIndentLevel(self: *const Value, indent: usize, level: usize) void { switch (self.*) { Value.Null => { std.debug.warn("null"); @@ -1013,7 +1013,7 @@ pub const Value = union(enum) { // A non-stream JSON parser which constructs a tree of Value's. pub const JsonParser = struct { - allocator: &Allocator, + allocator: *Allocator, state: State, copy_strings: bool, // Stores parent nodes and un-combined Values. @@ -1026,7 +1026,7 @@ pub const JsonParser = struct { Simple, }; - pub fn init(allocator: &Allocator, copy_strings: bool) JsonParser { + pub fn init(allocator: *Allocator, copy_strings: bool) JsonParser { return JsonParser{ .allocator = allocator, .state = State.Simple, @@ -1035,16 +1035,16 @@ pub const JsonParser = struct { }; } - pub fn deinit(p: &JsonParser) void { + pub fn deinit(p: *JsonParser) void { p.stack.deinit(); } - pub fn reset(p: &JsonParser) void { + pub fn reset(p: *JsonParser) void { p.state = State.Simple; p.stack.shrink(0); } - pub fn parse(p: &JsonParser, input: []const u8) !ValueTree { + pub fn parse(p: *JsonParser, input: []const u8) !ValueTree { var mp = StreamingJsonParser.init(); var arena = ArenaAllocator.init(p.allocator); @@ -1090,7 +1090,7 @@ pub const JsonParser = struct { // Even though p.allocator exists, we take an explicit allocator so that allocation state // can be cleaned up on error correctly during a `parse` on call. - fn transition(p: &JsonParser, allocator: &Allocator, input: []const u8, i: usize, token: &const Token) !void { + fn transition(p: *JsonParser, allocator: *Allocator, input: []const u8, i: usize, token: *const Token) !void { switch (p.state) { State.ObjectKey => switch (token.id) { Token.Id.ObjectEnd => { @@ -1223,7 +1223,7 @@ pub const JsonParser = struct { } } - fn pushToParent(p: &JsonParser, value: &const Value) !void { + fn pushToParent(p: *JsonParser, value: *const Value) !void { switch (p.stack.at(p.stack.len - 1)) { // Object Parent -> [ ..., object, , value ] Value.String => |key| { @@ -1244,14 +1244,14 @@ pub const JsonParser = struct { } } - fn parseString(p: &JsonParser, allocator: &Allocator, token: &const Token, input: []const u8, i: usize) !Value { + fn parseString(p: *JsonParser, allocator: *Allocator, token: *const Token, input: []const u8, i: usize) !Value { // TODO: We don't strictly have to copy values which do not contain any escape // characters if flagged with the option. const slice = token.slice(input, i); return Value{ .String = try mem.dupe(p.allocator, u8, slice) }; } - fn parseNumber(p: &JsonParser, token: &const Token, input: []const u8, i: usize) !Value { + fn parseNumber(p: *JsonParser, token: *const Token, input: []const u8, i: usize) !Value { return if (token.number_is_integer) Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } else diff --git a/std/linked_list.zig b/std/linked_list.zig index c6be08171e..fbc0a0c42a 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -21,11 +21,11 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// Node inside the linked list wrapping the actual data. pub const Node = struct { - prev: ?&Node, - next: ?&Node, + prev: ?*Node, + next: ?*Node, data: T, - pub fn init(value: &const T) Node { + pub fn init(value: *const T) Node { return Node{ .prev = null, .next = null, @@ -38,14 +38,14 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na return Node.init({}); } - pub fn toData(node: &Node) &ParentType { + pub fn toData(node: *Node) *ParentType { comptime assert(isIntrusive()); return @fieldParentPtr(ParentType, field_name, node); } }; - first: ?&Node, - last: ?&Node, + first: ?*Node, + last: ?*Node, len: usize, /// Initialize a linked list. @@ -69,7 +69,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// Arguments: /// node: Pointer to a node in the list. /// new_node: Pointer to the new node to insert. - pub fn insertAfter(list: &Self, node: &Node, new_node: &Node) void { + pub fn insertAfter(list: *Self, node: *Node, new_node: *Node) void { new_node.prev = node; if (node.next) |next_node| { // Intermediate node. @@ -90,7 +90,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// Arguments: /// node: Pointer to a node in the list. /// new_node: Pointer to the new node to insert. - pub fn insertBefore(list: &Self, node: &Node, new_node: &Node) void { + pub fn insertBefore(list: *Self, node: *Node, new_node: *Node) void { new_node.next = node; if (node.prev) |prev_node| { // Intermediate node. @@ -110,7 +110,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Arguments: /// new_node: Pointer to the new node to insert. - pub fn append(list: &Self, new_node: &Node) void { + pub fn append(list: *Self, new_node: *Node) void { if (list.last) |last| { // Insert after last. list.insertAfter(last, new_node); @@ -124,7 +124,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Arguments: /// new_node: Pointer to the new node to insert. - pub fn prepend(list: &Self, new_node: &Node) void { + pub fn prepend(list: *Self, new_node: *Node) void { if (list.first) |first| { // Insert before first. list.insertBefore(first, new_node); @@ -143,7 +143,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Arguments: /// node: Pointer to the node to be removed. - pub fn remove(list: &Self, node: &Node) void { + pub fn remove(list: *Self, node: *Node) void { if (node.prev) |prev_node| { // Intermediate node. prev_node.next = node.next; @@ -168,7 +168,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Returns: /// A pointer to the last node in the list. - pub fn pop(list: &Self) ?&Node { + pub fn pop(list: *Self) ?*Node { const last = list.last ?? return null; list.remove(last); return last; @@ -178,7 +178,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Returns: /// A pointer to the first node in the list. - pub fn popFirst(list: &Self) ?&Node { + pub fn popFirst(list: *Self) ?*Node { const first = list.first ?? return null; list.remove(first); return first; @@ -191,7 +191,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Returns: /// A pointer to the new node. - pub fn allocateNode(list: &Self, allocator: &Allocator) !&Node { + pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node { comptime assert(!isIntrusive()); return allocator.create(Node); } @@ -201,7 +201,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// Arguments: /// node: Pointer to the node to deallocate. /// allocator: Dynamic memory allocator. - pub fn destroyNode(list: &Self, node: &Node, allocator: &Allocator) void { + pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void { comptime assert(!isIntrusive()); allocator.destroy(node); } @@ -214,7 +214,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Returns: /// A pointer to the new node. - pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) !&Node { + pub fn createNode(list: *Self, data: *const T, allocator: *Allocator) !*Node { comptime assert(!isIntrusive()); var node = try list.allocateNode(allocator); node.* = Node.init(data); diff --git a/std/macho.zig b/std/macho.zig index 615569e4b4..e71ac76b1a 100644 --- a/std/macho.zig +++ b/std/macho.zig @@ -42,13 +42,13 @@ pub const Symbol = struct { name: []const u8, address: u64, - fn addressLessThan(lhs: &const Symbol, rhs: &const Symbol) bool { + fn addressLessThan(lhs: *const Symbol, rhs: *const Symbol) bool { return lhs.address < rhs.address; } }; pub const SymbolTable = struct { - allocator: &mem.Allocator, + allocator: *mem.Allocator, symbols: []const Symbol, strings: []const u8, @@ -56,7 +56,7 @@ pub const SymbolTable = struct { // Ideally we'd use _mh_execute_header because it's always at 0x100000000 // in the image but as it's located in a different section than executable // code, its displacement is different. - pub fn deinit(self: &SymbolTable) void { + pub fn deinit(self: *SymbolTable) void { self.allocator.free(self.symbols); self.symbols = []const Symbol{}; @@ -64,7 +64,7 @@ pub const SymbolTable = struct { self.strings = []const u8{}; } - pub fn search(self: &const SymbolTable, address: usize) ?&const Symbol { + pub fn search(self: *const SymbolTable, address: usize) ?*const Symbol { var min: usize = 0; var max: usize = self.symbols.len - 1; // Exclude sentinel. while (min < max) { @@ -83,7 +83,7 @@ pub const SymbolTable = struct { } }; -pub fn loadSymbols(allocator: &mem.Allocator, in: &io.FileInStream) !SymbolTable { +pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable { var file = in.file; try file.seekTo(0); @@ -160,13 +160,13 @@ pub fn loadSymbols(allocator: &mem.Allocator, in: &io.FileInStream) !SymbolTable }; } -fn readNoEof(in: &io.FileInStream, comptime T: type, result: []T) !void { +fn readNoEof(in: *io.FileInStream, comptime T: type, result: []T) !void { return in.stream.readNoEof(([]u8)(result)); } -fn readOneNoEof(in: &io.FileInStream, comptime T: type, result: &T) !void { +fn readOneNoEof(in: *io.FileInStream, comptime T: type, result: *T) !void { return readNoEof(in, T, result[0..1]); } -fn isSymbol(sym: &const Nlist64) bool { +fn isSymbol(sym: *const Nlist64) bool { return sym.n_value != 0 and sym.n_desc == 0; } diff --git a/std/math/complex/atan.zig b/std/math/complex/atan.zig index b7bbf930eb..9bfe5fe724 100644 --- a/std/math/complex/atan.zig +++ b/std/math/complex/atan.zig @@ -29,7 +29,7 @@ fn redupif32(x: f32) f32 { return ((x - u * DP1) - u * DP2) - t * DP3; } -fn atan32(z: &const Complex(f32)) Complex(f32) { +fn atan32(z: *const Complex(f32)) Complex(f32) { const maxnum = 1.0e38; const x = z.re; @@ -78,7 +78,7 @@ fn redupif64(x: f64) f64 { return ((x - u * DP1) - u * DP2) - t * DP3; } -fn atan64(z: &const Complex(f64)) Complex(f64) { +fn atan64(z: *const Complex(f64)) Complex(f64) { const maxnum = 1.0e308; const x = z.re; diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig index 96eac68556..c2f9a47b8d 100644 --- a/std/math/complex/cosh.zig +++ b/std/math/complex/cosh.zig @@ -15,7 +15,7 @@ pub fn cosh(z: var) Complex(@typeOf(z.re)) { }; } -fn cosh32(z: &const Complex(f32)) Complex(f32) { +fn cosh32(z: *const Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -78,7 +78,7 @@ fn cosh32(z: &const Complex(f32)) Complex(f32) { return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y)); } -fn cosh64(z: &const Complex(f64)) Complex(f64) { +fn cosh64(z: *const Complex(f64)) Complex(f64) { const x = z.re; const y = z.im; diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig index 8fe069a43d..44c354f246 100644 --- a/std/math/complex/exp.zig +++ b/std/math/complex/exp.zig @@ -16,7 +16,7 @@ pub fn exp(z: var) Complex(@typeOf(z.re)) { }; } -fn exp32(z: &const Complex(f32)) Complex(f32) { +fn exp32(z: *const Complex(f32)) Complex(f32) { @setFloatMode(this, @import("builtin").FloatMode.Strict); const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955 @@ -63,7 +63,7 @@ fn exp32(z: &const Complex(f32)) Complex(f32) { } } -fn exp64(z: &const Complex(f64)) Complex(f64) { +fn exp64(z: *const Complex(f64)) Complex(f64) { const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710 const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2 diff --git a/std/math/complex/index.zig b/std/math/complex/index.zig index 5902ffaa19..b00296beda 100644 --- a/std/math/complex/index.zig +++ b/std/math/complex/index.zig @@ -37,28 +37,28 @@ pub fn Complex(comptime T: type) type { }; } - pub fn add(self: &const Self, other: &const Self) Self { + pub fn add(self: *const Self, other: *const Self) Self { return Self{ .re = self.re + other.re, .im = self.im + other.im, }; } - pub fn sub(self: &const Self, other: &const Self) Self { + pub fn sub(self: *const Self, other: *const Self) Self { return Self{ .re = self.re - other.re, .im = self.im - other.im, }; } - pub fn mul(self: &const Self, other: &const Self) Self { + pub fn mul(self: *const Self, other: *const Self) Self { return Self{ .re = self.re * other.re - self.im * other.im, .im = self.im * other.re + self.re * other.im, }; } - pub fn div(self: &const Self, other: &const Self) Self { + pub fn div(self: *const Self, other: *const Self) Self { const re_num = self.re * other.re + self.im * other.im; const im_num = self.im * other.re - self.re * other.im; const den = other.re * other.re + other.im * other.im; @@ -69,14 +69,14 @@ pub fn Complex(comptime T: type) type { }; } - pub fn conjugate(self: &const Self) Self { + pub fn conjugate(self: *const Self) Self { return Self{ .re = self.re, .im = -self.im, }; } - pub fn reciprocal(self: &const Self) Self { + pub fn reciprocal(self: *const Self) Self { const m = self.re * self.re + self.im * self.im; return Self{ .re = self.re / m, @@ -84,7 +84,7 @@ pub fn Complex(comptime T: type) type { }; } - pub fn magnitude(self: &const Self) T { + pub fn magnitude(self: *const Self) T { return math.sqrt(self.re * self.re + self.im * self.im); } }; diff --git a/std/math/complex/ldexp.zig b/std/math/complex/ldexp.zig index 7ebefff40c..a56c2ef2eb 100644 --- a/std/math/complex/ldexp.zig +++ b/std/math/complex/ldexp.zig @@ -14,7 +14,7 @@ pub fn ldexp_cexp(z: var, expt: i32) Complex(@typeOf(z.re)) { }; } -fn frexp_exp32(x: f32, expt: &i32) f32 { +fn frexp_exp32(x: f32, expt: *i32) f32 { const k = 235; // reduction constant const kln2 = 162.88958740; // k * ln2 @@ -24,7 +24,7 @@ fn frexp_exp32(x: f32, expt: &i32) f32 { return @bitCast(f32, (hx & 0x7fffff) | ((0x7f + 127) << 23)); } -fn ldexp_cexp32(z: &const Complex(f32), expt: i32) Complex(f32) { +fn ldexp_cexp32(z: *const Complex(f32), expt: i32) Complex(f32) { var ex_expt: i32 = undefined; const exp_x = frexp_exp32(z.re, &ex_expt); const exptf = expt + ex_expt; @@ -38,7 +38,7 @@ fn ldexp_cexp32(z: &const Complex(f32), expt: i32) Complex(f32) { return Complex(f32).new(math.cos(z.im) * exp_x * scale1 * scale2, math.sin(z.im) * exp_x * scale1 * scale2); } -fn frexp_exp64(x: f64, expt: &i32) f64 { +fn frexp_exp64(x: f64, expt: *i32) f64 { const k = 1799; // reduction constant const kln2 = 1246.97177782734161156; // k * ln2 @@ -54,7 +54,7 @@ fn frexp_exp64(x: f64, expt: &i32) f64 { return @bitCast(f64, (u64(high_word) << 32) | lx); } -fn ldexp_cexp64(z: &const Complex(f64), expt: i32) Complex(f64) { +fn ldexp_cexp64(z: *const Complex(f64), expt: i32) Complex(f64) { var ex_expt: i32 = undefined; const exp_x = frexp_exp64(z.re, &ex_expt); const exptf = i64(expt + ex_expt); diff --git a/std/math/complex/pow.zig b/std/math/complex/pow.zig index bef9fde542..4c2cd9cf34 100644 --- a/std/math/complex/pow.zig +++ b/std/math/complex/pow.zig @@ -4,7 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; -pub fn pow(comptime T: type, z: &const T, c: &const T) T { +pub fn pow(comptime T: type, z: *const T, c: *const T) T { const p = cmath.log(z); const q = c.mul(p); return cmath.exp(q); diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig index 09a62ca058..3d196bfd50 100644 --- a/std/math/complex/sinh.zig +++ b/std/math/complex/sinh.zig @@ -15,7 +15,7 @@ pub fn sinh(z: var) Complex(@typeOf(z.re)) { }; } -fn sinh32(z: &const Complex(f32)) Complex(f32) { +fn sinh32(z: *const Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -78,7 +78,7 @@ fn sinh32(z: &const Complex(f32)) Complex(f32) { return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y)); } -fn sinh64(z: &const Complex(f64)) Complex(f64) { +fn sinh64(z: *const Complex(f64)) Complex(f64) { const x = z.re; const y = z.im; diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig index afda69f7c9..d4f5a67528 100644 --- a/std/math/complex/sqrt.zig +++ b/std/math/complex/sqrt.zig @@ -15,7 +15,7 @@ pub fn sqrt(z: var) Complex(@typeOf(z.re)) { }; } -fn sqrt32(z: &const Complex(f32)) Complex(f32) { +fn sqrt32(z: *const Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -57,7 +57,7 @@ fn sqrt32(z: &const Complex(f32)) Complex(f32) { } } -fn sqrt64(z: &const Complex(f64)) Complex(f64) { +fn sqrt64(z: *const Complex(f64)) Complex(f64) { // may encounter overflow for im,re >= DBL_MAX / (1 + sqrt(2)) const threshold = 0x1.a827999fcef32p+1022; diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig index 34250b1b4a..1d754838a3 100644 --- a/std/math/complex/tanh.zig +++ b/std/math/complex/tanh.zig @@ -13,7 +13,7 @@ pub fn tanh(z: var) Complex(@typeOf(z.re)) { }; } -fn tanh32(z: &const Complex(f32)) Complex(f32) { +fn tanh32(z: *const Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -51,7 +51,7 @@ fn tanh32(z: &const Complex(f32)) Complex(f32) { return Complex(f32).new((beta * rho * s) / den, t / den); } -fn tanh64(z: &const Complex(f64)) Complex(f64) { +fn tanh64(z: *const Complex(f64)) Complex(f64) { const x = z.re; const y = z.im; diff --git a/std/math/hypot.zig b/std/math/hypot.zig index fe0de3a1ea..494df22ba6 100644 --- a/std/math/hypot.zig +++ b/std/math/hypot.zig @@ -52,7 +52,7 @@ fn hypot32(x: f32, y: f32) f32 { return z * math.sqrt(f32(f64(x) * x + f64(y) * y)); } -fn sq(hi: &f64, lo: &f64, x: f64) void { +fn sq(hi: *f64, lo: *f64, x: f64) void { const split: f64 = 0x1.0p27 + 1.0; const xc = x * split; const xh = x - xc + xc; diff --git a/std/math/index.zig b/std/math/index.zig index 847e972500..33bc1082f7 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -46,12 +46,12 @@ pub fn forceEval(value: var) void { switch (T) { f32 => { var x: f32 = undefined; - const p = @ptrCast(&volatile f32, &x); + const p = @ptrCast(*volatile f32, &x); p.* = x; }, f64 => { var x: f64 = undefined; - const p = @ptrCast(&volatile f64, &x); + const p = @ptrCast(*volatile f64, &x); p.* = x; }, else => { diff --git a/std/mem.zig b/std/mem.zig index f4696cff9f..aec24e8491 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -13,7 +13,7 @@ pub const Allocator = struct { /// The returned newly allocated memory is undefined. /// `alignment` is guaranteed to be >= 1 /// `alignment` is guaranteed to be a power of 2 - allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8, + allocFn: fn (self: *Allocator, byte_count: usize, alignment: u29) Error![]u8, /// If `new_byte_count > old_mem.len`: /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn. @@ -26,22 +26,22 @@ pub const Allocator = struct { /// The returned newly allocated memory is undefined. /// `alignment` is guaranteed to be >= 1 /// `alignment` is guaranteed to be a power of 2 - reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, + reallocFn: fn (self: *Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` - freeFn: fn (self: &Allocator, old_mem: []u8) void, + freeFn: fn (self: *Allocator, old_mem: []u8) void, - fn create(self: &Allocator, comptime T: type) !&T { - if (@sizeOf(T) == 0) return &{}; + fn create(self: *Allocator, comptime T: type) !*T { + if (@sizeOf(T) == 0) return *{}; const slice = try self.alloc(T, 1); return &slice[0]; } // TODO once #733 is solved, this will replace create - fn construct(self: &Allocator, init: var) t: { + fn construct(self: *Allocator, init: var) t: { // TODO this is a workaround for type getting parsed as Error!&const T const T = @typeOf(init).Child; - break :t Error!&T; + break :t Error!*T; } { const T = @typeOf(init).Child; if (@sizeOf(T) == 0) return &{}; @@ -51,17 +51,17 @@ pub const Allocator = struct { return ptr; } - fn destroy(self: &Allocator, ptr: var) void { + fn destroy(self: *Allocator, ptr: var) void { self.free(ptr[0..1]); } - fn alloc(self: &Allocator, comptime T: type, n: usize) ![]T { + fn alloc(self: *Allocator, comptime T: type, n: usize) ![]T { return self.alignedAlloc(T, @alignOf(T), n); } - fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { + fn alignedAlloc(self: *Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { if (n == 0) { - return (&align(alignment) T)(undefined)[0..0]; + return (*align(alignment) T)(undefined)[0..0]; } const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; const byte_slice = try self.allocFn(self, byte_count, alignment); @@ -73,17 +73,17 @@ pub const Allocator = struct { return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); } - fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) ![]T { + fn realloc(self: *Allocator, comptime T: type, old_mem: []T, n: usize) ![]T { return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } - fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { + fn alignedRealloc(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { if (old_mem.len == 0) { return self.alloc(T, n); } if (n == 0) { self.free(old_mem); - return (&align(alignment) T)(undefined)[0..0]; + return (*align(alignment) T)(undefined)[0..0]; } const old_byte_slice = ([]u8)(old_mem); @@ -102,11 +102,11 @@ pub const Allocator = struct { /// Reallocate, but `n` must be less than or equal to `old_mem.len`. /// Unlike `realloc`, this function cannot fail. /// Shrinking to 0 is the same as calling `free`. - fn shrink(self: &Allocator, comptime T: type, old_mem: []T, n: usize) []T { + fn shrink(self: *Allocator, comptime T: type, old_mem: []T, n: usize) []T { return self.alignedShrink(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } - fn alignedShrink(self: &Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) []align(alignment) T { + fn alignedShrink(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) []align(alignment) T { if (n == 0) { self.free(old_mem); return old_mem[0..0]; @@ -123,10 +123,10 @@ pub const Allocator = struct { return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); } - fn free(self: &Allocator, memory: var) void { + fn free(self: *Allocator, memory: var) void { const bytes = ([]const u8)(memory); if (bytes.len == 0) return; - const non_const_ptr = @intToPtr(&u8, @ptrToInt(bytes.ptr)); + const non_const_ptr = @intToPtr(*u8, @ptrToInt(bytes.ptr)); self.freeFn(self, non_const_ptr[0..bytes.len]); } }; @@ -186,7 +186,7 @@ pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool { } /// Copies ::m to newly allocated memory. Caller is responsible to free it. -pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T { +pub fn dupe(allocator: *Allocator, comptime T: type, m: []const T) ![]T { const new_buf = try allocator.alloc(T, m.len); copy(T, new_buf, m); return new_buf; @@ -457,7 +457,7 @@ pub const SplitIterator = struct { split_bytes: []const u8, index: usize, - pub fn next(self: &SplitIterator) ?[]const u8 { + pub fn next(self: *SplitIterator) ?[]const u8 { // move to beginning of token while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} const start = self.index; @@ -473,14 +473,14 @@ pub const SplitIterator = struct { } /// Returns a slice of the remaining bytes. Does not affect iterator state. - pub fn rest(self: &const SplitIterator) []const u8 { + pub fn rest(self: *const SplitIterator) []const u8 { // move to beginning of token var index: usize = self.index; while (index < self.buffer.len and self.isSplitByte(self.buffer[index])) : (index += 1) {} return self.buffer[index..]; } - fn isSplitByte(self: &const SplitIterator, byte: u8) bool { + fn isSplitByte(self: *const SplitIterator, byte: u8) bool { for (self.split_bytes) |split_byte| { if (byte == split_byte) { return true; @@ -492,7 +492,7 @@ pub const SplitIterator = struct { /// Naively combines a series of strings with a separator. /// Allocates memory for the result, which must be freed by the caller. -pub fn join(allocator: &Allocator, sep: u8, strings: ...) ![]u8 { +pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 { comptime assert(strings.len >= 1); var total_strings_len: usize = strings.len; // 1 sep per string { @@ -649,7 +649,7 @@ test "mem.max" { assert(max(u8, "abcdefg") == 'g'); } -pub fn swap(comptime T: type, a: &T, b: &T) void { +pub fn swap(comptime T: type, a: *T, b: *T) void { const tmp = a.*; a.* = b.*; b.* = tmp; diff --git a/std/net.zig b/std/net.zig index 3af4e0b525..bfe4b1c2a0 100644 --- a/std/net.zig +++ b/std/net.zig @@ -31,7 +31,7 @@ pub const Address = struct { }; } - pub fn initIp6(ip6: &const Ip6Addr, port: u16) Address { + pub fn initIp6(ip6: *const Ip6Addr, port: u16) Address { return Address{ .family = posix.AF_INET6, .os_addr = posix.sockaddr{ @@ -46,15 +46,15 @@ pub const Address = struct { }; } - pub fn initPosix(addr: &const posix.sockaddr) Address { + pub fn initPosix(addr: *const posix.sockaddr) Address { return Address{ .os_addr = addr.* }; } - pub fn format(self: &const Address, out_stream: var) !void { + pub fn format(self: *const Address, out_stream: var) !void { switch (self.os_addr.in.family) { posix.AF_INET => { const native_endian_port = std.mem.endianSwapIfLe(u16, self.os_addr.in.port); - const bytes = ([]const u8)((&self.os_addr.in.addr)[0..1]); + const bytes = ([]const u8)((*self.os_addr.in.addr)[0..1]); try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port); }, posix.AF_INET6 => { diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 51f1bd96e5..30a2fd1408 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -20,7 +20,7 @@ pub const ChildProcess = struct { pub handle: if (is_windows) windows.HANDLE else void, pub thread_handle: if (is_windows) windows.HANDLE else void, - pub allocator: &mem.Allocator, + pub allocator: *mem.Allocator, pub stdin: ?os.File, pub stdout: ?os.File, @@ -31,7 +31,7 @@ pub const ChildProcess = struct { pub argv: []const []const u8, /// Leave as null to use the current env map using the supplied allocator. - pub env_map: ?&const BufMap, + pub env_map: ?*const BufMap, pub stdin_behavior: StdIo, pub stdout_behavior: StdIo, @@ -47,7 +47,7 @@ pub const ChildProcess = struct { pub cwd: ?[]const u8, err_pipe: if (is_windows) void else [2]i32, - llnode: if (is_windows) void else LinkedList(&ChildProcess).Node, + llnode: if (is_windows) void else LinkedList(*ChildProcess).Node, pub const SpawnError = error{ ProcessFdQuotaExceeded, @@ -84,7 +84,7 @@ pub const ChildProcess = struct { /// First argument in argv is the executable. /// On success must call deinit. - pub fn init(argv: []const []const u8, allocator: &mem.Allocator) !&ChildProcess { + pub fn init(argv: []const []const u8, allocator: *mem.Allocator) !*ChildProcess { const child = try allocator.create(ChildProcess); errdefer allocator.destroy(child); @@ -114,14 +114,14 @@ pub const ChildProcess = struct { return child; } - pub fn setUserName(self: &ChildProcess, name: []const u8) !void { + pub fn setUserName(self: *ChildProcess, name: []const u8) !void { const user_info = try os.getUserInfo(name); self.uid = user_info.uid; self.gid = user_info.gid; } /// On success must call `kill` or `wait`. - pub fn spawn(self: &ChildProcess) !void { + pub fn spawn(self: *ChildProcess) !void { if (is_windows) { return self.spawnWindows(); } else { @@ -129,13 +129,13 @@ pub const ChildProcess = struct { } } - pub fn spawnAndWait(self: &ChildProcess) !Term { + pub fn spawnAndWait(self: *ChildProcess) !Term { try self.spawn(); return self.wait(); } /// Forcibly terminates child process and then cleans up all resources. - pub fn kill(self: &ChildProcess) !Term { + pub fn kill(self: *ChildProcess) !Term { if (is_windows) { return self.killWindows(1); } else { @@ -143,7 +143,7 @@ pub const ChildProcess = struct { } } - pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) !Term { + pub fn killWindows(self: *ChildProcess, exit_code: windows.UINT) !Term { if (self.term) |term| { self.cleanupStreams(); return term; @@ -159,7 +159,7 @@ pub const ChildProcess = struct { return ??self.term; } - pub fn killPosix(self: &ChildProcess) !Term { + pub fn killPosix(self: *ChildProcess) !Term { if (self.term) |term| { self.cleanupStreams(); return term; @@ -179,7 +179,7 @@ pub const ChildProcess = struct { } /// Blocks until child process terminates and then cleans up all resources. - pub fn wait(self: &ChildProcess) !Term { + pub fn wait(self: *ChildProcess) !Term { if (is_windows) { return self.waitWindows(); } else { @@ -195,7 +195,7 @@ pub const ChildProcess = struct { /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. /// If it succeeds, the caller owns result.stdout and result.stderr memory. - pub fn exec(allocator: &mem.Allocator, argv: []const []const u8, cwd: ?[]const u8, env_map: ?&const BufMap, max_output_size: usize) !ExecResult { + pub fn exec(allocator: *mem.Allocator, argv: []const []const u8, cwd: ?[]const u8, env_map: ?*const BufMap, max_output_size: usize) !ExecResult { const child = try ChildProcess.init(argv, allocator); defer child.deinit(); @@ -225,7 +225,7 @@ pub const ChildProcess = struct { }; } - fn waitWindows(self: &ChildProcess) !Term { + fn waitWindows(self: *ChildProcess) !Term { if (self.term) |term| { self.cleanupStreams(); return term; @@ -235,7 +235,7 @@ pub const ChildProcess = struct { return ??self.term; } - fn waitPosix(self: &ChildProcess) !Term { + fn waitPosix(self: *ChildProcess) !Term { if (self.term) |term| { self.cleanupStreams(); return term; @@ -245,16 +245,16 @@ pub const ChildProcess = struct { return ??self.term; } - pub fn deinit(self: &ChildProcess) void { + pub fn deinit(self: *ChildProcess) void { self.allocator.destroy(self); } - fn waitUnwrappedWindows(self: &ChildProcess) !void { + fn waitUnwrappedWindows(self: *ChildProcess) !void { const result = os.windowsWaitSingle(self.handle, windows.INFINITE); self.term = (SpawnError!Term)(x: { var exit_code: windows.DWORD = undefined; - if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) { + if (windows.GetExitCodeProcess(self.handle, *exit_code) == 0) { break :x Term{ .Unknown = 0 }; } else { break :x Term{ .Exited = @bitCast(i32, exit_code) }; @@ -267,7 +267,7 @@ pub const ChildProcess = struct { return result; } - fn waitUnwrapped(self: &ChildProcess) void { + fn waitUnwrapped(self: *ChildProcess) void { var status: i32 = undefined; while (true) { const err = posix.getErrno(posix.waitpid(self.pid, &status, 0)); @@ -283,11 +283,11 @@ pub const ChildProcess = struct { } } - fn handleWaitResult(self: &ChildProcess, status: i32) void { + fn handleWaitResult(self: *ChildProcess, status: i32) void { self.term = self.cleanupAfterWait(status); } - fn cleanupStreams(self: &ChildProcess) void { + fn cleanupStreams(self: *ChildProcess) void { if (self.stdin) |*stdin| { stdin.close(); self.stdin = null; @@ -302,7 +302,7 @@ pub const ChildProcess = struct { } } - fn cleanupAfterWait(self: &ChildProcess, status: i32) !Term { + fn cleanupAfterWait(self: *ChildProcess, status: i32) !Term { defer { os.close(self.err_pipe[0]); os.close(self.err_pipe[1]); @@ -335,7 +335,7 @@ pub const ChildProcess = struct { Term{ .Unknown = status }; } - fn spawnPosix(self: &ChildProcess) !void { + fn spawnPosix(self: *ChildProcess) !void { const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try makePipe() else undefined; errdefer if (self.stdin_behavior == StdIo.Pipe) { destroyPipe(stdin_pipe); @@ -432,7 +432,7 @@ pub const ChildProcess = struct { self.pid = pid; self.err_pipe = err_pipe; - self.llnode = LinkedList(&ChildProcess).Node.init(self); + self.llnode = LinkedList(*ChildProcess).Node.init(self); self.term = null; if (self.stdin_behavior == StdIo.Pipe) { @@ -446,7 +446,7 @@ pub const ChildProcess = struct { } } - fn spawnWindows(self: &ChildProcess) !void { + fn spawnWindows(self: *ChildProcess) !void { const saAttr = windows.SECURITY_ATTRIBUTES{ .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), .bInheritHandle = windows.TRUE, @@ -639,8 +639,8 @@ pub const ChildProcess = struct { } }; -fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?&u8, lpStartupInfo: &windows.STARTUPINFOA, lpProcessInformation: &windows.PROCESS_INFORMATION) !void { - if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?&c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) { +fn windowsCreateProcess(app_name: *u8, cmd_line: *u8, envp_ptr: ?*u8, cwd_ptr: ?*u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void { + if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.FILE_NOT_FOUND, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, @@ -653,7 +653,7 @@ fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ? /// Caller must dealloc. /// Guarantees a null byte at result[result.len]. -fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) ![]u8 { +fn windowsCreateCommandLine(allocator: *mem.Allocator, argv: []const []const u8) ![]u8 { var buf = try Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -698,7 +698,7 @@ fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { // a namespace field lookup const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES; -fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void { +fn windowsMakePipe(rd: *windows.HANDLE, wr: *windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void { if (windows.CreatePipe(rd, wr, sattr, 0) == 0) { const err = windows.GetLastError(); return switch (err) { @@ -716,7 +716,7 @@ fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.D } } -fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void { +fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void { var rd_h: windows.HANDLE = undefined; var wr_h: windows.HANDLE = undefined; try windowsMakePipe(&rd_h, &wr_h, sattr); @@ -726,7 +726,7 @@ fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const S wr.* = wr_h; } -fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void { +fn windowsMakePipeOut(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void { var rd_h: windows.HANDLE = undefined; var wr_h: windows.HANDLE = undefined; try windowsMakePipe(&rd_h, &wr_h, sattr); @@ -748,7 +748,7 @@ fn makePipe() ![2]i32 { return fds; } -fn destroyPipe(pipe: &const [2]i32) void { +fn destroyPipe(pipe: *const [2]i32) void { os.close((pipe.*)[0]); os.close((pipe.*)[1]); } diff --git a/std/os/darwin.zig b/std/os/darwin.zig index a3fc230ac5..77e8b6bb6a 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -309,7 +309,7 @@ pub fn isatty(fd: i32) bool { return c.isatty(fd) != 0; } -pub fn fstat(fd: i32, buf: &c.Stat) usize { +pub fn fstat(fd: i32, buf: *c.Stat) usize { return errnoWrap(c.@"fstat$INODE64"(fd, buf)); } @@ -317,7 +317,7 @@ pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { return errnoWrap(c.lseek(fd, offset, whence)); } -pub fn open(path: &const u8, flags: u32, mode: usize) usize { +pub fn open(path: *const u8, flags: u32, mode: usize) usize { return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); } @@ -325,79 +325,79 @@ pub fn raise(sig: i32) usize { return errnoWrap(c.raise(sig)); } -pub fn read(fd: i32, buf: &u8, nbyte: usize) usize { - return errnoWrap(c.read(fd, @ptrCast(&c_void, buf), nbyte)); +pub fn read(fd: i32, buf: *u8, nbyte: usize) usize { + return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); } -pub fn stat(noalias path: &const u8, noalias buf: &stat) usize { +pub fn stat(noalias path: *const u8, noalias buf: *stat) usize { return errnoWrap(c.stat(path, buf)); } -pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize { - return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte)); +pub fn write(fd: i32, buf: *const u8, nbyte: usize) usize { + return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); } -pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap(@ptrCast(&c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); +pub fn mmap(address: ?*u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { + const ptr_result = c.mmap(@ptrCast(*c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); return errnoWrap(isize_result); } pub fn munmap(address: usize, length: usize) usize { - return errnoWrap(c.munmap(@intToPtr(&c_void, address), length)); + return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); } -pub fn unlink(path: &const u8) usize { +pub fn unlink(path: *const u8) usize { return errnoWrap(c.unlink(path)); } -pub fn getcwd(buf: &u8, size: usize) usize { +pub fn getcwd(buf: *u8, size: usize) usize { return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; } -pub fn waitpid(pid: i32, status: &i32, options: u32) usize { +pub fn waitpid(pid: i32, status: *i32, options: u32) usize { comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.waitpid(pid, @ptrCast(&c_int, status), @bitCast(c_int, options))); + return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); } pub fn fork() usize { return errnoWrap(c.fork()); } -pub fn access(path: &const u8, mode: u32) usize { +pub fn access(path: *const u8, mode: u32) usize { return errnoWrap(c.access(path, mode)); } -pub fn pipe(fds: &[2]i32) usize { +pub fn pipe(fds: *[2]i32) usize { comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.pipe(@ptrCast(&c_int, fds))); + return errnoWrap(c.pipe(@ptrCast(*c_int, fds))); } -pub fn getdirentries64(fd: i32, buf_ptr: &u8, buf_len: usize, basep: &i64) usize { +pub fn getdirentries64(fd: i32, buf_ptr: *u8, buf_len: usize, basep: *i64) usize { return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep))); } -pub fn mkdir(path: &const u8, mode: u32) usize { +pub fn mkdir(path: *const u8, mode: u32) usize { return errnoWrap(c.mkdir(path, mode)); } -pub fn symlink(existing: &const u8, new: &const u8) usize { +pub fn symlink(existing: *const u8, new: *const u8) usize { return errnoWrap(c.symlink(existing, new)); } -pub fn rename(old: &const u8, new: &const u8) usize { +pub fn rename(old: *const u8, new: *const u8) usize { return errnoWrap(c.rename(old, new)); } -pub fn rmdir(path: &const u8) usize { +pub fn rmdir(path: *const u8) usize { return errnoWrap(c.rmdir(path)); } -pub fn chdir(path: &const u8) usize { +pub fn chdir(path: *const u8) usize { return errnoWrap(c.chdir(path)); } -pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize { +pub fn execve(path: *const u8, argv: *const ?*const u8, envp: *const ?*const u8) usize { return errnoWrap(c.execve(path, argv, envp)); } @@ -405,19 +405,19 @@ pub fn dup2(old: i32, new: i32) usize { return errnoWrap(c.dup2(old, new)); } -pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) usize { +pub fn readlink(noalias path: *const u8, noalias buf_ptr: *u8, buf_len: usize) usize { return errnoWrap(c.readlink(path, buf_ptr, buf_len)); } -pub fn gettimeofday(tv: ?&timeval, tz: ?&timezone) usize { +pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) usize { return errnoWrap(c.gettimeofday(tv, tz)); } -pub fn nanosleep(req: &const timespec, rem: ?×pec) usize { +pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { return errnoWrap(c.nanosleep(req, rem)); } -pub fn realpath(noalias filename: &const u8, noalias resolved_name: &u8) usize { +pub fn realpath(noalias filename: *const u8, noalias resolved_name: *u8) usize { return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; } @@ -429,11 +429,11 @@ pub fn setregid(rgid: u32, egid: u32) usize { return errnoWrap(c.setregid(rgid, egid)); } -pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize { +pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { return errnoWrap(c.sigprocmask(@bitCast(c_int, flags), set, oldset)); } -pub fn sigaction(sig: u5, noalias act: &const Sigaction, noalias oact: ?&Sigaction) usize { +pub fn sigaction(sig: u5, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { assert(sig != SIGKILL); assert(sig != SIGSTOP); var cact = c.Sigaction{ @@ -442,7 +442,7 @@ pub fn sigaction(sig: u5, noalias act: &const Sigaction, noalias oact: ?&Sigacti .sa_mask = act.mask, }; var coact: c.Sigaction = undefined; - const result = errnoWrap(c.sigaction(sig, &cact, &coact)); + const result = errnoWrap(c.sigaction(sig, *cact, *coact)); if (result != 0) { return result; } @@ -473,7 +473,7 @@ pub const Sigaction = struct { flags: u32, }; -pub fn sigaddset(set: &sigset_t, signo: u5) void { +pub fn sigaddset(set: *sigset_t, signo: u5) void { set.* |= u32(1) << (signo - 1); } diff --git a/std/os/file.zig b/std/os/file.zig index c07e2c5c8b..d943da30ca 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -19,7 +19,7 @@ pub const File = struct { /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. /// Call close to clean up. - pub fn openRead(allocator: &mem.Allocator, path: []const u8) OpenError!File { + pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File { if (is_posix) { const flags = posix.O_LARGEFILE | posix.O_RDONLY; const fd = try os.posixOpen(allocator, path, flags, 0); @@ -40,7 +40,7 @@ pub const File = struct { } /// Calls `openWriteMode` with os.default_file_mode for the mode. - pub fn openWrite(allocator: &mem.Allocator, path: []const u8) OpenError!File { + pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File { return openWriteMode(allocator, path, os.default_file_mode); } @@ -48,7 +48,7 @@ pub const File = struct { /// If a file already exists in the destination it will be truncated. /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. /// Call close to clean up. - pub fn openWriteMode(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { + pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { if (is_posix) { const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC; const fd = try os.posixOpen(allocator, path, flags, file_mode); @@ -72,7 +72,7 @@ pub const File = struct { /// If a file already exists in the destination this returns OpenError.PathAlreadyExists /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. /// Call close to clean up. - pub fn openWriteNoClobber(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { + pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { if (is_posix) { const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL; const fd = try os.posixOpen(allocator, path, flags, file_mode); @@ -96,7 +96,7 @@ pub const File = struct { return File{ .handle = handle }; } - pub fn access(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool { + pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool { const path_with_null = try std.cstr.addNullByte(allocator, path); defer allocator.free(path_with_null); @@ -140,17 +140,17 @@ pub const File = struct { /// Upon success, the stream is in an uninitialized state. To continue using it, /// you must use the open() function. - pub fn close(self: &File) void { + pub fn close(self: *File) void { os.close(self.handle); self.handle = undefined; } /// Calls `os.isTty` on `self.handle`. - pub fn isTty(self: &File) bool { + pub fn isTty(self: *File) bool { return os.isTty(self.handle); } - pub fn seekForward(self: &File, amount: isize) !void { + pub fn seekForward(self: *File, amount: isize) !void { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const result = posix.lseek(self.handle, amount, posix.SEEK_CUR); @@ -179,7 +179,7 @@ pub const File = struct { } } - pub fn seekTo(self: &File, pos: usize) !void { + pub fn seekTo(self: *File, pos: usize) !void { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const ipos = try math.cast(isize, pos); @@ -210,7 +210,7 @@ pub const File = struct { } } - pub fn getPos(self: &File) !usize { + pub fn getPos(self: *File) !usize { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const result = posix.lseek(self.handle, 0, posix.SEEK_CUR); @@ -229,7 +229,7 @@ pub const File = struct { }, Os.windows => { var pos: windows.LARGE_INTEGER = undefined; - if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) { + if (windows.SetFilePointerEx(self.handle, 0, *pos, windows.FILE_CURRENT) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_PARAMETER => error.BadFd, @@ -250,7 +250,7 @@ pub const File = struct { } } - pub fn getEndPos(self: &File) !usize { + pub fn getEndPos(self: *File) !usize { if (is_posix) { var stat: posix.Stat = undefined; const err = posix.getErrno(posix.fstat(self.handle, &stat)); @@ -285,7 +285,7 @@ pub const File = struct { Unexpected, }; - fn mode(self: &File) ModeError!os.FileMode { + fn mode(self: *File) ModeError!os.FileMode { if (is_posix) { var stat: posix.Stat = undefined; const err = posix.getErrno(posix.fstat(self.handle, &stat)); @@ -309,7 +309,7 @@ pub const File = struct { pub const ReadError = error{}; - pub fn read(self: &File, buffer: []u8) !usize { + pub fn read(self: *File, buffer: []u8) !usize { if (is_posix) { var index: usize = 0; while (index < buffer.len) { @@ -334,7 +334,7 @@ pub const File = struct { while (index < buffer.len) { const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); var amt_read: windows.DWORD = undefined; - if (windows.ReadFile(self.handle, @ptrCast(&c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) { + if (windows.ReadFile(self.handle, @ptrCast(*c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.OPERATION_ABORTED => continue, @@ -353,7 +353,7 @@ pub const File = struct { pub const WriteError = os.WindowsWriteError || os.PosixWriteError; - fn write(self: &File, bytes: []const u8) WriteError!void { + fn write(self: *File, bytes: []const u8) WriteError!void { if (is_posix) { try os.posixWrite(self.handle, bytes); } else if (is_windows) { diff --git a/std/os/get_user_id.zig b/std/os/get_user_id.zig index 2a15e1d495..c0c1b1cc4b 100644 --- a/std/os/get_user_id.zig +++ b/std/os/get_user_id.zig @@ -77,8 +77,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { '0'...'9' => byte - '0', else => return error.CorruptPasswordFile, }; - if (@mulWithOverflow(u32, uid, 10, &uid)) return error.CorruptPasswordFile; - if (@addWithOverflow(u32, uid, digit, &uid)) return error.CorruptPasswordFile; + if (@mulWithOverflow(u32, uid, 10, *uid)) return error.CorruptPasswordFile; + if (@addWithOverflow(u32, uid, digit, *uid)) return error.CorruptPasswordFile; }, }, State.ReadGroupId => switch (byte) { @@ -93,8 +93,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { '0'...'9' => byte - '0', else => return error.CorruptPasswordFile, }; - if (@mulWithOverflow(u32, gid, 10, &gid)) return error.CorruptPasswordFile; - if (@addWithOverflow(u32, gid, digit, &gid)) return error.CorruptPasswordFile; + if (@mulWithOverflow(u32, gid, 10, *gid)) return error.CorruptPasswordFile; + if (@addWithOverflow(u32, gid, digit, *gid)) return error.CorruptPasswordFile; }, }, } diff --git a/std/os/index.zig b/std/os/index.zig index 70e654bcd9..ff638c670b 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -321,14 +321,14 @@ pub const PosixOpenError = error{ /// ::file_path needs to be copied in memory to add a null terminating byte. /// Calls POSIX open, keeps trying if it gets interrupted, and translates /// the return value into zig errors. -pub fn posixOpen(allocator: &Allocator, file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 { +pub fn posixOpen(allocator: *Allocator, file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 { const path_with_null = try cstr.addNullByte(allocator, file_path); defer allocator.free(path_with_null); return posixOpenC(path_with_null.ptr, flags, perm); } -pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 { +pub fn posixOpenC(file_path: *const u8, flags: u32, perm: usize) !i32 { while (true) { const result = posix.open(file_path, flags, perm); const err = posix.getErrno(result); @@ -374,10 +374,10 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void { } } -pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) ![]?&u8 { +pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?*u8 { const envp_count = env_map.count(); - const envp_buf = try allocator.alloc(?&u8, envp_count + 1); - mem.set(?&u8, envp_buf, null); + const envp_buf = try allocator.alloc(?*u8, envp_count + 1); + mem.set(?*u8, envp_buf, null); errdefer freeNullDelimitedEnvMap(allocator, envp_buf); { var it = env_map.iterator(); @@ -397,7 +397,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) return envp_buf; } -pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void { +pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?*u8) void { for (envp_buf) |env| { const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; allocator.free(env_buf); @@ -410,9 +410,9 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void { /// pointers after the args and after the environment variables. /// `argv[0]` is the executable path. /// This function also uses the PATH environment variable to get the full path to the executable. -pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, allocator: &Allocator) !void { - const argv_buf = try allocator.alloc(?&u8, argv.len + 1); - mem.set(?&u8, argv_buf, null); +pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: *Allocator) !void { + const argv_buf = try allocator.alloc(?*u8, argv.len + 1); + mem.set(?*u8, argv_buf, null); defer { for (argv_buf) |arg| { const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break; @@ -494,10 +494,10 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { } pub var linux_aux_raw = []usize{0} ** 38; -pub var posix_environ_raw: []&u8 = undefined; +pub var posix_environ_raw: []*u8 = undefined; /// Caller must free result when done. -pub fn getEnvMap(allocator: &Allocator) !BufMap { +pub fn getEnvMap(allocator: *Allocator) !BufMap { var result = BufMap.init(allocator); errdefer result.deinit(); @@ -557,7 +557,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 { } /// Caller must free returned memory. -pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) ![]u8 { +pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 { if (is_windows) { const key_with_null = try cstr.addNullByte(allocator, key); defer allocator.free(key_with_null); @@ -591,7 +591,7 @@ pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) ![]u8 { } /// Caller must free the returned memory. -pub fn getCwd(allocator: &Allocator) ![]u8 { +pub fn getCwd(allocator: *Allocator) ![]u8 { switch (builtin.os) { Os.windows => { var buf = try allocator.alloc(u8, 256); @@ -640,7 +640,7 @@ test "os.getCwd" { pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; -pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void { +pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void { if (is_windows) { return symLinkWindows(allocator, existing_path, new_path); } else { @@ -653,7 +653,7 @@ pub const WindowsSymLinkError = error{ Unexpected, }; -pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { +pub fn symLinkWindows(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { const existing_with_null = try cstr.addNullByte(allocator, existing_path); defer allocator.free(existing_with_null); const new_with_null = try cstr.addNullByte(allocator, new_path); @@ -683,7 +683,7 @@ pub const PosixSymLinkError = error{ Unexpected, }; -pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { +pub fn symLinkPosix(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2); defer allocator.free(full_buf); @@ -718,7 +718,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: // here we replace the standard +/ with -_ so that it can be used in a file name const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); -pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { +pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { if (symLink(allocator, existing_path, new_path)) { return; } else |err| switch (err) { @@ -746,7 +746,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: } } -pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void { +pub fn deleteFile(allocator: *Allocator, file_path: []const u8) !void { if (builtin.os == Os.windows) { return deleteFileWindows(allocator, file_path); } else { @@ -754,7 +754,7 @@ pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void { } } -pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void { +pub fn deleteFileWindows(allocator: *Allocator, file_path: []const u8) !void { const buf = try allocator.alloc(u8, file_path.len + 1); defer allocator.free(buf); @@ -772,7 +772,7 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void { } } -pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void { +pub fn deleteFilePosix(allocator: *Allocator, file_path: []const u8) !void { const buf = try allocator.alloc(u8, file_path.len + 1); defer allocator.free(buf); @@ -803,7 +803,7 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void { /// there is a possibility of power loss or application termination leaving temporary files present /// in the same directory as dest_path. /// Destination file will have the same mode as the source file. -pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) !void { +pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []const u8) !void { var in_file = try os.File.openRead(allocator, source_path); defer in_file.close(); @@ -825,7 +825,7 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is /// merged and readily available, /// there is a possibility of power loss or application termination leaving temporary files present -pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void { +pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void { var in_file = try os.File.openRead(allocator, source_path); defer in_file.close(); @@ -843,7 +843,7 @@ pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: [ } pub const AtomicFile = struct { - allocator: &Allocator, + allocator: *Allocator, file: os.File, tmp_path: []u8, dest_path: []const u8, @@ -851,7 +851,7 @@ pub const AtomicFile = struct { /// dest_path must remain valid for the lifetime of AtomicFile /// call finish to atomically replace dest_path with contents - pub fn init(allocator: &Allocator, dest_path: []const u8, mode: FileMode) !AtomicFile { + pub fn init(allocator: *Allocator, dest_path: []const u8, mode: FileMode) !AtomicFile { const dirname = os.path.dirname(dest_path); var rand_buf: [12]u8 = undefined; @@ -888,7 +888,7 @@ pub const AtomicFile = struct { } /// always call deinit, even after successful finish() - pub fn deinit(self: &AtomicFile) void { + pub fn deinit(self: *AtomicFile) void { if (!self.finished) { self.file.close(); deleteFile(self.allocator, self.tmp_path) catch {}; @@ -897,7 +897,7 @@ pub const AtomicFile = struct { } } - pub fn finish(self: &AtomicFile) !void { + pub fn finish(self: *AtomicFile) !void { assert(!self.finished); self.file.close(); try rename(self.allocator, self.tmp_path, self.dest_path); @@ -906,7 +906,7 @@ pub const AtomicFile = struct { } }; -pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) !void { +pub fn rename(allocator: *Allocator, old_path: []const u8, new_path: []const u8) !void { const full_buf = try allocator.alloc(u8, old_path.len + new_path.len + 2); defer allocator.free(full_buf); @@ -951,7 +951,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) } } -pub fn makeDir(allocator: &Allocator, dir_path: []const u8) !void { +pub fn makeDir(allocator: *Allocator, dir_path: []const u8) !void { if (is_windows) { return makeDirWindows(allocator, dir_path); } else { @@ -959,7 +959,7 @@ pub fn makeDir(allocator: &Allocator, dir_path: []const u8) !void { } } -pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) !void { +pub fn makeDirWindows(allocator: *Allocator, dir_path: []const u8) !void { const path_buf = try cstr.addNullByte(allocator, dir_path); defer allocator.free(path_buf); @@ -973,7 +973,7 @@ pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) !void { } } -pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void { +pub fn makeDirPosix(allocator: *Allocator, dir_path: []const u8) !void { const path_buf = try cstr.addNullByte(allocator, dir_path); defer allocator.free(path_buf); @@ -999,7 +999,7 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void { /// Calls makeDir recursively to make an entire path. Returns success if the path /// already exists and is a directory. -pub fn makePath(allocator: &Allocator, full_path: []const u8) !void { +pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { const resolved_path = try path.resolve(allocator, full_path); defer allocator.free(resolved_path); @@ -1033,7 +1033,7 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) !void { /// Returns ::error.DirNotEmpty if the directory is not empty. /// To delete a directory recursively, see ::deleteTree -pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void { +pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) !void { const path_buf = try allocator.alloc(u8, dir_path.len + 1); defer allocator.free(path_buf); @@ -1084,7 +1084,7 @@ const DeleteTreeError = error{ DirNotEmpty, Unexpected, }; -pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!void { +pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void { start_over: while (true) { var got_access_denied = false; // First, try deleting the item as a file. This way we don't follow sym links. @@ -1153,7 +1153,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! pub const Dir = struct { fd: i32, darwin_seek: darwin_seek_t, - allocator: &Allocator, + allocator: *Allocator, buf: []u8, index: usize, end_index: usize, @@ -1180,7 +1180,7 @@ pub const Dir = struct { }; }; - pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir { + pub fn open(allocator: *Allocator, dir_path: []const u8) !Dir { const fd = switch (builtin.os) { Os.windows => @compileError("TODO support Dir.open for windows"), Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0), @@ -1206,14 +1206,14 @@ pub const Dir = struct { }; } - pub fn close(self: &Dir) void { + pub fn close(self: *Dir) void { self.allocator.free(self.buf); os.close(self.fd); } /// Memory such as file names referenced in this returned entry becomes invalid /// with subsequent calls to next, as well as when this ::Dir is deinitialized. - pub fn next(self: &Dir) !?Entry { + pub fn next(self: *Dir) !?Entry { switch (builtin.os) { Os.linux => return self.nextLinux(), Os.macosx, Os.ios => return self.nextDarwin(), @@ -1222,7 +1222,7 @@ pub const Dir = struct { } } - fn nextDarwin(self: &Dir) !?Entry { + fn nextDarwin(self: *Dir) !?Entry { start_over: while (true) { if (self.index >= self.end_index) { if (self.buf.len == 0) { @@ -1248,7 +1248,7 @@ pub const Dir = struct { break; } } - const darwin_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]); + const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.buf[self.index]); const next_index = self.index + darwin_entry.d_reclen; self.index = next_index; @@ -1277,11 +1277,11 @@ pub const Dir = struct { } } - fn nextWindows(self: &Dir) !?Entry { + fn nextWindows(self: *Dir) !?Entry { @compileError("TODO support Dir.next for windows"); } - fn nextLinux(self: &Dir) !?Entry { + fn nextLinux(self: *Dir) !?Entry { start_over: while (true) { if (self.index >= self.end_index) { if (self.buf.len == 0) { @@ -1307,7 +1307,7 @@ pub const Dir = struct { break; } } - const linux_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]); + const linux_entry = @ptrCast(*align(1) posix.dirent, &self.buf[self.index]); const next_index = self.index + linux_entry.d_reclen; self.index = next_index; @@ -1337,7 +1337,7 @@ pub const Dir = struct { } }; -pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) !void { +pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void { const path_buf = try allocator.alloc(u8, dir_path.len + 1); defer allocator.free(path_buf); @@ -1361,7 +1361,7 @@ pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) !void { } /// Read value of a symbolic link. -pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 { +pub fn readLink(allocator: *Allocator, pathname: []const u8) ![]u8 { const path_buf = try allocator.alloc(u8, pathname.len + 1); defer allocator.free(path_buf); @@ -1468,7 +1468,7 @@ pub const ArgIteratorPosix = struct { }; } - pub fn next(self: &ArgIteratorPosix) ?[]const u8 { + pub fn next(self: *ArgIteratorPosix) ?[]const u8 { if (self.index == self.count) return null; const s = raw[self.index]; @@ -1476,7 +1476,7 @@ pub const ArgIteratorPosix = struct { return cstr.toSlice(s); } - pub fn skip(self: &ArgIteratorPosix) bool { + pub fn skip(self: *ArgIteratorPosix) bool { if (self.index == self.count) return false; self.index += 1; @@ -1485,12 +1485,12 @@ pub const ArgIteratorPosix = struct { /// This is marked as public but actually it's only meant to be used /// internally by zig's startup code. - pub var raw: []&u8 = undefined; + pub var raw: []*u8 = undefined; }; pub const ArgIteratorWindows = struct { index: usize, - cmd_line: &const u8, + cmd_line: *const u8, in_quote: bool, quote_count: usize, seen_quote_count: usize, @@ -1501,7 +1501,7 @@ pub const ArgIteratorWindows = struct { return initWithCmdLine(windows.GetCommandLineA()); } - pub fn initWithCmdLine(cmd_line: &const u8) ArgIteratorWindows { + pub fn initWithCmdLine(cmd_line: *const u8) ArgIteratorWindows { return ArgIteratorWindows{ .index = 0, .cmd_line = cmd_line, @@ -1512,7 +1512,7 @@ pub const ArgIteratorWindows = struct { } /// You must free the returned memory when done. - pub fn next(self: &ArgIteratorWindows, allocator: &Allocator) ?(NextError![]u8) { + pub fn next(self: *ArgIteratorWindows, allocator: *Allocator) ?(NextError![]u8) { // march forward over whitespace while (true) : (self.index += 1) { const byte = self.cmd_line[self.index]; @@ -1526,7 +1526,7 @@ pub const ArgIteratorWindows = struct { return self.internalNext(allocator); } - pub fn skip(self: &ArgIteratorWindows) bool { + pub fn skip(self: *ArgIteratorWindows) bool { // march forward over whitespace while (true) : (self.index += 1) { const byte = self.cmd_line[self.index]; @@ -1565,7 +1565,7 @@ pub const ArgIteratorWindows = struct { } } - fn internalNext(self: &ArgIteratorWindows, allocator: &Allocator) NextError![]u8 { + fn internalNext(self: *ArgIteratorWindows, allocator: *Allocator) NextError![]u8 { var buf = try Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -1609,14 +1609,14 @@ pub const ArgIteratorWindows = struct { } } - fn emitBackslashes(self: &ArgIteratorWindows, buf: &Buffer, emit_count: usize) !void { + fn emitBackslashes(self: *ArgIteratorWindows, buf: *Buffer, emit_count: usize) !void { var i: usize = 0; while (i < emit_count) : (i += 1) { try buf.appendByte('\\'); } } - fn countQuotes(cmd_line: &const u8) usize { + fn countQuotes(cmd_line: *const u8) usize { var result: usize = 0; var backslash_count: usize = 0; var index: usize = 0; @@ -1649,7 +1649,7 @@ pub const ArgIterator = struct { pub const NextError = ArgIteratorWindows.NextError; /// You must free the returned memory when done. - pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) { + pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) { if (builtin.os == Os.windows) { return self.inner.next(allocator); } else { @@ -1658,13 +1658,13 @@ pub const ArgIterator = struct { } /// If you only are targeting posix you can call this and not need an allocator. - pub fn nextPosix(self: &ArgIterator) ?[]const u8 { + pub fn nextPosix(self: *ArgIterator) ?[]const u8 { return self.inner.next(); } /// Parse past 1 argument without capturing it. /// Returns `true` if skipped an arg, `false` if we are at the end. - pub fn skip(self: &ArgIterator) bool { + pub fn skip(self: *ArgIterator) bool { return self.inner.skip(); } }; @@ -1674,7 +1674,7 @@ pub fn args() ArgIterator { } /// Caller must call freeArgs on result. -pub fn argsAlloc(allocator: &mem.Allocator) ![]const []u8 { +pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 { // TODO refactor to only make 1 allocation. var it = args(); var contents = try Buffer.initSize(allocator, 0); @@ -1711,12 +1711,12 @@ pub fn argsAlloc(allocator: &mem.Allocator) ![]const []u8 { return result_slice_list; } -pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void { +pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { var total_bytes: usize = 0; for (args_alloc) |arg| { total_bytes += @sizeOf([]u8) + arg.len; } - const unaligned_allocated_buf = @ptrCast(&const u8, args_alloc.ptr)[0..total_bytes]; + const unaligned_allocated_buf = @ptrCast(*const u8, args_alloc.ptr)[0..total_bytes]; const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf); return allocator.free(aligned_allocated_buf); } @@ -1765,7 +1765,7 @@ test "windows arg parsing" { }); } -fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const u8) void { +fn testWindowsCmdLine(input_cmd_line: *const u8, expected_args: []const []const u8) void { var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); for (expected_args) |expected_arg| { const arg = ??it.next(debug.global_allocator) catch unreachable; @@ -1832,7 +1832,7 @@ test "openSelfExe" { /// This function may return an error if the current executable /// was deleted after spawning. /// Caller owns returned memory. -pub fn selfExePath(allocator: &mem.Allocator) ![]u8 { +pub fn selfExePath(allocator: *mem.Allocator) ![]u8 { switch (builtin.os) { Os.linux => { // If the currently executing binary has been deleted, @@ -1875,7 +1875,7 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 { /// Get the directory path that contains the current executable. /// Caller owns returned memory. -pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 { +pub fn selfExeDirPath(allocator: *mem.Allocator) ![]u8 { switch (builtin.os) { Os.linux => { // If the currently executing binary has been deleted, @@ -2001,7 +2001,7 @@ pub const PosixBindError = error{ }; /// addr is `&const T` where T is one of the sockaddr -pub fn posixBind(fd: i32, addr: &const posix.sockaddr) PosixBindError!void { +pub fn posixBind(fd: i32, addr: *const posix.sockaddr) PosixBindError!void { const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr)); const err = posix.getErrno(rc); switch (err) { @@ -2096,7 +2096,7 @@ pub const PosixAcceptError = error{ Unexpected, }; -pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError!i32 { +pub fn posixAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { while (true) { var sockaddr_size = u32(@sizeOf(posix.sockaddr)); const rc = posix.accept4(fd, addr, &sockaddr_size, flags); @@ -2195,7 +2195,7 @@ pub const LinuxEpollCtlError = error{ Unexpected, }; -pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: &linux.epoll_event) LinuxEpollCtlError!void { +pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: *linux.epoll_event) LinuxEpollCtlError!void { const rc = posix.epoll_ctl(epfd, op, fd, event); const err = posix.getErrno(rc); switch (err) { @@ -2288,7 +2288,7 @@ pub const PosixConnectError = error{ Unexpected, }; -pub fn posixConnect(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void { +pub fn posixConnect(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectError!void { while (true) { const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); const err = posix.getErrno(rc); @@ -2319,7 +2319,7 @@ pub fn posixConnect(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectEr /// Same as posixConnect except it is for blocking socket file descriptors. /// It expects to receive EINPROGRESS. -pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void { +pub fn posixConnectAsync(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectError!void { while (true) { const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); const err = posix.getErrno(rc); @@ -2350,7 +2350,7 @@ pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConn pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { var err_code: i32 = undefined; var size: u32 = @sizeOf(i32); - const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast(&u8, &err_code), &size); + const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast(*u8, &err_code), &size); assert(size == 4); const err = posix.getErrno(rc); switch (err) { @@ -2401,13 +2401,13 @@ pub const Thread = struct { }, builtin.Os.windows => struct { handle: windows.HANDLE, - alloc_start: &c_void, + alloc_start: *c_void, heap_handle: windows.HANDLE, }, else => @compileError("Unsupported OS"), }; - pub fn wait(self: &const Thread) void { + pub fn wait(self: *const Thread) void { if (use_pthreads) { const err = c.pthread_join(self.data.handle, null); switch (err) { @@ -2473,7 +2473,7 @@ pub const SpawnThreadError = error{ /// fn startFn(@typeOf(context)) T /// where T is u8, noreturn, void, or !void /// caller must call wait on the returned thread -pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread { +pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread { // TODO compile-time call graph analysis to determine stack upper bound // https://github.com/ziglang/zig/issues/157 const default_stack_size = 8 * 1024 * 1024; @@ -2491,7 +2491,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread if (@sizeOf(Context) == 0) { return startFn({}); } else { - return startFn(@ptrCast(&Context, @alignCast(@alignOf(Context), arg)).*); + return startFn(@ptrCast(*Context, @alignCast(@alignOf(Context), arg)).*); } } }; @@ -2500,13 +2500,13 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory; errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); - const bytes = @ptrCast(&u8, bytes_ptr)[0..byte_count]; + const bytes = @ptrCast(*u8, bytes_ptr)[0..byte_count]; const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; outer_context.inner = context; outer_context.thread.data.heap_handle = heap_handle; outer_context.thread.data.alloc_start = bytes_ptr; - const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner); + const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? { const err = windows.GetLastError(); return switch (err) { @@ -2521,15 +2521,15 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread if (@sizeOf(Context) == 0) { return startFn({}); } else { - return startFn(@intToPtr(&const Context, ctx_addr).*); + return startFn(@intToPtr(*const Context, ctx_addr).*); } } - extern fn posixThreadMain(ctx: ?&c_void) ?&c_void { + extern fn posixThreadMain(ctx: ?*c_void) ?*c_void { if (@sizeOf(Context) == 0) { _ = startFn({}); return null; } else { - _ = startFn(@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx)).*); + _ = startFn(@ptrCast(*const Context, @alignCast(@alignOf(Context), ctx)).*); return null; } } @@ -2548,7 +2548,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread stack_end -= @sizeOf(Context); stack_end -= stack_end % @alignOf(Context); assert(stack_end >= stack_addr); - const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end)); + const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end)); context_ptr.* = context; arg = stack_end; } @@ -2556,7 +2556,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread stack_end -= @sizeOf(Thread); stack_end -= stack_end % @alignOf(Thread); assert(stack_end >= stack_addr); - const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end)); + const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, stack_end)); thread_ptr.data.stack_addr = stack_addr; thread_ptr.data.stack_len = mmap_len; @@ -2572,9 +2572,9 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread // align to page stack_end -= stack_end % os.page_size; - assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_end - stack_addr) == 0); + assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0); - const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); + const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); switch (err) { 0 => return thread_ptr, posix.EAGAIN => return SpawnThreadError.SystemResources, diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 5186ff32d3..3e7b836ac7 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -665,15 +665,15 @@ pub fn dup2(old: i32, new: i32) usize { return syscall2(SYS_dup2, usize(old), usize(new)); } -pub fn chdir(path: &const u8) usize { +pub fn chdir(path: *const u8) usize { return syscall1(SYS_chdir, @ptrToInt(path)); } -pub fn chroot(path: &const u8) usize { +pub fn chroot(path: *const u8) usize { return syscall1(SYS_chroot, @ptrToInt(path)); } -pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize { +pub fn execve(path: *const u8, argv: *const ?*const u8, envp: *const ?*const u8) usize { return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); } @@ -681,15 +681,15 @@ pub fn fork() usize { return syscall0(SYS_fork); } -pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?×pec) usize { +pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) usize { return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout)); } -pub fn getcwd(buf: &u8, size: usize) usize { +pub fn getcwd(buf: *u8, size: usize) usize { return syscall2(SYS_getcwd, @ptrToInt(buf), size); } -pub fn getdents(fd: i32, dirp: &u8, count: usize) usize { +pub fn getdents(fd: i32, dirp: *u8, count: usize) usize { return syscall3(SYS_getdents, usize(fd), @ptrToInt(dirp), count); } @@ -698,27 +698,27 @@ pub fn isatty(fd: i32) bool { return syscall3(SYS_ioctl, usize(fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; } -pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) usize { +pub fn readlink(noalias path: *const u8, noalias buf_ptr: *u8, buf_len: usize) usize { return syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); } -pub fn mkdir(path: &const u8, mode: u32) usize { +pub fn mkdir(path: *const u8, mode: u32) usize { return syscall2(SYS_mkdir, @ptrToInt(path), mode); } -pub fn mount(special: &const u8, dir: &const u8, fstype: &const u8, flags: usize, data: usize) usize { +pub fn mount(special: *const u8, dir: *const u8, fstype: *const u8, flags: usize, data: usize) usize { return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data); } -pub fn umount(special: &const u8) usize { +pub fn umount(special: *const u8) usize { return syscall2(SYS_umount2, @ptrToInt(special), 0); } -pub fn umount2(special: &const u8, flags: u32) usize { +pub fn umount2(special: *const u8, flags: u32) usize { return syscall2(SYS_umount2, @ptrToInt(special), flags); } -pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { +pub fn mmap(address: ?*u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd), @bitCast(usize, offset)); } @@ -726,60 +726,60 @@ pub fn munmap(address: usize, length: usize) usize { return syscall2(SYS_munmap, address, length); } -pub fn read(fd: i32, buf: &u8, count: usize) usize { +pub fn read(fd: i32, buf: *u8, count: usize) usize { return syscall3(SYS_read, usize(fd), @ptrToInt(buf), count); } -pub fn rmdir(path: &const u8) usize { +pub fn rmdir(path: *const u8) usize { return syscall1(SYS_rmdir, @ptrToInt(path)); } -pub fn symlink(existing: &const u8, new: &const u8) usize { +pub fn symlink(existing: *const u8, new: *const u8) usize { return syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new)); } -pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize { +pub fn pread(fd: i32, buf: *u8, count: usize, offset: usize) usize { return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset); } -pub fn access(path: &const u8, mode: u32) usize { +pub fn access(path: *const u8, mode: u32) usize { return syscall2(SYS_access, @ptrToInt(path), mode); } -pub fn pipe(fd: &[2]i32) usize { +pub fn pipe(fd: *[2]i32) usize { return pipe2(fd, 0); } -pub fn pipe2(fd: &[2]i32, flags: usize) usize { +pub fn pipe2(fd: *[2]i32, flags: usize) usize { return syscall2(SYS_pipe2, @ptrToInt(fd), flags); } -pub fn write(fd: i32, buf: &const u8, count: usize) usize { +pub fn write(fd: i32, buf: *const u8, count: usize) usize { return syscall3(SYS_write, usize(fd), @ptrToInt(buf), count); } -pub fn pwrite(fd: i32, buf: &const u8, count: usize, offset: usize) usize { +pub fn pwrite(fd: i32, buf: *const u8, count: usize, offset: usize) usize { return syscall4(SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset); } -pub fn rename(old: &const u8, new: &const u8) usize { +pub fn rename(old: *const u8, new: *const u8) usize { return syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new)); } -pub fn open(path: &const u8, flags: u32, perm: usize) usize { +pub fn open(path: *const u8, flags: u32, perm: usize) usize { return syscall3(SYS_open, @ptrToInt(path), flags, perm); } -pub fn create(path: &const u8, perm: usize) usize { +pub fn create(path: *const u8, perm: usize) usize { return syscall2(SYS_creat, @ptrToInt(path), perm); } -pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize { +pub fn openat(dirfd: i32, path: *const u8, flags: usize, mode: usize) usize { return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode); } /// See also `clone` (from the arch-specific include) -pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: &i32, child_tid: &i32, newtls: usize) usize { +pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: *i32, child_tid: *i32, newtls: usize) usize { return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls); } @@ -801,7 +801,7 @@ pub fn exit(status: i32) noreturn { unreachable; } -pub fn getrandom(buf: &u8, count: usize, flags: u32) usize { +pub fn getrandom(buf: *u8, count: usize, flags: u32) usize { return syscall3(SYS_getrandom, @ptrToInt(buf), count, usize(flags)); } @@ -809,15 +809,15 @@ pub fn kill(pid: i32, sig: i32) usize { return syscall2(SYS_kill, @bitCast(usize, isize(pid)), usize(sig)); } -pub fn unlink(path: &const u8) usize { +pub fn unlink(path: *const u8) usize { return syscall1(SYS_unlink, @ptrToInt(path)); } -pub fn waitpid(pid: i32, status: &i32, options: i32) usize { +pub fn waitpid(pid: i32, status: *i32, options: i32) usize { return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); } -pub fn clock_gettime(clk_id: i32, tp: ×pec) usize { +pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { if (VDSO_CGT_SYM.len != 0) { const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered); if (@ptrToInt(f) != 0) { @@ -831,7 +831,7 @@ pub fn clock_gettime(clk_id: i32, tp: ×pec) usize { return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); } var vdso_clock_gettime = init_vdso_clock_gettime; -extern fn init_vdso_clock_gettime(clk: i32, ts: ×pec) usize { +extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize { const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM); var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr); _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f, builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic); @@ -839,23 +839,23 @@ extern fn init_vdso_clock_gettime(clk: i32, ts: ×pec) usize { return f(clk, ts); } -pub fn clock_getres(clk_id: i32, tp: ×pec) usize { +pub fn clock_getres(clk_id: i32, tp: *timespec) usize { return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); } -pub fn clock_settime(clk_id: i32, tp: &const timespec) usize { +pub fn clock_settime(clk_id: i32, tp: *const timespec) usize { return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); } -pub fn gettimeofday(tv: &timeval, tz: &timezone) usize { +pub fn gettimeofday(tv: *timeval, tz: *timezone) usize { return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); } -pub fn settimeofday(tv: &const timeval, tz: &const timezone) usize { +pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize { return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); } -pub fn nanosleep(req: &const timespec, rem: ?×pec) usize { +pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); } @@ -899,11 +899,11 @@ pub fn setegid(egid: u32) usize { return syscall1(SYS_setegid, egid); } -pub fn getresuid(ruid: &u32, euid: &u32, suid: &u32) usize { +pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize { return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid)); } -pub fn getresgid(rgid: &u32, egid: &u32, sgid: &u32) usize { +pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize { return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid)); } @@ -915,11 +915,11 @@ pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize { return syscall3(SYS_setresgid, rgid, egid, sgid); } -pub fn getgroups(size: usize, list: &u32) usize { +pub fn getgroups(size: usize, list: *u32) usize { return syscall2(SYS_getgroups, size, @ptrToInt(list)); } -pub fn setgroups(size: usize, list: &const u32) usize { +pub fn setgroups(size: usize, list: *const u32) usize { return syscall2(SYS_setgroups, size, @ptrToInt(list)); } @@ -927,11 +927,11 @@ pub fn getpid() i32 { return @bitCast(i32, u32(syscall0(SYS_getpid))); } -pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize { +pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); } -pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigaction) usize { +pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { assert(sig >= 1); assert(sig != SIGKILL); assert(sig != SIGSTOP); @@ -942,8 +942,8 @@ pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigacti .restorer = @ptrCast(extern fn () void, restore_rt), }; var ksa_old: k_sigaction = undefined; - @memcpy(@ptrCast(&u8, &ksa.mask), @ptrCast(&const u8, &act.mask), 8); - const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask))); + @memcpy(@ptrCast(*u8, *ksa.mask), @ptrCast(*const u8, *act.mask), 8); + const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(*ksa), @ptrToInt(*ksa_old), @sizeOf(@typeOf(ksa.mask))); const err = getErrno(result); if (err != 0) { return result; @@ -951,7 +951,7 @@ pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigacti if (oact) |old| { old.handler = ksa_old.handler; old.flags = @truncate(u32, ksa_old.flags); - @memcpy(@ptrCast(&u8, &old.mask), @ptrCast(&const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); + @memcpy(@ptrCast(*u8, *old.mask), @ptrCast(*const u8, *ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); } return 0; } @@ -989,24 +989,24 @@ pub fn raise(sig: i32) usize { return ret; } -fn blockAllSignals(set: &sigset_t) void { +fn blockAllSignals(set: *sigset_t) void { _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); } -fn blockAppSignals(set: &sigset_t) void { +fn blockAppSignals(set: *sigset_t) void { _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8); } -fn restoreSignals(set: &sigset_t) void { +fn restoreSignals(set: *sigset_t) void { _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8); } -pub fn sigaddset(set: &sigset_t, sig: u6) void { +pub fn sigaddset(set: *sigset_t, sig: u6) void { const s = sig - 1; (set.*)[usize(s) / usize.bit_count] |= usize(1) << (s & (usize.bit_count - 1)); } -pub fn sigismember(set: &const sigset_t, sig: u6) bool { +pub fn sigismember(set: *const sigset_t, sig: u6) bool { const s = sig - 1; return ((set.*)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0; } @@ -1036,15 +1036,15 @@ pub const sockaddr_in6 = extern struct { }; pub const iovec = extern struct { - iov_base: &u8, + iov_base: *u8, iov_len: usize, }; -pub fn getsockname(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize { +pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { return syscall3(SYS_getsockname, usize(fd), @ptrToInt(addr), @ptrToInt(len)); } -pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize { +pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len)); } @@ -1052,27 +1052,27 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { return syscall3(SYS_socket, domain, socket_type, protocol); } -pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: &const u8, optlen: socklen_t) usize { +pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: *const u8, optlen: socklen_t) usize { return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen)); } -pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: &u8, noalias optlen: &socklen_t) usize { +pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: *u8, noalias optlen: *socklen_t) usize { return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); } -pub fn sendmsg(fd: i32, msg: &const msghdr, flags: u32) usize { +pub fn sendmsg(fd: i32, msg: *const msghdr, flags: u32) usize { return syscall3(SYS_sendmsg, usize(fd), @ptrToInt(msg), flags); } -pub fn connect(fd: i32, addr: &const sockaddr, len: socklen_t) usize { +pub fn connect(fd: i32, addr: *const sockaddr, len: socklen_t) usize { return syscall3(SYS_connect, usize(fd), @ptrToInt(addr), usize(len)); } -pub fn recvmsg(fd: i32, msg: &msghdr, flags: u32) usize { +pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { return syscall3(SYS_recvmsg, usize(fd), @ptrToInt(msg), flags); } -pub fn recvfrom(fd: i32, noalias buf: &u8, len: usize, flags: u32, noalias addr: ?&sockaddr, noalias alen: ?&socklen_t) usize { +pub fn recvfrom(fd: i32, noalias buf: *u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { return syscall6(SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); } @@ -1080,7 +1080,7 @@ pub fn shutdown(fd: i32, how: i32) usize { return syscall2(SYS_shutdown, usize(fd), usize(how)); } -pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) usize { +pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len)); } @@ -1088,79 +1088,79 @@ pub fn listen(fd: i32, backlog: u32) usize { return syscall2(SYS_listen, usize(fd), backlog); } -pub fn sendto(fd: i32, buf: &const u8, len: usize, flags: u32, addr: ?&const sockaddr, alen: socklen_t) usize { +pub fn sendto(fd: i32, buf: *const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { return syscall6(SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen)); } pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { - return syscall4(SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(&fd[0])); + return syscall4(SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(*fd[0])); } -pub fn accept(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize { +pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { return accept4(fd, addr, len, 0); } -pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags: u32) usize { +pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags); } -pub fn fstat(fd: i32, stat_buf: &Stat) usize { +pub fn fstat(fd: i32, stat_buf: *Stat) usize { return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf)); } -pub fn stat(pathname: &const u8, statbuf: &Stat) usize { +pub fn stat(pathname: *const u8, statbuf: *Stat) usize { return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf)); } -pub fn lstat(pathname: &const u8, statbuf: &Stat) usize { +pub fn lstat(pathname: *const u8, statbuf: *Stat) usize { return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf)); } -pub fn listxattr(path: &const u8, list: &u8, size: usize) usize { +pub fn listxattr(path: *const u8, list: *u8, size: usize) usize { return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); } -pub fn llistxattr(path: &const u8, list: &u8, size: usize) usize { +pub fn llistxattr(path: *const u8, list: *u8, size: usize) usize { return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); } -pub fn flistxattr(fd: usize, list: &u8, size: usize) usize { +pub fn flistxattr(fd: usize, list: *u8, size: usize) usize { return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); } -pub fn getxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize { +pub fn getxattr(path: *const u8, name: *const u8, value: *void, size: usize) usize { return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); } -pub fn lgetxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize { +pub fn lgetxattr(path: *const u8, name: *const u8, value: *void, size: usize) usize { return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); } -pub fn fgetxattr(fd: usize, name: &const u8, value: &void, size: usize) usize { +pub fn fgetxattr(fd: usize, name: *const u8, value: *void, size: usize) usize { return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); } -pub fn setxattr(path: &const u8, name: &const u8, value: &const void, size: usize, flags: usize) usize { +pub fn setxattr(path: *const u8, name: *const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn lsetxattr(path: &const u8, name: &const u8, value: &const void, size: usize, flags: usize) usize { +pub fn lsetxattr(path: *const u8, name: *const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn fsetxattr(fd: usize, name: &const u8, value: &const void, size: usize, flags: usize) usize { +pub fn fsetxattr(fd: usize, name: *const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn removexattr(path: &const u8, name: &const u8) usize { +pub fn removexattr(path: *const u8, name: *const u8) usize { return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); } -pub fn lremovexattr(path: &const u8, name: &const u8) usize { +pub fn lremovexattr(path: *const u8, name: *const u8) usize { return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); } -pub fn fremovexattr(fd: usize, name: &const u8) usize { +pub fn fremovexattr(fd: usize, name: *const u8) usize { return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); } @@ -1184,11 +1184,11 @@ pub fn epoll_create1(flags: usize) usize { return syscall1(SYS_epoll_create1, flags); } -pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: &epoll_event) usize { +pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev)); } -pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: u32, timeout: i32) usize { +pub fn epoll_wait(epoll_fd: i32, events: *epoll_event, maxevents: u32, timeout: i32) usize { return syscall4(SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout)); } @@ -1201,11 +1201,11 @@ pub const itimerspec = extern struct { it_value: timespec, }; -pub fn timerfd_gettime(fd: i32, curr_value: &itimerspec) usize { +pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { return syscall2(SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value)); } -pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_value: ?&itimerspec) usize { +pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value)); } @@ -1300,8 +1300,8 @@ pub fn CAP_TO_INDEX(cap: u8) u8 { } pub const cap_t = extern struct { - hdrp: &cap_user_header_t, - datap: &cap_user_data_t, + hdrp: *cap_user_header_t, + datap: *cap_user_data_t, }; pub const cap_user_header_t = extern struct { @@ -1319,11 +1319,11 @@ pub fn unshare(flags: usize) usize { return syscall1(SYS_unshare, usize(flags)); } -pub fn capget(hdrp: &cap_user_header_t, datap: &cap_user_data_t) usize { +pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize { return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap)); } -pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize { +pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize { return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); } diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig index 8e0a285841..1317da6388 100644 --- a/std/os/linux/vdso.zig +++ b/std/os/linux/vdso.zig @@ -8,11 +8,11 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { const vdso_addr = std.os.linux_aux_raw[std.elf.AT_SYSINFO_EHDR]; if (vdso_addr == 0) return 0; - const eh = @intToPtr(&elf.Ehdr, vdso_addr); + const eh = @intToPtr(*elf.Ehdr, vdso_addr); var ph_addr: usize = vdso_addr + eh.e_phoff; - const ph = @intToPtr(&elf.Phdr, ph_addr); + const ph = @intToPtr(*elf.Phdr, ph_addr); - var maybe_dynv: ?&usize = null; + var maybe_dynv: ?*usize = null; var base: usize = @maxValue(usize); { var i: usize = 0; @@ -20,10 +20,10 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { i += 1; ph_addr += eh.e_phentsize; }) { - const this_ph = @intToPtr(&elf.Phdr, ph_addr); + const this_ph = @intToPtr(*elf.Phdr, ph_addr); switch (this_ph.p_type) { elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr, - elf.PT_DYNAMIC => maybe_dynv = @intToPtr(&usize, vdso_addr + this_ph.p_offset), + elf.PT_DYNAMIC => maybe_dynv = @intToPtr(*usize, vdso_addr + this_ph.p_offset), else => {}, } } @@ -31,22 +31,22 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { const dynv = maybe_dynv ?? return 0; if (base == @maxValue(usize)) return 0; - var maybe_strings: ?&u8 = null; - var maybe_syms: ?&elf.Sym = null; - var maybe_hashtab: ?&linux.Elf_Symndx = null; - var maybe_versym: ?&u16 = null; - var maybe_verdef: ?&elf.Verdef = null; + var maybe_strings: ?*u8 = null; + var maybe_syms: ?*elf.Sym = null; + var maybe_hashtab: ?*linux.Elf_Symndx = null; + var maybe_versym: ?*u16 = null; + var maybe_verdef: ?*elf.Verdef = null; { var i: usize = 0; while (dynv[i] != 0) : (i += 2) { const p = base + dynv[i + 1]; switch (dynv[i]) { - elf.DT_STRTAB => maybe_strings = @intToPtr(&u8, p), - elf.DT_SYMTAB => maybe_syms = @intToPtr(&elf.Sym, p), - elf.DT_HASH => maybe_hashtab = @intToPtr(&linux.Elf_Symndx, p), - elf.DT_VERSYM => maybe_versym = @intToPtr(&u16, p), - elf.DT_VERDEF => maybe_verdef = @intToPtr(&elf.Verdef, p), + elf.DT_STRTAB => maybe_strings = @intToPtr(*u8, p), + elf.DT_SYMTAB => maybe_syms = @intToPtr(*elf.Sym, p), + elf.DT_HASH => maybe_hashtab = @intToPtr(*linux.Elf_Symndx, p), + elf.DT_VERSYM => maybe_versym = @intToPtr(*u16, p), + elf.DT_VERDEF => maybe_verdef = @intToPtr(*elf.Verdef, p), else => {}, } } @@ -76,7 +76,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { return 0; } -fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: &u8) bool { +fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: *u8) bool { var def = def_arg; const vsym = @bitCast(u32, vsym_arg) & 0x7fff; while (true) { @@ -84,8 +84,8 @@ fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: & break; if (def.vd_next == 0) return false; - def = @intToPtr(&elf.Verdef, @ptrToInt(def) + def.vd_next); + def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next); } - const aux = @intToPtr(&elf.Verdaux, @ptrToInt(def) + def.vd_aux); + const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux); return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name])); } diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index b43a642038..9a90e64757 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -463,7 +463,7 @@ pub fn syscall6( } /// This matches the libc clone function. -pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: &i32, tls: usize, ctid: &i32) usize; +pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub nakedcc fn restore_rt() void { return asm volatile ("syscall" @@ -474,12 +474,12 @@ pub nakedcc fn restore_rt() void { } pub const msghdr = extern struct { - msg_name: &u8, + msg_name: *u8, msg_namelen: socklen_t, - msg_iov: &iovec, + msg_iov: *iovec, msg_iovlen: i32, __pad1: i32, - msg_control: &u8, + msg_control: *u8, msg_controllen: socklen_t, __pad2: socklen_t, msg_flags: i32, diff --git a/std/os/path.zig b/std/os/path.zig index 162faffc42..4df6179bf5 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -32,7 +32,7 @@ pub fn isSep(byte: u8) bool { /// Naively combines a series of paths with the native path seperator. /// Allocates memory for the result, which must be freed by the caller. -pub fn join(allocator: &Allocator, paths: ...) ![]u8 { +pub fn join(allocator: *Allocator, paths: ...) ![]u8 { if (is_windows) { return joinWindows(allocator, paths); } else { @@ -40,11 +40,11 @@ pub fn join(allocator: &Allocator, paths: ...) ![]u8 { } } -pub fn joinWindows(allocator: &Allocator, paths: ...) ![]u8 { +pub fn joinWindows(allocator: *Allocator, paths: ...) ![]u8 { return mem.join(allocator, sep_windows, paths); } -pub fn joinPosix(allocator: &Allocator, paths: ...) ![]u8 { +pub fn joinPosix(allocator: *Allocator, paths: ...) ![]u8 { return mem.join(allocator, sep_posix, paths); } @@ -310,7 +310,7 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool { } /// Converts the command line arguments into a slice and calls `resolveSlice`. -pub fn resolve(allocator: &Allocator, args: ...) ![]u8 { +pub fn resolve(allocator: *Allocator, args: ...) ![]u8 { var paths: [args.len][]const u8 = undefined; comptime var arg_i = 0; inline while (arg_i < args.len) : (arg_i += 1) { @@ -320,7 +320,7 @@ pub fn resolve(allocator: &Allocator, args: ...) ![]u8 { } /// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`. -pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) ![]u8 { +pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (is_windows) { return resolveWindows(allocator, paths); } else { @@ -334,7 +334,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) ![]u8 { /// If all paths are relative it uses the current working directory as a starting point. /// Each drive has its own current working directory. /// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters. -pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) ![]u8 { +pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { assert(is_windows); // resolveWindows called on non windows can't use getCwd return os.getCwd(allocator); @@ -513,7 +513,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) ![]u8 { /// It resolves "." and "..". /// The result does not have a trailing path separator. /// If all paths are relative it uses the current working directory as a starting point. -pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) ![]u8 { +pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { assert(!is_windows); // resolvePosix called on windows can't use getCwd return os.getCwd(allocator); @@ -883,7 +883,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void { /// resolve to the same path (after calling `resolve` on each), a zero-length /// string is returned. /// On Windows this canonicalizes the drive to a capital letter and paths to `\\`. -pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 { +pub fn relative(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { if (is_windows) { return relativeWindows(allocator, from, to); } else { @@ -891,7 +891,7 @@ pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 { } } -pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 { +pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { const resolved_from = try resolveWindows(allocator, [][]const u8{from}); defer allocator.free(resolved_from); @@ -964,7 +964,7 @@ pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) return []u8{}; } -pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 { +pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { const resolved_from = try resolvePosix(allocator, [][]const u8{from}); defer allocator.free(resolved_from); @@ -1063,7 +1063,7 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons /// Expands all symbolic links and resolves references to `.`, `..`, and /// extra `/` characters in ::pathname. /// Caller must deallocate result. -pub fn real(allocator: &Allocator, pathname: []const u8) ![]u8 { +pub fn real(allocator: *Allocator, pathname: []const u8) ![]u8 { switch (builtin.os) { Os.windows => { const pathname_buf = try allocator.alloc(u8, pathname.len + 1); diff --git a/std/os/test.zig b/std/os/test.zig index 4dfe76224a..4aa3535829 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -63,7 +63,7 @@ fn start1(ctx: void) u8 { return 0; } -fn start2(ctx: &i32) u8 { +fn start2(ctx: *i32) u8 { _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); return 0; } diff --git a/std/os/time.zig b/std/os/time.zig index 9a7c682483..8629504323 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -200,7 +200,7 @@ pub const Timer = struct { } /// Reads the timer value since start or the last reset in nanoseconds - pub fn read(self: &Timer) u64 { + pub fn read(self: *Timer) u64 { var clock = clockNative() - self.start_time; return switch (builtin.os) { Os.windows => @divFloor(clock * ns_per_s, self.frequency), @@ -211,12 +211,12 @@ pub const Timer = struct { } /// Resets the timer value to 0/now. - pub fn reset(self: &Timer) void { + pub fn reset(self: *Timer) void { self.start_time = clockNative(); } /// Returns the current value of the timer in nanoseconds, then resets it - pub fn lap(self: &Timer) u64 { + pub fn lap(self: *Timer) u64 { var now = clockNative(); var lap_time = self.read(); self.start_time = now; diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 264ea391c4..85f69836d5 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -1,7 +1,7 @@ pub const ERROR = @import("error.zig"); pub extern "advapi32" stdcallcc fn CryptAcquireContextA( - phProv: &HCRYPTPROV, + phProv: *HCRYPTPROV, pszContainer: ?LPCSTR, pszProvider: ?LPCSTR, dwProvType: DWORD, @@ -10,13 +10,13 @@ pub extern "advapi32" stdcallcc fn CryptAcquireContextA( pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL; -pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: &BYTE) BOOL; +pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *BYTE) BOOL; pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; pub extern "kernel32" stdcallcc fn CreateDirectoryA( lpPathName: LPCSTR, - lpSecurityAttributes: ?&SECURITY_ATTRIBUTES, + lpSecurityAttributes: ?*SECURITY_ATTRIBUTES, ) BOOL; pub extern "kernel32" stdcallcc fn CreateFileA( @@ -30,23 +30,23 @@ pub extern "kernel32" stdcallcc fn CreateFileA( ) HANDLE; pub extern "kernel32" stdcallcc fn CreatePipe( - hReadPipe: &HANDLE, - hWritePipe: &HANDLE, - lpPipeAttributes: &const SECURITY_ATTRIBUTES, + hReadPipe: *HANDLE, + hWritePipe: *HANDLE, + lpPipeAttributes: *const SECURITY_ATTRIBUTES, nSize: DWORD, ) BOOL; pub extern "kernel32" stdcallcc fn CreateProcessA( lpApplicationName: ?LPCSTR, lpCommandLine: LPSTR, - lpProcessAttributes: ?&SECURITY_ATTRIBUTES, - lpThreadAttributes: ?&SECURITY_ATTRIBUTES, + lpProcessAttributes: ?*SECURITY_ATTRIBUTES, + lpThreadAttributes: ?*SECURITY_ATTRIBUTES, bInheritHandles: BOOL, dwCreationFlags: DWORD, - lpEnvironment: ?&c_void, + lpEnvironment: ?*c_void, lpCurrentDirectory: ?LPCSTR, - lpStartupInfo: &STARTUPINFOA, - lpProcessInformation: &PROCESS_INFORMATION, + lpStartupInfo: *STARTUPINFOA, + lpProcessInformation: *PROCESS_INFORMATION, ) BOOL; pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( @@ -65,7 +65,7 @@ pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: LPCH) BOOL; pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; -pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: &DWORD) BOOL; +pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD; @@ -73,9 +73,9 @@ pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?LPCH; pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD; -pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: &DWORD) BOOL; +pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL; -pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: &LARGE_INTEGER) BOOL; +pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; @@ -84,7 +84,7 @@ pub extern "kernel32" stdcallcc fn GetLastError() DWORD; pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( in_hFile: HANDLE, in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, - out_lpFileInformation: &c_void, + out_lpFileInformation: *c_void, in_dwBufferSize: DWORD, ) BOOL; @@ -97,21 +97,21 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; -pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?&FILETIME) void; +pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?*FILETIME) void; pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void; -pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: &const c_void) SIZE_T; -pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: &const c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; +pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; +pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; -pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?&c_void; +pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; -pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; pub extern "kernel32" stdcallcc fn MoveFileExA( lpExistingFileName: LPCSTR, @@ -119,24 +119,24 @@ pub extern "kernel32" stdcallcc fn MoveFileExA( dwFlags: DWORD, ) BOOL; -pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: &LARGE_INTEGER) BOOL; +pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL; -pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: &LARGE_INTEGER) BOOL; +pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL; pub extern "kernel32" stdcallcc fn ReadFile( in_hFile: HANDLE, - out_lpBuffer: &c_void, + out_lpBuffer: *c_void, in_nNumberOfBytesToRead: DWORD, - out_lpNumberOfBytesRead: &DWORD, - in_out_lpOverlapped: ?&OVERLAPPED, + out_lpNumberOfBytesRead: *DWORD, + in_out_lpOverlapped: ?*OVERLAPPED, ) BOOL; pub extern "kernel32" stdcallcc fn SetFilePointerEx( in_fFile: HANDLE, in_liDistanceToMove: LARGE_INTEGER, - out_opt_ldNewFilePointer: ?&LARGE_INTEGER, + out_opt_ldNewFilePointer: ?*LARGE_INTEGER, in_dwMoveMethod: DWORD, ) BOOL; @@ -150,10 +150,10 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis pub extern "kernel32" stdcallcc fn WriteFile( in_hFile: HANDLE, - in_lpBuffer: &const c_void, + in_lpBuffer: *const c_void, in_nNumberOfBytesToWrite: DWORD, - out_lpNumberOfBytesWritten: ?&DWORD, - in_out_lpOverlapped: ?&OVERLAPPED, + out_lpNumberOfBytesWritten: ?*DWORD, + in_out_lpOverlapped: ?*OVERLAPPED, ) BOOL; //TODO: call unicode versions instead of relying on ANSI code page @@ -171,23 +171,23 @@ pub const BYTE = u8; pub const CHAR = u8; pub const DWORD = u32; pub const FLOAT = f32; -pub const HANDLE = &c_void; +pub const HANDLE = *c_void; pub const HCRYPTPROV = ULONG_PTR; -pub const HINSTANCE = &@OpaqueType(); -pub const HMODULE = &@OpaqueType(); +pub const HINSTANCE = *@OpaqueType(); +pub const HMODULE = *@OpaqueType(); pub const INT = c_int; -pub const LPBYTE = &BYTE; -pub const LPCH = &CHAR; -pub const LPCSTR = &const CHAR; -pub const LPCTSTR = &const TCHAR; -pub const LPCVOID = &const c_void; -pub const LPDWORD = &DWORD; -pub const LPSTR = &CHAR; +pub const LPBYTE = *BYTE; +pub const LPCH = *CHAR; +pub const LPCSTR = *const CHAR; +pub const LPCTSTR = *const TCHAR; +pub const LPCVOID = *const c_void; +pub const LPDWORD = *DWORD; +pub const LPSTR = *CHAR; pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; -pub const LPVOID = &c_void; -pub const LPWSTR = &WCHAR; -pub const PVOID = &c_void; -pub const PWSTR = &WCHAR; +pub const LPVOID = *c_void; +pub const LPWSTR = *WCHAR; +pub const PVOID = *c_void; +pub const PWSTR = *WCHAR; pub const SIZE_T = usize; pub const TCHAR = if (UNICODE) WCHAR else u8; pub const UINT = c_uint; @@ -218,7 +218,7 @@ pub const OVERLAPPED = extern struct { Pointer: PVOID, hEvent: HANDLE, }; -pub const LPOVERLAPPED = &OVERLAPPED; +pub const LPOVERLAPPED = *OVERLAPPED; pub const MAX_PATH = 260; @@ -271,11 +271,11 @@ pub const VOLUME_NAME_NT = 0x2; pub const SECURITY_ATTRIBUTES = extern struct { nLength: DWORD, - lpSecurityDescriptor: ?&c_void, + lpSecurityDescriptor: ?*c_void, bInheritHandle: BOOL, }; -pub const PSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES; -pub const LPSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES; +pub const PSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; +pub const LPSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; pub const GENERIC_READ = 0x80000000; pub const GENERIC_WRITE = 0x40000000; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 2bd8a157e4..7170346108 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -42,7 +42,7 @@ pub const WriteError = error{ }; pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { - if (windows.WriteFile(handle, @ptrCast(&const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { + if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, @@ -68,11 +68,11 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { const size = @sizeOf(windows.FILE_NAME_INFO); var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = []u8{0} ** (size + windows.MAX_PATH); - if (windows.GetFileInformationByHandleEx(handle, windows.FileNameInfo, @ptrCast(&c_void, &name_info_bytes[0]), u32(name_info_bytes.len)) == 0) { + if (windows.GetFileInformationByHandleEx(handle, windows.FileNameInfo, @ptrCast(*c_void, &name_info_bytes[0]), u32(name_info_bytes.len)) == 0) { return true; } - const name_info = @ptrCast(&const windows.FILE_NAME_INFO, &name_info_bytes[0]); + const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]); const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)]; const name_wide = ([]u16)(name_bytes); return mem.indexOf(u16, name_wide, []u16{ 'm', 's', 'y', 's', '-' }) != null or @@ -91,7 +91,7 @@ pub const OpenError = error{ /// `file_path` needs to be copied in memory to add a null terminating byte, hence the allocator. pub fn windowsOpen( - allocator: &mem.Allocator, + allocator: *mem.Allocator, file_path: []const u8, desired_access: windows.DWORD, share_mode: windows.DWORD, @@ -119,7 +119,7 @@ pub fn windowsOpen( } /// Caller must free result. -pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) ![]u8 { +pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 { // count bytes needed const bytes_needed = x: { var bytes_needed: usize = 1; // 1 for the final null byte @@ -150,7 +150,7 @@ pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) return result; } -pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) !windows.HMODULE { +pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE { const padded_buff = try cstr.addNullByte(allocator, dll_path); defer allocator.free(padded_buff); return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound; diff --git a/std/os/zen.zig b/std/os/zen.zig index 2411c5363e..2312b36dea 100644 --- a/std/os/zen.zig +++ b/std/os/zen.zig @@ -8,7 +8,7 @@ pub const Message = struct { type: usize, payload: usize, - pub fn from(mailbox_id: &const MailboxId) Message { + pub fn from(mailbox_id: *const MailboxId) Message { return Message{ .sender = MailboxId.Undefined, .receiver = *mailbox_id, @@ -17,7 +17,7 @@ pub const Message = struct { }; } - pub fn to(mailbox_id: &const MailboxId, msg_type: usize) Message { + pub fn to(mailbox_id: *const MailboxId, msg_type: usize) Message { return Message{ .sender = MailboxId.This, .receiver = *mailbox_id, @@ -26,7 +26,7 @@ pub const Message = struct { }; } - pub fn withData(mailbox_id: &const MailboxId, msg_type: usize, payload: usize) Message { + pub fn withData(mailbox_id: *const MailboxId, msg_type: usize, payload: usize) Message { return Message{ .sender = MailboxId.This, .receiver = *mailbox_id, @@ -67,7 +67,7 @@ pub const getErrno = @import("linux/index.zig").getErrno; use @import("linux/errno.zig"); // TODO: implement this correctly. -pub fn read(fd: i32, buf: &u8, count: usize) usize { +pub fn read(fd: i32, buf: *u8, count: usize) usize { switch (fd) { STDIN_FILENO => { var i: usize = 0; @@ -75,7 +75,7 @@ pub fn read(fd: i32, buf: &u8, count: usize) usize { send(Message.to(Server.Keyboard, 0)); var message = Message.from(MailboxId.This); - receive(&message); + receive(*message); buf[i] = u8(message.payload); } @@ -86,7 +86,7 @@ pub fn read(fd: i32, buf: &u8, count: usize) usize { } // TODO: implement this correctly. -pub fn write(fd: i32, buf: &const u8, count: usize) usize { +pub fn write(fd: i32, buf: *const u8, count: usize) usize { switch (fd) { STDOUT_FILENO, STDERR_FILENO => { var i: usize = 0; @@ -126,22 +126,22 @@ pub fn exit(status: i32) noreturn { unreachable; } -pub fn createPort(mailbox_id: &const MailboxId) void { +pub fn createPort(mailbox_id: *const MailboxId) void { _ = switch (*mailbox_id) { MailboxId.Port => |id| syscall1(Syscall.createPort, id), else => unreachable, }; } -pub fn send(message: &const Message) void { +pub fn send(message: *const Message) void { _ = syscall1(Syscall.send, @ptrToInt(message)); } -pub fn receive(destination: &Message) void { +pub fn receive(destination: *Message) void { _ = syscall1(Syscall.receive, @ptrToInt(destination)); } -pub fn subscribeIRQ(irq: u8, mailbox_id: &const MailboxId) void { +pub fn subscribeIRQ(irq: u8, mailbox_id: *const MailboxId) void { _ = syscall2(Syscall.subscribeIRQ, irq, @ptrToInt(mailbox_id)); } diff --git a/std/rand/index.zig b/std/rand/index.zig index c32309a0fd..3a1a559cd9 100644 --- a/std/rand/index.zig +++ b/std/rand/index.zig @@ -28,15 +28,15 @@ pub const DefaultPrng = Xoroshiro128; pub const DefaultCsprng = Isaac64; pub const Random = struct { - fillFn: fn (r: &Random, buf: []u8) void, + fillFn: fn (r: *Random, buf: []u8) void, /// Read random bytes into the specified buffer until fill. - pub fn bytes(r: &Random, buf: []u8) void { + pub fn bytes(r: *Random, buf: []u8) void { r.fillFn(r, buf); } /// Return a random integer/boolean type. - pub fn scalar(r: &Random, comptime T: type) T { + pub fn scalar(r: *Random, comptime T: type) T { var rand_bytes: [@sizeOf(T)]u8 = undefined; r.bytes(rand_bytes[0..]); @@ -50,7 +50,7 @@ pub const Random = struct { /// Get a random unsigned integer with even distribution between `start` /// inclusive and `end` exclusive. - pub fn range(r: &Random, comptime T: type, start: T, end: T) T { + pub fn range(r: *Random, comptime T: type, start: T, end: T) T { assert(start <= end); if (T.is_signed) { const uint = @IntType(false, T.bit_count); @@ -92,7 +92,7 @@ pub const Random = struct { } /// Return a floating point value evenly distributed in the range [0, 1). - pub fn float(r: &Random, comptime T: type) T { + pub fn float(r: *Random, comptime T: type) T { // Generate a uniform value between [1, 2) and scale down to [0, 1). // Note: The lowest mantissa bit is always set to 0 so we only use half the available range. switch (T) { @@ -113,7 +113,7 @@ pub const Random = struct { /// Return a floating point value normally distributed with mean = 0, stddev = 1. /// /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean. - pub fn floatNorm(r: &Random, comptime T: type) T { + pub fn floatNorm(r: *Random, comptime T: type) T { const value = ziggurat.next_f64(r, ziggurat.NormDist); switch (T) { f32 => return f32(value), @@ -125,7 +125,7 @@ pub const Random = struct { /// Return an exponentially distributed float with a rate parameter of 1. /// /// To use a different rate parameter, use: floatExp(...) / desiredRate. - pub fn floatExp(r: &Random, comptime T: type) T { + pub fn floatExp(r: *Random, comptime T: type) T { const value = ziggurat.next_f64(r, ziggurat.ExpDist); switch (T) { f32 => return f32(value), @@ -135,7 +135,7 @@ pub const Random = struct { } /// Shuffle a slice into a random order. - pub fn shuffle(r: &Random, comptime T: type, buf: []T) void { + pub fn shuffle(r: *Random, comptime T: type, buf: []T) void { if (buf.len < 2) { return; } @@ -159,7 +159,7 @@ const SplitMix64 = struct { return SplitMix64{ .s = seed }; } - pub fn next(self: &SplitMix64) u64 { + pub fn next(self: *SplitMix64) u64 { self.s +%= 0x9e3779b97f4a7c15; var z = self.s; @@ -208,7 +208,7 @@ pub const Pcg = struct { return pcg; } - fn next(self: &Pcg) u32 { + fn next(self: *Pcg) u32 { const l = self.s; self.s = l *% default_multiplier +% (self.i | 1); @@ -218,13 +218,13 @@ pub const Pcg = struct { return (xor_s >> u5(rot)) | (xor_s << u5((0 -% rot) & 31)); } - fn seed(self: &Pcg, init_s: u64) void { + fn seed(self: *Pcg, init_s: u64) void { // Pcg requires 128-bits of seed. var gen = SplitMix64.init(init_s); self.seedTwo(gen.next(), gen.next()); } - fn seedTwo(self: &Pcg, init_s: u64, init_i: u64) void { + fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void { self.s = 0; self.i = (init_s << 1) | 1; self.s = self.s *% default_multiplier +% self.i; @@ -232,7 +232,7 @@ pub const Pcg = struct { self.s = self.s *% default_multiplier +% self.i; } - fn fill(r: &Random, buf: []u8) void { + fn fill(r: *Random, buf: []u8) void { const self = @fieldParentPtr(Pcg, "random", r); var i: usize = 0; @@ -297,7 +297,7 @@ pub const Xoroshiro128 = struct { return x; } - fn next(self: &Xoroshiro128) u64 { + fn next(self: *Xoroshiro128) u64 { const s0 = self.s[0]; var s1 = self.s[1]; const r = s0 +% s1; @@ -310,7 +310,7 @@ pub const Xoroshiro128 = struct { } // Skip 2^64 places ahead in the sequence - fn jump(self: &Xoroshiro128) void { + fn jump(self: *Xoroshiro128) void { var s0: u64 = 0; var s1: u64 = 0; @@ -334,7 +334,7 @@ pub const Xoroshiro128 = struct { self.s[1] = s1; } - fn seed(self: &Xoroshiro128, init_s: u64) void { + fn seed(self: *Xoroshiro128, init_s: u64) void { // Xoroshiro requires 128-bits of seed. var gen = SplitMix64.init(init_s); @@ -342,7 +342,7 @@ pub const Xoroshiro128 = struct { self.s[1] = gen.next(); } - fn fill(r: &Random, buf: []u8) void { + fn fill(r: *Random, buf: []u8) void { const self = @fieldParentPtr(Xoroshiro128, "random", r); var i: usize = 0; @@ -435,7 +435,7 @@ pub const Isaac64 = struct { return isaac; } - fn step(self: &Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void { + fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void { const x = self.m[base + m1]; self.a = mix +% self.m[base + m2]; @@ -446,7 +446,7 @@ pub const Isaac64 = struct { self.r[self.r.len - 1 - base - m1] = self.b; } - fn refill(self: &Isaac64) void { + fn refill(self: *Isaac64) void { const midpoint = self.r.len / 2; self.c +%= 1; @@ -475,7 +475,7 @@ pub const Isaac64 = struct { self.i = 0; } - fn next(self: &Isaac64) u64 { + fn next(self: *Isaac64) u64 { if (self.i >= self.r.len) { self.refill(); } @@ -485,7 +485,7 @@ pub const Isaac64 = struct { return value; } - fn seed(self: &Isaac64, init_s: u64, comptime rounds: usize) void { + fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void { // We ignore the multi-pass requirement since we don't currently expose full access to // seeding the self.m array completely. mem.set(u64, self.m[0..], 0); @@ -551,7 +551,7 @@ pub const Isaac64 = struct { self.i = self.r.len; // trigger refill on first value } - fn fill(r: &Random, buf: []u8) void { + fn fill(r: *Random, buf: []u8) void { const self = @fieldParentPtr(Isaac64, "random", r); var i: usize = 0; @@ -666,7 +666,7 @@ test "Random range" { testRange(&prng.random, 10, 14); } -fn testRange(r: &Random, start: i32, end: i32) void { +fn testRange(r: *Random, start: i32, end: i32) void { const count = usize(end - start); var values_buffer = []bool{false} ** 20; const values = values_buffer[0..count]; diff --git a/std/rand/ziggurat.zig b/std/rand/ziggurat.zig index 7daeb59165..774d3bd52a 100644 --- a/std/rand/ziggurat.zig +++ b/std/rand/ziggurat.zig @@ -12,7 +12,7 @@ const std = @import("../index.zig"); const math = std.math; const Random = std.rand.Random; -pub fn next_f64(random: &Random, comptime tables: &const ZigTable) f64 { +pub fn next_f64(random: *Random, comptime tables: *const ZigTable) f64 { while (true) { // We manually construct a float from parts as we can avoid an extra random lookup here by // using the unused exponent for the lookup table entry. @@ -60,7 +60,7 @@ pub const ZigTable = struct { // whether the distribution is symmetric is_symmetric: bool, // fallback calculation in the case we are in the 0 block - zero_case: fn (&Random, f64) f64, + zero_case: fn (*Random, f64) f64, }; // zigNorInit @@ -70,7 +70,7 @@ fn ZigTableGen( comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, - comptime zero_case: fn (&Random, f64) f64, + comptime zero_case: fn (*Random, f64) f64, ) ZigTable { var tables: ZigTable = undefined; @@ -110,7 +110,7 @@ fn norm_f(x: f64) f64 { fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); } -fn norm_zero_case(random: &Random, u: f64) f64 { +fn norm_zero_case(random: *Random, u: f64) f64 { var x: f64 = 1; var y: f64 = 0; @@ -149,7 +149,7 @@ fn exp_f(x: f64) f64 { fn exp_f_inv(y: f64) f64 { return -math.ln(y); } -fn exp_zero_case(random: &Random, _: f64) f64 { +fn exp_zero_case(random: *Random, _: f64) f64 { return exp_r - math.ln(random.float(f64)); } diff --git a/std/segmented_list.zig b/std/segmented_list.zig index d755135fe8..be9a2071a0 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -87,49 +87,49 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type const ShelfIndex = std.math.Log2Int(usize); prealloc_segment: [prealloc_item_count]T, - dynamic_segments: []&T, - allocator: &Allocator, + dynamic_segments: []*T, + allocator: *Allocator, len: usize, pub const prealloc_count = prealloc_item_count; /// Deinitialize with `deinit` - pub fn init(allocator: &Allocator) Self { + pub fn init(allocator: *Allocator) Self { return Self{ .allocator = allocator, .len = 0, .prealloc_segment = undefined, - .dynamic_segments = []&T{}, + .dynamic_segments = []*T{}, }; } - pub fn deinit(self: &Self) void { + pub fn deinit(self: *Self) void { self.freeShelves(ShelfIndex(self.dynamic_segments.len), 0); self.allocator.free(self.dynamic_segments); self.* = undefined; } - pub fn at(self: &Self, i: usize) &T { + pub fn at(self: *Self, i: usize) *T { assert(i < self.len); return self.uncheckedAt(i); } - pub fn count(self: &const Self) usize { + pub fn count(self: *const Self) usize { return self.len; } - pub fn push(self: &Self, item: &const T) !void { + pub fn push(self: *Self, item: *const T) !void { const new_item_ptr = try self.addOne(); new_item_ptr.* = item.*; } - pub fn pushMany(self: &Self, items: []const T) !void { + pub fn pushMany(self: *Self, items: []const T) !void { for (items) |item| { try self.push(item); } } - pub fn pop(self: &Self) ?T { + pub fn pop(self: *Self) ?T { if (self.len == 0) return null; const index = self.len - 1; @@ -138,7 +138,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return result; } - pub fn addOne(self: &Self) !&T { + pub fn addOne(self: *Self) !*T { const new_length = self.len + 1; try self.growCapacity(new_length); const result = self.uncheckedAt(self.len); @@ -147,7 +147,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } /// Grows or shrinks capacity to match usage. - pub fn setCapacity(self: &Self, new_capacity: usize) !void { + pub fn setCapacity(self: *Self, new_capacity: usize) !void { if (new_capacity <= usize(1) << (prealloc_exp + self.dynamic_segments.len)) { return self.shrinkCapacity(new_capacity); } else { @@ -156,15 +156,15 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } /// Only grows capacity, or retains current capacity - pub fn growCapacity(self: &Self, new_capacity: usize) !void { + pub fn growCapacity(self: *Self, new_capacity: usize) !void { const new_cap_shelf_count = shelfCount(new_capacity); const old_shelf_count = ShelfIndex(self.dynamic_segments.len); if (new_cap_shelf_count > old_shelf_count) { - self.dynamic_segments = try self.allocator.realloc(&T, self.dynamic_segments, new_cap_shelf_count); + self.dynamic_segments = try self.allocator.realloc(*T, self.dynamic_segments, new_cap_shelf_count); var i = old_shelf_count; errdefer { self.freeShelves(i, old_shelf_count); - self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, old_shelf_count); + self.dynamic_segments = self.allocator.shrink(*T, self.dynamic_segments, old_shelf_count); } while (i < new_cap_shelf_count) : (i += 1) { self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; @@ -173,12 +173,12 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } /// Only shrinks capacity or retains current capacity - pub fn shrinkCapacity(self: &Self, new_capacity: usize) void { + pub fn shrinkCapacity(self: *Self, new_capacity: usize) void { if (new_capacity <= prealloc_item_count) { const len = ShelfIndex(self.dynamic_segments.len); self.freeShelves(len, 0); self.allocator.free(self.dynamic_segments); - self.dynamic_segments = []&T{}; + self.dynamic_segments = []*T{}; return; } @@ -190,10 +190,10 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } self.freeShelves(old_shelf_count, new_cap_shelf_count); - self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, new_cap_shelf_count); + self.dynamic_segments = self.allocator.shrink(*T, self.dynamic_segments, new_cap_shelf_count); } - pub fn uncheckedAt(self: &Self, index: usize) &T { + pub fn uncheckedAt(self: *Self, index: usize) *T { if (index < prealloc_item_count) { return &self.prealloc_segment[index]; } @@ -230,7 +230,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return list_index + prealloc_item_count - (usize(1) << ((prealloc_exp + 1) + shelf_index)); } - fn freeShelves(self: &Self, from_count: ShelfIndex, to_count: ShelfIndex) void { + fn freeShelves(self: *Self, from_count: ShelfIndex, to_count: ShelfIndex) void { var i = from_count; while (i != to_count) { i -= 1; @@ -239,13 +239,13 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } pub const Iterator = struct { - list: &Self, + list: *Self, index: usize, box_index: usize, shelf_index: ShelfIndex, shelf_size: usize, - pub fn next(it: &Iterator) ?&T { + pub fn next(it: *Iterator) ?*T { if (it.index >= it.list.len) return null; if (it.index < prealloc_item_count) { const ptr = &it.list.prealloc_segment[it.index]; @@ -269,7 +269,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return ptr; } - pub fn prev(it: &Iterator) ?&T { + pub fn prev(it: *Iterator) ?*T { if (it.index == 0) return null; it.index -= 1; @@ -286,7 +286,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return &it.list.dynamic_segments[it.shelf_index][it.box_index]; } - pub fn peek(it: &Iterator) ?&T { + pub fn peek(it: *Iterator) ?*T { if (it.index >= it.list.len) return null; if (it.index < prealloc_item_count) @@ -295,7 +295,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return &it.list.dynamic_segments[it.shelf_index][it.box_index]; } - pub fn set(it: &Iterator, index: usize) void { + pub fn set(it: *Iterator, index: usize) void { it.index = index; if (index < prealloc_item_count) return; it.shelf_index = shelfIndex(index); @@ -304,7 +304,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } }; - pub fn iterator(self: &Self, start_index: usize) Iterator { + pub fn iterator(self: *Self, start_index: usize) Iterator { var it = Iterator{ .list = self, .index = undefined, @@ -331,7 +331,7 @@ test "std.SegmentedList" { try testSegmentedList(16, a); } -fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void { +fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { var list = SegmentedList(i32, prealloc).init(allocator); defer list.deinit(); diff --git a/std/sort.zig b/std/sort.zig index 4e17718241..1b44c18dd9 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -5,7 +5,7 @@ const math = std.math; const builtin = @import("builtin"); /// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required). -pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool) void { +pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) void { { var i: usize = 1; while (i < items.len) : (i += 1) { @@ -30,7 +30,7 @@ const Range = struct { }; } - fn length(self: &const Range) usize { + fn length(self: *const Range) usize { return self.end - self.start; } }; @@ -58,12 +58,12 @@ const Iterator = struct { }; } - fn begin(self: &Iterator) void { + fn begin(self: *Iterator) void { self.numerator = 0; self.decimal = 0; } - fn nextRange(self: &Iterator) Range { + fn nextRange(self: *Iterator) Range { const start = self.decimal; self.decimal += self.decimal_step; @@ -79,11 +79,11 @@ const Iterator = struct { }; } - fn finished(self: &Iterator) bool { + fn finished(self: *Iterator) bool { return self.decimal >= self.size; } - fn nextLevel(self: &Iterator) bool { + fn nextLevel(self: *Iterator) bool { self.decimal_step += self.decimal_step; self.numerator_step += self.numerator_step; if (self.numerator_step >= self.denominator) { @@ -94,7 +94,7 @@ const Iterator = struct { return (self.decimal_step < self.size); } - fn length(self: &Iterator) usize { + fn length(self: *Iterator) usize { return self.decimal_step; } }; @@ -108,7 +108,7 @@ const Pull = struct { /// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required). /// Currently implemented as block sort. -pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool) void { +pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) void { // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c var cache: [512]T = undefined; @@ -741,7 +741,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &con } // merge operation without a buffer -fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const Range, lessThan: fn (&const T, &const T) bool) void { +fn mergeInPlace(comptime T: type, items: []T, A_arg: *const Range, B_arg: *const Range, lessThan: fn (*const T, *const T) bool) void { if (A_arg.length() == 0 or B_arg.length() == 0) return; // this just repeatedly binary searches into B and rotates A into position. @@ -783,7 +783,7 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const } // merge operation using an internal buffer -fn mergeInternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn (&const T, &const T) bool, buffer: &const Range) void { +fn mergeInternal(comptime T: type, items: []T, A: *const Range, B: *const Range, lessThan: fn (*const T, *const T) bool, buffer: *const Range) void { // whenever we find a value to add to the final array, swap it with the value that's already in that spot // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order var A_count: usize = 0; @@ -819,7 +819,7 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s // combine a linear search with a binary search to reduce the number of comparisons in situations // where have some idea as to how many unique values there are and where the next value might be -fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool, unique: usize) usize { +fn findFirstForward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -833,7 +833,7 @@ fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan); } -fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool, unique: usize) usize { +fn findFirstBackward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -847,7 +847,7 @@ fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &cons return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan); } -fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool, unique: usize) usize { +fn findLastForward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -861,7 +861,7 @@ fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const return binaryLast(T, items, value, Range.init(index - skip, index), lessThan); } -fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool, unique: usize) usize { +fn findLastBackward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -875,7 +875,7 @@ fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const return binaryLast(T, items, value, Range.init(index, index + skip), lessThan); } -fn binaryFirst(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool) usize { +fn binaryFirst(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool) usize { var start = range.start; var end = range.end - 1; if (range.start >= range.end) return range.end; @@ -893,7 +893,7 @@ fn binaryFirst(comptime T: type, items: []T, value: &const T, range: &const Rang return start; } -fn binaryLast(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool) usize { +fn binaryLast(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool) usize { var start = range.start; var end = range.end - 1; if (range.start >= range.end) return range.end; @@ -911,7 +911,7 @@ fn binaryLast(comptime T: type, items: []T, value: &const T, range: &const Range return start; } -fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, lessThan: fn (&const T, &const T) bool, into: []T) void { +fn mergeInto(comptime T: type, from: []T, A: *const Range, B: *const Range, lessThan: fn (*const T, *const T) bool, into: []T) void { var A_index: usize = A.start; var B_index: usize = B.start; const A_last = A.end; @@ -941,7 +941,7 @@ fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, less } } -fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn (&const T, &const T) bool, cache: []T) void { +fn mergeExternal(comptime T: type, items: []T, A: *const Range, B: *const Range, lessThan: fn (*const T, *const T) bool, cache: []T) void { // A fits into the cache, so use that instead of the internal buffer var A_index: usize = 0; var B_index: usize = B.start; @@ -969,26 +969,26 @@ fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range, mem.copy(T, items[insert_index..], cache[A_index..A_last]); } -fn swap(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool, order: &[8]u8, x: usize, y: usize) void { +fn swap(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool, order: *[8]u8, x: usize, y: usize) void { if (lessThan(items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(items[x], items[y]))) { mem.swap(T, &items[x], &items[y]); mem.swap(u8, &(order.*)[x], &(order.*)[y]); } } -fn i32asc(lhs: &const i32, rhs: &const i32) bool { +fn i32asc(lhs: *const i32, rhs: *const i32) bool { return lhs.* < rhs.*; } -fn i32desc(lhs: &const i32, rhs: &const i32) bool { +fn i32desc(lhs: *const i32, rhs: *const i32) bool { return rhs.* < lhs.*; } -fn u8asc(lhs: &const u8, rhs: &const u8) bool { +fn u8asc(lhs: *const u8, rhs: *const u8) bool { return lhs.* < rhs.*; } -fn u8desc(lhs: &const u8, rhs: &const u8) bool { +fn u8desc(lhs: *const u8, rhs: *const u8) bool { return rhs.* < lhs.*; } @@ -1125,7 +1125,7 @@ const IdAndValue = struct { id: usize, value: i32, }; -fn cmpByValue(a: &const IdAndValue, b: &const IdAndValue) bool { +fn cmpByValue(a: *const IdAndValue, b: *const IdAndValue) bool { return i32asc(a.value, b.value); } @@ -1324,7 +1324,7 @@ test "sort fuzz testing" { var fixed_buffer_mem: [100 * 1024]u8 = undefined; -fn fuzzTest(rng: &std.rand.Random) void { +fn fuzzTest(rng: *std.rand.Random) void { const array_size = rng.range(usize, 0, 1000); var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); var array = fixed_allocator.allocator.alloc(IdAndValue, array_size) catch unreachable; @@ -1345,7 +1345,7 @@ fn fuzzTest(rng: &std.rand.Random) void { } } -pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool) T { +pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) T { var i: usize = 0; var smallest = items[0]; for (items[1..]) |item| { @@ -1356,7 +1356,7 @@ pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &cons return smallest; } -pub fn max(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool) T { +pub fn max(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) T { var i: usize = 0; var biggest = items[0]; for (items[1..]) |item| { diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index c10f4aa806..5ed7874ca5 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -5,7 +5,7 @@ const root = @import("@root"); const std = @import("std"); const builtin = @import("builtin"); -var argc_ptr: &usize = undefined; +var argc_ptr: *usize = undefined; comptime { const strong_linkage = builtin.GlobalLinkage.Strong; @@ -28,12 +28,12 @@ nakedcc fn _start() noreturn { switch (builtin.arch) { builtin.Arch.x86_64 => { argc_ptr = asm ("lea (%%rsp), %[argc]" - : [argc] "=r" (-> &usize) + : [argc] "=r" (-> *usize) ); }, builtin.Arch.i386 => { argc_ptr = asm ("lea (%%esp), %[argc]" - : [argc] "=r" (-> &usize) + : [argc] "=r" (-> *usize) ); }, else => @compileError("unsupported arch"), @@ -51,13 +51,13 @@ extern fn WinMainCRTStartup() noreturn { fn posixCallMainAndExit() noreturn { const argc = argc_ptr.*; - const argv = @ptrCast(&&u8, &argc_ptr[1]); - const envp_nullable = @ptrCast(&?&u8, &argv[argc + 1]); + const argv = @ptrCast(**u8, &argc_ptr[1]); + const envp_nullable = @ptrCast(*?*u8, &argv[argc + 1]); var envp_count: usize = 0; while (envp_nullable[envp_count]) |_| : (envp_count += 1) {} - const envp = @ptrCast(&&u8, envp_nullable)[0..envp_count]; + const envp = @ptrCast(**u8, envp_nullable)[0..envp_count]; if (builtin.os == builtin.Os.linux) { - const auxv = &@ptrCast(&usize, envp.ptr)[envp_count + 1]; + const auxv = &@ptrCast(*usize, envp.ptr)[envp_count + 1]; var i: usize = 0; while (auxv[i] != 0) : (i += 2) { if (auxv[i] < std.os.linux_aux_raw.len) std.os.linux_aux_raw[auxv[i]] = auxv[i + 1]; @@ -68,16 +68,16 @@ fn posixCallMainAndExit() noreturn { std.os.posix.exit(callMainWithArgs(argc, argv, envp)); } -fn callMainWithArgs(argc: usize, argv: &&u8, envp: []&u8) u8 { +fn callMainWithArgs(argc: usize, argv: **u8, envp: []*u8) u8 { std.os.ArgIteratorPosix.raw = argv[0..argc]; std.os.posix_environ_raw = envp; return callMain(); } -extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) i32 { +extern fn main(c_argc: i32, c_argv: **u8, c_envp: *?*u8) i32 { var env_count: usize = 0; while (c_envp[env_count] != null) : (env_count += 1) {} - const envp = @ptrCast(&&u8, c_envp)[0..env_count]; + const envp = @ptrCast(**u8, c_envp)[0..env_count]; return callMainWithArgs(usize(c_argc), c_argv, envp); } diff --git a/std/special/build_file_template.zig b/std/special/build_file_template.zig index 1c06c93cdc..1e3eb01136 100644 --- a/std/special/build_file_template.zig +++ b/std/special/build_file_template.zig @@ -1,10 +1,10 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); const exe = b.addExecutable("YOUR_NAME_HERE", "src/main.zig"); exe.setBuildMode(mode); - b.default_step.dependOn(&exe.step); + b.default_step.dependOn(*exe.step); b.installArtifact(exe); } diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index 3ff11bbee4..3471d6ed21 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -129,7 +129,7 @@ pub fn main() !void { }; } -fn runBuild(builder: &Builder) error!void { +fn runBuild(builder: *Builder) error!void { switch (@typeId(@typeOf(root.build).ReturnType)) { builtin.TypeId.Void => root.build(builder), builtin.TypeId.ErrorUnion => try root.build(builder), @@ -137,7 +137,7 @@ fn runBuild(builder: &Builder) error!void { } } -fn usage(builder: &Builder, already_ran_build: bool, out_stream: var) !void { +fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { // run the build script to collect the options if (!already_ran_build) { builder.setInstallPrefix(null); @@ -195,7 +195,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: var) !void { ); } -fn usageAndErr(builder: &Builder, already_ran_build: bool, out_stream: var) error { +fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: var) error { usage(builder, already_ran_build, out_stream) catch {}; return error.InvalidArgs; } diff --git a/std/special/builtin.zig b/std/special/builtin.zig index 63149d5161..9c9cd35103 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -5,7 +5,7 @@ const builtin = @import("builtin"); // Avoid dragging in the runtime safety mechanisms into this .o file, // unless we're trying to test this file. -pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn { +pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { if (builtin.is_test) { @setCold(true); @import("std").debug.panic("{}", msg); @@ -14,7 +14,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn } } -export fn memset(dest: ?&u8, c: u8, n: usize) ?&u8 { +export fn memset(dest: ?*u8, c: u8, n: usize) ?*u8 { @setRuntimeSafety(false); var index: usize = 0; @@ -24,7 +24,7 @@ export fn memset(dest: ?&u8, c: u8, n: usize) ?&u8 { return dest; } -export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) ?&u8 { +export fn memcpy(noalias dest: ?*u8, noalias src: ?*const u8, n: usize) ?*u8 { @setRuntimeSafety(false); var index: usize = 0; @@ -34,7 +34,7 @@ export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) ?&u8 { return dest; } -export fn memmove(dest: ?&u8, src: ?&const u8, n: usize) ?&u8 { +export fn memmove(dest: ?*u8, src: ?*const u8, n: usize) ?*u8 { @setRuntimeSafety(false); if (@ptrToInt(dest) < @ptrToInt(src)) { diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index 3e014d4d16..d328324320 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -78,7 +78,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; // Avoid dragging in the runtime safety mechanisms into this .o file, // unless we're trying to test this file. -pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn { +pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { @setCold(true); if (is_test) { std.debug.panic("{}", msg); @@ -284,7 +284,7 @@ nakedcc fn ___chkstk_ms() align(4) void { ); } -extern fn __udivmodsi4(a: u32, b: u32, rem: &u32) u32 { +extern fn __udivmodsi4(a: u32, b: u32, rem: *u32) u32 { @setRuntimeSafety(is_test); const d = __udivsi3(a, b); diff --git a/std/special/compiler_rt/udivmod.zig b/std/special/compiler_rt/udivmod.zig index 0dee5e45f6..894dd02239 100644 --- a/std/special/compiler_rt/udivmod.zig +++ b/std/special/compiler_rt/udivmod.zig @@ -7,15 +7,15 @@ const low = switch (builtin.endian) { }; const high = 1 - low; -pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: ?&DoubleInt) DoubleInt { +pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: ?*DoubleInt) DoubleInt { @setRuntimeSafety(is_test); const SingleInt = @IntType(false, @divExact(DoubleInt.bit_count, 2)); const SignedDoubleInt = @IntType(true, DoubleInt.bit_count); const Log2SingleInt = @import("std").math.Log2Int(SingleInt); - const n = @ptrCast(&const [2]SingleInt, &a).*; // TODO issue #421 - const d = @ptrCast(&const [2]SingleInt, &b).*; // TODO issue #421 + const n = @ptrCast(*const [2]SingleInt, &a).*; // TODO issue #421 + const d = @ptrCast(*const [2]SingleInt, &b).*; // TODO issue #421 var q: [2]SingleInt = undefined; var r: [2]SingleInt = undefined; var sr: c_uint = undefined; @@ -57,7 +57,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: if (maybe_rem) |rem| { r[high] = n[high] % d[high]; r[low] = 0; - rem.* = @ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 + rem.* = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 } return n[high] / d[high]; } @@ -69,7 +69,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: if (maybe_rem) |rem| { r[low] = n[low]; r[high] = n[high] & (d[high] - 1); - rem.* = @ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 + rem.* = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 } return n[high] >> Log2SingleInt(@ctz(d[high])); } @@ -109,7 +109,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: sr = @ctz(d[low]); q[high] = n[high] >> Log2SingleInt(sr); q[low] = (n[high] << Log2SingleInt(SingleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr)); - return @ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &q[0]).*; // TODO issue #421 + return @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &q[0]).*; // TODO issue #421 } // K X // --- @@ -183,13 +183,13 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // r.all -= b; // carry = 1; // } - r_all = @ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 + r_all = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 const s: SignedDoubleInt = SignedDoubleInt(b -% r_all -% 1) >> (DoubleInt.bit_count - 1); carry = u32(s & 1); r_all -= b & @bitCast(DoubleInt, s); - r = @ptrCast(&[2]SingleInt, &r_all).*; // TODO issue #421 + r = @ptrCast(*[2]SingleInt, &r_all).*; // TODO issue #421 } - const q_all = ((@ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &q[0]).*) << 1) | carry; // TODO issue #421 + const q_all = ((@ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &q[0]).*) << 1) | carry; // TODO issue #421 if (maybe_rem) |rem| { rem.* = r_all; } diff --git a/std/special/compiler_rt/udivmoddi4.zig b/std/special/compiler_rt/udivmoddi4.zig index 6cc54bb6bf..de86c845e5 100644 --- a/std/special/compiler_rt/udivmoddi4.zig +++ b/std/special/compiler_rt/udivmoddi4.zig @@ -1,7 +1,7 @@ const udivmod = @import("udivmod.zig").udivmod; const builtin = @import("builtin"); -pub extern fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?&u64) u64 { +pub extern fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?*u64) u64 { @setRuntimeSafety(builtin.is_test); return udivmod(u64, a, b, maybe_rem); } diff --git a/std/special/compiler_rt/udivmodti4.zig b/std/special/compiler_rt/udivmodti4.zig index 816f82b900..3fa596442f 100644 --- a/std/special/compiler_rt/udivmodti4.zig +++ b/std/special/compiler_rt/udivmodti4.zig @@ -2,12 +2,12 @@ const udivmod = @import("udivmod.zig").udivmod; const builtin = @import("builtin"); const compiler_rt = @import("index.zig"); -pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 { +pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?*u128) u128 { @setRuntimeSafety(builtin.is_test); return udivmod(u128, a, b, maybe_rem); } -pub extern fn __udivmodti4_windows_x86_64(a: &const u128, b: &const u128, maybe_rem: ?&u128) void { +pub extern fn __udivmodti4_windows_x86_64(a: *const u128, b: *const u128, maybe_rem: ?*u128) void { @setRuntimeSafety(builtin.is_test); compiler_rt.setXmm0(u128, udivmod(u128, a.*, b.*, maybe_rem)); } diff --git a/std/special/compiler_rt/udivti3.zig b/std/special/compiler_rt/udivti3.zig index ad0f09e733..510e21ac1d 100644 --- a/std/special/compiler_rt/udivti3.zig +++ b/std/special/compiler_rt/udivti3.zig @@ -6,7 +6,7 @@ pub extern fn __udivti3(a: u128, b: u128) u128 { return udivmodti4.__udivmodti4(a, b, null); } -pub extern fn __udivti3_windows_x86_64(a: &const u128, b: &const u128) void { +pub extern fn __udivti3_windows_x86_64(a: *const u128, b: *const u128) void { @setRuntimeSafety(builtin.is_test); udivmodti4.__udivmodti4_windows_x86_64(a, b, null); } diff --git a/std/special/compiler_rt/umodti3.zig b/std/special/compiler_rt/umodti3.zig index 11e2955bb3..9551e63a6f 100644 --- a/std/special/compiler_rt/umodti3.zig +++ b/std/special/compiler_rt/umodti3.zig @@ -9,7 +9,7 @@ pub extern fn __umodti3(a: u128, b: u128) u128 { return r; } -pub extern fn __umodti3_windows_x86_64(a: &const u128, b: &const u128) void { +pub extern fn __umodti3_windows_x86_64(a: *const u128, b: *const u128) void { @setRuntimeSafety(builtin.is_test); compiler_rt.setXmm0(u128, __umodti3(a.*, b.*)); } diff --git a/std/special/panic.zig b/std/special/panic.zig index 8f933ddd97..ca1caea73c 100644 --- a/std/special/panic.zig +++ b/std/special/panic.zig @@ -6,7 +6,7 @@ const builtin = @import("builtin"); const std = @import("std"); -pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn { +pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { @setCold(true); switch (builtin.os) { // TODO: fix panic in zen. diff --git a/std/unicode.zig b/std/unicode.zig index 36f04778f4..3d1bebdb55 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -196,7 +196,7 @@ pub const Utf8View = struct { } } - pub fn iterator(s: &const Utf8View) Utf8Iterator { + pub fn iterator(s: *const Utf8View) Utf8Iterator { return Utf8Iterator{ .bytes = s.bytes, .i = 0, @@ -208,7 +208,7 @@ const Utf8Iterator = struct { bytes: []const u8, i: usize, - pub fn nextCodepointSlice(it: &Utf8Iterator) ?[]const u8 { + pub fn nextCodepointSlice(it: *Utf8Iterator) ?[]const u8 { if (it.i >= it.bytes.len) { return null; } @@ -219,7 +219,7 @@ const Utf8Iterator = struct { return it.bytes[it.i - cp_len .. it.i]; } - pub fn nextCodepoint(it: &Utf8Iterator) ?u32 { + pub fn nextCodepoint(it: *Utf8Iterator) ?u32 { const slice = it.nextCodepointSlice() ?? return null; switch (slice.len) { diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 56d4f9c393..4d25ceb7db 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -9,26 +9,26 @@ pub const TokenIndex = usize; pub const Tree = struct { source: []const u8, tokens: TokenList, - root_node: &Node.Root, + root_node: *Node.Root, arena_allocator: std.heap.ArenaAllocator, errors: ErrorList, pub const TokenList = SegmentedList(Token, 64); pub const ErrorList = SegmentedList(Error, 0); - pub fn deinit(self: &Tree) void { + pub fn deinit(self: *Tree) void { self.arena_allocator.deinit(); } - pub fn renderError(self: &Tree, parse_error: &Error, stream: var) !void { + pub fn renderError(self: *Tree, parse_error: *Error, stream: var) !void { return parse_error.render(&self.tokens, stream); } - pub fn tokenSlice(self: &Tree, token_index: TokenIndex) []const u8 { + pub fn tokenSlice(self: *Tree, token_index: TokenIndex) []const u8 { return self.tokenSlicePtr(self.tokens.at(token_index)); } - pub fn tokenSlicePtr(self: &Tree, token: &const Token) []const u8 { + pub fn tokenSlicePtr(self: *Tree, token: *const Token) []const u8 { return self.source[token.start..token.end]; } @@ -39,7 +39,7 @@ pub const Tree = struct { line_end: usize, }; - pub fn tokenLocationPtr(self: &Tree, start_index: usize, token: &const Token) Location { + pub fn tokenLocationPtr(self: *Tree, start_index: usize, token: *const Token) Location { var loc = Location{ .line = 0, .column = 0, @@ -64,24 +64,24 @@ pub const Tree = struct { return loc; } - pub fn tokenLocation(self: &Tree, start_index: usize, token_index: TokenIndex) Location { + pub fn tokenLocation(self: *Tree, start_index: usize, token_index: TokenIndex) Location { return self.tokenLocationPtr(start_index, self.tokens.at(token_index)); } - pub fn tokensOnSameLine(self: &Tree, token1_index: TokenIndex, token2_index: TokenIndex) bool { + pub fn tokensOnSameLine(self: *Tree, token1_index: TokenIndex, token2_index: TokenIndex) bool { return self.tokensOnSameLinePtr(self.tokens.at(token1_index), self.tokens.at(token2_index)); } - pub fn tokensOnSameLinePtr(self: &Tree, token1: &const Token, token2: &const Token) bool { + pub fn tokensOnSameLinePtr(self: *Tree, token1: *const Token, token2: *const Token) bool { return mem.indexOfScalar(u8, self.source[token1.end..token2.start], '\n') == null; } - pub fn dump(self: &Tree) void { + pub fn dump(self: *Tree) void { self.root_node.base.dump(0); } /// Skips over comments - pub fn prevToken(self: &Tree, token_index: TokenIndex) TokenIndex { + pub fn prevToken(self: *Tree, token_index: TokenIndex) TokenIndex { var index = token_index - 1; while (self.tokens.at(index).id == Token.Id.LineComment) { index -= 1; @@ -90,7 +90,7 @@ pub const Tree = struct { } /// Skips over comments - pub fn nextToken(self: &Tree, token_index: TokenIndex) TokenIndex { + pub fn nextToken(self: *Tree, token_index: TokenIndex) TokenIndex { var index = token_index + 1; while (self.tokens.at(index).id == Token.Id.LineComment) { index += 1; @@ -120,7 +120,7 @@ pub const Error = union(enum) { ExpectedToken: ExpectedToken, ExpectedCommaOrEnd: ExpectedCommaOrEnd, - pub fn render(self: &const Error, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const Error, tokens: *Tree.TokenList, stream: var) !void { switch (self.*) { // TODO https://github.com/ziglang/zig/issues/683 @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream), @@ -145,7 +145,7 @@ pub const Error = union(enum) { } } - pub fn loc(self: &const Error) TokenIndex { + pub fn loc(self: *const Error) TokenIndex { switch (self.*) { // TODO https://github.com/ziglang/zig/issues/683 @TagType(Error).InvalidToken => |x| return x.token, @@ -188,17 +188,17 @@ pub const Error = union(enum) { pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier"); pub const ExpectedCall = struct { - node: &Node, + node: *Node, - pub fn render(self: &const ExpectedCall, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const ExpectedCall, tokens: *Tree.TokenList, stream: var) !void { return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", @tagName(self.node.id)); } }; pub const ExpectedCallOrFnProto = struct { - node: &Node, + node: *Node, - pub fn render(self: &const ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const ExpectedCallOrFnProto, tokens: *Tree.TokenList, stream: var) !void { return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id)); } }; @@ -207,7 +207,7 @@ pub const Error = union(enum) { token: TokenIndex, expected_id: @TagType(Token.Id), - pub fn render(self: &const ExpectedToken, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const ExpectedToken, tokens: *Tree.TokenList, stream: var) !void { const token_name = @tagName(tokens.at(self.token).id); return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name); } @@ -217,7 +217,7 @@ pub const Error = union(enum) { token: TokenIndex, end_id: @TagType(Token.Id), - pub fn render(self: &const ExpectedCommaOrEnd, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const ExpectedCommaOrEnd, tokens: *Tree.TokenList, stream: var) !void { const token_name = @tagName(tokens.at(self.token).id); return stream.print("expected ',' or {}, found {}", @tagName(self.end_id), token_name); } @@ -229,7 +229,7 @@ pub const Error = union(enum) { token: TokenIndex, - pub fn render(self: &const ThisError, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void { const token_name = @tagName(tokens.at(self.token).id); return stream.print(msg, token_name); } @@ -242,7 +242,7 @@ pub const Error = union(enum) { token: TokenIndex, - pub fn render(self: &const ThisError, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void { return stream.write(msg); } }; @@ -320,14 +320,14 @@ pub const Node = struct { FieldInitializer, }; - pub fn cast(base: &Node, comptime T: type) ?&T { + pub fn cast(base: *Node, comptime T: type) ?*T { if (base.id == comptime typeToId(T)) { return @fieldParentPtr(T, "base", base); } return null; } - pub fn iterate(base: &Node, index: usize) ?&Node { + pub fn iterate(base: *Node, index: usize) ?*Node { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { @@ -338,7 +338,7 @@ pub const Node = struct { unreachable; } - pub fn firstToken(base: &Node) TokenIndex { + pub fn firstToken(base: *Node) TokenIndex { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { @@ -349,7 +349,7 @@ pub const Node = struct { unreachable; } - pub fn lastToken(base: &Node) TokenIndex { + pub fn lastToken(base: *Node) TokenIndex { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { @@ -370,7 +370,7 @@ pub const Node = struct { unreachable; } - pub fn requireSemiColon(base: &const Node) bool { + pub fn requireSemiColon(base: *const Node) bool { var n = base; while (true) { switch (n.id) { @@ -443,7 +443,7 @@ pub const Node = struct { } } - pub fn dump(self: &Node, indent: usize) void { + pub fn dump(self: *Node, indent: usize) void { { var i: usize = 0; while (i < indent) : (i += 1) { @@ -460,44 +460,44 @@ pub const Node = struct { pub const Root = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, decls: DeclList, eof_token: TokenIndex, - pub const DeclList = SegmentedList(&Node, 4); + pub const DeclList = SegmentedList(*Node, 4); - pub fn iterate(self: &Root, index: usize) ?&Node { + pub fn iterate(self: *Root, index: usize) ?*Node { if (index < self.decls.len) { return self.decls.at(index).*; } return null; } - pub fn firstToken(self: &Root) TokenIndex { + pub fn firstToken(self: *Root) TokenIndex { return if (self.decls.len == 0) self.eof_token else (self.decls.at(0).*).firstToken(); } - pub fn lastToken(self: &Root) TokenIndex { + pub fn lastToken(self: *Root) TokenIndex { return if (self.decls.len == 0) self.eof_token else (self.decls.at(self.decls.len - 1).*).lastToken(); } }; pub const VarDecl = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, visib_token: ?TokenIndex, name_token: TokenIndex, eq_token: TokenIndex, mut_token: TokenIndex, comptime_token: ?TokenIndex, extern_export_token: ?TokenIndex, - lib_name: ?&Node, - type_node: ?&Node, - align_node: ?&Node, - init_node: ?&Node, + lib_name: ?*Node, + type_node: ?*Node, + align_node: ?*Node, + init_node: ?*Node, semicolon_token: TokenIndex, - pub fn iterate(self: &VarDecl, index: usize) ?&Node { + pub fn iterate(self: *VarDecl, index: usize) ?*Node { var i = index; if (self.type_node) |type_node| { @@ -518,7 +518,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &VarDecl) TokenIndex { + pub fn firstToken(self: *VarDecl) TokenIndex { if (self.visib_token) |visib_token| return visib_token; if (self.comptime_token) |comptime_token| return comptime_token; if (self.extern_export_token) |extern_export_token| return extern_export_token; @@ -526,20 +526,20 @@ pub const Node = struct { return self.mut_token; } - pub fn lastToken(self: &VarDecl) TokenIndex { + pub fn lastToken(self: *VarDecl) TokenIndex { return self.semicolon_token; } }; pub const Use = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, visib_token: ?TokenIndex, use_token: TokenIndex, - expr: &Node, + expr: *Node, semicolon_token: TokenIndex, - pub fn iterate(self: &Use, index: usize) ?&Node { + pub fn iterate(self: *Use, index: usize) ?*Node { var i = index; if (i < 1) return self.expr; @@ -548,12 +548,12 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Use) TokenIndex { + pub fn firstToken(self: *Use) TokenIndex { if (self.visib_token) |visib_token| return visib_token; return self.use_token; } - pub fn lastToken(self: &Use) TokenIndex { + pub fn lastToken(self: *Use) TokenIndex { return self.semicolon_token; } }; @@ -564,9 +564,9 @@ pub const Node = struct { decls: DeclList, rbrace_token: TokenIndex, - pub const DeclList = SegmentedList(&Node, 2); + pub const DeclList = SegmentedList(*Node, 2); - pub fn iterate(self: &ErrorSetDecl, index: usize) ?&Node { + pub fn iterate(self: *ErrorSetDecl, index: usize) ?*Node { var i = index; if (i < self.decls.len) return self.decls.at(i).*; @@ -575,11 +575,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ErrorSetDecl) TokenIndex { + pub fn firstToken(self: *ErrorSetDecl) TokenIndex { return self.error_token; } - pub fn lastToken(self: &ErrorSetDecl) TokenIndex { + pub fn lastToken(self: *ErrorSetDecl) TokenIndex { return self.rbrace_token; } }; @@ -597,11 +597,11 @@ pub const Node = struct { const InitArg = union(enum) { None, - Enum: ?&Node, - Type: &Node, + Enum: ?*Node, + Type: *Node, }; - pub fn iterate(self: &ContainerDecl, index: usize) ?&Node { + pub fn iterate(self: *ContainerDecl, index: usize) ?*Node { var i = index; switch (self.init_arg_expr) { @@ -618,26 +618,26 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ContainerDecl) TokenIndex { + pub fn firstToken(self: *ContainerDecl) TokenIndex { if (self.layout_token) |layout_token| { return layout_token; } return self.kind_token; } - pub fn lastToken(self: &ContainerDecl) TokenIndex { + pub fn lastToken(self: *ContainerDecl) TokenIndex { return self.rbrace_token; } }; pub const StructField = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, visib_token: ?TokenIndex, name_token: TokenIndex, - type_expr: &Node, + type_expr: *Node, - pub fn iterate(self: &StructField, index: usize) ?&Node { + pub fn iterate(self: *StructField, index: usize) ?*Node { var i = index; if (i < 1) return self.type_expr; @@ -646,24 +646,24 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &StructField) TokenIndex { + pub fn firstToken(self: *StructField) TokenIndex { if (self.visib_token) |visib_token| return visib_token; return self.name_token; } - pub fn lastToken(self: &StructField) TokenIndex { + pub fn lastToken(self: *StructField) TokenIndex { return self.type_expr.lastToken(); } }; pub const UnionTag = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, name_token: TokenIndex, - type_expr: ?&Node, - value_expr: ?&Node, + type_expr: ?*Node, + value_expr: ?*Node, - pub fn iterate(self: &UnionTag, index: usize) ?&Node { + pub fn iterate(self: *UnionTag, index: usize) ?*Node { var i = index; if (self.type_expr) |type_expr| { @@ -679,11 +679,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &UnionTag) TokenIndex { + pub fn firstToken(self: *UnionTag) TokenIndex { return self.name_token; } - pub fn lastToken(self: &UnionTag) TokenIndex { + pub fn lastToken(self: *UnionTag) TokenIndex { if (self.value_expr) |value_expr| { return value_expr.lastToken(); } @@ -697,11 +697,11 @@ pub const Node = struct { pub const EnumTag = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, name_token: TokenIndex, - value: ?&Node, + value: ?*Node, - pub fn iterate(self: &EnumTag, index: usize) ?&Node { + pub fn iterate(self: *EnumTag, index: usize) ?*Node { var i = index; if (self.value) |value| { @@ -712,11 +712,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &EnumTag) TokenIndex { + pub fn firstToken(self: *EnumTag) TokenIndex { return self.name_token; } - pub fn lastToken(self: &EnumTag) TokenIndex { + pub fn lastToken(self: *EnumTag) TokenIndex { if (self.value) |value| { return value.lastToken(); } @@ -727,25 +727,25 @@ pub const Node = struct { pub const ErrorTag = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, name_token: TokenIndex, - pub fn iterate(self: &ErrorTag, index: usize) ?&Node { + pub fn iterate(self: *ErrorTag, index: usize) ?*Node { var i = index; if (self.doc_comments) |comments| { - if (i < 1) return &comments.base; + if (i < 1) return *comments.base; i -= 1; } return null; } - pub fn firstToken(self: &ErrorTag) TokenIndex { + pub fn firstToken(self: *ErrorTag) TokenIndex { return self.name_token; } - pub fn lastToken(self: &ErrorTag) TokenIndex { + pub fn lastToken(self: *ErrorTag) TokenIndex { return self.name_token; } }; @@ -754,15 +754,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &Identifier, index: usize) ?&Node { + pub fn iterate(self: *Identifier, index: usize) ?*Node { return null; } - pub fn firstToken(self: &Identifier) TokenIndex { + pub fn firstToken(self: *Identifier) TokenIndex { return self.token; } - pub fn lastToken(self: &Identifier) TokenIndex { + pub fn lastToken(self: *Identifier) TokenIndex { return self.token; } }; @@ -770,10 +770,10 @@ pub const Node = struct { pub const AsyncAttribute = struct { base: Node, async_token: TokenIndex, - allocator_type: ?&Node, + allocator_type: ?*Node, rangle_bracket: ?TokenIndex, - pub fn iterate(self: &AsyncAttribute, index: usize) ?&Node { + pub fn iterate(self: *AsyncAttribute, index: usize) ?*Node { var i = index; if (self.allocator_type) |allocator_type| { @@ -784,11 +784,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &AsyncAttribute) TokenIndex { + pub fn firstToken(self: *AsyncAttribute) TokenIndex { return self.async_token; } - pub fn lastToken(self: &AsyncAttribute) TokenIndex { + pub fn lastToken(self: *AsyncAttribute) TokenIndex { if (self.rangle_bracket) |rangle_bracket| { return rangle_bracket; } @@ -799,7 +799,7 @@ pub const Node = struct { pub const FnProto = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, visib_token: ?TokenIndex, fn_token: TokenIndex, name_token: ?TokenIndex, @@ -808,19 +808,19 @@ pub const Node = struct { var_args_token: ?TokenIndex, extern_export_inline_token: ?TokenIndex, cc_token: ?TokenIndex, - async_attr: ?&AsyncAttribute, - body_node: ?&Node, - lib_name: ?&Node, // populated if this is an extern declaration - align_expr: ?&Node, // populated if align(A) is present + async_attr: ?*AsyncAttribute, + body_node: ?*Node, + lib_name: ?*Node, // populated if this is an extern declaration + align_expr: ?*Node, // populated if align(A) is present - pub const ParamList = SegmentedList(&Node, 2); + pub const ParamList = SegmentedList(*Node, 2); pub const ReturnType = union(enum) { - Explicit: &Node, - InferErrorSet: &Node, + Explicit: *Node, + InferErrorSet: *Node, }; - pub fn iterate(self: &FnProto, index: usize) ?&Node { + pub fn iterate(self: *FnProto, index: usize) ?*Node { var i = index; if (self.lib_name) |lib_name| { @@ -856,7 +856,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &FnProto) TokenIndex { + pub fn firstToken(self: *FnProto) TokenIndex { if (self.visib_token) |visib_token| return visib_token; if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token; assert(self.lib_name == null); @@ -864,7 +864,7 @@ pub const Node = struct { return self.fn_token; } - pub fn lastToken(self: &FnProto) TokenIndex { + pub fn lastToken(self: *FnProto) TokenIndex { if (self.body_node) |body_node| return body_node.lastToken(); switch (self.return_type) { // TODO allow this and next prong to share bodies since the types are the same @@ -881,10 +881,10 @@ pub const Node = struct { pub const Result = struct { arrow_token: TokenIndex, - return_type: &Node, + return_type: *Node, }; - pub fn iterate(self: &PromiseType, index: usize) ?&Node { + pub fn iterate(self: *PromiseType, index: usize) ?*Node { var i = index; if (self.result) |result| { @@ -895,11 +895,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PromiseType) TokenIndex { + pub fn firstToken(self: *PromiseType) TokenIndex { return self.promise_token; } - pub fn lastToken(self: &PromiseType) TokenIndex { + pub fn lastToken(self: *PromiseType) TokenIndex { if (self.result) |result| return result.return_type.lastToken(); return self.promise_token; } @@ -910,10 +910,10 @@ pub const Node = struct { comptime_token: ?TokenIndex, noalias_token: ?TokenIndex, name_token: ?TokenIndex, - type_node: &Node, + type_node: *Node, var_args_token: ?TokenIndex, - pub fn iterate(self: &ParamDecl, index: usize) ?&Node { + pub fn iterate(self: *ParamDecl, index: usize) ?*Node { var i = index; if (i < 1) return self.type_node; @@ -922,14 +922,14 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ParamDecl) TokenIndex { + pub fn firstToken(self: *ParamDecl) TokenIndex { if (self.comptime_token) |comptime_token| return comptime_token; if (self.noalias_token) |noalias_token| return noalias_token; if (self.name_token) |name_token| return name_token; return self.type_node.firstToken(); } - pub fn lastToken(self: &ParamDecl) TokenIndex { + pub fn lastToken(self: *ParamDecl) TokenIndex { if (self.var_args_token) |var_args_token| return var_args_token; return self.type_node.lastToken(); } @@ -944,7 +944,7 @@ pub const Node = struct { pub const StatementList = Root.DeclList; - pub fn iterate(self: &Block, index: usize) ?&Node { + pub fn iterate(self: *Block, index: usize) ?*Node { var i = index; if (i < self.statements.len) return self.statements.at(i).*; @@ -953,7 +953,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Block) TokenIndex { + pub fn firstToken(self: *Block) TokenIndex { if (self.label) |label| { return label; } @@ -961,7 +961,7 @@ pub const Node = struct { return self.lbrace; } - pub fn lastToken(self: &Block) TokenIndex { + pub fn lastToken(self: *Block) TokenIndex { return self.rbrace; } }; @@ -970,14 +970,14 @@ pub const Node = struct { base: Node, defer_token: TokenIndex, kind: Kind, - expr: &Node, + expr: *Node, const Kind = enum { Error, Unconditional, }; - pub fn iterate(self: &Defer, index: usize) ?&Node { + pub fn iterate(self: *Defer, index: usize) ?*Node { var i = index; if (i < 1) return self.expr; @@ -986,22 +986,22 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Defer) TokenIndex { + pub fn firstToken(self: *Defer) TokenIndex { return self.defer_token; } - pub fn lastToken(self: &Defer) TokenIndex { + pub fn lastToken(self: *Defer) TokenIndex { return self.expr.lastToken(); } }; pub const Comptime = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, comptime_token: TokenIndex, - expr: &Node, + expr: *Node, - pub fn iterate(self: &Comptime, index: usize) ?&Node { + pub fn iterate(self: *Comptime, index: usize) ?*Node { var i = index; if (i < 1) return self.expr; @@ -1010,11 +1010,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Comptime) TokenIndex { + pub fn firstToken(self: *Comptime) TokenIndex { return self.comptime_token; } - pub fn lastToken(self: &Comptime) TokenIndex { + pub fn lastToken(self: *Comptime) TokenIndex { return self.expr.lastToken(); } }; @@ -1022,10 +1022,10 @@ pub const Node = struct { pub const Payload = struct { base: Node, lpipe: TokenIndex, - error_symbol: &Node, + error_symbol: *Node, rpipe: TokenIndex, - pub fn iterate(self: &Payload, index: usize) ?&Node { + pub fn iterate(self: *Payload, index: usize) ?*Node { var i = index; if (i < 1) return self.error_symbol; @@ -1034,11 +1034,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Payload) TokenIndex { + pub fn firstToken(self: *Payload) TokenIndex { return self.lpipe; } - pub fn lastToken(self: &Payload) TokenIndex { + pub fn lastToken(self: *Payload) TokenIndex { return self.rpipe; } }; @@ -1047,10 +1047,10 @@ pub const Node = struct { base: Node, lpipe: TokenIndex, ptr_token: ?TokenIndex, - value_symbol: &Node, + value_symbol: *Node, rpipe: TokenIndex, - pub fn iterate(self: &PointerPayload, index: usize) ?&Node { + pub fn iterate(self: *PointerPayload, index: usize) ?*Node { var i = index; if (i < 1) return self.value_symbol; @@ -1059,11 +1059,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PointerPayload) TokenIndex { + pub fn firstToken(self: *PointerPayload) TokenIndex { return self.lpipe; } - pub fn lastToken(self: &PointerPayload) TokenIndex { + pub fn lastToken(self: *PointerPayload) TokenIndex { return self.rpipe; } }; @@ -1072,11 +1072,11 @@ pub const Node = struct { base: Node, lpipe: TokenIndex, ptr_token: ?TokenIndex, - value_symbol: &Node, - index_symbol: ?&Node, + value_symbol: *Node, + index_symbol: ?*Node, rpipe: TokenIndex, - pub fn iterate(self: &PointerIndexPayload, index: usize) ?&Node { + pub fn iterate(self: *PointerIndexPayload, index: usize) ?*Node { var i = index; if (i < 1) return self.value_symbol; @@ -1090,11 +1090,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PointerIndexPayload) TokenIndex { + pub fn firstToken(self: *PointerIndexPayload) TokenIndex { return self.lpipe; } - pub fn lastToken(self: &PointerIndexPayload) TokenIndex { + pub fn lastToken(self: *PointerIndexPayload) TokenIndex { return self.rpipe; } }; @@ -1102,10 +1102,10 @@ pub const Node = struct { pub const Else = struct { base: Node, else_token: TokenIndex, - payload: ?&Node, - body: &Node, + payload: ?*Node, + body: *Node, - pub fn iterate(self: &Else, index: usize) ?&Node { + pub fn iterate(self: *Else, index: usize) ?*Node { var i = index; if (self.payload) |payload| { @@ -1119,11 +1119,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Else) TokenIndex { + pub fn firstToken(self: *Else) TokenIndex { return self.else_token; } - pub fn lastToken(self: &Else) TokenIndex { + pub fn lastToken(self: *Else) TokenIndex { return self.body.lastToken(); } }; @@ -1131,15 +1131,15 @@ pub const Node = struct { pub const Switch = struct { base: Node, switch_token: TokenIndex, - expr: &Node, + expr: *Node, /// these must be SwitchCase nodes cases: CaseList, rbrace: TokenIndex, - pub const CaseList = SegmentedList(&Node, 2); + pub const CaseList = SegmentedList(*Node, 2); - pub fn iterate(self: &Switch, index: usize) ?&Node { + pub fn iterate(self: *Switch, index: usize) ?*Node { var i = index; if (i < 1) return self.expr; @@ -1151,11 +1151,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Switch) TokenIndex { + pub fn firstToken(self: *Switch) TokenIndex { return self.switch_token; } - pub fn lastToken(self: &Switch) TokenIndex { + pub fn lastToken(self: *Switch) TokenIndex { return self.rbrace; } }; @@ -1164,12 +1164,12 @@ pub const Node = struct { base: Node, items: ItemList, arrow_token: TokenIndex, - payload: ?&Node, - expr: &Node, + payload: ?*Node, + expr: *Node, - pub const ItemList = SegmentedList(&Node, 1); + pub const ItemList = SegmentedList(*Node, 1); - pub fn iterate(self: &SwitchCase, index: usize) ?&Node { + pub fn iterate(self: *SwitchCase, index: usize) ?*Node { var i = index; if (i < self.items.len) return self.items.at(i).*; @@ -1186,11 +1186,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &SwitchCase) TokenIndex { + pub fn firstToken(self: *SwitchCase) TokenIndex { return (self.items.at(0).*).firstToken(); } - pub fn lastToken(self: &SwitchCase) TokenIndex { + pub fn lastToken(self: *SwitchCase) TokenIndex { return self.expr.lastToken(); } }; @@ -1199,15 +1199,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &SwitchElse, index: usize) ?&Node { + pub fn iterate(self: *SwitchElse, index: usize) ?*Node { return null; } - pub fn firstToken(self: &SwitchElse) TokenIndex { + pub fn firstToken(self: *SwitchElse) TokenIndex { return self.token; } - pub fn lastToken(self: &SwitchElse) TokenIndex { + pub fn lastToken(self: *SwitchElse) TokenIndex { return self.token; } }; @@ -1217,13 +1217,13 @@ pub const Node = struct { label: ?TokenIndex, inline_token: ?TokenIndex, while_token: TokenIndex, - condition: &Node, - payload: ?&Node, - continue_expr: ?&Node, - body: &Node, - @"else": ?&Else, + condition: *Node, + payload: ?*Node, + continue_expr: ?*Node, + body: *Node, + @"else": ?*Else, - pub fn iterate(self: &While, index: usize) ?&Node { + pub fn iterate(self: *While, index: usize) ?*Node { var i = index; if (i < 1) return self.condition; @@ -1243,14 +1243,14 @@ pub const Node = struct { i -= 1; if (self.@"else") |@"else"| { - if (i < 1) return &@"else".base; + if (i < 1) return *@"else".base; i -= 1; } return null; } - pub fn firstToken(self: &While) TokenIndex { + pub fn firstToken(self: *While) TokenIndex { if (self.label) |label| { return label; } @@ -1262,7 +1262,7 @@ pub const Node = struct { return self.while_token; } - pub fn lastToken(self: &While) TokenIndex { + pub fn lastToken(self: *While) TokenIndex { if (self.@"else") |@"else"| { return @"else".body.lastToken(); } @@ -1276,12 +1276,12 @@ pub const Node = struct { label: ?TokenIndex, inline_token: ?TokenIndex, for_token: TokenIndex, - array_expr: &Node, - payload: ?&Node, - body: &Node, - @"else": ?&Else, + array_expr: *Node, + payload: ?*Node, + body: *Node, + @"else": ?*Else, - pub fn iterate(self: &For, index: usize) ?&Node { + pub fn iterate(self: *For, index: usize) ?*Node { var i = index; if (i < 1) return self.array_expr; @@ -1296,14 +1296,14 @@ pub const Node = struct { i -= 1; if (self.@"else") |@"else"| { - if (i < 1) return &@"else".base; + if (i < 1) return *@"else".base; i -= 1; } return null; } - pub fn firstToken(self: &For) TokenIndex { + pub fn firstToken(self: *For) TokenIndex { if (self.label) |label| { return label; } @@ -1315,7 +1315,7 @@ pub const Node = struct { return self.for_token; } - pub fn lastToken(self: &For) TokenIndex { + pub fn lastToken(self: *For) TokenIndex { if (self.@"else") |@"else"| { return @"else".body.lastToken(); } @@ -1327,12 +1327,12 @@ pub const Node = struct { pub const If = struct { base: Node, if_token: TokenIndex, - condition: &Node, - payload: ?&Node, - body: &Node, - @"else": ?&Else, + condition: *Node, + payload: ?*Node, + body: *Node, + @"else": ?*Else, - pub fn iterate(self: &If, index: usize) ?&Node { + pub fn iterate(self: *If, index: usize) ?*Node { var i = index; if (i < 1) return self.condition; @@ -1347,18 +1347,18 @@ pub const Node = struct { i -= 1; if (self.@"else") |@"else"| { - if (i < 1) return &@"else".base; + if (i < 1) return *@"else".base; i -= 1; } return null; } - pub fn firstToken(self: &If) TokenIndex { + pub fn firstToken(self: *If) TokenIndex { return self.if_token; } - pub fn lastToken(self: &If) TokenIndex { + pub fn lastToken(self: *If) TokenIndex { if (self.@"else") |@"else"| { return @"else".body.lastToken(); } @@ -1370,9 +1370,9 @@ pub const Node = struct { pub const InfixOp = struct { base: Node, op_token: TokenIndex, - lhs: &Node, + lhs: *Node, op: Op, - rhs: &Node, + rhs: *Node, pub const Op = union(enum) { Add, @@ -1401,7 +1401,7 @@ pub const Node = struct { BitXor, BoolAnd, BoolOr, - Catch: ?&Node, + Catch: ?*Node, Div, EqualEqual, ErrorUnion, @@ -1420,7 +1420,7 @@ pub const Node = struct { UnwrapMaybe, }; - pub fn iterate(self: &InfixOp, index: usize) ?&Node { + pub fn iterate(self: *InfixOp, index: usize) ?*Node { var i = index; if (i < 1) return self.lhs; @@ -1485,11 +1485,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &InfixOp) TokenIndex { + pub fn firstToken(self: *InfixOp) TokenIndex { return self.lhs.firstToken(); } - pub fn lastToken(self: &InfixOp) TokenIndex { + pub fn lastToken(self: *InfixOp) TokenIndex { return self.rhs.lastToken(); } }; @@ -1498,11 +1498,11 @@ pub const Node = struct { base: Node, op_token: TokenIndex, op: Op, - rhs: &Node, + rhs: *Node, pub const Op = union(enum) { AddrOf: AddrOfInfo, - ArrayType: &Node, + ArrayType: *Node, Await, BitNot, BoolNot, @@ -1523,17 +1523,17 @@ pub const Node = struct { volatile_token: ?TokenIndex, pub const Align = struct { - node: &Node, + node: *Node, bit_range: ?BitRange, pub const BitRange = struct { - start: &Node, - end: &Node, + start: *Node, + end: *Node, }; }; }; - pub fn iterate(self: &PrefixOp, index: usize) ?&Node { + pub fn iterate(self: *PrefixOp, index: usize) ?*Node { var i = index; switch (self.op) { @@ -1573,11 +1573,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PrefixOp) TokenIndex { + pub fn firstToken(self: *PrefixOp) TokenIndex { return self.op_token; } - pub fn lastToken(self: &PrefixOp) TokenIndex { + pub fn lastToken(self: *PrefixOp) TokenIndex { return self.rhs.lastToken(); } }; @@ -1586,9 +1586,9 @@ pub const Node = struct { base: Node, period_token: TokenIndex, name_token: TokenIndex, - expr: &Node, + expr: *Node, - pub fn iterate(self: &FieldInitializer, index: usize) ?&Node { + pub fn iterate(self: *FieldInitializer, index: usize) ?*Node { var i = index; if (i < 1) return self.expr; @@ -1597,45 +1597,45 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &FieldInitializer) TokenIndex { + pub fn firstToken(self: *FieldInitializer) TokenIndex { return self.period_token; } - pub fn lastToken(self: &FieldInitializer) TokenIndex { + pub fn lastToken(self: *FieldInitializer) TokenIndex { return self.expr.lastToken(); } }; pub const SuffixOp = struct { base: Node, - lhs: &Node, + lhs: *Node, op: Op, rtoken: TokenIndex, pub const Op = union(enum) { Call: Call, - ArrayAccess: &Node, + ArrayAccess: *Node, Slice: Slice, ArrayInitializer: InitList, StructInitializer: InitList, Deref, - pub const InitList = SegmentedList(&Node, 2); + pub const InitList = SegmentedList(*Node, 2); pub const Call = struct { params: ParamList, - async_attr: ?&AsyncAttribute, + async_attr: ?*AsyncAttribute, - pub const ParamList = SegmentedList(&Node, 2); + pub const ParamList = SegmentedList(*Node, 2); }; pub const Slice = struct { - start: &Node, - end: ?&Node, + start: *Node, + end: ?*Node, }; }; - pub fn iterate(self: &SuffixOp, index: usize) ?&Node { + pub fn iterate(self: *SuffixOp, index: usize) ?*Node { var i = index; if (i < 1) return self.lhs; @@ -1673,7 +1673,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &SuffixOp) TokenIndex { + pub fn firstToken(self: *SuffixOp) TokenIndex { switch (self.op) { @TagType(Op).Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(), else => {}, @@ -1681,7 +1681,7 @@ pub const Node = struct { return self.lhs.firstToken(); } - pub fn lastToken(self: &SuffixOp) TokenIndex { + pub fn lastToken(self: *SuffixOp) TokenIndex { return self.rtoken; } }; @@ -1689,10 +1689,10 @@ pub const Node = struct { pub const GroupedExpression = struct { base: Node, lparen: TokenIndex, - expr: &Node, + expr: *Node, rparen: TokenIndex, - pub fn iterate(self: &GroupedExpression, index: usize) ?&Node { + pub fn iterate(self: *GroupedExpression, index: usize) ?*Node { var i = index; if (i < 1) return self.expr; @@ -1701,11 +1701,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &GroupedExpression) TokenIndex { + pub fn firstToken(self: *GroupedExpression) TokenIndex { return self.lparen; } - pub fn lastToken(self: &GroupedExpression) TokenIndex { + pub fn lastToken(self: *GroupedExpression) TokenIndex { return self.rparen; } }; @@ -1714,15 +1714,15 @@ pub const Node = struct { base: Node, ltoken: TokenIndex, kind: Kind, - rhs: ?&Node, + rhs: ?*Node, const Kind = union(enum) { - Break: ?&Node, - Continue: ?&Node, + Break: ?*Node, + Continue: ?*Node, Return, }; - pub fn iterate(self: &ControlFlowExpression, index: usize) ?&Node { + pub fn iterate(self: *ControlFlowExpression, index: usize) ?*Node { var i = index; switch (self.kind) { @@ -1749,11 +1749,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ControlFlowExpression) TokenIndex { + pub fn firstToken(self: *ControlFlowExpression) TokenIndex { return self.ltoken; } - pub fn lastToken(self: &ControlFlowExpression) TokenIndex { + pub fn lastToken(self: *ControlFlowExpression) TokenIndex { if (self.rhs) |rhs| { return rhs.lastToken(); } @@ -1780,10 +1780,10 @@ pub const Node = struct { base: Node, label: ?TokenIndex, suspend_token: TokenIndex, - payload: ?&Node, - body: ?&Node, + payload: ?*Node, + body: ?*Node, - pub fn iterate(self: &Suspend, index: usize) ?&Node { + pub fn iterate(self: *Suspend, index: usize) ?*Node { var i = index; if (self.payload) |payload| { @@ -1799,12 +1799,12 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Suspend) TokenIndex { + pub fn firstToken(self: *Suspend) TokenIndex { if (self.label) |label| return label; return self.suspend_token; } - pub fn lastToken(self: &Suspend) TokenIndex { + pub fn lastToken(self: *Suspend) TokenIndex { if (self.body) |body| { return body.lastToken(); } @@ -1821,15 +1821,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &IntegerLiteral, index: usize) ?&Node { + pub fn iterate(self: *IntegerLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &IntegerLiteral) TokenIndex { + pub fn firstToken(self: *IntegerLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &IntegerLiteral) TokenIndex { + pub fn lastToken(self: *IntegerLiteral) TokenIndex { return self.token; } }; @@ -1838,15 +1838,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &FloatLiteral, index: usize) ?&Node { + pub fn iterate(self: *FloatLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &FloatLiteral) TokenIndex { + pub fn firstToken(self: *FloatLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &FloatLiteral) TokenIndex { + pub fn lastToken(self: *FloatLiteral) TokenIndex { return self.token; } }; @@ -1857,9 +1857,9 @@ pub const Node = struct { params: ParamList, rparen_token: TokenIndex, - pub const ParamList = SegmentedList(&Node, 2); + pub const ParamList = SegmentedList(*Node, 2); - pub fn iterate(self: &BuiltinCall, index: usize) ?&Node { + pub fn iterate(self: *BuiltinCall, index: usize) ?*Node { var i = index; if (i < self.params.len) return self.params.at(i).*; @@ -1868,11 +1868,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &BuiltinCall) TokenIndex { + pub fn firstToken(self: *BuiltinCall) TokenIndex { return self.builtin_token; } - pub fn lastToken(self: &BuiltinCall) TokenIndex { + pub fn lastToken(self: *BuiltinCall) TokenIndex { return self.rparen_token; } }; @@ -1881,15 +1881,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &StringLiteral, index: usize) ?&Node { + pub fn iterate(self: *StringLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &StringLiteral) TokenIndex { + pub fn firstToken(self: *StringLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &StringLiteral) TokenIndex { + pub fn lastToken(self: *StringLiteral) TokenIndex { return self.token; } }; @@ -1900,15 +1900,15 @@ pub const Node = struct { pub const LineList = SegmentedList(TokenIndex, 4); - pub fn iterate(self: &MultilineStringLiteral, index: usize) ?&Node { + pub fn iterate(self: *MultilineStringLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &MultilineStringLiteral) TokenIndex { + pub fn firstToken(self: *MultilineStringLiteral) TokenIndex { return self.lines.at(0).*; } - pub fn lastToken(self: &MultilineStringLiteral) TokenIndex { + pub fn lastToken(self: *MultilineStringLiteral) TokenIndex { return self.lines.at(self.lines.len - 1).*; } }; @@ -1917,15 +1917,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &CharLiteral, index: usize) ?&Node { + pub fn iterate(self: *CharLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &CharLiteral) TokenIndex { + pub fn firstToken(self: *CharLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &CharLiteral) TokenIndex { + pub fn lastToken(self: *CharLiteral) TokenIndex { return self.token; } }; @@ -1934,15 +1934,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &BoolLiteral, index: usize) ?&Node { + pub fn iterate(self: *BoolLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &BoolLiteral) TokenIndex { + pub fn firstToken(self: *BoolLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &BoolLiteral) TokenIndex { + pub fn lastToken(self: *BoolLiteral) TokenIndex { return self.token; } }; @@ -1951,15 +1951,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &NullLiteral, index: usize) ?&Node { + pub fn iterate(self: *NullLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &NullLiteral) TokenIndex { + pub fn firstToken(self: *NullLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &NullLiteral) TokenIndex { + pub fn lastToken(self: *NullLiteral) TokenIndex { return self.token; } }; @@ -1968,15 +1968,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &UndefinedLiteral, index: usize) ?&Node { + pub fn iterate(self: *UndefinedLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &UndefinedLiteral) TokenIndex { + pub fn firstToken(self: *UndefinedLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &UndefinedLiteral) TokenIndex { + pub fn lastToken(self: *UndefinedLiteral) TokenIndex { return self.token; } }; @@ -1985,15 +1985,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &ThisLiteral, index: usize) ?&Node { + pub fn iterate(self: *ThisLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &ThisLiteral) TokenIndex { + pub fn firstToken(self: *ThisLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &ThisLiteral) TokenIndex { + pub fn lastToken(self: *ThisLiteral) TokenIndex { return self.token; } }; @@ -2001,17 +2001,17 @@ pub const Node = struct { pub const AsmOutput = struct { base: Node, lbracket: TokenIndex, - symbolic_name: &Node, - constraint: &Node, + symbolic_name: *Node, + constraint: *Node, kind: Kind, rparen: TokenIndex, const Kind = union(enum) { - Variable: &Identifier, - Return: &Node, + Variable: *Identifier, + Return: *Node, }; - pub fn iterate(self: &AsmOutput, index: usize) ?&Node { + pub fn iterate(self: *AsmOutput, index: usize) ?*Node { var i = index; if (i < 1) return self.symbolic_name; @@ -2022,7 +2022,7 @@ pub const Node = struct { switch (self.kind) { Kind.Variable => |variable_name| { - if (i < 1) return &variable_name.base; + if (i < 1) return *variable_name.base; i -= 1; }, Kind.Return => |return_type| { @@ -2034,11 +2034,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &AsmOutput) TokenIndex { + pub fn firstToken(self: *AsmOutput) TokenIndex { return self.lbracket; } - pub fn lastToken(self: &AsmOutput) TokenIndex { + pub fn lastToken(self: *AsmOutput) TokenIndex { return self.rparen; } }; @@ -2046,12 +2046,12 @@ pub const Node = struct { pub const AsmInput = struct { base: Node, lbracket: TokenIndex, - symbolic_name: &Node, - constraint: &Node, - expr: &Node, + symbolic_name: *Node, + constraint: *Node, + expr: *Node, rparen: TokenIndex, - pub fn iterate(self: &AsmInput, index: usize) ?&Node { + pub fn iterate(self: *AsmInput, index: usize) ?*Node { var i = index; if (i < 1) return self.symbolic_name; @@ -2066,11 +2066,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &AsmInput) TokenIndex { + pub fn firstToken(self: *AsmInput) TokenIndex { return self.lbracket; } - pub fn lastToken(self: &AsmInput) TokenIndex { + pub fn lastToken(self: *AsmInput) TokenIndex { return self.rparen; } }; @@ -2079,33 +2079,33 @@ pub const Node = struct { base: Node, asm_token: TokenIndex, volatile_token: ?TokenIndex, - template: &Node, + template: *Node, outputs: OutputList, inputs: InputList, clobbers: ClobberList, rparen: TokenIndex, - const OutputList = SegmentedList(&AsmOutput, 2); - const InputList = SegmentedList(&AsmInput, 2); + const OutputList = SegmentedList(*AsmOutput, 2); + const InputList = SegmentedList(*AsmInput, 2); const ClobberList = SegmentedList(TokenIndex, 2); - pub fn iterate(self: &Asm, index: usize) ?&Node { + pub fn iterate(self: *Asm, index: usize) ?*Node { var i = index; - if (i < self.outputs.len) return &(self.outputs.at(index).*).base; + if (i < self.outputs.len) return *(self.outputs.at(index).*).base; i -= self.outputs.len; - if (i < self.inputs.len) return &(self.inputs.at(index).*).base; + if (i < self.inputs.len) return *(self.inputs.at(index).*).base; i -= self.inputs.len; return null; } - pub fn firstToken(self: &Asm) TokenIndex { + pub fn firstToken(self: *Asm) TokenIndex { return self.asm_token; } - pub fn lastToken(self: &Asm) TokenIndex { + pub fn lastToken(self: *Asm) TokenIndex { return self.rparen; } }; @@ -2114,15 +2114,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &Unreachable, index: usize) ?&Node { + pub fn iterate(self: *Unreachable, index: usize) ?*Node { return null; } - pub fn firstToken(self: &Unreachable) TokenIndex { + pub fn firstToken(self: *Unreachable) TokenIndex { return self.token; } - pub fn lastToken(self: &Unreachable) TokenIndex { + pub fn lastToken(self: *Unreachable) TokenIndex { return self.token; } }; @@ -2131,15 +2131,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &ErrorType, index: usize) ?&Node { + pub fn iterate(self: *ErrorType, index: usize) ?*Node { return null; } - pub fn firstToken(self: &ErrorType) TokenIndex { + pub fn firstToken(self: *ErrorType) TokenIndex { return self.token; } - pub fn lastToken(self: &ErrorType) TokenIndex { + pub fn lastToken(self: *ErrorType) TokenIndex { return self.token; } }; @@ -2148,15 +2148,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &VarType, index: usize) ?&Node { + pub fn iterate(self: *VarType, index: usize) ?*Node { return null; } - pub fn firstToken(self: &VarType) TokenIndex { + pub fn firstToken(self: *VarType) TokenIndex { return self.token; } - pub fn lastToken(self: &VarType) TokenIndex { + pub fn lastToken(self: *VarType) TokenIndex { return self.token; } }; @@ -2167,27 +2167,27 @@ pub const Node = struct { pub const LineList = SegmentedList(TokenIndex, 4); - pub fn iterate(self: &DocComment, index: usize) ?&Node { + pub fn iterate(self: *DocComment, index: usize) ?*Node { return null; } - pub fn firstToken(self: &DocComment) TokenIndex { + pub fn firstToken(self: *DocComment) TokenIndex { return self.lines.at(0).*; } - pub fn lastToken(self: &DocComment) TokenIndex { + pub fn lastToken(self: *DocComment) TokenIndex { return self.lines.at(self.lines.len - 1).*; } }; pub const TestDecl = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, test_token: TokenIndex, - name: &Node, - body_node: &Node, + name: *Node, + body_node: *Node, - pub fn iterate(self: &TestDecl, index: usize) ?&Node { + pub fn iterate(self: *TestDecl, index: usize) ?*Node { var i = index; if (i < 1) return self.body_node; @@ -2196,11 +2196,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &TestDecl) TokenIndex { + pub fn firstToken(self: *TestDecl) TokenIndex { return self.test_token; } - pub fn lastToken(self: &TestDecl) TokenIndex { + pub fn lastToken(self: *TestDecl) TokenIndex { return self.body_node.lastToken(); } }; diff --git a/std/zig/bench.zig b/std/zig/bench.zig index c3b6b0d3d3..59392889a6 100644 --- a/std/zig/bench.zig +++ b/std/zig/bench.zig @@ -24,15 +24,15 @@ pub fn main() !void { const mb_per_sec = bytes_per_sec / (1024 * 1024); var stdout_file = try std.io.getStdOut(); - const stdout = &std.io.FileOutStream.init(&stdout_file).stream; + const stdout = *std.io.FileOutStream.init(*stdout_file).stream; try stdout.print("{.3} MB/s, {} KB used \n", mb_per_sec, memory_used / 1024); } fn testOnce() usize { var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - var allocator = &fixed_buf_alloc.allocator; + var allocator = *fixed_buf_alloc.allocator; var tokenizer = Tokenizer.init(source); - var parser = Parser.init(&tokenizer, allocator, "(memory buffer)"); + var parser = Parser.init(*tokenizer, allocator, "(memory buffer)"); _ = parser.parse() catch @panic("parse failure"); return fixed_buf_alloc.end_index; } diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 05554f5d34..6d29300aed 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -9,7 +9,7 @@ const Error = ast.Error; /// Result should be freed with tree.deinit() when there are /// no more references to any of the tokens or nodes. -pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { +pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { var tree_arena = std.heap.ArenaAllocator.init(allocator); errdefer tree_arena.deinit(); @@ -2754,16 +2754,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } const AnnotatedToken = struct { - ptr: &Token, + ptr: *Token, index: TokenIndex, }; const TopLevelDeclCtx = struct { - decls: &ast.Node.Root.DeclList, + decls: *ast.Node.Root.DeclList, visib_token: ?TokenIndex, extern_export_inline_token: ?AnnotatedToken, - lib_name: ?&ast.Node, - comments: ?&ast.Node.DocComment, + lib_name: ?*ast.Node, + comments: ?*ast.Node.DocComment, }; const VarDeclCtx = struct { @@ -2771,21 +2771,21 @@ const VarDeclCtx = struct { visib_token: ?TokenIndex, comptime_token: ?TokenIndex, extern_export_token: ?TokenIndex, - lib_name: ?&ast.Node, - list: &ast.Node.Root.DeclList, - comments: ?&ast.Node.DocComment, + lib_name: ?*ast.Node, + list: *ast.Node.Root.DeclList, + comments: ?*ast.Node.DocComment, }; const TopLevelExternOrFieldCtx = struct { visib_token: TokenIndex, - container_decl: &ast.Node.ContainerDecl, - comments: ?&ast.Node.DocComment, + container_decl: *ast.Node.ContainerDecl, + comments: ?*ast.Node.DocComment, }; const ExternTypeCtx = struct { opt_ctx: OptionalCtx, extern_token: TokenIndex, - comments: ?&ast.Node.DocComment, + comments: ?*ast.Node.DocComment, }; const ContainerKindCtx = struct { @@ -2795,24 +2795,24 @@ const ContainerKindCtx = struct { const ExpectTokenSave = struct { id: @TagType(Token.Id), - ptr: &TokenIndex, + ptr: *TokenIndex, }; const OptionalTokenSave = struct { id: @TagType(Token.Id), - ptr: &?TokenIndex, + ptr: *?TokenIndex, }; const ExprListCtx = struct { - list: &ast.Node.SuffixOp.Op.InitList, + list: *ast.Node.SuffixOp.Op.InitList, end: Token.Id, - ptr: &TokenIndex, + ptr: *TokenIndex, }; fn ListSave(comptime List: type) type { return struct { - list: &List, - ptr: &TokenIndex, + list: *List, + ptr: *TokenIndex, }; } @@ -2841,7 +2841,7 @@ const LoopCtx = struct { const AsyncEndCtx = struct { ctx: OptionalCtx, - attribute: &ast.Node.AsyncAttribute, + attribute: *ast.Node.AsyncAttribute, }; const ErrorTypeOrSetDeclCtx = struct { @@ -2850,21 +2850,21 @@ const ErrorTypeOrSetDeclCtx = struct { }; const ParamDeclEndCtx = struct { - fn_proto: &ast.Node.FnProto, - param_decl: &ast.Node.ParamDecl, + fn_proto: *ast.Node.FnProto, + param_decl: *ast.Node.ParamDecl, }; const ComptimeStatementCtx = struct { comptime_token: TokenIndex, - block: &ast.Node.Block, + block: *ast.Node.Block, }; const OptionalCtx = union(enum) { - Optional: &?&ast.Node, - RequiredNull: &?&ast.Node, - Required: &&ast.Node, + Optional: *?*ast.Node, + RequiredNull: *?*ast.Node, + Required: **ast.Node, - pub fn store(self: &const OptionalCtx, value: &ast.Node) void { + pub fn store(self: *const OptionalCtx, value: *ast.Node) void { switch (self.*) { OptionalCtx.Optional => |ptr| ptr.* = value, OptionalCtx.RequiredNull => |ptr| ptr.* = value, @@ -2872,7 +2872,7 @@ const OptionalCtx = union(enum) { } } - pub fn get(self: &const OptionalCtx) ?&ast.Node { + pub fn get(self: *const OptionalCtx) ?*ast.Node { switch (self.*) { OptionalCtx.Optional => |ptr| return ptr.*, OptionalCtx.RequiredNull => |ptr| return ??ptr.*, @@ -2880,7 +2880,7 @@ const OptionalCtx = union(enum) { } } - pub fn toRequired(self: &const OptionalCtx) OptionalCtx { + pub fn toRequired(self: *const OptionalCtx) OptionalCtx { switch (self.*) { OptionalCtx.Optional => |ptr| { return OptionalCtx{ .RequiredNull = ptr }; @@ -2892,8 +2892,8 @@ const OptionalCtx = union(enum) { }; const AddCommentsCtx = struct { - node_ptr: &&ast.Node, - comments: ?&ast.Node.DocComment, + node_ptr: **ast.Node, + comments: ?*ast.Node.DocComment, }; const State = union(enum) { @@ -2904,67 +2904,67 @@ const State = union(enum) { TopLevelExternOrField: TopLevelExternOrFieldCtx, ContainerKind: ContainerKindCtx, - ContainerInitArgStart: &ast.Node.ContainerDecl, - ContainerInitArg: &ast.Node.ContainerDecl, - ContainerDecl: &ast.Node.ContainerDecl, + ContainerInitArgStart: *ast.Node.ContainerDecl, + ContainerInitArg: *ast.Node.ContainerDecl, + ContainerDecl: *ast.Node.ContainerDecl, VarDecl: VarDeclCtx, - VarDeclAlign: &ast.Node.VarDecl, - VarDeclEq: &ast.Node.VarDecl, - VarDeclSemiColon: &ast.Node.VarDecl, - - FnDef: &ast.Node.FnProto, - FnProto: &ast.Node.FnProto, - FnProtoAlign: &ast.Node.FnProto, - FnProtoReturnType: &ast.Node.FnProto, - - ParamDecl: &ast.Node.FnProto, - ParamDeclAliasOrComptime: &ast.Node.ParamDecl, - ParamDeclName: &ast.Node.ParamDecl, + VarDeclAlign: *ast.Node.VarDecl, + VarDeclEq: *ast.Node.VarDecl, + VarDeclSemiColon: *ast.Node.VarDecl, + + FnDef: *ast.Node.FnProto, + FnProto: *ast.Node.FnProto, + FnProtoAlign: *ast.Node.FnProto, + FnProtoReturnType: *ast.Node.FnProto, + + ParamDecl: *ast.Node.FnProto, + ParamDeclAliasOrComptime: *ast.Node.ParamDecl, + ParamDeclName: *ast.Node.ParamDecl, ParamDeclEnd: ParamDeclEndCtx, - ParamDeclComma: &ast.Node.FnProto, + ParamDeclComma: *ast.Node.FnProto, MaybeLabeledExpression: MaybeLabeledExpressionCtx, LabeledExpression: LabelCtx, Inline: InlineCtx, While: LoopCtx, - WhileContinueExpr: &?&ast.Node, + WhileContinueExpr: *?*ast.Node, For: LoopCtx, - Else: &?&ast.Node.Else, + Else: *?*ast.Node.Else, - Block: &ast.Node.Block, - Statement: &ast.Node.Block, + Block: *ast.Node.Block, + Statement: *ast.Node.Block, ComptimeStatement: ComptimeStatementCtx, - Semicolon: &&ast.Node, + Semicolon: **ast.Node, - AsmOutputItems: &ast.Node.Asm.OutputList, - AsmOutputReturnOrType: &ast.Node.AsmOutput, - AsmInputItems: &ast.Node.Asm.InputList, - AsmClobberItems: &ast.Node.Asm.ClobberList, + AsmOutputItems: *ast.Node.Asm.OutputList, + AsmOutputReturnOrType: *ast.Node.AsmOutput, + AsmInputItems: *ast.Node.Asm.InputList, + AsmClobberItems: *ast.Node.Asm.ClobberList, ExprListItemOrEnd: ExprListCtx, ExprListCommaOrEnd: ExprListCtx, FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), - FieldListCommaOrEnd: &ast.Node.ContainerDecl, + FieldListCommaOrEnd: *ast.Node.ContainerDecl, FieldInitValue: OptionalCtx, ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), SwitchCaseOrEnd: ListSave(ast.Node.Switch.CaseList), SwitchCaseCommaOrEnd: ListSave(ast.Node.Switch.CaseList), - SwitchCaseFirstItem: &ast.Node.SwitchCase, - SwitchCaseItemCommaOrEnd: &ast.Node.SwitchCase, - SwitchCaseItemOrEnd: &ast.Node.SwitchCase, + SwitchCaseFirstItem: *ast.Node.SwitchCase, + SwitchCaseItemCommaOrEnd: *ast.Node.SwitchCase, + SwitchCaseItemOrEnd: *ast.Node.SwitchCase, - SuspendBody: &ast.Node.Suspend, - AsyncAllocator: &ast.Node.AsyncAttribute, + SuspendBody: *ast.Node.Suspend, + AsyncAllocator: *ast.Node.AsyncAttribute, AsyncEnd: AsyncEndCtx, ExternType: ExternTypeCtx, - SliceOrArrayAccess: &ast.Node.SuffixOp, - SliceOrArrayType: &ast.Node.PrefixOp, - AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo, - AlignBitRange: &ast.Node.PrefixOp.AddrOfInfo.Align, + SliceOrArrayAccess: *ast.Node.SuffixOp, + SliceOrArrayType: *ast.Node.PrefixOp, + AddrOfModifiers: *ast.Node.PrefixOp.AddrOfInfo, + AlignBitRange: *ast.Node.PrefixOp.AddrOfInfo.Align, Payload: OptionalCtx, PointerPayload: OptionalCtx, @@ -3007,7 +3007,7 @@ const State = union(enum) { ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx, StringLiteral: OptionalCtx, Identifier: OptionalCtx, - ErrorTag: &&ast.Node, + ErrorTag: **ast.Node, IfToken: @TagType(Token.Id), IfTokenSave: ExpectTokenSave, @@ -3016,7 +3016,7 @@ const State = union(enum) { OptionalTokenSave: OptionalTokenSave, }; -fn pushDocComment(arena: &mem.Allocator, line_comment: TokenIndex, result: &?&ast.Node.DocComment) !void { +fn pushDocComment(arena: *mem.Allocator, line_comment: TokenIndex, result: *?*ast.Node.DocComment) !void { const node = blk: { if (result.*) |comment_node| { break :blk comment_node; @@ -3032,8 +3032,8 @@ fn pushDocComment(arena: &mem.Allocator, line_comment: TokenIndex, result: &?&as try node.lines.push(line_comment); } -fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) !?&ast.Node.DocComment { - var result: ?&ast.Node.DocComment = null; +fn eatDocComments(arena: *mem.Allocator, tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) !?*ast.Node.DocComment { + var result: ?*ast.Node.DocComment = null; while (true) { if (eatToken(tok_it, tree, Token.Id.DocComment)) |line_comment| { try pushDocComment(arena, line_comment, &result); @@ -3044,7 +3044,7 @@ fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, t return result; } -fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, token_ptr: &const Token, token_index: TokenIndex, tree: &ast.Tree) !?&ast.Node { +fn parseStringLiteral(arena: *mem.Allocator, tok_it: *ast.Tree.TokenList.Iterator, token_ptr: *const Token, token_index: TokenIndex, tree: *ast.Tree) !?*ast.Node { switch (token_ptr.id) { Token.Id.StringLiteral => { return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base; @@ -3071,11 +3071,11 @@ fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterato }, // TODO: We shouldn't need a cast, but: // zig: /home/jc/Documents/zig/src/ir.cpp:7962: TypeTableEntry* ir_resolve_peer_types(IrAnalyze*, AstNode*, IrInstruction**, size_t): Assertion `err_set_type != nullptr' failed. - else => return (?&ast.Node)(null), + else => return (?*ast.Node)(null), } } -fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx, token_ptr: &const Token, token_index: TokenIndex) !bool { +fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *const OptionalCtx, token_ptr: *const Token, token_index: TokenIndex) !bool { switch (token_ptr.id) { Token.Id.Keyword_suspend => { const node = try arena.construct(ast.Node.Suspend{ @@ -3189,7 +3189,7 @@ const ExpectCommaOrEndResult = union(enum) { parse_error: Error, }; -fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, end: @TagType(Token.Id)) ExpectCommaOrEndResult { +fn expectCommaOrEnd(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, end: @TagType(Token.Id)) ExpectCommaOrEndResult { const token = nextToken(tok_it, tree); const token_index = token.index; const token_ptr = token.ptr; @@ -3212,7 +3212,7 @@ fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, end: } } -fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op { +fn tokenIdToAssignment(id: *const Token.Id) ?ast.Node.InfixOp.Op { // TODO: We have to cast all cases because of this: // error: expected type '?InfixOp', found '?@TagType(InfixOp)' return switch (id.*) { @@ -3307,21 +3307,21 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { }; } -fn createLiteral(arena: &mem.Allocator, comptime T: type, token_index: TokenIndex) !&T { +fn createLiteral(arena: *mem.Allocator, comptime T: type, token_index: TokenIndex) !*T { return arena.construct(T{ .base = ast.Node{ .id = ast.Node.typeToId(T) }, .token = token_index, }); } -fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token_index: TokenIndex) !&T { +fn createToCtxLiteral(arena: *mem.Allocator, opt_ctx: *const OptionalCtx, comptime T: type, token_index: TokenIndex) !*T { const node = try createLiteral(arena, T, token_index); opt_ctx.store(&node.base); return node; } -fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, id: @TagType(Token.Id)) ?TokenIndex { +fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType(Token.Id)) ?TokenIndex { const token = ??tok_it.peek(); if (token.id == id) { @@ -3331,7 +3331,7 @@ fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, id: @TagType( return null; } -fn nextToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) AnnotatedToken { +fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedToken { const result = AnnotatedToken{ .index = tok_it.index, .ptr = ??tok_it.next(), @@ -3345,7 +3345,7 @@ fn nextToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) AnnotatedTok } } -fn prevToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) void { +fn prevToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) void { while (true) { const prev_tok = tok_it.prev() ?? return; if (prev_tok.id == Token.Id.LineComment) continue; diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 69903bc3fd..8507470bcc 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1803,7 +1803,7 @@ const io = std.io; var fixed_buffer_mem: [100 * 1024]u8 = undefined; -fn testParse(source: []const u8, allocator: &mem.Allocator, anything_changed: &bool) ![]u8 { +fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 { var stderr_file = try io.getStdErr(); var stderr = &io.FileOutStream.init(&stderr_file).stream; diff --git a/std/zig/render.zig b/std/zig/render.zig index ac07917ff1..07e01241b7 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -13,7 +13,7 @@ pub const Error = error{ }; /// Returns whether anything changed -pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf(stream).Child.Error || Error)!bool { +pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@typeOf(stream).Child.Error || Error)!bool { comptime assert(@typeId(@typeOf(stream)) == builtin.TypeId.Pointer); var anything_changed: bool = false; @@ -24,13 +24,13 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf( const StreamError = @typeOf(stream).Child.Error; const Stream = std.io.OutStream(StreamError); - anything_changed_ptr: &bool, + anything_changed_ptr: *bool, child_stream: @typeOf(stream), stream: Stream, source_index: usize, source: []const u8, - fn write(iface_stream: &Stream, bytes: []const u8) StreamError!void { + fn write(iface_stream: *Stream, bytes: []const u8) StreamError!void { const self = @fieldParentPtr(MyStream, "stream", iface_stream); if (!self.anything_changed_ptr.*) { @@ -63,9 +63,9 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf( } fn renderRoot( - allocator: &mem.Allocator, + allocator: *mem.Allocator, stream: var, - tree: &ast.Tree, + tree: *ast.Tree, ) (@typeOf(stream).Child.Error || Error)!void { // render all the line comments at the beginning of the file var tok_it = tree.tokens.iterator(0); @@ -90,7 +90,7 @@ fn renderRoot( } } -fn renderExtraNewline(tree: &ast.Tree, stream: var, start_col: &usize, node: &ast.Node) !void { +fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) !void { const first_token = node.firstToken(); var prev_token = first_token; while (tree.tokens.at(prev_token - 1).id == Token.Id.DocComment) { @@ -104,7 +104,7 @@ fn renderExtraNewline(tree: &ast.Tree, stream: var, start_col: &usize, node: &as } } -fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, start_col: &usize, decl: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { +fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@typeOf(stream).Child.Error || Error)!void { switch (decl.id) { ast.Node.Id.FnProto => { const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); @@ -214,12 +214,12 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i } fn renderExpression( - allocator: &mem.Allocator, + allocator: *mem.Allocator, stream: var, - tree: &ast.Tree, + tree: *ast.Tree, indent: usize, - start_col: &usize, - base: &ast.Node, + start_col: *usize, + base: *ast.Node, space: Space, ) (@typeOf(stream).Child.Error || Error)!void { switch (base.id) { @@ -1640,12 +1640,12 @@ fn renderExpression( } fn renderVarDecl( - allocator: &mem.Allocator, + allocator: *mem.Allocator, stream: var, - tree: &ast.Tree, + tree: *ast.Tree, indent: usize, - start_col: &usize, - var_decl: &ast.Node.VarDecl, + start_col: *usize, + var_decl: *ast.Node.VarDecl, ) (@typeOf(stream).Child.Error || Error)!void { if (var_decl.visib_token) |visib_token| { try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub @@ -1696,12 +1696,12 @@ fn renderVarDecl( } fn renderParamDecl( - allocator: &mem.Allocator, + allocator: *mem.Allocator, stream: var, - tree: &ast.Tree, + tree: *ast.Tree, indent: usize, - start_col: &usize, - base: &ast.Node, + start_col: *usize, + base: *ast.Node, space: Space, ) (@typeOf(stream).Child.Error || Error)!void { const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); @@ -1724,12 +1724,12 @@ fn renderParamDecl( } fn renderStatement( - allocator: &mem.Allocator, + allocator: *mem.Allocator, stream: var, - tree: &ast.Tree, + tree: *ast.Tree, indent: usize, - start_col: &usize, - base: &ast.Node, + start_col: *usize, + base: *ast.Node, ) (@typeOf(stream).Child.Error || Error)!void { switch (base.id) { ast.Node.Id.VarDecl => { @@ -1761,7 +1761,7 @@ const Space = enum { BlockStart, }; -fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, start_col: &usize, space: Space) (@typeOf(stream).Child.Error || Error)!void { +fn renderToken(tree: *ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, start_col: *usize, space: Space) (@typeOf(stream).Child.Error || Error)!void { if (space == Space.BlockStart) { if (start_col.* < indent + indent_delta) return renderToken(tree, stream, token_index, indent, start_col, Space.Space); @@ -1928,11 +1928,11 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent } fn renderDocComments( - tree: &ast.Tree, + tree: *ast.Tree, stream: var, node: var, indent: usize, - start_col: &usize, + start_col: *usize, ) (@typeOf(stream).Child.Error || Error)!void { const comment = node.doc_comments ?? return; var it = comment.lines.iterator(0); @@ -1949,7 +1949,7 @@ fn renderDocComments( } } -fn nodeIsBlock(base: &const ast.Node) bool { +fn nodeIsBlock(base: *const ast.Node) bool { return switch (base.id) { ast.Node.Id.Block, ast.Node.Id.If, @@ -1961,7 +1961,7 @@ fn nodeIsBlock(base: &const ast.Node) bool { }; } -fn nodeCausesSliceOpSpace(base: &ast.Node) bool { +fn nodeCausesSliceOpSpace(base: *ast.Node) bool { const infix_op = base.cast(ast.Node.InfixOp) ?? return false; return switch (infix_op.op) { ast.Node.InfixOp.Op.Period => false, diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 7c3b3210fb..8378a9011d 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -200,7 +200,7 @@ pub const Tokenizer = struct { pending_invalid_token: ?Token, /// For debugging purposes - pub fn dump(self: &Tokenizer, token: &const Token) void { + pub fn dump(self: *Tokenizer, token: *const Token) void { std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]); } @@ -265,7 +265,7 @@ pub const Tokenizer = struct { SawAtSign, }; - pub fn next(self: &Tokenizer) Token { + pub fn next(self: *Tokenizer) Token { if (self.pending_invalid_token) |token| { self.pending_invalid_token = null; return token; @@ -1089,7 +1089,7 @@ pub const Tokenizer = struct { return result; } - fn checkLiteralCharacter(self: &Tokenizer) void { + fn checkLiteralCharacter(self: *Tokenizer) void { if (self.pending_invalid_token != null) return; const invalid_length = self.getInvalidCharacterLength(); if (invalid_length == 0) return; @@ -1100,7 +1100,7 @@ pub const Tokenizer = struct { }; } - fn getInvalidCharacterLength(self: &Tokenizer) u3 { + fn getInvalidCharacterLength(self: *Tokenizer) u3 { const c0 = self.buffer[self.index]; if (c0 < 0x80) { if (c0 < 0x20 or c0 == 0x7f) { diff --git a/test/assemble_and_link.zig b/test/assemble_and_link.zig index 2593f3306a..8c727e87b5 100644 --- a/test/assemble_and_link.zig +++ b/test/assemble_and_link.zig @@ -1,7 +1,7 @@ const builtin = @import("builtin"); const tests = @import("tests.zig"); -pub fn addCases(cases: &tests.CompareOutputContext) void { +pub fn addCases(cases: *tests.CompareOutputContext) void { if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) { cases.addAsm("hello world linux x86_64", \\.text diff --git a/test/build_examples.zig b/test/build_examples.zig index 7a4c0f35d9..1ba0ca46cf 100644 --- a/test/build_examples.zig +++ b/test/build_examples.zig @@ -2,7 +2,7 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); const is_windows = builtin.os == builtin.Os.windows; -pub fn addCases(cases: &tests.BuildExamplesContext) void { +pub fn addCases(cases: *tests.BuildExamplesContext) void { cases.add("example/hello_world/hello.zig"); cases.addC("example/hello_world/hello_libc.zig"); cases.add("example/cat/main.zig"); diff --git a/test/cases/align.zig b/test/cases/align.zig index 582063766f..99bdcdf940 100644 --- a/test/cases/align.zig +++ b/test/cases/align.zig @@ -5,7 +5,7 @@ var foo: u8 align(4) = 100; test "global variable alignment" { assert(@typeOf(&foo).alignment == 4); - assert(@typeOf(&foo) == &align(4) u8); + assert(@typeOf(&foo) == *align(4) u8); const slice = (&foo)[0..1]; assert(@typeOf(slice) == []align(4) u8); } @@ -30,7 +30,7 @@ var baz: packed struct { } = undefined; test "packed struct alignment" { - assert(@typeOf(&baz.b) == &align(1) u32); + assert(@typeOf(&baz.b) == *align(1) u32); } const blah: packed struct { @@ -40,11 +40,11 @@ const blah: packed struct { } = undefined; test "bit field alignment" { - assert(@typeOf(&blah.b) == &align(1:3:6) const u3); + assert(@typeOf(&blah.b) == *align(1:3:6) const u3); } test "default alignment allows unspecified in type syntax" { - assert(&u32 == &align(@alignOf(u32)) u32); + assert(*u32 == *align(@alignOf(u32)) u32); } test "implicitly decreasing pointer alignment" { @@ -53,7 +53,7 @@ test "implicitly decreasing pointer alignment" { assert(addUnaligned(&a, &b) == 7); } -fn addUnaligned(a: &align(1) const u32, b: &align(1) const u32) u32 { +fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { return a.* + b.*; } @@ -76,7 +76,7 @@ fn testBytesAlign(b: u8) void { b, b, }; - const ptr = @ptrCast(&u32, &bytes[0]); + const ptr = @ptrCast(*u32, &bytes[0]); assert(ptr.* == 0x33333333); } @@ -99,10 +99,10 @@ test "@alignCast pointers" { expectsOnly1(&x); assert(x == 2); } -fn expectsOnly1(x: &align(1) u32) void { +fn expectsOnly1(x: *align(1) u32) void { expects4(@alignCast(4, x)); } -fn expects4(x: &align(4) u32) void { +fn expects4(x: *align(4) u32) void { x.* += 1; } @@ -163,8 +163,8 @@ fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { test "@ptrCast preserves alignment of bigger source" { var x: u32 align(16) = 1234; - const ptr = @ptrCast(&u8, &x); - assert(@typeOf(ptr) == &align(16) u8); + const ptr = @ptrCast(*u8, &x); + assert(@typeOf(ptr) == *align(16) u8); } test "compile-time known array index has best alignment possible" { @@ -175,10 +175,10 @@ test "compile-time known array index has best alignment possible" { 3, 4, }; - assert(@typeOf(&array[0]) == &align(4) u8); - assert(@typeOf(&array[1]) == &u8); - assert(@typeOf(&array[2]) == &align(2) u8); - assert(@typeOf(&array[3]) == &u8); + assert(@typeOf(&array[0]) == *align(4) u8); + assert(@typeOf(&array[1]) == *u8); + assert(@typeOf(&array[2]) == *align(2) u8); + assert(@typeOf(&array[3]) == *u8); // because align is too small but we still figure out to use 2 var bigger align(2) = []u64{ @@ -187,10 +187,10 @@ test "compile-time known array index has best alignment possible" { 3, 4, }; - assert(@typeOf(&bigger[0]) == &align(2) u64); - assert(@typeOf(&bigger[1]) == &align(2) u64); - assert(@typeOf(&bigger[2]) == &align(2) u64); - assert(@typeOf(&bigger[3]) == &align(2) u64); + assert(@typeOf(&bigger[0]) == *align(2) u64); + assert(@typeOf(&bigger[1]) == *align(2) u64); + assert(@typeOf(&bigger[2]) == *align(2) u64); + assert(@typeOf(&bigger[3]) == *align(2) u64); // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 var smaller align(2) = []u32{ @@ -199,21 +199,21 @@ test "compile-time known array index has best alignment possible" { 3, 4, }; - testIndex(&smaller[0], 0, &align(2) u32); - testIndex(&smaller[0], 1, &align(2) u32); - testIndex(&smaller[0], 2, &align(2) u32); - testIndex(&smaller[0], 3, &align(2) u32); + testIndex(&smaller[0], 0, *align(2) u32); + testIndex(&smaller[0], 1, *align(2) u32); + testIndex(&smaller[0], 2, *align(2) u32); + testIndex(&smaller[0], 3, *align(2) u32); // has to use ABI alignment because index known at runtime only - testIndex2(&array[0], 0, &u8); - testIndex2(&array[0], 1, &u8); - testIndex2(&array[0], 2, &u8); - testIndex2(&array[0], 3, &u8); + testIndex2(&array[0], 0, *u8); + testIndex2(&array[0], 1, *u8); + testIndex2(&array[0], 2, *u8); + testIndex2(&array[0], 3, *u8); } -fn testIndex(smaller: &align(2) u32, index: usize, comptime T: type) void { +fn testIndex(smaller: *align(2) u32, index: usize, comptime T: type) void { assert(@typeOf(&smaller[index]) == T); } -fn testIndex2(ptr: &align(4) u8, index: usize, comptime T: type) void { +fn testIndex2(ptr: *align(4) u8, index: usize, comptime T: type) void { assert(@typeOf(&ptr[index]) == T); } diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig index d406285d29..67c9ab3dd1 100644 --- a/test/cases/atomics.zig +++ b/test/cases/atomics.zig @@ -34,7 +34,7 @@ test "atomicrmw and atomicload" { testAtomicLoad(&data); } -fn testAtomicRmw(ptr: &u8) void { +fn testAtomicRmw(ptr: *u8) void { const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst); assert(prev_value == 200); comptime { @@ -45,7 +45,7 @@ fn testAtomicRmw(ptr: &u8) void { } } -fn testAtomicLoad(ptr: &u8) void { +fn testAtomicLoad(ptr: *u8) void { const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); assert(x == 42); } @@ -54,18 +54,18 @@ test "cmpxchg with ptr" { var data1: i32 = 1234; var data2: i32 = 5678; var data3: i32 = 9101; - var x: &i32 = &data1; - if (@cmpxchgWeak(&i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + var x: *i32 = &data1; + if (@cmpxchgWeak(*i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { assert(x1 == &data1); } else { @panic("cmpxchg should have failed"); } - while (@cmpxchgWeak(&i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + while (@cmpxchgWeak(*i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { assert(x1 == &data1); } assert(x == &data3); - assert(@cmpxchgStrong(&i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assert(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); assert(x == &data2); } diff --git a/test/cases/bugs/655.zig b/test/cases/bugs/655.zig index 4431767d5c..50374d4e6d 100644 --- a/test/cases/bugs/655.zig +++ b/test/cases/bugs/655.zig @@ -3,10 +3,10 @@ const other_file = @import("655_other_file.zig"); test "function with &const parameter with type dereferenced by namespace" { const x: other_file.Integer = 1234; - comptime std.debug.assert(@typeOf(&x) == &const other_file.Integer); + comptime std.debug.assert(@typeOf(&x) == *const other_file.Integer); foo(x); } -fn foo(x: &const other_file.Integer) void { +fn foo(x: *const other_file.Integer) void { std.debug.assert(x.* == 1234); } diff --git a/test/cases/bugs/828.zig b/test/cases/bugs/828.zig index 10d7370b90..50ae0fd279 100644 --- a/test/cases/bugs/828.zig +++ b/test/cases/bugs/828.zig @@ -3,7 +3,7 @@ const CountBy = struct { const One = CountBy{ .a = 1 }; - pub fn counter(self: &const CountBy) Counter { + pub fn counter(self: *const CountBy) Counter { return Counter{ .i = 0 }; } }; @@ -11,13 +11,13 @@ const CountBy = struct { const Counter = struct { i: usize, - pub fn count(self: &Counter) bool { + pub fn count(self: *Counter) bool { self.i += 1; return self.i <= 10; } }; -fn constCount(comptime cb: &const CountBy, comptime unused: u32) void { +fn constCount(comptime cb: *const CountBy, comptime unused: u32) void { comptime { var cnt = cb.counter(); if (cnt.i != 0) @compileError("Counter instance reused!"); diff --git a/test/cases/bugs/920.zig b/test/cases/bugs/920.zig index c315206072..2903f05a29 100644 --- a/test/cases/bugs/920.zig +++ b/test/cases/bugs/920.zig @@ -9,10 +9,10 @@ const ZigTable = struct { pdf: fn (f64) f64, is_symmetric: bool, - zero_case: fn (&Random, f64) f64, + zero_case: fn (*Random, f64) f64, }; -fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, comptime zero_case: fn (&Random, f64) f64) ZigTable { +fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, comptime zero_case: fn (*Random, f64) f64) ZigTable { var tables: ZigTable = undefined; tables.is_symmetric = is_symmetric; @@ -45,7 +45,7 @@ fn norm_f(x: f64) f64 { fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); } -fn norm_zero_case(random: &Random, u: f64) f64 { +fn norm_zero_case(random: *Random, u: f64) f64 { return 0.0; } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index e37451ea93..7358a4ffd8 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -3,20 +3,20 @@ const mem = @import("std").mem; test "int to ptr cast" { const x = usize(13); - const y = @intToPtr(&u8, x); + const y = @intToPtr(*u8, x); const z = @ptrToInt(y); assert(z == 13); } test "integer literal to pointer cast" { - const vga_mem = @intToPtr(&u16, 0xB8000); + const vga_mem = @intToPtr(*u16, 0xB8000); assert(@ptrToInt(vga_mem) == 0xB8000); } test "pointer reinterpret const float to int" { const float: f64 = 5.99999999999994648725e-01; const float_ptr = &float; - const int_ptr = @ptrCast(&const i32, float_ptr); + const int_ptr = @ptrCast(*const i32, float_ptr); const int_val = int_ptr.*; assert(int_val == 858993411); } @@ -28,7 +28,7 @@ test "implicitly cast a pointer to a const pointer of it" { assert(x == 2); } -fn funcWithConstPtrPtr(x: &const &i32) void { +fn funcWithConstPtrPtr(x: *const *i32) void { x.*.* += 1; } @@ -66,11 +66,11 @@ fn Struct(comptime T: type) type { const Self = this; x: T, - fn pointer(self: &const Self) Self { + fn pointer(self: *const Self) Self { return self.*; } - fn maybePointer(self: ?&const Self) Self { + fn maybePointer(self: ?*const Self) Self { const none = Self{ .x = if (T == void) void{} else 0 }; return (self ?? &none).*; } @@ -80,11 +80,11 @@ fn Struct(comptime T: type) type { const Union = union { x: u8, - fn pointer(self: &const Union) Union { + fn pointer(self: *const Union) Union { return self.*; } - fn maybePointer(self: ?&const Union) Union { + fn maybePointer(self: ?*const Union) Union { const none = Union{ .x = 0 }; return (self ?? &none).*; } @@ -94,11 +94,11 @@ const Enum = enum { None, Some, - fn pointer(self: &const Enum) Enum { + fn pointer(self: *const Enum) Enum { return self.*; } - fn maybePointer(self: ?&const Enum) Enum { + fn maybePointer(self: ?*const Enum) Enum { return (self ?? &Enum.None).*; } }; @@ -107,16 +107,16 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" { const S = struct { const Self = this; x: u8, - fn constConst(p: &const &const Self) u8 { + fn constConst(p: *const *const Self) u8 { return (p.*).x; } - fn maybeConstConst(p: ?&const &const Self) u8 { + fn maybeConstConst(p: ?*const *const Self) u8 { return ((??p).*).x; } - fn constConstConst(p: &const &const &const Self) u8 { + fn constConstConst(p: *const *const *const Self) u8 { return (p.*.*).x; } - fn maybeConstConstConst(p: ?&const &const &const Self) u8 { + fn maybeConstConstConst(p: ?*const *const *const Self) u8 { return ((??p).*.*).x; } }; @@ -166,12 +166,12 @@ fn testPeerResolveArrayConstSlice(b: bool) void { } test "integer literal to &const int" { - const x: &const i32 = 3; + const x: *const i32 = 3; assert(x.* == 3); } test "string literal to &const []const u8" { - const x: &const []const u8 = "hello"; + const x: *const []const u8 = "hello"; assert(mem.eql(u8, x.*, "hello")); } @@ -209,11 +209,11 @@ test "return null from fn() error!?&T" { const b = returnNullLitFromMaybeTypeErrorRef(); assert((try a) == null and (try b) == null); } -fn returnNullFromMaybeTypeErrorRef() error!?&A { - const a: ?&A = null; +fn returnNullFromMaybeTypeErrorRef() error!?*A { + const a: ?*A = null; return a; } -fn returnNullLitFromMaybeTypeErrorRef() error!?&A { +fn returnNullLitFromMaybeTypeErrorRef() error!?*A { return null; } @@ -312,7 +312,7 @@ test "implicit cast from &const [N]T to []const T" { fn testCastConstArrayRefToConstSlice() void { const blah = "aoeu"; const const_array_ref = &blah; - assert(@typeOf(const_array_ref) == &const [4]u8); + assert(@typeOf(const_array_ref) == *const [4]u8); const slice: []const u8 = const_array_ref; assert(mem.eql(u8, slice, "aoeu")); } @@ -322,7 +322,7 @@ test "var args implicitly casts by value arg to const ref" { } fn foo(args: ...) void { - assert(@typeOf(args[0]) == &const [5]u8); + assert(@typeOf(args[0]) == *const [5]u8); } test "peer type resolution: error and [N]T" { diff --git a/test/cases/const_slice_child.zig b/test/cases/const_slice_child.zig index a92c589186..e012c729a0 100644 --- a/test/cases/const_slice_child.zig +++ b/test/cases/const_slice_child.zig @@ -1,10 +1,10 @@ const debug = @import("std").debug; const assert = debug.assert; -var argv: &const &const u8 = undefined; +var argv: *const *const u8 = undefined; test "const slice child" { - const strs = ([]&const u8){ + const strs = ([]*const u8){ c"one", c"two", c"three", @@ -29,7 +29,7 @@ fn bar(argc: usize) void { foo(args); } -fn strlen(ptr: &const u8) usize { +fn strlen(ptr: *const u8) usize { var count: usize = 0; while (ptr[count] != 0) : (count += 1) {} return count; diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 8a071c6aad..4d2aa54a69 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -154,7 +154,7 @@ test "async function with dot syntax" { test "async fn pointer in a struct field" { var data: i32 = 1; const Foo = struct { - bar: async<&std.mem.Allocator> fn (&i32) void, + bar: async<*std.mem.Allocator> fn (*i32) void, }; var foo = Foo{ .bar = simpleAsyncFn2 }; const p = (async foo.bar(&data)) catch unreachable; @@ -162,7 +162,7 @@ test "async fn pointer in a struct field" { cancel p; assert(data == 4); } -async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void { +async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { defer y.* += 2; y.* += 1; suspend; @@ -220,7 +220,7 @@ test "break from suspend" { cancel p; std.debug.assert(my_result == 2); } -async fn testBreakFromSuspend(my_result: &i32) void { +async fn testBreakFromSuspend(my_result: *i32) void { s: suspend |p| { break :s; } diff --git a/test/cases/enum.zig b/test/cases/enum.zig index cbcbc5e306..ae9f04869b 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -56,14 +56,14 @@ test "constant enum with payload" { shouldBeNotEmpty(full); } -fn shouldBeEmpty(x: &const AnEnumWithPayload) void { +fn shouldBeEmpty(x: *const AnEnumWithPayload) void { switch (x.*) { AnEnumWithPayload.Empty => {}, else => unreachable, } } -fn shouldBeNotEmpty(x: &const AnEnumWithPayload) void { +fn shouldBeNotEmpty(x: *const AnEnumWithPayload) void { switch (x.*) { AnEnumWithPayload.Empty => unreachable, else => {}, @@ -750,15 +750,15 @@ test "bit field access with enum fields" { assert(data.b == B.Four3); } -fn getA(data: &const BitFieldOfEnums) A { +fn getA(data: *const BitFieldOfEnums) A { return data.a; } -fn getB(data: &const BitFieldOfEnums) B { +fn getB(data: *const BitFieldOfEnums) B { return data.b; } -fn getC(data: &const BitFieldOfEnums) C { +fn getC(data: *const BitFieldOfEnums) C { return data.c; } diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index 8fafa70b02..18174186a9 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -6,7 +6,7 @@ const ET = union(enum) { SINT: i32, UINT: u32, - pub fn print(a: &const ET, buf: []u8) error!usize { + pub fn print(a: *const ET, buf: []u8) error!usize { return switch (a.*) { ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 8a6dc25bd8..6c8bcfdbab 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -282,7 +282,7 @@ fn fnWithFloatMode() f32 { const SimpleStruct = struct { field: i32, - fn method(self: &const SimpleStruct) i32 { + fn method(self: *const SimpleStruct) i32 { return self.field + 3; } }; @@ -367,7 +367,7 @@ test "const global shares pointer with other same one" { assertEqualPtrs(&hi1[0], &hi2[0]); comptime assert(&hi1[0] == &hi2[0]); } -fn assertEqualPtrs(ptr1: &const u8, ptr2: &const u8) void { +fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) void { assert(ptr1 == ptr2); } @@ -418,9 +418,9 @@ test "string literal used as comptime slice is memoized" { } test "comptime slice of undefined pointer of length 0" { - const slice1 = (&i32)(undefined)[0..0]; + const slice1 = (*i32)(undefined)[0..0]; assert(slice1.len == 0); - const slice2 = (&i32)(undefined)[100..100]; + const slice2 = (*i32)(undefined)[100..100]; assert(slice2.len == 0); } @@ -472,7 +472,7 @@ test "comptime function with mutable pointer is not memoized" { } } -fn increment(value: &i32) void { +fn increment(value: *i32) void { value.* += 1; } @@ -517,7 +517,7 @@ test "comptime slice of pointer preserves comptime var" { const SingleFieldStruct = struct { x: i32, - fn read_x(self: &const SingleFieldStruct) i32 { + fn read_x(self: *const SingleFieldStruct) i32 { return self.x; } }; diff --git a/test/cases/field_parent_ptr.zig b/test/cases/field_parent_ptr.zig index 1a7de9ce35..00d4e0f367 100644 --- a/test/cases/field_parent_ptr.zig +++ b/test/cases/field_parent_ptr.zig @@ -24,7 +24,7 @@ const foo = Foo{ .d = -10, }; -fn testParentFieldPtr(c: &const i32) void { +fn testParentFieldPtr(c: *const i32) void { assert(c == &foo.c); const base = @fieldParentPtr(Foo, "c", c); @@ -32,7 +32,7 @@ fn testParentFieldPtr(c: &const i32) void { assert(&base.c == c); } -fn testParentFieldPtrFirst(a: &const bool) void { +fn testParentFieldPtrFirst(a: *const bool) void { assert(a == &foo.a); const base = @fieldParentPtr(Foo, "a", a); diff --git a/test/cases/fn_in_struct_in_comptime.zig b/test/cases/fn_in_struct_in_comptime.zig index c22da71940..fabb57e9cb 100644 --- a/test/cases/fn_in_struct_in_comptime.zig +++ b/test/cases/fn_in_struct_in_comptime.zig @@ -1,9 +1,9 @@ const assert = @import("std").debug.assert; -fn get_foo() fn (&u8) usize { +fn get_foo() fn (*u8) usize { comptime { return struct { - fn func(ptr: &u8) usize { + fn func(ptr: *u8) usize { var u = @ptrToInt(ptr); return u; } @@ -13,5 +13,5 @@ fn get_foo() fn (&u8) usize { test "define a function in an anonymous struct in comptime" { const foo = get_foo(); - assert(foo(@intToPtr(&u8, 12345)) == 12345); + assert(foo(@intToPtr(*u8, 12345)) == 12345); } diff --git a/test/cases/generics.zig b/test/cases/generics.zig index 37cd1b89e4..a76990e2a1 100644 --- a/test/cases/generics.zig +++ b/test/cases/generics.zig @@ -96,8 +96,8 @@ test "generic struct" { fn GenNode(comptime T: type) type { return struct { value: T, - next: ?&GenNode(T), - fn getVal(n: &const GenNode(T)) T { + next: ?*GenNode(T), + fn getVal(n: *const GenNode(T)) T { return n.value; } }; @@ -126,11 +126,11 @@ test "generic fn with implicit cast" { 13, }) == 0); } -fn getByte(ptr: ?&const u8) u8 { +fn getByte(ptr: ?*const u8) u8 { return (??ptr).*; } fn getFirstByte(comptime T: type, mem: []const T) u8 { - return getByte(@ptrCast(&const u8, &mem[0])); + return getByte(@ptrCast(*const u8, &mem[0])); } const foos = []fn (var) bool{ diff --git a/test/cases/incomplete_struct_param_tld.zig b/test/cases/incomplete_struct_param_tld.zig index a2f57743d0..552d6ef185 100644 --- a/test/cases/incomplete_struct_param_tld.zig +++ b/test/cases/incomplete_struct_param_tld.zig @@ -11,12 +11,12 @@ const B = struct { const C = struct { x: i32, - fn d(c: &const C) i32 { + fn d(c: *const C) i32 { return c.x; } }; -fn foo(a: &const A) i32 { +fn foo(a: *const A) i32 { return a.b.c.d(); } diff --git a/test/cases/math.zig b/test/cases/math.zig index 0b4622702f..5f16e903b2 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -28,13 +28,13 @@ fn testDivision() void { assert(divTrunc(f32, -5.0, 3.0) == -1.0); comptime { - assert(1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600); - assert(@rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600); - assert(1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2); - assert(@divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2); - assert(@divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2); - assert(@divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2); - assert(4126227191251978491697987544882340798050766755606969681711 % 10 == 1); + assert(1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,); + assert(@rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600,); + assert(1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,); + assert(@divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,); + assert(@divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,); + assert(@divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,); + assert(4126227191251978491697987544882340798050766755606969681711 % 10 == 1,); } } fn div(comptime T: type, a: T, b: T) T { @@ -324,8 +324,8 @@ test "big number addition" { test "big number multiplication" { comptime { - assert(45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567); - assert(594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016); + assert(45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567,); + assert(594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016,); } } diff --git a/test/cases/misc.zig b/test/cases/misc.zig index b6b2da8de5..919b978f9f 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -252,20 +252,20 @@ test "multiline C string" { } test "type equality" { - assert(&const u8 != &u8); + assert(*const u8 != *u8); } const global_a: i32 = 1234; -const global_b: &const i32 = &global_a; -const global_c: &const f32 = @ptrCast(&const f32, global_b); +const global_b: *const i32 = &global_a; +const global_c: *const f32 = @ptrCast(*const f32, global_b); test "compile time global reinterpret" { - const d = @ptrCast(&const i32, global_c); + const d = @ptrCast(*const i32, global_c); assert(d.* == 1234); } test "explicit cast maybe pointers" { - const a: ?&i32 = undefined; - const b: ?&f32 = @ptrCast(?&f32, a); + const a: ?*i32 = undefined; + const b: ?*f32 = @ptrCast(?*f32, a); } test "generic malloc free" { @@ -274,7 +274,7 @@ test "generic malloc free" { } var some_mem: [100]u8 = undefined; fn memAlloc(comptime T: type, n: usize) error![]T { - return @ptrCast(&T, &some_mem[0])[0..n]; + return @ptrCast(*T, &some_mem[0])[0..n]; } fn memFree(comptime T: type, memory: []T) void {} @@ -357,7 +357,7 @@ const test3_foo = Test3Foo{ }, }; const test3_bar = Test3Foo{ .Two = 13 }; -fn test3_1(f: &const Test3Foo) void { +fn test3_1(f: *const Test3Foo) void { switch (f.*) { Test3Foo.Three => |pt| { assert(pt.x == 3); @@ -366,7 +366,7 @@ fn test3_1(f: &const Test3Foo) void { else => unreachable, } } -fn test3_2(f: &const Test3Foo) void { +fn test3_2(f: *const Test3Foo) void { switch (f.*) { Test3Foo.Two => |x| { assert(x == 13); @@ -393,7 +393,7 @@ test "pointer comparison" { const b = &a; assert(ptrEql(b, b)); } -fn ptrEql(a: &const []const u8, b: &const []const u8) bool { +fn ptrEql(a: *const []const u8, b: *const []const u8) bool { return a == b; } @@ -446,13 +446,13 @@ fn testPointerToVoidReturnType() error!void { return a.*; } const test_pointer_to_void_return_type_x = void{}; -fn testPointerToVoidReturnType2() &const void { +fn testPointerToVoidReturnType2() *const void { return &test_pointer_to_void_return_type_x; } test "non const ptr to aliased type" { const int = i32; - assert(?&int == ?&i32); + assert(?*int == ?*i32); } test "array 2D const double ptr" { @@ -463,7 +463,7 @@ test "array 2D const double ptr" { testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]); } -fn testArray2DConstDoublePtr(ptr: &const f32) void { +fn testArray2DConstDoublePtr(ptr: *const f32) void { assert(ptr[0] == 1.0); assert(ptr[1] == 2.0); } @@ -497,7 +497,7 @@ test "@typeId" { assert(@typeId(u64) == Tid.Int); assert(@typeId(f32) == Tid.Float); assert(@typeId(f64) == Tid.Float); - assert(@typeId(&f32) == Tid.Pointer); + assert(@typeId(*f32) == Tid.Pointer); assert(@typeId([2]u8) == Tid.Array); assert(@typeId(AStruct) == Tid.Struct); assert(@typeId(@typeOf(1)) == Tid.IntLiteral); @@ -540,7 +540,7 @@ test "@typeName" { }; comptime { assert(mem.eql(u8, @typeName(i64), "i64")); - assert(mem.eql(u8, @typeName(&usize), "&usize")); + assert(mem.eql(u8, @typeName(*usize), "*usize")); // https://github.com/ziglang/zig/issues/675 assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); assert(mem.eql(u8, @typeName(Struct), "Struct")); @@ -555,7 +555,7 @@ fn TypeFromFn(comptime T: type) type { test "volatile load and store" { var number: i32 = 1234; - const ptr = (&volatile i32)(&number); + const ptr = (*volatile i32)(&number); ptr.* += 1; assert(ptr.* == 1235); } @@ -587,28 +587,28 @@ var global_ptr = &gdt[0]; // can't really run this test but we can make sure it has no compile error // and generates code -const vram = @intToPtr(&volatile u8, 0x20000000)[0..0x8000]; +const vram = @intToPtr(*volatile u8, 0x20000000)[0..0x8000]; export fn writeToVRam() void { vram[0] = 'X'; } test "pointer child field" { - assert((&u32).Child == u32); + assert((*u32).Child == u32); } const OpaqueA = @OpaqueType(); const OpaqueB = @OpaqueType(); test "@OpaqueType" { - assert(&OpaqueA != &OpaqueB); + assert(*OpaqueA != *OpaqueB); assert(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); assert(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); } test "variable is allowed to be a pointer to an opaque type" { var x: i32 = 1234; - _ = hereIsAnOpaqueType(@ptrCast(&OpaqueA, &x)); + _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x)); } -fn hereIsAnOpaqueType(ptr: &OpaqueA) &OpaqueA { +fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { var a = ptr; return a; } @@ -692,7 +692,7 @@ test "packed struct, enum, union parameters in extern function" { }, PackedUnion{ .a = 1 }, PackedEnum.A); } -export fn testPackedStuff(a: &const PackedStruct, b: &const PackedUnion, c: PackedEnum) void {} +export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion, c: PackedEnum) void {} test "slicing zero length array" { const s1 = ""[0..]; @@ -703,8 +703,8 @@ test "slicing zero length array" { assert(mem.eql(u32, s2, []u32{})); } -const addr1 = @ptrCast(&const u8, emptyFn); +const addr1 = @ptrCast(*const u8, emptyFn); test "comptime cast fn to ptr" { - const addr2 = @ptrCast(&const u8, emptyFn); + const addr2 = @ptrCast(*const u8, emptyFn); comptime assert(addr1 == addr2); } diff --git a/test/cases/null.zig b/test/cases/null.zig index 936e5fafbd..bd78990ff4 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -65,7 +65,7 @@ test "if var maybe pointer" { .d = 1, }) == 15); } -fn shouldBeAPlus1(p: &const Particle) u64 { +fn shouldBeAPlus1(p: *const Particle) u64 { var maybe_particle: ?Particle = p.*; if (maybe_particle) |*particle| { particle.a += 1; diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index b82ce6340f..48fcc9ef03 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -5,7 +5,7 @@ const reflection = this; test "reflection: array, pointer, nullable, error union type child" { comptime { assert(([10]u8).Child == u8); - assert((&u8).Child == u8); + assert((*u8).Child == u8); assert((error!u8).Payload == u8); assert((?u8).Child == u8); } diff --git a/test/cases/slice.zig b/test/cases/slice.zig index eae6fa895e..24e5239e2d 100644 --- a/test/cases/slice.zig +++ b/test/cases/slice.zig @@ -1,7 +1,7 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; -const x = @intToPtr(&i32, 0x1000)[0..0x500]; +const x = @intToPtr(*i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { assert(@ptrToInt(x.ptr) == 0x1000); diff --git a/test/cases/struct.zig b/test/cases/struct.zig index d4a1c7fbe3..0712e508de 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -43,7 +43,7 @@ const VoidStructFieldsFoo = struct { test "structs" { var foo: StructFoo = undefined; - @memset(@ptrCast(&u8, &foo), 0, @sizeOf(StructFoo)); + @memset(@ptrCast(*u8, &foo), 0, @sizeOf(StructFoo)); foo.a += 1; foo.b = foo.a == 1; testFoo(foo); @@ -55,16 +55,16 @@ const StructFoo = struct { b: bool, c: f32, }; -fn testFoo(foo: &const StructFoo) void { +fn testFoo(foo: *const StructFoo) void { assert(foo.b); } -fn testMutation(foo: &StructFoo) void { +fn testMutation(foo: *StructFoo) void { foo.c = 100; } const Node = struct { val: Val, - next: &Node, + next: *Node, }; const Val = struct { @@ -112,7 +112,7 @@ fn aFunc() i32 { return 13; } -fn callStructField(foo: &const Foo) i32 { +fn callStructField(foo: *const Foo) i32 { return foo.ptr(); } @@ -124,7 +124,7 @@ test "store member function in variable" { } const MemberFnTestFoo = struct { x: i32, - fn member(foo: &const MemberFnTestFoo) i32 { + fn member(foo: *const MemberFnTestFoo) i32 { return foo.x; } }; @@ -141,7 +141,7 @@ test "member functions" { } const MemberFnRand = struct { seed: u32, - pub fn getSeed(r: &const MemberFnRand) u32 { + pub fn getSeed(r: *const MemberFnRand) u32 { return r.seed; } }; @@ -166,7 +166,7 @@ test "empty struct method call" { assert(es.method() == 1234); } const EmptyStruct = struct { - fn method(es: &const EmptyStruct) i32 { + fn method(es: *const EmptyStruct) i32 { return 1234; } }; @@ -228,15 +228,15 @@ test "bit field access" { assert(data.b == 3); } -fn getA(data: &const BitField1) u3 { +fn getA(data: *const BitField1) u3 { return data.a; } -fn getB(data: &const BitField1) u3 { +fn getB(data: *const BitField1) u3 { return data.b; } -fn getC(data: &const BitField1) u2 { +fn getC(data: *const BitField1) u2 { return data.c; } @@ -396,8 +396,8 @@ const Bitfields = packed struct { test "native bit field understands endianness" { var all: u64 = 0x7765443322221111; var bytes: [8]u8 = undefined; - @memcpy(&bytes[0], @ptrCast(&u8, &all), 8); - var bitfields = @ptrCast(&Bitfields, &bytes[0]).*; + @memcpy(&bytes[0], @ptrCast(*u8, &all), 8); + var bitfields = @ptrCast(*Bitfields, &bytes[0]).*; assert(bitfields.f1 == 0x1111); assert(bitfields.f2 == 0x2222); @@ -415,7 +415,7 @@ test "align 1 field before self referential align 8 field as slice return type" const Expr = union(enum) { Literal: u8, - Question: &Expr, + Question: *Expr, }; fn alloc(comptime T: type) []T { diff --git a/test/cases/struct_contains_null_ptr_itself.zig b/test/cases/struct_contains_null_ptr_itself.zig index b6cb1a94cc..21175974b3 100644 --- a/test/cases/struct_contains_null_ptr_itself.zig +++ b/test/cases/struct_contains_null_ptr_itself.zig @@ -2,13 +2,13 @@ const std = @import("std"); const assert = std.debug.assert; test "struct contains null pointer which contains original struct" { - var x: ?&NodeLineComment = null; + var x: ?*NodeLineComment = null; assert(x == null); } pub const Node = struct { id: Id, - comment: ?&NodeLineComment, + comment: ?*NodeLineComment, pub const Id = enum { Root, diff --git a/test/cases/switch.zig b/test/cases/switch.zig index 495fa9f3ed..c6a4b60f09 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -90,7 +90,7 @@ const SwitchProngWithVarEnum = union(enum) { Two: f32, Meh: void, }; -fn switchProngWithVarFn(a: &const SwitchProngWithVarEnum) void { +fn switchProngWithVarFn(a: *const SwitchProngWithVarEnum) void { switch (a.*) { SwitchProngWithVarEnum.One => |x| { assert(x == 13); diff --git a/test/cases/this.zig b/test/cases/this.zig index 5e433b5037..ba51d0ac90 100644 --- a/test/cases/this.zig +++ b/test/cases/this.zig @@ -8,7 +8,7 @@ fn Point(comptime T: type) type { x: T, y: T, - fn addOne(self: &Self) void { + fn addOne(self: *Self) void { self.x += 1; self.y += 1; } diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index 2561d70865..921ff785a7 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -37,7 +37,7 @@ test "type info: pointer type info" { } fn testPointer() void { - const u32_ptr_info = @typeInfo(&u32); + const u32_ptr_info = @typeInfo(*u32); assert(TypeId(u32_ptr_info) == TypeId.Pointer); assert(u32_ptr_info.Pointer.is_const == false); assert(u32_ptr_info.Pointer.is_volatile == false); @@ -169,14 +169,14 @@ fn testUnion() void { assert(notag_union_info.Union.fields[1].field_type == u32); const TestExternUnion = extern union { - foo: &c_void, + foo: *c_void, }; const extern_union_info = @typeInfo(TestExternUnion); assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); assert(extern_union_info.Union.tag_type == @typeOf(undefined)); assert(extern_union_info.Union.fields[0].enum_field == null); - assert(extern_union_info.Union.fields[0].field_type == &c_void); + assert(extern_union_info.Union.fields[0].field_type == *c_void); } test "type info: struct info" { @@ -190,13 +190,13 @@ fn testStruct() void { assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); assert(struct_info.Struct.fields.len == 3); assert(struct_info.Struct.fields[1].offset == null); - assert(struct_info.Struct.fields[2].field_type == &TestStruct); + assert(struct_info.Struct.fields[2].field_type == *TestStruct); assert(struct_info.Struct.defs.len == 2); assert(struct_info.Struct.defs[0].is_pub); assert(!struct_info.Struct.defs[0].data.Fn.is_extern); assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); assert(struct_info.Struct.defs[0].data.Fn.return_type == void); - assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn (&const TestStruct) void); + assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn (*const TestStruct) void); } const TestStruct = packed struct { @@ -204,9 +204,9 @@ const TestStruct = packed struct { fieldA: usize, fieldB: void, - fieldC: &Self, + fieldC: *Self, - pub fn foo(self: &const Self) void {} + pub fn foo(self: *const Self) void {} }; test "type info: function type info" { @@ -227,7 +227,7 @@ fn testFunction() void { const test_instance: TestStruct = undefined; const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); assert(TypeId(bound_fn_info) == TypeId.BoundFn); - assert(bound_fn_info.BoundFn.args[0].arg_type == &const TestStruct); + assert(bound_fn_info.BoundFn.args[0].arg_type == *const TestStruct); } fn foo(comptime a: usize, b: bool, args: ...) usize { diff --git a/test/cases/undefined.zig b/test/cases/undefined.zig index f1af10e532..83c620d211 100644 --- a/test/cases/undefined.zig +++ b/test/cases/undefined.zig @@ -27,12 +27,12 @@ test "init static array to undefined" { const Foo = struct { x: i32, - fn setFooXMethod(foo: &Foo) void { + fn setFooXMethod(foo: *Foo) void { foo.x = 3; } }; -fn setFooX(foo: &Foo) void { +fn setFooX(foo: *Foo) void { foo.x = 2; } diff --git a/test/cases/union.zig b/test/cases/union.zig index 005ad08e6a..bdcbbdb452 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -68,11 +68,11 @@ test "init union with runtime value" { assert(foo.int == 42); } -fn setFloat(foo: &Foo, x: f64) void { +fn setFloat(foo: *Foo, x: f64) void { foo.* = Foo{ .float = x }; } -fn setInt(foo: &Foo, x: i32) void { +fn setInt(foo: *Foo, x: i32) void { foo.* = Foo{ .int = x }; } @@ -108,7 +108,7 @@ fn doTest() void { assert(bar(Payload{ .A = 1234 }) == -10); } -fn bar(value: &const Payload) i32 { +fn bar(value: *const Payload) i32 { assert(Letter(value.*) == Letter.A); return switch (value.*) { Payload.A => |x| return x - 1244, @@ -147,7 +147,7 @@ test "union(enum(u32)) with specified and unspecified tag values" { comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); } -fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) void { +fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: *const MultipleChoice2) void { assert(u32(@TagType(MultipleChoice2)(x.*)) == 60); assert(1123 == switch (x.*) { MultipleChoice2.A => 1, @@ -163,7 +163,7 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) void } const ExternPtrOrInt = extern union { - ptr: &u8, + ptr: *u8, int: u64, }; test "extern union size" { @@ -171,7 +171,7 @@ test "extern union size" { } const PackedPtrOrInt = packed union { - ptr: &u8, + ptr: *u8, int: u64, }; test "extern union size" { @@ -206,7 +206,7 @@ test "cast union to tag type of union" { comptime testCastUnionToTagType(TheUnion{ .B = 1234 }); } -fn testCastUnionToTagType(x: &const TheUnion) void { +fn testCastUnionToTagType(x: *const TheUnion) void { assert(TheTag(x.*) == TheTag.B); } @@ -243,7 +243,7 @@ const TheUnion2 = union(enum) { Item2: i32, }; -fn assertIsTheUnion2Item1(value: &const TheUnion2) void { +fn assertIsTheUnion2Item1(value: *const TheUnion2) void { assert(value.* == TheUnion2.Item1); } diff --git a/test/compare_output.zig b/test/compare_output.zig index 0170477b8b..00ad4a709b 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -3,10 +3,10 @@ const std = @import("std"); const os = std.os; const tests = @import("tests.zig"); -pub fn addCases(cases: &tests.CompareOutputContext) void { +pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addC("hello world with libc", \\const c = @cImport(@cInclude("stdio.h")); - \\export fn main(argc: c_int, argv: &&u8) c_int { + \\export fn main(argc: c_int, argv: **u8) c_int { \\ _ = c.puts(c"Hello, world!"); \\ return 0; \\} @@ -139,7 +139,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @cInclude("stdio.h"); \\}); \\ - \\export fn main(argc: c_int, argv: &&u8) c_int { + \\export fn main(argc: c_int, argv: **u8) c_int { \\ if (is_windows) { \\ // we want actual \n, not \r\n \\ _ = c._setmode(1, c._O_BINARY); @@ -284,9 +284,9 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { cases.addC("expose function pointer to C land", \\const c = @cImport(@cInclude("stdlib.h")); \\ - \\export fn compare_fn(a: ?&const c_void, b: ?&const c_void) c_int { - \\ const a_int = @ptrCast(&align(1) const i32, a ?? unreachable); - \\ const b_int = @ptrCast(&align(1) const i32, b ?? unreachable); + \\export fn compare_fn(a: ?*const c_void, b: ?*const c_void) c_int { + \\ const a_int = @ptrCast(*align(1) const i32, a ?? unreachable); + \\ const b_int = @ptrCast(*align(1) const i32, b ?? unreachable); \\ if (a_int.* < b_int.*) { \\ return -1; \\ } else if (a_int.* > b_int.*) { @@ -299,7 +299,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\export fn main() c_int { \\ var array = []u32 { 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; \\ - \\ c.qsort(@ptrCast(&c_void, &array[0]), c_ulong(array.len), @sizeOf(i32), compare_fn); + \\ c.qsort(@ptrCast(*c_void, &array[0]), c_ulong(array.len), @sizeOf(i32), compare_fn); \\ \\ for (array) |item, i| { \\ if (item != i) { @@ -324,7 +324,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @cInclude("stdio.h"); \\}); \\ - \\export fn main(argc: c_int, argv: &&u8) c_int { + \\export fn main(argc: c_int, argv: **u8) c_int { \\ if (is_windows) { \\ // we want actual \n, not \r\n \\ _ = c._setmode(1, c._O_BINARY); @@ -344,13 +344,13 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\const Foo = struct { \\ field1: Bar, \\ - \\ fn method(a: &const Foo) bool { return true; } + \\ fn method(a: *const Foo) bool { return true; } \\}; \\ \\const Bar = struct { \\ field2: i32, \\ - \\ fn method(b: &const Bar) bool { return true; } + \\ fn method(b: *const Bar) bool { return true; } \\}; \\ \\pub fn main() void { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 5215953d0a..1297ed29ab 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,6 @@ const tests = @import("tests.zig"); -pub fn addCases(cases: &tests.CompileErrorContext) void { +pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( "invalid deref on switch target", \\comptime { @@ -109,7 +109,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { "@ptrCast discards const qualifier", \\export fn entry() void { \\ const x: i32 = 1234; - \\ const y = @ptrCast(&i32, &x); + \\ const y = @ptrCast(*i32, &x); \\} , ".tmp_source.zig:3:15: error: cast discards const qualifier", @@ -118,7 +118,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "comptime slice of undefined pointer non-zero len", \\export fn entry() void { - \\ const slice = (&i32)(undefined)[0..1]; + \\ const slice = (*i32)(undefined)[0..1]; \\} , ".tmp_source.zig:2:36: error: non-zero length slice of undefined pointer", @@ -126,7 +126,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "type checking function pointers", - \\fn a(b: fn (&const u8) void) void { + \\fn a(b: fn (*const u8) void) void { \\ b('a'); \\} \\fn c(d: u8) void { @@ -136,7 +136,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ a(c); \\} , - ".tmp_source.zig:8:7: error: expected type 'fn(&const u8) void', found 'fn(u8) void'", + ".tmp_source.zig:8:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void'", ); cases.add( @@ -594,15 +594,15 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "attempt to use 0 bit type in extern fn", - \\extern fn foo(ptr: extern fn(&void) void) void; + \\extern fn foo(ptr: extern fn(*void) void) void; \\ \\export fn entry() void { \\ foo(bar); \\} \\ - \\extern fn bar(x: &void) void { } + \\extern fn bar(x: *void) void { } , - ".tmp_source.zig:7:18: error: parameter of type '&void' has 0 bits; not allowed in function with calling convention 'ccc'", + ".tmp_source.zig:7:18: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'", ); cases.add( @@ -911,10 +911,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "pointer to noreturn", - \\fn a() &noreturn {} + \\fn a() *noreturn {} \\export fn entry() void { _ = a(); } , - ".tmp_source.zig:1:9: error: pointer to noreturn not allowed", + ".tmp_source.zig:1:8: error: pointer to noreturn not allowed", ); cases.add( @@ -985,7 +985,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ return a; \\} , - ".tmp_source.zig:3:12: error: expected type 'i32', found '&const u8'", + ".tmp_source.zig:3:12: error: expected type 'i32', found '*const u8'", ); cases.add( @@ -1446,7 +1446,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "switch expression - switch on pointer type with no else", - \\fn foo(x: &u8) void { + \\fn foo(x: *u8) void { \\ switch (x) { \\ &y => {}, \\ } @@ -1454,7 +1454,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const y: u8 = 100; \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:2:5: error: else prong required when switching on type '&u8'", + ".tmp_source.zig:2:5: error: else prong required when switching on type '*u8'", ); cases.add( @@ -1501,10 +1501,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { "address of number literal", \\const x = 3; \\const y = &x; - \\fn foo() &const i32 { return y; } + \\fn foo() *const i32 { return y; } \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:3:30: error: expected type '&const i32', found '&const (integer literal)'", + ".tmp_source.zig:3:30: error: expected type '*const i32', found '*const (integer literal)'", ); cases.add( @@ -1529,10 +1529,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ a: i32, \\ b: i32, \\ - \\ fn member_a(foo: &const Foo) i32 { + \\ fn member_a(foo: *const Foo) i32 { \\ return foo.a; \\ } - \\ fn member_b(foo: &const Foo) i32 { + \\ fn member_b(foo: *const Foo) i32 { \\ return foo.b; \\ } \\}; @@ -1543,7 +1543,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ Foo.member_b, \\}; \\ - \\fn f(foo: &const Foo, index: usize) void { + \\fn f(foo: *const Foo, index: usize) void { \\ const result = members[index](); \\} \\ @@ -1692,11 +1692,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "assign null to non-nullable pointer", - \\const a: &u8 = null; + \\const a: *u8 = null; \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } , - ".tmp_source.zig:1:16: error: expected type '&u8', found '(null)'", + ".tmp_source.zig:1:16: error: expected type '*u8', found '(null)'", ); cases.add( @@ -1806,7 +1806,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ One: void, \\ Two: i32, \\}; - \\fn bad_eql_2(a: &const EnumWithData, b: &const EnumWithData) bool { + \\fn bad_eql_2(a: *const EnumWithData, b: *const EnumWithData) bool { \\ return a.* == b.*; \\} \\ @@ -2011,9 +2011,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "wrong number of arguments for method fn call", \\const Foo = struct { - \\ fn method(self: &const Foo, a: i32) void {} + \\ fn method(self: *const Foo, a: i32) void {} \\}; - \\fn f(foo: &const Foo) void { + \\fn f(foo: *const Foo) void { \\ \\ foo.method(1, 2); \\} @@ -2062,7 +2062,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "misspelled type with pointer only reference", \\const JasonHM = u8; - \\const JasonList = &JsonNode; + \\const JasonList = *JsonNode; \\ \\const JsonOA = union(enum) { \\ JSONArray: JsonList, @@ -2113,16 +2113,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ derp.init(); \\} , - ".tmp_source.zig:14:5: error: expected type 'i32', found '&const Foo'", + ".tmp_source.zig:14:5: error: expected type 'i32', found '*const Foo'", ); cases.add( "method call with first arg type wrong container", \\pub const List = struct { \\ len: usize, - \\ allocator: &Allocator, + \\ allocator: *Allocator, \\ - \\ pub fn init(allocator: &Allocator) List { + \\ pub fn init(allocator: *Allocator) List { \\ return List { \\ .len = 0, \\ .allocator = allocator, @@ -2143,7 +2143,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ x.init(); \\} , - ".tmp_source.zig:23:5: error: expected type '&Allocator', found '&List'", + ".tmp_source.zig:23:5: error: expected type '*Allocator', found '*List'", ); cases.add( @@ -2308,17 +2308,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ c: u2, \\}; \\ - \\fn foo(bit_field: &const BitField) u3 { + \\fn foo(bit_field: *const BitField) u3 { \\ return bar(&bit_field.b); \\} \\ - \\fn bar(x: &const u3) u3 { + \\fn bar(x: *const u3) u3 { \\ return x.*; \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:8:26: error: expected type '&const u3', found '&align(1:3:6) const u3'", + ".tmp_source.zig:8:26: error: expected type '*const u3', found '*align(1:3:6) const u3'", ); cases.add( @@ -2441,13 +2441,13 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ const b = &a; \\ return ptrEql(b, b); \\} - \\fn ptrEql(a: &[]const u8, b: &[]const u8) bool { + \\fn ptrEql(a: *[]const u8, b: *[]const u8) bool { \\ return true; \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:4:19: error: expected type '&[]const u8', found '&const []const u8'", + ".tmp_source.zig:4:19: error: expected type '*[]const u8', found '*const []const u8'", ); cases.addCase(x: { @@ -2493,7 +2493,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "ptrcast to non-pointer", - \\export fn entry(a: &i32) usize { + \\export fn entry(a: *i32) usize { \\ return @ptrCast(usize, a); \\} , @@ -2542,16 +2542,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { "int to ptr of 0 bits", \\export fn foo() void { \\ var x: usize = 0x1000; - \\ var y: &void = @intToPtr(&void, x); + \\ var y: *void = @intToPtr(*void, x); \\} , - ".tmp_source.zig:3:31: error: type '&void' has 0 bits and cannot store information", + ".tmp_source.zig:3:30: error: type '*void' has 0 bits and cannot store information", ); cases.add( "@fieldParentPtr - non struct", \\const Foo = i32; - \\export fn foo(a: &i32) &Foo { + \\export fn foo(a: *i32) *Foo { \\ return @fieldParentPtr(Foo, "a", a); \\} , @@ -2563,7 +2563,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const Foo = extern struct { \\ derp: i32, \\}; - \\export fn foo(a: &i32) &Foo { + \\export fn foo(a: *i32) *Foo { \\ return @fieldParentPtr(Foo, "a", a); \\} , @@ -2575,7 +2575,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const Foo = extern struct { \\ a: i32, \\}; - \\export fn foo(a: i32) &Foo { + \\export fn foo(a: i32) *Foo { \\ return @fieldParentPtr(Foo, "a", a); \\} , @@ -2591,7 +2591,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const foo = Foo { .a = 1, .b = 2, }; \\ \\comptime { - \\ const field_ptr = @intToPtr(&i32, 0x1234); + \\ const field_ptr = @intToPtr(*i32, 0x1234); \\ const another_foo_ptr = @fieldParentPtr(Foo, "b", field_ptr); \\} , @@ -2682,7 +2682,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "returning address of local variable - simple", - \\export fn foo() &i32 { + \\export fn foo() *i32 { \\ var a: i32 = undefined; \\ return &a; \\} @@ -2692,7 +2692,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "returning address of local variable - phi", - \\export fn foo(c: bool) &i32 { + \\export fn foo(c: bool) *i32 { \\ var a: i32 = undefined; \\ var b: i32 = undefined; \\ return if (c) &a else &b; @@ -3086,11 +3086,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ bar(&foo.b); \\} \\ - \\fn bar(x: &u32) void { + \\fn bar(x: *u32) void { \\ x.* += 1; \\} , - ".tmp_source.zig:8:13: error: expected type '&u32', found '&align(1) u32'", + ".tmp_source.zig:8:13: error: expected type '*u32', found '*align(1) u32'", ); cases.add( @@ -3117,13 +3117,13 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { "increase pointer alignment in @ptrCast", \\export fn entry() u32 { \\ var bytes: [4]u8 = []u8{0x01, 0x02, 0x03, 0x04}; - \\ const ptr = @ptrCast(&u32, &bytes[0]); + \\ const ptr = @ptrCast(*u32, &bytes[0]); \\ return ptr.*; \\} , ".tmp_source.zig:3:17: error: cast increases pointer alignment", - ".tmp_source.zig:3:38: note: '&u8' has alignment 1", - ".tmp_source.zig:3:27: note: '&u32' has alignment 4", + ".tmp_source.zig:3:38: note: '*u8' has alignment 1", + ".tmp_source.zig:3:26: note: '*u32' has alignment 4", ); cases.add( @@ -3169,7 +3169,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ return x == 5678; \\} , - ".tmp_source.zig:4:32: error: expected type '&i32', found '&align(1) i32'", + ".tmp_source.zig:4:32: error: expected type '*i32', found '*align(1) i32'", ); cases.add( @@ -3198,20 +3198,20 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "wrong pointer implicitly casted to pointer to @OpaqueType()", \\const Derp = @OpaqueType(); - \\extern fn bar(d: &Derp) void; + \\extern fn bar(d: *Derp) void; \\export fn foo() void { \\ var x = u8(1); - \\ bar(@ptrCast(&c_void, &x)); + \\ bar(@ptrCast(*c_void, &x)); \\} , - ".tmp_source.zig:5:9: error: expected type '&Derp', found '&c_void'", + ".tmp_source.zig:5:9: error: expected type '*Derp', found '*c_void'", ); cases.add( "non-const variables of things that require const variables", \\const Opaque = @OpaqueType(); \\ - \\export fn entry(opaque: &Opaque) void { + \\export fn entry(opaque: *Opaque) void { \\ var m2 = &2; \\ const y: u32 = m2.*; \\ @@ -3229,10 +3229,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\const Foo = struct { - \\ fn bar(self: &const Foo) void {} + \\ fn bar(self: *const Foo) void {} \\}; , - ".tmp_source.zig:4:4: error: variable of type '&const (integer literal)' must be const or comptime", + ".tmp_source.zig:4:4: error: variable of type '*const (integer literal)' must be const or comptime", ".tmp_source.zig:7:4: error: variable of type '(undefined)' must be const or comptime", ".tmp_source.zig:8:4: error: variable of type '(integer literal)' must be const or comptime", ".tmp_source.zig:9:4: error: variable of type '(float literal)' must be const or comptime", @@ -3241,7 +3241,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { ".tmp_source.zig:12:4: error: variable of type 'Opaque' must be const or comptime", ".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime", ".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime", - ".tmp_source.zig:15:4: error: variable of type '(bound fn(&const Foo) void)' must be const or comptime", + ".tmp_source.zig:15:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", ".tmp_source.zig:17:4: error: unreachable code", ); @@ -3397,14 +3397,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\export fn entry() bool { \\ var x: i32 = 1; - \\ return bar(@ptrCast(&MyType, &x)); + \\ return bar(@ptrCast(*MyType, &x)); \\} \\ - \\fn bar(x: &MyType) bool { + \\fn bar(x: *MyType) bool { \\ return x.blah; \\} , - ".tmp_source.zig:9:13: error: type '&MyType' does not support field access", + ".tmp_source.zig:9:13: error: type '*MyType' does not support field access", ); cases.add( @@ -3535,9 +3535,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\export fn entry() void { \\ foo("hello",); \\} - \\pub extern fn foo(format: &const u8, ...) void; + \\pub extern fn foo(format: *const u8, ...) void; , - ".tmp_source.zig:2:9: error: expected type '&const u8', found '[5]u8'", + ".tmp_source.zig:2:9: error: expected type '*const u8', found '[5]u8'", ); cases.add( @@ -3902,7 +3902,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ const a = Payload { .A = 1234 }; \\ foo(a); \\} - \\fn foo(a: &const Payload) void { + \\fn foo(a: *const Payload) void { \\ switch (a.*) { \\ Payload.A => {}, \\ else => unreachable, diff --git a/test/gen_h.zig b/test/gen_h.zig index 2def39bed7..9559c3395c 100644 --- a/test/gen_h.zig +++ b/test/gen_h.zig @@ -1,6 +1,6 @@ const tests = @import("tests.zig"); -pub fn addCases(cases: &tests.GenHContext) void { +pub fn addCases(cases: *tests.GenHContext) void { cases.add("declare enum", \\const Foo = extern enum { A, B, C }; \\export fn entry(foo: Foo) void { } diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 1fea6347ab..71d1d68764 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -1,6 +1,6 @@ const tests = @import("tests.zig"); -pub fn addCases(cases: &tests.CompareOutputContext) void { +pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("calling panic", \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); diff --git a/test/standalone/brace_expansion/build.zig b/test/standalone/brace_expansion/build.zig index 7752f599df..64f3c08583 100644 --- a/test/standalone/brace_expansion/build.zig +++ b/test/standalone/brace_expansion/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const main = b.addTest("main.zig"); main.setBuildMode(b.standardReleaseOptions()); diff --git a/test/standalone/brace_expansion/main.zig b/test/standalone/brace_expansion/main.zig index c96cc2cbb9..ccb4f6dd45 100644 --- a/test/standalone/brace_expansion/main.zig +++ b/test/standalone/brace_expansion/main.zig @@ -14,7 +14,7 @@ const Token = union(enum) { Eof, }; -var global_allocator: &mem.Allocator = undefined; +var global_allocator: *mem.Allocator = undefined; fn tokenize(input: []const u8) !ArrayList(Token) { const State = enum { @@ -73,7 +73,7 @@ const ParseError = error{ OutOfMemory, }; -fn parse(tokens: &const ArrayList(Token), token_index: &usize) ParseError!Node { +fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node { const first_token = tokens.items[token_index.*]; token_index.* += 1; @@ -109,7 +109,7 @@ fn parse(tokens: &const ArrayList(Token), token_index: &usize) ParseError!Node { } } -fn expandString(input: []const u8, output: &Buffer) !void { +fn expandString(input: []const u8, output: *Buffer) !void { const tokens = try tokenize(input); if (tokens.len == 1) { return output.resize(0); @@ -139,7 +139,7 @@ fn expandString(input: []const u8, output: &Buffer) !void { const ExpandNodeError = error{OutOfMemory}; -fn expandNode(node: &const Node, output: &ArrayList(Buffer)) ExpandNodeError!void { +fn expandNode(node: *const Node, output: *ArrayList(Buffer)) ExpandNodeError!void { assert(output.len == 0); switch (node.*) { Node.Scalar => |scalar| { diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig index f3ab327006..733b3729c1 100644 --- a/test/standalone/issue_339/build.zig +++ b/test/standalone/issue_339/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const obj = b.addObject("test", "test.zig"); const test_step = b.step("test", "Test the program"); diff --git a/test/standalone/issue_339/test.zig b/test/standalone/issue_339/test.zig index da0747b8e6..f4068dcfac 100644 --- a/test/standalone/issue_339/test.zig +++ b/test/standalone/issue_339/test.zig @@ -1,5 +1,5 @@ const StackTrace = @import("builtin").StackTrace; -pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) noreturn { +pub fn panic(msg: []const u8, stack_trace: ?*StackTrace) noreturn { @breakpoint(); while (true) {} } diff --git a/test/standalone/issue_794/build.zig b/test/standalone/issue_794/build.zig index 4f5dcd7ff4..06c37a83a3 100644 --- a/test/standalone/issue_794/build.zig +++ b/test/standalone/issue_794/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const test_artifact = b.addTest("main.zig"); test_artifact.addIncludeDir("a_directory"); diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig index bb9416d3c4..e0b3885dc3 100644 --- a/test/standalone/pkg_import/build.zig +++ b/test/standalone/pkg_import/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const exe = b.addExecutable("test", "test.zig"); exe.addPackagePath("my_pkg", "pkg.zig"); diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig index ecbba297d8..c700d43db9 100644 --- a/test/standalone/use_alias/build.zig +++ b/test/standalone/use_alias/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { b.addCIncludePath("."); const main = b.addTest("main.zig"); diff --git a/test/tests.zig b/test/tests.zig index b59b954122..cc562331fe 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -47,7 +47,7 @@ const test_targets = []TestTarget{ const max_stdout_size = 1 * 1024 * 1024; // 1 MB -pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(CompareOutputContext) catch unreachable; cases.* = CompareOutputContext{ .b = b, @@ -61,7 +61,7 @@ pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) &build return cases.step; } -pub fn addRuntimeSafetyTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(CompareOutputContext) catch unreachable; cases.* = CompareOutputContext{ .b = b, @@ -75,7 +75,7 @@ pub fn addRuntimeSafetyTests(b: &build.Builder, test_filter: ?[]const u8) &build return cases.step; } -pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(CompileErrorContext) catch unreachable; cases.* = CompileErrorContext{ .b = b, @@ -89,7 +89,7 @@ pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) &build. return cases.step; } -pub fn addBuildExampleTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(BuildExamplesContext) catch unreachable; cases.* = BuildExamplesContext{ .b = b, @@ -103,7 +103,7 @@ pub fn addBuildExampleTests(b: &build.Builder, test_filter: ?[]const u8) &build. return cases.step; } -pub fn addAssembleAndLinkTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(CompareOutputContext) catch unreachable; cases.* = CompareOutputContext{ .b = b, @@ -117,7 +117,7 @@ pub fn addAssembleAndLinkTests(b: &build.Builder, test_filter: ?[]const u8) &bui return cases.step; } -pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(TranslateCContext) catch unreachable; cases.* = TranslateCContext{ .b = b, @@ -131,7 +131,7 @@ pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) &build.St return cases.step; } -pub fn addGenHTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(GenHContext) catch unreachable; cases.* = GenHContext{ .b = b, @@ -145,7 +145,7 @@ pub fn addGenHTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { return cases.step; } -pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []const u8, name: []const u8, desc: []const u8, with_lldb: bool) &build.Step { +pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []const u8, name: []const u8, desc: []const u8, with_lldb: bool) *build.Step { const step = b.step(b.fmt("test-{}", name), desc); for (test_targets) |test_target| { const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch); @@ -193,8 +193,8 @@ pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []cons } pub const CompareOutputContext = struct { - b: &build.Builder, - step: &build.Step, + b: *build.Builder, + step: *build.Step, test_index: usize, test_filter: ?[]const u8, @@ -217,28 +217,28 @@ pub const CompareOutputContext = struct { source: []const u8, }; - pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) void { + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { self.sources.append(SourceFile{ .filename = filename, .source = source, }) catch unreachable; } - pub fn setCommandLineArgs(self: &TestCase, args: []const []const u8) void { + pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void { self.cli_args = args; } }; const RunCompareOutputStep = struct { step: build.Step, - context: &CompareOutputContext, + context: *CompareOutputContext, exe_path: []const u8, name: []const u8, expected_output: []const u8, test_index: usize, cli_args: []const []const u8, - pub fn create(context: &CompareOutputContext, exe_path: []const u8, name: []const u8, expected_output: []const u8, cli_args: []const []const u8) &RunCompareOutputStep { + pub fn create(context: *CompareOutputContext, exe_path: []const u8, name: []const u8, expected_output: []const u8, cli_args: []const []const u8) *RunCompareOutputStep { const allocator = context.b.allocator; const ptr = allocator.create(RunCompareOutputStep) catch unreachable; ptr.* = RunCompareOutputStep{ @@ -254,7 +254,7 @@ pub const CompareOutputContext = struct { return ptr; } - fn make(step: &build.Step) !void { + fn make(step: *build.Step) !void { const self = @fieldParentPtr(RunCompareOutputStep, "step", step); const b = self.context.b; @@ -321,12 +321,12 @@ pub const CompareOutputContext = struct { const RuntimeSafetyRunStep = struct { step: build.Step, - context: &CompareOutputContext, + context: *CompareOutputContext, exe_path: []const u8, name: []const u8, test_index: usize, - pub fn create(context: &CompareOutputContext, exe_path: []const u8, name: []const u8) &RuntimeSafetyRunStep { + pub fn create(context: *CompareOutputContext, exe_path: []const u8, name: []const u8) *RuntimeSafetyRunStep { const allocator = context.b.allocator; const ptr = allocator.create(RuntimeSafetyRunStep) catch unreachable; ptr.* = RuntimeSafetyRunStep{ @@ -340,7 +340,7 @@ pub const CompareOutputContext = struct { return ptr; } - fn make(step: &build.Step) !void { + fn make(step: *build.Step) !void { const self = @fieldParentPtr(RuntimeSafetyRunStep, "step", step); const b = self.context.b; @@ -382,7 +382,7 @@ pub const CompareOutputContext = struct { } }; - pub fn createExtra(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { + pub fn createExtra(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { var tc = TestCase{ .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), @@ -396,32 +396,32 @@ pub const CompareOutputContext = struct { return tc; } - pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { + pub fn create(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { return createExtra(self, name, source, expected_output, Special.None); } - pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { + pub fn addC(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { var tc = self.create(name, source, expected_output); tc.link_libc = true; self.addCase(tc); } - pub fn add(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { + pub fn add(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { const tc = self.create(name, source, expected_output); self.addCase(tc); } - pub fn addAsm(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { + pub fn addAsm(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { const tc = self.createExtra(name, source, expected_output, Special.Asm); self.addCase(tc); } - pub fn addRuntimeSafety(self: &CompareOutputContext, name: []const u8, source: []const u8) void { + pub fn addRuntimeSafety(self: *CompareOutputContext, name: []const u8, source: []const u8) void { const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety); self.addCase(tc); } - pub fn addCase(self: &CompareOutputContext, case: &const TestCase) void { + pub fn addCase(self: *CompareOutputContext, case: *const TestCase) void { const b = self.b; const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable; @@ -504,8 +504,8 @@ pub const CompareOutputContext = struct { }; pub const CompileErrorContext = struct { - b: &build.Builder, - step: &build.Step, + b: *build.Builder, + step: *build.Step, test_index: usize, test_filter: ?[]const u8, @@ -521,27 +521,27 @@ pub const CompileErrorContext = struct { source: []const u8, }; - pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) void { + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { self.sources.append(SourceFile{ .filename = filename, .source = source, }) catch unreachable; } - pub fn addExpectedError(self: &TestCase, text: []const u8) void { + pub fn addExpectedError(self: *TestCase, text: []const u8) void { self.expected_errors.append(text) catch unreachable; } }; const CompileCmpOutputStep = struct { step: build.Step, - context: &CompileErrorContext, + context: *CompileErrorContext, name: []const u8, test_index: usize, - case: &const TestCase, + case: *const TestCase, build_mode: Mode, - pub fn create(context: &CompileErrorContext, name: []const u8, case: &const TestCase, build_mode: Mode) &CompileCmpOutputStep { + pub fn create(context: *CompileErrorContext, name: []const u8, case: *const TestCase, build_mode: Mode) *CompileCmpOutputStep { const allocator = context.b.allocator; const ptr = allocator.create(CompileCmpOutputStep) catch unreachable; ptr.* = CompileCmpOutputStep{ @@ -556,7 +556,7 @@ pub const CompileErrorContext = struct { return ptr; } - fn make(step: &build.Step) !void { + fn make(step: *build.Step) !void { const self = @fieldParentPtr(CompileCmpOutputStep, "step", step); const b = self.context.b; @@ -661,7 +661,7 @@ pub const CompileErrorContext = struct { warn("\n"); } - pub fn create(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) &TestCase { + pub fn create(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { const tc = self.b.allocator.create(TestCase) catch unreachable; tc.* = TestCase{ .name = name, @@ -678,24 +678,24 @@ pub const CompileErrorContext = struct { return tc; } - pub fn addC(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn addC(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { var tc = self.create(name, source, expected_lines); tc.link_libc = true; self.addCase(tc); } - pub fn addExe(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn addExe(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { var tc = self.create(name, source, expected_lines); tc.is_exe = true; self.addCase(tc); } - pub fn add(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn add(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { const tc = self.create(name, source, expected_lines); self.addCase(tc); } - pub fn addCase(self: &CompileErrorContext, case: &const TestCase) void { + pub fn addCase(self: *CompileErrorContext, case: *const TestCase) void { const b = self.b; for ([]Mode{ @@ -720,20 +720,20 @@ pub const CompileErrorContext = struct { }; pub const BuildExamplesContext = struct { - b: &build.Builder, - step: &build.Step, + b: *build.Builder, + step: *build.Step, test_index: usize, test_filter: ?[]const u8, - pub fn addC(self: &BuildExamplesContext, root_src: []const u8) void { + pub fn addC(self: *BuildExamplesContext, root_src: []const u8) void { self.addAllArgs(root_src, true); } - pub fn add(self: &BuildExamplesContext, root_src: []const u8) void { + pub fn add(self: *BuildExamplesContext, root_src: []const u8) void { self.addAllArgs(root_src, false); } - pub fn addBuildFile(self: &BuildExamplesContext, build_file: []const u8) void { + pub fn addBuildFile(self: *BuildExamplesContext, build_file: []const u8) void { const b = self.b; const annotated_case_name = b.fmt("build {} (Debug)", build_file); @@ -763,7 +763,7 @@ pub const BuildExamplesContext = struct { self.step.dependOn(&log_step.step); } - pub fn addAllArgs(self: &BuildExamplesContext, root_src: []const u8, link_libc: bool) void { + pub fn addAllArgs(self: *BuildExamplesContext, root_src: []const u8, link_libc: bool) void { const b = self.b; for ([]Mode{ @@ -792,8 +792,8 @@ pub const BuildExamplesContext = struct { }; pub const TranslateCContext = struct { - b: &build.Builder, - step: &build.Step, + b: *build.Builder, + step: *build.Step, test_index: usize, test_filter: ?[]const u8, @@ -808,26 +808,26 @@ pub const TranslateCContext = struct { source: []const u8, }; - pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) void { + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { self.sources.append(SourceFile{ .filename = filename, .source = source, }) catch unreachable; } - pub fn addExpectedLine(self: &TestCase, text: []const u8) void { + pub fn addExpectedLine(self: *TestCase, text: []const u8) void { self.expected_lines.append(text) catch unreachable; } }; const TranslateCCmpOutputStep = struct { step: build.Step, - context: &TranslateCContext, + context: *TranslateCContext, name: []const u8, test_index: usize, - case: &const TestCase, + case: *const TestCase, - pub fn create(context: &TranslateCContext, name: []const u8, case: &const TestCase) &TranslateCCmpOutputStep { + pub fn create(context: *TranslateCContext, name: []const u8, case: *const TestCase) *TranslateCCmpOutputStep { const allocator = context.b.allocator; const ptr = allocator.create(TranslateCCmpOutputStep) catch unreachable; ptr.* = TranslateCCmpOutputStep{ @@ -841,7 +841,7 @@ pub const TranslateCContext = struct { return ptr; } - fn make(step: &build.Step) !void { + fn make(step: *build.Step) !void { const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step); const b = self.context.b; @@ -935,7 +935,7 @@ pub const TranslateCContext = struct { warn("\n"); } - pub fn create(self: &TranslateCContext, allow_warnings: bool, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) &TestCase { + pub fn create(self: *TranslateCContext, allow_warnings: bool, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { const tc = self.b.allocator.create(TestCase) catch unreachable; tc.* = TestCase{ .name = name, @@ -951,22 +951,22 @@ pub const TranslateCContext = struct { return tc; } - pub fn add(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn add(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { const tc = self.create(false, "source.h", name, source, expected_lines); self.addCase(tc); } - pub fn addC(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn addC(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { const tc = self.create(false, "source.c", name, source, expected_lines); self.addCase(tc); } - pub fn addAllowWarnings(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn addAllowWarnings(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { const tc = self.create(true, "source.h", name, source, expected_lines); self.addCase(tc); } - pub fn addCase(self: &TranslateCContext, case: &const TestCase) void { + pub fn addCase(self: *TranslateCContext, case: *const TestCase) void { const b = self.b; const annotated_case_name = fmt.allocPrint(self.b.allocator, "translate-c {}", case.name) catch unreachable; @@ -986,8 +986,8 @@ pub const TranslateCContext = struct { }; pub const GenHContext = struct { - b: &build.Builder, - step: &build.Step, + b: *build.Builder, + step: *build.Step, test_index: usize, test_filter: ?[]const u8, @@ -1001,27 +1001,27 @@ pub const GenHContext = struct { source: []const u8, }; - pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) void { + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { self.sources.append(SourceFile{ .filename = filename, .source = source, }) catch unreachable; } - pub fn addExpectedLine(self: &TestCase, text: []const u8) void { + pub fn addExpectedLine(self: *TestCase, text: []const u8) void { self.expected_lines.append(text) catch unreachable; } }; const GenHCmpOutputStep = struct { step: build.Step, - context: &GenHContext, + context: *GenHContext, h_path: []const u8, name: []const u8, test_index: usize, - case: &const TestCase, + case: *const TestCase, - pub fn create(context: &GenHContext, h_path: []const u8, name: []const u8, case: &const TestCase) &GenHCmpOutputStep { + pub fn create(context: *GenHContext, h_path: []const u8, name: []const u8, case: *const TestCase) *GenHCmpOutputStep { const allocator = context.b.allocator; const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; ptr.* = GenHCmpOutputStep{ @@ -1036,7 +1036,7 @@ pub const GenHContext = struct { return ptr; } - fn make(step: &build.Step) !void { + fn make(step: *build.Step) !void { const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); const b = self.context.b; @@ -1069,7 +1069,7 @@ pub const GenHContext = struct { warn("\n"); } - pub fn create(self: &GenHContext, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) &TestCase { + pub fn create(self: *GenHContext, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { const tc = self.b.allocator.create(TestCase) catch unreachable; tc.* = TestCase{ .name = name, @@ -1084,12 +1084,12 @@ pub const GenHContext = struct { return tc; } - pub fn add(self: &GenHContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn add(self: *GenHContext, name: []const u8, source: []const u8, expected_lines: ...) void { const tc = self.create("test.zig", name, source, expected_lines); self.addCase(tc); } - pub fn addCase(self: &GenHContext, case: &const TestCase) void { + pub fn addCase(self: *GenHContext, case: *const TestCase) void { const b = self.b; const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable; diff --git a/test/translate_c.zig b/test/translate_c.zig index 4cf1e047fa..9a07bc343d 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,6 @@ const tests = @import("tests.zig"); -pub fn addCases(cases: &tests.TranslateCContext) void { +pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("double define struct", \\typedef struct Bar Bar; \\typedef struct Foo Foo; @@ -14,11 +14,11 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\}; , \\pub const struct_Foo = extern struct { - \\ a: ?&Foo, + \\ a: ?*Foo, \\}; \\pub const Foo = struct_Foo; \\pub const struct_Bar = extern struct { - \\ a: ?&Foo, + \\ a: ?*Foo, \\}; ); @@ -99,7 +99,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { cases.add("restrict -> noalias", \\void foo(void *restrict bar, void *restrict); , - \\pub extern fn foo(noalias bar: ?&c_void, noalias arg1: ?&c_void) void; + \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; ); cases.add("simple struct", @@ -110,7 +110,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { , \\const struct_Foo = extern struct { \\ x: c_int, - \\ y: ?&u8, + \\ y: ?*u8, \\}; , \\pub const Foo = struct_Foo; @@ -141,7 +141,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { , \\pub const BarB = enum_Bar.B; , - \\pub extern fn func(a: ?&struct_Foo, b: ?&(?&enum_Bar)) void; + \\pub extern fn func(a: ?*struct_Foo, b: ?*(?*enum_Bar)) void; , \\pub const Foo = struct_Foo; , @@ -151,7 +151,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { cases.add("constant size array", \\void func(int array[20]); , - \\pub extern fn func(array: ?&c_int) void; + \\pub extern fn func(array: ?*c_int) void; ); cases.add("self referential struct with function pointer", @@ -160,7 +160,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\}; , \\pub const struct_Foo = extern struct { - \\ derp: ?extern fn(?&struct_Foo) void, + \\ derp: ?extern fn(?*struct_Foo) void, \\}; , \\pub const Foo = struct_Foo; @@ -172,7 +172,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { , \\pub const struct_Foo = @OpaqueType(); , - \\pub extern fn some_func(foo: ?&struct_Foo, x: c_int) ?&struct_Foo; + \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo; , \\pub const Foo = struct_Foo; ); @@ -219,11 +219,11 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\}; , \\pub const struct_Bar = extern struct { - \\ next: ?&struct_Foo, + \\ next: ?*struct_Foo, \\}; , \\pub const struct_Foo = extern struct { - \\ next: ?&struct_Bar, + \\ next: ?*struct_Bar, \\}; ); @@ -233,7 +233,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { , \\pub const Foo = c_void; , - \\pub extern fn fun(a: ?&Foo) Foo; + \\pub extern fn fun(a: ?*Foo) Foo; ); cases.add("generate inline func for #define global extern fn", @@ -505,7 +505,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return 6; \\} , - \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?&c_void) c_int { + \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ if ((a != 0) and (b != 0)) return 0; \\ if ((b != 0) and (c != null)) return 1; \\ if ((a != 0) and (c != null)) return 2; @@ -607,7 +607,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\pub const struct_Foo = extern struct { \\ field: c_int, \\}; - \\pub export fn read_field(foo: ?&struct_Foo) c_int { + \\pub export fn read_field(foo: ?*struct_Foo) c_int { \\ return (??foo).field; \\} ); @@ -653,8 +653,8 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return x; \\} , - \\pub export fn foo(x: ?&c_ushort) ?&c_void { - \\ return @ptrCast(?&c_void, x); + \\pub export fn foo(x: ?*c_ushort) ?*c_void { + \\ return @ptrCast(?*c_void, x); \\} ); @@ -674,7 +674,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return 0; \\} , - \\pub export fn foo() ?&c_int { + \\pub export fn foo() ?*c_int { \\ return null; \\} ); @@ -983,7 +983,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ *x = 1; \\} , - \\pub export fn foo(x: ?&c_int) void { + \\pub export fn foo(x: ?*c_int) void { \\ (??x).* = 1; \\} ); @@ -1011,7 +1011,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { , \\pub fn foo() c_int { \\ var x: c_int = 1234; - \\ var ptr: ?&c_int = &x; + \\ var ptr: ?*c_int = &x; \\ return (??ptr).*; \\} ); @@ -1021,7 +1021,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return "bar"; \\} , - \\pub fn foo() ?&const u8 { + \\pub fn foo() ?*const u8 { \\ return c"bar"; \\} ); @@ -1150,8 +1150,8 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return (float *)a; \\} , - \\fn ptrcast(a: ?&c_int) ?&f32 { - \\ return @ptrCast(?&f32, a); + \\fn ptrcast(a: ?*c_int) ?*f32 { + \\ return @ptrCast(?*f32, a); \\} ); @@ -1173,7 +1173,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return !c; \\} , - \\pub fn foo(a: c_int, b: f32, c: ?&c_void) c_int { + \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int { \\ return !(a == 0); \\ return !(a != 0); \\ return !(b != 0); @@ -1194,7 +1194,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { cases.add("const ptr initializer", \\static const char *v0 = "0.0.0"; , - \\pub var v0: ?&const u8 = c"0.0.0"; + \\pub var v0: ?*const u8 = c"0.0.0"; ); cases.add("static incomplete array inside function", @@ -1203,14 +1203,14 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\} , \\pub fn foo() void { - \\ const v2: &const u8 = c"2.2.2"; + \\ const v2: *const u8 = c"2.2.2"; \\} ); cases.add("macro pointer cast", \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) , - \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast(&NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr(&NRF_GPIO_Type, NRF_GPIO_BASE) else (&NRF_GPIO_Type)(NRF_GPIO_BASE); + \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast(*NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr(*NRF_GPIO_Type, NRF_GPIO_BASE) else (*NRF_GPIO_Type)(NRF_GPIO_BASE); ); cases.add("if on none bool", @@ -1231,7 +1231,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ B, \\ C, \\}; - \\pub fn if_none_bool(a: c_int, b: f32, c: ?&c_void, d: enum_SomeEnum) c_int { + \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { \\ if (a != 0) return 0; \\ if (b != 0) return 1; \\ if (c != null) return 2; @@ -1248,7 +1248,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn while_none_bool(a: c_int, b: f32, c: ?&c_void) c_int { + \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; @@ -1264,7 +1264,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn for_none_bool(a: c_int, b: f32, c: ?&c_void) c_int { + \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; -- cgit v1.2.3 From 2f614c42fe4f28e5adda8163bd50d6d3507d6353 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 31 May 2018 18:14:35 -0400 Subject: ir: rip out special logic for using addr-of instruction for types See #588 --- src/all_types.hpp | 2 - src/ir.cpp | 155 ++++++++++++++---------------------------------- std/zig/parser_test.zig | 4 +- 3 files changed, 46 insertions(+), 115 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index b9199c2757..d5906cae95 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2275,8 +2275,6 @@ struct IrInstructionVarPtr { IrInstruction base; VariableTableEntry *var; - bool is_const; - bool is_volatile; }; struct IrInstructionCall { diff --git a/src/ir.cpp b/src/ir.cpp index b1fac9f485..984cfd78d3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -104,8 +104,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg); static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type); -static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, - VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr); +static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, VariableTableEntry *var); static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); @@ -1000,13 +999,9 @@ static IrInstruction *ir_build_bin_op_from(IrBuilder *irb, IrInstruction *old_in return new_instruction; } -static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, - VariableTableEntry *var, bool is_const, bool is_volatile) -{ +static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, VariableTableEntry *var) { IrInstructionVarPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->var = var; - instruction->is_const = is_const; - instruction->is_volatile = is_volatile; ir_ref_var(var); @@ -3515,8 +3510,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name); if (var) { - IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var, - !lval.is_ptr || lval.is_const, lval.is_ptr && lval.is_volatile); + IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var); if (lval.is_ptr) return var_ptr; else @@ -5140,7 +5134,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node); ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); - IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var, false, false); + IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var); AstNode *index_var_source_node; VariableTableEntry *index_var; @@ -5158,7 +5152,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); - IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var, false, false); + IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var); IrBasicBlock *cond_block = ir_create_basic_block(irb, child_scope, "ForCond"); @@ -6387,7 +6381,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast IrInstruction *promise_result_type = ir_build_promise_result_type(irb, parent_scope, node, target_promise_type); ir_build_await_bookkeeping(irb, parent_scope, node, promise_result_type); ir_build_var_decl(irb, parent_scope, node, result_var, promise_result_type, nullptr, undefined_value); - IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var, false, false); + IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var); ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle); IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, @@ -6708,15 +6702,14 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type); // TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa ir_build_var_decl(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef); - coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var, false, false); + coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var); VariableTableEntry *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); - irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, - await_handle_var, false, false); + irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var); u8_ptr_type = ir_build_const_type(irb, coro_scope, node, get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false)); @@ -6856,7 +6849,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); - IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var, true, false); + IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false); size_t arg_count = 2; @@ -8958,35 +8951,15 @@ static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instructio ConstExprValue *pointee, TypeTableEntry *pointee_type, ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) { - // TODO remove this special case for types - if (pointee_type->id == TypeTableEntryIdMetaType) { - TypeTableEntry *type_entry = pointee->data.x_type; - if (type_entry->id == TypeTableEntryIdUnreachable) { - ir_add_error(ira, instruction, buf_sprintf("pointer to noreturn not allowed")); - return ira->codegen->invalid_instruction; - } - - IrInstruction *const_instr = ir_get_const(ira, instruction); - ConstExprValue *const_val = &const_instr->value; - const_val->type = pointee_type; - type_ensure_zero_bits_known(ira->codegen, type_entry); - if (type_is_invalid(type_entry)) { - return ira->codegen->invalid_instruction; - } - const_val->data.x_type = get_pointer_to_type_extra(ira->codegen, type_entry, - ptr_is_const, ptr_is_volatile, get_abi_alignment(ira->codegen, type_entry), 0, 0); - return const_instr; - } else { - TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, - ptr_is_const, ptr_is_volatile, ptr_align, 0, 0); - IrInstruction *const_instr = ir_get_const(ira, instruction); - ConstExprValue *const_val = &const_instr->value; - const_val->type = ptr_type; - const_val->data.x_ptr.special = ConstPtrSpecialRef; - const_val->data.x_ptr.mut = ptr_mut; - const_val->data.x_ptr.data.ref.pointee = pointee; - return const_instr; - } + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, + ptr_is_const, ptr_is_volatile, ptr_align, 0, 0); + IrInstruction *const_instr = ir_get_const(ira, instruction); + ConstExprValue *const_val = &const_instr->value; + const_val->type = ptr_type; + const_val->data.x_ptr.special = ConstPtrSpecialRef; + const_val->data.x_ptr.mut = ptr_mut; + const_val->data.x_ptr.data.ref.pointee = pointee; + return const_instr; } static TypeTableEntry *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instruction, @@ -9314,9 +9287,8 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi ConstExprValue *val = ir_resolve_const(ira, value, UndefOk); if (!val) return ira->codegen->invalid_instruction; - bool final_is_const = (value->value.type->id == TypeTableEntryIdMetaType) ? is_const : true; return ir_get_const_ptr(ira, source_instruction, val, value->value.type, - ConstPtrMutComptimeConst, final_is_const, is_volatile, + ConstPtrMutComptimeConst, is_const, is_volatile, get_abi_alignment(ira->codegen, value->value.type)); } @@ -10245,21 +10217,6 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc source_instruction->source_node, ptr); load_ptr_instruction->value.type = child_type; return load_ptr_instruction; - } else if (type_entry->id == TypeTableEntryIdMetaType) { - ConstExprValue *ptr_val = ir_resolve_const(ira, ptr, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_instruction; - - TypeTableEntry *ptr_type = ptr_val->data.x_type; - if (ptr_type->id == TypeTableEntryIdPointer) { - TypeTableEntry *child_type = ptr_type->data.pointer.child_type; - return ir_create_const_type(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, child_type); - } else { - ir_add_error(ira, source_instruction, - buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr_type->name))); - return ira->codegen->invalid_instruction; - } } else { ir_add_error_node(ira, source_instruction->source_node, buf_sprintf("attempt to dereference non pointer type '%s'", @@ -11966,7 +11923,7 @@ IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_i { VariableTableEntry *coro_allocator_var = ira->old_irb.exec->coro_allocator_var; assert(coro_allocator_var != nullptr); - IrInstruction *var_ptr_inst = ir_get_var_ptr(ira, source_instr, coro_allocator_var, true, false); + IrInstruction *var_ptr_inst = ir_get_var_ptr(ira, source_instr, coro_allocator_var); IrInstruction *result = ir_get_deref(ira, source_instr, var_ptr_inst); assert(result->value.type != nullptr); return result; @@ -12147,7 +12104,7 @@ static VariableTableEntry *get_fn_var_by_index(FnTableEntry *fn_entry, size_t in } static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, - VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr) + VariableTableEntry *var) { if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) { assert(ira->codegen->errors.length != 0); @@ -12173,8 +12130,8 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, } } - bool is_const = (var->value->type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const; - bool is_volatile = (var->value->type->id == TypeTableEntryIdMetaType) ? is_volatile_ptr : false; + bool is_const = var->src_is_const; + bool is_volatile = false; if (mem_slot != nullptr) { switch (mem_slot->special) { case ConstValSpecialRuntime: @@ -12200,7 +12157,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, no_mem_slot: IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, - instruction->scope, instruction->source_node, var, is_const, is_volatile); + instruction->scope, instruction->source_node, var); var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, var->src_is_const, is_volatile, var->align_bytes, 0, 0); type_ensure_zero_bits_known(ira->codegen, var->value->type); @@ -12486,7 +12443,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal buf_sprintf("compiler bug: var args can't handle void. https://github.com/ziglang/zig/issues/557")); return ira->codegen->builtin_types.entry_invalid; } - IrInstruction *arg_var_ptr_inst = ir_get_var_ptr(ira, arg, arg_var, true, false); + IrInstruction *arg_var_ptr_inst = ir_get_var_ptr(ira, arg, arg_var); if (type_is_invalid(arg_var_ptr_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -13120,17 +13077,16 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP } static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruction, - VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr) + VariableTableEntry *var) { - IrInstruction *result = ir_get_var_ptr(ira, instruction, var, is_const_ptr, is_volatile_ptr); + IrInstruction *result = ir_get_var_ptr(ira, instruction, var); ir_link_new_instruction(result, instruction); return result->value.type; } static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) { VariableTableEntry *var = var_ptr_instruction->var; - return ir_analyze_var_ptr(ira, &var_ptr_instruction->base, var, var_ptr_instruction->is_const, - var_ptr_instruction->is_volatile); + return ir_analyze_var_ptr(ira, &var_ptr_instruction->base, var); } static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, uint32_t new_align) { @@ -13152,11 +13108,6 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *ptr_type = array_ptr->value.type; - if (ptr_type->id == TypeTableEntryIdMetaType) { - ir_add_error(ira, &elem_ptr_instruction->base, - buf_sprintf("array access of non-array type '%s'", buf_ptr(&ptr_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *array_type = ptr_type->data.pointer.child_type; @@ -13218,8 +13169,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc bool is_const = true; bool is_volatile = false; if (var) { - return ir_analyze_var_ptr(ira, &elem_ptr_instruction->base, var, - is_const, is_volatile); + return ir_analyze_var_ptr(ira, &elem_ptr_instruction->base, var); } else { return ir_analyze_const_ptr(ira, &elem_ptr_instruction->base, &ira->codegen->const_void_val, ira->codegen->builtin_types.entry_void, ConstPtrMutComptimeConst, is_const, is_volatile); @@ -13603,7 +13553,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, source_instruction->source_node); } - return ir_analyze_var_ptr(ira, source_instruction, var, false, false); + return ir_analyze_var_ptr(ira, source_instruction, var); } case TldIdFn: { @@ -13652,14 +13602,8 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru if (type_is_invalid(container_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; - TypeTableEntry *container_type; - if (container_ptr->value.type->id == TypeTableEntryIdPointer) { - container_type = container_ptr->value.type->data.pointer.child_type; - } else if (container_ptr->value.type->id == TypeTableEntryIdMetaType) { - container_type = container_ptr->value.type; - } else { - zig_unreachable(); - } + TypeTableEntry *container_type = container_ptr->value.type->data.pointer.child_type; + assert(container_ptr->value.type->id == TypeTableEntryIdPointer); Buf *field_name = field_ptr_instruction->field_name_buffer; if (!field_name) { @@ -13732,17 +13676,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru if (!container_ptr_val) return ira->codegen->builtin_types.entry_invalid; - TypeTableEntry *child_type; - if (container_ptr->value.type->id == TypeTableEntryIdMetaType) { - TypeTableEntry *ptr_type = container_ptr_val->data.x_type; - assert(ptr_type->id == TypeTableEntryIdPointer); - child_type = ptr_type->data.pointer.child_type; - } else if (container_ptr->value.type->id == TypeTableEntryIdPointer) { - ConstExprValue *child_val = const_ptr_pointee(ira->codegen, container_ptr_val); - child_type = child_val->data.x_type; - } else { - zig_unreachable(); - } + assert(container_ptr->value.type->id == TypeTableEntryIdPointer); + ConstExprValue *child_val = const_ptr_pointee(ira->codegen, container_ptr_val); + TypeTableEntry *child_type = child_val->data.x_type; if (type_is_invalid(child_type)) { return ira->codegen->builtin_types.entry_invalid; @@ -17337,8 +17273,11 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio if (array_type->data.array.len == 0 && byte_alignment == 0) { byte_alignment = get_abi_alignment(ira->codegen, array_type->data.array.child_type); } + bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic && + ptr_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst; TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.is_const || is_comptime_const, + ptr_type->data.pointer.is_volatile, byte_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == TypeTableEntryIdPointer) { @@ -17525,6 +17464,10 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *container_type = ir_resolve_type(ira, container); + ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; + uint64_t result; if (type_is_invalid(container_type)) { return ira->codegen->builtin_types.entry_invalid; @@ -17912,15 +17855,6 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *ptr_type = value->value.type; - // Because we don't have Pointer Reform yet, we can't have a pointer to a 'type'. - // Therefor, we have to check for type 'type' here, so we can output a correct error - // without asserting the assert below. - if (ptr_type->id == TypeTableEntryIdMetaType) { - ir_add_error(ira, value, - buf_sprintf("expected error union type, found '%s'", buf_ptr(&ptr_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == TypeTableEntryIdPointer); @@ -18697,8 +18631,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, TldVar *tld_var = (TldVar *)tld; VariableTableEntry *var = tld_var->var; - IrInstruction *var_ptr = ir_get_var_ptr(ira, &instruction->base, var, - !lval.is_ptr || lval.is_const, lval.is_ptr && lval.is_volatile); + IrInstruction *var_ptr = ir_get_var_ptr(ira, &instruction->base, var); if (type_is_invalid(var_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 8507470bcc..d90cc9ec22 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1298,8 +1298,8 @@ test "zig fmt: struct declaration" { \\ f1: u8, \\ pub f3: u8, \\ - \\ fn method(self: &Self) Self { - \\ return *self; + \\ fn method(self: *Self) Self { + \\ return self.*; \\ } \\ \\ f2: u8, -- cgit v1.2.3 From 019217d7a23bee69bd5ceb38aeeb5f689d5c2a9c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Jun 2018 00:07:40 -0400 Subject: fix regressions --- src/ir.cpp | 105 ++++++++++++++++++++++++++++++++++++++---------- test/compile_errors.zig | 2 +- test/gen_h.zig | 4 +- test/runtime_safety.zig | 46 ++++++++++----------- 4 files changed, 110 insertions(+), 47 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 984cfd78d3..d996b4a2be 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9433,6 +9433,8 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag); assert(union_field != nullptr); type_ensure_zero_bits_known(ira->codegen, union_field->type_entry); + if (type_is_invalid(union_field->type_entry)) + return ira->codegen->invalid_instruction; if (!union_field->type_entry->zero_bits) { AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at( union_field->enum_field->decl_index); @@ -10015,6 +10017,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (actual_type->id == TypeTableEntryIdNumLitFloat || actual_type->id == TypeTableEntryIdNumLitInt) { + ensure_complete_type(ira->codegen, wanted_type); + if (type_is_invalid(wanted_type)) + return ira->codegen->invalid_instruction; if (wanted_type->id == TypeTableEntryIdEnum) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value); if (type_is_invalid(cast1->value.type)) @@ -12766,6 +12771,10 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op TypeTableEntry *type_entry = ir_resolve_type(ira, value); if (type_is_invalid(type_entry)) return ira->codegen->builtin_types.entry_invalid; + ensure_complete_type(ira->codegen, type_entry); + if (type_is_invalid(type_entry)) + return ira->codegen->builtin_types.entry_invalid; + switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); @@ -13187,6 +13196,9 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc bool safety_check_on = elem_ptr_instruction->safety_check_on; ensure_complete_type(ira->codegen, return_type->data.pointer.child_type); + if (type_is_invalid(return_type->data.pointer.child_type)) + return ira->codegen->builtin_types.entry_invalid; + uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type); uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type); uint64_t ptr_align = return_type->data.pointer.alignment; @@ -13696,7 +13708,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru } if (child_type->id == TypeTableEntryIdEnum) { ensure_complete_type(ira->codegen, child_type); - if (child_type->data.enumeration.is_invalid) + if (type_is_invalid(child_type)) return ira->codegen->builtin_types.entry_invalid; TypeEnumField *field = find_enum_type_field(child_type, field_name); @@ -14569,27 +14581,27 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *ptr_type = value->value.type; - if (ptr_type->id == TypeTableEntryIdMetaType) { + assert(ptr_type->id == TypeTableEntryIdPointer); + + TypeTableEntry *type_entry = ptr_type->data.pointer.child_type; + if (type_is_invalid(type_entry)) { + return ira->codegen->builtin_types.entry_invalid; + } else if (type_entry->id == TypeTableEntryIdMetaType) { // surprise! actually this is just ??T not an unwrap maybe instruction - TypeTableEntry *ptr_type_ptr = ir_resolve_type(ira, value); - assert(ptr_type_ptr->id == TypeTableEntryIdPointer); - TypeTableEntry *child_type = ptr_type_ptr->data.pointer.child_type; + ConstExprValue *ptr_val = const_ptr_pointee(ira->codegen, &value->value); + assert(ptr_val->type->id == TypeTableEntryIdMetaType); + TypeTableEntry *child_type = ptr_val->data.x_type; + type_ensure_zero_bits_known(ira->codegen, child_type); TypeTableEntry *layer1 = get_maybe_type(ira->codegen, child_type); TypeTableEntry *layer2 = get_maybe_type(ira->codegen, layer1); - TypeTableEntry *result_type = get_pointer_to_type(ira->codegen, layer2, true); IrInstruction *const_instr = ir_build_const_type(&ira->new_irb, unwrap_maybe_instruction->base.scope, - unwrap_maybe_instruction->base.source_node, result_type); - ir_link_new_instruction(const_instr, &unwrap_maybe_instruction->base); - return const_instr->value.type; - } - - assert(ptr_type->id == TypeTableEntryIdPointer); - - TypeTableEntry *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { - return ira->codegen->builtin_types.entry_invalid; + unwrap_maybe_instruction->base.source_node, layer2); + IrInstruction *result_instr = ir_get_ref(ira, &unwrap_maybe_instruction->base, const_instr, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile); + ir_link_new_instruction(result_instr, &unwrap_maybe_instruction->base); + return result_instr->value.type; } else if (type_entry->id != TypeTableEntryIdMaybe) { ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node, buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name))); @@ -15115,6 +15127,8 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir assert(container_type->id == TypeTableEntryIdUnion); ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; if (instr_field_count != 1) { ir_add_error(ira, instruction, @@ -15182,6 +15196,8 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru } ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; size_t actual_field_count = container_type->data.structure.src_field_count; @@ -15687,6 +15703,8 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; IrInstruction *field_name_value = instruction->field_name->other; Buf *field_name = ir_resolve_str(ira, field_name_value); @@ -15740,6 +15758,9 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na assert(type_info_var->type->id == TypeTableEntryIdMetaType); ensure_complete_type(ira->codegen, type_info_var->data.x_type); + if (type_is_invalid(type_info_var->data.x_type)) + return ira->codegen->builtin_types.entry_invalid; + type_info_type = type_info_var->data.x_type; assert(type_info_type->id == TypeTableEntryIdUnion); } @@ -15765,26 +15786,37 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na VariableTableEntry *var = tld->var; ensure_complete_type(ira->codegen, var->value->type); + if (type_is_invalid(var->value->type)) + return ira->codegen->builtin_types.entry_invalid; assert(var->value->type->id == TypeTableEntryIdMetaType); return var->value->data.x_type; } -static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) +static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) { TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition"); ensure_complete_type(ira->codegen, type_info_definition_type); + if (type_is_invalid(type_info_definition_type)) + return false; + ensure_field_index(type_info_definition_type, "name", 0); ensure_field_index(type_info_definition_type, "is_pub", 1); ensure_field_index(type_info_definition_type, "data", 2); TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type); ensure_complete_type(ira->codegen, type_info_definition_data_type); + if (type_is_invalid(type_info_definition_data_type)) + return false; TypeTableEntry *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type); ensure_complete_type(ira->codegen, type_info_fn_def_type); + if (type_is_invalid(type_info_fn_def_type)) + return false; TypeTableEntry *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type); ensure_complete_type(ira->codegen, type_info_fn_def_inline_type); + if (type_is_invalid(type_info_fn_def_inline_type)) + return false; // Loop through our definitions once to figure out how many definitions we will generate info for. auto decl_it = decls_scope->decl_table.entry_iterator(); @@ -15799,7 +15831,7 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop resolve_top_level_decl(ira->codegen, curr_entry->value, false, curr_entry->value->source_node); if (curr_entry->value->resolution != TldResolutionOk) { - return; + return false; } } @@ -15864,6 +15896,9 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop { VariableTableEntry *var = ((TldVar *)curr_entry->value)->var; ensure_complete_type(ira->codegen, var->value->type); + if (type_is_invalid(var->value->type)) + return false; + if (var->value->type->id == TypeTableEntryIdMetaType) { // We have a variable of type 'type', so it's actually a type definition. @@ -15991,6 +16026,9 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop { TypeTableEntry *type_entry = ((TldContainer *)curr_entry->value)->type_entry; ensure_complete_type(ira->codegen, type_entry); + if (type_is_invalid(type_entry)) + return false; + // This is a type. bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); @@ -16011,6 +16049,7 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop } assert(definition_index == definition_count); + return true; } static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) @@ -16019,6 +16058,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t assert(!type_is_invalid(type_entry)); ensure_complete_type(ira->codegen, type_entry); + if (type_is_invalid(type_entry)) + return nullptr; const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field, TypeTableEntry *type_info_enum_field_type) { @@ -16246,7 +16287,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); - ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope); + if (!ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope)) + return nullptr; break; } @@ -16401,7 +16443,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); - ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope); + if (!ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope)) + return nullptr; break; } @@ -16412,6 +16455,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t buf_init_from_str(&ptr_field_name, "ptr"); TypeTableEntry *ptr_type = type_entry->data.structure.fields_by_name.get(&ptr_field_name)->type_entry; ensure_complete_type(ira->codegen, ptr_type); + if (type_is_invalid(ptr_type)) + return nullptr; buf_deinit(&ptr_field_name); result = create_ptr_like_type_info("Slice", ptr_type); @@ -16482,7 +16527,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 2); - ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope); + if (!ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope)) + return nullptr; break; } @@ -17502,6 +17548,11 @@ static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInst if (type_is_invalid(container_type)) return ira->codegen->builtin_types.entry_invalid; + ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; + + uint64_t member_index; IrInstruction *index_value = instruction->member_index->other; if (!ir_resolve_usize(ira, index_value, &member_index)) @@ -17544,6 +17595,10 @@ static TypeTableEntry *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInst if (type_is_invalid(container_type)) return ira->codegen->builtin_types.entry_invalid; + ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; + uint64_t member_index; IrInstruction *index_value = instruction->member_index->other; if (!ir_resolve_usize(ira, index_value, &member_index)) @@ -18485,7 +18540,12 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_invalid; ensure_complete_type(ira->codegen, dest_type); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + ensure_complete_type(ira->codegen, src_type); + if (type_is_invalid(src_type)) + return ira->codegen->builtin_types.entry_invalid; if (get_codegen_ptr_type(src_type) != nullptr) { ir_add_error(ira, value, @@ -18724,6 +18784,9 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruc if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes)) return ira->codegen->builtin_types.entry_invalid; } else { + type_ensure_zero_bits_known(ira->codegen, child_type); + if (type_is_invalid(child_type)) + return ira->codegen->builtin_types.entry_invalid; align_bytes = get_abi_alignment(ira->codegen, child_type); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 1297ed29ab..132dc8cd80 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3232,7 +3232,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ fn bar(self: *const Foo) void {} \\}; , - ".tmp_source.zig:4:4: error: variable of type '*const (integer literal)' must be const or comptime", + ".tmp_source.zig:4:4: error: variable of type '*(integer literal)' must be const or comptime", ".tmp_source.zig:7:4: error: variable of type '(undefined)' must be const or comptime", ".tmp_source.zig:8:4: error: variable of type '(integer literal)' must be const or comptime", ".tmp_source.zig:9:4: error: variable of type '(float literal)' must be const or comptime", diff --git a/test/gen_h.zig b/test/gen_h.zig index 9559c3395c..e6a757ea6d 100644 --- a/test/gen_h.zig +++ b/test/gen_h.zig @@ -54,7 +54,7 @@ pub fn addCases(cases: *tests.GenHContext) void { cases.add("declare opaque type", \\export const Foo = @OpaqueType(); \\ - \\export fn entry(foo: ?&Foo) void { } + \\export fn entry(foo: ?*Foo) void { } , \\struct Foo; \\ @@ -64,7 +64,7 @@ pub fn addCases(cases: *tests.GenHContext) void { cases.add("array field-type", \\const Foo = extern struct { \\ A: [2]i32, - \\ B: [4]&u32, + \\ B: [4]*u32, \\}; \\export fn entry(foo: Foo, bar: [3]u8) void { } , diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 71d1d68764..61eba9458e 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -2,7 +2,7 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("calling panic", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() void { @@ -11,7 +11,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("out of bounds slice access", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() void { @@ -25,7 +25,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("integer addition overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -38,7 +38,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("integer subtraction overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -51,7 +51,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("integer multiplication overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -64,7 +64,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("integer negation overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -77,7 +77,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("signed integer division overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -90,7 +90,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("signed shift left overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -103,7 +103,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("unsigned shift left overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -116,7 +116,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("signed shift right overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -129,7 +129,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("unsigned shift right overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -142,7 +142,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("integer division by zero", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() void { @@ -154,7 +154,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("exact division failure", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -167,7 +167,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("cast []u8 to bigger slice of wrong size", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -180,7 +180,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("value does not fit in shortening cast", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -193,7 +193,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("signed integer not fitting in cast to unsigned integer", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -206,7 +206,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("unwrap error", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) { \\ @import("std").os.exit(126); // good \\ } @@ -221,7 +221,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("cast integer to global error and no code matches", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() void { @@ -233,7 +233,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("cast integer to non-global error set and no match", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\const Set1 = error{A, B}; @@ -247,7 +247,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("@alignCast misaligned", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -263,7 +263,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("bad union field access", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\ @@ -277,7 +277,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ bar(&f); \\} \\ - \\fn bar(f: &Foo) void { + \\fn bar(f: *Foo) void { \\ f.float = 12.34; \\} ); @@ -287,7 +287,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("error return trace across suspend points", \\const std = @import("std"); \\ - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ std.os.exit(126); \\} \\ -- cgit v1.2.3 From 2a7c8c5b1076667f5b50748c8153fe64ec5b9f13 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Jun 2018 00:18:10 -0400 Subject: add test case for pointer to type and slice of type closes #588 --- test/cases/eval.zig | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 6c8bcfdbab..b6d6a4f37b 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -576,3 +576,37 @@ test "comptime modification of const struct field" { assert(res.version == 1); } } + +test "pointer to type" { + comptime { + var T: type = i32; + assert(T == i32); + var ptr = &T; + assert(@typeOf(ptr) == *type); + ptr.* = f32; + assert(T == f32); + assert(*T == *f32); + } +} + +test "slice of type" { + comptime { + var types_array = []type{ i32, f64, type }; + for (types_array) |T, i| { + switch (i) { + 0 => assert(T == i32), + 1 => assert(T == f64), + 2 => assert(T == type), + else => unreachable, + } + } + for (types_array[0..]) |T, i| { + switch (i) { + 0 => assert(T == i32), + 1 => assert(T == f64), + 2 => assert(T == type), + else => unreachable, + } + } + } +} -- cgit v1.2.3 From 4d13ab07de85e8dd357f7d894f5b2d09e6305115 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Jun 2018 01:19:26 -0400 Subject: std.zig: update to new pointer syntax --- std/zig/ast.zig | 8 ++++---- std/zig/parse.zig | 26 +++++++++++++------------- std/zig/parser_test.zig | 46 +++++++++++++++++++++++----------------------- std/zig/render.zig | 34 ++++++++++++++++++++++------------ 4 files changed, 62 insertions(+), 52 deletions(-) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 4d25ceb7db..a4b64d5db2 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1501,23 +1501,23 @@ pub const Node = struct { rhs: *Node, pub const Op = union(enum) { - AddrOf: AddrOfInfo, + AddressOf, ArrayType: *Node, Await, BitNot, BoolNot, Cancel, - PointerType, MaybeType, Negation, NegationWrap, Resume, - SliceType: AddrOfInfo, + PtrType: PtrInfo, + SliceType: PtrInfo, Try, UnwrapMaybe, }; - pub const AddrOfInfo = struct { + pub const PtrInfo = struct { align_info: ?Align, const_token: ?TokenIndex, volatile_token: ?TokenIndex, diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 6d29300aed..6adcf34c95 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -1533,14 +1533,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.SliceOrArrayType => |node| { if (eatToken(&tok_it, &tree, Token.Id.RBracket)) |_| { node.op = ast.Node.PrefixOp.Op{ - .SliceType = ast.Node.PrefixOp.AddrOfInfo{ + .SliceType = ast.Node.PrefixOp.PtrInfo{ .align_info = null, .const_token = null, .volatile_token = null, }, }; stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; - try stack.append(State{ .AddrOfModifiers = &node.op.SliceType }); + try stack.append(State{ .PtrTypeModifiers = &node.op.SliceType }); continue; } @@ -1551,7 +1551,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, - State.AddrOfModifiers => |addr_of_info| { + State.PtrTypeModifiers => |addr_of_info| { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; @@ -1562,7 +1562,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { ((try tree.errors.addOne())).* = Error{ .ExtraAlignQualifier = Error.ExtraAlignQualifier{ .token = token_index } }; return tree; } - addr_of_info.align_info = ast.Node.PrefixOp.AddrOfInfo.Align{ + addr_of_info.align_info = ast.Node.PrefixOp.PtrInfo.Align{ .node = undefined, .bit_range = null, }; @@ -1603,7 +1603,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token = nextToken(&tok_it, &tree); switch (token.ptr.id) { Token.Id.Colon => { - align_info.bit_range = ast.Node.PrefixOp.AddrOfInfo.Align.BitRange(undefined); + align_info.bit_range = ast.Node.PrefixOp.PtrInfo.Align.BitRange(undefined); const bit_range = &??align_info.bit_range; try stack.append(State{ .ExpectToken = Token.Id.RParen }); @@ -2220,7 +2220,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }); opt_ctx.store(&node.base); - // Treat '**' token as two derefs + // Treat '**' token as two pointer types if (token_ptr.id == Token.Id.AsteriskAsterisk) { const child = try arena.construct(ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, @@ -2233,8 +2233,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { } stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; - if (node.op == ast.Node.PrefixOp.Op.AddrOf) { - try stack.append(State{ .AddrOfModifiers = &node.op.AddrOf }); + if (node.op == ast.Node.PrefixOp.Op.PtrType) { + try stack.append(State{ .PtrTypeModifiers = &node.op.PtrType }); } continue; } else { @@ -2963,8 +2963,8 @@ const State = union(enum) { ExternType: ExternTypeCtx, SliceOrArrayAccess: *ast.Node.SuffixOp, SliceOrArrayType: *ast.Node.PrefixOp, - AddrOfModifiers: *ast.Node.PrefixOp.AddrOfInfo, - AlignBitRange: *ast.Node.PrefixOp.AddrOfInfo.Align, + PtrTypeModifiers: *ast.Node.PrefixOp.PtrInfo, + AlignBitRange: *ast.Node.PrefixOp.PtrInfo.Align, Payload: OptionalCtx, PointerPayload: OptionalCtx, @@ -3291,9 +3291,9 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { Token.Id.Tilde => ast.Node.PrefixOp.Op{ .BitNot = void{} }, Token.Id.Minus => ast.Node.PrefixOp.Op{ .Negation = void{} }, Token.Id.MinusPercent => ast.Node.PrefixOp.Op{ .NegationWrap = void{} }, - Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op{ .PointerType = void{} }, - Token.Id.Ampersand => ast.Node.PrefixOp.Op{ - .AddrOf = ast.Node.PrefixOp.AddrOfInfo{ + Token.Id.Ampersand => ast.Node.PrefixOp.Op{ .AddressOf = void{} }, + Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op{ + .PtrType = ast.Node.PrefixOp.PtrInfo{ .align_info = null, .const_token = null, .volatile_token = null, diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index d90cc9ec22..bad677580c 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -529,7 +529,7 @@ test "zig fmt: line comment after doc comment" { test "zig fmt: float literal with exponent" { try testCanonical( \\test "bit field alignment" { - \\ assert(@typeOf(&blah.b) == &align(1:3:6) const u3); + \\ assert(@typeOf(&blah.b) == *align(1:3:6) const u3); \\} \\ ); @@ -1040,7 +1040,7 @@ test "zig fmt: alignment" { test "zig fmt: C main" { try testCanonical( - \\fn main(argc: c_int, argv: &&u8) c_int { + \\fn main(argc: c_int, argv: **u8) c_int { \\ const a = b; \\} \\ @@ -1049,7 +1049,7 @@ test "zig fmt: C main" { test "zig fmt: return" { try testCanonical( - \\fn foo(argc: c_int, argv: &&u8) c_int { + \\fn foo(argc: c_int, argv: **u8) c_int { \\ return 0; \\} \\ @@ -1062,20 +1062,20 @@ test "zig fmt: return" { test "zig fmt: pointer attributes" { try testCanonical( - \\extern fn f1(s: &align(&u8) u8) c_int; - \\extern fn f2(s: &&align(1) &const &volatile u8) c_int; - \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int; - \\extern fn f4(s: &align(1) const volatile u8) c_int; + \\extern fn f1(s: *align(*u8) u8) c_int; + \\extern fn f2(s: **align(1) *const *volatile u8) c_int; + \\extern fn f3(s: *align(1) const *align(1) volatile *const volatile u8) c_int; + \\extern fn f4(s: *align(1) const volatile u8) c_int; \\ ); } test "zig fmt: slice attributes" { try testCanonical( - \\extern fn f1(s: &align(&u8) u8) c_int; - \\extern fn f2(s: &&align(1) &const &volatile u8) c_int; - \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int; - \\extern fn f4(s: &align(1) const volatile u8) c_int; + \\extern fn f1(s: *align(*u8) u8) c_int; + \\extern fn f2(s: **align(1) *const *volatile u8) c_int; + \\extern fn f3(s: *align(1) const *align(1) volatile *const volatile u8) c_int; + \\extern fn f4(s: *align(1) const volatile u8) c_int; \\ ); } @@ -1212,18 +1212,18 @@ test "zig fmt: var type" { test "zig fmt: functions" { try testCanonical( - \\extern fn puts(s: &const u8) c_int; - \\extern "c" fn puts(s: &const u8) c_int; - \\export fn puts(s: &const u8) c_int; - \\inline fn puts(s: &const u8) c_int; - \\pub extern fn puts(s: &const u8) c_int; - \\pub extern "c" fn puts(s: &const u8) c_int; - \\pub export fn puts(s: &const u8) c_int; - \\pub inline fn puts(s: &const u8) c_int; - \\pub extern fn puts(s: &const u8) align(2 + 2) c_int; - \\pub extern "c" fn puts(s: &const u8) align(2 + 2) c_int; - \\pub export fn puts(s: &const u8) align(2 + 2) c_int; - \\pub inline fn puts(s: &const u8) align(2 + 2) c_int; + \\extern fn puts(s: *const u8) c_int; + \\extern "c" fn puts(s: *const u8) c_int; + \\export fn puts(s: *const u8) c_int; + \\inline fn puts(s: *const u8) c_int; + \\pub extern fn puts(s: *const u8) c_int; + \\pub extern "c" fn puts(s: *const u8) c_int; + \\pub export fn puts(s: *const u8) c_int; + \\pub inline fn puts(s: *const u8) c_int; + \\pub extern fn puts(s: *const u8) align(2 + 2) c_int; + \\pub extern "c" fn puts(s: *const u8) align(2 + 2) c_int; + \\pub export fn puts(s: *const u8) align(2 + 2) c_int; + \\pub inline fn puts(s: *const u8) align(2 + 2) c_int; \\ ); } diff --git a/std/zig/render.zig b/std/zig/render.zig index 07e01241b7..a4aaa9a2a4 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -343,9 +343,13 @@ fn renderExpression( const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); switch (prefix_op_node.op) { - ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { - try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // & - if (addr_of_info.align_info) |align_info| { + ast.Node.PrefixOp.Op.PtrType => |ptr_info| { + const star_offset = switch (tree.tokens.at(prefix_op_node.op_token).id) { + Token.Id.AsteriskAsterisk => usize(1), + else => usize(0), + }; + try renderTokenOffset(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None, star_offset); // * + if (ptr_info.align_info) |align_info| { const lparen_token = tree.prevToken(align_info.node.firstToken()); const align_token = tree.prevToken(lparen_token); @@ -370,19 +374,19 @@ fn renderExpression( try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) } } - if (addr_of_info.const_token) |const_token| { + if (ptr_info.const_token) |const_token| { try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const } - if (addr_of_info.volatile_token) |volatile_token| { + if (ptr_info.volatile_token) |volatile_token| { try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile } }, - ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { + ast.Node.PrefixOp.Op.SliceType => |ptr_info| { try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [ try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ] - if (addr_of_info.align_info) |align_info| { + if (ptr_info.align_info) |align_info| { const lparen_token = tree.prevToken(align_info.node.firstToken()); const align_token = tree.prevToken(lparen_token); @@ -407,10 +411,10 @@ fn renderExpression( try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) } } - if (addr_of_info.const_token) |const_token| { + if (ptr_info.const_token) |const_token| { try renderToken(tree, stream, const_token, indent, start_col, Space.Space); } - if (addr_of_info.volatile_token) |volatile_token| { + if (ptr_info.volatile_token) |volatile_token| { try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); } }, @@ -426,7 +430,7 @@ fn renderExpression( ast.Node.PrefixOp.Op.NegationWrap, ast.Node.PrefixOp.Op.UnwrapMaybe, ast.Node.PrefixOp.Op.MaybeType, - ast.Node.PrefixOp.Op.PointerType, + ast.Node.PrefixOp.Op.AddressOf, => { try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); }, @@ -1761,7 +1765,9 @@ const Space = enum { BlockStart, }; -fn renderToken(tree: *ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, start_col: *usize, space: Space) (@typeOf(stream).Child.Error || Error)!void { +fn renderTokenOffset(tree: *ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, start_col: *usize, space: Space, + token_skip_bytes: usize, +) (@typeOf(stream).Child.Error || Error)!void { if (space == Space.BlockStart) { if (start_col.* < indent + indent_delta) return renderToken(tree, stream, token_index, indent, start_col, Space.Space); @@ -1772,7 +1778,7 @@ fn renderToken(tree: *ast.Tree, stream: var, token_index: ast.TokenIndex, indent } var token = tree.tokens.at(token_index); - try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(token), " ")); + try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(token)[token_skip_bytes..], " ")); if (space == Space.NoComment) return; @@ -1927,6 +1933,10 @@ fn renderToken(tree: *ast.Tree, stream: var, token_index: ast.TokenIndex, indent } } +fn renderToken(tree: *ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, start_col: *usize, space: Space,) (@typeOf(stream).Child.Error || Error)!void { + return renderTokenOffset(tree, stream, token_index, indent, start_col, space, 0); +} + fn renderDocComments( tree: *ast.Tree, stream: var, -- cgit v1.2.3 From 5f38a01edeaa150bef741c940311a9e15d0cd327 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Jun 2018 01:22:22 -0400 Subject: run zig fmt --- std/heap.zig | 2 +- std/zig/render.zig | 17 +++++++++++++++-- test/cases/math.zig | 36 +++++++++++++++++++++++++++--------- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/std/heap.zig b/std/heap.zig index 81d6f25282..d15a99a757 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -343,7 +343,7 @@ pub const ThreadSafeFixedBufferAllocator = struct { if (new_end_index > self.buffer.len) { return error.OutOfMemory; } - end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst,) ?? return self.buffer[adjusted_index..new_end_index]; + end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index..new_end_index]; } } diff --git a/std/zig/render.zig b/std/zig/render.zig index a4aaa9a2a4..147adc6221 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -1765,7 +1765,13 @@ const Space = enum { BlockStart, }; -fn renderTokenOffset(tree: *ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, start_col: *usize, space: Space, +fn renderTokenOffset( + tree: *ast.Tree, + stream: var, + token_index: ast.TokenIndex, + indent: usize, + start_col: *usize, + space: Space, token_skip_bytes: usize, ) (@typeOf(stream).Child.Error || Error)!void { if (space == Space.BlockStart) { @@ -1933,7 +1939,14 @@ fn renderTokenOffset(tree: *ast.Tree, stream: var, token_index: ast.TokenIndex, } } -fn renderToken(tree: *ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, start_col: *usize, space: Space,) (@typeOf(stream).Child.Error || Error)!void { +fn renderToken( + tree: *ast.Tree, + stream: var, + token_index: ast.TokenIndex, + indent: usize, + start_col: *usize, + space: Space, +) (@typeOf(stream).Child.Error || Error)!void { return renderTokenOffset(tree, stream, token_index, indent, start_col, space, 0); } diff --git a/test/cases/math.zig b/test/cases/math.zig index 5f16e903b2..0c18293dd5 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -28,13 +28,27 @@ fn testDivision() void { assert(divTrunc(f32, -5.0, 3.0) == -1.0); comptime { - assert(1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,); - assert(@rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600,); - assert(1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,); - assert(@divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,); - assert(@divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,); - assert(@divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,); - assert(4126227191251978491697987544882340798050766755606969681711 % 10 == 1,); + assert( + 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, + ); + assert( + @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, + ); + assert( + 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, + ); + assert( + @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, + ); + assert( + @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, + ); + assert( + @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, + ); + assert( + 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, + ); } } fn div(comptime T: type, a: T, b: T) T { @@ -324,8 +338,12 @@ test "big number addition" { test "big number multiplication" { comptime { - assert(45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567,); - assert(594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016,); + assert( + 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, + ); + assert( + 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016, + ); } } -- cgit v1.2.3 From e29d12d8218c6f84d4fd59b7c8672d3b38c79390 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Jun 2018 01:29:34 -0400 Subject: fix incorrect address-of syntax on windows --- std/os/child_process.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 30a2fd1408..0e80ae09c1 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -254,7 +254,7 @@ pub const ChildProcess = struct { self.term = (SpawnError!Term)(x: { var exit_code: windows.DWORD = undefined; - if (windows.GetExitCodeProcess(self.handle, *exit_code) == 0) { + if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) { break :x Term{ .Unknown = 0 }; } else { break :x Term{ .Exited = @bitCast(i32, exit_code) }; -- cgit v1.2.3 From 08693411d26daefcda327199641c5d32f82f9827 Mon Sep 17 00:00:00 2001 From: Arthur Elliott Date: Fri, 1 Jun 2018 12:23:07 -0400 Subject: fix typo (#1034) --- std/json.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/json.zig b/std/json.zig index c8aef7688b..71673ad20f 100644 --- a/std/json.zig +++ b/std/json.zig @@ -10,7 +10,7 @@ const u256 = @IntType(false, 256); // A single token slice into the parent string. // -// Use `token.slice()` on the inptu at the current position to get the current slice. +// Use `token.slice()` on the input at the current position to get the current slice. pub const Token = struct { id: Id, // How many bytes do we skip before counting -- cgit v1.2.3 From 081072d3b6809d5e1725ceb5e2f6c0dcf4d39d9f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Jun 2018 11:58:37 -0400 Subject: docs: add missing builtin to langref syntax coloring --- doc/langref.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 3bd1124e00..fb02e6277d 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6544,7 +6544,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo newStackCall", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; -- cgit v1.2.3 From a3d7a807b77aebf3de61cf28319b4a34402a653a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Jun 2018 11:58:58 -0400 Subject: appveyor: remove llvm 5.0.1 from cache --- ci/appveyor/appveyor.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/ci/appveyor/appveyor.yml b/ci/appveyor/appveyor.yml index 2122153494..5a2ad9bca1 100644 --- a/ci/appveyor/appveyor.yml +++ b/ci/appveyor/appveyor.yml @@ -6,5 +6,4 @@ build_script: after_build: - '%APPVEYOR_BUILD_FOLDER%\ci\appveyor\after_build.bat' cache: - - 'llvm+clang-5.0.1-win64-msvc-release.tar.xz' - 'llvm+clang-6.0.0-win64-msvc-release.tar.xz' -- cgit v1.2.3 From 7b386ea24285db2fbc40233a232cb294dddf9879 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Jun 2018 21:51:54 -0400 Subject: fix build file template See #1035 --- std/special/build_file_template.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/special/build_file_template.zig b/std/special/build_file_template.zig index 1e3eb01136..11e9647698 100644 --- a/std/special/build_file_template.zig +++ b/std/special/build_file_template.zig @@ -5,6 +5,6 @@ pub fn build(b: *Builder) void { const exe = b.addExecutable("YOUR_NAME_HERE", "src/main.zig"); exe.setBuildMode(mode); - b.default_step.dependOn(*exe.step); + b.default_step.dependOn(&exe.step); b.installArtifact(exe); } -- cgit v1.2.3 From f06bce5ddaea368040560f584170aee2864fa399 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Jun 2018 04:03:25 -0400 Subject: introduce [*] for unknown length pointers See #770 Currently it does not have any different behavior than `*` but it is now recommended to use `[*]` for unknown length pointers to be future-proof. Instead of [ * ] being separate tokens as the proposal suggested, this commit implements `[*]` as a single token. --- doc/langref.html.in | 2 +- src/all_types.hpp | 1 + src/parser.cpp | 5 +++-- src/tokenizer.cpp | 31 ++++++++++++++++++++++++++++++- src/tokenizer.hpp | 1 + std/cstr.zig | 8 ++++---- std/zig/parse.zig | 2 +- std/zig/parser_test.zig | 7 +++++++ std/zig/tokenizer.zig | 42 +++++++++++++++++++++++++++++++++++++++--- 9 files changed, 87 insertions(+), 12 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index fb02e6277d..217f02777f 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6450,7 +6450,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",") StructLiteralField = "." Symbol "=" Expression -PrefixOp = "!" | "-" | "~" | ("*" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" +PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType diff --git a/src/all_types.hpp b/src/all_types.hpp index d5906cae95..8e65cfc789 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -625,6 +625,7 @@ struct AstNodePrefixOpExpr { }; struct AstNodePointerType { + Token *star_token; AstNode *align_expr; BigInt *bit_offset_start; BigInt *bit_offset_end; diff --git a/src/parser.cpp b/src/parser.cpp index ef390a3a2e..6c900c3bfa 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1174,6 +1174,7 @@ static PrefixOp tok_to_prefix_op(Token *token) { static AstNode *ast_parse_pointer_type(ParseContext *pc, size_t *token_index, Token *star_tok) { AstNode *node = ast_create_node(pc, NodeTypePointerType, star_tok); + node->data.pointer_type.star_token = star_tok; Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdKeywordAlign) { @@ -1211,11 +1212,11 @@ static AstNode *ast_parse_pointer_type(ParseContext *pc, size_t *token_index, To /* PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression -PrefixOp = "!" | "-" | "~" | ("*" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" +PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" */ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdStar) { + if (token->id == TokenIdStar || token->id == TokenIdBracketStarBracket) { *token_index += 1; return ast_parse_pointer_type(pc, token_index, token); } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 365b35cdfd..badbd695ec 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -219,6 +219,8 @@ enum TokenizeState { TokenizeStateSawAtSign, TokenizeStateCharCode, TokenizeStateError, + TokenizeStateLBracket, + TokenizeStateLBracketStar, }; @@ -539,8 +541,8 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); break; case '[': + t.state = TokenizeStateLBracket; begin_token(&t, TokenIdLBracket); - end_token(&t); break; case ']': begin_token(&t, TokenIdRBracket); @@ -852,6 +854,30 @@ void tokenize(Buf *buf, Tokenization *out) { continue; } break; + case TokenizeStateLBracket: + switch (c) { + case '*': + t.state = TokenizeStateLBracketStar; + set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket); + break; + default: + // reinterpret as just an lbracket + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateLBracketStar: + switch (c) { + case ']': + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + invalid_char_error(&t, c); + } + break; case TokenizeStateSawPlusPercent: switch (c) { case '=': @@ -1467,12 +1493,14 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateLineString: case TokenizeStateLineStringEnd: case TokenizeStateSawBarBar: + case TokenizeStateLBracket: end_token(&t); break; case TokenizeStateSawDotDot: case TokenizeStateSawBackslash: case TokenizeStateLineStringContinue: case TokenizeStateLineStringContinueC: + case TokenizeStateLBracketStar: tokenize_error(&t, "unexpected EOF"); break; case TokenizeStateLineComment: @@ -1509,6 +1537,7 @@ const char * token_name(TokenId id) { case TokenIdBitShiftRight: return ">>"; case TokenIdBitShiftRightEq: return ">>="; case TokenIdBitXorEq: return "^="; + case TokenIdBracketStarBracket: return "[*]"; case TokenIdCharLiteral: return "CharLiteral"; case TokenIdCmpEq: return "=="; case TokenIdCmpGreaterOrEq: return ">="; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index b719293704..d659c0a772 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -28,6 +28,7 @@ enum TokenId { TokenIdBitShiftRight, TokenIdBitShiftRightEq, TokenIdBitXorEq, + TokenIdBracketStarBracket, TokenIdCharLiteral, TokenIdCmpEq, TokenIdCmpGreaterOrEq, diff --git a/std/cstr.zig b/std/cstr.zig index dfbfb8047f..d60adf8faa 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -9,13 +9,13 @@ pub const line_sep = switch (builtin.os) { else => "\n", }; -pub fn len(ptr: *const u8) usize { +pub fn len(ptr: [*]const u8) usize { var count: usize = 0; while (ptr[count] != 0) : (count += 1) {} return count; } -pub fn cmp(a: *const u8, b: *const u8) i8 { +pub fn cmp(a: [*]const u8, b: [*]const u8) i8 { var index: usize = 0; while (a[index] == b[index] and a[index] != 0) : (index += 1) {} if (a[index] > b[index]) { @@ -27,11 +27,11 @@ pub fn cmp(a: *const u8, b: *const u8) i8 { } } -pub fn toSliceConst(str: *const u8) []const u8 { +pub fn toSliceConst(str: [*]const u8) []const u8 { return str[0..len(str)]; } -pub fn toSlice(str: *u8) []u8 { +pub fn toSlice(str: [*]u8) []u8 { return str[0..len(str)]; } diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 6adcf34c95..7faca8e11b 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -3292,7 +3292,7 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { Token.Id.Minus => ast.Node.PrefixOp.Op{ .Negation = void{} }, Token.Id.MinusPercent => ast.Node.PrefixOp.Op{ .NegationWrap = void{} }, Token.Id.Ampersand => ast.Node.PrefixOp.Op{ .AddressOf = void{} }, - Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op{ + Token.Id.Asterisk, Token.Id.AsteriskAsterisk, Token.Id.BracketStarBracket => ast.Node.PrefixOp.Op{ .PtrType = ast.Node.PrefixOp.PtrInfo{ .align_info = null, .const_token = null, diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index bad677580c..c28a70b770 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,10 @@ +test "zig fmt: pointer of unknown length" { + try testCanonical( + \\fn foo(ptr: [*]u8) void {} + \\ + ); +} + test "zig fmt: spaces around slice operator" { try testCanonical( \\var a = b[c..d]; diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 8378a9011d..b288a3adb7 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -143,6 +143,7 @@ pub const Token = struct { FloatLiteral, LineComment, DocComment, + BracketStarBracket, Keyword_align, Keyword_and, Keyword_asm, @@ -263,6 +264,8 @@ pub const Tokenizer = struct { Period, Period2, SawAtSign, + LBracket, + LBracketStar, }; pub fn next(self: *Tokenizer) Token { @@ -325,9 +328,7 @@ pub const Tokenizer = struct { break; }, '[' => { - result.id = Token.Id.LBracket; - self.index += 1; - break; + state = State.LBracket; }, ']' => { result.id = Token.Id.RBracket; @@ -429,6 +430,28 @@ pub const Tokenizer = struct { }, }, + State.LBracket => switch (c) { + '*' => { + state = State.LBracketStar; + }, + else => { + result.id = Token.Id.LBracket; + break; + }, + }, + + State.LBracketStar => switch (c) { + ']' => { + result.id = Token.Id.BracketStarBracket; + self.index += 1; + break; + }, + else => { + result.id = Token.Id.Invalid; + break; + }, + }, + State.Ampersand => switch (c) { '=' => { result.id = Token.Id.AmpersandEqual; @@ -1008,6 +1031,7 @@ pub const Tokenizer = struct { State.CharLiteralEscape2, State.CharLiteralEnd, State.StringLiteralBackslash, + State.LBracketStar, => { result.id = Token.Id.Invalid; }, @@ -1024,6 +1048,9 @@ pub const Tokenizer = struct { State.Slash => { result.id = Token.Id.Slash; }, + State.LBracket => { + result.id = Token.Id.LBracket; + }, State.Zero => { result.id = Token.Id.IntegerLiteral; }, @@ -1142,6 +1169,15 @@ test "tokenizer" { testTokenize("test", []Token.Id{Token.Id.Keyword_test}); } +test "tokenizer - unknown length pointer" { + testTokenize( + \\[*]u8 + , []Token.Id{ + Token.Id.BracketStarBracket, + Token.Id.Identifier, + }); +} + test "tokenizer - char literal with hex escape" { testTokenize( \\'\x1b' -- cgit v1.2.3 From e514454c0e092794171d4a62260971cc8de6f200 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 2 Jun 2018 20:49:35 +1200 Subject: Make zig fmt exit with error on any parse errors This is required for proper detection in editor plugins. Other files may have been formatted correctly, this only indicates that some failed. --- src-self-hosted/main.zig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 80b1c3889a..7a62f4985b 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -728,18 +728,21 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { } }; + var fmt_errors = false; for (flags.positionals.toSliceConst()) |file_path| { var file = try os.File.openRead(allocator, file_path); defer file.close(); const source_code = io.readFileAlloc(allocator, file_path) catch |err| { try stderr.print("unable to open '{}': {}", file_path, err); + fmt_errors = true; continue; }; defer allocator.free(source_code); var tree = std.zig.parse(allocator, source_code) catch |err| { try stderr.print("error parsing file '{}': {}\n", file_path, err); + fmt_errors = true; continue; }; defer tree.deinit(); @@ -752,6 +755,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { try errmsg.printToFile(&stderr_file, msg, color); } if (tree.errors.len != 0) { + fmt_errors = true; continue; } @@ -764,6 +768,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { try baf.finish(); } } + + if (fmt_errors) { + os.exit(1); + } } // cmd:targets ///////////////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From b85b68a7fd175169e0f07ab733bde6d5654b1044 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Jun 2018 15:20:51 -0400 Subject: better compile error for error sets behind nullable --- src/ir.cpp | 17 +++++++++++++---- test/compile_errors.zig | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index d996b4a2be..5cada29076 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7934,11 +7934,20 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, return ImplicitCastMatchResultReportedError; } + // implicit conversion from ?T to ?U + if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { + ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, + actual_type->data.maybe.child_type, value); + if (res != ImplicitCastMatchResultNo) + return res; + } + // implicit conversion from non maybe type to maybe type - if (expected_type->id == TypeTableEntryIdMaybe && - ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, actual_type, value)) - { - return ImplicitCastMatchResultYes; + if (expected_type->id == TypeTableEntryIdMaybe) { + ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, + actual_type, value); + if (res != ImplicitCastMatchResultNo) + return res; } // implicit conversion from null literal to maybe type diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 132dc8cd80..ea1357f5bb 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,23 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "invalid deref on switch target", + \\const NextError = error{NextError}; + \\const OtherError = error{OutOfMemory}; + \\ + \\export fn entry() void { + \\ const a: ?NextError!i32 = foo(); + \\} + \\ + \\fn foo() ?OtherError!i32 { + \\ return null; + \\} + , + ".tmp_source.zig:5:34: error: expected 'NextError!i32', found 'OtherError!i32'", + ".tmp_source.zig:2:26: note: 'error.OutOfMemory' not a member of destination error set", + ); + cases.add( "invalid deref on switch target", \\comptime { -- cgit v1.2.3 From 4c273126dfc44cf4fcf9d5d97bf1cb1da07d7bd7 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sun, 3 Jun 2018 18:30:43 +1200 Subject: Add context to zig_unreachable calls (#1039) This greatly aids debugging on platforms with no stack-traces. --- src/util.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/util.hpp b/src/util.hpp index ae33cb84af..25141d8435 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -38,11 +38,11 @@ ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF(1, 2) void zig_panic(const char *format, ...); -ATTRIBUTE_COLD -ATTRIBUTE_NORETURN -static inline void zig_unreachable(void) { - zig_panic("unreachable"); -} +#ifdef WIN32 +#define __func__ __FUNCTION__ +#endif + +#define zig_unreachable() zig_panic("unreachable: %s:%s:%d", __FILE__, __func__, __LINE__) #if defined(_MSC_VER) static inline int clzll(unsigned long long mask) { -- cgit v1.2.3 From 96164ce61377b36bcaf0c4087ca9b1ab822b9457 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Jun 2018 01:09:15 -0400 Subject: disallow single-item pointer indexing add pointer arithmetic for unknown length pointer --- doc/langref.html.in | 48 ++++++----- src/all_types.hpp | 9 ++ src/analyze.cpp | 53 ++++++++---- src/analyze.hpp | 2 +- src/ast_render.cpp | 8 +- src/codegen.cpp | 49 +++++++---- src/ir.cpp | 177 ++++++++++++++++++++++++++++----------- src/parser.cpp | 1 + std/buffer.zig | 2 +- std/c/darwin.zig | 4 +- std/c/index.zig | 50 +++++------ std/c/linux.zig | 2 +- std/cstr.zig | 10 +-- std/heap.zig | 18 ++-- std/os/child_process.zig | 2 +- std/os/darwin.zig | 45 +++++----- std/os/file.zig | 4 +- std/os/index.zig | 101 +++++++--------------- std/os/linux/index.zig | 123 ++++++++++++++++----------- std/os/linux/test.zig | 3 +- std/os/linux/vdso.zig | 26 +++--- std/os/windows/index.zig | 30 +++---- std/os/windows/util.zig | 2 +- std/segmented_list.zig | 12 +-- std/special/bootstrap.zig | 22 ++--- std/special/builtin.zig | 6 +- test/cases/align.zig | 49 ++++------- test/cases/const_slice_child.zig | 9 +- test/cases/for.zig | 26 +----- test/cases/misc.zig | 11 +-- test/cases/pointers.zig | 30 +++++++ test/cases/struct.zig | 6 +- test/compare_output.zig | 16 ++-- test/compile_errors.zig | 15 +++- test/translate_c.zig | 56 ++++++------- 35 files changed, 584 insertions(+), 443 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 217f02777f..32481ade50 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -458,7 +458,7 @@ test "string literals" { // A C string literal is a null terminated pointer. const null_terminated_bytes = c"hello"; - assert(@typeOf(null_terminated_bytes) == *const u8); + assert(@typeOf(null_terminated_bytes) == [*]const u8); assert(null_terminated_bytes[5] == 0); } {#code_end#} @@ -547,7 +547,7 @@ const c_string_literal = ; {#code_end#}

- In this example the variable c_string_literal has type *const char and + In this example the variable c_string_literal has type [*]const char and has a terminating null byte.

{#see_also|@embedFile#} @@ -1288,7 +1288,7 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; // array literal -const message = []u8{'h', 'e', 'l', 'l', 'o'}; +const message = []u8{ 'h', 'e', 'l', 'l', 'o' }; // get the size of an array comptime { @@ -1324,11 +1324,11 @@ test "modify an array" { // array concatenation works if the values are known // at compile time -const part_one = []i32{1, 2, 3, 4}; -const part_two = []i32{5, 6, 7, 8}; +const part_one = []i32{ 1, 2, 3, 4 }; +const part_two = []i32{ 5, 6, 7, 8 }; const all_of_it = part_one ++ part_two; comptime { - assert(mem.eql(i32, all_of_it, []i32{1,2,3,4,5,6,7,8})); + assert(mem.eql(i32, all_of_it, []i32{ 1, 2, 3, 4, 5, 6, 7, 8 })); } // remember that string literals are arrays @@ -1357,7 +1357,7 @@ comptime { var fancy_array = init: { var initial_value: [10]Point = undefined; for (initial_value) |*pt, i| { - pt.* = Point { + pt.* = Point{ .x = i32(i), .y = i32(i) * 2, }; @@ -1377,7 +1377,7 @@ test "compile-time array initalization" { // call a function to initialize an array var more_points = []Point{makePoint(3)} ** 10; fn makePoint(x: i32) Point { - return Point { + return Point{ .x = x, .y = x * 2, }; @@ -1414,25 +1414,24 @@ test "address of syntax" { } test "pointer array access" { - // Pointers do not support pointer arithmetic. If you - // need such a thing, use array index syntax: + // Taking an address of an individual element gives a + // pointer to a single item. This kind of pointer + // does not support pointer arithmetic. var array = []u8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - const ptr = &array[1]; + const ptr = &array[2]; + assert(@typeOf(ptr) == *u8); assert(array[2] == 3); - ptr[1] += 1; + ptr.* += 1; assert(array[2] == 4); } test "pointer slicing" { // In Zig, we prefer using slices over null-terminated pointers. - // You can turn a pointer into a slice using slice syntax: + // You can turn an array into a slice using slice syntax: var array = []u8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - const ptr = &array[1]; - const slice = ptr[1..3]; - - assert(slice.ptr == &ptr[1]); + const slice = array[2..4]; assert(slice.len == 2); // Slices have bounds checking and are therefore protected @@ -1622,18 +1621,27 @@ fn foo(bytes: []u8) u32 { const assert = @import("std").debug.assert; test "basic slices" { - var array = []i32{1, 2, 3, 4}; + var array = []i32{ 1, 2, 3, 4 }; // A slice is a pointer and a length. The difference between an array and // a slice is that the array's length is part of the type and known at // compile-time, whereas the slice's length is known at runtime. // Both can be accessed with the `len` field. const slice = array[0..array.len]; - assert(slice.ptr == &array[0]); + assert(&slice[0] == &array[0]); assert(slice.len == array.len); + // Using the address-of operator on a slice gives a pointer to a single + // item, while using the `ptr` field gives an unknown length pointer. + assert(@typeOf(slice.ptr) == [*]i32); + assert(@typeOf(&slice[0]) == *i32); + assert(@ptrToInt(slice.ptr) == @ptrToInt(&slice[0])); + // Slices have array bounds checking. If you try to access something out // of bounds, you'll get a safety check failure: slice[10] += 1; + + // Note that `slice.ptr` does not invoke safety checking, while `&slice[0]` + // asserts that the slice has len >= 1. } {#code_end#}

This is one reason we prefer slices to pointers.

@@ -5937,7 +5945,7 @@ pub const __zig_test_fn_slice = {}; // overwritten later {#header_open|C String Literals#} {#code_begin|exe#} {#link_libc#} -extern fn puts(*const u8) void; +extern fn puts([*]const u8) void; pub fn main() void { puts(c"this has a null terminator"); diff --git a/src/all_types.hpp b/src/all_types.hpp index 8e65cfc789..f1cf96238f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -974,8 +974,14 @@ struct FnTypeId { uint32_t fn_type_id_hash(FnTypeId*); bool fn_type_id_eql(FnTypeId *a, FnTypeId *b); +enum PtrLen { + PtrLenUnknown, + PtrLenSingle, +}; + struct TypeTableEntryPointer { TypeTableEntry *child_type; + PtrLen ptr_len; bool is_const; bool is_volatile; uint32_t alignment; @@ -1397,6 +1403,7 @@ struct TypeId { union { struct { TypeTableEntry *child_type; + PtrLen ptr_len; bool is_const; bool is_volatile; uint32_t alignment; @@ -2268,6 +2275,7 @@ struct IrInstructionElemPtr { IrInstruction *array_ptr; IrInstruction *elem_index; + PtrLen ptr_len; bool is_const; bool safety_check_on; }; @@ -2419,6 +2427,7 @@ struct IrInstructionPtrType { IrInstruction *child_type; uint32_t bit_offset_start; uint32_t bit_offset_end; + PtrLen ptr_len; bool is_const; bool is_volatile; }; diff --git a/src/analyze.cpp b/src/analyze.cpp index a5011035c5..2b9d776e78 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -381,14 +381,14 @@ TypeTableEntry *get_promise_type(CodeGen *g, TypeTableEntry *result_type) { } TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const, - bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count) + bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count) { assert(!type_is_invalid(child_type)); TypeId type_id = {}; TypeTableEntry **parent_pointer = nullptr; uint32_t abi_alignment = get_abi_alignment(g, child_type); - if (unaligned_bit_count != 0 || is_volatile || byte_alignment != abi_alignment) { + if (unaligned_bit_count != 0 || is_volatile || byte_alignment != abi_alignment || ptr_len != PtrLenSingle) { type_id.id = TypeTableEntryIdPointer; type_id.data.pointer.child_type = child_type; type_id.data.pointer.is_const = is_const; @@ -396,6 +396,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type type_id.data.pointer.alignment = byte_alignment; type_id.data.pointer.bit_offset = bit_offset; type_id.data.pointer.unaligned_bit_count = unaligned_bit_count; + type_id.data.pointer.ptr_len = ptr_len; auto existing_entry = g->type_table.maybe_get(type_id); if (existing_entry) @@ -414,16 +415,17 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer); entry->is_copyable = true; + const char *star_str = ptr_len == PtrLenSingle ? "*" : "[*]"; const char *const_str = is_const ? "const " : ""; const char *volatile_str = is_volatile ? "volatile " : ""; buf_resize(&entry->name, 0); if (unaligned_bit_count == 0 && byte_alignment == abi_alignment) { - buf_appendf(&entry->name, "*%s%s%s", const_str, volatile_str, buf_ptr(&child_type->name)); + buf_appendf(&entry->name, "%s%s%s%s", star_str, const_str, volatile_str, buf_ptr(&child_type->name)); } else if (unaligned_bit_count == 0) { - buf_appendf(&entry->name, "*align(%" PRIu32 ") %s%s%s", byte_alignment, + buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s", star_str, byte_alignment, const_str, volatile_str, buf_ptr(&child_type->name)); } else { - buf_appendf(&entry->name, "*align(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", byte_alignment, + buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str, byte_alignment, bit_offset, bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name)); } @@ -433,7 +435,9 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type if (!entry->zero_bits) { assert(byte_alignment > 0); - if (is_const || is_volatile || unaligned_bit_count != 0 || byte_alignment != abi_alignment) { + if (is_const || is_volatile || unaligned_bit_count != 0 || byte_alignment != abi_alignment || + ptr_len != PtrLenSingle) + { TypeTableEntry *peer_type = get_pointer_to_type(g, child_type, false); entry->type_ref = peer_type->type_ref; entry->di_type = peer_type->di_type; @@ -451,6 +455,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type entry->di_type = g->builtin_types.entry_void->di_type; } + entry->data.pointer.ptr_len = ptr_len; entry->data.pointer.child_type = child_type; entry->data.pointer.is_const = is_const; entry->data.pointer.is_volatile = is_volatile; @@ -467,7 +472,8 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type } TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) { - return get_pointer_to_type_extra(g, child_type, is_const, false, get_abi_alignment(g, child_type), 0, 0); + return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, + get_abi_alignment(g, child_type), 0, 0); } TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) { @@ -757,6 +763,7 @@ static void slice_type_common_init(CodeGen *g, TypeTableEntry *pointer_type, Typ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) { assert(ptr_type->id == TypeTableEntryIdPointer); + assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown); TypeTableEntry **parent_pointer = &ptr_type->data.pointer.slice_parent; if (*parent_pointer) { @@ -768,14 +775,16 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) { // replace the & with [] to go from a ptr type name to a slice type name buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + 1); + size_t name_offset = (ptr_type->data.pointer.ptr_len == PtrLenSingle) ? 1 : 3; + buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + name_offset); TypeTableEntry *child_type = ptr_type->data.pointer.child_type; - uint32_t abi_alignment; + uint32_t abi_alignment = get_abi_alignment(g, child_type); if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile || - ptr_type->data.pointer.alignment != (abi_alignment = get_abi_alignment(g, child_type))) + ptr_type->data.pointer.alignment != abi_alignment) { - TypeTableEntry *peer_ptr_type = get_pointer_to_type(g, child_type, false); + TypeTableEntry *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false, + PtrLenUnknown, abi_alignment, 0, 0); TypeTableEntry *peer_slice_type = get_slice_type(g, peer_ptr_type); slice_type_common_init(g, ptr_type, entry); @@ -799,9 +808,11 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) { if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile || child_ptr_type->data.pointer.alignment != get_abi_alignment(g, grand_child_type)) { - TypeTableEntry *bland_child_ptr_type = get_pointer_to_type(g, grand_child_type, false); + TypeTableEntry *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false, + PtrLenUnknown, get_abi_alignment(g, grand_child_type), 0, 0); TypeTableEntry *bland_child_slice = get_slice_type(g, bland_child_ptr_type); - TypeTableEntry *peer_ptr_type = get_pointer_to_type(g, bland_child_slice, false); + TypeTableEntry *peer_ptr_type = get_pointer_to_type_extra(g, bland_child_slice, false, false, + PtrLenUnknown, get_abi_alignment(g, bland_child_slice), 0, 0); TypeTableEntry *peer_slice_type = get_slice_type(g, peer_ptr_type); entry->type_ref = peer_slice_type->type_ref; @@ -1284,7 +1295,8 @@ static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_ } static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) { - TypeTableEntry *ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, ptr_type); IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr); if (type_is_invalid(instr->value.type)) @@ -2954,7 +2966,8 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { if (fn_type_id->param_count != 2) { return wrong_panic_prototype(g, proto_node, fn_type); } - TypeTableEntry *const_u8_ptr = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *const_u8_ptr = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *const_u8_slice = get_slice_type(g, const_u8_ptr); if (fn_type_id->param_info[0].type != const_u8_slice) { return wrong_panic_prototype(g, proto_node, fn_type); @@ -4994,7 +5007,9 @@ void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { // then make the pointer point to it const_val->special = ConstValSpecialStatic; - const_val->type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + // TODO make this `[*]null u8` instead of `[*]u8` + const_val->type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); const_val->data.x_ptr.special = ConstPtrSpecialBaseArray; const_val->data.x_ptr.data.base_array.array_val = array_val; const_val->data.x_ptr.data.base_array.elem_index = 0; @@ -5135,7 +5150,9 @@ void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *arr { assert(array_val->type->id == TypeTableEntryIdArray); - TypeTableEntry *ptr_type = get_pointer_to_type(g, array_val->type->data.array.child_type, is_const); + TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, array_val->type->data.array.child_type, + is_const, false, PtrLenUnknown, get_abi_alignment(g, array_val->type->data.array.child_type), + 0, 0); const_val->special = ConstValSpecialStatic; const_val->type = get_slice_type(g, ptr_type); @@ -5759,6 +5776,7 @@ uint32_t type_id_hash(TypeId x) { return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type); case TypeTableEntryIdPointer: return hash_ptr(x.data.pointer.child_type) + + ((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) + (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) + (x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) + (((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) + @@ -5807,6 +5825,7 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdPointer: return a.data.pointer.child_type == b.data.pointer.child_type && + a.data.pointer.ptr_len == b.data.pointer.ptr_len && a.data.pointer.is_const == b.data.pointer.is_const && a.data.pointer.is_volatile == b.data.pointer.is_volatile && a.data.pointer.alignment == b.data.pointer.alignment && diff --git a/src/analyze.hpp b/src/analyze.hpp index d538f042ce..905bfa86dd 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -16,7 +16,7 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m TypeTableEntry *new_type_table_entry(TypeTableEntryId id); TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const); TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const, - bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count); + bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count); uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry); uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry); TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_bits); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index f356f406b0..3785cb6ca1 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -625,7 +625,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { case NodeTypePointerType: { if (!grouped) fprintf(ar->f, "("); - fprintf(ar->f, "*"); + const char *star = "[*]"; + if (node->data.pointer_type.star_token != nullptr && + (node->data.pointer_type.star_token->id == TokenIdStar || node->data.pointer_type.star_token->id == TokenIdStarStar)) + { + star = "*"; + } + fprintf(ar->f, "%s", star); if (node->data.pointer_type.align_expr != nullptr) { fprintf(ar->f, "align("); render_node_grouped(ar, node->data.pointer_type.align_expr); diff --git a/src/codegen.cpp b/src/codegen.cpp index d07d427729..64e29a4da4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -893,7 +893,8 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) { assert(val->global_refs->llvm_global); } - TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0)); } @@ -1461,7 +1462,8 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { LLVMValueRef full_buf_ptr = LLVMConstInBoundsGEP(global_array, full_buf_ptr_indices, 2); - TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); LLVMValueRef global_slice_fields[] = { full_buf_ptr, @@ -2212,9 +2214,13 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, IrInstruction *op2 = bin_op_instruction->op2; assert(op1->value.type == op2->value.type || op_id == IrBinOpBitShiftLeftLossy || - op_id == IrBinOpBitShiftLeftExact || op_id == IrBinOpBitShiftRightLossy || - op_id == IrBinOpBitShiftRightExact || - (op1->value.type->id == TypeTableEntryIdErrorSet && op2->value.type->id == TypeTableEntryIdErrorSet)); + op_id == IrBinOpBitShiftLeftExact || op_id == IrBinOpBitShiftRightLossy || + op_id == IrBinOpBitShiftRightExact || + (op1->value.type->id == TypeTableEntryIdErrorSet && op2->value.type->id == TypeTableEntryIdErrorSet) || + (op1->value.type->id == TypeTableEntryIdPointer && + (op_id == IrBinOpAdd || op_id == IrBinOpSub) && + op1->value.type->data.pointer.ptr_len == PtrLenUnknown) + ); TypeTableEntry *type_entry = op1->value.type; bool want_runtime_safety = bin_op_instruction->safety_check_on && @@ -2222,6 +2228,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, LLVMValueRef op1_value = ir_llvm_value(g, op1); LLVMValueRef op2_value = ir_llvm_value(g, op2); + + switch (op_id) { case IrBinOpInvalid: case IrBinOpArrayCat: @@ -2260,7 +2268,11 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, } case IrBinOpAdd: case IrBinOpAddWrap: - if (type_entry->id == TypeTableEntryIdFloat) { + if (type_entry->id == TypeTableEntryIdPointer) { + assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); + // TODO runtime safety + return LLVMBuildInBoundsGEP(g->builder, op1_value, &op2_value, 1, ""); + } else if (type_entry->id == TypeTableEntryIdFloat) { ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); return LLVMBuildFAdd(g->builder, op1_value, op2_value, ""); } else if (type_entry->id == TypeTableEntryIdInt) { @@ -2323,7 +2335,12 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, } case IrBinOpSub: case IrBinOpSubWrap: - if (type_entry->id == TypeTableEntryIdFloat) { + if (type_entry->id == TypeTableEntryIdPointer) { + assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); + // TODO runtime safety + LLVMValueRef subscript_value = LLVMBuildNeg(g->builder, op2_value, ""); + return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); + } else if (type_entry->id == TypeTableEntryIdFloat) { ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); return LLVMBuildFSub(g->builder, op1_value, op2_value, ""); } else if (type_entry->id == TypeTableEntryIdInt) { @@ -2770,7 +2787,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, if (have_init_expr) { assert(var->value->type == init_value->value.type); TypeTableEntry *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, - var->align_bytes, 0, 0); + PtrLenSingle, var->align_bytes, 0, 0); gen_assign_raw(g, var->value_ref, var_ptr_type, ir_llvm_value(g, init_value)); } else { bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base); @@ -4172,7 +4189,7 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, uint32_t field_align_bytes = get_abi_alignment(g, type_struct_field->type_entry); TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, type_struct_field->type_entry, - false, false, field_align_bytes, + false, false, PtrLenSingle, field_align_bytes, (uint32_t)type_struct_field->packed_bits_offset, (uint32_t)type_struct_field->unaligned_bit_count); gen_assign_raw(g, field_ptr, ptr_type, value); @@ -4188,7 +4205,7 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I uint32_t field_align_bytes = get_abi_alignment(g, type_union_field->type_entry); TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, type_union_field->type_entry, - false, false, field_align_bytes, + false, false, PtrLenSingle, field_align_bytes, 0, 0); LLVMValueRef uncasted_union_ptr; @@ -4435,7 +4452,8 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f LLVMPositionBuilderAtEnd(g->builder, ok_block); LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_payload_index, ""); - TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *slice_type = get_slice_type(g, u8_ptr_type); size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, payload_ptr, ptr_field_index, ""); @@ -5377,7 +5395,8 @@ static void generate_error_name_table(CodeGen *g) { assert(g->errors_by_index.length > 0); - TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); LLVMValueRef *values = allocate(g->errors_by_index.length); @@ -5415,7 +5434,8 @@ static void generate_error_name_table(CodeGen *g) { } static void generate_enum_name_tables(CodeGen *g) { - TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); TypeTableEntry *usize = g->builtin_types.entry_usize; @@ -6869,7 +6889,8 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { exit(0); } - TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); TypeTableEntry *fn_type = get_test_fn_type(g); diff --git a/src/ir.cpp b/src/ir.cpp index 5cada29076..a230c60456 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1009,12 +1009,13 @@ static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *so } static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_ptr, - IrInstruction *elem_index, bool safety_check_on) + IrInstruction *elem_index, bool safety_check_on, PtrLen ptr_len) { IrInstructionElemPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->array_ptr = array_ptr; instruction->elem_index = elem_index; instruction->safety_check_on = safety_check_on; + instruction->ptr_len = ptr_len; ir_ref_instruction(array_ptr, irb->current_basic_block); ir_ref_instruction(elem_index, irb->current_basic_block); @@ -1022,15 +1023,6 @@ static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } -static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, - IrInstruction *array_ptr, IrInstruction *elem_index, bool safety_check_on) -{ - IrInstruction *new_instruction = ir_build_elem_ptr(irb, old_instruction->scope, - old_instruction->source_node, array_ptr, elem_index, safety_check_on); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_build_field_ptr_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_ptr, IrInstruction *field_name_expr) { @@ -1188,14 +1180,15 @@ static IrInstruction *ir_build_br_from(IrBuilder *irb, IrInstruction *old_instru } static IrInstruction *ir_build_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value, - uint32_t bit_offset_start, uint32_t bit_offset_end) + IrInstruction *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, + IrInstruction *align_value, uint32_t bit_offset_start, uint32_t bit_offset_end) { IrInstructionPtrType *ptr_type_of_instruction = ir_build_instruction(irb, scope, source_node); ptr_type_of_instruction->align_value = align_value; ptr_type_of_instruction->child_type = child_type; ptr_type_of_instruction->is_const = is_const; ptr_type_of_instruction->is_volatile = is_volatile; + ptr_type_of_instruction->ptr_len = ptr_len; ptr_type_of_instruction->bit_offset_start = bit_offset_start; ptr_type_of_instruction->bit_offset_end = bit_offset_end; @@ -3547,7 +3540,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode return subscript_instruction; IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction, - subscript_instruction, true); + subscript_instruction, true, PtrLenSingle); if (lval.is_ptr) return ptr_instruction; @@ -4626,6 +4619,11 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction * static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePointerType); + // The null check here is for C imports which don't set a token on the AST node. We could potentially + // update that code to create a fake token and then remove this check. + PtrLen ptr_len = (node->data.pointer_type.star_token != nullptr && + (node->data.pointer_type.star_token->id == TokenIdStar || + node->data.pointer_type.star_token->id == TokenIdStarStar)) ? PtrLenSingle : PtrLenUnknown; bool is_const = node->data.pointer_type.is_const; bool is_volatile = node->data.pointer_type.is_volatile; AstNode *expr_node = node->data.pointer_type.op_expr; @@ -4675,7 +4673,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode } return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile, - align_value, bit_offset_start, bit_offset_end); + ptr_len, align_value, bit_offset_start, bit_offset_end); } static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, @@ -5172,7 +5170,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, body_block); - IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false); + IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false, PtrLenSingle); IrInstruction *elem_val; if (node->data.for_expr.elem_is_ptr) { elem_val = elem_ptr; @@ -6811,9 +6809,13 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_normal_final); if (type_has_bits(return_type)) { + IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, + get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, + false, false, PtrLenUnknown, get_abi_alignment(irb->codegen, irb->codegen->builtin_types.entry_u8), + 0, 0)); IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); - IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, result_ptr); - IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, + IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); + IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr); IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node, fn_entry->type_entry->data.fn.fn_type_id.return_type); @@ -7691,6 +7693,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry // pointer const if (expected_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer && + (actual_type->data.pointer.ptr_len == expected_type->data.pointer.ptr_len) && (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) && (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) && actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset && @@ -8644,7 +8647,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (convert_to_const_slice) { assert(prev_inst->value.type->id == TypeTableEntryIdArray); - TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, prev_inst->value.type->data.array.child_type, true); + TypeTableEntry *ptr_type = get_pointer_to_type_extra( + ira->codegen, prev_inst->value.type->data.array.child_type, + true, false, PtrLenUnknown, + get_abi_alignment(ira->codegen, prev_inst->value.type->data.array.child_type), + 0, 0); TypeTableEntry *slice_type = get_slice_type(ira->codegen, ptr_type); if (err_set_type != nullptr) { return get_error_union_type(ira->codegen, err_set_type, slice_type); @@ -8961,7 +8968,7 @@ static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instructio ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) { TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, - ptr_is_const, ptr_is_volatile, ptr_align, 0, 0); + ptr_is_const, ptr_is_volatile, PtrLenSingle, ptr_align, 0, 0); IrInstruction *const_instr = ir_get_const(ira, instruction); ConstExprValue *const_val = &const_instr->value; const_val->type = ptr_type; @@ -9302,7 +9309,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi } TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type, - is_const, is_volatile, get_abi_alignment(ira->codegen, value->value.type), 0, 0); + is_const, is_volatile, PtrLenSingle, get_abi_alignment(ira->codegen, value->value.type), 0, 0); IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope, source_instruction->source_node, value, is_const, is_volatile); new_instruction->value.type = ptr_type; @@ -10399,7 +10406,9 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) { if (type_is_invalid(value->value.type)) return nullptr; - TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, + get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(ira->codegen, ptr_type); IrInstruction *casted_value = ir_implicit_cast(ira, value, str_type); if (type_is_invalid(casted_value->value.type)) @@ -11054,11 +11063,27 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; IrInstruction *op2 = bin_op_instruction->op2->other; + IrBinOp op_id = bin_op_instruction->op_id; + + // look for pointer math + if (op1->value.type->id == TypeTableEntryIdPointer && op1->value.type->data.pointer.ptr_len == PtrLenUnknown && + (op_id == IrBinOpAdd || op_id == IrBinOpSub)) + { + IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, ira->codegen->builtin_types.entry_usize); + if (casted_op2 == ira->codegen->invalid_instruction) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.scope, + bin_op_instruction->base.source_node, op_id, op1, casted_op2, true); + result->value.type = op1->value.type; + ir_link_new_instruction(result, &bin_op_instruction->base); + return result->value.type; + } + IrInstruction *instructions[] = {op1, op2}; TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, nullptr, instructions, 2); if (type_is_invalid(resolved_type)) return resolved_type; - IrBinOp op_id = bin_op_instruction->op_id; bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdNumLitInt; bool is_float = resolved_type->id == TypeTableEntryIdFloat || resolved_type->id == TypeTableEntryIdNumLitFloat; @@ -11331,7 +11356,8 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp * out_array_val = out_val; } else if (is_slice(op1_type) || is_slice(op2_type)) { - TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, child_type, true); + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, + true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, child_type), 0, 0); result_type = get_slice_type(ira->codegen, ptr_type); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; @@ -11351,7 +11377,9 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp * } else { new_len += 1; // null byte - result_type = get_pointer_to_type(ira->codegen, child_type, true); + // TODO make this `[*]null T` instead of `[*]T` + result_type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, + PtrLenUnknown, get_abi_alignment(ira->codegen, child_type), 0, 0); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; @@ -12173,7 +12201,7 @@ no_mem_slot: IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, instruction->scope, instruction->source_node, var); var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, - var->src_is_const, is_volatile, var->align_bytes, 0, 0); + var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0); type_ensure_zero_bits_known(ira->codegen, var->value->type); bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); @@ -12352,7 +12380,9 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal IrInstruction *casted_new_stack = nullptr; if (call_instruction->new_stack != nullptr) { - TypeTableEntry *u8_ptr = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); + TypeTableEntry *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + false, false, PtrLenUnknown, + get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); TypeTableEntry *u8_slice = get_slice_type(ira->codegen, u8_ptr); IrInstruction *new_stack = call_instruction->new_stack->other; if (type_is_invalid(new_stack->value.type)) @@ -13112,10 +13142,21 @@ static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, ui return get_pointer_to_type_extra(g, ptr_type->data.pointer.child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, new_align, ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count); } +static TypeTableEntry *adjust_ptr_len(CodeGen *g, TypeTableEntry *ptr_type, PtrLen ptr_len) { + assert(ptr_type->id == TypeTableEntryIdPointer); + return get_pointer_to_type_extra(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_len, + ptr_type->data.pointer.alignment, + ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count); +} + static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) { IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other; if (type_is_invalid(array_ptr->value.type)) @@ -13146,6 +13187,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc if (ptr_type->data.pointer.unaligned_bit_count == 0) { return_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + elem_ptr_instruction->ptr_len, ptr_type->data.pointer.alignment, 0, 0); } else { uint64_t elem_val_scalar; @@ -13157,12 +13199,19 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc return_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + elem_ptr_instruction->ptr_len, 1, (uint32_t)bit_offset, (uint32_t)bit_width); } } else if (array_type->id == TypeTableEntryIdPointer) { - return_type = array_type; + if (array_type->data.pointer.ptr_len == PtrLenSingle) { + ir_add_error_node(ira, elem_ptr_instruction->base.source_node, + buf_sprintf("indexing not allowed on pointer to single item")); + return ira->codegen->builtin_types.entry_invalid; + } + return_type = adjust_ptr_len(ira->codegen, array_type, elem_ptr_instruction->ptr_len); } else if (is_slice(array_type)) { - return_type = array_type->data.structure.fields[slice_ptr_index].type_entry; + return_type = adjust_ptr_len(ira->codegen, array_type->data.structure.fields[slice_ptr_index].type_entry, + elem_ptr_instruction->ptr_len); } else if (array_type->id == TypeTableEntryIdArgTuple) { ConstExprValue *ptr_val = ir_resolve_const(ira, array_ptr, UndefBad); if (!ptr_val) @@ -13304,8 +13353,10 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc } else if (is_slice(array_type)) { ConstExprValue *ptr_field = &array_ptr_val->data.x_struct.fields[slice_ptr_index]; if (ptr_field->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr, - casted_elem_index, false); + IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, + array_ptr, casted_elem_index, false, elem_ptr_instruction->ptr_len); + result->value.type = return_type; + ir_link_new_instruction(result, &elem_ptr_instruction->base); return return_type; } ConstExprValue *len_field = &array_ptr_val->data.x_struct.fields[slice_len_index]; @@ -13373,8 +13424,10 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc } } - ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr, - casted_elem_index, safety_check_on); + IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, + array_ptr, casted_elem_index, safety_check_on, elem_ptr_instruction->ptr_len); + result->value.type = return_type; + ir_link_new_instruction(result, &elem_ptr_instruction->base); return return_type; } @@ -13449,7 +13502,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ return ira->codegen->invalid_instruction; ConstExprValue *field_val = &struct_val->data.x_struct.fields[field->src_index]; TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, field_val->type, - is_const, is_volatile, align_bytes, + is_const, is_volatile, PtrLenSingle, align_bytes, (uint32_t)(ptr_bit_offset + field->packed_bits_offset), (uint32_t)unaligned_bit_count_for_result_type); IrInstruction *result = ir_get_const(ira, source_instr); @@ -13465,6 +13518,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ IrInstruction *result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, + PtrLenSingle, align_bytes, (uint32_t)(ptr_bit_offset + field->packed_bits_offset), (uint32_t)unaligned_bit_count_for_result_type); @@ -13511,7 +13565,9 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ payload_val->type = field_type; } - TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile, + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, + is_const, is_volatile, + PtrLenSingle, get_abi_alignment(ira->codegen, field_type), 0, 0); IrInstruction *result = ir_get_const(ira, source_instr); @@ -13526,7 +13582,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, - get_abi_alignment(ira->codegen, field->type_entry), 0, 0); + PtrLenSingle, get_abi_alignment(ira->codegen, field->type_entry), 0, 0); return result; } else { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, @@ -14119,7 +14175,7 @@ static TypeTableEntry *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, if (type_entry->id == TypeTableEntryIdArray) { ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.array.child_type, false); } else if (is_slice(type_entry)) { - ptr_type = type_entry->data.structure.fields[0].type_entry; + ptr_type = adjust_ptr_len(ira->codegen, type_entry->data.structure.fields[0].type_entry, PtrLenSingle); } else if (type_entry->id == TypeTableEntryIdArgTuple) { ConstExprValue *arg_tuple_val = ir_resolve_const(ira, value, UndefBad); if (!arg_tuple_val) @@ -14367,7 +14423,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, { type_ensure_zero_bits_known(ira->codegen, child_type); TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, - is_const, is_volatile, align_bytes, 0, 0); + is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0); TypeTableEntry *result_type = get_slice_type(ira->codegen, slice_ptr_type); ConstExprValue *out_val = ir_build_const_from(ira, &slice_type_instruction->base); out_val->data.x_type = result_type; @@ -14619,6 +14675,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, TypeTableEntry *child_type = type_entry->data.maybe.child_type; TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + PtrLenSingle, get_abi_alignment(ira->codegen, child_type), 0, 0); if (instr_is_comptime(value)) { @@ -15566,7 +15623,8 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc if (type_is_invalid(casted_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type); if (casted_value->value.special == ConstValSpecialStatic) { ErrorTableEntry *err = casted_value->value.data.x_err_set; @@ -15607,7 +15665,11 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope, instruction->base.source_node, target); ir_link_new_instruction(result, &instruction->base); - TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra( + ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, + get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), + 0, 0); result->value.type = get_slice_type(ira->codegen, u8_ptr_type); return result->value.type; } @@ -15660,6 +15722,7 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, TypeTableEntry *field_ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry, field_ptr->value.type->data.pointer.is_const, field_ptr->value.type->data.pointer.is_volatile, + PtrLenSingle, field_ptr_align, 0, 0); IrInstruction *casted_field_ptr = ir_implicit_cast(ira, field_ptr, field_ptr_type); if (type_is_invalid(casted_field_ptr->value.type)) @@ -15668,6 +15731,7 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, container_type, casted_field_ptr->value.type->data.pointer.is_const, casted_field_ptr->value.type->data.pointer.is_volatile, + PtrLenSingle, parent_ptr_align, 0, 0); if (instr_is_comptime(casted_field_ptr)) { @@ -15983,11 +16047,13 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop // lib_name: ?[]const u8 ensure_field_index(fn_def_val->type, "lib_name", 6); fn_def_fields[6].special = ConstValSpecialStatic; - fn_def_fields[6].type = get_maybe_type(ira->codegen, - get_slice_type(ira->codegen, get_pointer_to_type(ira->codegen, - ira->codegen->builtin_types.entry_u8, true))); - if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) - { + TypeTableEntry *u8_ptr = get_pointer_to_type_extra( + ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, + get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), + 0, 0); + fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); + if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { fn_def_fields[6].data.x_maybe = create_const_vals(1); ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); init_const_slice(ira->codegen, fn_def_fields[6].data.x_maybe, lib_name, 0, buf_len(fn_node->lib_name), true); @@ -16009,8 +16075,8 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop size_t fn_arg_count = fn_entry->variable_list.length; ConstExprValue *fn_arg_name_array = create_const_vals(1); fn_arg_name_array->special = ConstValSpecialStatic; - fn_arg_name_array->type = get_array_type(ira->codegen, get_slice_type(ira->codegen, - get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true)), fn_arg_count); + fn_arg_name_array->type = get_array_type(ira->codegen, + get_slice_type(ira->codegen, u8_ptr), fn_arg_count); fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; fn_arg_name_array->data.x_array.s_none.parent.id = ConstParentIdNone; fn_arg_name_array->data.x_array.s_none.elements = create_const_vals(fn_arg_count); @@ -17088,7 +17154,8 @@ static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructi TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8; uint32_t dest_align = (dest_uncasted_type->id == TypeTableEntryIdPointer) ? dest_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8); - TypeTableEntry *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, dest_align, 0, 0); + TypeTableEntry *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, + PtrLenUnknown, dest_align, 0, 0); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr); if (type_is_invalid(casted_dest_ptr->value.type)) @@ -17184,8 +17251,10 @@ static TypeTableEntry *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructi src_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8); TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; - TypeTableEntry *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, dest_align, 0, 0); - TypeTableEntry *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile, src_align, 0, 0); + TypeTableEntry *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, + PtrLenUnknown, dest_align, 0, 0); + TypeTableEntry *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile, + PtrLenUnknown, src_align, 0, 0); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut); if (type_is_invalid(casted_dest_ptr->value.type)) @@ -17333,11 +17402,13 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, ptr_type->data.pointer.is_const || is_comptime_const, ptr_type->data.pointer.is_volatile, + PtrLenUnknown, byte_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == TypeTableEntryIdPointer) { TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type, array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, + PtrLenUnknown, array_type->data.pointer.alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); if (!end) { @@ -17774,6 +17845,7 @@ static TypeTableEntry *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInst if (result_ptr->value.type->id == TypeTableEntryIdPointer) { expected_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_type, false, result_ptr->value.type->data.pointer.is_volatile, + PtrLenSingle, result_ptr->value.type->data.pointer.alignment, 0, 0); } else { expected_ptr_type = get_pointer_to_type(ira->codegen, dest_type, false); @@ -17929,6 +18001,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + PtrLenSingle, get_abi_alignment(ira->codegen, payload_type), 0, 0); if (instr_is_comptime(value)) { ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); @@ -18270,7 +18343,8 @@ static TypeTableEntry *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructio return ir_unreach_error(ira); } - TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type); IrInstruction *casted_msg = ir_implicit_cast(ira, msg, str_type); if (type_is_invalid(casted_msg->value.type)) @@ -18801,7 +18875,8 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruc ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_pointer_to_type_extra(ira->codegen, child_type, - instruction->is_const, instruction->is_volatile, align_bytes, + instruction->is_const, instruction->is_volatile, + instruction->ptr_len, align_bytes, instruction->bit_offset_start, instruction->bit_offset_end - instruction->bit_offset_start); return ira->codegen->builtin_types.entry_type; diff --git a/src/parser.cpp b/src/parser.cpp index 6c900c3bfa..3ad2de906b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1225,6 +1225,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, AstNode *child_node = ast_parse_pointer_type(pc, token_index, token); child_node->column += 1; AstNode *parent_node = ast_create_node(pc, NodeTypePointerType, token); + parent_node->data.pointer_type.star_token = token; parent_node->data.pointer_type.op_expr = child_node; return parent_node; } diff --git a/std/buffer.zig b/std/buffer.zig index 305746e183..3b2936d223 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -122,7 +122,7 @@ pub const Buffer = struct { } /// For passing to C functions. - pub fn ptr(self: *const Buffer) *u8 { + pub fn ptr(self: *const Buffer) [*]u8 { return self.list.items.ptr; } }; diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 69395e6b27..e3b53d9bea 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -1,7 +1,7 @@ extern "c" fn __error() *c_int; -pub extern "c" fn _NSGetExecutablePath(buf: *u8, bufsize: *u32) c_int; +pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int; -pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: *u8, buf_len: usize, basep: *i64) usize; +pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize; pub extern "c" fn mach_absolute_time() u64; pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void; diff --git a/std/c/index.zig b/std/c/index.zig index 114b79cdae..ade37f36c1 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -9,6 +9,8 @@ pub use switch (builtin.os) { }; const empty_import = @import("../empty.zig"); +// TODO https://github.com/ziglang/zig/issues/265 on this whole file + pub extern "c" fn abort() noreturn; pub extern "c" fn exit(code: c_int) noreturn; pub extern "c" fn isatty(fd: c_int) c_int; @@ -16,45 +18,45 @@ pub extern "c" fn close(fd: c_int) c_int; pub extern "c" fn fstat(fd: c_int, buf: *Stat) c_int; pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int; pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; -pub extern "c" fn open(path: *const u8, oflag: c_int, ...) c_int; +pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; pub extern "c" fn raise(sig: c_int) c_int; -pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; -pub extern "c" fn stat(noalias path: *const u8, noalias buf: *Stat) c_int; -pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; -pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; -pub extern "c" fn munmap(addr: *c_void, len: usize) c_int; -pub extern "c" fn unlink(path: *const u8) c_int; -pub extern "c" fn getcwd(buf: *u8, size: usize) ?*u8; +pub extern "c" fn read(fd: c_int, buf: [*]c_void, nbyte: usize) isize; +pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; +pub extern "c" fn write(fd: c_int, buf: [*]const c_void, nbyte: usize) isize; +pub extern "c" fn mmap(addr: ?[*]c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?[*]c_void; +pub extern "c" fn munmap(addr: [*]c_void, len: usize) c_int; +pub extern "c" fn unlink(path: [*]const u8) c_int; +pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; pub extern "c" fn fork() c_int; -pub extern "c" fn access(path: *const u8, mode: c_uint) c_int; -pub extern "c" fn pipe(fds: *c_int) c_int; -pub extern "c" fn mkdir(path: *const u8, mode: c_uint) c_int; -pub extern "c" fn symlink(existing: *const u8, new: *const u8) c_int; -pub extern "c" fn rename(old: *const u8, new: *const u8) c_int; -pub extern "c" fn chdir(path: *const u8) c_int; -pub extern "c" fn execve(path: *const u8, argv: *const ?*const u8, envp: *const ?*const u8) c_int; +pub extern "c" fn access(path: [*]const u8, mode: c_uint) c_int; +pub extern "c" fn pipe(fds: *[2]c_int) c_int; +pub extern "c" fn mkdir(path: [*]const u8, mode: c_uint) c_int; +pub extern "c" fn symlink(existing: [*]const u8, new: [*]const u8) c_int; +pub extern "c" fn rename(old: [*]const u8, new: [*]const u8) c_int; +pub extern "c" fn chdir(path: [*]const u8) c_int; +pub extern "c" fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) c_int; pub extern "c" fn dup(fd: c_int) c_int; pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; -pub extern "c" fn readlink(noalias path: *const u8, noalias buf: *u8, bufsize: usize) isize; -pub extern "c" fn realpath(noalias file_name: *const u8, noalias resolved_name: *u8) ?*u8; +pub extern "c" fn readlink(noalias path: [*]const u8, noalias buf: [*]u8, bufsize: usize) isize; +pub extern "c" fn realpath(noalias file_name: [*]const u8, noalias resolved_name: [*]u8) ?[*]u8; pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int; pub extern "c" fn gettimeofday(tv: ?*timeval, tz: ?*timezone) c_int; pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; -pub extern "c" fn rmdir(path: *const u8) c_int; +pub extern "c" fn rmdir(path: [*]const u8) c_int; -pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; -pub extern "c" fn malloc(usize) ?*c_void; -pub extern "c" fn realloc(*c_void, usize) ?*c_void; -pub extern "c" fn free(*c_void) void; -pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; +pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?[*]c_void; +pub extern "c" fn malloc(usize) ?[*]c_void; +pub extern "c" fn realloc([*]c_void, usize) ?[*]c_void; +pub extern "c" fn free([*]c_void) void; +pub extern "c" fn posix_memalign(memptr: *[*]c_void, alignment: usize, size: usize) c_int; pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int; pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; -pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; +pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: [*]c_void, stacksize: usize) c_int; pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; diff --git a/std/c/linux.zig b/std/c/linux.zig index 0ab043533e..2699e9bd09 100644 --- a/std/c/linux.zig +++ b/std/c/linux.zig @@ -1,6 +1,6 @@ pub use @import("../os/linux/errno.zig"); -pub extern "c" fn getrandom(buf_ptr: *u8, buf_len: usize, flags: c_uint) c_int; +pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int; extern "c" fn __errno_location() *c_int; pub const _errno = __errno_location; diff --git a/std/cstr.zig b/std/cstr.zig index d60adf8faa..d9106769c1 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -57,7 +57,7 @@ pub fn addNullByte(allocator: *mem.Allocator, slice: []const u8) ![]u8 { pub const NullTerminated2DArray = struct { allocator: *mem.Allocator, byte_count: usize, - ptr: ?*?*u8, + ptr: ?[*]?[*]u8, /// Takes N lists of strings, concatenates the lists together, and adds a null terminator /// Caller must deinit result @@ -79,12 +79,12 @@ pub const NullTerminated2DArray = struct { errdefer allocator.free(buf); var write_index = index_size; - const index_buf = ([]?*u8)(buf); + const index_buf = ([]?[*]u8)(buf); var i: usize = 0; for (slices) |slice| { for (slice) |inner| { - index_buf[i] = &buf[write_index]; + index_buf[i] = buf.ptr + write_index; i += 1; mem.copy(u8, buf[write_index..], inner); write_index += inner.len; @@ -97,12 +97,12 @@ pub const NullTerminated2DArray = struct { return NullTerminated2DArray{ .allocator = allocator, .byte_count = byte_count, - .ptr = @ptrCast(?*?*u8, buf.ptr), + .ptr = @ptrCast(?[*]?[*]u8, buf.ptr), }; } pub fn deinit(self: *NullTerminated2DArray) void { - const buf = @ptrCast(*u8, self.ptr); + const buf = @ptrCast([*]u8, self.ptr); self.allocator.free(buf[0..self.byte_count]); } }; diff --git a/std/heap.zig b/std/heap.zig index d15a99a757..0b8f4aeb3f 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -18,11 +18,11 @@ var c_allocator_state = Allocator{ fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 { assert(alignment <= @alignOf(c_longdouble)); - return if (c.malloc(n)) |buf| @ptrCast(*u8, buf)[0..n] else error.OutOfMemory; + return if (c.malloc(n)) |buf| @ptrCast([*]u8, buf)[0..n] else error.OutOfMemory; } fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { - const old_ptr = @ptrCast(*c_void, old_mem.ptr); + const old_ptr = @ptrCast([*]c_void, old_mem.ptr); if (c.realloc(old_ptr, new_size)) |buf| { return @ptrCast(*u8, buf)[0..new_size]; } else if (new_size <= old_mem.len) { @@ -33,7 +33,7 @@ fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![ } fn cFree(self: *Allocator, old_mem: []u8) void { - const old_ptr = @ptrCast(*c_void, old_mem.ptr); + const old_ptr = @ptrCast([*]c_void, old_mem.ptr); c.free(old_ptr); } @@ -74,7 +74,7 @@ pub const DirectAllocator = struct { const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0); if (addr == p.MAP_FAILED) return error.OutOfMemory; - if (alloc_size == n) return @intToPtr(*u8, addr)[0..n]; + if (alloc_size == n) return @intToPtr([*]u8, addr)[0..n]; var aligned_addr = addr & ~usize(alignment - 1); aligned_addr += alignment; @@ -93,7 +93,7 @@ pub const DirectAllocator = struct { //It is impossible that there is an unoccupied page at the top of our // mmap. - return @intToPtr(*u8, aligned_addr)[0..n]; + return @intToPtr([*]u8, aligned_addr)[0..n]; }, Os.windows => { const amt = n + alignment + @sizeOf(usize); @@ -109,7 +109,7 @@ pub const DirectAllocator = struct { const adjusted_addr = root_addr + march_forward_bytes; const record_addr = adjusted_addr + n; @intToPtr(*align(1) usize, record_addr).* = root_addr; - return @intToPtr(*u8, adjusted_addr)[0..n]; + return @intToPtr([*]u8, adjusted_addr)[0..n]; }, else => @compileError("Unsupported OS"), } @@ -140,7 +140,7 @@ pub const DirectAllocator = struct { const old_adjusted_addr = @ptrToInt(old_mem.ptr); const old_record_addr = old_adjusted_addr + old_mem.len; const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; - const old_ptr = @intToPtr(os.windows.LPVOID, root_addr); + const old_ptr = @intToPtr([*]c_void, root_addr); const amt = new_size + alignment + @sizeOf(usize); const new_ptr = os.windows.HeapReAlloc(??self.heap_handle, 0, old_ptr, amt) ?? blk: { if (new_size > old_mem.len) return error.OutOfMemory; @@ -154,7 +154,7 @@ pub const DirectAllocator = struct { assert(new_adjusted_addr % alignment == 0); const new_record_addr = new_adjusted_addr + new_size; @intToPtr(*align(1) usize, new_record_addr).* = new_root_addr; - return @intToPtr(*u8, new_adjusted_addr)[0..new_size]; + return @intToPtr([*]u8, new_adjusted_addr)[0..new_size]; }, else => @compileError("Unsupported OS"), } @@ -170,7 +170,7 @@ pub const DirectAllocator = struct { Os.windows => { const record_addr = @ptrToInt(bytes.ptr) + bytes.len; const root_addr = @intToPtr(*align(1) usize, record_addr).*; - const ptr = @intToPtr(os.windows.LPVOID, root_addr); + const ptr = @intToPtr([*]c_void, root_addr); _ = os.windows.HeapFree(??self.heap_handle, 0, ptr); }, else => @compileError("Unsupported OS"), diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 0e80ae09c1..822ade2eb8 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -639,7 +639,7 @@ pub const ChildProcess = struct { } }; -fn windowsCreateProcess(app_name: *u8, cmd_line: *u8, envp_ptr: ?*u8, cwd_ptr: ?*u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void { +fn windowsCreateProcess(app_name: [*]u8, cmd_line: [*]u8, envp_ptr: ?[*]u8, cwd_ptr: ?[*]u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void { if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) { const err = windows.GetLastError(); return switch (err) { diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 77e8b6bb6a..b8e18561cc 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -317,7 +317,8 @@ pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { return errnoWrap(c.lseek(fd, offset, whence)); } -pub fn open(path: *const u8, flags: u32, mode: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 on the whole file +pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); } @@ -325,33 +326,33 @@ pub fn raise(sig: i32) usize { return errnoWrap(c.raise(sig)); } -pub fn read(fd: i32, buf: *u8, nbyte: usize) usize { - return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); +pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { + return errnoWrap(c.read(fd, @ptrCast([*]c_void, buf), nbyte)); } -pub fn stat(noalias path: *const u8, noalias buf: *stat) usize { +pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize { return errnoWrap(c.stat(path, buf)); } -pub fn write(fd: i32, buf: *const u8, nbyte: usize) usize { - return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); +pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { + return errnoWrap(c.write(fd, @ptrCast([*]const c_void, buf), nbyte)); } -pub fn mmap(address: ?*u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap(@ptrCast(*c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); +pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { + const ptr_result = c.mmap(@ptrCast([*]c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); return errnoWrap(isize_result); } pub fn munmap(address: usize, length: usize) usize { - return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); + return errnoWrap(c.munmap(@intToPtr([*]c_void, address), length)); } -pub fn unlink(path: *const u8) usize { +pub fn unlink(path: [*]const u8) usize { return errnoWrap(c.unlink(path)); } -pub fn getcwd(buf: *u8, size: usize) usize { +pub fn getcwd(buf: [*]u8, size: usize) usize { return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; } @@ -364,40 +365,40 @@ pub fn fork() usize { return errnoWrap(c.fork()); } -pub fn access(path: *const u8, mode: u32) usize { +pub fn access(path: [*]const u8, mode: u32) usize { return errnoWrap(c.access(path, mode)); } pub fn pipe(fds: *[2]i32) usize { comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.pipe(@ptrCast(*c_int, fds))); + return errnoWrap(c.pipe(@ptrCast(*[2]c_int, fds))); } -pub fn getdirentries64(fd: i32, buf_ptr: *u8, buf_len: usize, basep: *i64) usize { +pub fn getdirentries64(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep))); } -pub fn mkdir(path: *const u8, mode: u32) usize { +pub fn mkdir(path: [*]const u8, mode: u32) usize { return errnoWrap(c.mkdir(path, mode)); } -pub fn symlink(existing: *const u8, new: *const u8) usize { +pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { return errnoWrap(c.symlink(existing, new)); } -pub fn rename(old: *const u8, new: *const u8) usize { +pub fn rename(old: [*]const u8, new: [*]const u8) usize { return errnoWrap(c.rename(old, new)); } -pub fn rmdir(path: *const u8) usize { +pub fn rmdir(path: [*]const u8) usize { return errnoWrap(c.rmdir(path)); } -pub fn chdir(path: *const u8) usize { +pub fn chdir(path: [*]const u8) usize { return errnoWrap(c.chdir(path)); } -pub fn execve(path: *const u8, argv: *const ?*const u8, envp: *const ?*const u8) usize { +pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { return errnoWrap(c.execve(path, argv, envp)); } @@ -405,7 +406,7 @@ pub fn dup2(old: i32, new: i32) usize { return errnoWrap(c.dup2(old, new)); } -pub fn readlink(noalias path: *const u8, noalias buf_ptr: *u8, buf_len: usize) usize { +pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { return errnoWrap(c.readlink(path, buf_ptr, buf_len)); } @@ -417,7 +418,7 @@ pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { return errnoWrap(c.nanosleep(req, rem)); } -pub fn realpath(noalias filename: *const u8, noalias resolved_name: *u8) usize { +pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; } diff --git a/std/os/file.zig b/std/os/file.zig index d943da30ca..378782507b 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -313,7 +313,7 @@ pub const File = struct { if (is_posix) { var index: usize = 0; while (index < buffer.len) { - const amt_read = posix.read(self.handle, &buffer[index], buffer.len - index); + const amt_read = posix.read(self.handle, buffer.ptr + index, buffer.len - index); const read_err = posix.getErrno(amt_read); if (read_err > 0) { switch (read_err) { @@ -334,7 +334,7 @@ pub const File = struct { while (index < buffer.len) { const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); var amt_read: windows.DWORD = undefined; - if (windows.ReadFile(self.handle, @ptrCast(*c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) { + if (windows.ReadFile(self.handle, @ptrCast([*]c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.OPERATION_ABORTED => continue, diff --git a/std/os/index.zig b/std/os/index.zig index ff638c670b..7e908af9eb 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -134,20 +134,7 @@ pub fn getRandomBytes(buf: []u8) !void { } }, Os.zen => { - const randomness = []u8{ - 42, - 1, - 7, - 12, - 22, - 17, - 99, - 16, - 26, - 87, - 41, - 45, - }; + const randomness = []u8{ 42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45 }; var i: usize = 0; while (i < buf.len) : (i += 1) { if (i > randomness.len) return error.Unknown; @@ -238,7 +225,7 @@ pub fn posixRead(fd: i32, buf: []u8) !void { var index: usize = 0; while (index < buf.len) { const want_to_read = math.min(buf.len - index, usize(max_buf_len)); - const rc = posix.read(fd, &buf[index], want_to_read); + const rc = posix.read(fd, buf.ptr + index, want_to_read); const err = posix.getErrno(rc); if (err > 0) { return switch (err) { @@ -278,7 +265,7 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { var index: usize = 0; while (index < bytes.len) { const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); - const rc = posix.write(fd, &bytes[index], amt_to_write); + const rc = posix.write(fd, bytes.ptr + index, amt_to_write); const write_err = posix.getErrno(rc); if (write_err > 0) { return switch (write_err) { @@ -328,7 +315,8 @@ pub fn posixOpen(allocator: *Allocator, file_path: []const u8, flags: u32, perm: return posixOpenC(path_with_null.ptr, flags, perm); } -pub fn posixOpenC(file_path: *const u8, flags: u32, perm: usize) !i32 { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { while (true) { const result = posix.open(file_path, flags, perm); const err = posix.getErrno(result); @@ -374,19 +362,19 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void { } } -pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?*u8 { +pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?[*]u8 { const envp_count = env_map.count(); - const envp_buf = try allocator.alloc(?*u8, envp_count + 1); - mem.set(?*u8, envp_buf, null); + const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1); + mem.set(?[*]u8, envp_buf, null); errdefer freeNullDelimitedEnvMap(allocator, envp_buf); { var it = env_map.iterator(); var i: usize = 0; while (it.next()) |pair| : (i += 1) { const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); - @memcpy(&env_buf[0], pair.key.ptr, pair.key.len); + @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); env_buf[pair.key.len] = '='; - @memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len); + @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); env_buf[env_buf.len - 1] = 0; envp_buf[i] = env_buf.ptr; @@ -397,7 +385,7 @@ pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) return envp_buf; } -pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?*u8) void { +pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?[*]u8) void { for (envp_buf) |env| { const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; allocator.free(env_buf); @@ -411,8 +399,8 @@ pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?*u8) void { /// `argv[0]` is the executable path. /// This function also uses the PATH environment variable to get the full path to the executable. pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: *Allocator) !void { - const argv_buf = try allocator.alloc(?*u8, argv.len + 1); - mem.set(?*u8, argv_buf, null); + const argv_buf = try allocator.alloc(?[*]u8, argv.len + 1); + mem.set(?[*]u8, argv_buf, null); defer { for (argv_buf) |arg| { const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break; @@ -422,7 +410,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: } for (argv) |arg, i| { const arg_buf = try allocator.alloc(u8, arg.len + 1); - @memcpy(&arg_buf[0], arg.ptr, arg.len); + @memcpy(arg_buf.ptr, arg.ptr, arg.len); arg_buf[arg.len] = 0; argv_buf[i] = arg_buf.ptr; @@ -494,7 +482,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { } pub var linux_aux_raw = []usize{0} ** 38; -pub var posix_environ_raw: []*u8 = undefined; +pub var posix_environ_raw: [][*]u8 = undefined; /// Caller must free result when done. pub fn getEnvMap(allocator: *Allocator) !BufMap { @@ -1311,7 +1299,7 @@ pub const Dir = struct { const next_index = self.index + linux_entry.d_reclen; self.index = next_index; - const name = cstr.toSlice(&linux_entry.d_name); + const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name)); // skip . and .. entries if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { @@ -1485,12 +1473,12 @@ pub const ArgIteratorPosix = struct { /// This is marked as public but actually it's only meant to be used /// internally by zig's startup code. - pub var raw: []*u8 = undefined; + pub var raw: [][*]u8 = undefined; }; pub const ArgIteratorWindows = struct { index: usize, - cmd_line: *const u8, + cmd_line: [*]const u8, in_quote: bool, quote_count: usize, seen_quote_count: usize, @@ -1501,7 +1489,7 @@ pub const ArgIteratorWindows = struct { return initWithCmdLine(windows.GetCommandLineA()); } - pub fn initWithCmdLine(cmd_line: *const u8) ArgIteratorWindows { + pub fn initWithCmdLine(cmd_line: [*]const u8) ArgIteratorWindows { return ArgIteratorWindows{ .index = 0, .cmd_line = cmd_line, @@ -1616,7 +1604,7 @@ pub const ArgIteratorWindows = struct { } } - fn countQuotes(cmd_line: *const u8) usize { + fn countQuotes(cmd_line: [*]const u8) usize { var result: usize = 0; var backslash_count: usize = 0; var index: usize = 0; @@ -1722,39 +1710,12 @@ pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { } test "windows arg parsing" { - testWindowsCmdLine(c"a b\tc d", [][]const u8{ - "a", - "b", - "c", - "d", - }); - testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ - "abc", - "d", - "e", - }); - testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ - "a\\\\\\b", - "de fg", - "h", - }); - testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ - "a\\\"b", - "c", - "d", - }); - testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ - "a\\\\b c", - "d", - "e", - }); - testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{ - "a", - "b", - "c", - "\"d", - "f", - }); + testWindowsCmdLine(c"a b\tc d", [][]const u8{ "a", "b", "c", "d" }); + testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ "abc", "d", "e" }); + testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ "a\\\\\\b", "de fg", "h" }); + testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ "a\\\"b", "c", "d" }); + testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ "a\\\\b c", "d", "e" }); + testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{ "a", "b", "c", "\"d", "f" }); testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8{ ".\\..\\zig-cache\\build", @@ -1765,7 +1726,7 @@ test "windows arg parsing" { }); } -fn testWindowsCmdLine(input_cmd_line: *const u8, expected_args: []const []const u8) void { +fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void { var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); for (expected_args) |expected_arg| { const arg = ??it.next(debug.global_allocator) catch unreachable; @@ -2350,7 +2311,7 @@ pub fn posixConnectAsync(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConn pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { var err_code: i32 = undefined; var size: u32 = @sizeOf(i32); - const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast(*u8, &err_code), &size); + const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast([*]u8, &err_code), &size); assert(size == 4); const err = posix.getErrno(rc); switch (err) { @@ -2401,7 +2362,7 @@ pub const Thread = struct { }, builtin.Os.windows => struct { handle: windows.HANDLE, - alloc_start: *c_void, + alloc_start: [*]c_void, heap_handle: windows.HANDLE, }, else => @compileError("Unsupported OS"), @@ -2500,7 +2461,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory; errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); - const bytes = @ptrCast(*u8, bytes_ptr)[0..byte_count]; + const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; outer_context.inner = context; outer_context.thread.data.heap_handle = heap_handle; @@ -2572,7 +2533,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread // align to page stack_end -= stack_end % os.page_size; - assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0); + assert(c.pthread_attr_setstack(&attr, @intToPtr([*]c_void, stack_addr), stack_end - stack_addr) == 0); const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); switch (err) { diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 3e7b836ac7..0e77371ec2 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -665,15 +665,18 @@ pub fn dup2(old: i32, new: i32) usize { return syscall2(SYS_dup2, usize(old), usize(new)); } -pub fn chdir(path: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn chdir(path: [*]const u8) usize { return syscall1(SYS_chdir, @ptrToInt(path)); } -pub fn chroot(path: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn chroot(path: [*]const u8) usize { return syscall1(SYS_chroot, @ptrToInt(path)); } -pub fn execve(path: *const u8, argv: *const ?*const u8, envp: *const ?*const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); } @@ -685,11 +688,11 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout)); } -pub fn getcwd(buf: *u8, size: usize) usize { +pub fn getcwd(buf: [*]u8, size: usize) usize { return syscall2(SYS_getcwd, @ptrToInt(buf), size); } -pub fn getdents(fd: i32, dirp: *u8, count: usize) usize { +pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { return syscall3(SYS_getdents, usize(fd), @ptrToInt(dirp), count); } @@ -698,27 +701,32 @@ pub fn isatty(fd: i32) bool { return syscall3(SYS_ioctl, usize(fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; } -pub fn readlink(noalias path: *const u8, noalias buf_ptr: *u8, buf_len: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { return syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); } -pub fn mkdir(path: *const u8, mode: u32) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn mkdir(path: [*]const u8, mode: u32) usize { return syscall2(SYS_mkdir, @ptrToInt(path), mode); } -pub fn mount(special: *const u8, dir: *const u8, fstype: *const u8, flags: usize, data: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn mount(special: [*]const u8, dir: [*]const u8, fstype: [*]const u8, flags: usize, data: usize) usize { return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data); } -pub fn umount(special: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn umount(special: [*]const u8) usize { return syscall2(SYS_umount2, @ptrToInt(special), 0); } -pub fn umount2(special: *const u8, flags: u32) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn umount2(special: [*]const u8, flags: u32) usize { return syscall2(SYS_umount2, @ptrToInt(special), flags); } -pub fn mmap(address: ?*u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { +pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd), @bitCast(usize, offset)); } @@ -726,23 +734,26 @@ pub fn munmap(address: usize, length: usize) usize { return syscall2(SYS_munmap, address, length); } -pub fn read(fd: i32, buf: *u8, count: usize) usize { +pub fn read(fd: i32, buf: [*]u8, count: usize) usize { return syscall3(SYS_read, usize(fd), @ptrToInt(buf), count); } -pub fn rmdir(path: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn rmdir(path: [*]const u8) usize { return syscall1(SYS_rmdir, @ptrToInt(path)); } -pub fn symlink(existing: *const u8, new: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { return syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new)); } -pub fn pread(fd: i32, buf: *u8, count: usize, offset: usize) usize { +pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset); } -pub fn access(path: *const u8, mode: u32) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn access(path: [*]const u8, mode: u32) usize { return syscall2(SYS_access, @ptrToInt(path), mode); } @@ -754,27 +765,31 @@ pub fn pipe2(fd: *[2]i32, flags: usize) usize { return syscall2(SYS_pipe2, @ptrToInt(fd), flags); } -pub fn write(fd: i32, buf: *const u8, count: usize) usize { +pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { return syscall3(SYS_write, usize(fd), @ptrToInt(buf), count); } -pub fn pwrite(fd: i32, buf: *const u8, count: usize, offset: usize) usize { +pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { return syscall4(SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset); } -pub fn rename(old: *const u8, new: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn rename(old: [*]const u8, new: [*]const u8) usize { return syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new)); } -pub fn open(path: *const u8, flags: u32, perm: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { return syscall3(SYS_open, @ptrToInt(path), flags, perm); } -pub fn create(path: *const u8, perm: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn create(path: [*]const u8, perm: usize) usize { return syscall2(SYS_creat, @ptrToInt(path), perm); } -pub fn openat(dirfd: i32, path: *const u8, flags: usize, mode: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode); } @@ -801,7 +816,7 @@ pub fn exit(status: i32) noreturn { unreachable; } -pub fn getrandom(buf: *u8, count: usize, flags: u32) usize { +pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { return syscall3(SYS_getrandom, @ptrToInt(buf), count, usize(flags)); } @@ -809,7 +824,8 @@ pub fn kill(pid: i32, sig: i32) usize { return syscall2(SYS_kill, @bitCast(usize, isize(pid)), usize(sig)); } -pub fn unlink(path: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn unlink(path: [*]const u8) usize { return syscall1(SYS_unlink, @ptrToInt(path)); } @@ -942,8 +958,8 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti .restorer = @ptrCast(extern fn () void, restore_rt), }; var ksa_old: k_sigaction = undefined; - @memcpy(@ptrCast(*u8, *ksa.mask), @ptrCast(*const u8, *act.mask), 8); - const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(*ksa), @ptrToInt(*ksa_old), @sizeOf(@typeOf(ksa.mask))); + @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), 8); + const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask))); const err = getErrno(result); if (err != 0) { return result; @@ -951,7 +967,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti if (oact) |old| { old.handler = ksa_old.handler; old.flags = @truncate(u32, ksa_old.flags); - @memcpy(@ptrCast(*u8, *old.mask), @ptrCast(*const u8, *ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); + @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); } return 0; } @@ -1036,7 +1052,7 @@ pub const sockaddr_in6 = extern struct { }; pub const iovec = extern struct { - iov_base: *u8, + iov_base: [*]u8, iov_len: usize, }; @@ -1052,11 +1068,11 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { return syscall3(SYS_socket, domain, socket_type, protocol); } -pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: *const u8, optlen: socklen_t) usize { +pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen)); } -pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: *u8, noalias optlen: *socklen_t) usize { +pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); } @@ -1072,7 +1088,7 @@ pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { return syscall3(SYS_recvmsg, usize(fd), @ptrToInt(msg), flags); } -pub fn recvfrom(fd: i32, noalias buf: *u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { +pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { return syscall6(SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); } @@ -1088,7 +1104,7 @@ pub fn listen(fd: i32, backlog: u32) usize { return syscall2(SYS_listen, usize(fd), backlog); } -pub fn sendto(fd: i32, buf: *const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { +pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { return syscall6(SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen)); } @@ -1108,59 +1124,72 @@ pub fn fstat(fd: i32, stat_buf: *Stat) usize { return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf)); } -pub fn stat(pathname: *const u8, statbuf: *Stat) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize { return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf)); } -pub fn lstat(pathname: *const u8, statbuf: *Stat) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize { return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf)); } -pub fn listxattr(path: *const u8, list: *u8, size: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn listxattr(path: [*]const u8, list: [*]u8, size: usize) usize { return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); } -pub fn llistxattr(path: *const u8, list: *u8, size: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn llistxattr(path: [*]const u8, list: [*]u8, size: usize) usize { return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); } -pub fn flistxattr(fd: usize, list: *u8, size: usize) usize { +pub fn flistxattr(fd: usize, list: [*]u8, size: usize) usize { return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); } -pub fn getxattr(path: *const u8, name: *const u8, value: *void, size: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn getxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); } -pub fn lgetxattr(path: *const u8, name: *const u8, value: *void, size: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lgetxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); } -pub fn fgetxattr(fd: usize, name: *const u8, value: *void, size: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fgetxattr(fd: usize, name: [*]const u8, value: [*]u8, size: usize) usize { return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); } -pub fn setxattr(path: *const u8, name: *const u8, value: *const void, size: usize, flags: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn setxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn lsetxattr(path: *const u8, name: *const u8, value: *const void, size: usize, flags: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lsetxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn fsetxattr(fd: usize, name: *const u8, value: *const void, size: usize, flags: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fsetxattr(fd: usize, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn removexattr(path: *const u8, name: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn removexattr(path: [*]const u8, name: [*]const u8) usize { return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); } -pub fn lremovexattr(path: *const u8, name: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lremovexattr(path: [*]const u8, name: [*]const u8) usize { return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); } -pub fn fremovexattr(fd: usize, name: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fremovexattr(fd: usize, name: [*]const u8) usize { return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); } @@ -1188,7 +1217,7 @@ pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev)); } -pub fn epoll_wait(epoll_fd: i32, events: *epoll_event, maxevents: u32, timeout: i32) usize { +pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { return syscall4(SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout)); } diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig index 06aae1968f..948a3ac96b 100644 --- a/std/os/linux/test.zig +++ b/std/os/linux/test.zig @@ -35,5 +35,6 @@ test "timer" { const events_one: linux.epoll_event = undefined; var events = []linux.epoll_event{events_one} ** 8; - err = linux.epoll_wait(i32(epoll_fd), &events[0], 8, -1); + // TODO implicit cast from *[N]T to [*]T + err = linux.epoll_wait(i32(epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1); } diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig index 1317da6388..2ab4d0cbc1 100644 --- a/std/os/linux/vdso.zig +++ b/std/os/linux/vdso.zig @@ -12,7 +12,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { var ph_addr: usize = vdso_addr + eh.e_phoff; const ph = @intToPtr(*elf.Phdr, ph_addr); - var maybe_dynv: ?*usize = null; + var maybe_dynv: ?[*]usize = null; var base: usize = @maxValue(usize); { var i: usize = 0; @@ -23,7 +23,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { const this_ph = @intToPtr(*elf.Phdr, ph_addr); switch (this_ph.p_type) { elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr, - elf.PT_DYNAMIC => maybe_dynv = @intToPtr(*usize, vdso_addr + this_ph.p_offset), + elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, vdso_addr + this_ph.p_offset), else => {}, } } @@ -31,10 +31,10 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { const dynv = maybe_dynv ?? return 0; if (base == @maxValue(usize)) return 0; - var maybe_strings: ?*u8 = null; - var maybe_syms: ?*elf.Sym = null; - var maybe_hashtab: ?*linux.Elf_Symndx = null; - var maybe_versym: ?*u16 = null; + var maybe_strings: ?[*]u8 = null; + var maybe_syms: ?[*]elf.Sym = null; + var maybe_hashtab: ?[*]linux.Elf_Symndx = null; + var maybe_versym: ?[*]u16 = null; var maybe_verdef: ?*elf.Verdef = null; { @@ -42,10 +42,10 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { while (dynv[i] != 0) : (i += 2) { const p = base + dynv[i + 1]; switch (dynv[i]) { - elf.DT_STRTAB => maybe_strings = @intToPtr(*u8, p), - elf.DT_SYMTAB => maybe_syms = @intToPtr(*elf.Sym, p), - elf.DT_HASH => maybe_hashtab = @intToPtr(*linux.Elf_Symndx, p), - elf.DT_VERSYM => maybe_versym = @intToPtr(*u16, p), + elf.DT_STRTAB => maybe_strings = @intToPtr([*]u8, p), + elf.DT_SYMTAB => maybe_syms = @intToPtr([*]elf.Sym, p), + elf.DT_HASH => maybe_hashtab = @intToPtr([*]linux.Elf_Symndx, p), + elf.DT_VERSYM => maybe_versym = @intToPtr([*]u16, p), elf.DT_VERDEF => maybe_verdef = @intToPtr(*elf.Verdef, p), else => {}, } @@ -65,7 +65,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { if (0 == (u32(1) << u5(syms[i].st_info & 0xf) & OK_TYPES)) continue; if (0 == (u32(1) << u5(syms[i].st_info >> 4) & OK_BINDS)) continue; if (0 == syms[i].st_shndx) continue; - if (!mem.eql(u8, name, cstr.toSliceConst(&strings[syms[i].st_name]))) continue; + if (!mem.eql(u8, name, cstr.toSliceConst(strings + syms[i].st_name))) continue; if (maybe_versym) |versym| { if (!checkver(??maybe_verdef, versym[i], vername, strings)) continue; @@ -76,7 +76,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { return 0; } -fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: *u8) bool { +fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*]u8) bool { var def = def_arg; const vsym = @bitCast(u32, vsym_arg) & 0x7fff; while (true) { @@ -87,5 +87,5 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: * def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next); } const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux); - return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name])); + return mem.eql(u8, vername, cstr.toSliceConst(strings + aux.vda_name)); } diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 85f69836d5..c491ae6538 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -10,7 +10,7 @@ pub extern "advapi32" stdcallcc fn CryptAcquireContextA( pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL; -pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *BYTE) BOOL; +pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL; pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; @@ -61,7 +61,7 @@ pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; -pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: LPCH) BOOL; +pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; @@ -69,7 +69,7 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD; -pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?LPCH; +pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8; pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD; @@ -101,17 +101,17 @@ pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?*FILETIME) void; pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; -pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; -pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void, dwBytes: SIZE_T) ?[*]c_void; +pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) SIZE_T; +pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) BOOL; pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; -pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; +pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?[*]c_void; -pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void) BOOL; pub extern "kernel32" stdcallcc fn MoveFileExA( lpExistingFileName: LPCSTR, @@ -127,7 +127,7 @@ pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL; pub extern "kernel32" stdcallcc fn ReadFile( in_hFile: HANDLE, - out_lpBuffer: *c_void, + out_lpBuffer: [*]c_void, in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: *DWORD, in_out_lpOverlapped: ?*OVERLAPPED, @@ -150,7 +150,7 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis pub extern "kernel32" stdcallcc fn WriteFile( in_hFile: HANDLE, - in_lpBuffer: *const c_void, + in_lpBuffer: [*]const c_void, in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?*DWORD, in_out_lpOverlapped: ?*OVERLAPPED, @@ -178,16 +178,16 @@ pub const HMODULE = *@OpaqueType(); pub const INT = c_int; pub const LPBYTE = *BYTE; pub const LPCH = *CHAR; -pub const LPCSTR = *const CHAR; -pub const LPCTSTR = *const TCHAR; +pub const LPCSTR = [*]const CHAR; +pub const LPCTSTR = [*]const TCHAR; pub const LPCVOID = *const c_void; pub const LPDWORD = *DWORD; -pub const LPSTR = *CHAR; +pub const LPSTR = [*]CHAR; pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; pub const LPVOID = *c_void; -pub const LPWSTR = *WCHAR; +pub const LPWSTR = [*]WCHAR; pub const PVOID = *c_void; -pub const PWSTR = *WCHAR; +pub const PWSTR = [*]WCHAR; pub const SIZE_T = usize; pub const TCHAR = if (UNICODE) WCHAR else u8; pub const UINT = c_uint; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 7170346108..5a40567310 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -42,7 +42,7 @@ pub const WriteError = error{ }; pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { - if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { + if (windows.WriteFile(handle, @ptrCast([*]const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, diff --git a/std/segmented_list.zig b/std/segmented_list.zig index be9a2071a0..a2f3607ad8 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -87,7 +87,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type const ShelfIndex = std.math.Log2Int(usize); prealloc_segment: [prealloc_item_count]T, - dynamic_segments: []*T, + dynamic_segments: [][*]T, allocator: *Allocator, len: usize, @@ -99,7 +99,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type .allocator = allocator, .len = 0, .prealloc_segment = undefined, - .dynamic_segments = []*T{}, + .dynamic_segments = [][*]T{}, }; } @@ -160,11 +160,11 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type const new_cap_shelf_count = shelfCount(new_capacity); const old_shelf_count = ShelfIndex(self.dynamic_segments.len); if (new_cap_shelf_count > old_shelf_count) { - self.dynamic_segments = try self.allocator.realloc(*T, self.dynamic_segments, new_cap_shelf_count); + self.dynamic_segments = try self.allocator.realloc([*]T, self.dynamic_segments, new_cap_shelf_count); var i = old_shelf_count; errdefer { self.freeShelves(i, old_shelf_count); - self.dynamic_segments = self.allocator.shrink(*T, self.dynamic_segments, old_shelf_count); + self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, old_shelf_count); } while (i < new_cap_shelf_count) : (i += 1) { self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; @@ -178,7 +178,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type const len = ShelfIndex(self.dynamic_segments.len); self.freeShelves(len, 0); self.allocator.free(self.dynamic_segments); - self.dynamic_segments = []*T{}; + self.dynamic_segments = [][*]T{}; return; } @@ -190,7 +190,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } self.freeShelves(old_shelf_count, new_cap_shelf_count); - self.dynamic_segments = self.allocator.shrink(*T, self.dynamic_segments, new_cap_shelf_count); + self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, new_cap_shelf_count); } pub fn uncheckedAt(self: *Self, index: usize) *T { diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 5ed7874ca5..64eae79ce4 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -5,7 +5,7 @@ const root = @import("@root"); const std = @import("std"); const builtin = @import("builtin"); -var argc_ptr: *usize = undefined; +var argc_ptr: [*]usize = undefined; comptime { const strong_linkage = builtin.GlobalLinkage.Strong; @@ -28,12 +28,12 @@ nakedcc fn _start() noreturn { switch (builtin.arch) { builtin.Arch.x86_64 => { argc_ptr = asm ("lea (%%rsp), %[argc]" - : [argc] "=r" (-> *usize) + : [argc] "=r" (-> [*]usize) ); }, builtin.Arch.i386 => { argc_ptr = asm ("lea (%%esp), %[argc]" - : [argc] "=r" (-> *usize) + : [argc] "=r" (-> [*]usize) ); }, else => @compileError("unsupported arch"), @@ -49,15 +49,17 @@ extern fn WinMainCRTStartup() noreturn { std.os.windows.ExitProcess(callMain()); } +// TODO https://github.com/ziglang/zig/issues/265 fn posixCallMainAndExit() noreturn { const argc = argc_ptr.*; - const argv = @ptrCast(**u8, &argc_ptr[1]); - const envp_nullable = @ptrCast(*?*u8, &argv[argc + 1]); + const argv = @ptrCast([*][*]u8, argc_ptr + 1); + + const envp_nullable = @ptrCast([*]?[*]u8, argv + argc + 1); var envp_count: usize = 0; while (envp_nullable[envp_count]) |_| : (envp_count += 1) {} - const envp = @ptrCast(**u8, envp_nullable)[0..envp_count]; + const envp = @ptrCast([*][*]u8, envp_nullable)[0..envp_count]; if (builtin.os == builtin.Os.linux) { - const auxv = &@ptrCast(*usize, envp.ptr)[envp_count + 1]; + const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1); var i: usize = 0; while (auxv[i] != 0) : (i += 2) { if (auxv[i] < std.os.linux_aux_raw.len) std.os.linux_aux_raw[auxv[i]] = auxv[i + 1]; @@ -68,16 +70,16 @@ fn posixCallMainAndExit() noreturn { std.os.posix.exit(callMainWithArgs(argc, argv, envp)); } -fn callMainWithArgs(argc: usize, argv: **u8, envp: []*u8) u8 { +fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { std.os.ArgIteratorPosix.raw = argv[0..argc]; std.os.posix_environ_raw = envp; return callMain(); } -extern fn main(c_argc: i32, c_argv: **u8, c_envp: *?*u8) i32 { +extern fn main(c_argc: i32, c_argv: [*][*]u8, c_envp: [*]?[*]u8) i32 { var env_count: usize = 0; while (c_envp[env_count] != null) : (env_count += 1) {} - const envp = @ptrCast(**u8, c_envp)[0..env_count]; + const envp = @ptrCast([*][*]u8, c_envp)[0..env_count]; return callMainWithArgs(usize(c_argc), c_argv, envp); } diff --git a/std/special/builtin.zig b/std/special/builtin.zig index 9c9cd35103..e537078924 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -14,7 +14,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn } } -export fn memset(dest: ?*u8, c: u8, n: usize) ?*u8 { +export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 { @setRuntimeSafety(false); var index: usize = 0; @@ -24,7 +24,7 @@ export fn memset(dest: ?*u8, c: u8, n: usize) ?*u8 { return dest; } -export fn memcpy(noalias dest: ?*u8, noalias src: ?*const u8, n: usize) ?*u8 { +export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*]u8 { @setRuntimeSafety(false); var index: usize = 0; @@ -34,7 +34,7 @@ export fn memcpy(noalias dest: ?*u8, noalias src: ?*const u8, n: usize) ?*u8 { return dest; } -export fn memmove(dest: ?*u8, src: ?*const u8, n: usize) ?*u8 { +export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 { @setRuntimeSafety(false); if (@ptrToInt(dest) < @ptrToInt(src)) { diff --git a/test/cases/align.zig b/test/cases/align.zig index 99bdcdf940..b80727258e 100644 --- a/test/cases/align.zig +++ b/test/cases/align.zig @@ -167,54 +167,41 @@ test "@ptrCast preserves alignment of bigger source" { assert(@typeOf(ptr) == *align(16) u8); } -test "compile-time known array index has best alignment possible" { +test "runtime known array index has best alignment possible" { // take full advantage of over-alignment - var array align(4) = []u8{ - 1, - 2, - 3, - 4, - }; + var array align(4) = []u8{ 1, 2, 3, 4 }; assert(@typeOf(&array[0]) == *align(4) u8); assert(@typeOf(&array[1]) == *u8); assert(@typeOf(&array[2]) == *align(2) u8); assert(@typeOf(&array[3]) == *u8); // because align is too small but we still figure out to use 2 - var bigger align(2) = []u64{ - 1, - 2, - 3, - 4, - }; + var bigger align(2) = []u64{ 1, 2, 3, 4 }; assert(@typeOf(&bigger[0]) == *align(2) u64); assert(@typeOf(&bigger[1]) == *align(2) u64); assert(@typeOf(&bigger[2]) == *align(2) u64); assert(@typeOf(&bigger[3]) == *align(2) u64); // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 - var smaller align(2) = []u32{ - 1, - 2, - 3, - 4, - }; - testIndex(&smaller[0], 0, *align(2) u32); - testIndex(&smaller[0], 1, *align(2) u32); - testIndex(&smaller[0], 2, *align(2) u32); - testIndex(&smaller[0], 3, *align(2) u32); + var smaller align(2) = []u32{ 1, 2, 3, 4 }; + comptime assert(@typeOf(smaller[0..]) == []align(2) u32); + comptime assert(@typeOf(smaller[0..].ptr) == [*]align(2) u32); + testIndex(smaller[0..].ptr, 0, *align(2) u32); + testIndex(smaller[0..].ptr, 1, *align(2) u32); + testIndex(smaller[0..].ptr, 2, *align(2) u32); + testIndex(smaller[0..].ptr, 3, *align(2) u32); // has to use ABI alignment because index known at runtime only - testIndex2(&array[0], 0, *u8); - testIndex2(&array[0], 1, *u8); - testIndex2(&array[0], 2, *u8); - testIndex2(&array[0], 3, *u8); + testIndex2(array[0..].ptr, 0, *u8); + testIndex2(array[0..].ptr, 1, *u8); + testIndex2(array[0..].ptr, 2, *u8); + testIndex2(array[0..].ptr, 3, *u8); } -fn testIndex(smaller: *align(2) u32, index: usize, comptime T: type) void { - assert(@typeOf(&smaller[index]) == T); +fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void { + comptime assert(@typeOf(&smaller[index]) == T); } -fn testIndex2(ptr: *align(4) u8, index: usize, comptime T: type) void { - assert(@typeOf(&ptr[index]) == T); +fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) void { + comptime assert(@typeOf(&ptr[index]) == T); } test "alignstack" { diff --git a/test/cases/const_slice_child.zig b/test/cases/const_slice_child.zig index e012c729a0..07d02d5df0 100644 --- a/test/cases/const_slice_child.zig +++ b/test/cases/const_slice_child.zig @@ -1,15 +1,16 @@ const debug = @import("std").debug; const assert = debug.assert; -var argv: *const *const u8 = undefined; +var argv: [*]const [*]const u8 = undefined; test "const slice child" { - const strs = ([]*const u8){ + const strs = ([][*]const u8){ c"one", c"two", c"three", }; - argv = &strs[0]; + // TODO this should implicitly cast + argv = @ptrCast([*]const [*]const u8, &strs); bar(strs.len); } @@ -29,7 +30,7 @@ fn bar(argc: usize) void { foo(args); } -fn strlen(ptr: *const u8) usize { +fn strlen(ptr: [*]const u8) usize { var count: usize = 0; while (ptr[count] != 0) : (count += 1) {} return count; diff --git a/test/cases/for.zig b/test/cases/for.zig index c624035708..bdbab312f6 100644 --- a/test/cases/for.zig +++ b/test/cases/for.zig @@ -35,34 +35,12 @@ fn mangleString(s: []u8) void { } test "basic for loop" { - const expected_result = []u8{ - 9, - 8, - 7, - 6, - 0, - 1, - 2, - 3, - 9, - 8, - 7, - 6, - 0, - 1, - 2, - 3, - }; + const expected_result = []u8{ 9, 8, 7, 6, 0, 1, 2, 3, 9, 8, 7, 6, 0, 1, 2, 3 }; var buffer: [expected_result.len]u8 = undefined; var buf_index: usize = 0; - const array = []u8{ - 9, - 8, - 7, - 6, - }; + const array = []u8{ 9, 8, 7, 6 }; for (array) |item| { buffer[buf_index] = item; buf_index += 1; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 919b978f9f..5899f20f9c 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -171,8 +171,8 @@ test "memcpy and memset intrinsics" { var foo: [20]u8 = undefined; var bar: [20]u8 = undefined; - @memset(&foo[0], 'A', foo.len); - @memcpy(&bar[0], &foo[0], bar.len); + @memset(foo[0..].ptr, 'A', foo.len); + @memcpy(bar[0..].ptr, foo[0..].ptr, bar.len); if (bar[11] != 'A') unreachable; } @@ -194,7 +194,7 @@ test "slicing" { if (slice.len != 5) unreachable; const ptr = &slice[0]; - if (ptr[0] != 1234) unreachable; + if (ptr.* != 1234) unreachable; var slice_rest = array[10..]; if (slice_rest.len != 10) unreachable; @@ -464,8 +464,9 @@ test "array 2D const double ptr" { } fn testArray2DConstDoublePtr(ptr: *const f32) void { - assert(ptr[0] == 1.0); - assert(ptr[1] == 2.0); + const ptr2 = @ptrCast([*]const f32, ptr); + assert(ptr2[0] == 1.0); + assert(ptr2[1] == 2.0); } const Tid = builtin.TypeId; diff --git a/test/cases/pointers.zig b/test/cases/pointers.zig index 87b3d25a74..47afb60a2e 100644 --- a/test/cases/pointers.zig +++ b/test/cases/pointers.zig @@ -12,3 +12,33 @@ fn testDerefPtr() void { y.* += 1; assert(x == 1235); } + +test "pointer arithmetic" { + var ptr = c"abcd"; + + assert(ptr[0] == 'a'); + ptr += 1; + assert(ptr[0] == 'b'); + ptr += 1; + assert(ptr[0] == 'c'); + ptr += 1; + assert(ptr[0] == 'd'); + ptr += 1; + assert(ptr[0] == 0); + ptr -= 1; + assert(ptr[0] == 'd'); + ptr -= 1; + assert(ptr[0] == 'c'); + ptr -= 1; + assert(ptr[0] == 'b'); + ptr -= 1; + assert(ptr[0] == 'a'); +} + +test "double pointer parsing" { + comptime assert(PtrOf(PtrOf(i32)) == **i32); +} + +fn PtrOf(comptime T: type) type { + return *T; +} diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 0712e508de..6f7d44e09b 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -43,7 +43,7 @@ const VoidStructFieldsFoo = struct { test "structs" { var foo: StructFoo = undefined; - @memset(@ptrCast(*u8, &foo), 0, @sizeOf(StructFoo)); + @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo)); foo.a += 1; foo.b = foo.a == 1; testFoo(foo); @@ -396,8 +396,8 @@ const Bitfields = packed struct { test "native bit field understands endianness" { var all: u64 = 0x7765443322221111; var bytes: [8]u8 = undefined; - @memcpy(&bytes[0], @ptrCast(*u8, &all), 8); - var bitfields = @ptrCast(*Bitfields, &bytes[0]).*; + @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8); + var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*; assert(bitfields.f1 == 0x1111); assert(bitfields.f2 == 0x2222); diff --git a/test/compare_output.zig b/test/compare_output.zig index 00ad4a709b..8d5dc68d45 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -6,7 +6,7 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addC("hello world with libc", \\const c = @cImport(@cInclude("stdio.h")); - \\export fn main(argc: c_int, argv: **u8) c_int { + \\export fn main(argc: c_int, argv: [*][*]u8) c_int { \\ _ = c.puts(c"Hello, world!"); \\ return 0; \\} @@ -139,7 +139,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ @cInclude("stdio.h"); \\}); \\ - \\export fn main(argc: c_int, argv: **u8) c_int { + \\export fn main(argc: c_int, argv: [*][*]u8) c_int { \\ if (is_windows) { \\ // we want actual \n, not \r\n \\ _ = c._setmode(1, c._O_BINARY); @@ -284,9 +284,9 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addC("expose function pointer to C land", \\const c = @cImport(@cInclude("stdlib.h")); \\ - \\export fn compare_fn(a: ?*const c_void, b: ?*const c_void) c_int { - \\ const a_int = @ptrCast(*align(1) const i32, a ?? unreachable); - \\ const b_int = @ptrCast(*align(1) const i32, b ?? unreachable); + \\export fn compare_fn(a: ?[*]const c_void, b: ?[*]const c_void) c_int { + \\ const a_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), a)); + \\ const b_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), b)); \\ if (a_int.* < b_int.*) { \\ return -1; \\ } else if (a_int.* > b_int.*) { @@ -297,9 +297,9 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\ \\export fn main() c_int { - \\ var array = []u32 { 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; + \\ var array = []u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; \\ - \\ c.qsort(@ptrCast(*c_void, &array[0]), c_ulong(array.len), @sizeOf(i32), compare_fn); + \\ c.qsort(@ptrCast(?[*]c_void, array[0..].ptr), c_ulong(array.len), @sizeOf(i32), compare_fn); \\ \\ for (array) |item, i| { \\ if (item != i) { @@ -324,7 +324,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ @cInclude("stdio.h"); \\}); \\ - \\export fn main(argc: c_int, argv: **u8) c_int { + \\export fn main(argc: c_int, argv: [*][*]u8) c_int { \\ if (is_windows) { \\ // we want actual \n, not \r\n \\ _ = c._setmode(1, c._O_BINARY); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index ea1357f5bb..7e9ef82e42 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "indexing single-item pointer", + \\export fn entry(ptr: *i32) i32 { + \\ return ptr[1]; + \\} + , + ".tmp_source.zig:2:15: error: indexing not allowed on pointer to single item", + ); + cases.add( "invalid deref on switch target", \\const NextError = error{NextError}; @@ -1002,7 +1011,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return a; \\} , - ".tmp_source.zig:3:12: error: expected type 'i32', found '*const u8'", + ".tmp_source.zig:3:12: error: expected type 'i32', found '[*]const u8'", ); cases.add( @@ -2442,13 +2451,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\var s_buffer: [10]u8 = undefined; \\pub fn pass(in: []u8) []u8 { \\ var out = &s_buffer; - \\ out[0].* = in[0]; + \\ out.*.* = in[0]; \\ return out.*[0..1]; \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(pass)); } , - ".tmp_source.zig:4:11: error: attempt to dereference non pointer type '[10]u8'", + ".tmp_source.zig:4:10: error: attempt to dereference non pointer type '[10]u8'", ); cases.add( diff --git a/test/translate_c.zig b/test/translate_c.zig index 9a07bc343d..ac0a98e6cc 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -14,11 +14,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; , \\pub const struct_Foo = extern struct { - \\ a: ?*Foo, + \\ a: ?[*]Foo, \\}; \\pub const Foo = struct_Foo; \\pub const struct_Bar = extern struct { - \\ a: ?*Foo, + \\ a: ?[*]Foo, \\}; ); @@ -99,7 +99,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("restrict -> noalias", \\void foo(void *restrict bar, void *restrict); , - \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; + \\pub extern fn foo(noalias bar: ?[*]c_void, noalias arg1: ?[*]c_void) void; ); cases.add("simple struct", @@ -110,7 +110,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\const struct_Foo = extern struct { \\ x: c_int, - \\ y: ?*u8, + \\ y: ?[*]u8, \\}; , \\pub const Foo = struct_Foo; @@ -141,7 +141,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const BarB = enum_Bar.B; , - \\pub extern fn func(a: ?*struct_Foo, b: ?*(?*enum_Bar)) void; + \\pub extern fn func(a: ?[*]struct_Foo, b: ?[*](?[*]enum_Bar)) void; , \\pub const Foo = struct_Foo; , @@ -151,7 +151,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("constant size array", \\void func(int array[20]); , - \\pub extern fn func(array: ?*c_int) void; + \\pub extern fn func(array: ?[*]c_int) void; ); cases.add("self referential struct with function pointer", @@ -160,7 +160,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; , \\pub const struct_Foo = extern struct { - \\ derp: ?extern fn(?*struct_Foo) void, + \\ derp: ?extern fn(?[*]struct_Foo) void, \\}; , \\pub const Foo = struct_Foo; @@ -172,7 +172,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const struct_Foo = @OpaqueType(); , - \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo; + \\pub extern fn some_func(foo: ?[*]struct_Foo, x: c_int) ?[*]struct_Foo; , \\pub const Foo = struct_Foo; ); @@ -219,11 +219,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; , \\pub const struct_Bar = extern struct { - \\ next: ?*struct_Foo, + \\ next: ?[*]struct_Foo, \\}; , \\pub const struct_Foo = extern struct { - \\ next: ?*struct_Bar, + \\ next: ?[*]struct_Bar, \\}; ); @@ -233,7 +233,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const Foo = c_void; , - \\pub extern fn fun(a: ?*Foo) Foo; + \\pub extern fn fun(a: ?[*]Foo) Foo; ); cases.add("generate inline func for #define global extern fn", @@ -505,7 +505,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 6; \\} , - \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { \\ if ((a != 0) and (b != 0)) return 0; \\ if ((b != 0) and (c != null)) return 1; \\ if ((a != 0) and (c != null)) return 2; @@ -607,7 +607,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const struct_Foo = extern struct { \\ field: c_int, \\}; - \\pub export fn read_field(foo: ?*struct_Foo) c_int { + \\pub export fn read_field(foo: ?[*]struct_Foo) c_int { \\ return (??foo).field; \\} ); @@ -653,8 +653,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return x; \\} , - \\pub export fn foo(x: ?*c_ushort) ?*c_void { - \\ return @ptrCast(?*c_void, x); + \\pub export fn foo(x: ?[*]c_ushort) ?[*]c_void { + \\ return @ptrCast(?[*]c_void, x); \\} ); @@ -674,7 +674,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 0; \\} , - \\pub export fn foo() ?*c_int { + \\pub export fn foo() ?[*]c_int { \\ return null; \\} ); @@ -983,7 +983,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ *x = 1; \\} , - \\pub export fn foo(x: ?*c_int) void { + \\pub export fn foo(x: ?[*]c_int) void { \\ (??x).* = 1; \\} ); @@ -1011,7 +1011,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub fn foo() c_int { \\ var x: c_int = 1234; - \\ var ptr: ?*c_int = &x; + \\ var ptr: ?[*]c_int = &x; \\ return (??ptr).*; \\} ); @@ -1021,7 +1021,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return "bar"; \\} , - \\pub fn foo() ?*const u8 { + \\pub fn foo() ?[*]const u8 { \\ return c"bar"; \\} ); @@ -1150,8 +1150,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return (float *)a; \\} , - \\fn ptrcast(a: ?*c_int) ?*f32 { - \\ return @ptrCast(?*f32, a); + \\fn ptrcast(a: ?[*]c_int) ?[*]f32 { + \\ return @ptrCast(?[*]f32, a); \\} ); @@ -1173,7 +1173,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return !c; \\} , - \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub fn foo(a: c_int, b: f32, c: ?[*]c_void) c_int { \\ return !(a == 0); \\ return !(a != 0); \\ return !(b != 0); @@ -1194,7 +1194,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("const ptr initializer", \\static const char *v0 = "0.0.0"; , - \\pub var v0: ?*const u8 = c"0.0.0"; + \\pub var v0: ?[*]const u8 = c"0.0.0"; ); cases.add("static incomplete array inside function", @@ -1203,14 +1203,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , \\pub fn foo() void { - \\ const v2: *const u8 = c"2.2.2"; + \\ const v2: [*]const u8 = c"2.2.2"; \\} ); cases.add("macro pointer cast", \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) , - \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast(*NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr(*NRF_GPIO_Type, NRF_GPIO_BASE) else (*NRF_GPIO_Type)(NRF_GPIO_BASE); + \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*]NRF_GPIO_Type, NRF_GPIO_BASE) else ([*]NRF_GPIO_Type)(NRF_GPIO_BASE); ); cases.add("if on none bool", @@ -1231,7 +1231,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ B, \\ C, \\}; - \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { + \\pub fn if_none_bool(a: c_int, b: f32, c: ?[*]c_void, d: enum_SomeEnum) c_int { \\ if (a != 0) return 0; \\ if (b != 0) return 1; \\ if (c != null) return 2; @@ -1248,7 +1248,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub fn while_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; @@ -1264,7 +1264,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub fn for_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; -- cgit v1.2.3 From d21a1922eb5d76b9b0d0611eaeb42c91f83234ab Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Jun 2018 12:15:02 -0400 Subject: support `zig fmt: off` and `zig fmt: on` between top level decls closes #1030 closes #1033 --- std/special/compiler_rt/udivmoddi4_test.zig | 2 ++ std/special/compiler_rt/udivmodti4_test.zig | 2 ++ std/zig/parser_test.zig | 26 ++++++++++++++++++ std/zig/render.zig | 41 +++++++++++++++++++++++++++-- test/cases/syntax.zig | 1 + 5 files changed, 70 insertions(+), 2 deletions(-) diff --git a/std/special/compiler_rt/udivmoddi4_test.zig b/std/special/compiler_rt/udivmoddi4_test.zig index 324626d3f9..34b9dda1ea 100644 --- a/std/special/compiler_rt/udivmoddi4_test.zig +++ b/std/special/compiler_rt/udivmoddi4_test.zig @@ -1,3 +1,5 @@ +// Disable formatting to avoid unnecessary source repository bloat. +// zig fmt: off const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; const assert = @import("std").debug.assert; diff --git a/std/special/compiler_rt/udivmodti4_test.zig b/std/special/compiler_rt/udivmodti4_test.zig index 48d65b43c6..f6b370c26e 100644 --- a/std/special/compiler_rt/udivmodti4_test.zig +++ b/std/special/compiler_rt/udivmodti4_test.zig @@ -1,3 +1,5 @@ +// Disable formatting to avoid unnecessary source repository bloat. +// zig fmt: off const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4; const assert = @import("std").debug.assert; diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index c28a70b770..91a56de827 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,29 @@ +test "zig fmt: comment to disable/enable zig fmt first" { + try testCanonical( + \\// Test trailing comma syntax + \\// zig fmt: off + \\ + \\const struct_trailing_comma = struct { x: i32, y: i32, }; + ); +} + +test "zig fmt: comment to disable/enable zig fmt" { + try testTransform( + \\const a = b; + \\// zig fmt: off + \\const c = d; + \\// zig fmt: on + \\const e = f; + , + \\const a = b; + \\// zig fmt: off + \\const c = d; + \\// zig fmt: on + \\const e = f; + \\ + ); +} + test "zig fmt: pointer of unknown length" { try testCanonical( \\fn foo(ptr: [*]u8) void {} diff --git a/std/zig/render.zig b/std/zig/render.zig index 147adc6221..7c9b53b77a 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -82,8 +82,45 @@ fn renderRoot( var start_col: usize = 0; var it = tree.root_node.decls.iterator(0); - while (it.next()) |decl| { - try renderTopLevelDecl(allocator, stream, tree, 0, &start_col, decl.*); + while (true) { + var decl = (it.next() ?? return).*; + // look for zig fmt: off comment + var start_token_index = decl.firstToken(); + zig_fmt_loop: while (start_token_index != 0) { + start_token_index -= 1; + const start_token = tree.tokens.at(start_token_index); + switch (start_token.id) { + Token.Id.LineComment => {}, + Token.Id.DocComment => continue, + else => break, + } + if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(start_token)[2..], " "), "zig fmt: off")) { + var end_token_index = start_token_index; + while (true) { + end_token_index += 1; + const end_token = tree.tokens.at(end_token_index); + switch (end_token.id) { + Token.Id.LineComment => {}, + Token.Id.Eof => { + const start = tree.tokens.at(start_token_index + 1).start; + try stream.write(tree.source[start..]); + return; + }, + else => continue, + } + if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(end_token)[2..], " "), "zig fmt: on")) { + const start = tree.tokens.at(start_token_index + 1).start; + try stream.print("{}\n", tree.source[start..end_token.end]); + while (tree.tokens.at(decl.firstToken()).start < end_token.end) { + decl = (it.next() ?? return).*; + } + break :zig_fmt_loop; + } + } + } + } + + try renderTopLevelDecl(allocator, stream, tree, 0, &start_col, decl); if (it.peek()) |next_decl| { try renderExtraNewline(tree, stream, &start_col, next_decl.*); } diff --git a/test/cases/syntax.zig b/test/cases/syntax.zig index 20fb6cd203..b497b060c4 100644 --- a/test/cases/syntax.zig +++ b/test/cases/syntax.zig @@ -1,4 +1,5 @@ // Test trailing comma syntax +// zig fmt: off const struct_trailing_comma = struct { x: i32, y: i32, }; const struct_no_comma = struct { x: i32, y: i32 }; -- cgit v1.2.3 From 32e0dfd4f0dab351a024e7680280343db5d7c43e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Jun 2018 14:09:31 -0400 Subject: never call malloc with size 0 instead we return nullptr. this makes the behavior consistent across all platforms. closes #1044 closes #1045 --- src/analyze.cpp | 4 ++-- src/util.hpp | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 2b9d776e78..3165227033 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1860,7 +1860,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) { } assert(!struct_type->data.structure.zero_bits_loop_flag); - assert(struct_type->data.structure.fields); + assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0); assert(decl_node->type == NodeTypeContainerDecl); size_t field_count = struct_type->data.structure.src_field_count; @@ -2677,8 +2677,8 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { return; } tag_type = enum_type; + abi_alignment_so_far = get_abi_alignment(g, enum_type); // this populates src_field_count covered_enum_fields = allocate(enum_type->data.enumeration.src_field_count); - abi_alignment_so_far = get_abi_alignment(g, enum_type); } else { tag_type = nullptr; abi_alignment_so_far = 0; diff --git a/src/util.hpp b/src/util.hpp index 25141d8435..52baab7ace 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -65,6 +65,11 @@ static inline int clzll(unsigned long long mask) { template ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) { +#ifndef NDEBUG + // make behavior when size == 0 portable + if (count == 0) + return nullptr; +#endif T *ptr = reinterpret_cast(malloc(count * sizeof(T))); if (!ptr) zig_panic("allocation failed"); @@ -73,6 +78,11 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) { template ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) { +#ifndef NDEBUG + // make behavior when size == 0 portable + if (count == 0) + return nullptr; +#endif T *ptr = reinterpret_cast(calloc(count, sizeof(T))); if (!ptr) zig_panic("allocation failed"); @@ -93,9 +103,7 @@ static inline void safe_memcpy(T *dest, const T *src, size_t count) { template static inline T *reallocate(T *old, size_t old_count, size_t new_count) { - T *ptr = reinterpret_cast(realloc(old, new_count * sizeof(T))); - if (!ptr) - zig_panic("allocation failed"); + T *ptr = reallocate_nonzero(old, old_count, new_count); if (new_count > old_count) { memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T)); } @@ -104,6 +112,11 @@ static inline T *reallocate(T *old, size_t old_count, size_t new_count) { template static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) { +#ifndef NDEBUG + // make behavior when size == 0 portable + if (new_count == 0 && old == nullptr) + return nullptr; +#endif T *ptr = reinterpret_cast(realloc(old, new_count * sizeof(T))); if (!ptr) zig_panic("allocation failed"); -- cgit v1.2.3 From e53b683bd3958a7b1c517e2391edce42b9d4e48b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Jun 2018 22:11:14 -0400 Subject: Pointer Reform: proper slicing and indexing (#1053) * enable slicing for single-item ptr to arrays * disable slicing for other single-item pointers * enable indexing for single-item ptr to arrays * disable indexing for other single-item pointers see #770 closes #386 --- doc/langref.html.in | 13 ++-- example/mix_o_files/base64.zig | 2 +- src/all_types.hpp | 5 ++ src/analyze.cpp | 14 ++-- src/analyze.hpp | 5 +- src/codegen.cpp | 27 +++++-- src/ir.cpp | 157 +++++++++++++++++++++++++++++++++++------ std/fmt/errol/index.zig | 2 +- std/fmt/index.zig | 8 +-- std/heap.zig | 2 +- std/io.zig | 4 +- std/macho.zig | 2 +- std/mem.zig | 34 +++++---- std/net.zig | 2 +- std/os/index.zig | 4 +- test/cases/align.zig | 4 +- test/cases/array.zig | 29 ++++++++ test/cases/eval.zig | 6 +- test/cases/misc.zig | 4 +- test/cases/slice.zig | 2 +- test/compile_errors.zig | 21 ++++-- 21 files changed, 268 insertions(+), 79 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 32481ade50..28fdf4d8b9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1565,7 +1565,7 @@ var foo: u8 align(4) = 100; test "global variable alignment" { assert(@typeOf(&foo).alignment == 4); assert(@typeOf(&foo) == *align(4) u8); - const slice = (&foo)[0..1]; + const slice = (*[1]u8)(&foo)[0..]; assert(@typeOf(slice) == []align(4) u8); } @@ -1671,7 +1671,7 @@ test "using slices for strings" { test "slice pointer" { var array: [10]u8 = undefined; - const ptr = &array[0]; + const ptr = &array; // You can use slicing syntax to convert a pointer into a slice: const slice = ptr[0..5]; @@ -6004,9 +6004,12 @@ const c = @cImport({ {#code_begin|syntax#} const base64 = @import("std").base64; -export fn decode_base_64(dest_ptr: *u8, dest_len: usize, - source_ptr: *const u8, source_len: usize) usize -{ +export fn decode_base_64( + dest_ptr: [*]u8, + dest_len: usize, + source_ptr: [*]const u8, + source_len: usize, +) usize { const src = source_ptr[0..source_len]; const dest = dest_ptr[0..dest_len]; const base64_decoder = base64.standard_decoder_unsafe; diff --git a/example/mix_o_files/base64.zig b/example/mix_o_files/base64.zig index 35b090825b..7ded9824a0 100644 --- a/example/mix_o_files/base64.zig +++ b/example/mix_o_files/base64.zig @@ -1,6 +1,6 @@ const base64 = @import("std").base64; -export fn decode_base_64(dest_ptr: *u8, dest_len: usize, source_ptr: *const u8, source_len: usize) usize { +export fn decode_base_64(dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize) usize { const src = source_ptr[0..source_len]; const dest = dest_ptr[0..dest_len]; const base64_decoder = base64.standard_decoder_unsafe; diff --git a/src/all_types.hpp b/src/all_types.hpp index f1cf96238f..d237eb00bb 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -83,6 +83,7 @@ enum ConstParentId { ConstParentIdStruct, ConstParentIdArray, ConstParentIdUnion, + ConstParentIdScalar, }; struct ConstParent { @@ -100,6 +101,9 @@ struct ConstParent { struct { ConstExprValue *union_val; } p_union; + struct { + ConstExprValue *scalar_val; + } p_scalar; } data; }; @@ -578,6 +582,7 @@ enum CastOp { CastOpBytesToSlice, CastOpNumLitToConcrete, CastOpErrSet, + CastOpBitCast, }; struct AstNodeFnCallExpr { diff --git a/src/analyze.cpp b/src/analyze.cpp index 3165227033..31c0726459 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5158,7 +5158,8 @@ void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *arr const_val->type = get_slice_type(g, ptr_type); const_val->data.x_struct.fields = create_const_vals(2); - init_const_ptr_array(g, &const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const); + init_const_ptr_array(g, &const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const, + PtrLenUnknown); init_const_usize(g, &const_val->data.x_struct.fields[slice_len_index], len); } @@ -5169,21 +5170,24 @@ ConstExprValue *create_const_slice(CodeGen *g, ConstExprValue *array_val, size_t } void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val, - size_t elem_index, bool is_const) + size_t elem_index, bool is_const, PtrLen ptr_len) { assert(array_val->type->id == TypeTableEntryIdArray); TypeTableEntry *child_type = array_val->type->data.array.child_type; const_val->special = ConstValSpecialStatic; - const_val->type = get_pointer_to_type(g, child_type, is_const); + const_val->type = get_pointer_to_type_extra(g, child_type, is_const, false, + ptr_len, get_abi_alignment(g, child_type), 0, 0); const_val->data.x_ptr.special = ConstPtrSpecialBaseArray; const_val->data.x_ptr.data.base_array.array_val = array_val; const_val->data.x_ptr.data.base_array.elem_index = elem_index; } -ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, bool is_const) { +ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, bool is_const, + PtrLen ptr_len) +{ ConstExprValue *const_val = create_const_vals(1); - init_const_ptr_array(g, const_val, array_val, elem_index, is_const); + init_const_ptr_array(g, const_val, array_val, elem_index, is_const, ptr_len); return const_val; } diff --git a/src/analyze.hpp b/src/analyze.hpp index 905bfa86dd..25bda198d6 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -152,8 +152,9 @@ ConstExprValue *create_const_ptr_hard_coded_addr(CodeGen *g, TypeTableEntry *poi size_t addr, bool is_const); void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val, - size_t elem_index, bool is_const); -ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, bool is_const); + size_t elem_index, bool is_const, PtrLen ptr_len); +ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, + bool is_const, PtrLen ptr_len); void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val, size_t start, size_t len, bool is_const); diff --git a/src/codegen.cpp b/src/codegen.cpp index 64e29a4da4..49c93feaa5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2574,6 +2574,8 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, add_error_range_check(g, wanted_type, g->err_tag_type, expr_val); } return expr_val; + case CastOpBitCast: + return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); } zig_unreachable(); } @@ -2884,7 +2886,13 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI bool safety_check_on = ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on; - if (array_type->id == TypeTableEntryIdArray) { + if (array_type->id == TypeTableEntryIdArray || + (array_type->id == TypeTableEntryIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) + { + if (array_type->id == TypeTableEntryIdPointer) { + assert(array_type->data.pointer.child_type->id == TypeTableEntryIdArray); + array_type = array_type->data.pointer.child_type; + } if (safety_check_on) { LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false); @@ -3794,7 +3802,12 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); - if (array_type->id == TypeTableEntryIdArray) { + if (array_type->id == TypeTableEntryIdArray || + (array_type->id == TypeTableEntryIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) + { + if (array_type->id == TypeTableEntryIdPointer) { + array_type = array_type->data.pointer.child_type; + } LLVMValueRef start_val = ir_llvm_value(g, instruction->start); LLVMValueRef end_val; if (instruction->end) { @@ -3835,6 +3848,7 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst return tmp_struct_ptr; } else if (array_type->id == TypeTableEntryIdPointer) { + assert(array_type->data.pointer.ptr_len == PtrLenUnknown); LLVMValueRef start_val = ir_llvm_value(g, instruction->start); LLVMValueRef end_val = ir_llvm_value(g, instruction->end); @@ -4812,7 +4826,7 @@ static void ir_render(CodeGen *g, FnTableEntry *fn_entry) { static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index); static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index); -static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *array_const_val); +static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val); static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) { switch (parent->id) { @@ -4828,6 +4842,10 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent parent->data.p_array.elem_index); case ConstParentIdUnion: return gen_const_ptr_union_recursive(g, parent->data.p_union.union_val); + case ConstParentIdScalar: + render_const_val(g, parent->data.p_scalar.scalar_val, ""); + render_const_val_global(g, parent->data.p_scalar.scalar_val, ""); + return parent->data.p_scalar.scalar_val->global_refs->llvm_global; } zig_unreachable(); } @@ -4853,7 +4871,8 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *ar }; return LLVMConstInBoundsGEP(base_ptr, indices, 2); } else { - zig_unreachable(); + assert(parent->id == ConstParentIdScalar); + return base_ptr; } } diff --git a/src/ir.cpp b/src/ir.cpp index a230c60456..5cea04ea55 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -107,6 +107,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, VariableTableEntry *var); static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); +static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, uint32_t new_align); ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == TypeTableEntryIdPointer); @@ -6849,7 +6850,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); - IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, coro_mem_ptr_maybe); + IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, + get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, + false, false, PtrLenUnknown, get_abi_alignment(irb->codegen, irb->codegen->builtin_types.entry_u8), + 0, 0)); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); @@ -8729,6 +8734,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, case CastOpNoCast: zig_unreachable(); case CastOpErrSet: + case CastOpBitCast: zig_panic("TODO"); case CastOpNoop: { @@ -9750,6 +9756,49 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc return result; } +static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, + TypeTableEntry *wanted_type) +{ + assert(wanted_type->id == TypeTableEntryIdPointer); + wanted_type = adjust_ptr_align(ira->codegen, wanted_type, target->value.type->data.pointer.alignment); + TypeTableEntry *array_type = wanted_type->data.pointer.child_type; + assert(array_type->id == TypeTableEntryIdArray); + assert(array_type->data.array.len == 1); + + if (instr_is_comptime(target)) { + ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + + assert(val->type->id == TypeTableEntryIdPointer); + ConstExprValue *pointee = const_ptr_pointee(ira->codegen, val); + if (pointee->special != ConstValSpecialRuntime) { + ConstExprValue *array_val = create_const_vals(1); + array_val->special = ConstValSpecialStatic; + array_val->type = array_type; + array_val->data.x_array.special = ConstArraySpecialNone; + array_val->data.x_array.s_none.elements = pointee; + array_val->data.x_array.s_none.parent.id = ConstParentIdScalar; + array_val->data.x_array.s_none.parent.data.p_scalar.scalar_val = pointee; + + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + source_instr->scope, source_instr->source_node); + const_instruction->base.value.type = wanted_type; + const_instruction->base.value.special = ConstValSpecialStatic; + const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialRef; + const_instruction->base.value.data.x_ptr.data.ref.pointee = array_val; + const_instruction->base.value.data.x_ptr.mut = val->data.x_ptr.mut; + return &const_instruction->base; + } + } + + // pointer to array and pointer to single item are represented the same way at runtime + IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, + wanted_type, target, CastOpBitCast); + result->value.type = wanted_type; + return result; +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, TypeTableEntry *wanted_type, IrInstruction *value) { @@ -10156,6 +10205,30 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } + // explicit cast from *T to *[1]T + if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle) + { + TypeTableEntry *array_type = wanted_type->data.pointer.child_type; + if (array_type->id == TypeTableEntryIdArray && array_type->data.array.len == 1 && + types_match_const_cast_only(ira, array_type->data.array.child_type, + actual_type->data.pointer.child_type, source_node).id == ConstCastResultIdOk) + { + if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); + add_error_note(ira->codegen, msg, value->source_node, + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), + actual_type->data.pointer.alignment)); + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), + wanted_type->data.pointer.alignment)); + return ira->codegen->invalid_instruction; + } + return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type); + } + } + + // explicit cast from undefined to anything if (actual_type->id == TypeTableEntryIdUndefLit) { return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); @@ -13162,11 +13235,13 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc if (type_is_invalid(array_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; + ConstExprValue *orig_array_ptr_val = &array_ptr->value; + IrInstruction *elem_index = elem_ptr_instruction->elem_index->other; if (type_is_invalid(elem_index->value.type)) return ira->codegen->builtin_types.entry_invalid; - TypeTableEntry *ptr_type = array_ptr->value.type; + TypeTableEntry *ptr_type = orig_array_ptr_val->type; assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *array_type = ptr_type->data.pointer.child_type; @@ -13177,7 +13252,18 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc if (type_is_invalid(array_type)) { return array_type; - } else if (array_type->id == TypeTableEntryIdArray) { + } else if (array_type->id == TypeTableEntryIdArray || + (array_type->id == TypeTableEntryIdPointer && + array_type->data.pointer.ptr_len == PtrLenSingle && + array_type->data.pointer.child_type->id == TypeTableEntryIdArray)) + { + if (array_type->id == TypeTableEntryIdPointer) { + array_type = array_type->data.pointer.child_type; + ptr_type = ptr_type->data.pointer.child_type; + if (orig_array_ptr_val->special != ConstValSpecialRuntime) { + orig_array_ptr_val = const_ptr_pointee(ira->codegen, orig_array_ptr_val); + } + } if (array_type->data.array.len == 0) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, buf_sprintf("index 0 outside array of size 0")); @@ -13205,7 +13291,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc } else if (array_type->id == TypeTableEntryIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, - buf_sprintf("indexing not allowed on pointer to single item")); + buf_sprintf("index of single-item pointer")); return ira->codegen->builtin_types.entry_invalid; } return_type = adjust_ptr_len(ira->codegen, array_type, elem_ptr_instruction->ptr_len); @@ -13294,9 +13380,9 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc } ConstExprValue *array_ptr_val; - if (array_ptr->value.special != ConstValSpecialRuntime && - (array_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar || array_type->id == TypeTableEntryIdArray) && - (array_ptr_val = const_ptr_pointee(ira->codegen, &array_ptr->value)) && + if (orig_array_ptr_val->special != ConstValSpecialRuntime && + (orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar || array_type->id == TypeTableEntryIdArray) && + (array_ptr_val = const_ptr_pointee(ira->codegen, orig_array_ptr_val)) && array_ptr_val->special != ConstValSpecialRuntime && (array_type->id != TypeTableEntryIdPointer || array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr)) @@ -13401,7 +13487,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc } else if (array_type->id == TypeTableEntryIdArray) { ConstExprValue *out_val = ir_build_const_from(ira, &elem_ptr_instruction->base); out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; - out_val->data.x_ptr.mut = array_ptr->value.data.x_ptr.mut; + out_val->data.x_ptr.mut = orig_array_ptr_val->data.x_ptr.mut; out_val->data.x_ptr.data.base_array.array_val = array_ptr_val; out_val->data.x_ptr.data.base_array.elem_index = index; return return_type; @@ -17406,14 +17492,29 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio byte_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == TypeTableEntryIdPointer) { - TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type, - array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, - PtrLenUnknown, - array_type->data.pointer.alignment, 0, 0); - return_type = get_slice_type(ira->codegen, slice_ptr_type); - if (!end) { - ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); - return ira->codegen->builtin_types.entry_invalid; + if (array_type->data.pointer.ptr_len == PtrLenSingle) { + TypeTableEntry *main_type = array_type->data.pointer.child_type; + if (main_type->id == TypeTableEntryIdArray) { + TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, + main_type->data.pointer.child_type, + array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, + PtrLenUnknown, + array_type->data.pointer.alignment, 0, 0); + return_type = get_slice_type(ira->codegen, slice_ptr_type); + } else { + ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer")); + return ira->codegen->builtin_types.entry_invalid; + } + } else { + TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type, + array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, + PtrLenUnknown, + array_type->data.pointer.alignment, 0, 0); + return_type = get_slice_type(ira->codegen, slice_ptr_type); + if (!end) { + ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); + return ira->codegen->builtin_types.entry_invalid; + } } } else if (is_slice(array_type)) { TypeTableEntry *ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry; @@ -17433,12 +17534,24 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio size_t abs_offset; size_t rel_end; bool ptr_is_undef = false; - if (array_type->id == TypeTableEntryIdArray) { - array_val = const_ptr_pointee(ira->codegen, &ptr_ptr->value); - abs_offset = 0; - rel_end = array_type->data.array.len; - parent_ptr = nullptr; + if (array_type->id == TypeTableEntryIdArray || + (array_type->id == TypeTableEntryIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) + { + if (array_type->id == TypeTableEntryIdPointer) { + TypeTableEntry *child_array_type = array_type->data.pointer.child_type; + assert(child_array_type->id == TypeTableEntryIdArray); + parent_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value); + array_val = const_ptr_pointee(ira->codegen, parent_ptr); + rel_end = child_array_type->data.array.len; + abs_offset = 0; + } else { + array_val = const_ptr_pointee(ira->codegen, &ptr_ptr->value); + rel_end = array_type->data.array.len; + parent_ptr = nullptr; + abs_offset = 0; + } } else if (array_type->id == TypeTableEntryIdPointer) { + assert(array_type->data.pointer.ptr_len == PtrLenUnknown); parent_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value); if (parent_ptr->special == ConstValSpecialUndef) { array_val = nullptr; @@ -17537,7 +17650,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio if (array_val) { size_t index = abs_offset + start_scalar; bool is_const = slice_is_const(return_type); - init_const_ptr_array(ira->codegen, ptr_val, array_val, index, is_const); + init_const_ptr_array(ira->codegen, ptr_val, array_val, index, is_const, PtrLenUnknown); if (array_type->id == TypeTableEntryIdArray) { ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut; } else if (is_slice(array_type)) { diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index 933958ac18..a906b714ab 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -59,7 +59,7 @@ pub fn roundToPrecision(float_decimal: *FloatDecimal, precision: usize, mode: Ro float_decimal.exp += 1; // Re-size the buffer to use the reserved leading byte. - const one_before = @intToPtr(*u8, @ptrToInt(&float_decimal.digits[0]) - 1); + const one_before = @intToPtr([*]u8, @ptrToInt(&float_decimal.digits[0]) - 1); float_decimal.digits = one_before[0 .. float_decimal.digits.len + 1]; float_decimal.digits[0] = '1'; return; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 21991e9ba3..047a154bb8 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -278,7 +278,7 @@ pub fn formatAsciiChar( comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, ) Errors!void { - return output(context, (&c)[0..1]); + return output(context, (*[1]u8)(&c)[0..]); } pub fn formatBuf( @@ -603,7 +603,7 @@ fn formatIntSigned( const uint = @IntType(false, @typeOf(value).bit_count); if (value < 0) { const minus_sign: u8 = '-'; - try output(context, (&minus_sign)[0..1]); + try output(context, (*[1]u8)(&minus_sign)[0..]); const new_value = uint(-(value + 1)) + 1; const new_width = if (width == 0) 0 else (width - 1); return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); @@ -611,7 +611,7 @@ fn formatIntSigned( return formatIntUnsigned(uint(value), base, uppercase, width, context, Errors, output); } else { const plus_sign: u8 = '+'; - try output(context, (&plus_sign)[0..1]); + try output(context, (*[1]u8)(&plus_sign)[0..]); const new_value = uint(value); const new_width = if (width == 0) 0 else (width - 1); return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); @@ -648,7 +648,7 @@ fn formatIntUnsigned( const zero_byte: u8 = '0'; var leftover_padding = padding - index; while (true) { - try output(context, (&zero_byte)[0..1]); + try output(context, (*[1]u8)(&zero_byte)[0..]); leftover_padding -= 1; if (leftover_padding == 0) break; } diff --git a/std/heap.zig b/std/heap.zig index 0b8f4aeb3f..4444a2307a 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -24,7 +24,7 @@ fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 { fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { const old_ptr = @ptrCast([*]c_void, old_mem.ptr); if (c.realloc(old_ptr, new_size)) |buf| { - return @ptrCast(*u8, buf)[0..new_size]; + return @ptrCast([*]u8, buf)[0..new_size]; } else if (new_size <= old_mem.len) { return old_mem[0..new_size]; } else { diff --git a/std/io.zig b/std/io.zig index e20a284e4e..a603d0cf5e 100644 --- a/std/io.zig +++ b/std/io.zig @@ -219,12 +219,12 @@ pub fn OutStream(comptime WriteError: type) type { } pub fn writeByte(self: *Self, byte: u8) !void { - const slice = (&byte)[0..1]; + const slice = (*[1]u8)(&byte)[0..]; return self.writeFn(self, slice); } pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) !void { - const slice = (&byte)[0..1]; + const slice = (*[1]u8)(&byte)[0..]; var i: usize = 0; while (i < n) : (i += 1) { try self.writeFn(self, slice); diff --git a/std/macho.zig b/std/macho.zig index e71ac76b1a..d6eef9a325 100644 --- a/std/macho.zig +++ b/std/macho.zig @@ -164,7 +164,7 @@ fn readNoEof(in: *io.FileInStream, comptime T: type, result: []T) !void { return in.stream.readNoEof(([]u8)(result)); } fn readOneNoEof(in: *io.FileInStream, comptime T: type, result: *T) !void { - return readNoEof(in, T, result[0..1]); + return readNoEof(in, T, (*[1]T)(result)[0..]); } fn isSymbol(sym: *const Nlist64) bool { diff --git a/std/mem.zig b/std/mem.zig index aec24e8491..423460e73b 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -31,14 +31,16 @@ pub const Allocator = struct { /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` freeFn: fn (self: *Allocator, old_mem: []u8) void, - fn create(self: *Allocator, comptime T: type) !*T { + /// Call destroy with the result + pub fn create(self: *Allocator, comptime T: type) !*T { if (@sizeOf(T) == 0) return *{}; const slice = try self.alloc(T, 1); return &slice[0]; } - // TODO once #733 is solved, this will replace create - fn construct(self: *Allocator, init: var) t: { + /// Call destroy with the result + /// TODO once #733 is solved, this will replace create + pub fn construct(self: *Allocator, init: var) t: { // TODO this is a workaround for type getting parsed as Error!&const T const T = @typeOf(init).Child; break :t Error!*T; @@ -51,17 +53,19 @@ pub const Allocator = struct { return ptr; } - fn destroy(self: *Allocator, ptr: var) void { - self.free(ptr[0..1]); + /// `ptr` should be the return value of `construct` or `create` + pub fn destroy(self: *Allocator, ptr: var) void { + const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr)); + self.freeFn(self, non_const_ptr[0..@sizeOf(@typeOf(ptr).Child)]); } - fn alloc(self: *Allocator, comptime T: type, n: usize) ![]T { + pub fn alloc(self: *Allocator, comptime T: type, n: usize) ![]T { return self.alignedAlloc(T, @alignOf(T), n); } - fn alignedAlloc(self: *Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { + pub fn alignedAlloc(self: *Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { if (n == 0) { - return (*align(alignment) T)(undefined)[0..0]; + return ([*]align(alignment) T)(undefined)[0..0]; } const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; const byte_slice = try self.allocFn(self, byte_count, alignment); @@ -73,17 +77,17 @@ pub const Allocator = struct { return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); } - fn realloc(self: *Allocator, comptime T: type, old_mem: []T, n: usize) ![]T { + pub fn realloc(self: *Allocator, comptime T: type, old_mem: []T, n: usize) ![]T { return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } - fn alignedRealloc(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { + pub fn alignedRealloc(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { if (old_mem.len == 0) { return self.alloc(T, n); } if (n == 0) { self.free(old_mem); - return (*align(alignment) T)(undefined)[0..0]; + return ([*]align(alignment) T)(undefined)[0..0]; } const old_byte_slice = ([]u8)(old_mem); @@ -102,11 +106,11 @@ pub const Allocator = struct { /// Reallocate, but `n` must be less than or equal to `old_mem.len`. /// Unlike `realloc`, this function cannot fail. /// Shrinking to 0 is the same as calling `free`. - fn shrink(self: *Allocator, comptime T: type, old_mem: []T, n: usize) []T { + pub fn shrink(self: *Allocator, comptime T: type, old_mem: []T, n: usize) []T { return self.alignedShrink(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } - fn alignedShrink(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) []align(alignment) T { + pub fn alignedShrink(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) []align(alignment) T { if (n == 0) { self.free(old_mem); return old_mem[0..0]; @@ -123,10 +127,10 @@ pub const Allocator = struct { return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); } - fn free(self: *Allocator, memory: var) void { + pub fn free(self: *Allocator, memory: var) void { const bytes = ([]const u8)(memory); if (bytes.len == 0) return; - const non_const_ptr = @intToPtr(*u8, @ptrToInt(bytes.ptr)); + const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr)); self.freeFn(self, non_const_ptr[0..bytes.len]); } }; diff --git a/std/net.zig b/std/net.zig index bfe4b1c2a0..f21611ff91 100644 --- a/std/net.zig +++ b/std/net.zig @@ -68,7 +68,7 @@ pub const Address = struct { pub fn parseIp4(buf: []const u8) !u32 { var result: u32 = undefined; - const out_ptr = ([]u8)((&result)[0..1]); + const out_ptr = ([]u8)((*[1]u32)(&result)[0..]); var x: u8 = 0; var index: u8 = 0; diff --git a/std/os/index.zig b/std/os/index.zig index 7e908af9eb..6023929b04 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1240,7 +1240,7 @@ pub const Dir = struct { const next_index = self.index + darwin_entry.d_reclen; self.index = next_index; - const name = (&darwin_entry.d_name)[0..darwin_entry.d_namlen]; + const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; // skip . and .. entries if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { @@ -1704,7 +1704,7 @@ pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { for (args_alloc) |arg| { total_bytes += @sizeOf([]u8) + arg.len; } - const unaligned_allocated_buf = @ptrCast(*const u8, args_alloc.ptr)[0..total_bytes]; + const unaligned_allocated_buf = @ptrCast([*]const u8, args_alloc.ptr)[0..total_bytes]; const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf); return allocator.free(aligned_allocated_buf); } diff --git a/test/cases/align.zig b/test/cases/align.zig index b80727258e..682c185e86 100644 --- a/test/cases/align.zig +++ b/test/cases/align.zig @@ -6,7 +6,7 @@ var foo: u8 align(4) = 100; test "global variable alignment" { assert(@typeOf(&foo).alignment == 4); assert(@typeOf(&foo) == *align(4) u8); - const slice = (&foo)[0..1]; + const slice = (*[1]u8)(&foo)[0..]; assert(@typeOf(slice) == []align(4) u8); } @@ -60,7 +60,7 @@ fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { test "implicitly decreasing slice alignment" { const a: u32 align(4) = 3; const b: u32 align(8) = 4; - assert(addUnalignedSlice((&a)[0..1], (&b)[0..1]) == 7); + assert(addUnalignedSlice((*[1]u32)(&a)[0..], (*[1]u32)(&b)[0..]) == 7); } fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { return a[0] + b[0]; diff --git a/test/cases/array.zig b/test/cases/array.zig index 9a405216d8..ef919b27bd 100644 --- a/test/cases/array.zig +++ b/test/cases/array.zig @@ -115,3 +115,32 @@ test "array len property" { var x: [5]i32 = undefined; assert(@typeOf(x).len == 5); } + +test "single-item pointer to array indexing and slicing" { + testSingleItemPtrArrayIndexSlice(); + comptime testSingleItemPtrArrayIndexSlice(); +} + +fn testSingleItemPtrArrayIndexSlice() void { + var array = "aaaa"; + doSomeMangling(&array); + assert(mem.eql(u8, "azya", array)); +} + +fn doSomeMangling(array: *[4]u8) void { + array[1] = 'z'; + array[2..3][0] = 'y'; +} + +test "implicit cast single-item pointer" { + testImplicitCastSingleItemPtr(); + comptime testImplicitCastSingleItemPtr(); +} + +fn testImplicitCastSingleItemPtr() void { + var byte: u8 = 100; + const slice = (*[1]u8)(&byte)[0..]; + slice[0] += 1; + assert(byte == 101); +} + diff --git a/test/cases/eval.zig b/test/cases/eval.zig index b6d6a4f37b..461408afea 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -418,9 +418,9 @@ test "string literal used as comptime slice is memoized" { } test "comptime slice of undefined pointer of length 0" { - const slice1 = (*i32)(undefined)[0..0]; + const slice1 = ([*]i32)(undefined)[0..0]; assert(slice1.len == 0); - const slice2 = (*i32)(undefined)[100..100]; + const slice2 = ([*]i32)(undefined)[100..100]; assert(slice2.len == 0); } @@ -508,7 +508,7 @@ test "comptime slice of slice preserves comptime var" { test "comptime slice of pointer preserves comptime var" { comptime { var buff: [10]u8 = undefined; - var a = &buff[0]; + var a = buff[0..].ptr; a[0..1][0] = 1; assert(buff[0..][0..][0] == 1); } diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 5899f20f9c..e007ec4c46 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -274,7 +274,7 @@ test "generic malloc free" { } var some_mem: [100]u8 = undefined; fn memAlloc(comptime T: type, n: usize) error![]T { - return @ptrCast(*T, &some_mem[0])[0..n]; + return @ptrCast([*]T, &some_mem[0])[0..n]; } fn memFree(comptime T: type, memory: []T) void {} @@ -588,7 +588,7 @@ var global_ptr = &gdt[0]; // can't really run this test but we can make sure it has no compile error // and generates code -const vram = @intToPtr(*volatile u8, 0x20000000)[0..0x8000]; +const vram = @intToPtr([*]volatile u8, 0x20000000)[0..0x8000]; export fn writeToVRam() void { vram[0] = 'X'; } diff --git a/test/cases/slice.zig b/test/cases/slice.zig index 24e5239e2d..b4b43bdd19 100644 --- a/test/cases/slice.zig +++ b/test/cases/slice.zig @@ -1,7 +1,7 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; -const x = @intToPtr(*i32, 0x1000)[0..0x500]; +const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { assert(@ptrToInt(x.ptr) == 0x1000); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 7e9ef82e42..17136e150f 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,13 +1,22 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "slicing single-item pointer", + \\export fn entry(ptr: *i32) void { + \\ const slice = ptr[0..2]; + \\} + , + ".tmp_source.zig:2:22: error: slice of single-item pointer", + ); + cases.add( "indexing single-item pointer", \\export fn entry(ptr: *i32) i32 { \\ return ptr[1]; \\} , - ".tmp_source.zig:2:15: error: indexing not allowed on pointer to single item", + ".tmp_source.zig:2:15: error: index of single-item pointer", ); cases.add( @@ -144,10 +153,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( "comptime slice of undefined pointer non-zero len", \\export fn entry() void { - \\ const slice = (*i32)(undefined)[0..1]; + \\ const slice = ([*]i32)(undefined)[0..1]; \\} , - ".tmp_source.zig:2:36: error: non-zero length slice of undefined pointer", + ".tmp_source.zig:2:38: error: non-zero length slice of undefined pointer", ); cases.add( @@ -3129,14 +3138,16 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var foo = Foo { .a = 1, .b = 10 }; \\ foo.b += 1; - \\ bar((&foo.b)[0..1]); + \\ bar((*[1]u32)(&foo.b)[0..]); \\} \\ \\fn bar(x: []u32) void { \\ x[0] += 1; \\} , - ".tmp_source.zig:9:17: error: expected type '[]u32', found '[]align(1) u32'", + ".tmp_source.zig:9:18: error: cast increases pointer alignment", + ".tmp_source.zig:9:23: note: '*align(1) u32' has alignment 1", + ".tmp_source.zig:9:18: note: '*[1]u32' has alignment 4", ); cases.add( -- cgit v1.2.3 From 02cb220faf0d527b656a3a87ec96e6738770c8e6 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Tue, 5 Jun 2018 11:14:43 +0200 Subject: Renamed "(int/float literal)" to "comptime_int/float" --- doc/langref.html.in | 18 ++-- src/all_types.hpp | 4 +- src/analyze.cpp | 94 ++++++++++---------- src/codegen.cpp | 38 ++++---- src/ir.cpp | 230 ++++++++++++++++++++++++------------------------ std/math/ln.zig | 4 +- std/math/log.zig | 6 +- std/math/log10.zig | 4 +- std/math/log2.zig | 4 +- std/math/sqrt.zig | 4 +- test/cases/math.zig | 24 +++-- test/cases/misc.zig | 4 +- test/compile_errors.zig | 12 +-- 13 files changed, 230 insertions(+), 216 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 28fdf4d8b9..0689baa6f9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4893,8 +4893,8 @@ pub const TypeId = enum { Pointer, Array, Struct, - FloatLiteral, - IntLiteral, + ComptimeFloat, + ComptimeInt, UndefinedLiteral, NullLiteral, Nullable, @@ -4927,8 +4927,8 @@ pub const TypeInfo = union(TypeId) { Pointer: Pointer, Array: Array, Struct: Struct, - FloatLiteral: void, - IntLiteral: void, + ComptimeFloat: void, + ComptimeInt: void, UndefinedLiteral: void, NullLiteral: void, Nullable: Nullable, @@ -5685,8 +5685,8 @@ pub const TypeId = enum { Pointer, Array, Struct, - FloatLiteral, - IntLiteral, + ComptimeFloat, + ComptimeInt, UndefinedLiteral, NullLiteral, Nullable, @@ -5713,10 +5713,10 @@ pub const TypeInfo = union(TypeId) { Pointer: Pointer, Array: Array, Struct: Struct, - FloatLiteral: void, - IntLiteral: void, + ComptimeFloat: void, + ComptimeInt: void, UndefinedLiteral: void, - NullLiteral: void, + Null: void, Nullable: Nullable, ErrorUnion: ErrorUnion, ErrorSet: ErrorSet, diff --git a/src/all_types.hpp b/src/all_types.hpp index d237eb00bb..bf635eae7c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1159,8 +1159,8 @@ enum TypeTableEntryId { TypeTableEntryIdPointer, TypeTableEntryIdArray, TypeTableEntryIdStruct, - TypeTableEntryIdNumLitFloat, - TypeTableEntryIdNumLitInt, + TypeTableEntryIdComptimeFloat, + TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefLit, TypeTableEntryIdNullLit, TypeTableEntryIdMaybe, diff --git a/src/analyze.cpp b/src/analyze.cpp index 31c0726459..21841d45b6 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -232,8 +232,8 @@ bool type_is_complete(TypeTableEntry *type_entry) { case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -268,8 +268,8 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) { case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -1333,8 +1333,8 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: @@ -1374,8 +1374,8 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdMetaType: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: @@ -1518,8 +1518,8 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c add_node_error(g, param_node->data.param_decl.type, buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -1607,8 +1607,8 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c buf_sprintf("return type '%s' not allowed", buf_ptr(&fn_type_id.return_type->name))); return g->builtin_types.entry_invalid; - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -3337,8 +3337,6 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdBlock: @@ -3347,6 +3345,8 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt add_node_error(g, source_node, buf_sprintf("variable of type '%s' not allowed", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdNamespace: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: @@ -3480,8 +3480,8 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable")); implicit_type = g->builtin_types.entry_invalid; } else if ((!is_const || linkage == VarLinkageExternal) && - (implicit_type->id == TypeTableEntryIdNumLitFloat || - implicit_type->id == TypeTableEntryIdNumLitInt)) + (implicit_type->id == TypeTableEntryIdComptimeFloat || + implicit_type->id == TypeTableEntryIdComptimeInt)) { add_node_error(g, source_node, buf_sprintf("unable to infer variable type")); implicit_type = g->builtin_types.entry_invalid; @@ -3730,8 +3730,8 @@ static bool is_container(TypeTableEntry *type_entry) { case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdArray: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -3779,8 +3779,8 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdArray: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -4283,8 +4283,8 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -4568,7 +4568,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case TypeTableEntryIdVoid: return (uint32_t)4149439618; case TypeTableEntryIdInt: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeInt: { uint32_t result = 1331471175; for (size_t i = 0; i < const_val->data.x_bigint.digit_count; i += 1) { @@ -4609,7 +4609,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { default: zig_unreachable(); } - case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdComptimeFloat: { float128_t f128 = bigfloat_to_f128(&const_val->data.x_bigfloat); uint32_t ints[4]; @@ -4754,8 +4754,8 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -4819,8 +4819,8 @@ static bool return_type_is_cacheable(TypeTableEntry *return_type) { case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -4930,8 +4930,8 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { case TypeTableEntryIdInvalid: case TypeTableEntryIdOpaque: zig_unreachable(); - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMetaType: @@ -5070,7 +5070,7 @@ ConstExprValue *create_const_signed(TypeTableEntry *type, int64_t x) { void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double value) { const_val->special = ConstValSpecialStatic; const_val->type = type; - if (type->id == TypeTableEntryIdNumLitFloat) { + if (type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_64(&const_val->data.x_bigfloat, value); } else if (type->id == TypeTableEntryIdFloat) { switch (type->data.floating.bit_count) { @@ -5350,10 +5350,10 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { default: zig_unreachable(); } - case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdComptimeFloat: return bigfloat_cmp(&a->data.x_bigfloat, &b->data.x_bigfloat) == CmpEQ; case TypeTableEntryIdInt: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeInt: return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ; case TypeTableEntryIdPointer: case TypeTableEntryIdFn: @@ -5514,7 +5514,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { case TypeTableEntryIdVoid: buf_appendf(buf, "{}"); return; - case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdComptimeFloat: bigfloat_append_buf(buf, &const_val->data.x_bigfloat); return; case TypeTableEntryIdFloat: @@ -5542,7 +5542,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { default: zig_unreachable(); } - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdInt: bigint_append_buf(buf, &const_val->data.x_bigint, 10); return; @@ -5761,8 +5761,8 @@ uint32_t type_id_hash(TypeId x) { case TypeTableEntryIdUnreachable: case TypeTableEntryIdFloat: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -5807,8 +5807,8 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdUnreachable: case TypeTableEntryIdFloat: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -5929,8 +5929,8 @@ static const TypeTableEntryId all_type_ids[] = { TypeTableEntryIdPointer, TypeTableEntryIdArray, TypeTableEntryIdStruct, - TypeTableEntryIdNumLitFloat, - TypeTableEntryIdNumLitInt, + TypeTableEntryIdComptimeFloat, + TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefLit, TypeTableEntryIdNullLit, TypeTableEntryIdMaybe, @@ -5980,9 +5980,9 @@ size_t type_id_index(TypeTableEntry *entry) { if (entry->data.structure.is_slice) return 25; return 8; - case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdComptimeFloat: return 9; - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeInt: return 10; case TypeTableEntryIdUndefLit: return 11; @@ -6038,10 +6038,10 @@ const char *type_id_name(TypeTableEntryId id) { return "Array"; case TypeTableEntryIdStruct: return "Struct"; - case TypeTableEntryIdNumLitFloat: - return "FloatLiteral"; - case TypeTableEntryIdNumLitInt: - return "IntLiteral"; + case TypeTableEntryIdComptimeFloat: + return "ComptimeFloat"; + case TypeTableEntryIdComptimeInt: + return "ComptimeInt"; case TypeTableEntryIdUndefLit: return "UndefinedLiteral"; case TypeTableEntryIdNullLit: diff --git a/src/codegen.cpp b/src/codegen.cpp index 49c93feaa5..dc915e766d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4916,8 +4916,8 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: @@ -5362,8 +5362,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -5604,7 +5604,7 @@ static void do_code_gen(CodeGen *g) { TldVar *tld_var = g->global_vars.at(i); VariableTableEntry *var = tld_var->var; - if (var->value->type->id == TypeTableEntryIdNumLitFloat) { + if (var->value->type->id == TypeTableEntryIdComptimeFloat) { // Generate debug info for it but that's it. ConstExprValue *const_val = var->value; assert(const_val->special != ConstValSpecialRuntime); @@ -5618,7 +5618,7 @@ static void do_code_gen(CodeGen *g) { continue; } - if (var->value->type->id == TypeTableEntryIdNumLitInt) { + if (var->value->type->id == TypeTableEntryIdComptimeInt) { // Generate debug info for it but that's it. ConstExprValue *const_val = var->value; assert(const_val->special != ConstValSpecialRuntime); @@ -6012,16 +6012,18 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_block = entry; } { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitFloat); - buf_init_from_str(&entry->name, "(float literal)"); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdComptimeFloat); + buf_init_from_str(&entry->name, "comptime_float"); entry->zero_bits = true; g->builtin_types.entry_num_lit_float = entry; + g->primitive_type_table.put(&entry->name, entry); } { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitInt); - buf_init_from_str(&entry->name, "(integer literal)"); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdComptimeInt); + buf_init_from_str(&entry->name, "comptime_int"); entry->zero_bits = true; g->builtin_types.entry_num_lit_int = entry; + g->primitive_type_table.put(&entry->name, entry); } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUndefLit); @@ -6495,8 +6497,8 @@ static void define_builtin_compile_vars(CodeGen *g) { " Slice: Slice,\n" " Array: Array,\n" " Struct: Struct,\n" - " FloatLiteral: void,\n" - " IntLiteral: void,\n" + " ComptimeFloat: void,\n" + " ComptimeInt: void,\n" " UndefinedLiteral: void,\n" " NullLiteral: void,\n" " Nullable: Nullable,\n" @@ -7070,8 +7072,8 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -7255,8 +7257,8 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf case TypeTableEntryIdBoundFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdArgTuple: @@ -7407,8 +7409,8 @@ static void gen_h_file(CodeGen *g) { case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdArray: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: diff --git a/src/ir.cpp b/src/ir.cpp index 5cea04ea55..2819ef5b0e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6945,14 +6945,14 @@ static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *so } static bool const_val_fits_in_num_lit(ConstExprValue *const_val, TypeTableEntry *num_lit_type) { - return ((num_lit_type->id == TypeTableEntryIdNumLitFloat && - (const_val->type->id == TypeTableEntryIdFloat || const_val->type->id == TypeTableEntryIdNumLitFloat)) || - (num_lit_type->id == TypeTableEntryIdNumLitInt && - (const_val->type->id == TypeTableEntryIdInt || const_val->type->id == TypeTableEntryIdNumLitInt))); + return ((num_lit_type->id == TypeTableEntryIdComptimeFloat && + (const_val->type->id == TypeTableEntryIdFloat || const_val->type->id == TypeTableEntryIdComptimeFloat)) || + (num_lit_type->id == TypeTableEntryIdComptimeInt && + (const_val->type->id == TypeTableEntryIdInt || const_val->type->id == TypeTableEntryIdComptimeInt))); } static bool float_has_fraction(ConstExprValue *const_val) { - if (const_val->type->id == TypeTableEntryIdNumLitFloat) { + if (const_val->type->id == TypeTableEntryIdComptimeFloat) { return bigfloat_has_fraction(&const_val->data.x_bigfloat); } else if (const_val->type->id == TypeTableEntryIdFloat) { switch (const_val->type->data.floating.bit_count) { @@ -6975,7 +6975,7 @@ static bool float_has_fraction(ConstExprValue *const_val) { } static void float_append_buf(Buf *buf, ConstExprValue *const_val) { - if (const_val->type->id == TypeTableEntryIdNumLitFloat) { + if (const_val->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_append_buf(buf, &const_val->data.x_bigfloat); } else if (const_val->type->id == TypeTableEntryIdFloat) { switch (const_val->type->data.floating.bit_count) { @@ -7010,7 +7010,7 @@ static void float_append_buf(Buf *buf, ConstExprValue *const_val) { } static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) { - if (const_val->type->id == TypeTableEntryIdNumLitFloat) { + if (const_val->type->id == TypeTableEntryIdComptimeFloat) { bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat); } else if (const_val->type->id == TypeTableEntryIdFloat) { switch (const_val->type->data.floating.bit_count) { @@ -7046,7 +7046,7 @@ static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) { } static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { - if (dest_val->type->id == TypeTableEntryIdNumLitFloat) { + if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { @@ -7068,7 +7068,7 @@ static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { } static void float_init_f32(ConstExprValue *dest_val, float x) { - if (dest_val->type->id == TypeTableEntryIdNumLitFloat) { + if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_32(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { @@ -7094,7 +7094,7 @@ static void float_init_f32(ConstExprValue *dest_val, float x) { } static void float_init_f64(ConstExprValue *dest_val, double x) { - if (dest_val->type->id == TypeTableEntryIdNumLitFloat) { + if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_64(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { @@ -7120,7 +7120,7 @@ static void float_init_f64(ConstExprValue *dest_val, double x) { } static void float_init_f128(ConstExprValue *dest_val, float128_t x) { - if (dest_val->type->id == TypeTableEntryIdNumLitFloat) { + if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_128(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { @@ -7150,7 +7150,7 @@ static void float_init_f128(ConstExprValue *dest_val, float128_t x) { } static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) { - if (src_val->type->id == TypeTableEntryIdNumLitFloat) { + if (src_val->type->id == TypeTableEntryIdComptimeFloat) { float_init_bigfloat(dest_val, &src_val->data.x_bigfloat); } else if (src_val->type->id == TypeTableEntryIdFloat) { switch (src_val->type->data.floating.bit_count) { @@ -7173,7 +7173,7 @@ static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7210,7 +7210,7 @@ static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) { } static Cmp float_cmp_zero(ConstExprValue *op) { - if (op->type->id == TypeTableEntryIdNumLitFloat) { + if (op->type->id == TypeTableEntryIdComptimeFloat) { return bigfloat_cmp_zero(&op->data.x_bigfloat); } else if (op->type->id == TypeTableEntryIdFloat) { switch (op->type->data.floating.bit_count) { @@ -7251,7 +7251,7 @@ static Cmp float_cmp_zero(ConstExprValue *op) { static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7275,7 +7275,7 @@ static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7299,7 +7299,7 @@ static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7323,7 +7323,7 @@ static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7347,7 +7347,7 @@ static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7382,7 +7382,7 @@ static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstE static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7407,7 +7407,7 @@ static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstE static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7431,7 +7431,7 @@ static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7456,7 +7456,7 @@ static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal static void float_negate(ConstExprValue *out_val, ConstExprValue *op) { out_val->type = op->type; - if (op->type->id == TypeTableEntryIdNumLitFloat) { + if (op->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat); } else if (op->type->id == TypeTableEntryIdFloat) { switch (op->type->data.floating.bit_count) { @@ -7530,9 +7530,9 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc assert(const_val->special != ConstValSpecialRuntime); bool const_val_is_int = (const_val->type->id == TypeTableEntryIdInt || - const_val->type->id == TypeTableEntryIdNumLitInt); + const_val->type->id == TypeTableEntryIdComptimeInt); bool const_val_is_float = (const_val->type->id == TypeTableEntryIdFloat || - const_val->type->id == TypeTableEntryIdNumLitFloat); + const_val->type->id == TypeTableEntryIdComptimeFloat); if (other_type->id == TypeTableEntryIdFloat) { return true; } else if (other_type->id == TypeTableEntryIdInt && const_val_is_int) { @@ -7576,7 +7576,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc return true; } } - if (explicit_cast && (other_type->id == TypeTableEntryIdInt || other_type->id == TypeTableEntryIdNumLitInt) && + if (explicit_cast && (other_type->id == TypeTableEntryIdInt || other_type->id == TypeTableEntryIdComptimeInt) && const_val_is_float) { if (float_has_fraction(const_val)) { @@ -7589,7 +7589,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc buf_ptr(&other_type->name))); return false; } else { - if (other_type->id == TypeTableEntryIdNumLitInt) { + if (other_type->id == TypeTableEntryIdComptimeInt) { return true; } else { BigInt bigint; @@ -8078,8 +8078,8 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit number literal to typed number // implicit number literal to &const integer - if (actual_type->id == TypeTableEntryIdNumLitFloat || - actual_type->id == TypeTableEntryIdNumLitInt) + if (actual_type->id == TypeTableEntryIdComptimeFloat || + actual_type->id == TypeTableEntryIdComptimeInt) { if (expected_type->id == TypeTableEntryIdPointer && expected_type->data.pointer.is_const) @@ -8099,9 +8099,9 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit typed number to integer or float literal. // works when the number is known if (value->value.special == ConstValSpecialStatic) { - if (actual_type->id == TypeTableEntryIdInt && expected_type->id == TypeTableEntryIdNumLitInt) { + if (actual_type->id == TypeTableEntryIdInt && expected_type->id == TypeTableEntryIdComptimeInt) { return ImplicitCastMatchResultYes; - } else if (actual_type->id == TypeTableEntryIdFloat && expected_type->id == TypeTableEntryIdNumLitFloat) { + } else if (actual_type->id == TypeTableEntryIdFloat && expected_type->id == TypeTableEntryIdComptimeFloat) { return ImplicitCastMatchResultYes; } } @@ -8555,8 +8555,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } - if (prev_type->id == TypeTableEntryIdNumLitInt || - prev_type->id == TypeTableEntryIdNumLitFloat) + if (prev_type->id == TypeTableEntryIdComptimeInt || + prev_type->id == TypeTableEntryIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, prev_inst, cur_type, false)) { prev_inst = cur_inst; @@ -8566,8 +8566,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } - if (cur_type->id == TypeTableEntryIdNumLitInt || - cur_type->id == TypeTableEntryIdNumLitFloat) + if (cur_type->id == TypeTableEntryIdComptimeInt || + cur_type->id == TypeTableEntryIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type, false)) { continue; @@ -8671,8 +8671,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else if (expected_type != nullptr && expected_type->id == TypeTableEntryIdErrorUnion) { return get_error_union_type(ira->codegen, err_set_type, expected_type->data.error_union.payload_type); } else { - if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || - prev_inst->value.type->id == TypeTableEntryIdNumLitFloat) + if (prev_inst->value.type->id == TypeTableEntryIdComptimeInt || + prev_inst->value.type->id == TypeTableEntryIdComptimeFloat) { ir_add_error_node(ira, source_node, buf_sprintf("unable to make error union out of number literal")); @@ -8686,8 +8686,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } } else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNullLit) { - if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || - prev_inst->value.type->id == TypeTableEntryIdNumLitFloat) + if (prev_inst->value.type->id == TypeTableEntryIdComptimeInt || + prev_inst->value.type->id == TypeTableEntryIdComptimeFloat) { ir_add_error_node(ira, source_node, buf_sprintf("unable to make maybe out of number literal")); @@ -8743,7 +8743,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, break; } case CastOpNumLitToConcrete: - if (other_val->type->id == TypeTableEntryIdNumLitFloat) { + if (other_val->type->id == TypeTableEntryIdComptimeFloat) { assert(new_type->id == TypeTableEntryIdFloat); switch (new_type->data.floating.bit_count) { case 32: @@ -8758,7 +8758,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, default: zig_unreachable(); } - } else if (other_val->type->id == TypeTableEntryIdNumLitInt) { + } else if (other_val->type->id == TypeTableEntryIdComptimeInt) { bigint_init_bigint(&const_val->data.x_bigint, &other_val->data.x_bigint); } else { zig_unreachable(); @@ -9601,9 +9601,9 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - if (wanted_type->id == TypeTableEntryIdNumLitFloat) { + if (wanted_type->id == TypeTableEntryIdComptimeFloat) { float_init_float(&result->value, val); - } else if (wanted_type->id == TypeTableEntryIdNumLitInt) { + } else if (wanted_type->id == TypeTableEntryIdComptimeInt) { bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); } else { zig_unreachable(); @@ -9978,8 +9978,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); - } else if (actual_type->id == TypeTableEntryIdNumLitInt || - actual_type->id == TypeTableEntryIdNumLitFloat) + } else if (actual_type->id == TypeTableEntryIdComptimeInt || + actual_type->id == TypeTableEntryIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); @@ -10013,8 +10013,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (wanted_type->id == TypeTableEntryIdErrorUnion) { if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); - } else if (actual_type->id == TypeTableEntryIdNumLitInt || - actual_type->id == TypeTableEntryIdNumLitFloat) + } else if (actual_type->id == TypeTableEntryIdComptimeInt || + actual_type->id == TypeTableEntryIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); @@ -10062,8 +10062,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk || actual_type->id == TypeTableEntryIdNullLit || - actual_type->id == TypeTableEntryIdNumLitInt || - actual_type->id == TypeTableEntryIdNumLitFloat) + actual_type->id == TypeTableEntryIdComptimeInt || + actual_type->id == TypeTableEntryIdComptimeFloat) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); if (type_is_invalid(cast1->value.type)) @@ -10079,8 +10079,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from number literal to another type // explicit cast from number literal to &const integer - if (actual_type->id == TypeTableEntryIdNumLitFloat || - actual_type->id == TypeTableEntryIdNumLitInt) + if (actual_type->id == TypeTableEntryIdComptimeFloat || + actual_type->id == TypeTableEntryIdComptimeInt) { ensure_complete_type(ira->codegen, wanted_type); if (type_is_invalid(wanted_type)) @@ -10109,9 +10109,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return cast2; } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { CastOp op; - if ((actual_type->id == TypeTableEntryIdNumLitFloat && + if ((actual_type->id == TypeTableEntryIdComptimeFloat && wanted_type->id == TypeTableEntryIdFloat) || - (actual_type->id == TypeTableEntryIdNumLitInt && + (actual_type->id == TypeTableEntryIdComptimeInt && wanted_type->id == TypeTableEntryIdInt)) { op = CastOpNumLitToConcrete; @@ -10131,8 +10131,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from typed number to integer or float literal. // works when the number is known at compile time if (instr_is_comptime(value) && - ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdNumLitInt) || - (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdNumLitFloat))) + ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) || + (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdComptimeFloat))) { return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); } @@ -10759,8 +10759,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdInvalid: zig_unreachable(); // handled above - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: break; @@ -10818,10 +10818,10 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type); if (one_possible_value || (value_is_comptime(op1_val) && value_is_comptime(op2_val))) { bool answer; - if (resolved_type->id == TypeTableEntryIdNumLitFloat || resolved_type->id == TypeTableEntryIdFloat) { + if (resolved_type->id == TypeTableEntryIdComptimeFloat || resolved_type->id == TypeTableEntryIdFloat) { Cmp cmp_result = float_cmp(op1_val, op2_val); answer = resolve_cmp_op_id(op_id, cmp_result); - } else if (resolved_type->id == TypeTableEntryIdNumLitInt || resolved_type->id == TypeTableEntryIdInt) { + } else if (resolved_type->id == TypeTableEntryIdComptimeInt || resolved_type->id == TypeTableEntryIdInt) { Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint); answer = resolve_cmp_op_id(op_id, cmp_result); } else { @@ -10885,12 +10885,12 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val, bool is_int; bool is_float; Cmp op2_zcmp; - if (type_entry->id == TypeTableEntryIdInt || type_entry->id == TypeTableEntryIdNumLitInt) { + if (type_entry->id == TypeTableEntryIdInt || type_entry->id == TypeTableEntryIdComptimeInt) { is_int = true; is_float = false; op2_zcmp = bigint_cmp_zero(&op2_val->data.x_bigint); } else if (type_entry->id == TypeTableEntryIdFloat || - type_entry->id == TypeTableEntryIdNumLitFloat) + type_entry->id == TypeTableEntryIdComptimeFloat) { is_int = false; is_float = true; @@ -11064,7 +11064,7 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * if (type_is_invalid(op1->value.type)) return ira->codegen->builtin_types.entry_invalid; - if (op1->value.type->id != TypeTableEntryIdInt && op1->value.type->id != TypeTableEntryIdNumLitInt) { + if (op1->value.type->id != TypeTableEntryIdInt && op1->value.type->id != TypeTableEntryIdComptimeInt) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("bit shifting operation expected integer type, found '%s'", buf_ptr(&op1->value.type->name))); @@ -11077,7 +11077,7 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * IrInstruction *casted_op2; IrBinOp op_id = bin_op_instruction->op_id; - if (op1->value.type->id == TypeTableEntryIdNumLitInt) { + if (op1->value.type->id == TypeTableEntryIdComptimeInt) { casted_op2 = op2; if (op_id == IrBinOpBitShiftLeftLossy) { @@ -11122,7 +11122,7 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * ir_num_lit_fits_in_other_type(ira, result_instruction, op1->value.type, false); return op1->value.type; - } else if (op1->value.type->id == TypeTableEntryIdNumLitInt) { + } else if (op1->value.type->id == TypeTableEntryIdComptimeInt) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known")); return ira->codegen->builtin_types.entry_invalid; @@ -11158,15 +11158,15 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (type_is_invalid(resolved_type)) return resolved_type; - bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdNumLitInt; - bool is_float = resolved_type->id == TypeTableEntryIdFloat || resolved_type->id == TypeTableEntryIdNumLitFloat; + bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdComptimeInt; + bool is_float = resolved_type->id == TypeTableEntryIdFloat || resolved_type->id == TypeTableEntryIdComptimeFloat; bool is_signed_div = ( (resolved_type->id == TypeTableEntryIdInt && resolved_type->data.integral.is_signed) || resolved_type->id == TypeTableEntryIdFloat || - (resolved_type->id == TypeTableEntryIdNumLitFloat && + (resolved_type->id == TypeTableEntryIdComptimeFloat && ((bigfloat_cmp_zero(&op1->value.data.x_bigfloat) != CmpGT) != (bigfloat_cmp_zero(&op2->value.data.x_bigfloat) != CmpGT))) || - (resolved_type->id == TypeTableEntryIdNumLitInt && + (resolved_type->id == TypeTableEntryIdComptimeInt && ((bigint_cmp_zero(&op1->value.data.x_bigint) != CmpGT) != (bigint_cmp_zero(&op2->value.data.x_bigint) != CmpGT))) ); @@ -11267,7 +11267,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp return ira->codegen->builtin_types.entry_invalid; } - if (resolved_type->id == TypeTableEntryIdNumLitInt) { + if (resolved_type->id == TypeTableEntryIdComptimeInt) { if (op_id == IrBinOpAddWrap) { op_id = IrBinOpAdd; } else if (op_id == IrBinOpSubWrap) { @@ -11641,8 +11641,8 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { case TypeTableEntryIdFn: case TypeTableEntryIdPromise: return VarClassRequiredAny; - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdBlock: case TypeTableEntryIdNullLit: @@ -11910,8 +11910,8 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -11934,8 +11934,8 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -12149,7 +12149,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod } bool comptime_arg = param_decl_node->data.param_decl.is_inline || - casted_arg->value.type->id == TypeTableEntryIdNumLitInt || casted_arg->value.type->id == TypeTableEntryIdNumLitFloat; + casted_arg->value.type->id == TypeTableEntryIdComptimeInt || casted_arg->value.type->id == TypeTableEntryIdComptimeFloat; ConstExprValue *arg_val; @@ -12174,8 +12174,8 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod var->shadowable = !comptime_arg; *next_proto_i += 1; - } else if (casted_arg->value.type->id == TypeTableEntryIdNumLitInt || - casted_arg->value.type->id == TypeTableEntryIdNumLitFloat) + } else if (casted_arg->value.type->id == TypeTableEntryIdComptimeInt || + casted_arg->value.type->id == TypeTableEntryIdComptimeFloat) { ir_add_error(ira, casted_arg, buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/ziglang/zig/issues/557")); @@ -12898,8 +12898,8 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -12935,10 +12935,10 @@ static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un bool is_wrap_op = (un_op_instruction->op_id == IrUnOpNegationWrap); - bool is_float = (expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdNumLitFloat); + bool is_float = (expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdComptimeFloat); if ((expr_type->id == TypeTableEntryIdInt && expr_type->data.integral.is_signed) || - expr_type->id == TypeTableEntryIdNumLitInt || (is_float && !is_wrap_op)) + expr_type->id == TypeTableEntryIdComptimeInt || (is_float && !is_wrap_op)) { if (instr_is_comptime(value)) { ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad); @@ -12954,7 +12954,7 @@ static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un } else { bigint_negate(&out_val->data.x_bigint, &target_const_val->data.x_bigint); } - if (is_wrap_op || is_float || expr_type->id == TypeTableEntryIdNumLitInt) { + if (is_wrap_op || is_float || expr_type->id == TypeTableEntryIdComptimeInt) { return expr_type; } @@ -13150,8 +13150,8 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP if (type_is_invalid(resolved_type)) return resolved_type; - if (resolved_type->id == TypeTableEntryIdNumLitFloat || - resolved_type->id == TypeTableEntryIdNumLitInt || + if (resolved_type->id == TypeTableEntryIdComptimeFloat || + resolved_type->id == TypeTableEntryIdComptimeInt || resolved_type->id == TypeTableEntryIdNullLit || resolved_type->id == TypeTableEntryIdUndefLit) { @@ -14213,8 +14213,8 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); // handled above - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -14495,8 +14495,8 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -14603,8 +14603,8 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -14659,8 +14659,8 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdBlock: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdBoundFn: case TypeTableEntryIdMetaType: case TypeTableEntryIdNamespace: @@ -15020,8 +15020,8 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdPointer: case TypeTableEntryIdPromise: case TypeTableEntryIdFn: @@ -15618,8 +15618,8 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ case TypeTableEntryIdPromise: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -16280,8 +16280,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -17143,7 +17143,7 @@ static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_invalid; if (dest_type->id != TypeTableEntryIdInt && - dest_type->id != TypeTableEntryIdNumLitInt) + dest_type->id != TypeTableEntryIdComptimeInt) { ir_add_error(ira, dest_type_value, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->builtin_types.entry_invalid; @@ -17155,7 +17155,7 @@ static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_invalid; if (src_type->id != TypeTableEntryIdInt && - src_type->id != TypeTableEntryIdNumLitInt) + src_type->id != TypeTableEntryIdComptimeInt) { ir_add_error(ira, target, buf_sprintf("expected integer type, found '%s'", buf_ptr(&src_type->name))); return ira->codegen->builtin_types.entry_invalid; @@ -17876,8 +17876,8 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -18377,8 +18377,8 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (!end_val) return ira->codegen->builtin_types.entry_invalid; - assert(start_val->type->id == TypeTableEntryIdInt || start_val->type->id == TypeTableEntryIdNumLitInt); - assert(end_val->type->id == TypeTableEntryIdInt || end_val->type->id == TypeTableEntryIdNumLitInt); + assert(start_val->type->id == TypeTableEntryIdInt || start_val->type->id == TypeTableEntryIdComptimeInt); + assert(end_val->type->id == TypeTableEntryIdInt || end_val->type->id == TypeTableEntryIdComptimeInt); AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, start_value->source_node); if (prev_node != nullptr) { @@ -18610,8 +18610,8 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdPromise: @@ -18677,8 +18677,8 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdPromise: @@ -18758,8 +18758,8 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: ir_add_error(ira, dest_type_value, @@ -18784,8 +18784,8 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: ir_add_error(ira, dest_type_value, @@ -19560,7 +19560,7 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction if (type_is_invalid(op->value.type)) return ira->codegen->builtin_types.entry_invalid; - bool ok_type = float_type->id == TypeTableEntryIdNumLitFloat || float_type->id == TypeTableEntryIdFloat; + bool ok_type = float_type->id == TypeTableEntryIdComptimeFloat || float_type->id == TypeTableEntryIdFloat; if (!ok_type) { ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name))); return ira->codegen->builtin_types.entry_invalid; @@ -19577,7 +19577,7 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - if (float_type->id == TypeTableEntryIdNumLitFloat) { + if (float_type->id == TypeTableEntryIdComptimeFloat) { bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat); } else if (float_type->id == TypeTableEntryIdFloat) { switch (float_type->data.floating.bit_count) { diff --git a/std/math/ln.zig b/std/math/ln.zig index 263e5955cb..3fd75977b9 100644 --- a/std/math/ln.zig +++ b/std/math/ln.zig @@ -14,7 +14,7 @@ const TypeId = builtin.TypeId; pub fn ln(x: var) @typeOf(x) { const T = @typeOf(x); switch (@typeId(T)) { - TypeId.FloatLiteral => { + TypeId.ComptimeFloat => { return @typeOf(1.0)(ln_64(x)); }, TypeId.Float => { @@ -24,7 +24,7 @@ pub fn ln(x: var) @typeOf(x) { else => @compileError("ln not implemented for " ++ @typeName(T)), }; }, - TypeId.IntLiteral => { + TypeId.ComptimeInt => { return @typeOf(1)(math.floor(ln_64(f64(x)))); }, TypeId.Int => { diff --git a/std/math/log.zig b/std/math/log.zig index 1cba1138db..2c876081d8 100644 --- a/std/math/log.zig +++ b/std/math/log.zig @@ -9,15 +9,15 @@ pub fn log(comptime T: type, base: T, x: T) T { return math.log2(x); } else if (base == 10) { return math.log10(x); - } else if ((@typeId(T) == TypeId.Float or @typeId(T) == TypeId.FloatLiteral) and base == math.e) { + } else if ((@typeId(T) == TypeId.Float or @typeId(T) == TypeId.ComptimeFloat) and base == math.e) { return math.ln(x); } switch (@typeId(T)) { - TypeId.FloatLiteral => { + TypeId.ComptimeFloat => { return @typeOf(1.0)(math.ln(f64(x)) / math.ln(f64(base))); }, - TypeId.IntLiteral => { + TypeId.ComptimeInt => { return @typeOf(1)(math.floor(math.ln(f64(x)) / math.ln(f64(base)))); }, builtin.TypeId.Int => { diff --git a/std/math/log10.zig b/std/math/log10.zig index d9fa1dcb02..c444add9ac 100644 --- a/std/math/log10.zig +++ b/std/math/log10.zig @@ -14,7 +14,7 @@ const TypeId = builtin.TypeId; pub fn log10(x: var) @typeOf(x) { const T = @typeOf(x); switch (@typeId(T)) { - TypeId.FloatLiteral => { + TypeId.ComptimeFloat => { return @typeOf(1.0)(log10_64(x)); }, TypeId.Float => { @@ -24,7 +24,7 @@ pub fn log10(x: var) @typeOf(x) { else => @compileError("log10 not implemented for " ++ @typeName(T)), }; }, - TypeId.IntLiteral => { + TypeId.ComptimeInt => { return @typeOf(1)(math.floor(log10_64(f64(x)))); }, TypeId.Int => { diff --git a/std/math/log2.zig b/std/math/log2.zig index 22cc8082b3..2530519941 100644 --- a/std/math/log2.zig +++ b/std/math/log2.zig @@ -14,7 +14,7 @@ const TypeId = builtin.TypeId; pub fn log2(x: var) @typeOf(x) { const T = @typeOf(x); switch (@typeId(T)) { - TypeId.FloatLiteral => { + TypeId.ComptimeFloat => { return @typeOf(1.0)(log2_64(x)); }, TypeId.Float => { @@ -24,7 +24,7 @@ pub fn log2(x: var) @typeOf(x) { else => @compileError("log2 not implemented for " ++ @typeName(T)), }; }, - TypeId.IntLiteral => comptime { + TypeId.ComptimeInt => comptime { var result = 0; var x_shifted = x; while (b: { diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig index 982bd28b72..7a3ddb3b96 100644 --- a/std/math/sqrt.zig +++ b/std/math/sqrt.zig @@ -14,9 +14,9 @@ const TypeId = builtin.TypeId; pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) { const T = @typeOf(x); switch (@typeId(T)) { - TypeId.FloatLiteral => return T(@sqrt(f64, x)), // TODO upgrade to f128 + TypeId.ComptimeFloat => return T(@sqrt(f64, x)), // TODO upgrade to f128 TypeId.Float => return @sqrt(T, x), - TypeId.IntLiteral => comptime { + TypeId.ComptimeInt => comptime { if (x > @maxValue(u128)) { @compileError("sqrt not implemented for comptime_int greater than 128 bits"); } diff --git a/test/cases/math.zig b/test/cases/math.zig index 0c18293dd5..0bf99cff0e 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -329,14 +329,14 @@ fn testShrExact(x: u8) void { assert(shifted == 0b00101101); } -test "big number addition" { +test "comptime_int addition" { comptime { assert(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); assert(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); } } -test "big number multiplication" { +test "comptime_int multiplication" { comptime { assert( 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, @@ -347,13 +347,13 @@ test "big number multiplication" { } } -test "big number shifting" { +test "comptime_int shifting" { comptime { assert((u128(1) << 127) == 0x80000000000000000000000000000000); } } -test "big number multi-limb shift and mask" { +test "comptime_int multi-limb shift and mask" { comptime { var a = 0xefffffffa0000001eeeeeeefaaaaaaab; @@ -370,7 +370,7 @@ test "big number multi-limb shift and mask" { } } -test "big number multi-limb partial shift right" { +test "comptime_int multi-limb partial shift right" { comptime { var a = 0x1ffffffffeeeeeeee; a >>= 16; @@ -391,7 +391,7 @@ fn test_xor() void { assert(0xFF ^ 0xFF == 0x00); } -test "big number xor" { +test "comptime_int xor" { comptime { assert(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); assert(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); @@ -449,3 +449,15 @@ test "@sqrt" { fn testSqrt(comptime T: type, x: T) void { assert(@sqrt(T, x * x) == x); } + +test "comptime_int param and return" { + const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); + assert(a == 137114567242441932203689521744947848950); + + const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); + assert(b == 985095453608931032642182098849559179469148836107390954364380); +} + +fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { + return a + b; +} diff --git a/test/cases/misc.zig b/test/cases/misc.zig index e007ec4c46..1821e29a20 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -501,8 +501,8 @@ test "@typeId" { assert(@typeId(*f32) == Tid.Pointer); assert(@typeId([2]u8) == Tid.Array); assert(@typeId(AStruct) == Tid.Struct); - assert(@typeId(@typeOf(1)) == Tid.IntLiteral); - assert(@typeId(@typeOf(1.0)) == Tid.FloatLiteral); + assert(@typeId(@typeOf(1)) == Tid.ComptimeInt); + assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); assert(@typeId(@typeOf(undefined)) == Tid.UndefinedLiteral); assert(@typeId(@typeOf(null)) == Tid.NullLiteral); assert(@typeId(?i32) == Tid.Nullable); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 17136e150f..e264d57b5e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1539,7 +1539,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn foo() *const i32 { return y; } \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:3:30: error: expected type '*const i32', found '*const (integer literal)'", + ".tmp_source.zig:3:30: error: expected type '*const i32', found '*const comptime_int'", ); cases.add( @@ -1555,7 +1555,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\const x = 2 == 2.0; \\export fn entry() usize { return @sizeOf(@typeOf(x)); } , - ".tmp_source.zig:1:11: error: integer value 2 cannot be implicitly casted to type '(float literal)'", + ".tmp_source.zig:1:11: error: integer value 2 cannot be implicitly casted to type 'comptime_float'", ); cases.add( @@ -2189,7 +2189,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(block_aligned_stuff)); } , - ".tmp_source.zig:3:60: error: unable to perform binary not operation on type '(integer literal)'", + ".tmp_source.zig:3:60: error: unable to perform binary not operation on type 'comptime_int'", ); cases.addCase(x: { @@ -3269,10 +3269,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ fn bar(self: *const Foo) void {} \\}; , - ".tmp_source.zig:4:4: error: variable of type '*(integer literal)' must be const or comptime", + ".tmp_source.zig:4:4: error: variable of type '*comptime_int' must be const or comptime", ".tmp_source.zig:7:4: error: variable of type '(undefined)' must be const or comptime", - ".tmp_source.zig:8:4: error: variable of type '(integer literal)' must be const or comptime", - ".tmp_source.zig:9:4: error: variable of type '(float literal)' must be const or comptime", + ".tmp_source.zig:8:4: error: variable of type 'comptime_int' must be const or comptime", + ".tmp_source.zig:9:4: error: variable of type 'comptime_float' must be const or comptime", ".tmp_source.zig:10:4: error: variable of type '(block)' must be const or comptime", ".tmp_source.zig:11:4: error: variable of type '(null)' must be const or comptime", ".tmp_source.zig:12:4: error: variable of type 'Opaque' must be const or comptime", -- cgit v1.2.3 From 236c680f6bae490fddab4935892bd75240176d0b Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Tue, 5 Jun 2018 11:30:01 +0200 Subject: Removed NullLiteral to Null --- doc/langref.html.in | 6 ++--- src/all_types.hpp | 2 +- src/analyze.cpp | 46 ++++++++++++++++++------------------- src/codegen.cpp | 14 ++++++------ src/ir.cpp | 66 ++++++++++++++++++++++++++--------------------------- test/cases/misc.zig | 2 +- 6 files changed, 68 insertions(+), 68 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 0689baa6f9..70f11c0e2b 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4896,7 +4896,7 @@ pub const TypeId = enum { ComptimeFloat, ComptimeInt, UndefinedLiteral, - NullLiteral, + Null, Nullable, ErrorUnion, Error, @@ -4930,7 +4930,7 @@ pub const TypeInfo = union(TypeId) { ComptimeFloat: void, ComptimeInt: void, UndefinedLiteral: void, - NullLiteral: void, + Null: void, Nullable: Nullable, ErrorUnion: ErrorUnion, ErrorSet: ErrorSet, @@ -5688,7 +5688,7 @@ pub const TypeId = enum { ComptimeFloat, ComptimeInt, UndefinedLiteral, - NullLiteral, + Null, Nullable, ErrorUnion, ErrorSet, diff --git a/src/all_types.hpp b/src/all_types.hpp index bf635eae7c..6b30a1155d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1162,7 +1162,7 @@ enum TypeTableEntryId { TypeTableEntryIdComptimeFloat, TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefLit, - TypeTableEntryIdNullLit, + TypeTableEntryIdNull, TypeTableEntryIdMaybe, TypeTableEntryIdErrorUnion, TypeTableEntryIdErrorSet, diff --git a/src/analyze.cpp b/src/analyze.cpp index 21841d45b6..a605cb3a7f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -235,7 +235,7 @@ bool type_is_complete(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -271,7 +271,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -1336,7 +1336,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: @@ -1377,7 +1377,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: @@ -1512,7 +1512,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: add_node_error(g, param_node->data.param_decl.type, @@ -1600,7 +1600,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c zig_unreachable(); case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: add_node_error(g, fn_proto->return_type, @@ -3338,7 +3338,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: @@ -3485,7 +3485,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { { add_node_error(g, source_node, buf_sprintf("unable to infer variable type")); implicit_type = g->builtin_types.entry_invalid; - } else if (implicit_type->id == TypeTableEntryIdNullLit) { + } else if (implicit_type->id == TypeTableEntryIdNull) { add_node_error(g, source_node, buf_sprintf("unable to infer variable type")); implicit_type = g->builtin_types.entry_invalid; } else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) { @@ -3733,7 +3733,7 @@ static bool is_container(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -3782,7 +3782,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -4286,7 +4286,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -4674,7 +4674,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { return 223048345; case TypeTableEntryIdUndefLit: return 162837799; - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: return 844854567; case TypeTableEntryIdArray: // TODO better hashing algorithm @@ -4757,7 +4757,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: case TypeTableEntryIdFn: @@ -4822,7 +4822,7 @@ static bool return_type_is_cacheable(TypeTableEntry *return_type) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: case TypeTableEntryIdFn: @@ -4933,7 +4933,7 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMetaType: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -5412,7 +5412,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return true; case TypeTableEntryIdUndefLit: zig_panic("TODO"); - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: zig_panic("TODO"); case TypeTableEntryIdMaybe: if (a->data.x_maybe == nullptr || b->data.x_maybe == nullptr) { @@ -5646,7 +5646,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "}"); return; } - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: { buf_appendf(buf, "null"); return; @@ -5764,7 +5764,7 @@ uint32_t type_id_hash(TypeId x) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -5810,7 +5810,7 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdPromise: case TypeTableEntryIdErrorSet: @@ -5932,7 +5932,7 @@ static const TypeTableEntryId all_type_ids[] = { TypeTableEntryIdComptimeFloat, TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefLit, - TypeTableEntryIdNullLit, + TypeTableEntryIdNull, TypeTableEntryIdMaybe, TypeTableEntryIdErrorUnion, TypeTableEntryIdErrorSet, @@ -5986,7 +5986,7 @@ size_t type_id_index(TypeTableEntry *entry) { return 10; case TypeTableEntryIdUndefLit: return 11; - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: return 12; case TypeTableEntryIdMaybe: return 13; @@ -6044,8 +6044,8 @@ const char *type_id_name(TypeTableEntryId id) { return "ComptimeInt"; case TypeTableEntryIdUndefLit: return "UndefinedLiteral"; - case TypeTableEntryIdNullLit: - return "NullLiteral"; + case TypeTableEntryIdNull: + return "Null"; case TypeTableEntryIdMaybe: return "Nullable"; case TypeTableEntryIdErrorUnion: diff --git a/src/codegen.cpp b/src/codegen.cpp index dc915e766d..3177a2491f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4919,7 +4919,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: @@ -5365,7 +5365,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -6032,7 +6032,7 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_undef = entry; } { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNullLit); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNull); buf_init_from_str(&entry->name, "(null)"); entry->zero_bits = true; g->builtin_types.entry_null = entry; @@ -6500,7 +6500,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " ComptimeFloat: void,\n" " ComptimeInt: void,\n" " UndefinedLiteral: void,\n" - " NullLiteral: void,\n" + " Null: void,\n" " Nullable: Nullable,\n" " ErrorUnion: ErrorUnion,\n" " ErrorSet: ErrorSet,\n" @@ -7075,7 +7075,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -7260,7 +7260,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdPromise: zig_unreachable(); @@ -7413,7 +7413,7 @@ static void gen_h_file(CodeGen *g) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdArray: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: diff --git a/src/ir.cpp b/src/ir.cpp index 2819ef5b0e..2fbc72309a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7960,7 +7960,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit conversion from null literal to maybe type if (expected_type->id == TypeTableEntryIdMaybe && - actual_type->id == TypeTableEntryIdNullLit) + actual_type->id == TypeTableEntryIdNull) { return ImplicitCastMatchResultYes; } @@ -8190,7 +8190,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } - bool any_are_null = (prev_inst->value.type->id == TypeTableEntryIdNullLit); + bool any_are_null = (prev_inst->value.type->id == TypeTableEntryIdNull); bool convert_to_const_slice = false; for (size_t i = 1; i < instruction_count; i += 1) { IrInstruction *cur_inst = instructions[i]; @@ -8469,12 +8469,12 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } - if (prev_type->id == TypeTableEntryIdNullLit) { + if (prev_type->id == TypeTableEntryIdNull) { prev_inst = cur_inst; continue; } - if (cur_type->id == TypeTableEntryIdNullLit) { + if (cur_type->id == TypeTableEntryIdNull) { any_are_null = true; continue; } @@ -8677,7 +8677,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod ir_add_error_node(ira, source_node, buf_sprintf("unable to make error union out of number literal")); return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value.type->id == TypeTableEntryIdNullLit) { + } else if (prev_inst->value.type->id == TypeTableEntryIdNull) { ir_add_error_node(ira, source_node, buf_sprintf("unable to make error union out of null literal")); return ira->codegen->builtin_types.entry_invalid; @@ -8685,7 +8685,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type); } } - } else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNullLit) { + } else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNull) { if (prev_inst->value.type->id == TypeTableEntryIdComptimeInt || prev_inst->value.type->id == TypeTableEntryIdComptimeFloat) { @@ -10004,7 +10004,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from null literal to maybe type if (wanted_type->id == TypeTableEntryIdMaybe && - actual_type->id == TypeTableEntryIdNullLit) + actual_type->id == TypeTableEntryIdNull) { return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); } @@ -10061,7 +10061,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst { TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk || - actual_type->id == TypeTableEntryIdNullLit || + actual_type->id == TypeTableEntryIdNull || actual_type->id == TypeTableEntryIdComptimeInt || actual_type->id == TypeTableEntryIdComptimeFloat) { @@ -10627,19 +10627,19 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp IrBinOp op_id = bin_op_instruction->op_id; bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); if (is_equality_cmp && - ((op1->value.type->id == TypeTableEntryIdNullLit && op2->value.type->id == TypeTableEntryIdMaybe) || - (op2->value.type->id == TypeTableEntryIdNullLit && op1->value.type->id == TypeTableEntryIdMaybe) || - (op1->value.type->id == TypeTableEntryIdNullLit && op2->value.type->id == TypeTableEntryIdNullLit))) + ((op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdMaybe) || + (op2->value.type->id == TypeTableEntryIdNull && op1->value.type->id == TypeTableEntryIdMaybe) || + (op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdNull))) { - if (op1->value.type->id == TypeTableEntryIdNullLit && op2->value.type->id == TypeTableEntryIdNullLit) { + if (op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdNull) { ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = (op_id == IrBinOpCmpEq); return ira->codegen->builtin_types.entry_bool; } IrInstruction *maybe_op; - if (op1->value.type->id == TypeTableEntryIdNullLit) { + if (op1->value.type->id == TypeTableEntryIdNull) { maybe_op = op2; - } else if (op2->value.type->id == TypeTableEntryIdNullLit) { + } else if (op2->value.type->id == TypeTableEntryIdNull) { maybe_op = op1; } else { zig_unreachable(); @@ -10796,7 +10796,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdUnion: @@ -11645,7 +11645,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdBlock: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdOpaque: case TypeTableEntryIdMetaType: case TypeTableEntryIdNamespace: @@ -11913,7 +11913,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -11937,7 +11937,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -12901,7 +12901,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -13152,7 +13152,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP if (resolved_type->id == TypeTableEntryIdComptimeFloat || resolved_type->id == TypeTableEntryIdComptimeInt || - resolved_type->id == TypeTableEntryIdNullLit || + resolved_type->id == TypeTableEntryIdNull || resolved_type->id == TypeTableEntryIdUndefLit) { ir_add_error_node(ira, phi_instruction->base.source_node, @@ -14216,7 +14216,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -14480,7 +14480,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, zig_unreachable(); case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: @@ -14588,7 +14588,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, zig_unreachable(); case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: @@ -14657,7 +14657,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, zig_unreachable(); case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: @@ -14713,7 +14713,7 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn ir_build_test_nonnull_from(&ira->new_irb, &instruction->base, value); return ira->codegen->builtin_types.entry_bool; - } else if (type_entry->id == TypeTableEntryIdNullLit) { + } else if (type_entry->id == TypeTableEntryIdNull) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_bool = false; return ira->codegen->builtin_types.entry_bool; @@ -15100,7 +15100,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -15621,7 +15621,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -16283,7 +16283,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: @@ -17879,7 +17879,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -18613,7 +18613,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdPromise: zig_unreachable(); case TypeTableEntryIdVoid: @@ -18680,7 +18680,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdPromise: zig_unreachable(); case TypeTableEntryIdVoid: @@ -18761,7 +18761,7 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: ir_add_error(ira, dest_type_value, buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name))); return ira->codegen->builtin_types.entry_invalid; @@ -18787,7 +18787,7 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: ir_add_error(ira, dest_type_value, buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->builtin_types.entry_invalid; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 1821e29a20..ed14243b39 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -504,7 +504,7 @@ test "@typeId" { assert(@typeId(@typeOf(1)) == Tid.ComptimeInt); assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); assert(@typeId(@typeOf(undefined)) == Tid.UndefinedLiteral); - assert(@typeId(@typeOf(null)) == Tid.NullLiteral); + assert(@typeId(@typeOf(null)) == Tid.Null); assert(@typeId(?i32) == Tid.Nullable); assert(@typeId(error!i32) == Tid.ErrorUnion); assert(@typeId(error) == Tid.ErrorSet); -- cgit v1.2.3 From a8146ade2a57bea12ea2d16bd273f03578e5d559 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Tue, 5 Jun 2018 11:54:11 +0200 Subject: Renamed UndefinedLiteral to Undefined --- doc/langref.html.in | 8 ++++---- src/all_types.hpp | 2 +- src/analyze.cpp | 44 ++++++++++++++++++++++---------------------- src/codegen.cpp | 14 +++++++------- src/ir.cpp | 44 ++++++++++++++++++++++---------------------- test/cases/misc.zig | 2 +- 6 files changed, 57 insertions(+), 57 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 70f11c0e2b..4359cadb58 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4895,7 +4895,7 @@ pub const TypeId = enum { Struct, ComptimeFloat, ComptimeInt, - UndefinedLiteral, + Undefined, Null, Nullable, ErrorUnion, @@ -4929,7 +4929,7 @@ pub const TypeInfo = union(TypeId) { Struct: Struct, ComptimeFloat: void, ComptimeInt: void, - UndefinedLiteral: void, + Undefined: void, Null: void, Nullable: Nullable, ErrorUnion: ErrorUnion, @@ -5687,7 +5687,7 @@ pub const TypeId = enum { Struct, ComptimeFloat, ComptimeInt, - UndefinedLiteral, + Undefined, Null, Nullable, ErrorUnion, @@ -5715,7 +5715,7 @@ pub const TypeInfo = union(TypeId) { Struct: Struct, ComptimeFloat: void, ComptimeInt: void, - UndefinedLiteral: void, + Undefined: void, Null: void, Nullable: Nullable, ErrorUnion: ErrorUnion, diff --git a/src/all_types.hpp b/src/all_types.hpp index 6b30a1155d..3b2ea02b71 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1161,7 +1161,7 @@ enum TypeTableEntryId { TypeTableEntryIdStruct, TypeTableEntryIdComptimeFloat, TypeTableEntryIdComptimeInt, - TypeTableEntryIdUndefLit, + TypeTableEntryIdUndefined, TypeTableEntryIdNull, TypeTableEntryIdMaybe, TypeTableEntryIdErrorUnion, diff --git a/src/analyze.cpp b/src/analyze.cpp index a605cb3a7f..8008bea68d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -234,7 +234,7 @@ bool type_is_complete(TypeTableEntry *type_entry) { case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -270,7 +270,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) { case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -1335,7 +1335,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -1376,7 +1376,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdMetaType: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -1511,7 +1511,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: @@ -1599,7 +1599,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdInvalid: zig_unreachable(); - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: @@ -3337,7 +3337,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: @@ -3732,7 +3732,7 @@ static bool is_container(TypeTableEntry *type_entry) { case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -3781,7 +3781,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -4285,7 +4285,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { case TypeTableEntryIdMetaType: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -4672,7 +4672,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case TypeTableEntryIdPromise: // TODO better hashing algorithm return 223048345; - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: return 162837799; case TypeTableEntryIdNull: return 844854567; @@ -4756,7 +4756,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { case TypeTableEntryIdFloat: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: @@ -4821,7 +4821,7 @@ static bool return_type_is_cacheable(TypeTableEntry *return_type) { case TypeTableEntryIdFloat: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: @@ -4932,7 +4932,7 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { zig_unreachable(); case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMetaType: case TypeTableEntryIdNamespace: @@ -5410,7 +5410,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return false; } return true; - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: zig_panic("TODO"); case TypeTableEntryIdNull: zig_panic("TODO"); @@ -5651,7 +5651,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "null"); return; } - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: { buf_appendf(buf, "undefined"); return; @@ -5763,7 +5763,7 @@ uint32_t type_id_hash(TypeId x) { case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorSet: @@ -5809,7 +5809,7 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdPromise: @@ -5931,7 +5931,7 @@ static const TypeTableEntryId all_type_ids[] = { TypeTableEntryIdStruct, TypeTableEntryIdComptimeFloat, TypeTableEntryIdComptimeInt, - TypeTableEntryIdUndefLit, + TypeTableEntryIdUndefined, TypeTableEntryIdNull, TypeTableEntryIdMaybe, TypeTableEntryIdErrorUnion, @@ -5984,7 +5984,7 @@ size_t type_id_index(TypeTableEntry *entry) { return 9; case TypeTableEntryIdComptimeInt: return 10; - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: return 11; case TypeTableEntryIdNull: return 12; @@ -6042,8 +6042,8 @@ const char *type_id_name(TypeTableEntryId id) { return "ComptimeFloat"; case TypeTableEntryIdComptimeInt: return "ComptimeInt"; - case TypeTableEntryIdUndefLit: - return "UndefinedLiteral"; + case TypeTableEntryIdUndefined: + return "Undefined"; case TypeTableEntryIdNull: return "Null"; case TypeTableEntryIdMaybe: diff --git a/src/codegen.cpp b/src/codegen.cpp index 3177a2491f..a977c34daf 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4918,7 +4918,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -5364,7 +5364,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -6026,7 +6026,7 @@ static void define_builtin_types(CodeGen *g) { g->primitive_type_table.put(&entry->name, entry); } { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUndefLit); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUndefined); buf_init_from_str(&entry->name, "(undefined)"); entry->zero_bits = true; g->builtin_types.entry_undef = entry; @@ -6499,7 +6499,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " Struct: Struct,\n" " ComptimeFloat: void,\n" " ComptimeInt: void,\n" - " UndefinedLiteral: void,\n" + " Undefined: void,\n" " Null: void,\n" " Nullable: Nullable,\n" " ErrorUnion: ErrorUnion,\n" @@ -7074,7 +7074,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry case TypeTableEntryIdMetaType: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -7259,7 +7259,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf case TypeTableEntryIdBlock: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdPromise: @@ -7412,7 +7412,7 @@ static void gen_h_file(CodeGen *g) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdArray: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: diff --git a/src/ir.cpp b/src/ir.cpp index 2fbc72309a..9578795fcc 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8142,7 +8142,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit undefined literal to anything - if (actual_type->id == TypeTableEntryIdUndefLit) { + if (actual_type->id == TypeTableEntryIdUndefined) { return ImplicitCastMatchResultYes; } @@ -8546,11 +8546,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } - if (cur_type->id == TypeTableEntryIdUndefLit) { + if (cur_type->id == TypeTableEntryIdUndefined) { continue; } - if (prev_type->id == TypeTableEntryIdUndefLit) { + if (prev_type->id == TypeTableEntryIdUndefined) { prev_inst = cur_inst; continue; } @@ -10230,7 +10230,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from undefined to anything - if (actual_type->id == TypeTableEntryIdUndefLit) { + if (actual_type->id == TypeTableEntryIdUndefined) { return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); } @@ -10795,7 +10795,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdUnreachable: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -11643,7 +11643,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { return VarClassRequiredAny; case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdBlock: case TypeTableEntryIdNull: case TypeTableEntryIdOpaque: @@ -11912,7 +11912,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -11936,7 +11936,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -12900,7 +12900,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -13153,7 +13153,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP if (resolved_type->id == TypeTableEntryIdComptimeFloat || resolved_type->id == TypeTableEntryIdComptimeInt || resolved_type->id == TypeTableEntryIdNull || - resolved_type->id == TypeTableEntryIdUndefLit) + resolved_type->id == TypeTableEntryIdUndefined) { ir_add_error_node(ira, phi_instruction->base.source_node, buf_sprintf("unable to infer expression type")); @@ -14215,7 +14215,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi zig_unreachable(); // handled above case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -14479,7 +14479,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, case TypeTableEntryIdInvalid: // handled above zig_unreachable(); case TypeTableEntryIdUnreachable: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: @@ -14587,7 +14587,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, case TypeTableEntryIdInvalid: // handled above zig_unreachable(); case TypeTableEntryIdUnreachable: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: @@ -14656,7 +14656,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, case TypeTableEntryIdInvalid: // handled above zig_unreachable(); case TypeTableEntryIdUnreachable: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdComptimeFloat: @@ -15099,7 +15099,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, case TypeTableEntryIdUnreachable: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdBlock: @@ -15620,7 +15620,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -16282,7 +16282,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -17878,7 +17878,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -18612,7 +18612,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdPromise: zig_unreachable(); @@ -18679,7 +18679,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdPromise: zig_unreachable(); @@ -18760,7 +18760,7 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: ir_add_error(ira, dest_type_value, buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name))); @@ -18786,7 +18786,7 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: ir_add_error(ira, dest_type_value, buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index ed14243b39..9450cf5e6e 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -503,7 +503,7 @@ test "@typeId" { assert(@typeId(AStruct) == Tid.Struct); assert(@typeId(@typeOf(1)) == Tid.ComptimeInt); assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); - assert(@typeId(@typeOf(undefined)) == Tid.UndefinedLiteral); + assert(@typeId(@typeOf(undefined)) == Tid.Undefined); assert(@typeId(@typeOf(null)) == Tid.Null); assert(@typeId(?i32) == Tid.Nullable); assert(@typeId(error!i32) == Tid.ErrorUnion); -- cgit v1.2.3 From 7a0948253636080e5abe59b938761ee7348a7025 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Jun 2018 10:48:53 -0400 Subject: fix crash when evaluating return type has compile error closes #1058 --- src/analyze.cpp | 2 ++ test/compile_errors.zig | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/analyze.cpp b/src/analyze.cpp index 8008bea68d..b0f0196020 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1018,6 +1018,8 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { } if (fn_type_id->return_type != nullptr) { ensure_complete_type(g, fn_type_id->return_type); + if (type_is_invalid(fn_type_id->return_type)) + return g->builtin_types.entry_invalid; } else { zig_panic("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index e264d57b5e..4bd6e9bc24 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,22 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "error when evaluating return type", + \\const Foo = struct { + \\ map: i32(i32), + \\ + \\ fn init() Foo { + \\ return undefined; + \\ } + \\}; + \\export fn entry() void { + \\ var rule_set = try Foo.init(); + \\} + , + ".tmp_source.zig:2:13: error: invalid cast from type 'type' to 'i32'", + ); + cases.add( "slicing single-item pointer", \\export fn entry(ptr: *i32) void { -- cgit v1.2.3 From 652f4bdf6242462182005f4c7149f13beaaa3259 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Jun 2018 18:03:21 -0400 Subject: disallow unknown-length pointer to opaque This also means that translate-c has to detect when a pointer to opaque is happening, and use `*` instead of `[*]`. See #1059 --- src/analyze.cpp | 1 + src/ir.cpp | 10 +++++----- src/tokenizer.hpp | 2 ++ src/translate_c.cpp | 37 +++++++++++++++++++++++++++++++++---- std/c/index.zig | 20 ++++++++++---------- std/heap.zig | 8 ++++---- std/os/darwin.zig | 8 ++++---- std/os/file.zig | 2 +- std/os/index.zig | 4 ++-- std/os/windows/index.zig | 14 +++++++------- std/os/windows/util.zig | 2 +- test/compare_output.zig | 4 ++-- test/compile_errors.zig | 7 +++++++ test/translate_c.zig | 20 ++++++++++---------- 14 files changed, 89 insertions(+), 50 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index b0f0196020..0adb992798 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -384,6 +384,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count) { assert(!type_is_invalid(child_type)); + assert(ptr_len == PtrLenSingle || child_type->id != TypeTableEntryIdOpaque); TypeId type_id = {}; TypeTableEntry **parent_pointer = nullptr; diff --git a/src/ir.cpp b/src/ir.cpp index 9578795fcc..5c44e7c0ff 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4620,11 +4620,8 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction * static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePointerType); - // The null check here is for C imports which don't set a token on the AST node. We could potentially - // update that code to create a fake token and then remove this check. - PtrLen ptr_len = (node->data.pointer_type.star_token != nullptr && - (node->data.pointer_type.star_token->id == TokenIdStar || - node->data.pointer_type.star_token->id == TokenIdStarStar)) ? PtrLenSingle : PtrLenUnknown; + PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar || + node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown; bool is_const = node->data.pointer_type.is_const; bool is_volatile = node->data.pointer_type.is_volatile; AstNode *expr_node = node->data.pointer_type.op_expr; @@ -18973,6 +18970,9 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruc if (child_type->id == TypeTableEntryIdUnreachable) { ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed")); return ira->codegen->builtin_types.entry_invalid; + } else if (child_type->id == TypeTableEntryIdOpaque && instruction->ptr_len == PtrLenUnknown) { + ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque")); + return ira->codegen->builtin_types.entry_invalid; } uint32_t align_bytes; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index d659c0a772..d0089909cd 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -170,6 +170,8 @@ struct Token { TokenCharLit char_lit; } data; }; +// work around conflicting name Token which is also found in libclang +typedef Token ZigToken; struct Tokenization { ZigList *tokens; diff --git a/src/translate_c.cpp b/src/translate_c.cpp index db541d34f3..d78bd1fa70 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -276,8 +276,11 @@ static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNod node); } -static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node) { +static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node, PtrLen ptr_len) { AstNode *node = trans_create_node(c, NodeTypePointerType); + node->data.pointer_type.star_token = allocate(1); + node->data.pointer_type.star_token->id = (ptr_len == PtrLenSingle) ? TokenIdStar: TokenIdBracketStarBracket; + node->data.pointer_type.is_const = is_const; node->data.pointer_type.is_const = is_const; node->data.pointer_type.is_volatile = is_volatile; node->data.pointer_type.op_expr = child_node; @@ -731,6 +734,30 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) { } } +static bool type_is_opaque(Context *c, const Type *ty, const SourceLocation &source_loc) { + switch (ty->getTypeClass()) { + case Type::Builtin: { + const BuiltinType *builtin_ty = static_cast(ty); + return builtin_ty->getKind() == BuiltinType::Void; + } + case Type::Record: { + const RecordType *record_ty = static_cast(ty); + return record_ty->getDecl()->getDefinition() == nullptr; + } + case Type::Elaborated: { + const ElaboratedType *elaborated_ty = static_cast(ty); + return type_is_opaque(c, elaborated_ty->getNamedType().getTypePtr(), source_loc); + } + case Type::Typedef: { + const TypedefType *typedef_ty = static_cast(ty); + const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); + return type_is_opaque(c, typedef_decl->getUnderlyingType().getTypePtr(), source_loc); + } + default: + return false; + } +} + static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) { switch (ty->getTypeClass()) { case Type::Builtin: @@ -855,8 +882,10 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return trans_create_node_prefix_op(c, PrefixOpMaybe, child_node); } + PtrLen ptr_len = type_is_opaque(c, child_qt.getTypePtr(), source_loc) ? PtrLenSingle : PtrLenUnknown; + AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), - child_qt.isVolatileQualified(), child_node); + child_qt.isVolatileQualified(), child_node, ptr_len); return trans_create_node_prefix_op(c, PrefixOpMaybe, pointer_node); } case Type::Typedef: @@ -1041,7 +1070,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return nullptr; } AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), - child_qt.isVolatileQualified(), child_type_node); + child_qt.isVolatileQualified(), child_type_node, PtrLenUnknown); return pointer_node; } case Type::BlockPointer: @@ -4448,7 +4477,7 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t } else if (first_tok->id == CTokIdAsterisk) { *tok_i += 1; - node = trans_create_node_ptr_type(c, false, false, node); + node = trans_create_node_ptr_type(c, false, false, node, PtrLenUnknown); } else { return node; } diff --git a/std/c/index.zig b/std/c/index.zig index ade37f36c1..7de8634d07 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -20,11 +20,11 @@ pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int; pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; pub extern "c" fn raise(sig: c_int) c_int; -pub extern "c" fn read(fd: c_int, buf: [*]c_void, nbyte: usize) isize; +pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; -pub extern "c" fn write(fd: c_int, buf: [*]const c_void, nbyte: usize) isize; -pub extern "c" fn mmap(addr: ?[*]c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?[*]c_void; -pub extern "c" fn munmap(addr: [*]c_void, len: usize) c_int; +pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; +pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; +pub extern "c" fn munmap(addr: *c_void, len: usize) c_int; pub extern "c" fn unlink(path: [*]const u8) c_int; pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; @@ -48,15 +48,15 @@ pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; pub extern "c" fn rmdir(path: [*]const u8) c_int; -pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?[*]c_void; -pub extern "c" fn malloc(usize) ?[*]c_void; -pub extern "c" fn realloc([*]c_void, usize) ?[*]c_void; -pub extern "c" fn free([*]c_void) void; -pub extern "c" fn posix_memalign(memptr: *[*]c_void, alignment: usize, size: usize) c_int; +pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; +pub extern "c" fn malloc(usize) ?*c_void; +pub extern "c" fn realloc(*c_void, usize) ?*c_void; +pub extern "c" fn free(*c_void) void; +pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int; pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; -pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: [*]c_void, stacksize: usize) c_int; +pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; diff --git a/std/heap.zig b/std/heap.zig index 4444a2307a..5d430bc761 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -22,7 +22,7 @@ fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 { } fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { - const old_ptr = @ptrCast([*]c_void, old_mem.ptr); + const old_ptr = @ptrCast(*c_void, old_mem.ptr); if (c.realloc(old_ptr, new_size)) |buf| { return @ptrCast([*]u8, buf)[0..new_size]; } else if (new_size <= old_mem.len) { @@ -33,7 +33,7 @@ fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![ } fn cFree(self: *Allocator, old_mem: []u8) void { - const old_ptr = @ptrCast([*]c_void, old_mem.ptr); + const old_ptr = @ptrCast(*c_void, old_mem.ptr); c.free(old_ptr); } @@ -140,7 +140,7 @@ pub const DirectAllocator = struct { const old_adjusted_addr = @ptrToInt(old_mem.ptr); const old_record_addr = old_adjusted_addr + old_mem.len; const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; - const old_ptr = @intToPtr([*]c_void, root_addr); + const old_ptr = @intToPtr(*c_void, root_addr); const amt = new_size + alignment + @sizeOf(usize); const new_ptr = os.windows.HeapReAlloc(??self.heap_handle, 0, old_ptr, amt) ?? blk: { if (new_size > old_mem.len) return error.OutOfMemory; @@ -170,7 +170,7 @@ pub const DirectAllocator = struct { Os.windows => { const record_addr = @ptrToInt(bytes.ptr) + bytes.len; const root_addr = @intToPtr(*align(1) usize, record_addr).*; - const ptr = @intToPtr([*]c_void, root_addr); + const ptr = @intToPtr(*c_void, root_addr); _ = os.windows.HeapFree(??self.heap_handle, 0, ptr); }, else => @compileError("Unsupported OS"), diff --git a/std/os/darwin.zig b/std/os/darwin.zig index b8e18561cc..a835959103 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -327,7 +327,7 @@ pub fn raise(sig: i32) usize { } pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { - return errnoWrap(c.read(fd, @ptrCast([*]c_void, buf), nbyte)); + return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); } pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize { @@ -335,17 +335,17 @@ pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize { } pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { - return errnoWrap(c.write(fd, @ptrCast([*]const c_void, buf), nbyte)); + return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); } pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap(@ptrCast([*]c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); + const ptr_result = c.mmap(@ptrCast(*c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); return errnoWrap(isize_result); } pub fn munmap(address: usize, length: usize) usize { - return errnoWrap(c.munmap(@intToPtr([*]c_void, address), length)); + return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); } pub fn unlink(path: [*]const u8) usize { diff --git a/std/os/file.zig b/std/os/file.zig index 378782507b..d5af55b5e4 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -334,7 +334,7 @@ pub const File = struct { while (index < buffer.len) { const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); var amt_read: windows.DWORD = undefined; - if (windows.ReadFile(self.handle, @ptrCast([*]c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) { + if (windows.ReadFile(self.handle, @ptrCast(*c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.OPERATION_ABORTED => continue, diff --git a/std/os/index.zig b/std/os/index.zig index 6023929b04..fe5ecc38ba 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2362,7 +2362,7 @@ pub const Thread = struct { }, builtin.Os.windows => struct { handle: windows.HANDLE, - alloc_start: [*]c_void, + alloc_start: *c_void, heap_handle: windows.HANDLE, }, else => @compileError("Unsupported OS"), @@ -2533,7 +2533,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread // align to page stack_end -= stack_end % os.page_size; - assert(c.pthread_attr_setstack(&attr, @intToPtr([*]c_void, stack_addr), stack_end - stack_addr) == 0); + assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0); const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); switch (err) { diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index c491ae6538..53e12500e7 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -101,17 +101,17 @@ pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?*FILETIME) void; pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void, dwBytes: SIZE_T) ?[*]c_void; -pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) SIZE_T; -pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; +pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; +pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; -pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?[*]c_void; +pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; -pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; pub extern "kernel32" stdcallcc fn MoveFileExA( lpExistingFileName: LPCSTR, @@ -127,7 +127,7 @@ pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL; pub extern "kernel32" stdcallcc fn ReadFile( in_hFile: HANDLE, - out_lpBuffer: [*]c_void, + out_lpBuffer: *c_void, in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: *DWORD, in_out_lpOverlapped: ?*OVERLAPPED, @@ -150,7 +150,7 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis pub extern "kernel32" stdcallcc fn WriteFile( in_hFile: HANDLE, - in_lpBuffer: [*]const c_void, + in_lpBuffer: *const c_void, in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?*DWORD, in_out_lpOverlapped: ?*OVERLAPPED, diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 5a40567310..7170346108 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -42,7 +42,7 @@ pub const WriteError = error{ }; pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { - if (windows.WriteFile(handle, @ptrCast([*]const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { + if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, diff --git a/test/compare_output.zig b/test/compare_output.zig index 8d5dc68d45..eec077ef85 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -284,7 +284,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addC("expose function pointer to C land", \\const c = @cImport(@cInclude("stdlib.h")); \\ - \\export fn compare_fn(a: ?[*]const c_void, b: ?[*]const c_void) c_int { + \\export fn compare_fn(a: ?*const c_void, b: ?*const c_void) c_int { \\ const a_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), a)); \\ const b_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), b)); \\ if (a_int.* < b_int.*) { @@ -299,7 +299,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\export fn main() c_int { \\ var array = []u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; \\ - \\ c.qsort(@ptrCast(?[*]c_void, array[0..].ptr), c_ulong(array.len), @sizeOf(i32), compare_fn); + \\ c.qsort(@ptrCast(?*c_void, array[0..].ptr), c_ulong(array.len), @sizeOf(i32), compare_fn); \\ \\ for (array) |item, i| { \\ if (item != i) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 4bd6e9bc24..9cecb859fa 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,13 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "unknown length pointer to opaque", + \\export const T = [*]@OpaqueType(); + , + ".tmp_source.zig:1:18: error: unknown-length pointer to opaque", + ); + cases.add( "error when evaluating return type", \\const Foo = struct { diff --git a/test/translate_c.zig b/test/translate_c.zig index ac0a98e6cc..3489f9da21 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -99,7 +99,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("restrict -> noalias", \\void foo(void *restrict bar, void *restrict); , - \\pub extern fn foo(noalias bar: ?[*]c_void, noalias arg1: ?[*]c_void) void; + \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; ); cases.add("simple struct", @@ -172,7 +172,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const struct_Foo = @OpaqueType(); , - \\pub extern fn some_func(foo: ?[*]struct_Foo, x: c_int) ?[*]struct_Foo; + \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo; , \\pub const Foo = struct_Foo; ); @@ -233,7 +233,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const Foo = c_void; , - \\pub extern fn fun(a: ?[*]Foo) Foo; + \\pub extern fn fun(a: ?*Foo) Foo; ); cases.add("generate inline func for #define global extern fn", @@ -505,7 +505,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 6; \\} , - \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { + \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ if ((a != 0) and (b != 0)) return 0; \\ if ((b != 0) and (c != null)) return 1; \\ if ((a != 0) and (c != null)) return 2; @@ -653,8 +653,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return x; \\} , - \\pub export fn foo(x: ?[*]c_ushort) ?[*]c_void { - \\ return @ptrCast(?[*]c_void, x); + \\pub export fn foo(x: ?[*]c_ushort) ?*c_void { + \\ return @ptrCast(?*c_void, x); \\} ); @@ -1173,7 +1173,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return !c; \\} , - \\pub fn foo(a: c_int, b: f32, c: ?[*]c_void) c_int { + \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int { \\ return !(a == 0); \\ return !(a != 0); \\ return !(b != 0); @@ -1231,7 +1231,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ B, \\ C, \\}; - \\pub fn if_none_bool(a: c_int, b: f32, c: ?[*]c_void, d: enum_SomeEnum) c_int { + \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { \\ if (a != 0) return 0; \\ if (b != 0) return 1; \\ if (c != null) return 2; @@ -1248,7 +1248,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn while_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { + \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; @@ -1264,7 +1264,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn for_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { + \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; -- cgit v1.2.3 From bbb565a21e40f305b9fa10c385124455fafe647f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Jun 2018 21:56:19 -0400 Subject: README: update support table macosx does not run on some of these architectures --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b5bf13f095..a5868bf44e 100644 --- a/README.md +++ b/README.md @@ -55,18 +55,18 @@ that counts as "freestanding" for the purposes of this table. |i386 | OK | planned | OK | planned | planned | |x86_64 | OK | OK | OK | OK | planned | |arm | OK | planned | planned | N/A | planned | -|aarch64 | OK | planned | planned | planned | planned | -|bpf | OK | planned | planned | N/A | planned | -|hexagon | OK | planned | planned | N/A | planned | -|mips | OK | planned | planned | N/A | planned | -|powerpc | OK | planned | planned | N/A | planned | -|r600 | OK | planned | planned | N/A | planned | -|amdgcn | OK | planned | planned | N/A | planned | -|sparc | OK | planned | planned | N/A | planned | -|s390x | OK | planned | planned | N/A | planned | -|thumb | OK | planned | planned | N/A | planned | -|spir | OK | planned | planned | N/A | planned | -|lanai | OK | planned | planned | N/A | planned | +|aarch64 | OK | planned | N/A | planned | planned | +|bpf | OK | planned | N/A | N/A | planned | +|hexagon | OK | planned | N/A | N/A | planned | +|mips | OK | planned | N/A | N/A | planned | +|powerpc | OK | planned | N/A | N/A | planned | +|r600 | OK | planned | N/A | N/A | planned | +|amdgcn | OK | planned | N/A | N/A | planned | +|sparc | OK | planned | N/A | N/A | planned | +|s390x | OK | planned | N/A | N/A | planned | +|thumb | OK | planned | N/A | N/A | planned | +|spir | OK | planned | N/A | N/A | planned | +|lanai | OK | planned | N/A | N/A | planned | ## Community -- cgit v1.2.3 From 0ccc18686921dce8e7f2feb95eed83b894ca8df4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Jun 2018 20:24:11 -0400 Subject: disable field access for unknown length pointers See #770 --- src/analyze.cpp | 4 ++-- test/compile_errors.zig | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 0adb992798..15f08aa3fe 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3753,13 +3753,13 @@ static bool is_container(TypeTableEntry *type_entry) { } bool is_container_ref(TypeTableEntry *type_entry) { - return (type_entry->id == TypeTableEntryIdPointer) ? + return (type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle) ? is_container(type_entry->data.pointer.child_type) : is_container(type_entry); } TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) { assert(is_container_ref(type_entry)); - return (type_entry->id == TypeTableEntryIdPointer) ? + return (type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle) ? type_entry->data.pointer.child_type : type_entry; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 9cecb859fa..ab539dd94a 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,19 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "field access of unknown length pointer", + \\const Foo = extern struct { + \\ a: i32, + \\}; + \\ + \\export fn entry(foo: [*]Foo) void { + \\ foo.a += 1; + \\} + , + ".tmp_source.zig:6:8: error: type '[*]Foo' does not support field access", + ); + cases.add( "unknown length pointer to opaque", \\export const T = [*]@OpaqueType(); -- cgit v1.2.3 From bd13e757e7e36994d2a1fd4595c617d14e22b7c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Jun 2018 22:23:23 -0400 Subject: disable deref syntax for unknown length pointers See #770 --- src/ir.cpp | 12 ++++++++++++ std/special/bootstrap.zig | 2 +- test/compile_errors.zig | 9 +++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 5c44e7c0ff..a6686aae76 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11132,7 +11132,13 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; + if (type_is_invalid(op1->value.type)) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *op2 = bin_op_instruction->op2->other; + if (type_is_invalid(op2->value.type)) + return ira->codegen->builtin_types.entry_invalid; + IrBinOp op_id = bin_op_instruction->op_id; // look for pointer math @@ -12851,6 +12857,12 @@ static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp if (type_is_invalid(ptr_type)) { return ira->codegen->builtin_types.entry_invalid; } else if (ptr_type->id == TypeTableEntryIdPointer) { + if (ptr_type->data.pointer.ptr_len == PtrLenUnknown) { + ir_add_error_node(ira, un_op_instruction->base.source_node, + buf_sprintf("index syntax required for unknown-length pointer type '%s'", + buf_ptr(&ptr_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } child_type = ptr_type->data.pointer.child_type; } else { ir_add_error_node(ira, un_op_instruction->base.source_node, diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 64eae79ce4..8aefe4751f 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -51,7 +51,7 @@ extern fn WinMainCRTStartup() noreturn { // TODO https://github.com/ziglang/zig/issues/265 fn posixCallMainAndExit() noreturn { - const argc = argc_ptr.*; + const argc = argc_ptr[0]; const argv = @ptrCast([*][*]u8, argc_ptr + 1); const envp_nullable = @ptrCast([*]?[*]u8, argv + argc + 1); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index ab539dd94a..412b2d5fc9 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "dereference unknown length pointer", + \\export fn entry(x: [*]i32) i32 { + \\ return x.*; + \\} + , + ".tmp_source.zig:2:13: error: index syntax required for unknown-length pointer type '[*]i32'", + ); + cases.add( "field access of unknown length pointer", \\const Foo = extern struct { -- cgit v1.2.3 From 76c8efd56c84c189a52d3dc559fff109d5d34ce4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Jun 2018 23:54:14 -0400 Subject: add test for not allowing implicit cast from T to [*]const T See #770 --- test/compile_errors.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 412b2d5fc9..c995cd679e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "attempted implicit cast from T to [*]const T", + \\export fn entry() void { + \\ const x: [*]const bool = true; + \\} + , + ".tmp_source.zig:2:30: error: expected type '[*]const bool', found 'bool'", + ); + cases.add( "dereference unknown length pointer", \\export fn entry(x: [*]i32) i32 { -- cgit v1.2.3 From d3693dca73dfc726aed32908691437abe614e5cf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 6 Jun 2018 00:39:39 -0400 Subject: Pointer Reform: update @typeInfo * add assertion for trying to do @typeInfo on global error set * remove TypeInfo.Slice * add TypeInfo.Pointer.Size with possible values - One - Many - Slice See #770 --- src/analyze.cpp | 2 +- src/codegen.cpp | 11 ++++--- src/ir.cpp | 80 ++++++++++++++++++++++++++++++------------------ std/fmt/index.zig | 31 +++++++++++-------- test/cases/type_info.zig | 33 +++++++++++++++----- 5 files changed, 102 insertions(+), 55 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 15f08aa3fe..93373f6ec2 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5981,7 +5981,7 @@ size_t type_id_index(TypeTableEntry *entry) { return 7; case TypeTableEntryIdStruct: if (entry->data.structure.is_slice) - return 25; + return 6; return 8; case TypeTableEntryIdComptimeFloat: return 9; diff --git a/src/codegen.cpp b/src/codegen.cpp index a977c34daf..7f95f335d1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6481,7 +6481,6 @@ static void define_builtin_compile_vars(CodeGen *g) { const TypeTableEntryId id = type_id_at_index(i); buf_appendf(contents, " %s,\n", type_id_name(id)); } - buf_appendf(contents, " Slice,\n"); buf_appendf(contents, "};\n\n"); } { @@ -6494,7 +6493,6 @@ static void define_builtin_compile_vars(CodeGen *g) { " Int: Int,\n" " Float: Float,\n" " Pointer: Pointer,\n" - " Slice: Slice,\n" " Array: Array,\n" " Struct: Struct,\n" " ComptimeFloat: void,\n" @@ -6524,13 +6522,18 @@ static void define_builtin_compile_vars(CodeGen *g) { " };\n" "\n" " pub const Pointer = struct {\n" + " size: Size,\n" " is_const: bool,\n" " is_volatile: bool,\n" " alignment: u32,\n" " child: type,\n" - " };\n" "\n" - " pub const Slice = Pointer;\n" + " pub const Size = enum {\n" + " One,\n" + " Many,\n" + " Slice,\n" + " };\n" + " };\n" "\n" " pub const Array = struct {\n" " len: usize,\n" diff --git a/src/ir.cpp b/src/ir.cpp index a6686aae76..3486e8c047 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16222,8 +16222,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop return true; } -static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) -{ +static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) { assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); @@ -16248,38 +16247,67 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t enum_field_val->data.x_struct.fields = inner_fields; }; - const auto create_ptr_like_type_info = [ira](const char *name, TypeTableEntry *ptr_type_entry) { + const auto create_ptr_like_type_info = [ira](TypeTableEntry *ptr_type_entry) { + TypeTableEntry *attrs_type; + uint32_t size_enum_index; + if (is_slice(ptr_type_entry)) { + attrs_type = ptr_type_entry->data.structure.fields[slice_ptr_index].type_entry; + size_enum_index = 2; + } else if (ptr_type_entry->id == TypeTableEntryIdPointer) { + attrs_type = ptr_type_entry; + size_enum_index = (ptr_type_entry->data.pointer.ptr_len == PtrLenSingle) ? 0 : 1; + } else { + zig_unreachable(); + } + + TypeTableEntry *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer"); + ensure_complete_type(ira->codegen, type_info_pointer_type); + assert(!type_is_invalid(type_info_pointer_type)); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, name); + result->type = type_info_pointer_type; - ConstExprValue *fields = create_const_vals(4); + ConstExprValue *fields = create_const_vals(5); result->data.x_struct.fields = fields; - // is_const: bool - ensure_field_index(result->type, "is_const", 0); + // size: Size + ensure_field_index(result->type, "size", 0); + TypeTableEntry *type_info_pointer_size_type = ir_type_info_get_type(ira, "Size", type_info_pointer_type); + ensure_complete_type(ira->codegen, type_info_pointer_size_type); + assert(!type_is_invalid(type_info_pointer_size_type)); fields[0].special = ConstValSpecialStatic; - fields[0].type = ira->codegen->builtin_types.entry_bool; - fields[0].data.x_bool = ptr_type_entry->data.pointer.is_const; - // is_volatile: bool - ensure_field_index(result->type, "is_volatile", 1); + fields[0].type = type_info_pointer_size_type; + bigint_init_unsigned(&fields[0].data.x_enum_tag, size_enum_index); + + // is_const: bool + ensure_field_index(result->type, "is_const", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_bool; - fields[1].data.x_bool = ptr_type_entry->data.pointer.is_volatile; - // alignment: u32 - ensure_field_index(result->type, "alignment", 2); + fields[1].data.x_bool = attrs_type->data.pointer.is_const; + // is_volatile: bool + ensure_field_index(result->type, "is_volatile", 2); fields[2].special = ConstValSpecialStatic; - fields[2].type = ira->codegen->builtin_types.entry_u32; - bigint_init_unsigned(&fields[2].data.x_bigint, ptr_type_entry->data.pointer.alignment); - // child: type - ensure_field_index(result->type, "child", 3); + fields[2].type = ira->codegen->builtin_types.entry_bool; + fields[2].data.x_bool = attrs_type->data.pointer.is_volatile; + // alignment: u32 + ensure_field_index(result->type, "alignment", 3); fields[3].special = ConstValSpecialStatic; - fields[3].type = ira->codegen->builtin_types.entry_type; - fields[3].data.x_type = ptr_type_entry->data.pointer.child_type; + fields[3].type = ira->codegen->builtin_types.entry_u32; + bigint_init_unsigned(&fields[3].data.x_bigint, attrs_type->data.pointer.alignment); + // child: type + ensure_field_index(result->type, "child", 4); + fields[4].special = ConstValSpecialStatic; + fields[4].type = ira->codegen->builtin_types.entry_type; + fields[4].data.x_type = attrs_type->data.pointer.child_type; return result; }; + if (type_entry == ira->codegen->builtin_types.entry_global_error_set) { + zig_panic("TODO implement @typeInfo for global error set"); + } + ConstExprValue *result = nullptr; switch (type_entry->id) { @@ -16348,7 +16376,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } case TypeTableEntryIdPointer: { - result = create_ptr_like_type_info("Pointer", type_entry); + result = create_ptr_like_type_info(type_entry); break; } case TypeTableEntryIdArray: @@ -16621,15 +16649,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t case TypeTableEntryIdStruct: { if (type_entry->data.structure.is_slice) { - Buf ptr_field_name = BUF_INIT; - buf_init_from_str(&ptr_field_name, "ptr"); - TypeTableEntry *ptr_type = type_entry->data.structure.fields_by_name.get(&ptr_field_name)->type_entry; - ensure_complete_type(ira->codegen, ptr_type); - if (type_is_invalid(ptr_type)) - return nullptr; - buf_deinit(&ptr_field_name); - - result = create_ptr_like_type_info("Slice", ptr_type); + result = create_ptr_like_type_info(type_entry); break; } diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 047a154bb8..bbf48df0cf 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -97,7 +97,11 @@ pub fn formatType( output: fn (@typeOf(context), []const u8) Errors!void, ) Errors!void { const T = @typeOf(value); - switch (@typeId(T)) { + if (T == error) { + try output(context, "error."); + return output(context, @errorName(value)); + } + switch (@typeInfo(T)) { builtin.TypeId.Int, builtin.TypeId.Float => { return formatValue(value, fmt, context, Errors, output); }, @@ -125,12 +129,13 @@ pub fn formatType( try output(context, "error."); return output(context, @errorName(value)); }, - builtin.TypeId.Pointer => { - switch (@typeId(T.Child)) { - builtin.TypeId.Array => { - if (T.Child.Child == u8) { + builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) { + builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) { + builtin.TypeId.Array => |info| { + if (info.child == u8) { return formatText(value, fmt, context, Errors, output); } + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); }, builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { const has_cust_fmt = comptime cf: { @@ -154,14 +159,16 @@ pub fn formatType( return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); }, else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), - } - }, - else => if (@canImplicitCast([]const u8, value)) { - const casted_value = ([]const u8)(value); - return output(context, casted_value); - } else { - @compileError("Unable to format type '" ++ @typeName(T) ++ "'"); + }, + builtin.TypeInfo.Pointer.Size.Many => { + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); + }, + builtin.TypeInfo.Pointer.Size.Slice => { + const casted_value = ([]const u8)(value); + return output(context, casted_value); + }, }, + else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), } } diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index 921ff785a7..b452c8e9f6 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -39,12 +39,28 @@ test "type info: pointer type info" { fn testPointer() void { const u32_ptr_info = @typeInfo(*u32); assert(TypeId(u32_ptr_info) == TypeId.Pointer); + assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); assert(u32_ptr_info.Pointer.is_const == false); assert(u32_ptr_info.Pointer.is_volatile == false); - assert(u32_ptr_info.Pointer.alignment == 4); + assert(u32_ptr_info.Pointer.alignment == @alignOf(u32)); assert(u32_ptr_info.Pointer.child == u32); } +test "type info: unknown length pointer type info" { + testUnknownLenPtr(); + comptime testUnknownLenPtr(); +} + +fn testUnknownLenPtr() void { + const u32_ptr_info = @typeInfo([*]const volatile f64); + assert(TypeId(u32_ptr_info) == TypeId.Pointer); + assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); + assert(u32_ptr_info.Pointer.is_const == true); + assert(u32_ptr_info.Pointer.is_volatile == true); + assert(u32_ptr_info.Pointer.alignment == @alignOf(f64)); + assert(u32_ptr_info.Pointer.child == f64); +} + test "type info: slice type info" { testSlice(); comptime testSlice(); @@ -52,11 +68,12 @@ test "type info: slice type info" { fn testSlice() void { const u32_slice_info = @typeInfo([]u32); - assert(TypeId(u32_slice_info) == TypeId.Slice); - assert(u32_slice_info.Slice.is_const == false); - assert(u32_slice_info.Slice.is_volatile == false); - assert(u32_slice_info.Slice.alignment == 4); - assert(u32_slice_info.Slice.child == u32); + assert(TypeId(u32_slice_info) == TypeId.Pointer); + assert(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice); + assert(u32_slice_info.Pointer.is_const == false); + assert(u32_slice_info.Pointer.is_volatile == false); + assert(u32_slice_info.Pointer.alignment == 4); + assert(u32_slice_info.Pointer.child == u32); } test "type info: array type info" { @@ -149,11 +166,11 @@ fn testUnion() void { assert(TypeId(typeinfo_info) == TypeId.Union); assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); assert(typeinfo_info.Union.tag_type == TypeId); - assert(typeinfo_info.Union.fields.len == 26); + assert(typeinfo_info.Union.fields.len == 25); assert(typeinfo_info.Union.fields[4].enum_field != null); assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); - assert(typeinfo_info.Union.defs.len == 21); + assert(typeinfo_info.Union.defs.len == 20); const TestNoTagUnion = union { Foo: void, -- cgit v1.2.3 From 212449bc231047571ab27af0d1ae112ed0ffea47 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Wed, 6 Jun 2018 22:41:55 +1200 Subject: Fix Log2Int type construction The following case for example, would previously fail: const a = u24(1) << Log2Int(u24)(22); --- std/math/index.zig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/std/math/index.zig b/std/math/index.zig index 33bc1082f7..a118f3ed47 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -306,7 +306,14 @@ test "math.rotl" { } pub fn Log2Int(comptime T: type) type { - return @IntType(false, log2(T.bit_count)); + // comptime ceil log2 + comptime var count: usize = 0; + comptime var s = T.bit_count - 1; + inline while (s != 0) : (s >>= 1) { + count += 1; + } + + return @IntType(false, count); } test "math overflow functions" { -- cgit v1.2.3 From f389e5373580a5a4ac48ccdb8da9dc951c01dee5 Mon Sep 17 00:00:00 2001 From: Braedon Date: Thu, 7 Jun 2018 00:45:19 +1000 Subject: Add newline to zig fmt error (#1064) --- src-self-hosted/main.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 7a62f4985b..a264b5484a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -734,7 +734,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { defer file.close(); const source_code = io.readFileAlloc(allocator, file_path) catch |err| { - try stderr.print("unable to open '{}': {}", file_path, err); + try stderr.print("unable to open '{}': {}\n", file_path, err); fmt_errors = true; continue; }; -- cgit v1.2.3 From e7f141b3762b9b6c07e17cfa68f9d4c3fd02aba2 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 7 Jun 2018 03:24:36 +1200 Subject: Add json.TokenStream (#1062) This hides some of the low-level parsing details from the StreamingParser. These don't need to be known when parsing a complete slice at once (which is we can usually do). Also, remove `Json` from Parser names. The namespace `json` is sufficient. --- std/json.zig | 237 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 159 insertions(+), 78 deletions(-) diff --git a/std/json.zig b/std/json.zig index 71673ad20f..6cf83eef1a 100644 --- a/std/json.zig +++ b/std/json.zig @@ -3,6 +3,7 @@ // https://tools.ietf.org/html/rfc8259 const std = @import("index.zig"); +const debug = std.debug; const mem = std.mem; const u1 = @IntType(false, 1); @@ -86,7 +87,9 @@ pub const Token = struct { // parsing state requires ~40-50 bytes of stack space. // // Conforms strictly to RFC8529. -pub const StreamingJsonParser = struct { +// +// For a non-byte based wrapper, consider using TokenStream instead. +pub const StreamingParser = struct { // Current state state: State, // How many bytes we have counted for the current token @@ -109,13 +112,13 @@ pub const StreamingJsonParser = struct { const array_bit = 1; const max_stack_size = @maxValue(u8); - pub fn init() StreamingJsonParser { - var p: StreamingJsonParser = undefined; + pub fn init() StreamingParser { + var p: StreamingParser = undefined; p.reset(); return p; } - pub fn reset(p: *StreamingJsonParser) void { + pub fn reset(p: *StreamingParser) void { p.state = State.TopLevelBegin; p.count = 0; // Set before ever read in main transition function @@ -175,7 +178,7 @@ pub const StreamingJsonParser = struct { // Only call this function to generate array/object final state. pub fn fromInt(x: var) State { - std.debug.assert(x == 0 or x == 1); + debug.assert(x == 0 or x == 1); const T = @TagType(State); return State(T(x)); } @@ -205,7 +208,7 @@ pub const StreamingJsonParser = struct { // tokens. token2 is always null if token1 is null. // // There is currently no error recovery on a bad stream. - pub fn feed(p: *StreamingJsonParser, c: u8, token1: *?Token, token2: *?Token) Error!void { + pub fn feed(p: *StreamingParser, c: u8, token1: *?Token, token2: *?Token) Error!void { token1.* = null; token2.* = null; p.count += 1; @@ -217,7 +220,7 @@ pub const StreamingJsonParser = struct { } // Perform a single transition on the state machine and return any possible token. - fn transition(p: *StreamingJsonParser, c: u8, token: *?Token) Error!bool { + fn transition(p: *StreamingParser, c: u8, token: *?Token) Error!bool { switch (p.state) { State.TopLevelBegin => switch (c) { '{' => { @@ -852,10 +855,116 @@ pub const StreamingJsonParser = struct { } }; +// A small wrapper over a StreamingParser for full slices. Returns a stream of json Tokens. +pub const TokenStream = struct { + i: usize, + slice: []const u8, + parser: StreamingParser, + token: ?Token, + + pub fn init(slice: []const u8) TokenStream { + return TokenStream{ + .i = 0, + .slice = slice, + .parser = StreamingParser.init(), + .token = null, + }; + } + + pub fn next(self: *TokenStream) !?Token { + if (self.token) |token| { + self.token = null; + return token; + } + + var t1: ?Token = undefined; + var t2: ?Token = undefined; + + while (self.i < self.slice.len) { + try self.parser.feed(self.slice[self.i], &t1, &t2); + self.i += 1; + + if (t1) |token| { + self.token = t2; + return token; + } + } + + if (self.i > self.slice.len) { + try self.parser.feed(' ', &t1, &t2); + self.i += 1; + + if (t1) |token| { + return token; + } + } + + return null; + } +}; + +fn checkNext(p: *TokenStream, id: Token.Id) void { + const token = ??(p.next() catch unreachable); + debug.assert(token.id == id); +} + +test "token" { + const s = + \\{ + \\ "Image": { + \\ "Width": 800, + \\ "Height": 600, + \\ "Title": "View from 15th Floor", + \\ "Thumbnail": { + \\ "Url": "http://www.example.com/image/481989943", + \\ "Height": 125, + \\ "Width": 100 + \\ }, + \\ "Animated" : false, + \\ "IDs": [116, 943, 234, 38793] + \\ } + \\} + ; + + var p = TokenStream.init(s); + + checkNext(&p, Token.Id.ObjectBegin); + checkNext(&p, Token.Id.String); // Image + checkNext(&p, Token.Id.ObjectBegin); + checkNext(&p, Token.Id.String); // Width + checkNext(&p, Token.Id.Number); + checkNext(&p, Token.Id.String); // Height + checkNext(&p, Token.Id.Number); + checkNext(&p, Token.Id.String); // Title + checkNext(&p, Token.Id.String); + checkNext(&p, Token.Id.String); // Thumbnail + checkNext(&p, Token.Id.ObjectBegin); + checkNext(&p, Token.Id.String); // Url + checkNext(&p, Token.Id.String); + checkNext(&p, Token.Id.String); // Height + checkNext(&p, Token.Id.Number); + checkNext(&p, Token.Id.String); // Width + checkNext(&p, Token.Id.Number); + checkNext(&p, Token.Id.ObjectEnd); + checkNext(&p, Token.Id.String); // Animated + checkNext(&p, Token.Id.False); + checkNext(&p, Token.Id.String); // IDs + checkNext(&p, Token.Id.ArrayBegin); + checkNext(&p, Token.Id.Number); + checkNext(&p, Token.Id.Number); + checkNext(&p, Token.Id.Number); + checkNext(&p, Token.Id.Number); + checkNext(&p, Token.Id.ArrayEnd); + checkNext(&p, Token.Id.ObjectEnd); + checkNext(&p, Token.Id.ObjectEnd); + + debug.assert((try p.next()) == null); +} + // Validate a JSON string. This does not limit number precision so a decoder may not necessarily // be able to decode the string even if this returns true. pub fn validate(s: []const u8) bool { - var p = StreamingJsonParser.init(); + var p = StreamingParser.init(); for (s) |c, i| { var token1: ?Token = undefined; @@ -897,46 +1006,46 @@ pub const Value = union(enum) { pub fn dump(self: *const Value) void { switch (self.*) { Value.Null => { - std.debug.warn("null"); + debug.warn("null"); }, Value.Bool => |inner| { - std.debug.warn("{}", inner); + debug.warn("{}", inner); }, Value.Integer => |inner| { - std.debug.warn("{}", inner); + debug.warn("{}", inner); }, Value.Float => |inner| { - std.debug.warn("{.5}", inner); + debug.warn("{.5}", inner); }, Value.String => |inner| { - std.debug.warn("\"{}\"", inner); + debug.warn("\"{}\"", inner); }, Value.Array => |inner| { var not_first = false; - std.debug.warn("["); + debug.warn("["); for (inner.toSliceConst()) |value| { if (not_first) { - std.debug.warn(","); + debug.warn(","); } not_first = true; value.dump(); } - std.debug.warn("]"); + debug.warn("]"); }, Value.Object => |inner| { var not_first = false; - std.debug.warn("{{"); + debug.warn("{{"); var it = inner.iterator(); while (it.next()) |entry| { if (not_first) { - std.debug.warn(","); + debug.warn(","); } not_first = true; - std.debug.warn("\"{}\":", entry.key); + debug.warn("\"{}\":", entry.key); entry.value.dump(); } - std.debug.warn("}}"); + debug.warn("}}"); }, } } @@ -952,53 +1061,53 @@ pub const Value = union(enum) { fn dumpIndentLevel(self: *const Value, indent: usize, level: usize) void { switch (self.*) { Value.Null => { - std.debug.warn("null"); + debug.warn("null"); }, Value.Bool => |inner| { - std.debug.warn("{}", inner); + debug.warn("{}", inner); }, Value.Integer => |inner| { - std.debug.warn("{}", inner); + debug.warn("{}", inner); }, Value.Float => |inner| { - std.debug.warn("{.5}", inner); + debug.warn("{.5}", inner); }, Value.String => |inner| { - std.debug.warn("\"{}\"", inner); + debug.warn("\"{}\"", inner); }, Value.Array => |inner| { var not_first = false; - std.debug.warn("[\n"); + debug.warn("[\n"); for (inner.toSliceConst()) |value| { if (not_first) { - std.debug.warn(",\n"); + debug.warn(",\n"); } not_first = true; padSpace(level + indent); value.dumpIndentLevel(indent, level + indent); } - std.debug.warn("\n"); + debug.warn("\n"); padSpace(level); - std.debug.warn("]"); + debug.warn("]"); }, Value.Object => |inner| { var not_first = false; - std.debug.warn("{{\n"); + debug.warn("{{\n"); var it = inner.iterator(); while (it.next()) |entry| { if (not_first) { - std.debug.warn(",\n"); + debug.warn(",\n"); } not_first = true; padSpace(level + indent); - std.debug.warn("\"{}\": ", entry.key); + debug.warn("\"{}\": ", entry.key); entry.value.dumpIndentLevel(indent, level + indent); } - std.debug.warn("\n"); + debug.warn("\n"); padSpace(level); - std.debug.warn("}}"); + debug.warn("}}"); }, } } @@ -1006,13 +1115,13 @@ pub const Value = union(enum) { fn padSpace(indent: usize) void { var i: usize = 0; while (i < indent) : (i += 1) { - std.debug.warn(" "); + debug.warn(" "); } } }; // A non-stream JSON parser which constructs a tree of Value's. -pub const JsonParser = struct { +pub const Parser = struct { allocator: *Allocator, state: State, copy_strings: bool, @@ -1026,8 +1135,8 @@ pub const JsonParser = struct { Simple, }; - pub fn init(allocator: *Allocator, copy_strings: bool) JsonParser { - return JsonParser{ + pub fn init(allocator: *Allocator, copy_strings: bool) Parser { + return Parser{ .allocator = allocator, .state = State.Simple, .copy_strings = copy_strings, @@ -1035,52 +1144,26 @@ pub const JsonParser = struct { }; } - pub fn deinit(p: *JsonParser) void { + pub fn deinit(p: *Parser) void { p.stack.deinit(); } - pub fn reset(p: *JsonParser) void { + pub fn reset(p: *Parser) void { p.state = State.Simple; p.stack.shrink(0); } - pub fn parse(p: *JsonParser, input: []const u8) !ValueTree { - var mp = StreamingJsonParser.init(); + pub fn parse(p: *Parser, input: []const u8) !ValueTree { + var s = TokenStream.init(input); var arena = ArenaAllocator.init(p.allocator); errdefer arena.deinit(); - for (input) |c, i| { - var mt1: ?Token = undefined; - var mt2: ?Token = undefined; - - try mp.feed(c, &mt1, &mt2); - if (mt1) |t1| { - try p.transition(&arena.allocator, input, i, t1); - - if (mt2) |t2| { - try p.transition(&arena.allocator, input, i, t2); - } - } + while (try s.next()) |token| { + try p.transition(&arena.allocator, input, s.i - 1, token); } - // Handle top-level lonely number values. - { - const i = input.len; - var mt1: ?Token = undefined; - var mt2: ?Token = undefined; - - try mp.feed(' ', &mt1, &mt2); - if (mt1) |t1| { - try p.transition(&arena.allocator, input, i, t1); - } - } - - if (!mp.complete) { - return error.IncompleteJsonInput; - } - - std.debug.assert(p.stack.len == 1); + debug.assert(p.stack.len == 1); return ValueTree{ .arena = arena, @@ -1090,7 +1173,7 @@ pub const JsonParser = struct { // Even though p.allocator exists, we take an explicit allocator so that allocation state // can be cleaned up on error correctly during a `parse` on call. - fn transition(p: *JsonParser, allocator: *Allocator, input: []const u8, i: usize, token: *const Token) !void { + fn transition(p: *Parser, allocator: *Allocator, input: []const u8, i: usize, token: *const Token) !void { switch (p.state) { State.ObjectKey => switch (token.id) { Token.Id.ObjectEnd => { @@ -1223,7 +1306,7 @@ pub const JsonParser = struct { } } - fn pushToParent(p: *JsonParser, value: *const Value) !void { + fn pushToParent(p: *Parser, value: *const Value) !void { switch (p.stack.at(p.stack.len - 1)) { // Object Parent -> [ ..., object, , value ] Value.String => |key| { @@ -1244,14 +1327,14 @@ pub const JsonParser = struct { } } - fn parseString(p: *JsonParser, allocator: *Allocator, token: *const Token, input: []const u8, i: usize) !Value { + fn parseString(p: *Parser, allocator: *Allocator, token: *const Token, input: []const u8, i: usize) !Value { // TODO: We don't strictly have to copy values which do not contain any escape // characters if flagged with the option. const slice = token.slice(input, i); return Value{ .String = try mem.dupe(p.allocator, u8, slice) }; } - fn parseNumber(p: *JsonParser, token: *const Token, input: []const u8, i: usize) !Value { + fn parseNumber(p: *Parser, token: *const Token, input: []const u8, i: usize) !Value { return if (token.number_is_integer) Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } else @@ -1259,10 +1342,8 @@ pub const JsonParser = struct { } }; -const debug = std.debug; - test "json parser dynamic" { - var p = JsonParser.init(std.debug.global_allocator, false); + var p = Parser.init(debug.global_allocator, false); defer p.deinit(); const s = -- cgit v1.2.3 From 4fc601895b9f89bf0d3d3c1de1b0bbc959444298 Mon Sep 17 00:00:00 2001 From: isaachier Date: Wed, 6 Jun 2018 14:09:47 -0400 Subject: Fix const-ness of buffer in replaceContents method (#1065) --- std/buffer.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/std/buffer.zig b/std/buffer.zig index 3b2936d223..469f81709b 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -28,7 +28,6 @@ pub const Buffer = struct { /// Must deinitialize with deinit. /// None of the other operations are valid until you do one of these: /// * ::replaceContents - /// * ::replaceContentsBuffer /// * ::resize pub fn initNull(allocator: *Allocator) Buffer { return Buffer{ .list = ArrayList(u8).init(allocator) }; @@ -116,7 +115,7 @@ pub const Buffer = struct { return mem.eql(u8, self.list.items[start..l], m); } - pub fn replaceContents(self: *const Buffer, m: []const u8) !void { + pub fn replaceContents(self: *Buffer, m: []const u8) !void { try self.resize(m.len); mem.copy(u8, self.list.toSlice(), m); } -- cgit v1.2.3 From b11c5d8f8256fc19e08eacfc50be209878f00e73 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 6 Jun 2018 15:36:47 -0400 Subject: fix std.os.windows.PathFileExists specified in the wrong DLL (#1066) closes #1054 --- std/os/file.zig | 3 ++- std/os/windows/index.zig | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/std/os/file.zig b/std/os/file.zig index d5af55b5e4..f15fa77688 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -123,7 +123,8 @@ pub const File = struct { } return true; } else if (is_windows) { - if (os.windows.PathFileExists(path_with_null.ptr) == os.windows.TRUE) { + // TODO do not depend on shlwapi.dll + if (os.windows.PathFileExistsA(path_with_null.ptr) == os.windows.TRUE) { return true; } diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 53e12500e7..0934c3fd90 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -123,8 +123,6 @@ pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: * pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; -pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL; - pub extern "kernel32" stdcallcc fn ReadFile( in_hFile: HANDLE, out_lpBuffer: *c_void, @@ -163,6 +161,8 @@ pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; +pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL; + pub const PROV_RSA_FULL = 1; pub const BOOL = c_int; -- cgit v1.2.3 From 31aefa6a2179dfae752020195fb193c6333bae7e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 7 Jun 2018 17:26:41 -0400 Subject: fix structs that contain types which require comptime Now, if a struct has any fields which require comptime, such as `type`, then the struct is marked as requiring comptime as well. Same goes for unions. This means that a function will implicitly be called at comptime if the return type is a struct which contains a field of type `type`. closes #586 --- src/all_types.hpp | 8 ++++ src/analyze.cpp | 23 +++++++++- src/ir.cpp | 112 ++++++++++++++---------------------------------- test/cases/eval.zig | 13 ++++++ test/compile_errors.zig | 2 +- 5 files changed, 77 insertions(+), 81 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 3b2ea02b71..b193fe8ae8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1037,6 +1037,10 @@ struct TypeTableEntryStruct { // whether we've finished resolving it bool complete; + // whether any of the fields require comptime + // the value is not valid until zero_bits_known == true + bool requires_comptime; + bool zero_bits_loop_flag; bool zero_bits_known; uint32_t abi_alignment; // also figured out with zero_bits pass @@ -1105,6 +1109,10 @@ struct TypeTableEntryUnion { // whether we've finished resolving it bool complete; + // whether any of the fields require comptime + // the value is not valid until zero_bits_known == true + bool requires_comptime; + bool zero_bits_loop_flag; bool zero_bits_known; uint32_t abi_alignment; // also figured out with zero_bits pass diff --git a/src/analyze.cpp b/src/analyze.cpp index 93373f6ec2..e05fb23237 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2533,6 +2533,10 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { continue; } + if (type_requires_comptime(field_type)) { + struct_type->data.structure.requires_comptime = true; + } + if (!type_has_bits(field_type)) continue; @@ -2724,6 +2728,11 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { } union_field->type_entry = field_type; + if (type_requires_comptime(field_type)) { + union_type->data.unionation.requires_comptime = true; + } + + if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) { ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value, buf_sprintf("non-enum union field assignment")); @@ -4944,17 +4953,29 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { case TypeTableEntryIdArgTuple: return true; case TypeTableEntryIdArray: + return type_requires_comptime(type_entry->data.array.child_type); case TypeTableEntryIdStruct: + assert(type_has_zero_bits_known(type_entry)); + return type_entry->data.structure.requires_comptime; case TypeTableEntryIdUnion: + assert(type_has_zero_bits_known(type_entry)); + return type_entry->data.unionation.requires_comptime; case TypeTableEntryIdMaybe: + return type_requires_comptime(type_entry->data.maybe.child_type); case TypeTableEntryIdErrorUnion: + return type_requires_comptime(type_entry->data.error_union.payload_type); + case TypeTableEntryIdPointer: + if (type_entry->data.pointer.child_type->id == TypeTableEntryIdOpaque) { + return false; + } else { + return type_requires_comptime(type_entry->data.pointer.child_type); + } case TypeTableEntryIdEnum: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: - case TypeTableEntryIdPointer: case TypeTableEntryIdVoid: case TypeTableEntryIdUnreachable: case TypeTableEntryIdPromise: diff --git a/src/ir.cpp b/src/ir.cpp index 3486e8c047..304127b099 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11624,61 +11624,6 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi zig_unreachable(); } -enum VarClassRequired { - VarClassRequiredAny, - VarClassRequiredConst, - VarClassRequiredIllegal, -}; - -static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { - switch (type_entry->id) { - case TypeTableEntryIdInvalid: - zig_unreachable(); - case TypeTableEntryIdUnreachable: - return VarClassRequiredIllegal; - case TypeTableEntryIdBool: - case TypeTableEntryIdInt: - case TypeTableEntryIdFloat: - case TypeTableEntryIdVoid: - case TypeTableEntryIdErrorSet: - case TypeTableEntryIdFn: - case TypeTableEntryIdPromise: - return VarClassRequiredAny; - case TypeTableEntryIdComptimeFloat: - case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefined: - case TypeTableEntryIdBlock: - case TypeTableEntryIdNull: - case TypeTableEntryIdOpaque: - case TypeTableEntryIdMetaType: - case TypeTableEntryIdNamespace: - case TypeTableEntryIdBoundFn: - case TypeTableEntryIdArgTuple: - return VarClassRequiredConst; - - case TypeTableEntryIdPointer: - if (type_entry->data.pointer.child_type->id == TypeTableEntryIdOpaque) { - return VarClassRequiredAny; - } else { - return get_var_class_required(type_entry->data.pointer.child_type); - } - case TypeTableEntryIdArray: - return get_var_class_required(type_entry->data.array.child_type); - case TypeTableEntryIdMaybe: - return get_var_class_required(type_entry->data.maybe.child_type); - case TypeTableEntryIdErrorUnion: - return get_var_class_required(type_entry->data.error_union.payload_type); - - case TypeTableEntryIdStruct: - case TypeTableEntryIdEnum: - case TypeTableEntryIdUnion: - // TODO check the fields of these things and make sure that they don't recursively - // contain any of the other variable classes - return VarClassRequiredAny; - } - zig_unreachable(); -} - static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { VariableTableEntry *var = decl_var_instruction->var; @@ -11713,36 +11658,41 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc if (type_is_invalid(result_type)) { result_type = ira->codegen->builtin_types.entry_invalid; } else { - switch (get_var_class_required(result_type)) { - case VarClassRequiredIllegal: + type_ensure_zero_bits_known(ira->codegen, result_type); + if (type_is_invalid(result_type)) { + result_type = ira->codegen->builtin_types.entry_invalid; + } + } + + if (!type_is_invalid(result_type)) { + if (result_type->id == TypeTableEntryIdUnreachable || + result_type->id == TypeTableEntryIdOpaque) + { + ir_add_error_node(ira, source_node, + buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name))); + result_type = ira->codegen->builtin_types.entry_invalid; + } else if (type_requires_comptime(result_type)) { + var_class_requires_const = true; + if (!var->src_is_const && !is_comptime_var) { ir_add_error_node(ira, source_node, - buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name))); + buf_sprintf("variable of type '%s' must be const or comptime", + buf_ptr(&result_type->name))); result_type = ira->codegen->builtin_types.entry_invalid; - break; - case VarClassRequiredConst: + } + } else { + if (casted_init_value->value.special == ConstValSpecialStatic && + casted_init_value->value.type->id == TypeTableEntryIdFn && + casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) + { var_class_requires_const = true; if (!var->src_is_const && !is_comptime_var) { - ir_add_error_node(ira, source_node, - buf_sprintf("variable of type '%s' must be const or comptime", - buf_ptr(&result_type->name))); + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("functions marked inline must be stored in const or comptime var")); + AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node; + add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); result_type = ira->codegen->builtin_types.entry_invalid; } - break; - case VarClassRequiredAny: - if (casted_init_value->value.special == ConstValSpecialStatic && - casted_init_value->value.type->id == TypeTableEntryIdFn && - casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) - { - var_class_requires_const = true; - if (!var->src_is_const && !is_comptime_var) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("functions marked inline must be stored in const or comptime var")); - AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node; - add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); - result_type = ira->codegen->builtin_types.entry_invalid; - } - } - break; + } } } @@ -12623,6 +12573,10 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal inst_fn_type_id.return_type = specified_return_type; } + type_ensure_zero_bits_known(ira->codegen, specified_return_type); + if (type_is_invalid(specified_return_type)) + return ira->codegen->builtin_types.entry_invalid; + if (type_requires_comptime(specified_return_type)) { // Throw out our work and call the function as if it were comptime. return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto); diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 461408afea..9612466a86 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -610,3 +610,16 @@ test "slice of type" { } } } + +const Wrapper = struct { + T: type, +}; + +fn wrap(comptime T: type) Wrapper { + return Wrapper{ .T = T }; +} + +test "function which returns struct with type field causes implicit comptime" { + const ty = wrap(i32).T; + assert(ty == i32); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index c995cd679e..102c4e428d 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3329,7 +3329,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:9:4: error: variable of type 'comptime_float' must be const or comptime", ".tmp_source.zig:10:4: error: variable of type '(block)' must be const or comptime", ".tmp_source.zig:11:4: error: variable of type '(null)' must be const or comptime", - ".tmp_source.zig:12:4: error: variable of type 'Opaque' must be const or comptime", + ".tmp_source.zig:12:4: error: variable of type 'Opaque' not allowed", ".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime", ".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime", ".tmp_source.zig:15:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", -- cgit v1.2.3 From 688ff2830d82ea36a9f022ecb7cf4c2bf2e4c586 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 7 Jun 2018 19:10:45 -0400 Subject: langref: automatic update of builtin.zig now the docs can't get out of date for this See #367 --- doc/docgen.zig | 18 +++ doc/langref.html.in | 423 +--------------------------------------------------- src/codegen.cpp | 21 ++- src/codegen.hpp | 2 + src/main.cpp | 15 ++ 5 files changed, 51 insertions(+), 428 deletions(-) diff --git a/doc/docgen.zig b/doc/docgen.zig index fed4bb8eba..ed0e1be273 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -300,6 +300,7 @@ const Link = struct { const Node = union(enum) { Content: []const u8, Nav, + Builtin, HeaderOpen: HeaderOpen, SeeAlso: []const SeeAlsoItem, Code: Code, @@ -356,6 +357,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { _ = try eatToken(tokenizer, Token.Id.BracketClose); try nodes.append(Node.Nav); + } else if (mem.eql(u8, tag_name, "builtin")) { + _ = try eatToken(tokenizer, Token.Id.BracketClose); + try nodes.append(Node.Builtin); } else if (mem.eql(u8, tag_name, "header_open")) { _ = try eatToken(tokenizer, Token.Id.Separator); const content_token = try eatToken(tokenizer, Token.Id.TagContent); @@ -690,6 +694,9 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 { fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void { var code_progress_index: usize = 0; + + const builtin_code = try escapeHtml(allocator, try getBuiltinCode(allocator, zig_exe)); + for (toc.nodes) |node| { switch (node) { Node.Content => |data| { @@ -704,6 +711,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var Node.Nav => { try out.write(toc.toc); }, + Node.Builtin => { + try out.print("
{}
", builtin_code); + }, Node.HeaderOpen => |info| { try out.print("{}\n", info.n, info.url, info.name, info.n); }, @@ -1060,3 +1070,11 @@ fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.Ex } return result; } + +fn getBuiltinCode(allocator: *mem.Allocator, zig_exe: []const u8) ![]const u8 { + const result = try exec(allocator, []const []const u8{ + zig_exe, + "builtin", + }); + return result.stdout; +} diff --git a/doc/langref.html.in b/doc/langref.html.in index 4359cadb58..adb5470d98 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5474,425 +5474,7 @@ const separator = if (builtin.os == builtin.Os.windows) '\\' else '/';

Example of what is imported with @import("builtin"):

- {#code_begin|syntax#} -pub const StackTrace = struct { - index: usize, - instruction_addresses: []usize, -}; - -pub const Os = enum { - freestanding, - ananas, - cloudabi, - dragonfly, - freebsd, - fuchsia, - ios, - kfreebsd, - linux, - lv2, - macosx, - netbsd, - openbsd, - solaris, - windows, - haiku, - minix, - rtems, - nacl, - cnk, - aix, - cuda, - nvcl, - amdhsa, - ps4, - elfiamcu, - tvos, - watchos, - mesa3d, - contiki, - amdpal, - zen, -}; - -pub const Arch = enum { - armv8_3a, - armv8_2a, - armv8_1a, - armv8, - armv8r, - armv8m_baseline, - armv8m_mainline, - armv7, - armv7em, - armv7m, - armv7s, - armv7k, - armv7ve, - armv6, - armv6m, - armv6k, - armv6t2, - armv5, - armv5te, - armv4t, - armebv8_3a, - armebv8_2a, - armebv8_1a, - armebv8, - armebv8r, - armebv8m_baseline, - armebv8m_mainline, - armebv7, - armebv7em, - armebv7m, - armebv7s, - armebv7k, - armebv7ve, - armebv6, - armebv6m, - armebv6k, - armebv6t2, - armebv5, - armebv5te, - armebv4t, - aarch64, - aarch64_be, - arc, - avr, - bpfel, - bpfeb, - hexagon, - mips, - mipsel, - mips64, - mips64el, - msp430, - nios2, - powerpc, - powerpc64, - powerpc64le, - r600, - amdgcn, - riscv32, - riscv64, - sparc, - sparcv9, - sparcel, - s390x, - tce, - tcele, - thumb, - thumbeb, - i386, - x86_64, - xcore, - nvptx, - nvptx64, - le32, - le64, - amdil, - amdil64, - hsail, - hsail64, - spir, - spir64, - kalimbav3, - kalimbav4, - kalimbav5, - shave, - lanai, - wasm32, - wasm64, - renderscript32, - renderscript64, -}; - -pub const Environ = enum { - unknown, - gnu, - gnuabin32, - gnuabi64, - gnueabi, - gnueabihf, - gnux32, - code16, - eabi, - eabihf, - android, - musl, - musleabi, - musleabihf, - msvc, - itanium, - cygnus, - amdopencl, - coreclr, - opencl, - simulator, -}; - -pub const ObjectFormat = enum { - unknown, - coff, - elf, - macho, - wasm, -}; - -pub const GlobalLinkage = enum { - Internal, - Strong, - Weak, - LinkOnce, -}; - -pub const AtomicOrder = enum { - Unordered, - Monotonic, - Acquire, - Release, - AcqRel, - SeqCst, -}; - -pub const AtomicRmwOp = enum { - Xchg, - Add, - Sub, - And, - Nand, - Or, - Xor, - Max, - Min, -}; - -pub const Mode = enum { - Debug, - ReleaseSafe, - ReleaseFast, - ReleaseSmall, -}; - -pub const TypeId = enum { - Type, - Void, - Bool, - NoReturn, - Int, - Float, - Pointer, - Array, - Struct, - ComptimeFloat, - ComptimeInt, - Undefined, - Null, - Nullable, - ErrorUnion, - ErrorSet, - Enum, - Union, - Fn, - Namespace, - Block, - BoundFn, - ArgTuple, - Opaque, - Promise, -}; - -pub const TypeInfo = union(TypeId) { - Type: void, - Void: void, - Bool: void, - NoReturn: void, - Int: Int, - Float: Float, - Pointer: Pointer, - Array: Array, - Struct: Struct, - ComptimeFloat: void, - ComptimeInt: void, - Undefined: void, - Null: void, - Nullable: Nullable, - ErrorUnion: ErrorUnion, - ErrorSet: ErrorSet, - Enum: Enum, - Union: Union, - Fn: Fn, - Namespace: void, - Block: void, - BoundFn: Fn, - ArgTuple: void, - Opaque: void, - Promise: Promise, - - - pub const Int = struct { - is_signed: bool, - bits: u8, - }; - - pub const Float = struct { - bits: u8, - }; - - pub const Pointer = struct { - is_const: bool, - is_volatile: bool, - alignment: u32, - child: type, - }; - - pub const Array = struct { - len: usize, - child: type, - }; - - pub const ContainerLayout = enum { - Auto, - Extern, - Packed, - }; - - pub const StructField = struct { - name: []const u8, - offset: ?usize, - field_type: type, - }; - - pub const Struct = struct { - layout: ContainerLayout, - fields: []StructField, - defs: []Definition, - }; - - pub const Nullable = struct { - child: type, - }; - - pub const ErrorUnion = struct { - error_set: type, - payload: type, - }; - - pub const Error = struct { - name: []const u8, - value: usize, - }; - - pub const ErrorSet = struct { - errors: []Error, - }; - - pub const EnumField = struct { - name: []const u8, - value: usize, - }; - - pub const Enum = struct { - layout: ContainerLayout, - tag_type: type, - fields: []EnumField, - defs: []Definition, - }; - - pub const UnionField = struct { - name: []const u8, - enum_field: ?EnumField, - field_type: type, - }; - - pub const Union = struct { - layout: ContainerLayout, - tag_type: type, - fields: []UnionField, - defs: []Definition, - }; - - pub const CallingConvention = enum { - Unspecified, - C, - Cold, - Naked, - Stdcall, - Async, - }; - - pub const FnArg = struct { - is_generic: bool, - is_noalias: bool, - arg_type: type, - }; - - pub const Fn = struct { - calling_convention: CallingConvention, - is_generic: bool, - is_var_args: bool, - return_type: type, - async_allocator_type: type, - args: []FnArg, - }; - - pub const Promise = struct { - child: type, - }; - - pub const Definition = struct { - name: []const u8, - is_pub: bool, - data: Data, - - pub const Data = union(enum) { - Type: type, - Var: type, - Fn: FnDef, - - pub const FnDef = struct { - fn_type: type, - inline_type: Inline, - calling_convention: CallingConvention, - is_var_args: bool, - is_extern: bool, - is_export: bool, - lib_name: ?[]const u8, - return_type: type, - arg_names: [][] const u8, - - pub const Inline = enum { - Auto, - Always, - Never, - }; - }; - }; - }; -}; - -pub const FloatMode = enum { - Optimized, - Strict, -}; - -pub const Endian = enum { - Big, - Little, -}; - -pub const endian = Endian.Little; -pub const is_test = true; -pub const os = Os.linux; -pub const arch = Arch.x86_64; -pub const environ = Environ.gnu; -pub const object_format = ObjectFormat.elf; -pub const mode = Mode.Debug; -pub const link_libc = false; -pub const have_error_return_tracing = true; -pub const __zig_test_fn_slice = {}; // overwritten later - {#code_end#} + {#builtin#} {#see_also|Build Mode#} {#header_close#} {#header_open|Root Source File#} @@ -6053,8 +5635,7 @@ pub fn build(b: *Builder) void { b.default_step.dependOn(&exe.step); } {#code_end#} - {#header_close#} - {#header_open|Terminal#} +

terminal

$ zig build
 $ ./test
 all your base are belong to us
diff --git a/src/codegen.cpp b/src/codegen.cpp index 7f95f335d1..fb59ca7569 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6335,13 +6335,7 @@ static const char *build_mode_to_str(BuildMode build_mode) { zig_unreachable(); } -static void define_builtin_compile_vars(CodeGen *g) { - if (g->std_package == nullptr) - return; - - const char *builtin_zig_basename = "builtin.zig"; - Buf *builtin_zig_path = buf_alloc(); - os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); +Buf *codegen_generate_builtin_source(CodeGen *g) { Buf *contents = buf_alloc(); // Modifications to this struct must be coordinated with code that does anything with @@ -6707,6 +6701,19 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n"); + + return contents; +} + +static void define_builtin_compile_vars(CodeGen *g) { + if (g->std_package == nullptr) + return; + + const char *builtin_zig_basename = "builtin.zig"; + Buf *builtin_zig_path = buf_alloc(); + os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); + + Buf *contents = codegen_generate_builtin_source(g); ensure_cache_dir(g); os_write_file(builtin_zig_path, contents); diff --git a/src/codegen.hpp b/src/codegen.hpp index a7a4b748c4..b5f3374ec4 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -59,5 +59,7 @@ void codegen_add_object(CodeGen *g, Buf *object_path); void codegen_translate_c(CodeGen *g, Buf *path); +Buf *codegen_generate_builtin_source(CodeGen *g); + #endif diff --git a/src/main.cpp b/src/main.cpp index 9c36f9b091..c63a143bff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ static int usage(const char *arg0) { " build-exe [source] create executable from source or object files\n" " build-lib [source] create library from source or object files\n" " build-obj [source] create object from source or assembly\n" + " builtin show the source code of that @import(\"builtin\")\n" " run [source] create executable and run immediately\n" " translate-c [source] convert c code to zig code\n" " targets list available compilation targets\n" @@ -214,6 +215,7 @@ static Buf *resolve_zig_lib_dir(void) { enum Cmd { CmdInvalid, CmdBuild, + CmdBuiltin, CmdRun, CmdTest, CmdVersion, @@ -664,6 +666,8 @@ int main(int argc, char **argv) { out_type = OutTypeExe; } else if (strcmp(arg, "targets") == 0) { cmd = CmdTargets; + } else if (strcmp(arg, "builtin") == 0) { + cmd = CmdBuiltin; } else { fprintf(stderr, "Unrecognized command: %s\n", arg); return usage(arg0); @@ -681,6 +685,7 @@ int main(int argc, char **argv) { return usage(arg0); } break; + case CmdBuiltin: case CmdVersion: case CmdZen: case CmdTargets: @@ -727,6 +732,16 @@ int main(int argc, char **argv) { } switch (cmd) { + case CmdBuiltin: { + Buf *zig_lib_dir_buf = resolve_zig_lib_dir(); + CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, zig_lib_dir_buf); + Buf *builtin_source = codegen_generate_builtin_source(g); + if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { + fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout))); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + } case CmdRun: case CmdBuild: case CmdTranslateC: -- cgit v1.2.3 From b65203f5736199bdc8d98d27728be5e92a17d565 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 7 Jun 2018 19:50:25 -0400 Subject: remove @canImplicitCast builtin nobody will miss it --- doc/langref.html.in | 11 +--------- src/all_types.hpp | 9 -------- src/codegen.cpp | 2 -- src/ir.cpp | 60 ----------------------------------------------------- src/ir_print.cpp | 11 ---------- test/cases/misc.zig | 8 ------- 6 files changed, 1 insertion(+), 100 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index adb5470d98..6a1f1c3102 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -3845,9 +3845,6 @@ pub fn printValue(self: *OutStream, value: var) !void { return self.printInt(T, value); } else if (@isFloat(T)) { return self.printFloat(T, value); - } else if (@canImplicitCast([]const u8, value)) { - const casted_value = ([]const u8)(value); - return self.write(casted_value); } else { @compileError("Unable to print type '" ++ @typeName(T) ++ "'"); } @@ -4102,12 +4099,6 @@ comptime {

{#see_also|Import from C Header File|@cImport|@cDefine|@cInclude#} {#header_close#} - {#header_open|@canImplicitCast#} -
@canImplicitCast(comptime T: type, value) bool
-

- Returns whether a value can be implicitly casted to a given type. -

- {#header_close#} {#header_open|@clz#}
@clz(x: T) U

@@ -6136,7 +6127,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index b193fe8ae8..9d41b86fa0 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1354,7 +1354,6 @@ enum BuiltinFnId { BuiltinFnIdSetRuntimeSafety, BuiltinFnIdSetFloatMode, BuiltinFnIdTypeName, - BuiltinFnIdCanImplicitCast, BuiltinFnIdPanic, BuiltinFnIdPtrCast, BuiltinFnIdBitCast, @@ -2065,7 +2064,6 @@ enum IrInstructionId { IrInstructionIdCheckSwitchProngs, IrInstructionIdCheckStatementIsVoid, IrInstructionIdTypeName, - IrInstructionIdCanImplicitCast, IrInstructionIdDeclRef, IrInstructionIdPanic, IrInstructionIdTagName, @@ -2858,13 +2856,6 @@ struct IrInstructionTypeName { IrInstruction *type_value; }; -struct IrInstructionCanImplicitCast { - IrInstruction base; - - IrInstruction *type_value; - IrInstruction *target_value; -}; - struct IrInstructionDeclRef { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index fb59ca7569..d156a8a178 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4625,7 +4625,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdCheckSwitchProngs: case IrInstructionIdCheckStatementIsVoid: case IrInstructionIdTypeName: - case IrInstructionIdCanImplicitCast: case IrInstructionIdDeclRef: case IrInstructionIdSwitchVar: case IrInstructionIdOffsetOf: @@ -6277,7 +6276,6 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1); create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1); create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1); - create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2); create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1); create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6); create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6); diff --git a/src/ir.cpp b/src/ir.cpp index 304127b099..3c9adab796 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -585,10 +585,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeName *) { return IrInstructionIdTypeName; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionCanImplicitCast *) { - return IrInstructionIdCanImplicitCast; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclRef *) { return IrInstructionIdDeclRef; } @@ -2348,20 +2344,6 @@ static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } -static IrInstruction *ir_build_can_implicit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *type_value, IrInstruction *target_value) -{ - IrInstructionCanImplicitCast *instruction = ir_build_instruction( - irb, scope, source_node); - instruction->type_value = type_value; - instruction->target_value = target_value; - - ir_ref_instruction(type_value, irb->current_basic_block); - ir_ref_instruction(target_value, irb->current_basic_block); - - return &instruction->base; -} - static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, Tld *tld, LVal lval) { @@ -4132,21 +4114,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, type_name, lval); } - case BuiltinFnIdCanImplicitCast: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_instruction) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_instruction) - return arg1_value; - - IrInstruction *can_implicit_cast = ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, can_implicit_cast, lval); - } case BuiltinFnIdPanic: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -18405,30 +18372,6 @@ static TypeTableEntry *ir_analyze_instruction_check_statement_is_void(IrAnalyze return ira->codegen->builtin_types.entry_void; } -static TypeTableEntry *ir_analyze_instruction_can_implicit_cast(IrAnalyze *ira, - IrInstructionCanImplicitCast *instruction) -{ - IrInstruction *type_value = instruction->type_value->other; - TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); - if (type_is_invalid(type_entry)) - return ira->codegen->builtin_types.entry_invalid; - - IrInstruction *target_value = instruction->target_value->other; - if (type_is_invalid(target_value->value.type)) - return ira->codegen->builtin_types.entry_invalid; - - ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, type_entry, target_value->value.type, - target_value); - - if (result == ImplicitCastMatchResultReportedError) { - zig_panic("TODO refactor implicit cast tester to return bool without reporting errors"); - } - - ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_bool = (result == ImplicitCastMatchResultYes); - return ira->codegen->builtin_types.entry_bool; -} - static TypeTableEntry *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic *instruction) { IrInstruction *msg = instruction->msg->other; if (type_is_invalid(msg->value.type)) @@ -19762,8 +19705,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction); case IrInstructionIdCheckStatementIsVoid: return ir_analyze_instruction_check_statement_is_void(ira, (IrInstructionCheckStatementIsVoid *)instruction); - case IrInstructionIdCanImplicitCast: - return ir_analyze_instruction_can_implicit_cast(ira, (IrInstructionCanImplicitCast *)instruction); case IrInstructionIdDeclRef: return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction); case IrInstructionIdPanic: @@ -20043,7 +19984,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdIntToEnum: case IrInstructionIdIntToErr: case IrInstructionIdErrToInt: - case IrInstructionIdCanImplicitCast: case IrInstructionIdDeclRef: case IrInstructionIdErrName: case IrInstructionIdTypeName: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 3c177a8bbf..776ef64566 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -913,14 +913,6 @@ static void ir_print_tag_name(IrPrint *irp, IrInstructionTagName *instruction) { ir_print_other_instruction(irp, instruction->target); } -static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCast *instruction) { - fprintf(irp->f, "@canImplicitCast("); - ir_print_other_instruction(irp, instruction->type_value); - fprintf(irp->f, ","); - ir_print_other_instruction(irp, instruction->target_value); - fprintf(irp->f, ")"); -} - static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) { fprintf(irp->f, "&"); if (instruction->align_value != nullptr) { @@ -1524,9 +1516,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTagName: ir_print_tag_name(irp, (IrInstructionTagName *)instruction); break; - case IrInstructionIdCanImplicitCast: - ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction); - break; case IrInstructionIdPtrType: ir_print_ptr_type(irp, (IrInstructionPtrType *)instruction); break; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 9450cf5e6e..369d8e5cf3 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -523,14 +523,6 @@ test "@typeId" { } } -test "@canImplicitCast" { - comptime { - assert(@canImplicitCast(i64, i32(3))); - assert(!@canImplicitCast(i32, f32(1.234))); - assert(@canImplicitCast([]const u8, "aoeu")); - } -} - test "@typeName" { const Struct = struct {}; const Union = union { -- cgit v1.2.3 From f0b6dac1f2d37ea9eff0116bec34e9b2be9f3ce7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 7 Jun 2018 22:19:00 -0400 Subject: add implicit casts from `*[N]T` * to `[]T` * to `[*]T` See #770 --- src/all_types.hpp | 1 + src/codegen.cpp | 26 ++++++++++- src/ir.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test/cases/cast.zig | 16 +++++++ 4 files changed, 163 insertions(+), 2 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 9d41b86fa0..c671682363 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -583,6 +583,7 @@ enum CastOp { CastOpNumLitToConcrete, CastOpErrSet, CastOpBitCast, + CastOpPtrOfArrayToSlice, }; struct AstNodeFnCallExpr { diff --git a/src/codegen.cpp b/src/codegen.cpp index d156a8a178..fab2ad659e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2530,7 +2530,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, assert(wanted_type->data.structure.is_slice); assert(actual_type->id == TypeTableEntryIdArray); - TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; + TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type; @@ -2576,6 +2576,29 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, return expr_val; case CastOpBitCast: return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); + case CastOpPtrOfArrayToSlice: { + assert(cast_instruction->tmp_ptr); + assert(actual_type->id == TypeTableEntryIdPointer); + TypeTableEntry *array_type = actual_type->data.pointer.child_type; + assert(array_type->id == TypeTableEntryIdArray); + + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + slice_ptr_index, ""); + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->type_ref), + LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false), + }; + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + slice_len_index, ""); + LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref, + array_type->data.array.len, false); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + + return cast_instruction->tmp_ptr; + } } zig_unreachable(); } @@ -3815,7 +3838,6 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst } else { end_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false); } - if (want_runtime_safety) { add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); if (instruction->end) { diff --git a/src/ir.cpp b/src/ir.cpp index 3c9adab796..cc4ffb44a9 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -108,6 +108,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, uint32_t new_align); +static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type, uint32_t new_align); ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == TypeTableEntryIdPointer); @@ -8024,6 +8025,33 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } } + // implicit *[N]T to [*]T + if (expected_type->id == TypeTableEntryIdPointer && + expected_type->data.pointer.ptr_len == PtrLenUnknown && + actual_type->id == TypeTableEntryIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == TypeTableEntryIdArray && + types_match_const_cast_only(ira, expected_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + { + return ImplicitCastMatchResultYes; + } + + // implicit *[N]T to []T + if (is_slice(expected_type) && + actual_type->id == TypeTableEntryIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) + { + TypeTableEntry *slice_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == TypeTableEntryIdPointer); + if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + { + return ImplicitCastMatchResultYes; + } + } + // implicit [N]T to ?[]const T if (expected_type->id == TypeTableEntryIdMaybe && is_slice(expected_type->data.maybe.child_type) && @@ -8699,6 +8727,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, zig_unreachable(); case CastOpErrSet: case CastOpBitCast: + case CastOpPtrOfArrayToSlice: zig_panic("TODO"); case CastOpNoop: { @@ -8786,6 +8815,63 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst } } +static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *value, TypeTableEntry *wanted_type) +{ + assert(value->value.type->id == TypeTableEntryIdPointer); + wanted_type = adjust_ptr_align(ira->codegen, wanted_type, value->value.type->data.pointer.alignment); + + if (instr_is_comptime(value)) { + ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value); + if (pointee->special != ConstValSpecialRuntime) { + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + result->value.type = wanted_type; + result->value.data.x_ptr.special = ConstPtrSpecialBaseArray; + result->value.data.x_ptr.mut = value->value.data.x_ptr.mut; + result->value.data.x_ptr.data.base_array.array_val = pointee; + result->value.data.x_ptr.data.base_array.elem_index = 0; + result->value.data.x_ptr.data.base_array.is_cstr = false; + return result; + } + } + + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, + wanted_type, value, CastOpBitCast); + result->value.type = wanted_type; + return result; +} + +static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *value, TypeTableEntry *wanted_type) +{ + wanted_type = adjust_slice_align(ira->codegen, wanted_type, value->value.type->data.pointer.alignment); + + if (instr_is_comptime(value)) { + ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value); + if (pointee->special != ConstValSpecialRuntime) { + assert(value->value.type->id == TypeTableEntryIdPointer); + TypeTableEntry *array_type = value->value.type->data.pointer.child_type; + assert(is_slice(wanted_type)); + bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; + + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + init_const_slice(ira->codegen, &result->value, pointee, 0, array_type->data.array.len, is_const); + result->value.data.x_struct.fields[slice_ptr_index].data.x_ptr.mut = + value->value.data.x_ptr.mut; + result->value.type = wanted_type; + return result; + } + } + + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, + wanted_type, value, CastOpPtrOfArrayToSlice); + result->value.type = wanted_type; + ir_add_alloca(ira, result, wanted_type); + return result; +} + static bool is_container(TypeTableEntry *type) { return type->id == TypeTableEntryIdStruct || type->id == TypeTableEntryIdEnum || @@ -9937,6 +10023,35 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } + // explicit *[N]T to [*]T + if (wanted_type->id == TypeTableEntryIdPointer && + wanted_type->data.pointer.ptr_len == PtrLenUnknown && + actual_type->id == TypeTableEntryIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == TypeTableEntryIdArray && + actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + { + return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); + } + + // explicit *[N]T to []T + if (is_slice(wanted_type) && + actual_type->id == TypeTableEntryIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) + { + TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == TypeTableEntryIdPointer); + if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + { + return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type); + } + } + + // explicit cast from child type of maybe type to maybe type if (wanted_type->id == TypeTableEntryIdMaybe) { TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; @@ -13150,6 +13265,13 @@ static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, ui ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count); } +static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type, uint32_t new_align) { + assert(is_slice(slice_type)); + TypeTableEntry *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index].type_entry, + new_align); + return get_slice_type(g, ptr_type); +} + static TypeTableEntry *adjust_ptr_len(CodeGen *g, TypeTableEntry *ptr_type, PtrLen ptr_len) { assert(ptr_type->id == TypeTableEntryIdPointer); return get_pointer_to_type_extra(g, diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 7358a4ffd8..c3ef24cd78 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -384,3 +384,19 @@ test "const slice widen cast" { assert(@bitCast(u32, bytes) == 0x12121212); } + +test "single-item pointer of array to slice and to unknown length pointer" { + testCastPtrOfArrayToSliceAndPtr(); + comptime testCastPtrOfArrayToSliceAndPtr(); +} + +fn testCastPtrOfArrayToSliceAndPtr() void { + var array = "ao" ++ "eu"; // TODO https://github.com/ziglang/zig/issues/1076 + const x: [*]u8 = &array; + x[0] += 1; + assert(mem.eql(u8, array[0..], "boeu")); + const y: []u8 = &array; + y[0] += 1; + assert(mem.eql(u8, array[0..], "coeu")); +} + -- cgit v1.2.3 From ffb089a9f5fa95fd559a7c88081310d0be73f206 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Fri, 8 Jun 2018 17:43:13 +1200 Subject: Fix json parser comma after empty object case --- std/json.zig | 18 +++++++++++++----- std/json_test.zig | 10 ++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/std/json.zig b/std/json.zig index 6cf83eef1a..03b19a7fa4 100644 --- a/std/json.zig +++ b/std/json.zig @@ -324,7 +324,9 @@ pub const StreamingParser = struct { p.complete = true; p.state = State.TopLevelEnd; }, - else => {}, + else => { + p.state = State.ValueEnd; + }, } token.* = Token.initMarker(Token.Id.ObjectEnd); @@ -348,7 +350,9 @@ pub const StreamingParser = struct { p.complete = true; p.state = State.TopLevelEnd; }, - else => {}, + else => { + p.state = State.ValueEnd; + }, } token.* = Token.initMarker(Token.Id.ArrayEnd); @@ -970,7 +974,7 @@ pub fn validate(s: []const u8) bool { var token1: ?Token = undefined; var token2: ?Token = undefined; - p.feed(c, *token1, *token2) catch |err| { + p.feed(c, &token1, &token2) catch |err| { return false; }; } @@ -978,6 +982,10 @@ pub fn validate(s: []const u8) bool { return p.complete; } +test "json validate" { + debug.assert(validate("{}")); +} + const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const ArrayList = std.ArrayList; @@ -1230,7 +1238,7 @@ pub const Parser = struct { _ = p.stack.pop(); p.state = State.ObjectKey; }, - else => { + Token.Id.ObjectEnd, Token.Id.ArrayEnd => { unreachable; }, } @@ -1270,7 +1278,7 @@ pub const Parser = struct { Token.Id.Null => { try array.append(Value.Null); }, - else => { + Token.Id.ObjectEnd => { unreachable; }, } diff --git a/std/json_test.zig b/std/json_test.zig index cb054d8e4e..8c8862441a 100644 --- a/std/json_test.zig +++ b/std/json_test.zig @@ -17,6 +17,16 @@ fn any(comptime s: []const u8) void { std.debug.assert(true); } +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Additional tests not part of test JSONTestSuite. + +test "y_trailing_comma_after_empty" { + ok( + \\{"1":[],"2":{},"3":"4"} + ); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// test "y_array_arraysWithSpaces" { -- cgit v1.2.3 From bf3d1c1aab336c4a650bb67dcaca132d4a0f6164 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Fri, 8 Jun 2018 09:21:31 +0200 Subject: Allow access of array.len through a pointer --- src/analyze.cpp | 14 ++++++++++++-- src/analyze.hpp | 2 ++ src/ir.cpp | 8 ++++++-- test/cases/array.zig | 10 +++++++++- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index e05fb23237..84f1473ea1 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3761,14 +3761,24 @@ static bool is_container(TypeTableEntry *type_entry) { zig_unreachable(); } +bool is_ref(TypeTableEntry *type_entry) { + return type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle; +} + +bool is_array_ref(TypeTableEntry *type_entry) { + TypeTableEntry *array = is_ref(type_entry) ? + type_entry->data.pointer.child_type : type_entry; + return array->id == TypeTableEntryIdArray; +} + bool is_container_ref(TypeTableEntry *type_entry) { - return (type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle) ? + return is_ref(type_entry) ? is_container(type_entry->data.pointer.child_type) : is_container(type_entry); } TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) { assert(is_container_ref(type_entry)); - return (type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle) ? + return is_ref(type_entry) ? type_entry->data.pointer.child_type : type_entry; } diff --git a/src/analyze.hpp b/src/analyze.hpp index 25bda198d6..88e06b2390 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -70,6 +70,8 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name); TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag); TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag); +bool is_ref(TypeTableEntry *type_entry); +bool is_array_ref(TypeTableEntry *type_entry); bool is_container_ref(TypeTableEntry *type_entry); void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); void scan_import(CodeGen *g, ImportTableEntry *import); diff --git a/src/ir.cpp b/src/ir.cpp index cc4ffb44a9..4766bff5e7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13846,10 +13846,14 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru ir_link_new_instruction(result, &field_ptr_instruction->base); return result->value.type; } - } else if (container_type->id == TypeTableEntryIdArray) { + } else if (is_array_ref(container_type)) { if (buf_eql_str(field_name, "len")) { ConstExprValue *len_val = create_const_vals(1); - init_const_usize(ira->codegen, len_val, container_type->data.array.len); + if (container_type->id == TypeTableEntryIdPointer) { + init_const_usize(ira->codegen, len_val, container_type->data.pointer.child_type->data.array.len); + } else { + init_const_usize(ira->codegen, len_val, container_type->data.array.len); + } TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; bool ptr_is_const = true; diff --git a/test/cases/array.zig b/test/cases/array.zig index ef919b27bd..b481261b4f 100644 --- a/test/cases/array.zig +++ b/test/cases/array.zig @@ -116,6 +116,15 @@ test "array len property" { assert(@typeOf(x).len == 5); } +test "array len field" { + var arr = [4]u8{ 0, 0, 0, 0 }; + var ptr = &arr; + assert(arr.len == 4); + comptime assert(arr.len == 4); + assert(ptr.len == 4); + comptime assert(ptr.len == 4); +} + test "single-item pointer to array indexing and slicing" { testSingleItemPtrArrayIndexSlice(); comptime testSingleItemPtrArrayIndexSlice(); @@ -143,4 +152,3 @@ fn testImplicitCastSingleItemPtr() void { slice[0] += 1; assert(byte == 101); } - -- cgit v1.2.3 From 39fa313ad881d242c4fbb6789bab26fed72449a2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 8 Jun 2018 14:57:16 -0400 Subject: disable some implicit casts for unknown length pointers closes #770 --- src/ir.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 4766bff5e7..e62ec71875 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7994,6 +7994,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit &const [N]T to []const T if (is_slice(expected_type) && actual_type->id == TypeTableEntryIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.is_const && actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) { @@ -8012,6 +8013,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit [N]T to &const []const T if (expected_type->id == TypeTableEntryIdPointer && expected_type->data.pointer.is_const && + expected_type->data.pointer.ptr_len == PtrLenSingle && is_slice(expected_type->data.pointer.child_type) && actual_type->id == TypeTableEntryIdArray) { @@ -8074,6 +8076,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, actual_type->id == TypeTableEntryIdComptimeInt) { if (expected_type->id == TypeTableEntryIdPointer && + expected_type->data.pointer.ptr_len == PtrLenSingle && expected_type->data.pointer.is_const) { if (ir_num_lit_fits_in_other_type(ira, value, expected_type->data.pointer.child_type, false)) { @@ -8121,7 +8124,10 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit enum to &const union which has the enum as the tag type - if (actual_type->id == TypeTableEntryIdEnum && expected_type->id == TypeTableEntryIdPointer) { + if (actual_type->id == TypeTableEntryIdEnum && + expected_type->id == TypeTableEntryIdPointer && + expected_type->data.pointer.ptr_len == PtrLenSingle) + { TypeTableEntry *union_type = expected_type->data.pointer.child_type; if (union_type->data.unionation.decl_node->data.container_decl.auto_enum || union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr) @@ -8141,7 +8147,11 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicitly take a const pointer to something if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (types_match_const_cast_only(ira, expected_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) { + if (expected_type->id == TypeTableEntryIdPointer && + expected_type->data.pointer.ptr_len == PtrLenSingle && + types_match_const_cast_only(ira, expected_type, const_ptr_actual, + source_node).id == ConstCastResultIdOk) + { return ImplicitCastMatchResultYes; } } -- cgit v1.2.3 From 1a9d2f3aae780873eefedaf3fdf095b3cd87b55f Mon Sep 17 00:00:00 2001 From: isaachier Date: Fri, 8 Jun 2018 19:24:48 -0400 Subject: Fix error handling in Buffer::fromOwnedSlice (#1082) --- std/buffer.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/buffer.zig b/std/buffer.zig index 469f81709b..0d82918580 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -41,9 +41,9 @@ pub const Buffer = struct { /// Buffer takes ownership of the passed in slice. The slice must have been /// allocated with `allocator`. /// Must deinitialize with deinit. - pub fn fromOwnedSlice(allocator: *Allocator, slice: []u8) Buffer { + pub fn fromOwnedSlice(allocator: *Allocator, slice: []u8) !Buffer { var self = Buffer{ .list = ArrayList(u8).fromOwnedSlice(allocator, slice) }; - self.list.append(0); + try self.list.append(0); return self; } -- cgit v1.2.3 From 6edd81109d16178f1dc688dacee4b38964b617c4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 9 Jun 2018 00:15:23 -0400 Subject: nullable pointers follow const-casting rules any *T -> ?*T cast is allowed implicitly, even when it occurs deep inside the type, and the cast is a no-op at runtime. in order to add this I had to make the comptime value representation of nullable pointers the same as the comptime value representation of normal pointers, so that we don't have to do any recursive transformation of values when doing this kind of cast. --- src/all_types.hpp | 5 +- src/analyze.cpp | 280 ++++++++++++++++++++++++++++------------------------ src/codegen.cpp | 158 +++++++++++++++-------------- src/ir.cpp | 121 +++++++++++++++-------- test/cases/cast.zig | 10 +- 5 files changed, 322 insertions(+), 252 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index c671682363..14a44ea768 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -144,6 +144,9 @@ enum ConstPtrSpecial { // understand the value of pointee at compile time. However, we will still // emit a binary with a compile time known address. // In this case index is the numeric address value. + // We also use this for null pointer. We need the data layout for ConstCastOnly == true + // types to be the same, so all nullables of pointer types use x_ptr + // instead of x_nullable ConstPtrSpecialHardCodedAddr, // This means that the pointer represents memory of assigning to _. // That is, storing discards the data, and loading is invalid. @@ -251,7 +254,7 @@ struct ConstExprValue { bool x_bool; ConstBoundFnValue x_bound_fn; TypeTableEntry *x_type; - ConstExprValue *x_maybe; + ConstExprValue *x_nullable; ConstErrValue x_err_union; ErrorTableEntry *x_err_set; BigInt x_enum_tag; diff --git a/src/analyze.cpp b/src/analyze.cpp index 84f1473ea1..16b2cb0590 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4578,6 +4578,52 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { return true; } +static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { + uint32_t hash_val = 0; + switch (const_val->data.x_ptr.mut) { + case ConstPtrMutRuntimeVar: + hash_val += (uint32_t)3500721036; + break; + case ConstPtrMutComptimeConst: + hash_val += (uint32_t)4214318515; + break; + case ConstPtrMutComptimeVar: + hash_val += (uint32_t)1103195694; + break; + } + switch (const_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + hash_val += (uint32_t)2478261866; + hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee); + return hash_val; + case ConstPtrSpecialBaseArray: + hash_val += (uint32_t)1764906839; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val); + hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index); + hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492; + return hash_val; + case ConstPtrSpecialBaseStruct: + hash_val += (uint32_t)3518317043; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val); + hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index); + return hash_val; + case ConstPtrSpecialHardCodedAddr: + hash_val += (uint32_t)4048518294; + hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr); + return hash_val; + case ConstPtrSpecialDiscard: + hash_val += 2010123162; + return hash_val; + case ConstPtrSpecialFunction: + hash_val += (uint32_t)2590901619; + hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); + return hash_val; + } + zig_unreachable(); +} + static uint32_t hash_const_val(ConstExprValue *const_val) { assert(const_val->special == ConstValSpecialStatic); switch (const_val->type->id) { @@ -4646,51 +4692,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction); return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); case TypeTableEntryIdPointer: - { - uint32_t hash_val = 0; - switch (const_val->data.x_ptr.mut) { - case ConstPtrMutRuntimeVar: - hash_val += (uint32_t)3500721036; - break; - case ConstPtrMutComptimeConst: - hash_val += (uint32_t)4214318515; - break; - case ConstPtrMutComptimeVar: - hash_val += (uint32_t)1103195694; - break; - } - switch (const_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - zig_unreachable(); - case ConstPtrSpecialRef: - hash_val += (uint32_t)2478261866; - hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee); - return hash_val; - case ConstPtrSpecialBaseArray: - hash_val += (uint32_t)1764906839; - hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val); - hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index); - hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492; - return hash_val; - case ConstPtrSpecialBaseStruct: - hash_val += (uint32_t)3518317043; - hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val); - hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index); - return hash_val; - case ConstPtrSpecialHardCodedAddr: - hash_val += (uint32_t)4048518294; - hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr); - return hash_val; - case ConstPtrSpecialDiscard: - hash_val += 2010123162; - return hash_val; - case ConstPtrSpecialFunction: - hash_val += (uint32_t)2590901619; - hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); - return hash_val; - } - zig_unreachable(); - } + return hash_const_val_ptr(const_val); case TypeTableEntryIdPromise: // TODO better hashing algorithm return 223048345; @@ -4708,10 +4710,14 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { // TODO better hashing algorithm return 2709806591; case TypeTableEntryIdMaybe: - if (const_val->data.x_maybe) { - return hash_const_val(const_val->data.x_maybe) * 1992916303; + if (get_codegen_ptr_type(const_val->type) != nullptr) { + return hash_const_val(const_val) * 1992916303; } else { - return 4016830364; + if (const_val->data.x_nullable) { + return hash_const_val(const_val->data.x_nullable) * 1992916303; + } else { + return 4016830364; + } } case TypeTableEntryIdErrorUnion: // TODO better hashing algorithm @@ -4812,9 +4818,11 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { return false; case TypeTableEntryIdMaybe: - if (value->data.x_maybe == nullptr) + if (get_codegen_ptr_type(value->type) != nullptr) + return value->data.x_ptr.mut == ConstPtrMutComptimeVar; + if (value->data.x_nullable == nullptr) return false; - return can_mutate_comptime_var_state(value->data.x_maybe); + return can_mutate_comptime_var_state(value->data.x_nullable); case TypeTableEntryIdErrorUnion: if (value->data.x_err_union.err != nullptr) @@ -5340,6 +5348,52 @@ bool ir_get_var_is_comptime(VariableTableEntry *var) { return var->is_comptime->value.data.x_bool; } +bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { + if (a->data.x_ptr.special != b->data.x_ptr.special) + return false; + if (a->data.x_ptr.mut != b->data.x_ptr.mut) + return false; + switch (a->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee) + return false; + return true; + case ConstPtrSpecialBaseArray: + if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val && + a->data.x_ptr.data.base_array.array_val->global_refs != + b->data.x_ptr.data.base_array.array_val->global_refs) + { + return false; + } + if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index) + return false; + if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr) + return false; + return true; + case ConstPtrSpecialBaseStruct: + if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val && + a->data.x_ptr.data.base_struct.struct_val->global_refs != + b->data.x_ptr.data.base_struct.struct_val->global_refs) + { + return false; + } + if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index) + return false; + return true; + case ConstPtrSpecialHardCodedAddr: + if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr) + return false; + return true; + case ConstPtrSpecialDiscard: + return true; + case ConstPtrSpecialFunction: + return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry; + } + zig_unreachable(); +} + bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { assert(a->type->id == b->type->id); assert(a->special == ConstValSpecialStatic); @@ -5391,49 +5445,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ; case TypeTableEntryIdPointer: case TypeTableEntryIdFn: - if (a->data.x_ptr.special != b->data.x_ptr.special) - return false; - if (a->data.x_ptr.mut != b->data.x_ptr.mut) - return false; - switch (a->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - zig_unreachable(); - case ConstPtrSpecialRef: - if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee) - return false; - return true; - case ConstPtrSpecialBaseArray: - if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val && - a->data.x_ptr.data.base_array.array_val->global_refs != - b->data.x_ptr.data.base_array.array_val->global_refs) - { - return false; - } - if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index) - return false; - if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr) - return false; - return true; - case ConstPtrSpecialBaseStruct: - if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val && - a->data.x_ptr.data.base_struct.struct_val->global_refs != - b->data.x_ptr.data.base_struct.struct_val->global_refs) - { - return false; - } - if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index) - return false; - return true; - case ConstPtrSpecialHardCodedAddr: - if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr) - return false; - return true; - case ConstPtrSpecialDiscard: - return true; - case ConstPtrSpecialFunction: - return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry; - } - zig_unreachable(); + return const_values_equal_ptr(a, b); case TypeTableEntryIdArray: zig_panic("TODO"); case TypeTableEntryIdStruct: @@ -5449,10 +5461,12 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { case TypeTableEntryIdNull: zig_panic("TODO"); case TypeTableEntryIdMaybe: - if (a->data.x_maybe == nullptr || b->data.x_maybe == nullptr) { - return (a->data.x_maybe == nullptr && b->data.x_maybe == nullptr); + if (get_codegen_ptr_type(a->type) != nullptr) + return const_values_equal_ptr(a, b); + if (a->data.x_nullable == nullptr || b->data.x_nullable == nullptr) { + return (a->data.x_nullable == nullptr && b->data.x_nullable == nullptr); } else { - return const_values_equal(a->data.x_maybe, b->data.x_maybe); + return const_values_equal(a->data.x_nullable, b->data.x_nullable); } case TypeTableEntryIdErrorUnion: zig_panic("TODO"); @@ -5525,6 +5539,41 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue * } } +void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeTableEntry *type_entry) { + switch (const_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + case ConstPtrSpecialBaseStruct: + buf_appendf(buf, "*"); + render_const_value(g, buf, const_ptr_pointee(g, const_val)); + return; + case ConstPtrSpecialBaseArray: + if (const_val->data.x_ptr.data.base_array.is_cstr) { + buf_appendf(buf, "*(c str lit)"); + return; + } else { + buf_appendf(buf, "*"); + render_const_value(g, buf, const_ptr_pointee(g, const_val)); + return; + } + case ConstPtrSpecialHardCodedAddr: + buf_appendf(buf, "(*%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name), + const_val->data.x_ptr.data.hard_coded_addr.addr); + return; + case ConstPtrSpecialDiscard: + buf_append_str(buf, "*_"); + return; + case ConstPtrSpecialFunction: + { + FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry; + buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name)); + return; + } + } + zig_unreachable(); +} + void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { switch (const_val->special) { case ConstValSpecialRuntime: @@ -5601,38 +5650,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { return; } case TypeTableEntryIdPointer: - switch (const_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - zig_unreachable(); - case ConstPtrSpecialRef: - case ConstPtrSpecialBaseStruct: - buf_appendf(buf, "&"); - render_const_value(g, buf, const_ptr_pointee(g, const_val)); - return; - case ConstPtrSpecialBaseArray: - if (const_val->data.x_ptr.data.base_array.is_cstr) { - buf_appendf(buf, "&(c str lit)"); - return; - } else { - buf_appendf(buf, "&"); - render_const_value(g, buf, const_ptr_pointee(g, const_val)); - return; - } - case ConstPtrSpecialHardCodedAddr: - buf_appendf(buf, "(&%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name), - const_val->data.x_ptr.data.hard_coded_addr.addr); - return; - case ConstPtrSpecialDiscard: - buf_append_str(buf, "&_"); - return; - case ConstPtrSpecialFunction: - { - FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry; - buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name)); - return; - } - } - zig_unreachable(); + return render_const_val_ptr(g, buf, const_val, type_entry); case TypeTableEntryIdBlock: { AstNode *node = const_val->data.x_block->source_node; @@ -5692,8 +5710,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case TypeTableEntryIdMaybe: { - if (const_val->data.x_maybe) { - render_const_value(g, buf, const_val->data.x_maybe); + if (get_codegen_ptr_type(const_val->type) != nullptr) + return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type); + if (const_val->data.x_nullable) { + render_const_value(g, buf, const_val->data.x_nullable); } else { buf_appendf(buf, "null"); } diff --git a/src/codegen.cpp b/src/codegen.cpp index fab2ad659e..65b465a519 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5020,6 +5020,79 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef return LLVMTypeOf(val) != type_entry->type_ref; } +static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, const char *name) { + render_const_val_global(g, const_val, name); + switch (const_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialRef: + { + ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee; + render_const_val(g, pointee, ""); + render_const_val_global(g, pointee, ""); + ConstExprValue *other_val = pointee; + const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + case ConstPtrSpecialBaseArray: + { + ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; + size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; + assert(array_const_val->type->id == TypeTableEntryIdArray); + if (array_const_val->type->zero_bits) { + // make this a null pointer + TypeTableEntry *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, + elem_index); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } + case ConstPtrSpecialBaseStruct: + { + ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val; + assert(struct_const_val->type->id == TypeTableEntryIdStruct); + if (struct_const_val->type->zero_bits) { + // make this a null pointer + TypeTableEntry *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index; + size_t gen_field_index = + struct_const_val->type->data.structure.fields[src_field_index].gen_index; + LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val, + gen_field_index); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } + case ConstPtrSpecialHardCodedAddr: + { + uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr; + TypeTableEntry *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + case ConstPtrSpecialFunction: + return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref); + } + zig_unreachable(); +} + static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) { TypeTableEntry *type_entry = const_val->type; assert(!type_entry->zero_bits); @@ -5068,19 +5141,15 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c { TypeTableEntry *child_type = type_entry->data.maybe.child_type; if (child_type->zero_bits) { - return LLVMConstInt(LLVMInt1Type(), const_val->data.x_maybe ? 1 : 0, false); + return LLVMConstInt(LLVMInt1Type(), const_val->data.x_nullable ? 1 : 0, false); } else if (type_is_codegen_pointer(child_type)) { - if (const_val->data.x_maybe) { - return gen_const_val(g, const_val->data.x_maybe, ""); - } else { - return LLVMConstNull(child_type->type_ref); - } + return gen_const_val_ptr(g, const_val, name); } else { LLVMValueRef child_val; LLVMValueRef maybe_val; bool make_unnamed_struct; - if (const_val->data.x_maybe) { - child_val = gen_const_val(g, const_val->data.x_maybe, ""); + if (const_val->data.x_nullable) { + child_val = gen_const_val(g, const_val->data.x_nullable, ""); maybe_val = LLVMConstAllOnes(LLVMInt1Type()); make_unnamed_struct = is_llvm_value_unnamed_type(const_val->type, child_val); @@ -5270,78 +5339,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst); return fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry); case TypeTableEntryIdPointer: - { - render_const_val_global(g, const_val, name); - switch (const_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialRef: - { - ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee; - render_const_val(g, pointee, ""); - render_const_val_global(g, pointee, ""); - ConstExprValue *other_val = pointee; - const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref); - render_const_val_global(g, const_val, ""); - return const_val->global_refs->llvm_value; - } - case ConstPtrSpecialBaseArray: - { - ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; - size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; - assert(array_const_val->type->id == TypeTableEntryIdArray); - if (array_const_val->type->zero_bits) { - // make this a null pointer - TypeTableEntry *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), - const_val->type->type_ref); - render_const_val_global(g, const_val, ""); - return const_val->global_refs->llvm_value; - } - LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, - elem_index); - LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); - const_val->global_refs->llvm_value = ptr_val; - render_const_val_global(g, const_val, ""); - return ptr_val; - } - case ConstPtrSpecialBaseStruct: - { - ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val; - assert(struct_const_val->type->id == TypeTableEntryIdStruct); - if (struct_const_val->type->zero_bits) { - // make this a null pointer - TypeTableEntry *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), - const_val->type->type_ref); - render_const_val_global(g, const_val, ""); - return const_val->global_refs->llvm_value; - } - size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index; - size_t gen_field_index = - struct_const_val->type->data.structure.fields[src_field_index].gen_index; - LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val, - gen_field_index); - LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); - const_val->global_refs->llvm_value = ptr_val; - render_const_val_global(g, const_val, ""); - return ptr_val; - } - case ConstPtrSpecialHardCodedAddr: - { - uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr; - TypeTableEntry *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false), - const_val->type->type_ref); - render_const_val_global(g, const_val, ""); - return const_val->global_refs->llvm_value; - } - case ConstPtrSpecialFunction: - return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref); - } - } - zig_unreachable(); + return gen_const_val_ptr(g, const_val, name); case TypeTableEntryIdErrorUnion: { TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; diff --git a/src/ir.cpp b/src/ir.cpp index e62ec71875..13ecfd4233 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -62,6 +62,7 @@ enum ConstCastResultId { ConstCastResultIdType, ConstCastResultIdUnresolvedInferredErrSet, ConstCastResultIdAsyncAllocatorType, + ConstCastResultIdNullWrapPtr, }; struct ConstCastErrSetMismatch { @@ -90,6 +91,7 @@ struct ConstCastOnly { ConstCastOnly *error_union_error_set; ConstCastOnly *return_type; ConstCastOnly *async_allocator_type; + ConstCastOnly *null_wrap_ptr_child; ConstCastArg fn_arg; ConstCastArgNoAlias arg_no_alias; } data; @@ -7660,6 +7662,21 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry if (expected_type == actual_type) return result; + // * and [*] can do a const-cast-only to ?* and ?[*], respectively + if (expected_type->id == TypeTableEntryIdMaybe && + expected_type->data.maybe.child_type->id == TypeTableEntryIdPointer && + actual_type->id == TypeTableEntryIdPointer) + { + ConstCastOnly child = types_match_const_cast_only(ira, + expected_type->data.maybe.child_type, actual_type, source_node); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdNullWrapPtr; + result.data.null_wrap_ptr_child = allocate_nonzero(1); + *result.data.null_wrap_ptr_child = child; + } + return result; + } + // pointer const if (expected_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer && @@ -8741,7 +8758,8 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, zig_panic("TODO"); case CastOpNoop: { - copy_const_val(const_val, other_val, other_val->special == ConstValSpecialStatic); + bool same_global_refs = other_val->special == ConstValSpecialStatic; + copy_const_val(const_val, other_val, same_global_refs); const_val->type = new_type; break; } @@ -9189,9 +9207,13 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); - const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_maybe = val; + if (get_codegen_ptr_type(wanted_type) != nullptr) { + copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst); + } else { + const_instruction->base.value.data.x_nullable = val; + } + const_instruction->base.value.type = wanted_type; return &const_instruction->base; } @@ -9346,9 +9368,14 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so assert(val); IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); - const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_maybe = nullptr; + if (get_codegen_ptr_type(wanted_type) != nullptr) { + const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; + const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0; + } else { + const_instruction->base.value.data.x_nullable = nullptr; + } + const_instruction->base.value.type = wanted_type; return &const_instruction->base; } @@ -10062,7 +10089,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } - // explicit cast from child type of maybe type to maybe type + // explicit cast from T to ?T + // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism if (wanted_type->id == TypeTableEntryIdMaybe) { TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk) { @@ -10113,7 +10141,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from [N]T to %[]const T + // explicit cast from [N]T to E![]const T if (wanted_type->id == TypeTableEntryIdErrorUnion && is_slice(wanted_type->data.error_union.payload_type) && actual_type->id == TypeTableEntryIdArray) @@ -10143,7 +10171,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type); } - // explicit cast from T to %?T + // explicit cast from T to E!?T if (wanted_type->id == TypeTableEntryIdErrorUnion && wanted_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe && actual_type->id != TypeTableEntryIdMaybe) @@ -10167,7 +10195,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // explicit cast from number literal to another type - // explicit cast from number literal to &const integer + // explicit cast from number literal to *const integer if (actual_type->id == TypeTableEntryIdComptimeFloat || actual_type->id == TypeTableEntryIdComptimeInt) { @@ -10391,6 +10419,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, source_instruction->source_node, child_type); copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst); + result->value.type = child_type; return result; } } @@ -10708,6 +10737,16 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) { } } +static bool nullable_value_is_null(ConstExprValue *val) { + assert(val->special == ConstValSpecialStatic); + if (get_codegen_ptr_type(val->type) != nullptr) { + return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && + val->data.x_ptr.data.hard_coded_addr.addr == 0; + } else { + return val->data.x_nullable == nullptr; + } +} + static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; IrInstruction *op2 = bin_op_instruction->op2->other; @@ -10737,7 +10776,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad); if (!maybe_val) return ira->codegen->builtin_types.entry_invalid; - bool is_null = (maybe_val->data.x_maybe == nullptr); + bool is_null = nullable_value_is_null(maybe_val); ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = (op_id == IrBinOpCmpEq) ? is_null : !is_null; return ira->codegen->builtin_types.entry_bool; @@ -12015,7 +12054,9 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_maybe = nullptr; + assert(get_codegen_ptr_type(nullable_type) != nullptr); + out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; + out_val->data.x_ptr.data.hard_coded_addr.addr = 0; return nullable_type; } IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, @@ -14207,6 +14248,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { IrInstruction *ptr = load_ptr_instruction->ptr->other; + if (type_is_invalid(ptr->value.type)) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *result = ir_get_deref(ira, &load_ptr_instruction->base, ptr); ir_link_new_instruction(result, &load_ptr_instruction->base); assert(result->value.type); @@ -14773,7 +14817,7 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_bool = (maybe_val->data.x_maybe != nullptr); + out_val->data.x_bool = !nullable_value_is_null(maybe_val); return ira->codegen->builtin_types.entry_bool; } @@ -14837,13 +14881,18 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, ConstExprValue *maybe_val = const_ptr_pointee(ira->codegen, val); if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - if (!maybe_val->data.x_maybe) { + if (nullable_value_is_null(maybe_val)) { ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null")); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &unwrap_maybe_instruction->base); out_val->data.x_ptr.special = ConstPtrSpecialRef; - out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_maybe; + out_val->data.x_ptr.mut = val->data.x_ptr.mut; + if (type_is_codegen_pointer(child_type)) { + out_val->data.x_ptr.data.ref.pointee = maybe_val; + } else { + out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_nullable; + } return result_type; } } @@ -16206,12 +16255,12 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop 0, 0); fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { - fn_def_fields[6].data.x_maybe = create_const_vals(1); + fn_def_fields[6].data.x_nullable = create_const_vals(1); ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); - init_const_slice(ira->codegen, fn_def_fields[6].data.x_maybe, lib_name, 0, buf_len(fn_node->lib_name), true); + init_const_slice(ira->codegen, fn_def_fields[6].data.x_nullable, lib_name, 0, buf_len(fn_node->lib_name), true); + } else { + fn_def_fields[6].data.x_nullable = nullptr; } - else - fn_def_fields[6].data.x_maybe = nullptr; // return_type: type ensure_field_index(fn_def_val->type, "return_type", 7); fn_def_fields[7].special = ConstValSpecialStatic; @@ -16664,8 +16713,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField"); - for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) - { + for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) { TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index]; ConstExprValue *union_field_val = &union_field_array->data.x_array.s_none.elements[union_field_index]; @@ -16676,12 +16724,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].special = ConstValSpecialStatic; inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type); - if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) - inner_fields[1].data.x_maybe = nullptr; - else - { - inner_fields[1].data.x_maybe = create_const_vals(1); - make_enum_field_val(inner_fields[1].data.x_maybe, union_field->enum_field, type_info_enum_field_type); + if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) { + inner_fields[1].data.x_nullable = nullptr; + } else { + inner_fields[1].data.x_nullable = create_const_vals(1); + make_enum_field_val(inner_fields[1].data.x_nullable, union_field->enum_field, type_info_enum_field_type); } inner_fields[2].special = ConstValSpecialStatic; @@ -16737,8 +16784,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false); - for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) - { + for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) { TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index]; ConstExprValue *struct_field_val = &struct_field_array->data.x_array.s_none.elements[struct_field_index]; @@ -16749,15 +16795,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].special = ConstValSpecialStatic; inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize); - if (!type_has_bits(struct_field->type_entry)) - inner_fields[1].data.x_maybe = nullptr; - else - { + if (!type_has_bits(struct_field->type_entry)) { + inner_fields[1].data.x_nullable = nullptr; + } else { size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index); - inner_fields[1].data.x_maybe = create_const_vals(1); - inner_fields[1].data.x_maybe->special = ConstValSpecialStatic; - inner_fields[1].data.x_maybe->type = ira->codegen->builtin_types.entry_usize; - bigint_init_unsigned(&inner_fields[1].data.x_maybe->data.x_bigint, byte_offset); + inner_fields[1].data.x_nullable = create_const_vals(1); + inner_fields[1].data.x_nullable->special = ConstValSpecialStatic; + inner_fields[1].data.x_nullable->type = ira->codegen->builtin_types.entry_usize; + bigint_init_unsigned(&inner_fields[1].data.x_nullable->data.x_bigint, byte_offset); } inner_fields[2].special = ConstValSpecialStatic; @@ -19008,9 +19053,6 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->builtin_types.entry_invalid; - if (target->value.type->id == TypeTableEntryIdMaybe) { - val = val->data.x_maybe; - } if (val->type->id == TypeTableEntryIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { IrInstruction *result = ir_create_const(&ira->new_irb, instruction->base.scope, instruction->base.source_node, usize); @@ -19936,6 +19978,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) { TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction); instruction->value.type = instruction_type; + if (instruction->other) { instruction->other->value.type = instruction_type; } else { diff --git a/test/cases/cast.zig b/test/cases/cast.zig index c3ef24cd78..da3cba7d80 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -1,5 +1,6 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; test "int to ptr cast" { const x = usize(13); @@ -400,3 +401,8 @@ fn testCastPtrOfArrayToSliceAndPtr() void { assert(mem.eql(u8, array[0..], "coeu")); } +test "cast *[1][*]const u8 to [*]const ?[*]const u8" { + const window_name = [1][*]const u8{c"window name"}; + const x: [*]const ?[*]const u8 = &window_name; + assert(mem.eql(u8, std.cstr.toSliceConst(??x[0]), "window name")); +} -- cgit v1.2.3 From 9046b5eac01540a783740451a6593ef0207c181e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 9 Jun 2018 11:41:59 -0400 Subject: fix assertion failure when debug printing comptime values --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 13ecfd4233..10098f3c32 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -113,7 +113,7 @@ static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, ui static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type, uint32_t new_align); ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { - assert(const_val->type->id == TypeTableEntryIdPointer); + assert(get_codegen_ptr_type(const_val->type) != nullptr); assert(const_val->special == ConstValSpecialStatic); switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: -- cgit v1.2.3 From e0092ee4a573502a4110a6d4aeb7e7d3cdc8987b Mon Sep 17 00:00:00 2001 From: Arthur Elliott Date: Thu, 7 Jun 2018 10:00:27 -0400 Subject: add set function to arraylist so you can set a value without growing the underlying buffer, with range safety checks --- std/array_list.zig | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/std/array_list.zig b/std/array_list.zig index 07a1db6451..7fc97474e6 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -44,6 +44,11 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return l.toSliceConst()[n]; } + pub fn set(self: *const Self, n: usize, item: *const T) !void { + if (n >= self.len) return error.OutOfBounds; + self.items[n] = item.*; + } + pub fn count(self: *const Self) usize { return self.len; } @@ -162,6 +167,11 @@ test "basic ArrayList test" { var list = ArrayList(i32).init(debug.global_allocator); defer list.deinit(); + // setting on empty list is out of bounds + list.set(0, 1) catch |err| { + assert(err == error.OutOfBounds); + }; + { var i: usize = 0; while (i < 10) : (i += 1) { @@ -200,6 +210,20 @@ test "basic ArrayList test" { list.appendSlice([]const i32{}) catch unreachable; assert(list.len == 9); + + // can only set on indices < self.len + list.set(7, 33) catch unreachable; + list.set(8, 42) catch unreachable; + + list.set(9, 99) catch |err| { + assert(err == error.OutOfBounds); + }; + list.set(10, 123) catch |err| { + assert(err == error.OutOfBounds); + }; + + assert(list.pop() == 42); + assert(list.pop() == 33); } test "iterator ArrayList test" { -- cgit v1.2.3 From fc6446702ed8261a1d02b7fbb8410a303cb5daaa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 9 Jun 2018 12:03:11 -0400 Subject: clean up std.ArrayList * add `std.debug.assertError` * `std.ArrayList` update everything to follow `self` convention * rename `std.ArrayList.set` to `std.ArrayList.setOrError` * add `std.ArrayList.set` which asserts Before 1.0.0 we might remove some of this API, because you can use `toSlice()` for everything, but it's ok to add these functions as an experiment before then. --- std/array_list.zig | 118 +++++++++++++++++++++++++++------------------------- std/debug/index.zig | 10 +++++ 2 files changed, 72 insertions(+), 56 deletions(-) diff --git a/std/array_list.zig b/std/array_list.zig index 7fc97474e6..30715f4d6f 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -1,6 +1,7 @@ const std = @import("index.zig"); const debug = std.debug; const assert = debug.assert; +const assertError = debug.assertError; const mem = std.mem; const Allocator = mem.Allocator; @@ -28,25 +29,33 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { }; } - pub fn deinit(l: *const Self) void { - l.allocator.free(l.items); + pub fn deinit(self: *const Self) void { + self.allocator.free(self.items); } - pub fn toSlice(l: *const Self) []align(A) T { - return l.items[0..l.len]; + pub fn toSlice(self: *const Self) []align(A) T { + return self.items[0..self.len]; } - pub fn toSliceConst(l: *const Self) []align(A) const T { - return l.items[0..l.len]; + pub fn toSliceConst(self: *const Self) []align(A) const T { + return self.items[0..self.len]; } - pub fn at(l: *const Self, n: usize) T { - return l.toSliceConst()[n]; + pub fn at(self: *const Self, n: usize) T { + return self.toSliceConst()[n]; } - pub fn set(self: *const Self, n: usize, item: *const T) !void { - if (n >= self.len) return error.OutOfBounds; - self.items[n] = item.*; + /// Sets the value at index `i`, or returns `error.OutOfBounds` if + /// the index is not in range. + pub fn setOrError(self: *const Self, i: usize, item: *const T) !void { + if (i >= self.len) return error.OutOfBounds; + self.items[i] = item.*; + } + + /// Sets the value at index `i`, asserting that the value is in range. + pub fn set(self: *const Self, i: usize, item: *const T) void { + assert(i < self.len); + self.items[i] = item.*; } pub fn count(self: *const Self) usize { @@ -72,58 +81,58 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return result; } - pub fn insert(l: *Self, n: usize, item: *const T) !void { - try l.ensureCapacity(l.len + 1); - l.len += 1; + pub fn insert(self: *Self, n: usize, item: *const T) !void { + try self.ensureCapacity(self.len + 1); + self.len += 1; - mem.copy(T, l.items[n + 1 .. l.len], l.items[n .. l.len - 1]); - l.items[n] = item.*; + mem.copy(T, self.items[n + 1 .. self.len], self.items[n .. self.len - 1]); + self.items[n] = item.*; } - pub fn insertSlice(l: *Self, n: usize, items: []align(A) const T) !void { - try l.ensureCapacity(l.len + items.len); - l.len += items.len; + pub fn insertSlice(self: *Self, n: usize, items: []align(A) const T) !void { + try self.ensureCapacity(self.len + items.len); + self.len += items.len; - mem.copy(T, l.items[n + items.len .. l.len], l.items[n .. l.len - items.len]); - mem.copy(T, l.items[n .. n + items.len], items); + mem.copy(T, self.items[n + items.len .. self.len], self.items[n .. self.len - items.len]); + mem.copy(T, self.items[n .. n + items.len], items); } - pub fn append(l: *Self, item: *const T) !void { - const new_item_ptr = try l.addOne(); + pub fn append(self: *Self, item: *const T) !void { + const new_item_ptr = try self.addOne(); new_item_ptr.* = item.*; } - pub fn appendSlice(l: *Self, items: []align(A) const T) !void { - try l.ensureCapacity(l.len + items.len); - mem.copy(T, l.items[l.len..], items); - l.len += items.len; + pub fn appendSlice(self: *Self, items: []align(A) const T) !void { + try self.ensureCapacity(self.len + items.len); + mem.copy(T, self.items[self.len..], items); + self.len += items.len; } - pub fn resize(l: *Self, new_len: usize) !void { - try l.ensureCapacity(new_len); - l.len = new_len; + pub fn resize(self: *Self, new_len: usize) !void { + try self.ensureCapacity(new_len); + self.len = new_len; } - pub fn shrink(l: *Self, new_len: usize) void { - assert(new_len <= l.len); - l.len = new_len; + pub fn shrink(self: *Self, new_len: usize) void { + assert(new_len <= self.len); + self.len = new_len; } - pub fn ensureCapacity(l: *Self, new_capacity: usize) !void { - var better_capacity = l.items.len; + pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { + var better_capacity = self.items.len; if (better_capacity >= new_capacity) return; while (true) { better_capacity += better_capacity / 2 + 8; if (better_capacity >= new_capacity) break; } - l.items = try l.allocator.alignedRealloc(T, A, l.items, better_capacity); + self.items = try self.allocator.alignedRealloc(T, A, self.items, better_capacity); } - pub fn addOne(l: *Self) !*T { - const new_length = l.len + 1; - try l.ensureCapacity(new_length); - const result = &l.items[l.len]; - l.len = new_length; + pub fn addOne(self: *Self) !*T { + const new_length = self.len + 1; + try self.ensureCapacity(new_length); + const result = &self.items[self.len]; + self.len = new_length; return result; } @@ -164,13 +173,14 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { } test "basic ArrayList test" { - var list = ArrayList(i32).init(debug.global_allocator); + var bytes: [1024]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + + var list = ArrayList(i32).init(allocator); defer list.deinit(); - // setting on empty list is out of bounds - list.set(0, 1) catch |err| { - assert(err == error.OutOfBounds); - }; + // setting on empty list is out of bounds + assertError(list.setOrError(0, 1), error.OutOfBounds); { var i: usize = 0; @@ -210,17 +220,13 @@ test "basic ArrayList test" { list.appendSlice([]const i32{}) catch unreachable; assert(list.len == 9); - + // can only set on indices < self.len - list.set(7, 33) catch unreachable; - list.set(8, 42) catch unreachable; - - list.set(9, 99) catch |err| { - assert(err == error.OutOfBounds); - }; - list.set(10, 123) catch |err| { - assert(err == error.OutOfBounds); - }; + list.set(7, 33); + list.set(8, 42); + + assertError(list.setOrError(9, 99), error.OutOfBounds); + assertError(list.setOrError(10, 123), error.OutOfBounds); assert(list.pop() == 42); assert(list.pop() == 33); diff --git a/std/debug/index.zig b/std/debug/index.zig index 00d9bef121..be47ab76bc 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -88,6 +88,16 @@ pub fn assert(ok: bool) void { } } +/// TODO: add `==` operator for `error_union == error_set`, and then +/// remove this function +pub fn assertError(value: var, expected_error: error) void { + if (value) { + @panic("expected error"); + } else |actual_error| { + assert(actual_error == expected_error); + } +} + /// Call this function when you want to panic if the condition is not true. /// If `ok` is `false`, this function will panic in every release mode. pub fn assertOrPanic(ok: bool) void { -- cgit v1.2.3 From 7a9635555b5ddc681134ebe0e0e9f4f373ac5025 Mon Sep 17 00:00:00 2001 From: marleck55 <40122305+marleck55@users.noreply.github.com> Date: Sat, 9 Jun 2018 18:05:58 +0200 Subject: std/fmt: Use lowercase k for kilo in base 1000 (#1090) --- std/fmt/index.zig | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index bbf48df0cf..3844fbb10a 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -559,14 +559,19 @@ pub fn formatBytes( return output(context, "0B"); } - const mags = " KMGTPEZY"; + const mags_si = " kMGTPEZY"; + const mags_iec = " KMGTPEZY"; const magnitude = switch (radix) { - 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags.len - 1), - 1024 => math.min(math.log2(value) / 10, mags.len - 1), + 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags_si.len - 1), + 1024 => math.min(math.log2(value) / 10, mags_iec.len - 1), else => unreachable, }; const new_value = f64(value) / math.pow(f64, f64(radix), f64(magnitude)); - const suffix = mags[magnitude]; + const suffix = switch (radix) { + 1000 => mags_si[magnitude], + 1024 => mags_iec[magnitude], + else => unreachable, + }; try formatFloatDecimal(new_value, width, context, Errors, output); -- cgit v1.2.3 From d464b2532200de3778ac7362e701791a11150d55 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 10 Jun 2018 04:39:22 +0200 Subject: support `--target-arch wasm32` (#1094) Add wasm32 support to the build-obj, build-exe and build-lib commands of the stage 1 compiler. Wasm64 should work transparently once it's supported in upstream LLVM. To export a function: // lib.zig - for exposition, not necessary for this example pub use @import("add.zig"); // add.zig export fn add(a: i32, b: i32) i32 { return a + b; } To import a function: // cube.zig extern fn square(x: i32) i32; export fn cube(x: i32) i32 { return x * square(x); } --- build.zig | 1 + src/link.cpp | 15 ++++++++++++++- src/target.cpp | 7 +++++-- src/zig_llvm.cpp | 2 +- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index 109a799ac9..08a47570ef 100644 --- a/build.zig +++ b/build.zig @@ -63,6 +63,7 @@ pub fn build(b: *Builder) !void { exe.addObjectFile(lib); } } else { + addCppLib(b, exe, cmake_binary_dir, "embedded_lld_wasm"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib"); diff --git a/src/link.cpp b/src/link.cpp index d454d77aae..d2925cb5a8 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -391,6 +391,19 @@ static void construct_linker_job_elf(LinkJob *lj) { } } +static void construct_linker_job_wasm(LinkJob *lj) { + CodeGen *g = lj->codegen; + + lj->args.append("--relocatable"); // So lld doesn't look for _start. + lj->args.append("-o"); + lj->args.append(buf_ptr(&lj->out_file)); + + // .o files + for (size_t i = 0; i < g->link_objects.length; i += 1) { + lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); + } +} + //static bool is_target_cyg_mingw(const ZigTarget *target) { // return (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_Cygnus) || // (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU); @@ -924,7 +937,7 @@ static void construct_linker_job(LinkJob *lj) { case ZigLLVM_MachO: return construct_linker_job_macho(lj); case ZigLLVM_Wasm: - zig_panic("TODO link wasm"); + return construct_linker_job_wasm(lj); } } diff --git a/src/target.cpp b/src/target.cpp index c53ed74d14..bd4aa4d4c2 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -597,12 +597,15 @@ void resolve_target_object_format(ZigTarget *target) { case ZigLLVM_tce: case ZigLLVM_tcele: case ZigLLVM_thumbeb: - case ZigLLVM_wasm32: - case ZigLLVM_wasm64: case ZigLLVM_xcore: target->oformat= ZigLLVM_ELF; return; + case ZigLLVM_wasm32: + case ZigLLVM_wasm64: + target->oformat = ZigLLVM_Wasm; + return; + case ZigLLVM_ppc: case ZigLLVM_ppc64: if (is_os_darwin(target)) { diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 5905fa8167..24f2a8a343 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -838,7 +838,7 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_ return lld::mach_o::link(array_ref_args, diag); case ZigLLVM_Wasm: - assert(false); // TODO ZigLLDLink for Wasm + return lld::wasm::link(array_ref_args, false, diag); } assert(false); // unreachable abort(); -- cgit v1.2.3 From ec1b6f66737f8c3cbc0420715c2c502c7e710081 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 9 Jun 2018 23:42:14 -0400 Subject: breaking syntax change: ??x to x.? (#1095) See #1023 This also renames Nullable/Maybe to Optional --- build.zig | 2 +- doc/codegen.md | 8 +- doc/langref.html.in | 122 ++++++++++++++-------------- example/cat/main.zig | 2 +- src-self-hosted/arg.zig | 10 +-- src-self-hosted/llvm.zig | 2 +- src-self-hosted/main.zig | 8 +- src/all_types.hpp | 44 +++++------ src/analyze.cpp | 70 ++++++++-------- src/ast_render.cpp | 6 +- src/codegen.cpp | 60 +++++++------- src/ir.cpp | 198 +++++++++++++++++++++++----------------------- src/ir_print.cpp | 16 ++-- src/parser.cpp | 21 +++-- src/tokenizer.cpp | 10 +-- src/tokenizer.hpp | 3 +- src/translate_c.cpp | 14 ++-- std/array_list.zig | 2 +- std/buf_map.zig | 6 +- std/event.zig | 4 +- std/fmt/index.zig | 6 +- std/hash_map.zig | 8 +- std/heap.zig | 4 +- std/json.zig | 12 +-- std/linked_list.zig | 8 +- std/macho.zig | 2 +- std/mem.zig | 22 +++--- std/os/child_process.zig | 18 ++--- std/os/index.zig | 4 +- std/os/linux/vdso.zig | 2 +- std/os/path.zig | 8 +- std/segmented_list.zig | 8 +- std/special/bootstrap.zig | 6 +- std/special/builtin.zig | 8 +- std/unicode.zig | 24 +++--- std/zig/ast.zig | 12 +-- std/zig/parse.zig | 35 +++++--- std/zig/parser_test.zig | 5 +- std/zig/render.zig | 25 +++--- test/cases/bugs/656.zig | 2 +- test/cases/cast.zig | 50 ++++++------ test/cases/error.zig | 2 +- test/cases/eval.zig | 2 +- test/cases/generics.zig | 2 +- test/cases/misc.zig | 2 +- test/cases/null.zig | 30 +++---- test/cases/reflection.zig | 2 +- test/cases/type_info.zig | 14 ++-- test/cases/while.zig | 12 +-- test/compile_errors.zig | 16 ++-- test/tests.zig | 12 +-- 51 files changed, 489 insertions(+), 482 deletions(-) diff --git a/build.zig b/build.zig index 08a47570ef..eada37816c 100644 --- a/build.zig +++ b/build.zig @@ -75,7 +75,7 @@ pub fn build(b: *Builder) !void { cxx_compiler, "-print-file-name=libstdc++.a", }); - const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next(); + const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?; if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) { warn( \\Unable to determine path to libstdc++.a diff --git a/doc/codegen.md b/doc/codegen.md index 02406fae82..65f12f4875 100644 --- a/doc/codegen.md +++ b/doc/codegen.md @@ -6,7 +6,7 @@ Every type has a "handle". If a type is a simple primitive type such as i32 or f64, the handle is "by value", meaning that we pass around the value itself when we refer to a value of that type. -If a type is a container, error union, maybe type, slice, or array, then its +If a type is a container, error union, optional type, slice, or array, then its handle is a pointer, and everywhere we refer to a value of this type we refer to a pointer. @@ -19,7 +19,7 @@ Error union types are represented as: payload: T, } -Maybe types are represented as: +Optional types are represented as: struct { payload: T, @@ -28,6 +28,6 @@ Maybe types are represented as: ## Data Optimizations -Maybe pointer types are special: the 0x0 pointer value is used to represent a -null pointer. Thus, instead of the struct above, maybe pointer types are +Optional pointer types are special: the 0x0 pointer value is used to represent a +null pointer. Thus, instead of the struct above, optional pointer types are represented as a `usize` in codegen and the handle is by value. diff --git a/doc/langref.html.in b/doc/langref.html.in index 6a1f1c3102..4c4a637095 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -156,18 +156,18 @@ pub fn main() void { true or false, !true); - // nullable - var nullable_value: ?[]const u8 = null; - assert(nullable_value == null); + // optional + var optional_value: ?[]const u8 = null; + assert(optional_value == null); - warn("\nnullable 1\ntype: {}\nvalue: {}\n", - @typeName(@typeOf(nullable_value)), nullable_value); + warn("\noptional 1\ntype: {}\nvalue: {}\n", + @typeName(@typeOf(optional_value)), optional_value); - nullable_value = "hi"; - assert(nullable_value != null); + optional_value = "hi"; + assert(optional_value != null); - warn("\nnullable 2\ntype: {}\nvalue: {}\n", - @typeName(@typeOf(nullable_value)), nullable_value); + warn("\noptional 2\ntype: {}\nvalue: {}\n", + @typeName(@typeOf(optional_value)), optional_value); // error union var number_or_error: error!i32 = error.ArgNotFound; @@ -428,7 +428,7 @@ pub fn main() void { null - used to set a nullable type to null + used to set an optional type to null undefined @@ -440,7 +440,7 @@ pub fn main() void { - {#see_also|Nullables|this#} + {#see_also|Optionals|this#} {#header_close#} {#header_open|String Literals#} {#code_begin|test#} @@ -988,7 +988,7 @@ a ^= b

a ?? b
    -
  • {#link|Nullables#}
  • +
  • {#link|Optionals#}
If a is null, @@ -1003,10 +1003,10 @@ unwrapped == 1234 -
??a
+
a.?
    -
  • {#link|Nullables#}
  • +
  • {#link|Optionals#}
@@ -1015,7 +1015,7 @@ unwrapped == 1234
const value: ?u32 = 5678;
-??value == 5678
+value.? == 5678 @@ -1103,7 +1103,7 @@ unwrapped == 1234
a == null
    -
  • {#link|Nullables#}
  • +
  • {#link|Optionals#}
@@ -1267,8 +1267,8 @@ x.* == 1234
{#header_open|Precedence#}
x() x[] x.y
 a!b
-!x -x -%x ~x &x ?x ??x
-x{} x.*
+!x -x -%x ~x &x ?x
+x{} x.* x.?
 ! * / % ** *%
 + - ++ +% -%
 << >>
@@ -1483,17 +1483,17 @@ test "volatile" {
     assert(@typeOf(mmio_ptr) == *volatile u8);
 }
 
-test "nullable pointers" {
-    // Pointers cannot be null. If you want a null pointer, use the nullable
-    // prefix `?` to make the pointer type nullable.
+test "optional pointers" {
+    // Pointers cannot be null. If you want a null pointer, use the optional
+    // prefix `?` to make the pointer type optional.
     var ptr: ?*i32 = null;
 
     var x: i32 = 1;
     ptr = &x;
 
-    assert((??ptr).* == 1);
+    assert(ptr.?.* == 1);
 
-    // Nullable pointers are the same size as normal pointers, because pointer
+    // Optional pointers are the same size as normal pointers, because pointer
     // value 0 is used as the null value.
     assert(@sizeOf(?*i32) == @sizeOf(*i32));
 }
@@ -1832,7 +1832,7 @@ test "linked list" {
         .last = &node,
         .len = 1,
     };
-    assert((??list2.first).data == 1234);
+    assert(list2.first.?.data == 1234);
 }
       {#code_end#}
       {#see_also|comptime|@fieldParentPtr#}
@@ -2270,7 +2270,7 @@ fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
 }
 
 test "while null capture" {
-    // Just like if expressions, while loops can take a nullable as the
+    // Just like if expressions, while loops can take an optional as the
     // condition and capture the payload. When null is encountered the loop
     // exits.
     var sum1: u32 = 0;
@@ -2280,7 +2280,7 @@ test "while null capture" {
     }
     assert(sum1 == 3);
 
-    // The else branch is allowed on nullable iteration. In this case, it will
+    // The else branch is allowed on optional iteration. In this case, it will
     // be executed on the first null value encountered.
     var sum2: u32 = 0;
     numbers_left = 3;
@@ -2340,7 +2340,7 @@ fn typeNameLength(comptime T: type) usize {
     return @typeName(T).len;
 }
       {#code_end#}
-      {#see_also|if|Nullables|Errors|comptime|unreachable#}
+      {#see_also|if|Optionals|Errors|comptime|unreachable#}
       {#header_close#}
       {#header_open|for#}
       {#code_begin|test|for#}
@@ -2400,7 +2400,7 @@ test "for else" {
         if (value == null) {
             break 9;
         } else {
-            sum += ??value;
+            sum += value.?;
         }
     } else blk: {
         assert(sum == 7);
@@ -2461,7 +2461,7 @@ test "if boolean" {
     assert(result == 47);
 }
 
-test "if nullable" {
+test "if optional" {
     // If expressions test for null.
 
     const a: ?u32 = 0;
@@ -2544,7 +2544,7 @@ test "if error union" {
     }
 }
       {#code_end#}
-      {#see_also|Nullables|Errors#}
+      {#see_also|Optionals|Errors#}
       {#header_close#}
       {#header_open|defer#}
       {#code_begin|test|defer#}
@@ -3167,24 +3167,24 @@ test "inferred error set" {
       

TODO

{#header_close#} {#header_close#} - {#header_open|Nullables#} + {#header_open|Optionals#}

One area that Zig provides safety without compromising efficiency or - readability is with the nullable type. + readability is with the optional type.

- The question mark symbolizes the nullable type. You can convert a type to a nullable + The question mark symbolizes the optional type. You can convert a type to an optional type by putting a question mark in front of it, like this:

{#code_begin|syntax#} // normal integer const normal_int: i32 = 1234; -// nullable integer -const nullable_int: ?i32 = 5678; +// optional integer +const optional_int: ?i32 = 5678; {#code_end#}

- Now the variable nullable_int could be an i32, or null. + Now the variable optional_int could be an i32, or null.

Instead of integers, let's talk about pointers. Null references are the source of many runtime @@ -3193,8 +3193,8 @@ const nullable_int: ?i32 = 5678;

Zig does not have them.

- Instead, you can use a nullable pointer. This secretly compiles down to a normal pointer, - since we know we can use 0 as the null value for the nullable type. But the compiler + Instead, you can use an optional pointer. This secretly compiles down to a normal pointer, + since we know we can use 0 as the null value for the optional type. But the compiler can check your work and make sure you don't assign null to something that can't be null.

@@ -3226,7 +3226,7 @@ fn doAThing() ?*Foo {

Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr" is *u8 not ?*u8. The ?? operator - unwrapped the nullable type and therefore ptr is guaranteed to be non-null everywhere + unwrapped the optional type and therefore ptr is guaranteed to be non-null everywhere it is used in the function.

@@ -3245,10 +3245,10 @@ fn doAThing() ?*Foo { In Zig you can accomplish the same thing:

{#code_begin|syntax#} -fn doAThing(nullable_foo: ?*Foo) void { +fn doAThing(optional_foo: ?*Foo) void { // do some stuff - if (nullable_foo) |foo| { + if (optional_foo) |foo| { doSomethingWithFoo(foo); } @@ -3257,7 +3257,7 @@ fn doAThing(nullable_foo: ?*Foo) void { {#code_end#}

Once again, the notable thing here is that inside the if block, - foo is no longer a nullable pointer, it is a pointer, which + foo is no longer an optional pointer, it is a pointer, which cannot be null.

@@ -3267,20 +3267,20 @@ fn doAThing(nullable_foo: ?*Foo) void { The optimizer can sometimes make better decisions knowing that pointer arguments cannot be null.

- {#header_open|Nullable Type#} -

A nullable is created by putting ? in front of a type. You can use compile-time - reflection to access the child type of a nullable:

+ {#header_open|Optional Type#} +

An optional is created by putting ? in front of a type. You can use compile-time + reflection to access the child type of an optional:

{#code_begin|test#} const assert = @import("std").debug.assert; -test "nullable type" { - // Declare a nullable and implicitly cast from null: +test "optional type" { + // Declare an optional and implicitly cast from null: var foo: ?i32 = null; - // Implicitly cast from child type of a nullable + // Implicitly cast from child type of an optional foo = 1234; - // Use compile-time reflection to access the child type of the nullable: + // Use compile-time reflection to access the child type of the optional: comptime assert(@typeOf(foo).Child == i32); } {#code_end#} @@ -4888,7 +4888,7 @@ pub const TypeId = enum { ComptimeInt, Undefined, Null, - Nullable, + Optional, ErrorUnion, Error, Enum, @@ -4922,7 +4922,7 @@ pub const TypeInfo = union(TypeId) { ComptimeInt: void, Undefined: void, Null: void, - Nullable: Nullable, + Optional: Optional, ErrorUnion: ErrorUnion, ErrorSet: ErrorSet, Enum: Enum, @@ -4975,7 +4975,7 @@ pub const TypeInfo = union(TypeId) { defs: []Definition, }; - pub const Nullable = struct { + pub const Optional = struct { child: type, }; @@ -5366,8 +5366,8 @@ comptime {

At compile-time:

{#code_begin|test_err|unable to unwrap null#} comptime { - const nullable_number: ?i32 = null; - const number = ??nullable_number; + const optional_number: ?i32 = null; + const number = optional_number.?; } {#code_end#}

At runtime crashes with the message attempt to unwrap null and a stack trace.

@@ -5376,9 +5376,9 @@ comptime { {#code_begin|exe|test#} const warn = @import("std").debug.warn; pub fn main() void { - const nullable_number: ?i32 = null; + const optional_number: ?i32 = null; - if (nullable_number) |number| { + if (optional_number) |number| { warn("got number: {}\n", number); } else { warn("it's null\n"); @@ -5939,9 +5939,9 @@ AsmInputItem = "[" Symbol "]" String "(" Expression ")" AsmClobbers= ":" list(String, ",") -UnwrapExpression = BoolOrExpression (UnwrapNullable | UnwrapError) | BoolOrExpression +UnwrapExpression = BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression -UnwrapNullable = "??" Expression +UnwrapOptional = "??" Expression UnwrapError = "catch" option("|" Symbol "|") Expression @@ -6015,12 +6015,10 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression -SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | PtrDerefExpression) +SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ".*" | ".?") FieldAccessExpression = "." Symbol -PtrDerefExpression = ".*" - FnCallExpression = "(" list(Expression, ",") ")" ArrayAccessExpression = "[" Expression "]" @@ -6033,7 +6031,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",") StructLiteralField = "." Symbol "=" Expression -PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" +PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "-%" | "try" | "await" PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType diff --git a/example/cat/main.zig b/example/cat/main.zig index 1b34cb22eb..27690d2695 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -7,7 +7,7 @@ const allocator = std.debug.global_allocator; pub fn main() !void { var args_it = os.args(); - const exe = try unwrapArg(??args_it.next(allocator)); + const exe = try unwrapArg(args_it.next(allocator).?); var catted_anything = false; var stdout_file = try io.getStdOut(); diff --git a/src-self-hosted/arg.zig b/src-self-hosted/arg.zig index df2c04ef1f..dc89483213 100644 --- a/src-self-hosted/arg.zig +++ b/src-self-hosted/arg.zig @@ -99,7 +99,7 @@ pub const Args = struct { error.ArgumentNotInAllowedSet => { std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg); std.debug.warn("allowed options are "); - for (??flag.allowed_set) |possible| { + for (flag.allowed_set.?) |possible| { std.debug.warn("'{}' ", possible); } std.debug.warn("\n"); @@ -276,14 +276,14 @@ test "parse arguments" { debug.assert(!args.present("help2")); debug.assert(!args.present("init")); - debug.assert(mem.eql(u8, ??args.single("build-file"), "build.zig")); - debug.assert(mem.eql(u8, ??args.single("color"), "on")); + debug.assert(mem.eql(u8, args.single("build-file").?, "build.zig")); + debug.assert(mem.eql(u8, args.single("color").?, "on")); - const objects = ??args.many("object"); + const objects = args.many("object").?; debug.assert(mem.eql(u8, objects[0], "obj1")); debug.assert(mem.eql(u8, objects[1], "obj2")); - debug.assert(mem.eql(u8, ??args.single("library"), "lib2")); + debug.assert(mem.eql(u8, args.single("library").?, "lib2")); const pos = args.positionals.toSliceConst(); debug.assert(mem.eql(u8, pos[0], "build")); diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index 16c359adcf..391a92cd63 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -8,6 +8,6 @@ pub const ContextRef = removeNullability(c.LLVMContextRef); pub const BuilderRef = removeNullability(c.LLVMBuilderRef); fn removeNullability(comptime T: type) type { - comptime assert(@typeId(T) == builtin.TypeId.Nullable); + comptime assert(@typeId(T) == builtin.TypeId.Optional); return T.Child; } diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index a264b5484a..64734f077a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -490,7 +490,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo try stderr.print("encountered --pkg-end with no matching --pkg-begin\n"); os.exit(1); } - cur_pkg = ??cur_pkg.parent; + cur_pkg = cur_pkg.parent.?; } } @@ -514,7 +514,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo }, } - const basename = os.path.basename(??in_file); + const basename = os.path.basename(in_file.?); var it = mem.split(basename, "."); const root_name = it.next() ?? { try stderr.write("file name cannot be empty\n"); @@ -523,12 +523,12 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo const asm_a = flags.many("assembly"); const obj_a = flags.many("object"); - if (in_file == null and (obj_a == null or (??obj_a).len == 0) and (asm_a == null or (??asm_a).len == 0)) { + if (in_file == null and (obj_a == null or obj_a.?.len == 0) and (asm_a == null or asm_a.?.len == 0)) { try stderr.write("Expected source file argument or at least one --object or --assembly argument\n"); os.exit(1); } - if (out_type == Module.Kind.Obj and (obj_a != null and (??obj_a).len != 0)) { + if (out_type == Module.Kind.Obj and (obj_a != null and obj_a.?.len != 0)) { try stderr.write("When building an object file, --object arguments are invalid\n"); os.exit(1); } diff --git a/src/all_types.hpp b/src/all_types.hpp index 14a44ea768..2a5a0ad740 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -145,8 +145,8 @@ enum ConstPtrSpecial { // emit a binary with a compile time known address. // In this case index is the numeric address value. // We also use this for null pointer. We need the data layout for ConstCastOnly == true - // types to be the same, so all nullables of pointer types use x_ptr - // instead of x_nullable + // types to be the same, so all optionals of pointer types use x_ptr + // instead of x_optional ConstPtrSpecialHardCodedAddr, // This means that the pointer represents memory of assigning to _. // That is, storing discards the data, and loading is invalid. @@ -222,10 +222,10 @@ enum RuntimeHintErrorUnion { RuntimeHintErrorUnionNonError, }; -enum RuntimeHintMaybe { - RuntimeHintMaybeUnknown, - RuntimeHintMaybeNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known. - RuntimeHintMaybeNonNull, +enum RuntimeHintOptional { + RuntimeHintOptionalUnknown, + RuntimeHintOptionalNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known. + RuntimeHintOptionalNonNull, }; enum RuntimeHintPtr { @@ -254,7 +254,7 @@ struct ConstExprValue { bool x_bool; ConstBoundFnValue x_bound_fn; TypeTableEntry *x_type; - ConstExprValue *x_nullable; + ConstExprValue *x_optional; ConstErrValue x_err_union; ErrorTableEntry *x_err_set; BigInt x_enum_tag; @@ -268,7 +268,7 @@ struct ConstExprValue { // populated if special == ConstValSpecialRuntime RuntimeHintErrorUnion rh_error_union; - RuntimeHintMaybe rh_maybe; + RuntimeHintOptional rh_maybe; RuntimeHintPtr rh_ptr; } data; }; @@ -556,7 +556,7 @@ enum BinOpType { BinOpTypeMultWrap, BinOpTypeDiv, BinOpTypeMod, - BinOpTypeUnwrapMaybe, + BinOpTypeUnwrapOptional, BinOpTypeArrayCat, BinOpTypeArrayMult, BinOpTypeErrorUnion, @@ -623,8 +623,8 @@ enum PrefixOp { PrefixOpBinNot, PrefixOpNegation, PrefixOpNegationWrap, - PrefixOpMaybe, - PrefixOpUnwrapMaybe, + PrefixOpOptional, + PrefixOpUnwrapOptional, PrefixOpAddrOf, }; @@ -1052,7 +1052,7 @@ struct TypeTableEntryStruct { HashMap fields_by_name; }; -struct TypeTableEntryMaybe { +struct TypeTableEntryOptional { TypeTableEntry *child_type; }; @@ -1175,7 +1175,7 @@ enum TypeTableEntryId { TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefined, TypeTableEntryIdNull, - TypeTableEntryIdMaybe, + TypeTableEntryIdOptional, TypeTableEntryIdErrorUnion, TypeTableEntryIdErrorSet, TypeTableEntryIdEnum, @@ -1206,7 +1206,7 @@ struct TypeTableEntry { TypeTableEntryFloat floating; TypeTableEntryArray array; TypeTableEntryStruct structure; - TypeTableEntryMaybe maybe; + TypeTableEntryOptional maybe; TypeTableEntryErrorUnion error_union; TypeTableEntryErrorSet error_set; TypeTableEntryEnum enumeration; @@ -1402,7 +1402,7 @@ enum PanicMsgId { PanicMsgIdRemainderDivisionByZero, PanicMsgIdExactDivisionRemainder, PanicMsgIdSliceWidenRemainder, - PanicMsgIdUnwrapMaybeFail, + PanicMsgIdUnwrapOptionalFail, PanicMsgIdInvalidErrorCode, PanicMsgIdIncorrectAlignment, PanicMsgIdBadUnionField, @@ -2016,8 +2016,8 @@ enum IrInstructionId { IrInstructionIdAsm, IrInstructionIdSizeOf, IrInstructionIdTestNonNull, - IrInstructionIdUnwrapMaybe, - IrInstructionIdMaybeWrap, + IrInstructionIdUnwrapOptional, + IrInstructionIdOptionalWrap, IrInstructionIdUnionTag, IrInstructionIdClz, IrInstructionIdCtz, @@ -2184,7 +2184,7 @@ enum IrUnOp { IrUnOpNegation, IrUnOpNegationWrap, IrUnOpDereference, - IrUnOpMaybe, + IrUnOpOptional, }; struct IrInstructionUnOp { @@ -2487,7 +2487,7 @@ struct IrInstructionTestNonNull { IrInstruction *value; }; -struct IrInstructionUnwrapMaybe { +struct IrInstructionUnwrapOptional { IrInstruction base; IrInstruction *value; @@ -2745,7 +2745,7 @@ struct IrInstructionUnwrapErrPayload { bool safety_check_on; }; -struct IrInstructionMaybeWrap { +struct IrInstructionOptionalWrap { IrInstruction base; IrInstruction *value; @@ -2954,10 +2954,10 @@ struct IrInstructionExport { struct IrInstructionErrorReturnTrace { IrInstruction base; - enum Nullable { + enum Optional { Null, NonNull, - } nullable; + } optional; }; struct IrInstructionErrorUnion { diff --git a/src/analyze.cpp b/src/analyze.cpp index 16b2cb0590..ed261148ea 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -236,7 +236,7 @@ bool type_is_complete(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: @@ -272,7 +272,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: @@ -520,7 +520,7 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { } else { ensure_complete_type(g, child_type); - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMaybe); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdOptional); assert(child_type->type_ref || child_type->zero_bits); assert(child_type->di_type); entry->is_copyable = type_is_copyable(g, child_type); @@ -1361,7 +1361,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { return type_entry->data.structure.layout == ContainerLayoutPacked; case TypeTableEntryIdUnion: return type_entry->data.unionation.layout == ContainerLayoutPacked; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; return type_is_codegen_pointer(child_type); @@ -1415,7 +1415,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { return type_allowed_in_extern(g, type_entry->data.pointer.child_type); case TypeTableEntryIdStruct: return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn; @@ -1538,7 +1538,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -1632,7 +1632,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -2985,8 +2985,8 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { return wrong_panic_prototype(g, proto_node, fn_type); } - TypeTableEntry *nullable_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g)); - if (fn_type_id->param_info[1].type != nullable_ptr_to_stack_trace_type) { + TypeTableEntry *optional_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g)); + if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) { return wrong_panic_prototype(g, proto_node, fn_type); } @@ -3368,7 +3368,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -3746,7 +3746,7 @@ static bool is_container(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: @@ -3805,7 +3805,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: @@ -3824,7 +3824,7 @@ TypeTableEntry *get_codegen_ptr_type(TypeTableEntry *type) { if (type->id == TypeTableEntryIdPointer) return type; if (type->id == TypeTableEntryIdFn) return type; if (type->id == TypeTableEntryIdPromise) return type; - if (type->id == TypeTableEntryIdMaybe) { + if (type->id == TypeTableEntryIdOptional) { if (type->data.maybe.child_type->id == TypeTableEntryIdPointer) return type->data.maybe.child_type; if (type->data.maybe.child_type->id == TypeTableEntryIdFn) return type->data.maybe.child_type; if (type->data.maybe.child_type->id == TypeTableEntryIdPromise) return type->data.maybe.child_type; @@ -4331,7 +4331,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { return type_has_bits(type_entry); case TypeTableEntryIdErrorUnion: return type_has_bits(type_entry->data.error_union.payload_type); - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: return type_has_bits(type_entry->data.maybe.child_type) && !type_is_codegen_pointer(type_entry->data.maybe.child_type); case TypeTableEntryIdUnion: @@ -4709,12 +4709,12 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case TypeTableEntryIdUnion: // TODO better hashing algorithm return 2709806591; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: if (get_codegen_ptr_type(const_val->type) != nullptr) { return hash_const_val(const_val) * 1992916303; } else { - if (const_val->data.x_nullable) { - return hash_const_val(const_val->data.x_nullable) * 1992916303; + if (const_val->data.x_optional) { + return hash_const_val(const_val->data.x_optional) * 1992916303; } else { return 4016830364; } @@ -4817,12 +4817,12 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { } return false; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: if (get_codegen_ptr_type(value->type) != nullptr) return value->data.x_ptr.mut == ConstPtrMutComptimeVar; - if (value->data.x_nullable == nullptr) + if (value->data.x_optional == nullptr) return false; - return can_mutate_comptime_var_state(value->data.x_nullable); + return can_mutate_comptime_var_state(value->data.x_optional); case TypeTableEntryIdErrorUnion: if (value->data.x_err_union.err != nullptr) @@ -4869,7 +4869,7 @@ static bool return_type_is_cacheable(TypeTableEntry *return_type) { case TypeTableEntryIdUnion: return false; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: return return_type_is_cacheable(return_type->data.maybe.child_type); case TypeTableEntryIdErrorUnion: @@ -4978,7 +4978,7 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { case TypeTableEntryIdUnion: assert(type_has_zero_bits_known(type_entry)); return type_entry->data.unionation.requires_comptime; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: return type_requires_comptime(type_entry->data.maybe.child_type); case TypeTableEntryIdErrorUnion: return type_requires_comptime(type_entry->data.error_union.payload_type); @@ -5460,13 +5460,13 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { zig_panic("TODO"); case TypeTableEntryIdNull: zig_panic("TODO"); - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: if (get_codegen_ptr_type(a->type) != nullptr) return const_values_equal_ptr(a, b); - if (a->data.x_nullable == nullptr || b->data.x_nullable == nullptr) { - return (a->data.x_nullable == nullptr && b->data.x_nullable == nullptr); + if (a->data.x_optional == nullptr || b->data.x_optional == nullptr) { + return (a->data.x_optional == nullptr && b->data.x_optional == nullptr); } else { - return const_values_equal(a->data.x_nullable, b->data.x_nullable); + return const_values_equal(a->data.x_optional, b->data.x_optional); } case TypeTableEntryIdErrorUnion: zig_panic("TODO"); @@ -5708,12 +5708,12 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "undefined"); return; } - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: { if (get_codegen_ptr_type(const_val->type) != nullptr) return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type); - if (const_val->data.x_nullable) { - render_const_value(g, buf, const_val->data.x_nullable); + if (const_val->data.x_optional) { + render_const_value(g, buf, const_val->data.x_optional); } else { buf_appendf(buf, "null"); } @@ -5819,7 +5819,7 @@ uint32_t type_id_hash(TypeId x) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: @@ -5865,7 +5865,7 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdPromise: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -5987,7 +5987,7 @@ static const TypeTableEntryId all_type_ids[] = { TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefined, TypeTableEntryIdNull, - TypeTableEntryIdMaybe, + TypeTableEntryIdOptional, TypeTableEntryIdErrorUnion, TypeTableEntryIdErrorSet, TypeTableEntryIdEnum, @@ -6042,7 +6042,7 @@ size_t type_id_index(TypeTableEntry *entry) { return 11; case TypeTableEntryIdNull: return 12; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: return 13; case TypeTableEntryIdErrorUnion: return 14; @@ -6100,8 +6100,8 @@ const char *type_id_name(TypeTableEntryId id) { return "Undefined"; case TypeTableEntryIdNull: return "Null"; - case TypeTableEntryIdMaybe: - return "Nullable"; + case TypeTableEntryIdOptional: + return "Optional"; case TypeTableEntryIdErrorUnion: return "ErrorUnion"; case TypeTableEntryIdErrorSet: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 3785cb6ca1..2c8c03b226 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -50,7 +50,7 @@ static const char *bin_op_str(BinOpType bin_op) { case BinOpTypeAssignBitXor: return "^="; case BinOpTypeAssignBitOr: return "|="; case BinOpTypeAssignMergeErrorSets: return "||="; - case BinOpTypeUnwrapMaybe: return "??"; + case BinOpTypeUnwrapOptional: return "??"; case BinOpTypeArrayCat: return "++"; case BinOpTypeArrayMult: return "**"; case BinOpTypeErrorUnion: return "!"; @@ -66,8 +66,8 @@ static const char *prefix_op_str(PrefixOp prefix_op) { case PrefixOpNegationWrap: return "-%"; case PrefixOpBoolNot: return "!"; case PrefixOpBinNot: return "~"; - case PrefixOpMaybe: return "?"; - case PrefixOpUnwrapMaybe: return "??"; + case PrefixOpOptional: return "?"; + case PrefixOpUnwrapOptional: return "??"; case PrefixOpAddrOf: return "&"; } zig_unreachable(); diff --git a/src/codegen.cpp b/src/codegen.cpp index 65b465a519..da08ecfc9e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -865,7 +865,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { return buf_create_from_str("exact division produced remainder"); case PanicMsgIdSliceWidenRemainder: return buf_create_from_str("slice widening size mismatch"); - case PanicMsgIdUnwrapMaybeFail: + case PanicMsgIdUnwrapOptionalFail: return buf_create_from_str("attempt to unwrap null"); case PanicMsgIdUnreachable: return buf_create_from_str("reached unreachable code"); @@ -2734,7 +2734,7 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst switch (op_id) { case IrUnOpInvalid: - case IrUnOpMaybe: + case IrUnOpOptional: case IrUnOpDereference: zig_unreachable(); case IrUnOpNegation: @@ -3333,7 +3333,7 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru } static LLVMValueRef gen_non_null_bit(CodeGen *g, TypeTableEntry *maybe_type, LLVMValueRef maybe_handle) { - assert(maybe_type->id == TypeTableEntryIdMaybe); + assert(maybe_type->id == TypeTableEntryIdOptional); TypeTableEntry *child_type = maybe_type->data.maybe.child_type; if (child_type->zero_bits) { return maybe_handle; @@ -3355,23 +3355,23 @@ static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable } static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, - IrInstructionUnwrapMaybe *instruction) + IrInstructionUnwrapOptional *instruction) { TypeTableEntry *ptr_type = instruction->value->value.type; assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *maybe_type = ptr_type->data.pointer.child_type; - assert(maybe_type->id == TypeTableEntryIdMaybe); + assert(maybe_type->id == TypeTableEntryIdOptional); TypeTableEntry *child_type = maybe_type->data.maybe.child_type; LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value); LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) { LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdUnwrapMaybeFail); + gen_safety_crash(g, PanicMsgIdUnwrapOptionalFail); LLVMPositionBuilderAtEnd(g->builder, ok_block); } @@ -3593,17 +3593,17 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I } else if (target_type->id == TypeTableEntryIdFn) { align_bytes = target_type->data.fn.fn_type_id.alignment; ptr_val = target_val; - } else if (target_type->id == TypeTableEntryIdMaybe && + } else if (target_type->id == TypeTableEntryIdOptional && target_type->data.maybe.child_type->id == TypeTableEntryIdPointer) { align_bytes = target_type->data.maybe.child_type->data.pointer.alignment; ptr_val = target_val; - } else if (target_type->id == TypeTableEntryIdMaybe && + } else if (target_type->id == TypeTableEntryIdOptional && target_type->data.maybe.child_type->id == TypeTableEntryIdFn) { align_bytes = target_type->data.maybe.child_type->data.fn.fn_type_id.alignment; ptr_val = target_val; - } else if (target_type->id == TypeTableEntryIdMaybe && + } else if (target_type->id == TypeTableEntryIdOptional && target_type->data.maybe.child_type->id == TypeTableEntryIdPromise) { zig_panic("TODO audit this function"); @@ -3705,7 +3705,7 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn success_order, failure_order, instruction->is_weak); TypeTableEntry *maybe_type = instruction->base.value.type; - assert(maybe_type->id == TypeTableEntryIdMaybe); + assert(maybe_type->id == TypeTableEntryIdOptional); TypeTableEntry *child_type = maybe_type->data.maybe.child_type; if (type_is_codegen_pointer(child_type)) { @@ -4115,10 +4115,10 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu } } -static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, IrInstructionMaybeWrap *instruction) { +static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, IrInstructionOptionalWrap *instruction) { TypeTableEntry *wanted_type = instruction->base.value.type; - assert(wanted_type->id == TypeTableEntryIdMaybe); + assert(wanted_type->id == TypeTableEntryIdOptional); TypeTableEntry *child_type = wanted_type->data.maybe.child_type; @@ -4699,8 +4699,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_asm(g, executable, (IrInstructionAsm *)instruction); case IrInstructionIdTestNonNull: return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapMaybe: - return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapMaybe *)instruction); + case IrInstructionIdUnwrapOptional: + return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapOptional *)instruction); case IrInstructionIdClz: return ir_render_clz(g, executable, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -4741,8 +4741,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_unwrap_err_code(g, executable, (IrInstructionUnwrapErrCode *)instruction); case IrInstructionIdUnwrapErrPayload: return ir_render_unwrap_err_payload(g, executable, (IrInstructionUnwrapErrPayload *)instruction); - case IrInstructionIdMaybeWrap: - return ir_render_maybe_wrap(g, executable, (IrInstructionMaybeWrap *)instruction); + case IrInstructionIdOptionalWrap: + return ir_render_maybe_wrap(g, executable, (IrInstructionOptionalWrap *)instruction); case IrInstructionIdErrWrapCode: return ir_render_err_wrap_code(g, executable, (IrInstructionErrWrapCode *)instruction); case IrInstructionIdErrWrapPayload: @@ -4972,7 +4972,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con } case TypeTableEntryIdPointer: case TypeTableEntryIdFn: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdPromise: { LLVMValueRef ptr_val = gen_const_val(g, const_val, ""); @@ -5137,19 +5137,19 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c } else { return LLVMConstNull(LLVMInt1Type()); } - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; if (child_type->zero_bits) { - return LLVMConstInt(LLVMInt1Type(), const_val->data.x_nullable ? 1 : 0, false); + return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false); } else if (type_is_codegen_pointer(child_type)) { return gen_const_val_ptr(g, const_val, name); } else { LLVMValueRef child_val; LLVMValueRef maybe_val; bool make_unnamed_struct; - if (const_val->data.x_nullable) { - child_val = gen_const_val(g, const_val->data.x_nullable, ""); + if (const_val->data.x_optional) { + child_val = gen_const_val(g, const_val->data.x_optional, ""); maybe_val = LLVMConstAllOnes(LLVMInt1Type()); make_unnamed_struct = is_llvm_value_unnamed_type(const_val->type, child_val); @@ -5755,8 +5755,8 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdSlice) { IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction; slot = &slice_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdMaybeWrap) { - IrInstructionMaybeWrap *maybe_wrap_instruction = (IrInstructionMaybeWrap *)instruction; + } else if (instruction->id == IrInstructionIdOptionalWrap) { + IrInstructionOptionalWrap *maybe_wrap_instruction = (IrInstructionOptionalWrap *)instruction; slot = &maybe_wrap_instruction->tmp_ptr; } else if (instruction->id == IrInstructionIdErrWrapPayload) { IrInstructionErrWrapPayload *err_wrap_payload_instruction = (IrInstructionErrWrapPayload *)instruction; @@ -6511,7 +6511,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " ComptimeInt: void,\n" " Undefined: void,\n" " Null: void,\n" - " Nullable: Nullable,\n" + " Optional: Optional,\n" " ErrorUnion: ErrorUnion,\n" " ErrorSet: ErrorSet,\n" " Enum: Enum,\n" @@ -6570,7 +6570,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " defs: []Definition,\n" " };\n" "\n" - " pub const Nullable = struct {\n" + " pub const Optional = struct {\n" " child: type,\n" " };\n" "\n" @@ -7145,7 +7145,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry case TypeTableEntryIdArray: prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type); return; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type); return; case TypeTableEntryIdFn: @@ -7234,7 +7234,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf buf_appendf(out_buf, "%s%s *", const_str, buf_ptr(&child_buf)); break; } - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; if (child_type->zero_bits) { @@ -7448,7 +7448,7 @@ static void gen_h_file(CodeGen *g) { case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdFn: case TypeTableEntryIdPromise: zig_unreachable(); diff --git a/src/ir.cpp b/src/ir.cpp index 10098f3c32..02606fc4aa 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -47,7 +47,7 @@ enum ConstCastResultId { ConstCastResultIdErrSetGlobal, ConstCastResultIdPointerChild, ConstCastResultIdSliceChild, - ConstCastResultIdNullableChild, + ConstCastResultIdOptionalChild, ConstCastResultIdErrorUnionPayload, ConstCastResultIdErrorUnionErrorSet, ConstCastResultIdFnAlign, @@ -86,7 +86,7 @@ struct ConstCastOnly { ConstCastErrSetMismatch error_set; ConstCastOnly *pointer_child; ConstCastOnly *slice_child; - ConstCastOnly *nullable_child; + ConstCastOnly *optional_child; ConstCastOnly *error_union_payload; ConstCastOnly *error_union_error_set; ConstCastOnly *return_type; @@ -372,8 +372,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) { return IrInstructionIdTestNonNull; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapMaybe *) { - return IrInstructionIdUnwrapMaybe; +static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapOptional *) { + return IrInstructionIdUnwrapOptional; } static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) { @@ -524,8 +524,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrPayload return IrInstructionIdUnwrapErrPayload; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionMaybeWrap *) { - return IrInstructionIdMaybeWrap; +static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalWrap *) { + return IrInstructionIdOptionalWrap; } static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapPayload *) { @@ -1571,7 +1571,7 @@ static IrInstruction *ir_build_test_nonnull_from(IrBuilder *irb, IrInstruction * static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, bool safety_check_on) { - IrInstructionUnwrapMaybe *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionUnwrapOptional *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; instruction->safety_check_on = safety_check_on; @@ -1590,7 +1590,7 @@ static IrInstruction *ir_build_unwrap_maybe_from(IrBuilder *irb, IrInstruction * } static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { - IrInstructionMaybeWrap *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionOptionalWrap *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); @@ -2496,9 +2496,9 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } -static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Nullable nullable) { +static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Optional optional) { IrInstructionErrorReturnTrace *instruction = ir_build_instruction(irb, scope, source_node); - instruction->nullable = nullable; + instruction->optional = optional; return &instruction->base; } @@ -3295,9 +3295,9 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null); } - IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "MaybeNonNull"); - IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "MaybeNull"); - IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "MaybeEnd"); + IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull"); + IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull"); + IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd"); ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, null_block); @@ -3426,7 +3426,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult); case BinOpTypeMergeErrorSets: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets); - case BinOpTypeUnwrapMaybe: + case BinOpTypeUnwrapOptional: return ir_gen_maybe_ok_or(irb, scope, node); case BinOpTypeErrorUnion: return ir_gen_error_union(irb, scope, node); @@ -4703,9 +4703,9 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval); case PrefixOpNegationWrap: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval); - case PrefixOpMaybe: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe), lval); - case PrefixOpUnwrapMaybe: + case PrefixOpOptional: + return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); + case PrefixOpUnwrapOptional: return ir_gen_maybe_assert_ok(irb, scope, node, lval); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; @@ -5370,9 +5370,9 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val); - IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "MaybeThen"); - IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "MaybeElse"); - IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "MaybeEndIf"); + IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "OptionalThen"); + IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "OptionalElse"); + IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "OptionalEndIf"); IrInstruction *is_comptime; if (ir_should_inline(irb->exec, scope)) { @@ -7519,7 +7519,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc } } else if (const_val_fits_in_num_lit(const_val, other_type)) { return true; - } else if (other_type->id == TypeTableEntryIdMaybe) { + } else if (other_type->id == TypeTableEntryIdOptional) { TypeTableEntry *child_type = other_type->data.maybe.child_type; if (const_val_fits_in_num_lit(const_val, child_type)) { return true; @@ -7663,7 +7663,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry return result; // * and [*] can do a const-cast-only to ?* and ?[*], respectively - if (expected_type->id == TypeTableEntryIdMaybe && + if (expected_type->id == TypeTableEntryIdOptional && expected_type->data.maybe.child_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer) { @@ -7718,12 +7718,12 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry } // maybe - if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { + if (expected_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdOptional) { ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type, source_node); if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdNullableChild; - result.data.nullable_child = allocate_nonzero(1); - *result.data.nullable_child = child; + result.id = ConstCastResultIdOptionalChild; + result.data.optional_child = allocate_nonzero(1); + *result.data.optional_child = child; } return result; } @@ -7925,7 +7925,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit conversion from ?T to ?U - if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { + if (expected_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdOptional) { ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type, value); if (res != ImplicitCastMatchResultNo) @@ -7933,7 +7933,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit conversion from non maybe type to maybe type - if (expected_type->id == TypeTableEntryIdMaybe) { + if (expected_type->id == TypeTableEntryIdOptional) { ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, actual_type, value); if (res != ImplicitCastMatchResultNo) @@ -7941,7 +7941,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit conversion from null literal to maybe type - if (expected_type->id == TypeTableEntryIdMaybe && + if (expected_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdNull) { return ImplicitCastMatchResultYes; @@ -7963,7 +7963,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit conversion from T to U!?T if (expected_type->id == TypeTableEntryIdErrorUnion && - expected_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe && + expected_type->data.error_union.payload_type->id == TypeTableEntryIdOptional && ir_types_match_with_implicit_cast(ira, expected_type->data.error_union.payload_type->data.maybe.child_type, actual_type, value)) @@ -8072,7 +8072,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit [N]T to ?[]const T - if (expected_type->id == TypeTableEntryIdMaybe && + if (expected_type->id == TypeTableEntryIdOptional && is_slice(expected_type->data.maybe.child_type) && actual_type->id == TypeTableEntryIdArray) { @@ -8552,13 +8552,13 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } - if (prev_type->id == TypeTableEntryIdMaybe && + if (prev_type->id == TypeTableEntryIdOptional && types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, source_node).id == ConstCastResultIdOk) { continue; } - if (cur_type->id == TypeTableEntryIdMaybe && + if (cur_type->id == TypeTableEntryIdOptional && types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, source_node).id == ConstCastResultIdOk) { prev_inst = cur_inst; @@ -8711,7 +8711,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod ir_add_error_node(ira, source_node, buf_sprintf("unable to make maybe out of number literal")); return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value.type->id == TypeTableEntryIdMaybe) { + } else if (prev_inst->value.type->id == TypeTableEntryIdOptional) { return prev_inst->value.type; } else { return get_maybe_type(ira->codegen, prev_inst->value.type); @@ -9193,7 +9193,7 @@ static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { } static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) { - assert(wanted_type->id == TypeTableEntryIdMaybe); + assert(wanted_type->id == TypeTableEntryIdOptional); if (instr_is_comptime(value)) { TypeTableEntry *payload_type = wanted_type->data.maybe.child_type; @@ -9211,7 +9211,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc if (get_codegen_ptr_type(wanted_type) != nullptr) { copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst); } else { - const_instruction->base.value.data.x_nullable = val; + const_instruction->base.value.data.x_optional = val; } const_instruction->base.value.type = wanted_type; return &const_instruction->base; @@ -9219,7 +9219,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; - result->value.data.rh_maybe = RuntimeHintMaybeNonNull; + result->value.data.rh_maybe = RuntimeHintOptionalNonNull; ir_add_alloca(ira, result, wanted_type); return result; } @@ -9361,7 +9361,7 @@ static IrInstruction *ir_analyze_cast_ref(IrAnalyze *ira, IrInstruction *source_ } static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) { - assert(wanted_type->id == TypeTableEntryIdMaybe); + assert(wanted_type->id == TypeTableEntryIdOptional); assert(instr_is_comptime(value)); ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); @@ -9373,7 +9373,7 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0; } else { - const_instruction->base.value.data.x_nullable = nullptr; + const_instruction->base.value.data.x_optional = nullptr; } const_instruction->base.value.type = wanted_type; return &const_instruction->base; @@ -9992,7 +9992,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // explicit cast from [N]T to ?[]const N - if (wanted_type->id == TypeTableEntryIdMaybe && + if (wanted_type->id == TypeTableEntryIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == TypeTableEntryIdArray) { @@ -10091,7 +10091,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from T to ?T // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism - if (wanted_type->id == TypeTableEntryIdMaybe) { + if (wanted_type->id == TypeTableEntryIdOptional) { TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); @@ -10120,7 +10120,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // explicit cast from null literal to maybe type - if (wanted_type->id == TypeTableEntryIdMaybe && + if (wanted_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdNull) { return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); @@ -10173,8 +10173,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from T to E!?T if (wanted_type->id == TypeTableEntryIdErrorUnion && - wanted_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe && - actual_type->id != TypeTableEntryIdMaybe) + wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional && + actual_type->id != TypeTableEntryIdOptional) { TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk || @@ -10737,13 +10737,13 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) { } } -static bool nullable_value_is_null(ConstExprValue *val) { +static bool optional_value_is_null(ConstExprValue *val) { assert(val->special == ConstValSpecialStatic); if (get_codegen_ptr_type(val->type) != nullptr) { return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && val->data.x_ptr.data.hard_coded_addr.addr == 0; } else { - return val->data.x_nullable == nullptr; + return val->data.x_optional == nullptr; } } @@ -10755,8 +10755,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp IrBinOp op_id = bin_op_instruction->op_id; bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); if (is_equality_cmp && - ((op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdMaybe) || - (op2->value.type->id == TypeTableEntryIdNull && op1->value.type->id == TypeTableEntryIdMaybe) || + ((op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdOptional) || + (op2->value.type->id == TypeTableEntryIdNull && op1->value.type->id == TypeTableEntryIdOptional) || (op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdNull))) { if (op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdNull) { @@ -10776,7 +10776,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad); if (!maybe_val) return ira->codegen->builtin_types.entry_invalid; - bool is_null = nullable_value_is_null(maybe_val); + bool is_null = optional_value_is_null(maybe_val); ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = (op_id == IrBinOpCmpEq) ? is_null : !is_null; return ira->codegen->builtin_types.entry_bool; @@ -10925,7 +10925,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdStruct: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdUnion: ir_add_error_node(ira, source_node, @@ -11998,7 +11998,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: @@ -12022,7 +12022,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name)); @@ -12049,24 +12049,24 @@ static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) { static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, IrInstructionErrorReturnTrace *instruction) { - if (instruction->nullable == IrInstructionErrorReturnTrace::Null) { + if (instruction->optional == IrInstructionErrorReturnTrace::Null) { TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); - TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); + TypeTableEntry *optional_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - assert(get_codegen_ptr_type(nullable_type) != nullptr); + assert(get_codegen_ptr_type(optional_type) != nullptr); out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; out_val->data.x_ptr.data.hard_coded_addr.addr = 0; - return nullable_type; + return optional_type; } IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, instruction->nullable); + instruction->base.source_node, instruction->optional); ir_link_new_instruction(new_instruction, &instruction->base); - return nullable_type; + return optional_type; } else { assert(ira->codegen->have_err_ret_tracing); IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, instruction->nullable); + instruction->base.source_node, instruction->optional); ir_link_new_instruction(new_instruction, &instruction->base); return get_ptr_to_stack_trace_type(ira->codegen); } @@ -12998,7 +12998,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -13017,7 +13017,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdUnreachable: case TypeTableEntryIdOpaque: ir_add_error_node(ira, un_op_instruction->base.source_node, - buf_sprintf("type '%s' not nullable", buf_ptr(&type_entry->name))); + buf_sprintf("type '%s' not optional", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } zig_unreachable(); @@ -13109,7 +13109,7 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio return ir_analyze_negation(ira, un_op_instruction); case IrUnOpDereference: return ir_analyze_dereference(ira, un_op_instruction); - case IrUnOpMaybe: + case IrUnOpOptional: return ir_analyze_maybe(ira, un_op_instruction); } zig_unreachable(); @@ -14155,7 +14155,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } - } else if (child_type->id == TypeTableEntryIdMaybe) { + } else if (child_type->id == TypeTableEntryIdOptional) { if (buf_eql_str(field_name, "Child")) { bool ptr_is_const = true; bool ptr_is_volatile = false; @@ -14339,7 +14339,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -14607,7 +14607,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -14715,7 +14715,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -14786,7 +14786,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -14810,14 +14810,14 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn TypeTableEntry *type_entry = value->value.type; - if (type_entry->id == TypeTableEntryIdMaybe) { + if (type_entry->id == TypeTableEntryIdOptional) { if (instr_is_comptime(value)) { ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefBad); if (!maybe_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_bool = !nullable_value_is_null(maybe_val); + out_val->data.x_bool = !optional_value_is_null(maybe_val); return ira->codegen->builtin_types.entry_bool; } @@ -14835,7 +14835,7 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn } static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, - IrInstructionUnwrapMaybe *unwrap_maybe_instruction) + IrInstructionUnwrapOptional *unwrap_maybe_instruction) { IrInstruction *value = unwrap_maybe_instruction->value->other; if (type_is_invalid(value->value.type)) @@ -14863,9 +14863,9 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile); ir_link_new_instruction(result_instr, &unwrap_maybe_instruction->base); return result_instr->value.type; - } else if (type_entry->id != TypeTableEntryIdMaybe) { + } else if (type_entry->id != TypeTableEntryIdOptional) { ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node, - buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name))); + buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } TypeTableEntry *child_type = type_entry->data.maybe.child_type; @@ -14881,7 +14881,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, ConstExprValue *maybe_val = const_ptr_pointee(ira->codegen, val); if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - if (nullable_value_is_null(maybe_val)) { + if (optional_value_is_null(maybe_val)) { ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null")); return ira->codegen->builtin_types.entry_invalid; } @@ -14891,7 +14891,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, if (type_is_codegen_pointer(child_type)) { out_val->data.x_ptr.data.ref.pointee = maybe_val; } else { - out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_nullable; + out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_optional; } return result_type; } @@ -15216,7 +15216,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, case TypeTableEntryIdStruct: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: @@ -15737,7 +15737,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdUnion: @@ -16255,11 +16255,11 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop 0, 0); fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { - fn_def_fields[6].data.x_nullable = create_const_vals(1); + fn_def_fields[6].data.x_optional = create_const_vals(1); ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); - init_const_slice(ira->codegen, fn_def_fields[6].data.x_nullable, lib_name, 0, buf_len(fn_node->lib_name), true); + init_const_slice(ira->codegen, fn_def_fields[6].data.x_optional, lib_name, 0, buf_len(fn_node->lib_name), true); } else { - fn_def_fields[6].data.x_nullable = nullptr; + fn_def_fields[6].data.x_optional = nullptr; } // return_type: type ensure_field_index(fn_def_val->type, "return_type", 7); @@ -16507,11 +16507,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t break; } - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: { result = create_const_vals(1); result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Nullable"); + result->type = ir_type_info_get_type(ira, "Optional"); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; @@ -16725,10 +16725,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type); if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) { - inner_fields[1].data.x_nullable = nullptr; + inner_fields[1].data.x_optional = nullptr; } else { - inner_fields[1].data.x_nullable = create_const_vals(1); - make_enum_field_val(inner_fields[1].data.x_nullable, union_field->enum_field, type_info_enum_field_type); + inner_fields[1].data.x_optional = create_const_vals(1); + make_enum_field_val(inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type); } inner_fields[2].special = ConstValSpecialStatic; @@ -16796,13 +16796,13 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize); if (!type_has_bits(struct_field->type_entry)) { - inner_fields[1].data.x_nullable = nullptr; + inner_fields[1].data.x_optional = nullptr; } else { size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index); - inner_fields[1].data.x_nullable = create_const_vals(1); - inner_fields[1].data.x_nullable->special = ConstValSpecialStatic; - inner_fields[1].data.x_nullable->type = ira->codegen->builtin_types.entry_usize; - bigint_init_unsigned(&inner_fields[1].data.x_nullable->data.x_bigint, byte_offset); + inner_fields[1].data.x_optional = create_const_vals(1); + inner_fields[1].data.x_optional->special = ConstValSpecialStatic; + inner_fields[1].data.x_optional->type = ira->codegen->builtin_types.entry_usize; + bigint_init_unsigned(&inner_fields[1].data.x_optional->data.x_bigint, byte_offset); } inner_fields[2].special = ConstValSpecialStatic; @@ -18027,7 +18027,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc case TypeTableEntryIdPromise: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -18591,7 +18591,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 old_align_bytes = fn_type_id.alignment; fn_type_id.alignment = align_bytes; result_type = get_fn_type(ira->codegen, &fn_type_id); - } else if (target_type->id == TypeTableEntryIdMaybe && + } else if (target_type->id == TypeTableEntryIdOptional && target_type->data.maybe.child_type->id == TypeTableEntryIdPointer) { TypeTableEntry *ptr_type = target_type->data.maybe.child_type; @@ -18599,7 +18599,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 TypeTableEntry *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes); result_type = get_maybe_type(ira->codegen, better_ptr_type); - } else if (target_type->id == TypeTableEntryIdMaybe && + } else if (target_type->id == TypeTableEntryIdOptional && target_type->data.maybe.child_type->id == TypeTableEntryIdFn) { FnTypeId fn_type_id = target_type->data.maybe.child_type->data.fn.fn_type_id; @@ -18757,7 +18757,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue return; case TypeTableEntryIdStruct: zig_panic("TODO buf_write_value_bytes struct type"); - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: zig_panic("TODO buf_write_value_bytes maybe type"); case TypeTableEntryIdErrorUnion: zig_panic("TODO buf_write_value_bytes error union"); @@ -18815,7 +18815,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_panic("TODO buf_read_value_bytes array type"); case TypeTableEntryIdStruct: zig_panic("TODO buf_read_value_bytes struct type"); - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: zig_panic("TODO buf_read_value_bytes maybe type"); case TypeTableEntryIdErrorUnion: zig_panic("TODO buf_read_value_bytes error union"); @@ -19731,7 +19731,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi case IrInstructionIdUnionInit: case IrInstructionIdStructFieldPtr: case IrInstructionIdUnionFieldPtr: - case IrInstructionIdMaybeWrap: + case IrInstructionIdOptionalWrap: case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: @@ -19791,8 +19791,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction); case IrInstructionIdTestNonNull: return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapMaybe: - return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapMaybe *)instruction); + case IrInstructionIdUnwrapOptional: + return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapOptional *)instruction); case IrInstructionIdClz: return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -20128,7 +20128,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdSliceType: case IrInstructionIdSizeOf: case IrInstructionIdTestNonNull: - case IrInstructionIdUnwrapMaybe: + case IrInstructionIdUnwrapOptional: case IrInstructionIdClz: case IrInstructionIdCtz: case IrInstructionIdSwitchVar: @@ -20150,7 +20150,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdFrameAddress: case IrInstructionIdTestErr: case IrInstructionIdUnwrapErrCode: - case IrInstructionIdMaybeWrap: + case IrInstructionIdOptionalWrap: case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdFnProto: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 776ef64566..43907fa9d4 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -148,7 +148,7 @@ static const char *ir_un_op_id_str(IrUnOp op_id) { return "-%"; case IrUnOpDereference: return "*"; - case IrUnOpMaybe: + case IrUnOpOptional: return "?"; } zig_unreachable(); @@ -481,7 +481,7 @@ static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instructi fprintf(irp->f, " != null"); } -static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapMaybe *instruction) { +static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapOptional *instruction) { fprintf(irp->f, "&??*"); ir_print_other_instruction(irp, instruction->value); if (!instruction->safety_check_on) { @@ -777,7 +777,7 @@ static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayl } } -static void ir_print_maybe_wrap(IrPrint *irp, IrInstructionMaybeWrap *instruction) { +static void ir_print_maybe_wrap(IrPrint *irp, IrInstructionOptionalWrap *instruction) { fprintf(irp->f, "@maybeWrap("); ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, ")"); @@ -1032,7 +1032,7 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) { static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) { fprintf(irp->f, "@errorReturnTrace("); - switch (instruction->nullable) { + switch (instruction->optional) { case IrInstructionErrorReturnTrace::Null: fprintf(irp->f, "Null"); break; @@ -1348,8 +1348,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTestNonNull: ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction); break; - case IrInstructionIdUnwrapMaybe: - ir_print_unwrap_maybe(irp, (IrInstructionUnwrapMaybe *)instruction); + case IrInstructionIdUnwrapOptional: + ir_print_unwrap_maybe(irp, (IrInstructionUnwrapOptional *)instruction); break; case IrInstructionIdCtz: ir_print_ctz(irp, (IrInstructionCtz *)instruction); @@ -1465,8 +1465,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdUnwrapErrPayload: ir_print_unwrap_err_payload(irp, (IrInstructionUnwrapErrPayload *)instruction); break; - case IrInstructionIdMaybeWrap: - ir_print_maybe_wrap(irp, (IrInstructionMaybeWrap *)instruction); + case IrInstructionIdOptionalWrap: + ir_print_maybe_wrap(irp, (IrInstructionOptionalWrap *)instruction); break; case IrInstructionIdErrWrapCode: ir_print_err_wrap_code(irp, (IrInstructionErrWrapCode *)instruction); diff --git a/src/parser.cpp b/src/parser.cpp index 3ad2de906b..2ee69f81ab 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1046,12 +1046,11 @@ static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index } /* -SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | PtrDerefExpression | SliceExpression) +SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ".*" | ".?") FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) ArrayAccessExpression : token(LBracket) Expression token(RBracket) SliceExpression = "[" Expression ".." option(Expression) "]" FieldAccessExpression : token(Dot) token(Symbol) -PtrDerefExpression = ".*" StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression */ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { @@ -1148,6 +1147,14 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token); node->data.ptr_deref_expr.target = primary_expr; + primary_expr = node; + } else if (token->id == TokenIdQuestion) { + *token_index += 1; + + AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, first_token); + node->data.prefix_op_expr.prefix_op = PrefixOpUnwrapOptional; + node->data.prefix_op_expr.primary_expr = primary_expr; + primary_expr = node; } else { ast_invalid_token_error(pc, token); @@ -1165,8 +1172,8 @@ static PrefixOp tok_to_prefix_op(Token *token) { case TokenIdDash: return PrefixOpNegation; case TokenIdMinusPercent: return PrefixOpNegationWrap; case TokenIdTilde: return PrefixOpBinNot; - case TokenIdMaybe: return PrefixOpMaybe; - case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe; + case TokenIdQuestion: return PrefixOpOptional; + case TokenIdDoubleQuestion: return PrefixOpUnwrapOptional; case TokenIdAmpersand: return PrefixOpAddrOf; default: return PrefixOpInvalid; } @@ -2304,8 +2311,8 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, size_t *token_index, bool ma } /* -UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression -UnwrapMaybe : "??" BoolOrExpression +UnwrapExpression : BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression +UnwrapOptional : "??" BoolOrExpression UnwrapError = "catch" option("|" Symbol "|") Expression */ static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) { @@ -2322,7 +2329,7 @@ static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, boo AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = lhs; - node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe; + node->data.bin_op_expr.bin_op = BinOpTypeUnwrapOptional; node->data.bin_op_expr.op2 = rhs; return node; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index badbd695ec..cfabdf11ad 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -625,7 +625,7 @@ void tokenize(Buf *buf, Tokenization *out) { t.state = TokenizeStateSawDot; break; case '?': - begin_token(&t, TokenIdMaybe); + begin_token(&t, TokenIdQuestion); t.state = TokenizeStateSawQuestionMark; break; default: @@ -639,11 +639,6 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); t.state = TokenizeStateStart; break; - case '=': - set_token_id(&t, t.cur_tok, TokenIdMaybeAssign); - end_token(&t); - t.state = TokenizeStateStart; - break; default: t.pos -= 1; end_token(&t); @@ -1609,8 +1604,7 @@ const char * token_name(TokenId id) { case TokenIdLBrace: return "{"; case TokenIdLBracket: return "["; case TokenIdLParen: return "("; - case TokenIdMaybe: return "?"; - case TokenIdMaybeAssign: return "?="; + case TokenIdQuestion: return "?"; case TokenIdMinusEq: return "-="; case TokenIdMinusPercent: return "-%"; case TokenIdMinusPercentEq: return "-%="; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index d0089909cd..7c617f85c6 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -100,8 +100,7 @@ enum TokenId { TokenIdLBrace, TokenIdLBracket, TokenIdLParen, - TokenIdMaybe, - TokenIdMaybeAssign, + TokenIdQuestion, TokenIdMinusEq, TokenIdMinusPercent, TokenIdMinusPercentEq, diff --git a/src/translate_c.cpp b/src/translate_c.cpp index d78bd1fa70..aaaf5a1edb 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -382,7 +382,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r fn_def->data.fn_def.fn_proto = fn_proto; fn_proto->data.fn_proto.fn_def_node = fn_def; - AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, ref_node); + AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, ref_node); AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr); fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node; @@ -410,7 +410,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r } static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child) { - return trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, child); + return trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, child); } static AstNode *get_global(Context *c, Buf *name) { @@ -879,14 +879,14 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou } if (qual_type_child_is_fn_proto(child_qt)) { - return trans_create_node_prefix_op(c, PrefixOpMaybe, child_node); + return trans_create_node_prefix_op(c, PrefixOpOptional, child_node); } PtrLen ptr_len = type_is_opaque(c, child_qt.getTypePtr(), source_loc) ? PtrLenSingle : PtrLenUnknown; AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), child_qt.isVolatileQualified(), child_node, ptr_len); - return trans_create_node_prefix_op(c, PrefixOpMaybe, pointer_node); + return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node); } case Type::Typedef: { @@ -1963,7 +1963,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc bool is_fn_ptr = qual_type_is_fn_ptr(stmt->getSubExpr()->getType()); if (is_fn_ptr) return value_node; - AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node); + AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, value_node); return trans_create_node_ptr_deref(c, unwrapped); } case UO_Plus: @@ -2587,7 +2587,7 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope * } } if (callee_node == nullptr) { - callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, callee_raw_node); + callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, callee_raw_node); } } else { callee_node = callee_raw_node; @@ -4301,7 +4301,7 @@ static AstNode *trans_lookup_ast_maybe_fn(Context *c, AstNode *ref_node) { return nullptr; if (prefix_node->type != NodeTypePrefixOpExpr) return nullptr; - if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpMaybe) + if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpOptional) return nullptr; AstNode *fn_proto_node = prefix_node->data.prefix_op_expr.primary_expr; diff --git a/std/array_list.zig b/std/array_list.zig index 30715f4d6f..1a235d28a3 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -258,7 +258,7 @@ test "iterator ArrayList test" { } it.reset(); - assert(??it.next() == 1); + assert(it.next().? == 1); } test "insert ArrayList test" { diff --git a/std/buf_map.zig b/std/buf_map.zig index 22d821ae7b..0d4f3a6d5e 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -72,15 +72,15 @@ test "BufMap" { defer bufmap.deinit(); try bufmap.set("x", "1"); - assert(mem.eql(u8, ??bufmap.get("x"), "1")); + assert(mem.eql(u8, bufmap.get("x").?, "1")); assert(1 == bufmap.count()); try bufmap.set("x", "2"); - assert(mem.eql(u8, ??bufmap.get("x"), "2")); + assert(mem.eql(u8, bufmap.get("x").?, "2")); assert(1 == bufmap.count()); try bufmap.set("x", "3"); - assert(mem.eql(u8, ??bufmap.get("x"), "3")); + assert(mem.eql(u8, bufmap.get("x").?, "3")); assert(1 == bufmap.count()); bufmap.delete("x"); diff --git a/std/event.zig b/std/event.zig index 89ab816bb6..0821c789b7 100644 --- a/std/event.zig +++ b/std/event.zig @@ -40,9 +40,9 @@ pub const TcpServer = struct { self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd)); self.accept_coro = try async TcpServer.handler(self); - errdefer cancel ??self.accept_coro; + errdefer cancel self.accept_coro.?; - try self.loop.addFd(self.sockfd, ??self.accept_coro); + try self.loop.addFd(self.sockfd, self.accept_coro.?); errdefer self.loop.removeFd(self.sockfd); } diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 3844fbb10a..b52625e26e 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -111,7 +111,7 @@ pub fn formatType( builtin.TypeId.Bool => { return output(context, if (value) "true" else "false"); }, - builtin.TypeId.Nullable => { + builtin.TypeId.Optional => { if (value) |payload| { return formatType(payload, fmt, context, Errors, output); } else { @@ -819,11 +819,11 @@ test "parse unsigned comptime" { test "fmt.format" { { const value: ?i32 = 1234; - try testFmt("nullable: 1234\n", "nullable: {}\n", value); + try testFmt("optional: 1234\n", "optional: {}\n", value); } { const value: ?i32 = null; - try testFmt("nullable: null\n", "nullable: {}\n", value); + try testFmt("optional: null\n", "optional: {}\n", value); } { const value: error!i32 = 1234; diff --git a/std/hash_map.zig b/std/hash_map.zig index a323cdc197..3bd03d4f28 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -265,11 +265,11 @@ test "basic hash map usage" { assert((map.put(4, 44) catch unreachable) == null); assert((map.put(5, 55) catch unreachable) == null); - assert(??(map.put(5, 66) catch unreachable) == 55); - assert(??(map.put(5, 55) catch unreachable) == 66); + assert((map.put(5, 66) catch unreachable).? == 55); + assert((map.put(5, 55) catch unreachable).? == 66); assert(map.contains(2)); - assert((??map.get(2)).value == 22); + assert(map.get(2).?.value == 22); _ = map.remove(2); assert(map.remove(2) == null); assert(map.get(2) == null); @@ -317,7 +317,7 @@ test "iterator hash map" { } it.reset(); - var entry = ??it.next(); + var entry = it.next().?; assert(entry.key == keys[0]); assert(entry.value == values[0]); } diff --git a/std/heap.zig b/std/heap.zig index 5d430bc761..d1fbf9ca0a 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -142,7 +142,7 @@ pub const DirectAllocator = struct { const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; const old_ptr = @intToPtr(*c_void, root_addr); const amt = new_size + alignment + @sizeOf(usize); - const new_ptr = os.windows.HeapReAlloc(??self.heap_handle, 0, old_ptr, amt) ?? blk: { + const new_ptr = os.windows.HeapReAlloc(self.heap_handle.?, 0, old_ptr, amt) ?? blk: { if (new_size > old_mem.len) return error.OutOfMemory; const new_record_addr = old_record_addr - new_size + old_mem.len; @intToPtr(*align(1) usize, new_record_addr).* = root_addr; @@ -171,7 +171,7 @@ pub const DirectAllocator = struct { const record_addr = @ptrToInt(bytes.ptr) + bytes.len; const root_addr = @intToPtr(*align(1) usize, record_addr).*; const ptr = @intToPtr(*c_void, root_addr); - _ = os.windows.HeapFree(??self.heap_handle, 0, ptr); + _ = os.windows.HeapFree(self.heap_handle.?, 0, ptr); }, else => @compileError("Unsupported OS"), } diff --git a/std/json.zig b/std/json.zig index 03b19a7fa4..75ea2eee1c 100644 --- a/std/json.zig +++ b/std/json.zig @@ -908,7 +908,7 @@ pub const TokenStream = struct { }; fn checkNext(p: *TokenStream, id: Token.Id) void { - const token = ??(p.next() catch unreachable); + const token = (p.next() catch unreachable).?; debug.assert(token.id == id); } @@ -1376,17 +1376,17 @@ test "json parser dynamic" { var root = tree.root; - var image = (??root.Object.get("Image")).value; + var image = root.Object.get("Image").?.value; - const width = (??image.Object.get("Width")).value; + const width = image.Object.get("Width").?.value; debug.assert(width.Integer == 800); - const height = (??image.Object.get("Height")).value; + const height = image.Object.get("Height").?.value; debug.assert(height.Integer == 600); - const title = (??image.Object.get("Title")).value; + const title = image.Object.get("Title").?.value; debug.assert(mem.eql(u8, title.String, "View from 15th Floor")); - const animated = (??image.Object.get("Animated")).value; + const animated = image.Object.get("Animated").?.value; debug.assert(animated.Bool == false); } diff --git a/std/linked_list.zig b/std/linked_list.zig index fbc0a0c42a..536c6d24d0 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -270,8 +270,8 @@ test "basic linked list test" { var last = list.pop(); // {2, 3, 4} list.remove(three); // {2, 4} - assert((??list.first).data == 2); - assert((??list.last).data == 4); + assert(list.first.?.data == 2); + assert(list.last.?.data == 4); assert(list.len == 2); } @@ -336,7 +336,7 @@ test "basic intrusive linked list test" { var last = list.pop(); // {2, 3, 4} list.remove(&three.link); // {2, 4} - assert((??list.first).toData().value == 2); - assert((??list.last).toData().value == 4); + assert(list.first.?.toData().value == 2); + assert(list.last.?.toData().value == 4); assert(list.len == 2); } diff --git a/std/macho.zig b/std/macho.zig index d6eef9a325..64f78ae4a3 100644 --- a/std/macho.zig +++ b/std/macho.zig @@ -130,7 +130,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable for (syms) |sym| { if (!isSymbol(sym)) continue; const start = sym.n_strx; - const end = ??mem.indexOfScalarPos(u8, strings, start, 0); + const end = mem.indexOfScalarPos(u8, strings, start, 0).?; const name = strings[start..end]; const address = sym.n_value; symbols[nsym] = Symbol{ .name = name, .address = address }; diff --git a/std/mem.zig b/std/mem.zig index 423460e73b..f961c7862b 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -304,20 +304,20 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee } test "mem.indexOf" { - assert(??indexOf(u8, "one two three four", "four") == 14); - assert(??lastIndexOf(u8, "one two three two four", "two") == 14); + assert(indexOf(u8, "one two three four", "four").? == 14); + assert(lastIndexOf(u8, "one two three two four", "two").? == 14); assert(indexOf(u8, "one two three four", "gour") == null); assert(lastIndexOf(u8, "one two three four", "gour") == null); - assert(??indexOf(u8, "foo", "foo") == 0); - assert(??lastIndexOf(u8, "foo", "foo") == 0); + assert(indexOf(u8, "foo", "foo").? == 0); + assert(lastIndexOf(u8, "foo", "foo").? == 0); assert(indexOf(u8, "foo", "fool") == null); assert(lastIndexOf(u8, "foo", "lfoo") == null); assert(lastIndexOf(u8, "foo", "fool") == null); - assert(??indexOf(u8, "foo foo", "foo") == 0); - assert(??lastIndexOf(u8, "foo foo", "foo") == 4); - assert(??lastIndexOfAny(u8, "boo, cat", "abo") == 6); - assert(??lastIndexOfScalar(u8, "boo", 'o') == 2); + assert(indexOf(u8, "foo foo", "foo").? == 0); + assert(lastIndexOf(u8, "foo foo", "foo").? == 4); + assert(lastIndexOfAny(u8, "boo, cat", "abo").? == 6); + assert(lastIndexOfScalar(u8, "boo", 'o').? == 2); } /// Reads an integer from memory with size equal to bytes.len. @@ -432,9 +432,9 @@ pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator { test "mem.split" { var it = split(" abc def ghi ", " "); - assert(eql(u8, ??it.next(), "abc")); - assert(eql(u8, ??it.next(), "def")); - assert(eql(u8, ??it.next(), "ghi")); + assert(eql(u8, it.next().?, "abc")); + assert(eql(u8, it.next().?, "def")); + assert(eql(u8, it.next().?, "ghi")); assert(it.next() == null); } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 822ade2eb8..1e3a732498 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -156,7 +156,7 @@ pub const ChildProcess = struct { }; } try self.waitUnwrappedWindows(); - return ??self.term; + return self.term.?; } pub fn killPosix(self: *ChildProcess) !Term { @@ -175,7 +175,7 @@ pub const ChildProcess = struct { }; } self.waitUnwrapped(); - return ??self.term; + return self.term.?; } /// Blocks until child process terminates and then cleans up all resources. @@ -212,8 +212,8 @@ pub const ChildProcess = struct { defer Buffer.deinit(&stdout); defer Buffer.deinit(&stderr); - var stdout_file_in_stream = io.FileInStream.init(&??child.stdout); - var stderr_file_in_stream = io.FileInStream.init(&??child.stderr); + var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?); + var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?); try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size); try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size); @@ -232,7 +232,7 @@ pub const ChildProcess = struct { } try self.waitUnwrappedWindows(); - return ??self.term; + return self.term.?; } fn waitPosix(self: *ChildProcess) !Term { @@ -242,7 +242,7 @@ pub const ChildProcess = struct { } self.waitUnwrapped(); - return ??self.term; + return self.term.?; } pub fn deinit(self: *ChildProcess) void { @@ -619,13 +619,13 @@ pub const ChildProcess = struct { self.term = null; if (self.stdin_behavior == StdIo.Pipe) { - os.close(??g_hChildStd_IN_Rd); + os.close(g_hChildStd_IN_Rd.?); } if (self.stderr_behavior == StdIo.Pipe) { - os.close(??g_hChildStd_ERR_Wr); + os.close(g_hChildStd_ERR_Wr.?); } if (self.stdout_behavior == StdIo.Pipe) { - os.close(??g_hChildStd_OUT_Wr); + os.close(g_hChildStd_OUT_Wr.?); } } diff --git a/std/os/index.zig b/std/os/index.zig index fe5ecc38ba..807b2c398b 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -422,7 +422,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: const exe_path = argv[0]; if (mem.indexOfScalar(u8, exe_path, '/') != null) { - return posixExecveErrnoToErr(posix.getErrno(posix.execve(??argv_buf[0], argv_buf.ptr, envp_buf.ptr))); + return posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); } const PATH = getEnvPosix("PATH") ?? "/usr/local/bin:/bin/:/usr/bin"; @@ -1729,7 +1729,7 @@ test "windows arg parsing" { fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void { var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); for (expected_args) |expected_arg| { - const arg = ??it.next(debug.global_allocator) catch unreachable; + const arg = it.next(debug.global_allocator).? catch unreachable; assert(mem.eql(u8, arg, expected_arg)); } assert(it.next(debug.global_allocator) == null); diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig index 2ab4d0cbc1..1414b8185b 100644 --- a/std/os/linux/vdso.zig +++ b/std/os/linux/vdso.zig @@ -67,7 +67,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { if (0 == syms[i].st_shndx) continue; if (!mem.eql(u8, name, cstr.toSliceConst(strings + syms[i].st_name))) continue; if (maybe_versym) |versym| { - if (!checkver(??maybe_verdef, versym[i], vername, strings)) + if (!checkver(maybe_verdef.?, versym[i], vername, strings)) continue; } return base + syms[i].st_value; diff --git a/std/os/path.zig b/std/os/path.zig index 4df6179bf5..430dda2934 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -265,7 +265,7 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) bool { var it2 = mem.split(ns2, []u8{sep2}); // TODO ASCII is wrong, we actually need full unicode support to compare paths. - return asciiEqlIgnoreCase(??it1.next(), ??it2.next()); + return asciiEqlIgnoreCase(it1.next().?, it2.next().?); } fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8) bool { @@ -286,7 +286,7 @@ fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8 var it2 = mem.split(p2, []u8{sep2}); // TODO ASCII is wrong, we actually need full unicode support to compare paths. - return asciiEqlIgnoreCase(??it1.next(), ??it2.next()) and asciiEqlIgnoreCase(??it1.next(), ??it2.next()); + return asciiEqlIgnoreCase(it1.next().?, it2.next().?) and asciiEqlIgnoreCase(it1.next().?, it2.next().?); }, } } @@ -414,8 +414,8 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { WindowsPath.Kind.NetworkShare => { result = try allocator.alloc(u8, max_size); var it = mem.split(paths[first_index], "/\\"); - const server_name = ??it.next(); - const other_name = ??it.next(); + const server_name = it.next().?; + const other_name = it.next().?; result[result_index] = '\\'; result_index += 1; diff --git a/std/segmented_list.zig b/std/segmented_list.zig index a2f3607ad8..9f10f4d44a 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -364,7 +364,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { assert(x == 0); } - assert(??list.pop() == 100); + assert(list.pop().? == 100); assert(list.len == 99); try list.pushMany([]i32{ @@ -373,9 +373,9 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { 3, }); assert(list.len == 102); - assert(??list.pop() == 3); - assert(??list.pop() == 2); - assert(??list.pop() == 1); + assert(list.pop().? == 3); + assert(list.pop().? == 2); + assert(list.pop().? == 1); assert(list.len == 99); try list.pushMany([]const i32{}); diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 8aefe4751f..dd37f1edb6 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -54,10 +54,10 @@ fn posixCallMainAndExit() noreturn { const argc = argc_ptr[0]; const argv = @ptrCast([*][*]u8, argc_ptr + 1); - const envp_nullable = @ptrCast([*]?[*]u8, argv + argc + 1); + const envp_optional = @ptrCast([*]?[*]u8, argv + argc + 1); var envp_count: usize = 0; - while (envp_nullable[envp_count]) |_| : (envp_count += 1) {} - const envp = @ptrCast([*][*]u8, envp_nullable)[0..envp_count]; + while (envp_optional[envp_count]) |_| : (envp_count += 1) {} + const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count]; if (builtin.os == builtin.Os.linux) { const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1); var i: usize = 0; diff --git a/std/special/builtin.zig b/std/special/builtin.zig index e537078924..e97b0a89e4 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -19,7 +19,7 @@ export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 { var index: usize = 0; while (index != n) : (index += 1) - (??dest)[index] = c; + dest.?[index] = c; return dest; } @@ -29,7 +29,7 @@ export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*] var index: usize = 0; while (index != n) : (index += 1) - (??dest)[index] = (??src)[index]; + dest.?[index] = src.?[index]; return dest; } @@ -40,13 +40,13 @@ export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 { if (@ptrToInt(dest) < @ptrToInt(src)) { var index: usize = 0; while (index != n) : (index += 1) { - (??dest)[index] = (??src)[index]; + dest.?[index] = src.?[index]; } } else { var index = n; while (index != 0) { index -= 1; - (??dest)[index] = (??src)[index]; + dest.?[index] = src.?[index]; } } diff --git a/std/unicode.zig b/std/unicode.zig index 3d1bebdb55..21ae12f59c 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -286,15 +286,15 @@ fn testUtf8IteratorOnAscii() void { const s = Utf8View.initComptime("abc"); var it1 = s.iterator(); - debug.assert(std.mem.eql(u8, "a", ??it1.nextCodepointSlice())); - debug.assert(std.mem.eql(u8, "b", ??it1.nextCodepointSlice())); - debug.assert(std.mem.eql(u8, "c", ??it1.nextCodepointSlice())); + debug.assert(std.mem.eql(u8, "a", it1.nextCodepointSlice().?)); + debug.assert(std.mem.eql(u8, "b", it1.nextCodepointSlice().?)); + debug.assert(std.mem.eql(u8, "c", it1.nextCodepointSlice().?)); debug.assert(it1.nextCodepointSlice() == null); var it2 = s.iterator(); - debug.assert(??it2.nextCodepoint() == 'a'); - debug.assert(??it2.nextCodepoint() == 'b'); - debug.assert(??it2.nextCodepoint() == 'c'); + debug.assert(it2.nextCodepoint().? == 'a'); + debug.assert(it2.nextCodepoint().? == 'b'); + debug.assert(it2.nextCodepoint().? == 'c'); debug.assert(it2.nextCodepoint() == null); } @@ -321,15 +321,15 @@ fn testUtf8ViewOk() void { const s = Utf8View.initComptime("東京市"); var it1 = s.iterator(); - debug.assert(std.mem.eql(u8, "東", ??it1.nextCodepointSlice())); - debug.assert(std.mem.eql(u8, "京", ??it1.nextCodepointSlice())); - debug.assert(std.mem.eql(u8, "市", ??it1.nextCodepointSlice())); + debug.assert(std.mem.eql(u8, "東", it1.nextCodepointSlice().?)); + debug.assert(std.mem.eql(u8, "京", it1.nextCodepointSlice().?)); + debug.assert(std.mem.eql(u8, "市", it1.nextCodepointSlice().?)); debug.assert(it1.nextCodepointSlice() == null); var it2 = s.iterator(); - debug.assert(??it2.nextCodepoint() == 0x6771); - debug.assert(??it2.nextCodepoint() == 0x4eac); - debug.assert(??it2.nextCodepoint() == 0x5e02); + debug.assert(it2.nextCodepoint().? == 0x6771); + debug.assert(it2.nextCodepoint().? == 0x4eac); + debug.assert(it2.nextCodepoint().? == 0x5e02); debug.assert(it2.nextCodepoint() == null); } diff --git a/std/zig/ast.zig b/std/zig/ast.zig index a4b64d5db2..defaded78a 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1417,7 +1417,7 @@ pub const Node = struct { Range, Sub, SubWrap, - UnwrapMaybe, + UnwrapOptional, }; pub fn iterate(self: *InfixOp, index: usize) ?*Node { @@ -1475,7 +1475,7 @@ pub const Node = struct { Op.Range, Op.Sub, Op.SubWrap, - Op.UnwrapMaybe, + Op.UnwrapOptional, => {}, } @@ -1507,14 +1507,13 @@ pub const Node = struct { BitNot, BoolNot, Cancel, - MaybeType, + OptionalType, Negation, NegationWrap, Resume, PtrType: PtrInfo, SliceType: PtrInfo, Try, - UnwrapMaybe, }; pub const PtrInfo = struct { @@ -1557,12 +1556,12 @@ pub const Node = struct { Op.BitNot, Op.BoolNot, Op.Cancel, - Op.MaybeType, + Op.OptionalType, Op.Negation, Op.NegationWrap, Op.Try, Op.Resume, - Op.UnwrapMaybe, + Op.UnwrapOptional, Op.PointerType, => {}, } @@ -1619,6 +1618,7 @@ pub const Node = struct { ArrayInitializer: InitList, StructInitializer: InitList, Deref, + UnwrapOptional, pub const InitList = SegmentedList(*Node, 2); diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 7faca8e11b..9f8ef3c3d6 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -711,7 +711,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { else => { // TODO: this is a special case. Remove this when #760 is fixed if (token_ptr.id == Token.Id.Keyword_error) { - if ((??tok_it.peek()).id == Token.Id.LBrace) { + if (tok_it.peek().?.id == Token.Id.LBrace) { const error_type_node = try arena.construct(ast.Node.ErrorType{ .base = ast.Node{ .id = ast.Node.Id.ErrorType }, .token = token_index, @@ -1434,8 +1434,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ .id = Token.Id.AngleBracketRight, - .ptr = &??async_node.rangle_bracket, - }, + .ptr = &async_node.rangle_bracket.? }, }); try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &async_node.allocator_type } }); continue; @@ -1567,7 +1566,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .bit_range = null, }; // TODO https://github.com/ziglang/zig/issues/1022 - const align_info = &??addr_of_info.align_info; + const align_info = &addr_of_info.align_info.?; try stack.append(State{ .AlignBitRange = align_info }); try stack.append(State{ .Expression = OptionalCtx{ .Required = &align_info.node } }); @@ -1604,7 +1603,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { switch (token.ptr.id) { Token.Id.Colon => { align_info.bit_range = ast.Node.PrefixOp.PtrInfo.Align.BitRange(undefined); - const bit_range = &??align_info.bit_range; + const bit_range = &align_info.bit_range.?; try stack.append(State{ .ExpectToken = Token.Id.RParen }); try stack.append(State{ .Expression = OptionalCtx{ .Required = &bit_range.end } }); @@ -2144,7 +2143,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.CurlySuffixExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - if ((??tok_it.peek()).id == Token.Id.Period) { + if (tok_it.peek().?.id == Token.Id.Period) { const node = try arena.construct(ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, @@ -2326,6 +2325,17 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; continue; } + if (eatToken(&tok_it, &tree, Token.Id.QuestionMark)) |question_token| { + const node = try arena.construct(ast.Node.SuffixOp{ + .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op.UnwrapOptional, + .rtoken = question_token, + }); + opt_ctx.store(&node.base); + stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + continue; + } const node = try arena.construct(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, @@ -2403,7 +2413,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .arrow_token = next_token_index, .return_type = undefined, }; - const return_type_ptr = &((??node.result).return_type); + const return_type_ptr = &node.result.?.return_type; try stack.append(State{ .Expression = OptionalCtx{ .Required = return_type_ptr } }); continue; }, @@ -2875,7 +2885,7 @@ const OptionalCtx = union(enum) { pub fn get(self: *const OptionalCtx) ?*ast.Node { switch (self.*) { OptionalCtx.Optional => |ptr| return ptr.*, - OptionalCtx.RequiredNull => |ptr| return ??ptr.*, + OptionalCtx.RequiredNull => |ptr| return ptr.*.?, OptionalCtx.Required => |ptr| return ptr.*, } } @@ -3237,7 +3247,7 @@ fn tokenIdToAssignment(id: *const Token.Id) ?ast.Node.InfixOp.Op { fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { Token.Id.Keyword_catch => ast.Node.InfixOp.Op{ .Catch = null }, - Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op{ .UnwrapMaybe = void{} }, + Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op{ .UnwrapOptional = void{} }, else => null, }; } @@ -3299,8 +3309,7 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { .volatile_token = null, }, }, - Token.Id.QuestionMark => ast.Node.PrefixOp.Op{ .MaybeType = void{} }, - Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op{ .UnwrapMaybe = void{} }, + Token.Id.QuestionMark => ast.Node.PrefixOp.Op{ .OptionalType = void{} }, Token.Id.Keyword_await => ast.Node.PrefixOp.Op{ .Await = void{} }, Token.Id.Keyword_try => ast.Node.PrefixOp.Op{ .Try = void{} }, else => null, @@ -3322,7 +3331,7 @@ fn createToCtxLiteral(arena: *mem.Allocator, opt_ctx: *const OptionalCtx, compti } fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType(Token.Id)) ?TokenIndex { - const token = ??tok_it.peek(); + const token = tok_it.peek().?; if (token.id == id) { return nextToken(tok_it, tree).index; @@ -3334,7 +3343,7 @@ fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType( fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedToken { const result = AnnotatedToken{ .index = tok_it.index, - .ptr = ??tok_it.next(), + .ptr = tok_it.next().?, }; assert(result.ptr.id != Token.Id.LineComment); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 91a56de827..ea3a4858b0 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -650,9 +650,10 @@ test "zig fmt: statements with empty line between" { ); } -test "zig fmt: ptr deref operator" { +test "zig fmt: ptr deref operator and unwrap optional operator" { try testCanonical( \\const a = b.*; + \\const a = b.?; \\ ); } @@ -1209,7 +1210,7 @@ test "zig fmt: precedence" { test "zig fmt: prefix operators" { try testCanonical( \\test "prefix operators" { - \\ try return --%~??!*&0; + \\ try return --%~!*&0; \\} \\ ); diff --git a/std/zig/render.zig b/std/zig/render.zig index 7c9b53b77a..0b8e4d1453 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -222,7 +222,7 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i } } - const value_expr = ??tag.value_expr; + const value_expr = tag.value_expr.?; try renderToken(tree, stream, tree.prevToken(value_expr.firstToken()), indent, start_col, Space.Space); // = try renderExpression(allocator, stream, tree, indent, start_col, value_expr, Space.Comma); // value, }, @@ -465,8 +465,7 @@ fn renderExpression( ast.Node.PrefixOp.Op.BoolNot, ast.Node.PrefixOp.Op.Negation, ast.Node.PrefixOp.Op.NegationWrap, - ast.Node.PrefixOp.Op.UnwrapMaybe, - ast.Node.PrefixOp.Op.MaybeType, + ast.Node.PrefixOp.Op.OptionalType, ast.Node.PrefixOp.Op.AddressOf, => { try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); @@ -513,7 +512,7 @@ fn renderExpression( var it = call_info.params.iterator(0); while (true) { - const param_node = ??it.next(); + const param_node = it.next().?; const param_node_new_indent = if (param_node.*.id == ast.Node.Id.MultilineStringLiteral) blk: { break :blk indent; @@ -559,10 +558,10 @@ fn renderExpression( return renderToken(tree, stream, rbracket, indent, start_col, space); // ] }, - ast.Node.SuffixOp.Op.Deref => { + ast.Node.SuffixOp.Op.Deref, ast.Node.SuffixOp.Op.UnwrapOptional => { try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // . - return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // * + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // * or ? }, @TagType(ast.Node.SuffixOp.Op).Slice => |range| { @@ -595,7 +594,7 @@ fn renderExpression( } if (field_inits.len == 1) blk: { - const field_init = ??field_inits.at(0).*.cast(ast.Node.FieldInitializer); + const field_init = field_inits.at(0).*.cast(ast.Node.FieldInitializer).?; if (field_init.expr.cast(ast.Node.SuffixOp)) |nested_suffix_op| { if (nested_suffix_op.op == ast.Node.SuffixOp.Op.StructInitializer) { @@ -688,7 +687,7 @@ fn renderExpression( var count: usize = 1; var it = exprs.iterator(0); while (true) { - const expr = (??it.next()).*; + const expr = it.next().?.*; if (it.peek()) |next_expr| { const expr_last_token = expr.*.lastToken() + 1; const loc = tree.tokenLocation(tree.tokens.at(expr_last_token).end, next_expr.*.firstToken()); @@ -806,7 +805,7 @@ fn renderExpression( }, } - return renderExpression(allocator, stream, tree, indent, start_col, ??flow_expr.rhs, space); + return renderExpression(allocator, stream, tree, indent, start_col, flow_expr.rhs.?, space); }, ast.Node.Id.Payload => { @@ -1245,7 +1244,7 @@ fn renderExpression( } else { var it = switch_case.items.iterator(0); while (true) { - const node = ??it.next(); + const node = it.next().?; if (it.peek()) |next_node| { try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None); @@ -1550,7 +1549,7 @@ fn renderExpression( var it = asm_node.outputs.iterator(0); while (true) { - const asm_output = ??it.next(); + const asm_output = it.next().?; const node = &(asm_output.*).base; if (it.peek()) |next_asm_output| { @@ -1588,7 +1587,7 @@ fn renderExpression( var it = asm_node.inputs.iterator(0); while (true) { - const asm_input = ??it.next(); + const asm_input = it.next().?; const node = &(asm_input.*).base; if (it.peek()) |next_asm_input| { @@ -1620,7 +1619,7 @@ fn renderExpression( var it = asm_node.clobbers.iterator(0); while (true) { - const clobber_token = ??it.next(); + const clobber_token = it.next().?; if (it.peek() == null) { try renderToken(tree, stream, clobber_token.*, indent_once, start_col, Space.Newline); diff --git a/test/cases/bugs/656.zig b/test/cases/bugs/656.zig index a6035d51bb..f93f0ac4d5 100644 --- a/test/cases/bugs/656.zig +++ b/test/cases/bugs/656.zig @@ -9,7 +9,7 @@ const Value = struct { align_expr: ?u32, }; -test "nullable if after an if in a switch prong of a switch with 2 prongs in an else" { +test "optional if after an if in a switch prong of a switch with 2 prongs in an else" { foo(false, true); } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index da3cba7d80..a56c470408 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -109,16 +109,16 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" { const Self = this; x: u8, fn constConst(p: *const *const Self) u8 { - return (p.*).x; + return p.*.x; } fn maybeConstConst(p: ?*const *const Self) u8 { - return ((??p).*).x; + return p.?.*.x; } fn constConstConst(p: *const *const *const Self) u8 { - return (p.*.*).x; + return p.*.*.x; } fn maybeConstConstConst(p: ?*const *const *const Self) u8 { - return ((??p).*.*).x; + return p.?.*.*.x; } }; const s = S{ .x = 42 }; @@ -177,56 +177,56 @@ test "string literal to &const []const u8" { } test "implicitly cast from T to error!?T" { - castToMaybeTypeError(1); - comptime castToMaybeTypeError(1); + castToOptionalTypeError(1); + comptime castToOptionalTypeError(1); } const A = struct { a: i32, }; -fn castToMaybeTypeError(z: i32) void { +fn castToOptionalTypeError(z: i32) void { const x = i32(1); const y: error!?i32 = x; - assert(??(try y) == 1); + assert((try y).? == 1); const f = z; const g: error!?i32 = f; const a = A{ .a = z }; const b: error!?A = a; - assert((??(b catch unreachable)).a == 1); + assert((b catch unreachable).?.a == 1); } test "implicitly cast from int to error!?T" { - implicitIntLitToMaybe(); - comptime implicitIntLitToMaybe(); + implicitIntLitToOptional(); + comptime implicitIntLitToOptional(); } -fn implicitIntLitToMaybe() void { +fn implicitIntLitToOptional() void { const f: ?i32 = 1; const g: error!?i32 = 1; } test "return null from fn() error!?&T" { - const a = returnNullFromMaybeTypeErrorRef(); - const b = returnNullLitFromMaybeTypeErrorRef(); + const a = returnNullFromOptionalTypeErrorRef(); + const b = returnNullLitFromOptionalTypeErrorRef(); assert((try a) == null and (try b) == null); } -fn returnNullFromMaybeTypeErrorRef() error!?*A { +fn returnNullFromOptionalTypeErrorRef() error!?*A { const a: ?*A = null; return a; } -fn returnNullLitFromMaybeTypeErrorRef() error!?*A { +fn returnNullLitFromOptionalTypeErrorRef() error!?*A { return null; } test "peer type resolution: ?T and T" { - assert(??peerTypeTAndMaybeT(true, false) == 0); - assert(??peerTypeTAndMaybeT(false, false) == 3); + assert(peerTypeTAndOptionalT(true, false).? == 0); + assert(peerTypeTAndOptionalT(false, false).? == 3); comptime { - assert(??peerTypeTAndMaybeT(true, false) == 0); - assert(??peerTypeTAndMaybeT(false, false) == 3); + assert(peerTypeTAndOptionalT(true, false).? == 0); + assert(peerTypeTAndOptionalT(false, false).? == 3); } } -fn peerTypeTAndMaybeT(c: bool, b: bool) ?usize { +fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { if (c) { return if (b) null else usize(0); } @@ -251,11 +251,11 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { } test "implicitly cast from [N]T to ?[]const T" { - assert(mem.eql(u8, ??castToMaybeSlice(), "hi")); - comptime assert(mem.eql(u8, ??castToMaybeSlice(), "hi")); + assert(mem.eql(u8, castToOptionalSlice().?, "hi")); + comptime assert(mem.eql(u8, castToOptionalSlice().?, "hi")); } -fn castToMaybeSlice() ?[]const u8 { +fn castToOptionalSlice() ?[]const u8 { return "hi"; } @@ -404,5 +404,5 @@ fn testCastPtrOfArrayToSliceAndPtr() void { test "cast *[1][*]const u8 to [*]const ?[*]const u8" { const window_name = [1][*]const u8{c"window name"}; const x: [*]const ?[*]const u8 = &window_name; - assert(mem.eql(u8, std.cstr.toSliceConst(??x[0]), "window name")); + assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); } diff --git a/test/cases/error.zig b/test/cases/error.zig index ced49419d5..693631fe2d 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -140,7 +140,7 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { if (x) |v| assert(v == 1234) else |err| @compileError("bad"); } -test "syntax: nullable operator in front of error union operator" { +test "syntax: optional operator in front of error union operator" { comptime { assert(?error!i32 == ?(error!i32)); } diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 9612466a86..08d3f3a841 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -12,7 +12,7 @@ fn fibonacci(x: i32) i32 { } fn unwrapAndAddOne(blah: ?i32) i32 { - return ??blah + 1; + return blah.? + 1; } const should_be_1235 = unwrapAndAddOne(1234); test "static add one" { diff --git a/test/cases/generics.zig b/test/cases/generics.zig index a76990e2a1..52aa013989 100644 --- a/test/cases/generics.zig +++ b/test/cases/generics.zig @@ -127,7 +127,7 @@ test "generic fn with implicit cast" { }) == 0); } fn getByte(ptr: ?*const u8) u8 { - return (??ptr).*; + return ptr.?.*; } fn getFirstByte(comptime T: type, mem: []const T) u8 { return getByte(@ptrCast(*const u8, &mem[0])); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 369d8e5cf3..beb0d6d456 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -505,7 +505,7 @@ test "@typeId" { assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); assert(@typeId(@typeOf(undefined)) == Tid.Undefined); assert(@typeId(@typeOf(null)) == Tid.Null); - assert(@typeId(?i32) == Tid.Nullable); + assert(@typeId(?i32) == Tid.Optional); assert(@typeId(error!i32) == Tid.ErrorUnion); assert(@typeId(error) == Tid.ErrorSet); assert(@typeId(AnEnum) == Tid.Enum); diff --git a/test/cases/null.zig b/test/cases/null.zig index bd78990ff4..62565784ac 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -1,6 +1,6 @@ const assert = @import("std").debug.assert; -test "nullable type" { +test "optional type" { const x: ?bool = true; if (x) |y| { @@ -33,7 +33,7 @@ test "test maybe object and get a pointer to the inner value" { b.* = false; } - assert(??maybe_bool == false); + assert(maybe_bool.? == false); } test "rhs maybe unwrap return" { @@ -47,9 +47,9 @@ test "maybe return" { } fn maybeReturnImpl() void { - assert(??foo(1235)); + assert(foo(1235).?); if (foo(null) != null) unreachable; - assert(!??foo(1234)); + assert(!foo(1234).?); } fn foo(x: ?i32) ?bool { @@ -102,12 +102,12 @@ fn testTestNullRuntime(x: ?i32) void { assert(!(x != null)); } -test "nullable void" { - nullableVoidImpl(); - comptime nullableVoidImpl(); +test "optional void" { + optionalVoidImpl(); + comptime optionalVoidImpl(); } -fn nullableVoidImpl() void { +fn optionalVoidImpl() void { assert(bar(null) == null); assert(bar({}) != null); } @@ -120,19 +120,19 @@ fn bar(x: ?void) ?void { } } -const StructWithNullable = struct { +const StructWithOptional = struct { field: ?i32, }; -var struct_with_nullable: StructWithNullable = undefined; +var struct_with_optional: StructWithOptional = undefined; -test "unwrap nullable which is field of global var" { - struct_with_nullable.field = null; - if (struct_with_nullable.field) |payload| { +test "unwrap optional which is field of global var" { + struct_with_optional.field = null; + if (struct_with_optional.field) |payload| { unreachable; } - struct_with_nullable.field = 1234; - if (struct_with_nullable.field) |payload| { + struct_with_optional.field = 1234; + if (struct_with_optional.field) |payload| { assert(payload == 1234); } else { unreachable; diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index 48fcc9ef03..3d3af3c889 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -2,7 +2,7 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; const reflection = this; -test "reflection: array, pointer, nullable, error union type child" { +test "reflection: array, pointer, optional, error union type child" { comptime { assert(([10]u8).Child == u8); assert((*u8).Child == u8); diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index b452c8e9f6..1bc58b14e1 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -88,15 +88,15 @@ fn testArray() void { assert(arr_info.Array.child == bool); } -test "type info: nullable type info" { - testNullable(); - comptime testNullable(); +test "type info: optional type info" { + testOptional(); + comptime testOptional(); } -fn testNullable() void { +fn testOptional() void { const null_info = @typeInfo(?void); - assert(TypeId(null_info) == TypeId.Nullable); - assert(null_info.Nullable.child == void); + assert(TypeId(null_info) == TypeId.Optional); + assert(null_info.Optional.child == void); } test "type info: promise info" { @@ -168,7 +168,7 @@ fn testUnion() void { assert(typeinfo_info.Union.tag_type == TypeId); assert(typeinfo_info.Union.fields.len == 25); assert(typeinfo_info.Union.fields[4].enum_field != null); - assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); + assert(typeinfo_info.Union.fields[4].enum_field.?.value == 4); assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); assert(typeinfo_info.Union.defs.len == 20); diff --git a/test/cases/while.zig b/test/cases/while.zig index a95481668d..fe53522ea6 100644 --- a/test/cases/while.zig +++ b/test/cases/while.zig @@ -81,7 +81,7 @@ test "while with else" { assert(got_else == 1); } -test "while with nullable as condition" { +test "while with optional as condition" { numbers_left = 10; var sum: i32 = 0; while (getNumberOrNull()) |value| { @@ -90,7 +90,7 @@ test "while with nullable as condition" { assert(sum == 45); } -test "while with nullable as condition with else" { +test "while with optional as condition with else" { numbers_left = 10; var sum: i32 = 0; var got_else: i32 = 0; @@ -132,7 +132,7 @@ fn getNumberOrNull() ?i32 { }; } -test "while on nullable with else result follow else prong" { +test "while on optional with else result follow else prong" { const result = while (returnNull()) |value| { break value; } else @@ -140,8 +140,8 @@ test "while on nullable with else result follow else prong" { assert(result == 2); } -test "while on nullable with else result follow break prong" { - const result = while (returnMaybe(10)) |value| { +test "while on optional with else result follow break prong" { + const result = while (returnOptional(10)) |value| { break value; } else i32(2); @@ -210,7 +210,7 @@ fn testContinueOuter() void { fn returnNull() ?i32 { return null; } -fn returnMaybe(x: i32) ?i32 { +fn returnOptional(x: i32) ?i32 { return x; } fn returnError() error!i32 { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 102c4e428d..1c737a59e7 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1341,7 +1341,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ if (true) |x| { } \\} , - ".tmp_source.zig:2:9: error: expected nullable type, found 'bool'", + ".tmp_source.zig:2:9: error: expected optional type, found 'bool'", ); cases.add( @@ -1780,7 +1780,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "assign null to non-nullable pointer", + "assign null to non-optional pointer", \\const a: *u8 = null; \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } @@ -2817,7 +2817,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "while expected bool, got nullable", + "while expected bool, got optional", \\export fn foo() void { \\ while (bar()) {} \\} @@ -2837,23 +2837,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "while expected nullable, got bool", + "while expected optional, got bool", \\export fn foo() void { \\ while (bar()) |x| {} \\} \\fn bar() bool { return true; } , - ".tmp_source.zig:2:15: error: expected nullable type, found 'bool'", + ".tmp_source.zig:2:15: error: expected optional type, found 'bool'", ); cases.add( - "while expected nullable, got error union", + "while expected optional, got error union", \\export fn foo() void { \\ while (bar()) |x| {} \\} \\fn bar() error!i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected nullable type, found 'error!i32'", + ".tmp_source.zig:2:15: error: expected optional type, found 'error!i32'", ); cases.add( @@ -2867,7 +2867,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "while expected error union, got nullable", + "while expected error union, got optional", \\export fn foo() void { \\ while (bar()) |x| {} else |err| {} \\} diff --git a/test/tests.zig b/test/tests.zig index cc562331fe..b66441f628 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -282,8 +282,8 @@ pub const CompareOutputContext = struct { var stdout = Buffer.initNull(b.allocator); var stderr = Buffer.initNull(b.allocator); - var stdout_file_in_stream = io.FileInStream.init(&??child.stdout); - var stderr_file_in_stream = io.FileInStream.init(&??child.stderr); + var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?); + var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?); stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable; stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable; @@ -601,8 +601,8 @@ pub const CompileErrorContext = struct { var stdout_buf = Buffer.initNull(b.allocator); var stderr_buf = Buffer.initNull(b.allocator); - var stdout_file_in_stream = io.FileInStream.init(&??child.stdout); - var stderr_file_in_stream = io.FileInStream.init(&??child.stderr); + var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?); + var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?); stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable; stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; @@ -872,8 +872,8 @@ pub const TranslateCContext = struct { var stdout_buf = Buffer.initNull(b.allocator); var stderr_buf = Buffer.initNull(b.allocator); - var stdout_file_in_stream = io.FileInStream.init(&??child.stdout); - var stderr_file_in_stream = io.FileInStream.init(&??child.stderr); + var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?); + var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?); stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable; stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; -- cgit v1.2.3 From 77678b2cbc7ac9ba2d5d4725241f6a9f7ac64fa4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Jun 2018 01:13:51 -0400 Subject: breaking syntax change: orelse keyword instead of ?? (#1096) use the `zig-fmt-optional-default` branch to have zig fmt automatically do the changes. closes #1023 --- build.zig | 6 +++--- doc/docgen.zig | 6 +++--- doc/langref.html.in | 16 +++++++-------- src-self-hosted/main.zig | 14 ++++++------- src-self-hosted/module.zig | 8 ++++---- src/all_types.hpp | 7 ++++++- src/analyze.cpp | 1 + src/ast_render.cpp | 12 +++++++++-- src/ir.cpp | 31 ++++++++++++----------------- src/parser.cpp | 13 ++++++------ src/tokenizer.cpp | 27 ++++++------------------- src/tokenizer.hpp | 2 +- src/translate_c.cpp | 16 ++++++++------- std/atomic/queue.zig | 4 ++-- std/atomic/stack.zig | 4 ++-- std/buf_map.zig | 6 +++--- std/buf_set.zig | 4 ++-- std/build.zig | 24 +++++++++++----------- std/debug/index.zig | 20 +++++++++---------- std/heap.zig | 10 +++++----- std/linked_list.zig | 4 ++-- std/os/index.zig | 14 ++++++------- std/os/linux/vdso.zig | 8 ++++---- std/os/path.zig | 12 +++++------ std/os/windows/util.zig | 2 +- std/special/build_runner.zig | 10 +++++----- std/unicode.zig | 2 +- std/zig/parse.zig | 47 ++++++++++++++++++++++---------------------- std/zig/render.zig | 8 ++++---- test/cases/cast.zig | 6 +++--- test/cases/null.zig | 10 +++++----- test/compile_errors.zig | 2 +- test/translate_c.zig | 20 +++++++++---------- 33 files changed, 187 insertions(+), 189 deletions(-) diff --git a/build.zig b/build.zig index eada37816c..fd154c7504 100644 --- a/build.zig +++ b/build.zig @@ -102,11 +102,11 @@ pub fn build(b: *Builder) !void { b.default_step.dependOn(&exe.step); - const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") ?? false; + const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false; if (!skip_self_hosted) { test_step.dependOn(&exe.step); } - const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") ?? false; + const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") orelse false; exe.setVerboseLink(verbose_link_exe); b.installArtifact(exe); @@ -114,7 +114,7 @@ pub fn build(b: *Builder) !void { installCHeaders(b, c_header_files); const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); - const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") ?? false; + const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") orelse false; test_step.dependOn(docs_step); diff --git a/doc/docgen.zig b/doc/docgen.zig index ed0e1be273..3283d146b0 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -25,13 +25,13 @@ pub fn main() !void { if (!args_it.skip()) @panic("expected self arg"); - const zig_exe = try (args_it.next(allocator) ?? @panic("expected zig exe arg")); + const zig_exe = try (args_it.next(allocator) orelse @panic("expected zig exe arg")); defer allocator.free(zig_exe); - const in_file_name = try (args_it.next(allocator) ?? @panic("expected input arg")); + const in_file_name = try (args_it.next(allocator) orelse @panic("expected input arg")); defer allocator.free(in_file_name); - const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg")); + const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg")); defer allocator.free(out_file_name); var in_file = try os.File.openRead(allocator, in_file_name); diff --git a/doc/langref.html.in b/doc/langref.html.in index 4c4a637095..0ada8a5196 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -985,7 +985,7 @@ a ^= b
-
a ?? b
+
a orelse b
+ {#header_open|catch#}

If you want to provide a default value, you can use the catch binary operator:

{#code_begin|syntax#} fn doAThing(str: []u8) void { @@ -3011,6 +3014,8 @@ fn doAThing(str: []u8) void { a default value of 13. The type of the right hand side of the binary catch operator must match the unwrapped error union type, or be of type noreturn.

+ {#header_close#} + {#header_open|try#}

Let's say you wanted to return the error if you got one, otherwise continue with the function logic:

{#code_begin|syntax#} @@ -3033,6 +3038,7 @@ fn doAThing(str: []u8) !void { from the current function with the same error. Otherwise, the expression results in the unwrapped value.

+ {#header_close#}

Maybe you know with complete certainty that an expression will never be an error. In this case you can do this: @@ -3047,7 +3053,7 @@ fn doAThing(str: []u8) !void {

Finally, you may want to take a different action for every situation. For that, we combine - the if and switch expression: + the {#link|if#} and {#link|switch#} expression:

{#code_begin|syntax#} fn doAThing(str: []u8) void { @@ -3062,9 +3068,10 @@ fn doAThing(str: []u8) void { } } {#code_end#} + {#header_open|errdefer#}

The other component to error handling is defer statements. - In addition to an unconditional defer, Zig has errdefer, + In addition to an unconditional {#link|defer#}, Zig has errdefer, which evaluates the deferred expression on block exit path if and only if the function returned with an error from the block.

@@ -3095,6 +3102,7 @@ fn createFoo(param: i32) !Foo { the verbosity and cognitive overhead of trying to make sure every exit path is covered. The deallocation code is always directly following the allocation code.

+ {#header_close#}

A couple of other tidbits about error handling:

@@ -3223,7 +3231,174 @@ test "inferred error set" { {#header_close#} {#header_close#} {#header_open|Error Return Traces#} -

TODO

+

+ Error Return Traces show all the points in the code that an error was returned to the calling function. This makes it practical to use {#link|try#} everywhere and then still be able to know what happened if an error ends up bubbling all the way out of your application. +

+ {#code_begin|exe_err#} +pub fn main() !void { + try foo(12); +} + +fn foo(x: i32) !void { + if (x >= 5) { + try bar(); + } else { + try bang2(); + } +} + +fn bar() !void { + if (baz()) { + try quux(); + } else |err| switch (err) { + error.FileNotFound => try hello(), + else => try another(), + } +} + +fn baz() !void { + try bang1(); +} + +fn quux() !void { + try bang2(); +} + +fn hello() !void { + try bang2(); +} + +fn another() !void { + try bang1(); +} + +fn bang1() !void { + return error.FileNotFound; +} + +fn bang2() !void { + return error.PermissionDenied; +} + {#code_end#} +

+ Look closely at this example. This is no stack trace. +

+

+ You can see that the final error bubbled up was PermissionDenied, + but the original error that started this whole thing was FileNotFound. In the bar function, the code handles the original error code, + and then returns another one, from the switch statement. Error Return Traces make this clear, whereas a stack trace would look like this: +

+ {#code_begin|exe_err#} +pub fn main() void { + foo(12); +} + +fn foo(x: i32) void { + if (x >= 5) { + bar(); + } else { + bang2(); + } +} + +fn bar() void { + if (baz()) { + quux(); + } else { + hello(); + } +} + +fn baz() bool { + return bang1(); +} + +fn quux() void { + bang2(); +} + +fn hello() void { + bang2(); +} + +fn bang1() bool { + return false; +} + +fn bang2() void { + @panic("PermissionDenied"); +} + {#code_end#} +

+ Here, the stack trace does not explain how the control + flow in bar got to the hello() call. + One would have to open a debugger or further instrument the application + in order to find out. The error return trace, on the other hand, + shows exactly how the error bubbled up. +

+

+ This debugging feature makes it easier to iterate quickly on code that + robustly handles all error conditions. This means that Zig developers + will naturally find themselves writing correct, robust code in order + to increase their development pace. +

+

+ Error Return Traces are enabled by default in {#link|Debug#} and {#link|ReleaseSafe#} builds and disabled by default in {#link|ReleaseFast#} and {#link|ReleaseSmall#} builds. +

+

+ There are a few ways to activate this error return tracing feature: +

+
    +
  • Return an error from main
  • +
  • An error makes its way to catch unreachable and you have not overridden the default panic handler
  • +
  • Use {#link|errorReturnTrace#} to access the current return trace. You can use std.debug.dumpStackTrace to print it. This function returns comptime-known {#link|null#} when building without error return tracing support.
  • +
+ {#header_open|Implementation Details#} +

+ To analyze performance cost, there are two cases: +

+
    +
  • when no errors are returned
  • +
  • when returning errors
  • +
+

+ For the case when no errors are returned, the cost is a single memory write operation, only in the first non-failable function in the call graph that calls a failable function, i.e. when a function returning void calls a function returning error. + This is to initialize this struct in the stack memory: +

+ {#code_begin|syntax#} +pub const StackTrace = struct { + index: usize, + instruction_addresses: [N]usize, +}; + {#code_end#} +

+ Here, N is the maximum function call depth as determined by call graph analysis. Recursion is ignored and counts for 2. +

+

+ A pointer to StackTrace is passed as a secret parameter to every function that can return an error, but it's always the first parameter, so it can likely sit in a register and stay there. +

+

+ That's it for the path when no errors occur. It's practically free in terms of performance. +

+

+ When generating the code for a function that returns an error, just before the return statement (only for the return statements that return errors), Zig generates a call to this function: +

+ {#code_begin|syntax#} +// marked as "no-inline" in LLVM IR +fn __zig_return_error(stack_trace: *StackTrace) void { + stack_trace.instruction_addresses[stack_trace.index] = @returnAddress(); + stack_trace.index = (stack_trace.index + 1) % N; +} + {#code_end#} +

+ The cost is 2 math operations plus some memory reads and writes. The memory accessed is constrained and should remain cached for the duration of the error return bubbling. +

+

+ As for code size cost, 1 function call before a return statement is no big deal. Even so, + I have a plan to make the call to + __zig_return_error a tail call, which brings the code size cost down to actually zero. What is a return statement in code without error return tracing can become a jump instruction in code with error return tracing. +

+ {#header_close#} {#header_close#} {#header_close#} {#header_open|Optionals#} @@ -3342,6 +3517,15 @@ test "optional type" { // Use compile-time reflection to access the child type of the optional: comptime assert(@typeOf(foo).Child == i32); } + {#code_end#} + {#header_close#} + {#header_open|null#} +

+ Just like {#link|undefined#}, null has its own type, and the only way to use it is to + cast it to a different type: +

+ {#code_begin|syntax#} +const optional_value: ?i32 = null; {#code_end#} {#header_close#} {#header_close#} @@ -5426,12 +5610,13 @@ pub const TypeInfo = union(TypeId) { {#header_close#} {#header_open|Build Mode#}

- Zig has three build modes: + Zig has four build modes:

  • {#link|Debug#} (default)
  • {#link|ReleaseFast#}
  • {#link|ReleaseSafe#}
  • +
  • {#link|ReleaseSmall#}

To add standard build options to a build.zig file: @@ -5448,14 +5633,16 @@ pub fn build(b: &Builder) void {

This causes these options to be available:

-
  -Drelease-safe=(bool)  optimizations on and safety on
-  -Drelease-fast=(bool)  optimizations on and safety off
+
  -Drelease-safe=[bool] optimizations on and safety on
+  -Drelease-fast=[bool] optimizations on and safety off
+  -Drelease-small=[bool] size optimizations on and safety off
{#header_open|Debug#}
$ zig build-exe example.zig
  • Fast compilation speed
  • Safety checks enabled
  • Slow runtime performance
  • +
  • Large binary size
{#header_close#} {#header_open|ReleaseFast#} @@ -5464,6 +5651,7 @@ pub fn build(b: &Builder) void {
  • Fast runtime performance
  • Safety checks disabled
  • Slow compilation speed
  • +
  • Large binary size
  • {#header_close#} {#header_open|ReleaseSafe#} @@ -5472,9 +5660,19 @@ pub fn build(b: &Builder) void {
  • Medium runtime performance
  • Safety checks enabled
  • Slow compilation speed
  • +
  • Large binary size
  • - {#see_also|Compile Variables|Zig Build System|Undefined Behavior#} {#header_close#} + {#header_open|ReleaseSmall#} +
    $ zig build-exe example.zig --release-small
    +
      +
    • Medium runtime performance
    • +
    • Safety checks disabled
    • +
    • Slow compilation speed
    • +
    • Small binary size
    • +
    + {#header_close#} + {#see_also|Compile Variables|Zig Build System|Undefined Behavior#} {#header_close#} {#header_open|Undefined Behavior#}

    @@ -5482,7 +5680,7 @@ pub fn build(b: &Builder) void { detected at compile-time, Zig emits an error. Most undefined behavior that cannot be detected at compile-time can be detected at runtime. In these cases, Zig has safety checks. Safety checks can be disabled on a per-block basis - with @setRuntimeSafety. The {#link|ReleaseFast#} + with {#link|setRuntimeSafety#}. The {#link|ReleaseFast#} build mode disables all safety checks in order to facilitate optimizations.

    -- cgit v1.2.3 From b3a3e2094e8b88e40eecf65d29f39af10442bde4 Mon Sep 17 00:00:00 2001 From: Jay Weisskopf Date: Fri, 15 Jun 2018 14:06:56 -0400 Subject: Make `zig version` compliant with SemVer (#1113) The git revision is build metadata and should be appended with a plus sign. https://semver.org/#spec-item-10 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cfa0146bb1..dd4770ad72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ if(GIT_EXE) message("WARNING: Tag does not match configured Zig version") endif() else() - set(ZIG_VERSION "${ZIG_VERSION_MAJOR}.${ZIG_VERSION_MINOR}.${ZIG_VERSION_PATCH}.${ZIG_GIT_REV}") + set(ZIG_VERSION "${ZIG_VERSION}+${ZIG_GIT_REV}") endif() endif() message("Configuring zig version ${ZIG_VERSION}") -- cgit v1.2.3 From 48de57d8248d9203b44d28d7749b5d7c1a00deba Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jun 2018 17:01:23 -0400 Subject: add basic std lib code for loading dynamic libraries this is going to only work for very basic libraries; I plan to slowly add more features over time to support more complicated libraries --- CMakeLists.txt | 1 + src/codegen.cpp | 12 +- src/link.cpp | 4 +- std/dynamic_library.zig | 161 +++++++++++++++++++++++++ std/elf.zig | 15 +++ std/index.zig | 1 + std/io.zig | 7 +- std/math/index.zig | 11 ++ std/os/file.zig | 11 +- std/os/index.zig | 14 +++ test/build_examples.zig | 5 + test/standalone/load_dynamic_library/add.zig | 3 + test/standalone/load_dynamic_library/build.zig | 22 ++++ test/standalone/load_dynamic_library/main.zig | 17 +++ 14 files changed, 265 insertions(+), 19 deletions(-) create mode 100644 std/dynamic_library.zig create mode 100644 test/standalone/load_dynamic_library/add.zig create mode 100644 test/standalone/load_dynamic_library/build.zig create mode 100644 test/standalone/load_dynamic_library/main.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index dd4770ad72..e502901bd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -438,6 +438,7 @@ set(ZIG_STD_FILES "debug/failing_allocator.zig" "debug/index.zig" "dwarf.zig" + "dynamic_library.zig" "elf.zig" "empty.zig" "event.zig" diff --git a/src/codegen.cpp b/src/codegen.cpp index d05bcba2ce..fedfcfa744 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6768,7 +6768,7 @@ static void define_builtin_compile_vars(CodeGen *g) { int err; Buf *abs_full_path = buf_alloc(); if ((err = os_path_real(builtin_zig_path, abs_full_path))) { - fprintf(stderr, "unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err)); exit(1); } @@ -6936,11 +6936,11 @@ static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package Buf *abs_full_path = buf_alloc(); int err; if ((err = os_path_real(&path_to_code_src, abs_full_path))) { - zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err)); + zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err)); } Buf *import_code = buf_alloc(); if ((err = os_fetch_file_path(abs_full_path, import_code, false))) { - zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err)); + zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err)); } return add_source_file(g, package, abs_full_path, import_code); @@ -7024,13 +7024,13 @@ static void gen_root_source(CodeGen *g) { Buf *abs_full_path = buf_alloc(); int err; if ((err = os_path_real(rel_full_path, abs_full_path))) { - fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(rel_full_path), err_str(err)); exit(1); } Buf *source_code = buf_alloc(); if ((err = os_fetch_file_path(rel_full_path, source_code, true))) { - fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(rel_full_path), err_str(err)); exit(1); } @@ -7374,7 +7374,7 @@ static void gen_h_file(CodeGen *g) { FILE *out_h = fopen(buf_ptr(g->out_h_path), "wb"); if (!out_h) - zig_panic("unable to open %s: %s", buf_ptr(g->out_h_path), strerror(errno)); + zig_panic("unable to open %s: %s\n", buf_ptr(g->out_h_path), strerror(errno)); Buf *export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name))); buf_upcase(export_macro); diff --git a/src/link.cpp b/src/link.cpp index d2925cb5a8..a4631b1daf 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -208,7 +208,7 @@ static Buf *get_dynamic_linker_path(CodeGen *g) { static void construct_linker_job_elf(LinkJob *lj) { CodeGen *g = lj->codegen; - if (lj->link_in_crt) { + if (g->libc_link_lib != nullptr) { find_libc_lib_path(g); } @@ -432,7 +432,7 @@ static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, si static void construct_linker_job_coff(LinkJob *lj) { CodeGen *g = lj->codegen; - if (lj->link_in_crt) { + if (g->libc_link_lib != nullptr) { find_libc_lib_path(g); } diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig new file mode 100644 index 0000000000..87b58ec207 --- /dev/null +++ b/std/dynamic_library.zig @@ -0,0 +1,161 @@ +const std = @import("index.zig"); +const mem = std.mem; +const elf = std.elf; +const cstr = std.cstr; +const linux = std.os.linux; + +pub const DynLib = struct { + allocator: *mem.Allocator, + elf_lib: ElfLib, + fd: i32, + map_addr: usize, + map_size: usize, + + /// Trusts the file + pub fn findAndOpen(allocator: *mem.Allocator, name: []const u8) !DynLib { + return open(allocator, name); + } + + /// Trusts the file + pub fn open(allocator: *mem.Allocator, path: []const u8) !DynLib { + const fd = try std.os.posixOpen(allocator, path, 0, linux.O_RDONLY); + errdefer std.os.close(fd); + + const size = usize((try std.os.posixFStat(fd)).size); + + const addr = linux.mmap( + null, + size, + linux.PROT_READ | linux.PROT_EXEC, + linux.MAP_PRIVATE | linux.MAP_LOCKED, + fd, + 0, + ); + errdefer _ = linux.munmap(addr, size); + + const bytes = @intToPtr([*]align(std.os.page_size) u8, addr)[0..size]; + + return DynLib{ + .allocator = allocator, + .elf_lib = try ElfLib.init(bytes), + .fd = fd, + .map_addr = addr, + .map_size = size, + }; + } + + pub fn close(self: *DynLib) void { + _ = linux.munmap(self.map_addr, self.map_size); + std.os.close(self.fd); + self.* = undefined; + } + + pub fn lookup(self: *DynLib, name: []const u8) ?usize { + return self.elf_lib.lookup("", name); + } +}; + +pub const ElfLib = struct { + strings: [*]u8, + syms: [*]elf.Sym, + hashtab: [*]linux.Elf_Symndx, + versym: ?[*]u16, + verdef: ?*elf.Verdef, + base: usize, + + // Trusts the memory + pub fn init(bytes: []align(@alignOf(elf.Ehdr)) u8) !ElfLib { + const eh = @ptrCast(*elf.Ehdr, bytes.ptr); + if (!mem.eql(u8, eh.e_ident[0..4], "\x7fELF")) return error.NotElfFile; + if (eh.e_type != elf.ET_DYN) return error.NotDynamicLibrary; + + const elf_addr = @ptrToInt(bytes.ptr); + var ph_addr: usize = elf_addr + eh.e_phoff; + + var base: usize = @maxValue(usize); + var maybe_dynv: ?[*]usize = null; + { + var i: usize = 0; + while (i < eh.e_phnum) : ({ + i += 1; + ph_addr += eh.e_phentsize; + }) { + const ph = @intToPtr(*elf.Phdr, ph_addr); + switch (ph.p_type) { + elf.PT_LOAD => base = elf_addr + ph.p_offset - ph.p_vaddr, + elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, elf_addr + ph.p_offset), + else => {}, + } + } + } + const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation; + if (base == @maxValue(usize)) return error.BaseNotFound; + + var maybe_strings: ?[*]u8 = null; + var maybe_syms: ?[*]elf.Sym = null; + var maybe_hashtab: ?[*]linux.Elf_Symndx = null; + var maybe_versym: ?[*]u16 = null; + var maybe_verdef: ?*elf.Verdef = null; + + { + var i: usize = 0; + while (dynv[i] != 0) : (i += 2) { + const p = base + dynv[i + 1]; + switch (dynv[i]) { + elf.DT_STRTAB => maybe_strings = @intToPtr([*]u8, p), + elf.DT_SYMTAB => maybe_syms = @intToPtr([*]elf.Sym, p), + elf.DT_HASH => maybe_hashtab = @intToPtr([*]linux.Elf_Symndx, p), + elf.DT_VERSYM => maybe_versym = @intToPtr([*]u16, p), + elf.DT_VERDEF => maybe_verdef = @intToPtr(*elf.Verdef, p), + else => {}, + } + } + } + + return ElfLib{ + .base = base, + .strings = maybe_strings orelse return error.ElfStringSectionNotFound, + .syms = maybe_syms orelse return error.ElfSymSectionNotFound, + .hashtab = maybe_hashtab orelse return error.ElfHashTableNotFound, + .versym = maybe_versym, + .verdef = maybe_verdef, + }; + } + + /// Returns the address of the symbol + pub fn lookup(self: *const ElfLib, vername: []const u8, name: []const u8) ?usize { + const maybe_versym = if (self.verdef == null) null else self.versym; + + const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON); + const OK_BINDS = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE); + + var i: usize = 0; + while (i < self.hashtab[1]) : (i += 1) { + if (0 == (u32(1) << u5(self.syms[i].st_info & 0xf) & OK_TYPES)) continue; + if (0 == (u32(1) << u5(self.syms[i].st_info >> 4) & OK_BINDS)) continue; + if (0 == self.syms[i].st_shndx) continue; + if (!mem.eql(u8, name, cstr.toSliceConst(self.strings + self.syms[i].st_name))) continue; + if (maybe_versym) |versym| { + if (!checkver(self.verdef.?, versym[i], vername, self.strings)) + continue; + } + return self.base + self.syms[i].st_value; + } + + return null; + } +}; + +fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*]u8) bool { + var def = def_arg; + const vsym = @bitCast(u32, vsym_arg) & 0x7fff; + while (true) { + if (0 == (def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym) + break; + if (def.vd_next == 0) + return false; + def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next); + } + const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux); + return mem.eql(u8, vername, cstr.toSliceConst(strings + aux.vda_name)); +} diff --git a/std/elf.zig b/std/elf.zig index 50e97ab271..8e6445c631 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -305,6 +305,21 @@ pub const STT_ARM_16BIT = STT_HIPROC; pub const VER_FLG_BASE = 0x1; pub const VER_FLG_WEAK = 0x2; +/// An unknown type. +pub const ET_NONE = 0; + +/// A relocatable file. +pub const ET_REL = 1; + +/// An executable file. +pub const ET_EXEC = 2; + +/// A shared object. +pub const ET_DYN = 3; + +/// A core file. +pub const ET_CORE = 4; + pub const FileType = enum { Relocatable, Executable, diff --git a/std/index.zig b/std/index.zig index 8abfa3db88..3b523f519f 100644 --- a/std/index.zig +++ b/std/index.zig @@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap; pub const LinkedList = @import("linked_list.zig").LinkedList; pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList; pub const SegmentedList = @import("segmented_list.zig").SegmentedList; +pub const DynLib = @import("dynamic_library.zig").DynLib; pub const atomic = @import("atomic/index.zig"); pub const base64 = @import("base64.zig"); diff --git a/std/io.zig b/std/io.zig index a603d0cf5e..cfe1a7f585 100644 --- a/std/io.zig +++ b/std/io.zig @@ -242,11 +242,16 @@ pub fn writeFile(allocator: *mem.Allocator, path: []const u8, data: []const u8) /// On success, caller owns returned buffer. pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { + return readFileAllocAligned(allocator, path, @alignOf(u8)); +} + +/// On success, caller owns returned buffer. +pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 { var file = try File.openRead(allocator, path); defer file.close(); const size = try file.getEndPos(); - const buf = try allocator.alloc(u8, size); + const buf = try allocator.alignedAlloc(u8, A, size); errdefer allocator.free(buf); var adapter = FileInStream.init(&file); diff --git a/std/math/index.zig b/std/math/index.zig index cc1b833a37..8c1dcc32c4 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -536,6 +536,17 @@ test "math.cast" { assert(@typeOf(try cast(u8, u32(255))) == u8); } +pub const AlignCastError = error{UnalignedMemory}; + +/// Align cast a pointer but return an error if it's the wrong field +pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@typeOf(@alignCast(alignment, ptr)) { + const addr = @ptrToInt(ptr); + if (addr % alignment != 0) { + return error.UnalignedMemory; + } + return @alignCast(alignment, ptr); +} + pub fn floorPowerOfTwo(comptime T: type, value: T) T { var x = value; diff --git a/std/os/file.zig b/std/os/file.zig index 56da4f73a6..41d3dfbf95 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -265,16 +265,7 @@ pub const File = struct { pub fn getEndPos(self: *File) !usize { if (is_posix) { - var stat: posix.Stat = undefined; - const err = posix.getErrno(posix.fstat(self.handle, &stat)); - if (err > 0) { - return switch (err) { - posix.EBADF => error.BadFd, - posix.ENOMEM => error.SystemResources, - else => os.unexpectedErrorPosix(err), - }; - } - + const stat = try os.posixFStat(self.handle); return usize(stat.size); } else if (is_windows) { var file_size: windows.LARGE_INTEGER = undefined; diff --git a/std/os/index.zig b/std/os/index.zig index 62eeb7e43e..fb4605fce0 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2697,3 +2697,17 @@ pub fn posixWait(pid: i32) i32 { } } } + +pub fn posixFStat(fd: i32) !posix.Stat { + var stat: posix.Stat = undefined; + const err = posix.getErrno(posix.fstat(fd, &stat)); + if (err > 0) { + return switch (err) { + posix.EBADF => error.BadFd, + posix.ENOMEM => error.SystemResources, + else => os.unexpectedErrorPosix(err), + }; + } + + return stat; +} diff --git a/test/build_examples.zig b/test/build_examples.zig index 1ba0ca46cf..7cae734677 100644 --- a/test/build_examples.zig +++ b/test/build_examples.zig @@ -18,4 +18,9 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void { cases.addBuildFile("test/standalone/pkg_import/build.zig"); cases.addBuildFile("test/standalone/use_alias/build.zig"); cases.addBuildFile("test/standalone/brace_expansion/build.zig"); + if (builtin.os == builtin.Os.linux) { + // TODO hook up the DynLib API for windows using LoadLibraryA + // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it + cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); + } } diff --git a/test/standalone/load_dynamic_library/add.zig b/test/standalone/load_dynamic_library/add.zig new file mode 100644 index 0000000000..a04ec1544d --- /dev/null +++ b/test/standalone/load_dynamic_library/add.zig @@ -0,0 +1,3 @@ +export fn add(a: i32, b: i32) i32 { + return a + b; +} diff --git a/test/standalone/load_dynamic_library/build.zig b/test/standalone/load_dynamic_library/build.zig new file mode 100644 index 0000000000..1f981a5c7d --- /dev/null +++ b/test/standalone/load_dynamic_library/build.zig @@ -0,0 +1,22 @@ +const Builder = @import("std").build.Builder; + +pub fn build(b: *Builder) void { + const opts = b.standardReleaseOptions(); + + const lib = b.addSharedLibrary("add", "add.zig", b.version(1, 0, 0)); + lib.setBuildMode(opts); + lib.linkSystemLibrary("c"); + + const main = b.addExecutable("main", "main.zig"); + main.setBuildMode(opts); + + const run = b.addCommand(".", b.env_map, [][]const u8{ + main.getOutputPath(), + lib.getOutputPath(), + }); + run.step.dependOn(&lib.step); + run.step.dependOn(&main.step); + + const test_step = b.step("test", "Test the program"); + test_step.dependOn(&run.step); +} diff --git a/test/standalone/load_dynamic_library/main.zig b/test/standalone/load_dynamic_library/main.zig new file mode 100644 index 0000000000..4c45ad6fde --- /dev/null +++ b/test/standalone/load_dynamic_library/main.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn main() !void { + const args = try std.os.argsAlloc(std.debug.global_allocator); + defer std.os.argsFree(std.debug.global_allocator, args); + + const dynlib_name = args[1]; + + var lib = try std.DynLib.open(std.debug.global_allocator, dynlib_name); + defer lib.close(); + + const addr = lib.lookup("add") orelse return error.SymbolNotFound; + const addFn = @intToPtr(extern fn (i32, i32) i32, addr); + + const result = addFn(12, 34); + std.debug.assert(result == 46); +} -- cgit v1.2.3 From 65d04cbeb42318c66313346bb88999aee17f856f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jun 2018 17:27:45 -0400 Subject: std.DynLib: open the fd with CLOEXEC --- std/dynamic_library.zig | 7 +------ std/math/index.zig | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig index 87b58ec207..8fe5f7f818 100644 --- a/std/dynamic_library.zig +++ b/std/dynamic_library.zig @@ -11,14 +11,9 @@ pub const DynLib = struct { map_addr: usize, map_size: usize, - /// Trusts the file - pub fn findAndOpen(allocator: *mem.Allocator, name: []const u8) !DynLib { - return open(allocator, name); - } - /// Trusts the file pub fn open(allocator: *mem.Allocator, path: []const u8) !DynLib { - const fd = try std.os.posixOpen(allocator, path, 0, linux.O_RDONLY); + const fd = try std.os.posixOpen(allocator, path, 0, linux.O_RDONLY | linux.O_CLOEXEC); errdefer std.os.close(fd); const size = usize((try std.os.posixFStat(fd)).size); diff --git a/std/math/index.zig b/std/math/index.zig index 8c1dcc32c4..99e5b4ecf1 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -538,7 +538,7 @@ test "math.cast" { pub const AlignCastError = error{UnalignedMemory}; -/// Align cast a pointer but return an error if it's the wrong field +/// Align cast a pointer but return an error if it's the wrong alignment pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@typeOf(@alignCast(alignment, ptr)) { const addr = @ptrToInt(ptr); if (addr % alignment != 0) { -- cgit v1.2.3 From c529b814ee292250ad34b0f2b00f5bd5e6796597 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jun 2018 18:54:41 -0400 Subject: load_dynamic_library test: no need to link libc --- test/standalone/load_dynamic_library/build.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/test/standalone/load_dynamic_library/build.zig b/test/standalone/load_dynamic_library/build.zig index 1f981a5c7d..2d47a893f2 100644 --- a/test/standalone/load_dynamic_library/build.zig +++ b/test/standalone/load_dynamic_library/build.zig @@ -5,7 +5,6 @@ pub fn build(b: *Builder) void { const lib = b.addSharedLibrary("add", "add.zig", b.version(1, 0, 0)); lib.setBuildMode(opts); - lib.linkSystemLibrary("c"); const main = b.addExecutable("main", "main.zig"); main.setBuildMode(opts); -- cgit v1.2.3 From a7d59086b49b0ae11a4830d2ea72b63be05fab94 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jun 2018 19:36:06 -0400 Subject: disable load dynamic library test it's failing on CI. I will troubleshoot it and then re-enable --- test/build_examples.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/build_examples.zig b/test/build_examples.zig index 7cae734677..b48fcbb698 100644 --- a/test/build_examples.zig +++ b/test/build_examples.zig @@ -18,7 +18,9 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void { cases.addBuildFile("test/standalone/pkg_import/build.zig"); cases.addBuildFile("test/standalone/use_alias/build.zig"); cases.addBuildFile("test/standalone/brace_expansion/build.zig"); - if (builtin.os == builtin.Os.linux) { + if (false) { + // TODO this test is disabled because it is failing on the CI server's linux. when this is fixed + // enable it for at least linux // TODO hook up the DynLib API for windows using LoadLibraryA // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); -- cgit v1.2.3 From 59b3dc8907f76b93caa689732e878a5bfa2f65c2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 13 Jun 2018 22:40:38 -0400 Subject: allow passing by non-copying value closes #733 --- doc/langref.html.in | 37 ++++++++++++++----------------------- src/analyze.cpp | 11 ++++------- test/cases/fn.zig | 13 +++++++++++++ test/compile_errors.zig | 23 ----------------------- 4 files changed, 31 insertions(+), 53 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 814de721a6..b32c8165e2 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2797,39 +2797,30 @@ fn foo() void { } {#code_end#} {#header_open|Pass-by-value Parameters#}

    - In Zig, structs, unions, and enums with payloads cannot be passed by value - to a function. + In Zig, structs, unions, and enums with payloads can be passed directly to a function:

    - {#code_begin|test_err|not copyable; cannot pass by value#} -const Foo = struct { + {#code_begin|test#} +const Point = struct { x: i32, + y: i32, }; -fn bar(foo: Foo) void {} - -test "pass aggregate type by value to function" { - bar(Foo {.x = 12,}); +fn foo(point: Point) i32 { + return point.x + point.y; } - {#code_end#} -

    - Instead, one must use *const. Zig allows implicitly casting something - to a const pointer to it: -

    - {#code_begin|test#} -const Foo = struct { - x: i32, -}; -fn bar(foo: *const Foo) void {} +const assert = @import("std").debug.assert; -test "implicitly cast to const pointer" { - bar(Foo {.x = 12,}); +test "pass aggregate type by non-copy value to function" { + assert(foo(Point{ .x = 1, .y = 2 }) == 3); } {#code_end#}

    - However, - the C ABI does allow passing structs and unions by value. So functions which - use the C calling convention may pass structs and unions by value. + In this case, the value may be passed by reference, or by value, whichever way + Zig decides will be faster. +

    +

    + For extern functions, Zig follows the C ABI for passing structs and unions by value.

    {#header_close#} {#header_open|Function Reflection#} diff --git a/src/analyze.cpp b/src/analyze.cpp index cbeac7bc21..758bc1a045 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1135,7 +1135,10 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { gen_param_info->src_index = i; gen_param_info->gen_index = SIZE_MAX; - type_ensure_zero_bits_known(g, type_entry); + ensure_complete_type(g, type_entry); + if (type_is_invalid(type_entry)) + return g->builtin_types.entry_invalid; + if (type_has_bits(type_entry)) { TypeTableEntry *gen_type; if (handle_is_ptr(type_entry)) { @@ -1546,12 +1549,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdPromise: - ensure_complete_type(g, type_entry); - if (calling_convention_allows_zig_types(fn_type_id.cc) && !type_is_copyable(g, type_entry)) { - add_node_error(g, param_node->data.param_decl.type, - buf_sprintf("type '%s' is not copyable; cannot pass by value", buf_ptr(&type_entry->name))); - return g->builtin_types.entry_invalid; - } break; } FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; diff --git a/test/cases/fn.zig b/test/cases/fn.zig index dfb254c6aa..2426a411df 100644 --- a/test/cases/fn.zig +++ b/test/cases/fn.zig @@ -119,3 +119,16 @@ test "assign inline fn to const variable" { } inline fn inlineFn() void {} + +test "pass by non-copying value" { + assert(bar(Point{ .x = 1, .y = 2 }) == 3); +} + +const Point = struct { + x: i32, + y: i32, +}; + +fn bar(pt: Point) i32 { + return pt.x + pt.y; +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 06f17a37ee..60ba255172 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2573,15 +2573,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { break :x tc; }); - cases.add( - "pass non-copyable type by value to function", - \\const Point = struct { x: i32, y: i32, }; - \\fn foo(p: Point) void { } - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , - ".tmp_source.zig:2:11: error: type 'Point' is not copyable; cannot pass by value", - ); - cases.add( "implicit cast from array to mutable slice", \\var global_array: [10]i32 = undefined; @@ -4066,20 +4057,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:5: note: field 'A' has type 'i32'", ); - cases.add( - "self-referencing function pointer field", - \\const S = struct { - \\ f: fn(_: S) void, - \\}; - \\fn f(_: S) void { - \\} - \\export fn entry() void { - \\ var _ = S { .f = f }; - \\} - , - ".tmp_source.zig:4:9: error: type 'S' is not copyable; cannot pass by value", - ); - cases.add( "taking offset of void field in struct", \\const Empty = struct { -- cgit v1.2.3 From e311cd562b47529bdcd2423658915539ddb6bc36 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 15 Jun 2018 13:49:39 -0400 Subject: don't automatically take pointer when passing by non-copying value this commit does not have all tests passing --- src/ir.cpp | 57 ++++---- std/array_list.zig | 26 ++-- std/build.zig | 2 +- std/fmt/index.zig | 8 +- std/json.zig | 2 +- std/math/big/int.zig | 346 ++++++++++++++++++++---------------------------- std/mem.zig | 10 +- test/cases/cast.zig | 8 -- test/cases/fn.zig | 48 ++++++- test/cases/var_args.zig | 12 -- test/compile_errors.zig | 2 +- 11 files changed, 249 insertions(+), 272 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index e5e8dcbb9d..d008ead113 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10463,13 +10463,6 @@ static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, Typ zig_unreachable(); } -static IrInstruction *ir_implicit_byval_const_ref_cast(IrAnalyze *ira, IrInstruction *inst) { - if (type_is_copyable(ira->codegen, inst->value.type)) - return inst; - TypeTableEntry *const_ref_type = get_pointer_to_type(ira->codegen, inst->value.type, true); - return ir_implicit_cast(ira, inst, const_ref_type); -} - static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) { TypeTableEntry *type_entry = ptr->value.type; if (type_is_invalid(type_entry)) { @@ -12283,7 +12276,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod IrInstruction *casted_arg; if (is_var_args) { arg_part_of_generic_id = true; - casted_arg = ir_implicit_byval_const_ref_cast(ira, arg); + casted_arg = arg; } else { if (param_decl_node->data.param_decl.var_token == nullptr) { AstNode *param_type_node = param_decl_node->data.param_decl.type; @@ -12296,7 +12289,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod return false; } else { arg_part_of_generic_id = true; - casted_arg = ir_implicit_byval_const_ref_cast(ira, arg); + casted_arg = arg; } } @@ -12515,9 +12508,18 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal size_t next_proto_i = 0; if (first_arg_ptr) { - IrInstruction *first_arg; assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer); - if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { + + bool first_arg_known_bare = false; + if (fn_type_id->next_param_index >= 1) { + TypeTableEntry *param_type = fn_type_id->param_info[next_proto_i].type; + if (type_is_invalid(param_type)) + return ira->codegen->builtin_types.entry_invalid; + first_arg_known_bare = param_type->id != TypeTableEntryIdPointer; + } + + IrInstruction *first_arg; + if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); @@ -12667,9 +12669,18 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal size_t next_proto_i = 0; if (first_arg_ptr) { - IrInstruction *first_arg; assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer); - if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { + + bool first_arg_known_bare = false; + if (fn_type_id->next_param_index >= 1) { + TypeTableEntry *param_type = fn_type_id->param_info[next_proto_i].type; + if (type_is_invalid(param_type)) + return ira->codegen->builtin_types.entry_invalid; + first_arg_known_bare = param_type->id != TypeTableEntryIdPointer; + } + + IrInstruction *first_arg; + if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); @@ -12802,10 +12813,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return ira->codegen->builtin_types.entry_invalid; } if (inst_fn_type_id.async_allocator_type == nullptr) { - IrInstruction *casted_inst = ir_implicit_byval_const_ref_cast(ira, uncasted_async_allocator_inst); - if (type_is_invalid(casted_inst->value.type)) - return ira->codegen->builtin_types.entry_invalid; - inst_fn_type_id.async_allocator_type = casted_inst->value.type; + inst_fn_type_id.async_allocator_type = uncasted_async_allocator_inst->value.type; } async_allocator_inst = ir_implicit_cast(ira, uncasted_async_allocator_inst, inst_fn_type_id.async_allocator_type); if (type_is_invalid(async_allocator_inst->value.type)) @@ -12866,9 +12874,16 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal IrInstruction **casted_args = allocate(call_param_count); size_t next_arg_index = 0; if (first_arg_ptr) { - IrInstruction *first_arg; assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer); - if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { + + TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type; + if (type_is_invalid(param_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *first_arg; + if (param_type->id == TypeTableEntryIdPointer && + handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) + { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); @@ -12876,10 +12891,6 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return ira->codegen->builtin_types.entry_invalid; } - TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type; - if (type_is_invalid(param_type)) - return ira->codegen->builtin_types.entry_invalid; - IrInstruction *casted_arg = ir_implicit_cast(ira, first_arg, param_type); if (type_is_invalid(casted_arg->value.type)) return ira->codegen->builtin_types.entry_invalid; diff --git a/std/array_list.zig b/std/array_list.zig index 1a235d28a3..fd1d5cbe26 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -29,36 +29,36 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { }; } - pub fn deinit(self: *const Self) void { + pub fn deinit(self: Self) void { self.allocator.free(self.items); } - pub fn toSlice(self: *const Self) []align(A) T { + pub fn toSlice(self: Self) []align(A) T { return self.items[0..self.len]; } - pub fn toSliceConst(self: *const Self) []align(A) const T { + pub fn toSliceConst(self: Self) []align(A) const T { return self.items[0..self.len]; } - pub fn at(self: *const Self, n: usize) T { + pub fn at(self: Self, n: usize) T { return self.toSliceConst()[n]; } /// Sets the value at index `i`, or returns `error.OutOfBounds` if /// the index is not in range. - pub fn setOrError(self: *const Self, i: usize, item: *const T) !void { + pub fn setOrError(self: Self, i: usize, item: T) !void { if (i >= self.len) return error.OutOfBounds; - self.items[i] = item.*; + self.items[i] = item; } /// Sets the value at index `i`, asserting that the value is in range. - pub fn set(self: *const Self, i: usize, item: *const T) void { + pub fn set(self: *Self, i: usize, item: T) void { assert(i < self.len); - self.items[i] = item.*; + self.items[i] = item; } - pub fn count(self: *const Self) usize { + pub fn count(self: Self) usize { return self.len; } @@ -81,12 +81,12 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return result; } - pub fn insert(self: *Self, n: usize, item: *const T) !void { + pub fn insert(self: *Self, n: usize, item: T) !void { try self.ensureCapacity(self.len + 1); self.len += 1; mem.copy(T, self.items[n + 1 .. self.len], self.items[n .. self.len - 1]); - self.items[n] = item.*; + self.items[n] = item; } pub fn insertSlice(self: *Self, n: usize, items: []align(A) const T) !void { @@ -97,9 +97,9 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { mem.copy(T, self.items[n .. n + items.len], items); } - pub fn append(self: *Self, item: *const T) !void { + pub fn append(self: *Self, item: T) !void { const new_item_ptr = try self.addOne(); - new_item_ptr.* = item.*; + new_item_ptr.* = item; } pub fn appendSlice(self: *Self, items: []align(A) const T) !void { diff --git a/std/build.zig b/std/build.zig index 16ce426bcb..92454a183a 100644 --- a/std/build.zig +++ b/std/build.zig @@ -234,7 +234,7 @@ pub const Builder = struct { defer wanted_steps.deinit(); if (step_names.len == 0) { - try wanted_steps.append(&self.default_step); + try wanted_steps.append(self.default_step); } else { for (step_names) |step_name| { const s = try self.getTopLevelStepByName(step_name); diff --git a/std/fmt/index.zig b/std/fmt/index.zig index cfc0948d2c..90d3a559c4 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -162,8 +162,6 @@ pub fn formatType( }, builtin.TypeInfo.Pointer.Size.Many => { if (ptr_info.child == u8) { - //This is a bit of a hack, but it made more sense to - // do this check here than have formatText do it if (fmt[0] == 's') { const len = std.cstr.len(value); return formatText(value[0..len], fmt, context, Errors, output); @@ -176,6 +174,12 @@ pub fn formatType( return output(context, casted_value); }, }, + builtin.TypeId.Array => |info| { + if (info.child == u8) { + return formatText(value, fmt, context, Errors, output); + } + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); + }, else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), } } diff --git a/std/json.zig b/std/json.zig index 75ea2eee1c..8bbee981e3 100644 --- a/std/json.zig +++ b/std/json.zig @@ -1326,7 +1326,7 @@ pub const Parser = struct { }, // Array Parent -> [ ..., , value ] Value.Array => |*array| { - try array.append(value); + try array.append(value.*); p.state = State.ArrayValue; }, else => { diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 19af10e695..5e15cfb895 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -18,39 +18,6 @@ comptime { debug.assert(Limb.is_signed == false); } -const wrapped_buffer_size = 512; - -// Converts primitive integer values onto a stack-based big integer, or passes through existing -// Int types with no modifications. This can fail at runtime if using a very large dynamic -// integer but it is very unlikely and is considered a user error. -fn wrapInt(allocator: *Allocator, bn: var) *const Int { - const T = @typeOf(bn); - switch (@typeInfo(T)) { - TypeId.Pointer => |info| { - if (info.child == Int) { - return bn; - } else { - @compileError("cannot set Int using type " ++ @typeName(T)); - } - }, - else => { - var s = allocator.create(Int) catch unreachable; - s.* = Int{ - .allocator = allocator, - .positive = false, - .limbs = block: { - var limbs = allocator.alloc(Limb, Int.default_capacity) catch unreachable; - limbs[0] = 0; - break :block limbs; - }, - .len = 1, - }; - s.set(bn) catch unreachable; - return s; - }, - } -} - pub const Int = struct { allocator: *Allocator, positive: bool, @@ -93,11 +60,11 @@ pub const Int = struct { self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity); } - pub fn deinit(self: *const Int) void { + pub fn deinit(self: Int) void { self.allocator.free(self.limbs); } - pub fn clone(other: *const Int) !Int { + pub fn clone(other: Int) !Int { return Int{ .allocator = other.allocator, .positive = other.positive, @@ -110,8 +77,8 @@ pub const Int = struct { }; } - pub fn copy(self: *Int, other: *const Int) !void { - if (self == other) { + pub fn copy(self: *Int, other: Int) !void { + if (self == &other) { return; } @@ -125,7 +92,7 @@ pub const Int = struct { mem.swap(Int, self, other); } - pub fn dump(self: *const Int) void { + pub fn dump(self: Int) void { for (self.limbs) |limb| { debug.warn("{x} ", limb); } @@ -140,20 +107,20 @@ pub const Int = struct { r.positive = true; } - pub fn isOdd(r: *const Int) bool { + pub fn isOdd(r: Int) bool { return r.limbs[0] & 1 != 0; } - pub fn isEven(r: *const Int) bool { + pub fn isEven(r: Int) bool { return !r.isOdd(); } - fn bitcount(self: *const Int) usize { + fn bitcount(self: Int) usize { const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); return usize(!self.positive) + u_bit_count; } - pub fn sizeInBase(self: *const Int, base: usize) usize { + pub fn sizeInBase(self: Int, base: usize) usize { return (self.bitcount() / math.log2(base)) + 1; } @@ -219,7 +186,7 @@ pub const Int = struct { TargetTooSmall, }; - pub fn to(self: *const Int, comptime T: type) ConvertError!T { + pub fn to(self: Int, comptime T: type) ConvertError!T { switch (@typeId(T)) { TypeId.Int => { const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T; @@ -286,16 +253,28 @@ pub const Int = struct { i += 1; } + // TODO values less than limb size should guarantee non allocating + var base_buffer: [512]u8 = undefined; + const base_al = &std.heap.FixedBufferAllocator.init(base_buffer[0..]).allocator; + const base_ap = try Int.initSet(base_al, base); + + var d_buffer: [512]u8 = undefined; + var d_fba = std.heap.FixedBufferAllocator.init(d_buffer[0..]); + const d_al = &d_fba.allocator; + try self.set(0); for (value[i..]) |ch| { const d = try charToDigit(ch, base); - try self.mul(self, base); - try self.add(self, d); + d_fba.end_index = 0; + const d_ap = try Int.initSet(d_al, d); + + try self.mul(self.*, base_ap); + try self.add(self.*, d_ap); } self.positive = positive; } - pub fn toString(self: *const Int, allocator: *Allocator, base: u8) ![]const u8 { + pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 { if (base < 2 or base > 16) { return error.InvalidBase; } @@ -345,7 +324,7 @@ pub const Int = struct { var b = try Int.initSet(allocator, limb_base); while (q.len >= 2) { - try Int.divTrunc(&q, &r, &q, &b); + try Int.divTrunc(&q, &r, q, b); var r_word = r.limbs[0]; var i: usize = 0; @@ -378,12 +357,7 @@ pub const Int = struct { } // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively. - pub fn cmpAbs(a: *const Int, bv: var) i8 { - // TODO: Thread-local buffer. - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var b = wrapInt(&stack.allocator, bv); - + pub fn cmpAbs(a: Int, b: Int) i8 { if (a.len < b.len) { return -1; } @@ -408,11 +382,7 @@ pub const Int = struct { } // returns -1, 0, 1 if a < b, a == b or a > b respectively. - pub fn cmp(a: *const Int, bv: var) i8 { - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var b = wrapInt(&stack.allocator, bv); - + pub fn cmp(a: Int, b: Int) i8 { if (a.positive != b.positive) { return if (a.positive) i8(1) else -1; } else { @@ -422,17 +392,17 @@ pub const Int = struct { } // if a == 0 - pub fn eqZero(a: *const Int) bool { + pub fn eqZero(a: Int) bool { return a.len == 1 and a.limbs[0] == 0; } // if |a| == |b| - pub fn eqAbs(a: *const Int, b: var) bool { + pub fn eqAbs(a: Int, b: Int) bool { return cmpAbs(a, b) == 0; } // if a == b - pub fn eq(a: *const Int, b: var) bool { + pub fn eq(a: Int, b: Int) bool { return cmp(a, b) == 0; } @@ -473,12 +443,7 @@ pub const Int = struct { } // r = a + b - pub fn add(r: *Int, av: var, bv: var) Allocator.Error!void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void { if (a.eqZero()) { try r.copy(b); return; @@ -547,12 +512,7 @@ pub const Int = struct { } // r = a - b - pub fn sub(r: *Int, av: var, bv: var) !void { - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn sub(r: *Int, a: Int, b: Int) !void { if (a.positive != b.positive) { if (a.positive) { // (a) - (-b) => a + b @@ -632,14 +592,9 @@ pub const Int = struct { // rma = a * b // // For greatest efficiency, ensure rma does not alias a or b. - pub fn mul(rma: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn mul(rma: *Int, a: Int, b: Int) !void { var r = rma; - var aliased = rma == a or rma == b; + var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr; var sr: Int = undefined; if (aliased) { @@ -714,29 +669,29 @@ pub const Int = struct { } } - pub fn divFloor(q: *Int, r: *Int, a: var, b: var) !void { + pub fn divFloor(q: *Int, r: *Int, a: Int, b: Int) !void { try div(q, r, a, b); // Trunc -> Floor. if (!q.positive) { - try q.sub(q, 1); - try r.add(q, 1); + // TODO values less than limb size should guarantee non allocating + var one_buffer: [512]u8 = undefined; + const one_al = &std.heap.FixedBufferAllocator.init(one_buffer[0..]).allocator; + const one_ap = try Int.initSet(one_al, 1); + + try q.sub(q.*, one_ap); + try r.add(q.*, one_ap); } r.positive = b.positive; } - pub fn divTrunc(q: *Int, r: *Int, a: var, b: var) !void { + pub fn divTrunc(q: *Int, r: *Int, a: Int, b: Int) !void { try div(q, r, a, b); r.positive = a.positive; } // Truncates by default. - fn div(quo: *Int, rem: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + fn div(quo: *Int, rem: *Int, a: Int, b: Int) !void { if (b.eqZero()) { @panic("division by zero"); } @@ -821,8 +776,8 @@ pub const Int = struct { // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) const norm_shift = @clz(y.limbs[y.len - 1]); - try x.shiftLeft(x, norm_shift); - try y.shiftLeft(y, norm_shift); + try x.shiftLeft(x.*, norm_shift); + try y.shiftLeft(y.*, norm_shift); const n = x.len - 1; const t = y.len - 1; @@ -832,10 +787,10 @@ pub const Int = struct { mem.set(Limb, q.limbs[0..q.len], 0); // 2. - try tmp.shiftLeft(y, Limb.bit_count * (n - t)); - while (x.cmp(&tmp) >= 0) { + try tmp.shiftLeft(y.*, Limb.bit_count * (n - t)); + while (x.cmp(tmp) >= 0) { q.limbs[n - t] += 1; - try x.sub(x, tmp); + try x.sub(x.*, tmp); } // 3. @@ -864,7 +819,7 @@ pub const Int = struct { r.limbs[2] = carry; r.normN(3); - if (r.cmpAbs(&tmp) <= 0) { + if (r.cmpAbs(tmp) <= 0) { break; } @@ -873,13 +828,13 @@ pub const Int = struct { // 3.3 try tmp.set(q.limbs[i - t - 1]); - try tmp.mul(&tmp, y); - try tmp.shiftLeft(&tmp, Limb.bit_count * (i - t - 1)); - try x.sub(x, &tmp); + try tmp.mul(tmp, y.*); + try tmp.shiftLeft(tmp, Limb.bit_count * (i - t - 1)); + try x.sub(x.*, tmp); if (!x.positive) { - try tmp.shiftLeft(y, Limb.bit_count * (i - t - 1)); - try x.add(x, &tmp); + try tmp.shiftLeft(y.*, Limb.bit_count * (i - t - 1)); + try x.add(x.*, tmp); q.limbs[i - t - 1] -= 1; } } @@ -887,16 +842,12 @@ pub const Int = struct { // Denormalize q.normN(q.len); - try r.shiftRight(x, norm_shift); + try r.shiftRight(x.*, norm_shift); r.normN(r.len); } // r = a << shift, in other words, r = a * 2^shift - pub fn shiftLeft(r: *Int, av: var, shift: usize) !void { - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - + pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void { try r.ensureCapacity(a.len + (shift / Limb.bit_count) + 1); llshl(r.limbs[0..], a.limbs[0..a.len], shift); r.norm1(a.len + (shift / Limb.bit_count) + 1); @@ -927,11 +878,7 @@ pub const Int = struct { } // r = a >> shift - pub fn shiftRight(r: *Int, av: var, shift: usize) !void { - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - + pub fn shiftRight(r: *Int, a: Int, shift: usize) !void { if (a.len <= shift / Limb.bit_count) { r.len = 1; r.limbs[0] = 0; @@ -966,12 +913,7 @@ pub const Int = struct { } // r = a | b - pub fn bitOr(r: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn bitOr(r: *Int, a: Int, b: Int) !void { if (a.len > b.len) { try r.ensureCapacity(a.len); llor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]); @@ -998,12 +940,7 @@ pub const Int = struct { } // r = a & b - pub fn bitAnd(r: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn bitAnd(r: *Int, a: Int, b: Int) !void { if (a.len > b.len) { try r.ensureCapacity(b.len); lland(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]); @@ -1027,12 +964,7 @@ pub const Int = struct { } // r = a ^ b - pub fn bitXor(r: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn bitXor(r: *Int, a: Int, b: Int) !void { if (a.len > b.len) { try r.ensureCapacity(a.len); llxor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]); @@ -1065,7 +997,7 @@ pub const Int = struct { // may be untested in some cases. const u256 = @IntType(false, 256); -var al = debug.global_allocator; +const al = debug.global_allocator; test "big.int comptime_int set" { comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab; @@ -1198,7 +1130,7 @@ test "big.int bitcount + sizeInBase" { debug.assert(a.sizeInBase(2) >= 32); debug.assert(a.sizeInBase(10) >= 10); - try a.shiftLeft(&a, 5000); + try a.shiftLeft(a, 5000); debug.assert(a.bitcount() == 5032); debug.assert(a.sizeInBase(2) >= 5032); a.positive = false; @@ -1320,40 +1252,40 @@ test "big.int compare" { var a = try Int.initSet(al, -11); var b = try Int.initSet(al, 10); - debug.assert(a.cmpAbs(&b) == 1); - debug.assert(a.cmp(&b) == -1); + debug.assert(a.cmpAbs(b) == 1); + debug.assert(a.cmp(b) == -1); } test "big.int compare similar" { var a = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeee); var b = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeef); - debug.assert(a.cmpAbs(&b) == -1); - debug.assert(b.cmpAbs(&a) == 1); + debug.assert(a.cmpAbs(b) == -1); + debug.assert(b.cmpAbs(a) == 1); } test "big.int compare different limb size" { var a = try Int.initSet(al, @maxValue(Limb) + 1); var b = try Int.initSet(al, 1); - debug.assert(a.cmpAbs(&b) == 1); - debug.assert(b.cmpAbs(&a) == -1); + debug.assert(a.cmpAbs(b) == 1); + debug.assert(b.cmpAbs(a) == -1); } test "big.int compare multi-limb" { var a = try Int.initSet(al, -0x7777777799999999ffffeeeeffffeeeeffffeeeef); var b = try Int.initSet(al, 0x7777777799999999ffffeeeeffffeeeeffffeeeee); - debug.assert(a.cmpAbs(&b) == 1); - debug.assert(a.cmp(&b) == -1); + debug.assert(a.cmpAbs(b) == 1); + debug.assert(a.cmp(b) == -1); } test "big.int equality" { var a = try Int.initSet(al, 0xffffffff1); var b = try Int.initSet(al, -0xffffffff1); - debug.assert(a.eqAbs(&b)); - debug.assert(!a.eq(&b)); + debug.assert(a.eqAbs(b)); + debug.assert(!a.eq(b)); } test "big.int abs" { @@ -1381,7 +1313,7 @@ test "big.int add single-single" { var b = try Int.initSet(al, 5); var c = try Int.init(al); - try c.add(&a, &b); + try c.add(a, b); debug.assert((try c.to(u32)) == 55); } @@ -1392,10 +1324,10 @@ test "big.int add multi-single" { var c = try Int.init(al); - try c.add(&a, &b); + try c.add(a, b); debug.assert((try c.to(DoubleLimb)) == @maxValue(Limb) + 2); - try c.add(&b, &a); + try c.add(b, a); debug.assert((try c.to(DoubleLimb)) == @maxValue(Limb) + 2); } @@ -1406,7 +1338,7 @@ test "big.int add multi-multi" { var b = try Int.initSet(al, op2); var c = try Int.init(al); - try c.add(&a, &b); + try c.add(a, b); debug.assert((try c.to(u128)) == op1 + op2); } @@ -1416,7 +1348,7 @@ test "big.int add zero-zero" { var b = try Int.initSet(al, 0); var c = try Int.init(al); - try c.add(&a, &b); + try c.add(a, b); debug.assert((try c.to(u32)) == 0); } @@ -1426,7 +1358,7 @@ test "big.int add alias multi-limb nonzero-zero" { var a = try Int.initSet(al, op1); var b = try Int.initSet(al, 0); - try a.add(&a, &b); + try a.add(a, b); debug.assert((try a.to(u128)) == op1); } @@ -1434,16 +1366,21 @@ test "big.int add alias multi-limb nonzero-zero" { test "big.int add sign" { var a = try Int.init(al); - try a.add(1, 2); + const one = try Int.initSet(al, 1); + const two = try Int.initSet(al, 2); + const neg_one = try Int.initSet(al, -1); + const neg_two = try Int.initSet(al, -2); + + try a.add(one, two); debug.assert((try a.to(i32)) == 3); - try a.add(-1, 2); + try a.add(neg_one, two); debug.assert((try a.to(i32)) == 1); - try a.add(1, -2); + try a.add(one, neg_two); debug.assert((try a.to(i32)) == -1); - try a.add(-1, -2); + try a.add(neg_one, neg_two); debug.assert((try a.to(i32)) == -3); } @@ -1452,7 +1389,7 @@ test "big.int sub single-single" { var b = try Int.initSet(al, 5); var c = try Int.init(al); - try c.sub(&a, &b); + try c.sub(a, b); debug.assert((try c.to(u32)) == 45); } @@ -1462,7 +1399,7 @@ test "big.int sub multi-single" { var b = try Int.initSet(al, 1); var c = try Int.init(al); - try c.sub(&a, &b); + try c.sub(a, b); debug.assert((try c.to(Limb)) == @maxValue(Limb)); } @@ -1475,7 +1412,7 @@ test "big.int sub multi-multi" { var b = try Int.initSet(al, op2); var c = try Int.init(al); - try c.sub(&a, &b); + try c.sub(a, b); debug.assert((try c.to(u128)) == op1 - op2); } @@ -1485,7 +1422,7 @@ test "big.int sub equal" { var b = try Int.initSet(al, 0x11efefefefefefefefefefefef); var c = try Int.init(al); - try c.sub(&a, &b); + try c.sub(a, b); debug.assert((try c.to(u32)) == 0); } @@ -1493,19 +1430,24 @@ test "big.int sub equal" { test "big.int sub sign" { var a = try Int.init(al); - try a.sub(1, 2); + const one = try Int.initSet(al, 1); + const two = try Int.initSet(al, 2); + const neg_one = try Int.initSet(al, -1); + const neg_two = try Int.initSet(al, -2); + + try a.sub(one, two); debug.assert((try a.to(i32)) == -1); - try a.sub(-1, 2); + try a.sub(neg_one, two); debug.assert((try a.to(i32)) == -3); - try a.sub(1, -2); + try a.sub(one, neg_two); debug.assert((try a.to(i32)) == 3); - try a.sub(-1, -2); + try a.sub(neg_one, neg_two); debug.assert((try a.to(i32)) == 1); - try a.sub(-2, -1); + try a.sub(neg_two, neg_one); debug.assert((try a.to(i32)) == -1); } @@ -1514,7 +1456,7 @@ test "big.int mul single-single" { var b = try Int.initSet(al, 5); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(u64)) == 250); } @@ -1524,7 +1466,7 @@ test "big.int mul multi-single" { var b = try Int.initSet(al, 2); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(DoubleLimb)) == 2 * @maxValue(Limb)); } @@ -1536,7 +1478,7 @@ test "big.int mul multi-multi" { var b = try Int.initSet(al, op2); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(u256)) == op1 * op2); } @@ -1545,7 +1487,7 @@ test "big.int mul alias r with a" { var a = try Int.initSet(al, @maxValue(Limb)); var b = try Int.initSet(al, 2); - try a.mul(&a, &b); + try a.mul(a, b); debug.assert((try a.to(DoubleLimb)) == 2 * @maxValue(Limb)); } @@ -1554,7 +1496,7 @@ test "big.int mul alias r with b" { var a = try Int.initSet(al, @maxValue(Limb)); var b = try Int.initSet(al, 2); - try a.mul(&b, &a); + try a.mul(b, a); debug.assert((try a.to(DoubleLimb)) == 2 * @maxValue(Limb)); } @@ -1562,7 +1504,7 @@ test "big.int mul alias r with b" { test "big.int mul alias r with a and b" { var a = try Int.initSet(al, @maxValue(Limb)); - try a.mul(&a, &a); + try a.mul(a, a); debug.assert((try a.to(DoubleLimb)) == @maxValue(Limb) * @maxValue(Limb)); } @@ -1572,7 +1514,7 @@ test "big.int mul a*0" { var b = try Int.initSet(al, 0); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(u32)) == 0); } @@ -1582,7 +1524,7 @@ test "big.int mul 0*0" { var b = try Int.initSet(al, 0); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(u32)) == 0); } @@ -1593,7 +1535,7 @@ test "big.int div single-single no rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u32)) == 10); debug.assert((try r.to(u32)) == 0); @@ -1605,7 +1547,7 @@ test "big.int div single-single with rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u32)) == 9); debug.assert((try r.to(u32)) == 4); @@ -1620,7 +1562,7 @@ test "big.int div multi-single no rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u64)) == op1 / op2); debug.assert((try r.to(u64)) == 0); @@ -1635,7 +1577,7 @@ test "big.int div multi-single with rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u64)) == op1 / op2); debug.assert((try r.to(u64)) == 3); @@ -1650,7 +1592,7 @@ test "big.int div multi>2-single" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == op1 / op2); debug.assert((try r.to(u32)) == 0x3e4e); @@ -1662,7 +1604,7 @@ test "big.int div single-single q < r" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u64)) == 0); debug.assert((try r.to(u64)) == 0x0078f432); @@ -1674,7 +1616,7 @@ test "big.int div single-single q == r" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u64)) == 1); debug.assert((try r.to(u64)) == 0); @@ -1684,7 +1626,7 @@ test "big.int div q=0 alias" { var a = try Int.initSet(al, 3); var b = try Int.initSet(al, 10); - try Int.divTrunc(&a, &b, &a, &b); + try Int.divTrunc(&a, &b, a, b); debug.assert((try a.to(u64)) == 0); debug.assert((try b.to(u64)) == 3); @@ -1698,7 +1640,7 @@ test "big.int div multi-multi q < r" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0); debug.assert((try r.to(u128)) == op1); @@ -1713,7 +1655,7 @@ test "big.int div trunc single-single +/+" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); // n = q * d + r // 5 = 1 * 3 + 2 @@ -1733,7 +1675,7 @@ test "big.int div trunc single-single -/+" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); // n = q * d + r // -5 = 1 * -3 - 2 @@ -1753,7 +1695,7 @@ test "big.int div trunc single-single +/-" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); // n = q * d + r // 5 = -1 * -3 + 2 @@ -1773,7 +1715,7 @@ test "big.int div trunc single-single -/-" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); // n = q * d + r // -5 = 1 * -3 - 2 @@ -1793,7 +1735,7 @@ test "big.int div floor single-single +/+" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divFloor(&q, &r, &a, &b); + try Int.divFloor(&q, &r, a, b); // n = q * d + r // 5 = 1 * 3 + 2 @@ -1813,7 +1755,7 @@ test "big.int div floor single-single -/+" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divFloor(&q, &r, &a, &b); + try Int.divFloor(&q, &r, a, b); // n = q * d + r // -5 = -2 * 3 + 1 @@ -1833,7 +1775,7 @@ test "big.int div floor single-single +/-" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divFloor(&q, &r, &a, &b); + try Int.divFloor(&q, &r, a, b); // n = q * d + r // 5 = -2 * -3 - 1 @@ -1853,7 +1795,7 @@ test "big.int div floor single-single -/-" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divFloor(&q, &r, &a, &b); + try Int.divFloor(&q, &r, a, b); // n = q * d + r // -5 = 2 * -3 + 1 @@ -1870,7 +1812,7 @@ test "big.int div multi-multi with rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); debug.assert((try r.to(u128)) == 0x28de0acacd806823638); @@ -1882,7 +1824,7 @@ test "big.int div multi-multi no rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); debug.assert((try r.to(u128)) == 0); @@ -1894,7 +1836,7 @@ test "big.int div multi-multi (2 branch)" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0x10000000000000000); debug.assert((try r.to(u128)) == 0x44444443444444431111111111111111); @@ -1906,7 +1848,7 @@ test "big.int div multi-multi (3.1/3.3 branch)" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0xfffffffffffffffffff); debug.assert((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282); @@ -1943,17 +1885,17 @@ test "big.int shift-left multi" { test "big.int shift-right negative" { var a = try Int.init(al); - try a.shiftRight(-20, 2); + try a.shiftRight(try Int.initSet(al, -20), 2); debug.assert((try a.to(i32)) == -20 >> 2); - try a.shiftRight(-5, 10); + try a.shiftRight(try Int.initSet(al, -5), 10); debug.assert((try a.to(i32)) == -5 >> 10); } test "big.int shift-left negative" { var a = try Int.init(al); - try a.shiftRight(-10, 1232); + try a.shiftRight(try Int.initSet(al, -10), 1232); debug.assert((try a.to(i32)) == -10 >> 1232); } @@ -1961,7 +1903,7 @@ test "big.int bitwise and simple" { var a = try Int.initSet(al, 0xffffffff11111111); var b = try Int.initSet(al, 0xeeeeeeee22222222); - try a.bitAnd(&a, &b); + try a.bitAnd(a, b); debug.assert((try a.to(u64)) == 0xeeeeeeee00000000); } @@ -1970,7 +1912,7 @@ test "big.int bitwise and multi-limb" { var a = try Int.initSet(al, @maxValue(Limb) + 1); var b = try Int.initSet(al, @maxValue(Limb)); - try a.bitAnd(&a, &b); + try a.bitAnd(a, b); debug.assert((try a.to(u128)) == 0); } @@ -1979,7 +1921,7 @@ test "big.int bitwise xor simple" { var a = try Int.initSet(al, 0xffffffff11111111); var b = try Int.initSet(al, 0xeeeeeeee22222222); - try a.bitXor(&a, &b); + try a.bitXor(a, b); debug.assert((try a.to(u64)) == 0x1111111133333333); } @@ -1988,7 +1930,7 @@ test "big.int bitwise xor multi-limb" { var a = try Int.initSet(al, @maxValue(Limb) + 1); var b = try Int.initSet(al, @maxValue(Limb)); - try a.bitXor(&a, &b); + try a.bitXor(a, b); debug.assert((try a.to(DoubleLimb)) == (@maxValue(Limb) + 1) ^ @maxValue(Limb)); } @@ -1997,7 +1939,7 @@ test "big.int bitwise or simple" { var a = try Int.initSet(al, 0xffffffff11111111); var b = try Int.initSet(al, 0xeeeeeeee22222222); - try a.bitOr(&a, &b); + try a.bitOr(a, b); debug.assert((try a.to(u64)) == 0xffffffff33333333); } @@ -2006,7 +1948,7 @@ test "big.int bitwise or multi-limb" { var a = try Int.initSet(al, @maxValue(Limb) + 1); var b = try Int.initSet(al, @maxValue(Limb)); - try a.bitOr(&a, &b); + try a.bitOr(a, b); // TODO: big.int.cpp or is wrong on multi-limb. debug.assert((try a.to(DoubleLimb)) == (@maxValue(Limb) + 1) + @maxValue(Limb)); @@ -2015,9 +1957,9 @@ test "big.int bitwise or multi-limb" { test "big.int var args" { var a = try Int.initSet(al, 5); - try a.add(&a, 6); + try a.add(a, try Int.initSet(al, 6)); debug.assert((try a.to(u64)) == 11); - debug.assert(a.cmp(11) == 0); - debug.assert(a.cmp(14) <= 0); + debug.assert(a.cmp(try Int.initSet(al, 11)) == 0); + debug.assert(a.cmp(try Int.initSet(al, 14)) <= 0); } diff --git a/std/mem.zig b/std/mem.zig index f961c7862b..10b3eb8fef 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -40,16 +40,12 @@ pub const Allocator = struct { /// Call destroy with the result /// TODO once #733 is solved, this will replace create - pub fn construct(self: *Allocator, init: var) t: { - // TODO this is a workaround for type getting parsed as Error!&const T - const T = @typeOf(init).Child; - break :t Error!*T; - } { - const T = @typeOf(init).Child; + pub fn construct(self: *Allocator, init: var) Error!*@typeOf(init) { + const T = @typeOf(init); if (@sizeOf(T) == 0) return &{}; const slice = try self.alloc(T, 1); const ptr = &slice[0]; - ptr.* = init.*; + ptr.* = init; return ptr; } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index ade1cf78aa..4c216010eb 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -318,14 +318,6 @@ fn testCastConstArrayRefToConstSlice() void { assert(mem.eql(u8, slice, "aoeu")); } -test "var args implicitly casts by value arg to const ref" { - foo("hello"); -} - -fn foo(args: ...) void { - assert(@typeOf(args[0]) == *const [5]u8); -} - test "peer type resolution: error and [N]T" { // TODO: implicit error!T to error!U where T can implicitly cast to U //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); diff --git a/test/cases/fn.zig b/test/cases/fn.zig index 2426a411df..12f22bfc35 100644 --- a/test/cases/fn.zig +++ b/test/cases/fn.zig @@ -121,7 +121,7 @@ test "assign inline fn to const variable" { inline fn inlineFn() void {} test "pass by non-copying value" { - assert(bar(Point{ .x = 1, .y = 2 }) == 3); + assert(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); } const Point = struct { @@ -129,6 +129,50 @@ const Point = struct { y: i32, }; -fn bar(pt: Point) i32 { +fn addPointCoords(pt: Point) i32 { return pt.x + pt.y; } + +test "pass by non-copying value through var arg" { + assert(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); +} + +fn addPointCoordsVar(pt: var) i32 { + comptime assert(@typeOf(pt) == Point); + return pt.x + pt.y; +} + +test "pass by non-copying value as method" { + var pt = Point2{ .x = 1, .y = 2 }; + assert(pt.addPointCoords() == 3); +} + +const Point2 = struct { + x: i32, + y: i32, + + fn addPointCoords(self: Point2) i32 { + return self.x + self.y; + } +}; + +test "pass by non-copying value as method, which is generic" { + var pt = Point3{ .x = 1, .y = 2 }; + assert(pt.addPointCoords(i32) == 3); +} + +const Point3 = struct { + x: i32, + y: i32, + + fn addPointCoords(self: Point3, comptime T: type) i32 { + return self.x + self.y; + } +}; + +test "pass by non-copying value as method, at comptime" { + comptime { + var pt = Point2{ .x = 1, .y = 2 }; + assert(pt.addPointCoords() == 3); + } +} diff --git a/test/cases/var_args.zig b/test/cases/var_args.zig index 5ef41f52ba..3eb6e30448 100644 --- a/test/cases/var_args.zig +++ b/test/cases/var_args.zig @@ -75,18 +75,6 @@ test "array of var args functions" { assert(!foos[1]()); } -test "pass array and slice of same array to var args should have same pointers" { - const array = "hi"; - const slice: []const u8 = array; - return assertSlicePtrsEql(array, slice); -} - -fn assertSlicePtrsEql(args: ...) void { - const s1 = ([]const u8)(args[0]); - const s2 = args[1]; - assert(s1.ptr == s2.ptr); -} - test "pass zero length array to var args param" { doNothingWithFirstArg(""); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 60ba255172..ed46e43066 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2215,7 +2215,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ derp.init(); \\} , - ".tmp_source.zig:14:5: error: expected type 'i32', found '*const Foo'", + ".tmp_source.zig:14:5: error: expected type 'i32', found 'Foo'", ); cases.add( -- cgit v1.2.3 From 472b7ef7e6db4bdd4717ff5b44b63e233853bb06 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jun 2018 19:14:14 -0400 Subject: disable byval --- src/codegen.cpp | 21 --------------------- test/behavior.zig | 1 + test/cases/byval_arg_var.zig | 27 +++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 test/cases/byval_arg_var.zig diff --git a/src/codegen.cpp b/src/codegen.cpp index fedfcfa744..425cdac024 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -326,13 +326,6 @@ static void addLLVMArgAttr(LLVMValueRef arg_val, unsigned param_index, const cha return addLLVMAttr(arg_val, param_index + 1, attr_name); } -static void addLLVMCallsiteAttr(LLVMValueRef call_instr, unsigned param_index, const char *attr_name) { - unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); - assert(kind_id != 0); - LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), kind_id, 0); - LLVMAddCallSiteAttribute(call_instr, param_index + 1, llvm_attr); -} - static bool is_symbol_available(CodeGen *g, Buf *name) { return g->exported_symbol_names.maybe_get(name) == nullptr && g->external_prototypes.maybe_get(name) == nullptr; } @@ -581,11 +574,6 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { if (param_type->id == TypeTableEntryIdPointer) { addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull"); } - // Note: byval is disabled on windows due to an LLVM bug: - // https://github.com/ziglang/zig/issues/536 - if (is_byval && g->zig_target.os != OsWindows) { - addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "byval"); - } } uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); @@ -3114,15 +3102,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } - for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { - FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; - // Note: byval is disabled on windows due to an LLVM bug: - // https://github.com/ziglang/zig/issues/536 - if (gen_info->is_byval && g->zig_target.os != OsWindows) { - addLLVMCallsiteAttr(result, (unsigned)gen_info->gen_index, "byval"); - } - } - if (instruction->is_async) { LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, ""); LLVMBuildStore(g->builder, result, payload_ptr); diff --git a/test/behavior.zig b/test/behavior.zig index eb8b643bb7..096c07b2e0 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -13,6 +13,7 @@ comptime { _ = @import("cases/bugs/656.zig"); _ = @import("cases/bugs/828.zig"); _ = @import("cases/bugs/920.zig"); + _ = @import("cases/byval_arg_var.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); _ = @import("cases/coroutines.zig"); diff --git a/test/cases/byval_arg_var.zig b/test/cases/byval_arg_var.zig new file mode 100644 index 0000000000..826b9cc9e5 --- /dev/null +++ b/test/cases/byval_arg_var.zig @@ -0,0 +1,27 @@ +const std = @import("std"); + +var result: []const u8 = "wrong"; + +test "aoeu" { + start(); + blowUpStack(10); + + std.debug.assert(std.mem.eql(u8, result, "string literal")); +} + +fn start() void { + foo("string literal"); +} + +fn foo(x: var) void { + bar(x); +} + +fn bar(x: var) void { + result = x; +} + +fn blowUpStack(x: u32) void { + if (x == 0) return; + blowUpStack(x - 1); +} -- cgit v1.2.3 From eae9634ac959bdb32c95f2acdcadde59beec23d5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jun 2018 19:53:52 -0400 Subject: langref: be clear that float types are always IEEE 754 --- doc/langref.html.in | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 814de721a6..039109f938 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -370,17 +370,17 @@ pub fn main() void { f32 float - 32-bit floating point (23-bit mantissa) + 32-bit floating point (23-bit mantissa) IEEE-754-2008 binary32 f64 double - 64-bit floating point (52-bit mantissa) + 64-bit floating point (52-bit mantissa) IEEE-754-2008 binary64 f128 (none) - 128-bit floating point (112-bit mantissa) + 128-bit floating point (112-bit mantissa) IEEE-754-2008 binary128 bool @@ -407,6 +407,16 @@ pub fn main() void { (none) an error code + + comptime_int + (none) + Only allowed for {#link|comptime#}-known values. The type of integer literals. + + + comptime_float + (none) + Only allowed for {#link|comptime#}-known values. The type of float literals. + {#see_also|Integers|Floats|void|Errors#} @@ -642,7 +652,18 @@ fn divide(a: i32, b: i32) i32 { {#header_close#} {#header_close#} {#header_open|Floats#} +

    Zig has the following floating point types:

    +
      +
    • f32 - IEEE-754-2008 binary32
    • +
    • f64 - IEEE-754-2008 binary64
    • +
    • f128 - IEEE-754-2008 binary128
    • +
    • c_longdouble - matches long double for the target C ABI
    • +
    {#header_open|Float Literals#} +

    + Float literals have type comptime_float which is guaranteed to hold at least all possible values + that the largest other floating point type can hold. Float literals implicitly cast to any other type. +

    {#code_begin|syntax#} const floating_point = 123.0E+77; const another_float = 123.0; -- cgit v1.2.3 From 3ee4d23ebdd149e734ca33904a4786d5bd3fa8aa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jun 2018 19:54:16 -0400 Subject: posix read can return error.IsDir --- std/debug/index.zig | 1 + std/os/file.zig | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/std/debug/index.zig b/std/debug/index.zig index 25f7a58b25..fb1dac537c 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -639,6 +639,7 @@ const ParseFormValueError = error{ Unexpected, InvalidDebugInfo, EndOfFile, + IsDir, OutOfMemory, }; diff --git a/std/os/file.zig b/std/os/file.zig index 41d3dfbf95..7e05501831 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -311,9 +311,15 @@ pub const File = struct { } } - pub const ReadError = error{}; + pub const ReadError = error{ + BadFd, + Io, + IsDir, + + Unexpected, + }; - pub fn read(self: *File, buffer: []u8) !usize { + pub fn read(self: *File, buffer: []u8) ReadError!usize { if (is_posix) { var index: usize = 0; while (index < buffer.len) { @@ -326,6 +332,7 @@ pub const File = struct { posix.EFAULT => unreachable, posix.EBADF => return error.BadFd, posix.EIO => return error.Io, + posix.EISDIR => return error.IsDir, else => return os.unexpectedErrorPosix(read_err), } } -- cgit v1.2.3 From 06a26f0965deff3d752da3d448b34872010d80f3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jun 2018 21:32:53 -0400 Subject: std.Complex: use better arg passing convention and fix a TODO --- std/math/complex/index.zig | 14 +++++++------- std/math/complex/sqrt.zig | 9 ++++----- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/std/math/complex/index.zig b/std/math/complex/index.zig index b00296beda..63a2616984 100644 --- a/std/math/complex/index.zig +++ b/std/math/complex/index.zig @@ -37,28 +37,28 @@ pub fn Complex(comptime T: type) type { }; } - pub fn add(self: *const Self, other: *const Self) Self { + pub fn add(self: Self, other: Self) Self { return Self{ .re = self.re + other.re, .im = self.im + other.im, }; } - pub fn sub(self: *const Self, other: *const Self) Self { + pub fn sub(self: Self, other: Self) Self { return Self{ .re = self.re - other.re, .im = self.im - other.im, }; } - pub fn mul(self: *const Self, other: *const Self) Self { + pub fn mul(self: Self, other: Self) Self { return Self{ .re = self.re * other.re - self.im * other.im, .im = self.im * other.re + self.re * other.im, }; } - pub fn div(self: *const Self, other: *const Self) Self { + pub fn div(self: Self, other: Self) Self { const re_num = self.re * other.re + self.im * other.im; const im_num = self.im * other.re - self.re * other.im; const den = other.re * other.re + other.im * other.im; @@ -69,14 +69,14 @@ pub fn Complex(comptime T: type) type { }; } - pub fn conjugate(self: *const Self) Self { + pub fn conjugate(self: Self) Self { return Self{ .re = self.re, .im = -self.im, }; } - pub fn reciprocal(self: *const Self) Self { + pub fn reciprocal(self: Self) Self { const m = self.re * self.re + self.im * self.im; return Self{ .re = self.re / m, @@ -84,7 +84,7 @@ pub fn Complex(comptime T: type) type { }; } - pub fn magnitude(self: *const Self) T { + pub fn magnitude(self: Self) T { return math.sqrt(self.re * self.re + self.im * self.im); } }; diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig index d4f5a67528..caa3bd2868 100644 --- a/std/math/complex/sqrt.zig +++ b/std/math/complex/sqrt.zig @@ -4,18 +4,17 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; -// TODO when #733 is solved this can be @typeOf(z) instead of Complex(@typeOf(z.re)) -pub fn sqrt(z: var) Complex(@typeOf(z.re)) { +pub fn sqrt(z: var) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { f32 => sqrt32(z), f64 => sqrt64(z), - else => @compileError("sqrt not implemented for " ++ @typeName(z)), + else => @compileError("sqrt not implemented for " ++ @typeName(T)), }; } -fn sqrt32(z: *const Complex(f32)) Complex(f32) { +fn sqrt32(z: Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -57,7 +56,7 @@ fn sqrt32(z: *const Complex(f32)) Complex(f32) { } } -fn sqrt64(z: *const Complex(f64)) Complex(f64) { +fn sqrt64(z: Complex(f64)) Complex(f64) { // may encounter overflow for im,re >= DBL_MAX / (1 + sqrt(2)) const threshold = 0x1.a827999fcef32p+1022; -- cgit v1.2.3 From 79120612267f55901029dd57290ee90c0a3ec987 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 17 Jun 2018 02:57:07 -0400 Subject: remove integer and float casting syntax * add `@intCast` * add `@floatCast` * add `@floatToInt` * add `@intToFloat` See #1061 --- doc/langref.html.in | 12 +- src/all_types.hpp | 36 +++++ src/codegen.cpp | 8 + src/ir.cpp | 290 +++++++++++++++++++++++++++++++++--- src/ir_print.cpp | 44 ++++++ src/main.cpp | 2 +- std/array_list.zig | 8 +- std/base64.zig | 4 +- std/crypto/blake2.zig | 10 +- std/crypto/md5.zig | 6 +- std/crypto/sha1.zig | 6 +- std/crypto/sha2.zig | 12 +- std/debug/index.zig | 8 +- std/fmt/errol/index.zig | 78 +++++----- std/fmt/index.zig | 19 +-- std/hash/crc.zig | 4 +- std/hash/siphash.zig | 6 +- std/heap.zig | 2 +- std/json.zig | 2 +- std/math/acos.zig | 4 +- std/math/asin.zig | 4 +- std/math/atan.zig | 4 +- std/math/atan2.zig | 8 +- std/math/atanh.zig | 2 +- std/math/big/int.zig | 22 +-- std/math/cbrt.zig | 6 +- std/math/ceil.zig | 4 +- std/math/complex/atan.zig | 10 +- std/math/complex/cosh.zig | 4 +- std/math/complex/exp.zig | 6 +- std/math/complex/ldexp.zig | 13 +- std/math/complex/sinh.zig | 10 +- std/math/complex/sqrt.zig | 10 +- std/math/complex/tanh.zig | 10 +- std/math/cos.zig | 4 +- std/math/cosh.zig | 2 +- std/math/exp.zig | 12 +- std/math/exp2.zig | 14 +- std/math/expm1.zig | 20 +-- std/math/floor.zig | 4 +- std/math/fma.zig | 6 +- std/math/frexp.zig | 4 +- std/math/hypot.zig | 2 +- std/math/ilogb.zig | 4 +- std/math/index.zig | 25 +++- std/math/ln.zig | 12 +- std/math/log.zig | 11 +- std/math/log10.zig | 14 +- std/math/log1p.zig | 12 +- std/math/log2.zig | 12 +- std/math/modf.zig | 8 +- std/math/pow.zig | 4 +- std/math/scalbn.zig | 4 +- std/math/sin.zig | 4 +- std/math/sinh.zig | 2 +- std/math/sqrt.zig | 2 +- std/math/tan.zig | 4 +- std/math/tanh.zig | 4 +- std/math/trunc.zig | 8 +- std/mem.zig | 2 +- std/os/child_process.zig | 2 +- std/os/darwin.zig | 11 +- std/os/file.zig | 6 +- std/os/index.zig | 16 +- std/os/linux/index.zig | 80 +++++----- std/os/linux/test.zig | 6 +- std/os/linux/vdso.zig | 4 +- std/os/time.zig | 24 +-- std/os/windows/util.zig | 9 +- std/rand/index.zig | 16 +- std/segmented_list.zig | 12 +- std/special/bootstrap.zig | 2 +- std/special/builtin.zig | 30 ++-- std/special/compiler_rt/divti3.zig | 2 +- std/special/compiler_rt/fixuint.zig | 8 +- std/special/compiler_rt/index.zig | 12 +- std/special/compiler_rt/udivmod.zig | 34 ++--- std/unicode.zig | 20 +-- std/zig/tokenizer.zig | 2 +- test/cases/cast.zig | 18 ++- test/cases/enum.zig | 2 +- test/cases/eval.zig | 8 +- test/cases/fn.zig | 2 +- test/cases/for.zig | 4 +- test/cases/struct.zig | 8 +- 85 files changed, 799 insertions(+), 413 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 651629d8fb..35ca9a13b4 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1355,7 +1355,7 @@ var some_integers: [100]i32 = undefined; test "modify an array" { for (some_integers) |*item, i| { - item.* = i32(i); + item.* = @intCast(i32, i); } assert(some_integers[10] == 10); assert(some_integers[99] == 99); @@ -1397,8 +1397,8 @@ var fancy_array = init: { var initial_value: [10]Point = undefined; for (initial_value) |*pt, i| { pt.* = Point{ - .x = i32(i), - .y = i32(i) * 2, + .x = @intCast(i32, i), + .y = @intCast(i32, i) * 2, }; } break :init initial_value; @@ -2410,7 +2410,7 @@ test "for basics" { var sum2: i32 = 0; for (items) |value, i| { assert(@typeOf(i) == usize); - sum2 += i32(i); + sum2 += @intCast(i32, i); } assert(sum2 == 10); } @@ -5730,7 +5730,7 @@ comptime { {#code_begin|test_err|attempt to cast negative value to unsigned integer#} comptime { const value: i32 = -1; - const unsigned = u32(value); + const unsigned = @intCast(u32, value); } {#code_end#}

    At runtime crashes with the message attempt to cast negative value to unsigned integer and a stack trace.

    @@ -5744,7 +5744,7 @@ comptime { {#code_begin|test_err|cast from 'u16' to 'u8' truncates bits#} comptime { const spartan_count: u16 = 300; - const byte = u8(spartan_count); + const byte = @intCast(u8, spartan_count); } {#code_end#}

    At runtime crashes with the message integer cast truncated bits and a stack trace.

    diff --git a/src/all_types.hpp b/src/all_types.hpp index 0d364915f9..bc2fe07c18 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1357,6 +1357,10 @@ enum BuiltinFnId { BuiltinFnIdMod, BuiltinFnIdSqrt, BuiltinFnIdTruncate, + BuiltinFnIdIntCast, + BuiltinFnIdFloatCast, + BuiltinFnIdIntToFloat, + BuiltinFnIdFloatToInt, BuiltinFnIdIntType, BuiltinFnIdSetCold, BuiltinFnIdSetRuntimeSafety, @@ -2040,6 +2044,10 @@ enum IrInstructionId { IrInstructionIdCmpxchg, IrInstructionIdFence, IrInstructionIdTruncate, + IrInstructionIdIntCast, + IrInstructionIdFloatCast, + IrInstructionIdIntToFloat, + IrInstructionIdFloatToInt, IrInstructionIdIntType, IrInstructionIdBoolNot, IrInstructionIdMemset, @@ -2632,6 +2640,34 @@ struct IrInstructionTruncate { IrInstruction *target; }; +struct IrInstructionIntCast { + IrInstruction base; + + IrInstruction *dest_type; + IrInstruction *target; +}; + +struct IrInstructionFloatCast { + IrInstruction base; + + IrInstruction *dest_type; + IrInstruction *target; +}; + +struct IrInstructionIntToFloat { + IrInstruction base; + + IrInstruction *dest_type; + IrInstruction *target; +}; + +struct IrInstructionFloatToInt { + IrInstruction base; + + IrInstruction *dest_type; + IrInstruction *target; +}; + struct IrInstructionIntType { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 425cdac024..4108cbbd68 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4722,6 +4722,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdPromiseResultType: case IrInstructionIdAwaitBookkeeping: case IrInstructionIdAddImplicitReturnType: + case IrInstructionIdIntCast: + case IrInstructionIdFloatCast: + case IrInstructionIdIntToFloat: + case IrInstructionIdFloatToInt: zig_unreachable(); case IrInstructionIdReturn: @@ -6310,6 +6314,10 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6); create_builtin_fn(g, BuiltinFnIdFence, "fence", 1); create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); + create_builtin_fn(g, BuiltinFnIdIntCast, "intCast", 2); + create_builtin_fn(g, BuiltinFnIdFloatCast, "floatCast", 2); + create_builtin_fn(g, BuiltinFnIdIntToFloat, "intToFloat", 2); + create_builtin_fn(g, BuiltinFnIdFloatToInt, "floatToInt", 2); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int diff --git a/src/ir.cpp b/src/ir.cpp index d008ead113..0b847fc4e4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -460,6 +460,22 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTruncate *) { return IrInstructionIdTruncate; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionIntCast *) { + return IrInstructionIdIntCast; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatCast *) { + return IrInstructionIdFloatCast; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToFloat *) { + return IrInstructionIdIntToFloat; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatToInt *) { + return IrInstructionIdFloatToInt; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) { return IrInstructionIdIntType; } @@ -1899,10 +1915,48 @@ static IrInstruction *ir_build_truncate(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } -static IrInstruction *ir_build_truncate_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *dest_type, IrInstruction *target) { - IrInstruction *new_instruction = ir_build_truncate(irb, old_instruction->scope, old_instruction->source_node, dest_type, target); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; +static IrInstruction *ir_build_int_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { + IrInstructionIntCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_float_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { + IrInstructionFloatCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_int_to_float(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { + IrInstructionIntToFloat *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_float_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { + IrInstructionFloatToInt *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; } static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *is_signed, IrInstruction *bit_count) { @@ -3957,6 +4011,66 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, truncate, lval); } + case BuiltinFnIdIntCast: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdFloatCast: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdIntToFloat: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdFloatToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } case BuiltinFnIdIntType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -9948,34 +10062,37 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBoolToInt, false); } - // explicit widening or shortening cast - if ((wanted_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdInt) || - (wanted_type->id == TypeTableEntryIdFloat && - actual_type->id == TypeTableEntryIdFloat)) + // explicit widening conversion + if (wanted_type->id == TypeTableEntryIdInt && + actual_type->id == TypeTableEntryIdInt && + wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && + wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count) { return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } - // explicit error set cast - if (wanted_type->id == TypeTableEntryIdErrorSet && - actual_type->id == TypeTableEntryIdErrorSet) + // small enough unsigned ints can get casted to large enough signed ints + if (wanted_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed && + actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed && + wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count) { - return ir_analyze_err_set_cast(ira, source_instr, value, wanted_type); + return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } - // explicit cast from int to float + // explicit float widening conversion if (wanted_type->id == TypeTableEntryIdFloat && - actual_type->id == TypeTableEntryIdInt) + actual_type->id == TypeTableEntryIdFloat && + wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpIntToFloat, false); + return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } - // explicit cast from float to int - if (wanted_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdFloat) + + // explicit error set cast + if (wanted_type->id == TypeTableEntryIdErrorSet && + actual_type->id == TypeTableEntryIdErrorSet) { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpFloatToInt, false); + return ir_analyze_err_set_cast(ira, source_instr, value, wanted_type); } // explicit cast from [N]T to []const T @@ -17365,7 +17482,126 @@ static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruc return dest_type; } - ir_build_truncate_from(&ira->new_irb, &instruction->base, dest_type_value, target); + IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, dest_type_value, target); + ir_link_new_instruction(new_instruction, &instruction->base); + return dest_type; +} + +static TypeTableEntry *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstructionIntCast *instruction) { + TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + if (dest_type->id != TypeTableEntryIdInt) { + ir_add_error(ira, instruction->dest_type, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (target->value.type->id == TypeTableEntryIdComptimeInt) { + if (ir_num_lit_fits_in_other_type(ira, target, dest_type, true)) { + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, + CastOpNumLitToConcrete, false); + if (type_is_invalid(result->value.type)) + return ira->codegen->builtin_types.entry_invalid; + ir_link_new_instruction(result, &instruction->base); + return dest_type; + } else { + return ira->codegen->builtin_types.entry_invalid; + } + } + + if (target->value.type->id != TypeTableEntryIdInt) { + ir_add_error(ira, instruction->target, buf_sprintf("expected integer type, found '%s'", + buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *result = ir_analyze_widen_or_shorten(ira, &instruction->base, target, dest_type); + if (type_is_invalid(result->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + ir_link_new_instruction(result, &instruction->base); + return dest_type; +} + +static TypeTableEntry *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstructionFloatCast *instruction) { + TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + if (dest_type->id != TypeTableEntryIdFloat) { + ir_add_error(ira, instruction->dest_type, + buf_sprintf("expected float type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (target->value.type->id == TypeTableEntryIdComptimeInt || + target->value.type->id == TypeTableEntryIdComptimeFloat) + { + if (ir_num_lit_fits_in_other_type(ira, target, dest_type, true)) { + CastOp op; + if (target->value.type->id == TypeTableEntryIdComptimeInt) { + op = CastOpIntToFloat; + } else { + op = CastOpNumLitToConcrete; + } + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, op, false); + if (type_is_invalid(result->value.type)) + return ira->codegen->builtin_types.entry_invalid; + ir_link_new_instruction(result, &instruction->base); + return dest_type; + } else { + return ira->codegen->builtin_types.entry_invalid; + } + } + + if (target->value.type->id != TypeTableEntryIdFloat) { + ir_add_error(ira, instruction->target, buf_sprintf("expected float type, found '%s'", + buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *result = ir_analyze_widen_or_shorten(ira, &instruction->base, target, dest_type); + if (type_is_invalid(result->value.type)) + return ira->codegen->builtin_types.entry_invalid; + ir_link_new_instruction(result, &instruction->base); + return dest_type; +} + +static TypeTableEntry *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { + TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpIntToFloat, false); + ir_link_new_instruction(result, &instruction->base); + return dest_type; +} + +static TypeTableEntry *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstructionFloatToInt *instruction) { + TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpFloatToInt, false); + ir_link_new_instruction(result, &instruction->base); return dest_type; } @@ -19899,6 +20135,14 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction); case IrInstructionIdTruncate: return ir_analyze_instruction_truncate(ira, (IrInstructionTruncate *)instruction); + case IrInstructionIdIntCast: + return ir_analyze_instruction_int_cast(ira, (IrInstructionIntCast *)instruction); + case IrInstructionIdFloatCast: + return ir_analyze_instruction_float_cast(ira, (IrInstructionFloatCast *)instruction); + case IrInstructionIdIntToFloat: + return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction); + case IrInstructionIdFloatToInt: + return ir_analyze_instruction_float_to_int(ira, (IrInstructionFloatToInt *)instruction); case IrInstructionIdIntType: return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction); case IrInstructionIdBoolNot: @@ -20242,6 +20486,10 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdPromiseResultType: case IrInstructionIdSqrt: case IrInstructionIdAtomicLoad: + case IrInstructionIdIntCast: + case IrInstructionIdFloatCast: + case IrInstructionIdIntToFloat: + case IrInstructionIdFloatToInt: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 43907fa9d4..b5722c52fa 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -648,6 +648,38 @@ static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction) fprintf(irp->f, ")"); } +static void ir_print_int_cast(IrPrint *irp, IrInstructionIntCast *instruction) { + fprintf(irp->f, "@intCast("); + ir_print_other_instruction(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_float_cast(IrPrint *irp, IrInstructionFloatCast *instruction) { + fprintf(irp->f, "@floatCast("); + ir_print_other_instruction(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_int_to_float(IrPrint *irp, IrInstructionIntToFloat *instruction) { + fprintf(irp->f, "@intToFloat("); + ir_print_other_instruction(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_float_to_int(IrPrint *irp, IrInstructionFloatToInt *instruction) { + fprintf(irp->f, "@floatToInt("); + ir_print_other_instruction(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) { fprintf(irp->f, "@IntType("); ir_print_other_instruction(irp, instruction->is_signed); @@ -1417,6 +1449,18 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTruncate: ir_print_truncate(irp, (IrInstructionTruncate *)instruction); break; + case IrInstructionIdIntCast: + ir_print_int_cast(irp, (IrInstructionIntCast *)instruction); + break; + case IrInstructionIdFloatCast: + ir_print_float_cast(irp, (IrInstructionFloatCast *)instruction); + break; + case IrInstructionIdIntToFloat: + ir_print_int_to_float(irp, (IrInstructionIntToFloat *)instruction); + break; + case IrInstructionIdFloatToInt: + ir_print_float_to_int(irp, (IrInstructionFloatToInt *)instruction); + break; case IrInstructionIdIntType: ir_print_int_type(irp, (IrInstructionIntType *)instruction); break; diff --git a/src/main.cpp b/src/main.cpp index c63a143bff..0fe12bb0cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,7 +34,7 @@ static int usage(const char *arg0) { " --assembly [source] add assembly file to build\n" " --cache-dir [path] override the cache directory\n" " --color [auto|off|on] enable or disable colored error messages\n" - " --emit [filetype] emit a specific file format as compilation output\n" + " --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n" " --enable-timing-info print timing diagnostics\n" " --libc-include-dir [path] directory where libc stdlib.h resides\n" " --name [name] override output name\n" diff --git a/std/array_list.zig b/std/array_list.zig index fd1d5cbe26..b71f5be6ab 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -185,23 +185,23 @@ test "basic ArrayList test" { { var i: usize = 0; while (i < 10) : (i += 1) { - list.append(i32(i + 1)) catch unreachable; + list.append(@intCast(i32, i + 1)) catch unreachable; } } { var i: usize = 0; while (i < 10) : (i += 1) { - assert(list.items[i] == i32(i + 1)); + assert(list.items[i] == @intCast(i32, i + 1)); } } for (list.toSlice()) |v, i| { - assert(v == i32(i + 1)); + assert(v == @intCast(i32, i + 1)); } for (list.toSliceConst()) |v, i| { - assert(v == i32(i + 1)); + assert(v == @intCast(i32, i + 1)); } assert(list.pop() == 10); diff --git a/std/base64.zig b/std/base64.zig index d27bcbd201..45c8e22c7e 100644 --- a/std/base64.zig +++ b/std/base64.zig @@ -99,7 +99,7 @@ pub const Base64Decoder = struct { assert(!result.char_in_alphabet[c]); assert(c != pad_char); - result.char_to_index[c] = u8(i); + result.char_to_index[c] = @intCast(u8, i); result.char_in_alphabet[c] = true; } @@ -284,7 +284,7 @@ pub const Base64DecoderUnsafe = struct { }; for (alphabet_chars) |c, i| { assert(c != pad_char); - result.char_to_index[c] = u8(i); + result.char_to_index[c] = @intCast(u8, i); } return result; } diff --git a/std/crypto/blake2.zig b/std/crypto/blake2.zig index f0a9766c00..947133e4cf 100644 --- a/std/crypto/blake2.zig +++ b/std/crypto/blake2.zig @@ -79,7 +79,7 @@ fn Blake2s(comptime out_len: usize) type { mem.copy(u32, d.h[0..], iv[0..]); // No key plus default parameters - d.h[0] ^= 0x01010000 ^ u32(out_len >> 3); + d.h[0] ^= 0x01010000 ^ @intCast(u32, out_len >> 3); d.t = 0; d.buf_len = 0; } @@ -110,7 +110,7 @@ fn Blake2s(comptime out_len: usize) type { // Copy any remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); } pub fn final(d: *Self, out: []u8) void { @@ -144,7 +144,7 @@ fn Blake2s(comptime out_len: usize) type { } v[12] ^= @truncate(u32, d.t); - v[13] ^= u32(d.t >> 32); + v[13] ^= @intCast(u32, d.t >> 32); if (last) v[14] = ~v[14]; const rounds = comptime []RoundParam{ @@ -345,7 +345,7 @@ fn Blake2b(comptime out_len: usize) type { // Copy any remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); } pub fn final(d: *Self, out: []u8) void { @@ -377,7 +377,7 @@ fn Blake2b(comptime out_len: usize) type { } v[12] ^= @truncate(u64, d.t); - v[13] ^= u64(d.t >> 64); + v[13] ^= @intCast(u64, d.t >> 64); if (last) v[14] = ~v[14]; const rounds = comptime []RoundParam{ diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig index c0d1732d37..23fe2313a0 100644 --- a/std/crypto/md5.zig +++ b/std/crypto/md5.zig @@ -78,7 +78,7 @@ pub const Md5 = struct { // Copy any remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); // Md5 uses the bottom 64-bits for length padding d.total_len +%= b.len; @@ -103,9 +103,9 @@ pub const Md5 = struct { // Append message length. var i: usize = 1; var len = d.total_len >> 5; - d.buf[56] = u8(d.total_len & 0x1f) << 3; + d.buf[56] = @intCast(u8, d.total_len & 0x1f) << 3; while (i < 8) : (i += 1) { - d.buf[56 + i] = u8(len & 0xff); + d.buf[56 + i] = @intCast(u8, len & 0xff); len >>= 8; } diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig index 9e46fc9239..5c91590c88 100644 --- a/std/crypto/sha1.zig +++ b/std/crypto/sha1.zig @@ -78,7 +78,7 @@ pub const Sha1 = struct { // Copy any remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); d.total_len += b.len; } @@ -102,9 +102,9 @@ pub const Sha1 = struct { // Append message length. var i: usize = 1; var len = d.total_len >> 5; - d.buf[63] = u8(d.total_len & 0x1f) << 3; + d.buf[63] = @intCast(u8, d.total_len & 0x1f) << 3; while (i < 8) : (i += 1) { - d.buf[63 - i] = u8(len & 0xff); + d.buf[63 - i] = @intCast(u8, len & 0xff); len >>= 8; } diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig index d1375d73e8..d1b915835c 100644 --- a/std/crypto/sha2.zig +++ b/std/crypto/sha2.zig @@ -131,7 +131,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { // Copy any remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); d.total_len += b.len; } @@ -155,9 +155,9 @@ fn Sha2_32(comptime params: Sha2Params32) type { // Append message length. var i: usize = 1; var len = d.total_len >> 5; - d.buf[63] = u8(d.total_len & 0x1f) << 3; + d.buf[63] = @intCast(u8, d.total_len & 0x1f) << 3; while (i < 8) : (i += 1) { - d.buf[63 - i] = u8(len & 0xff); + d.buf[63 - i] = @intCast(u8, len & 0xff); len >>= 8; } @@ -472,7 +472,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { // Copy any remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); d.total_len += b.len; } @@ -496,9 +496,9 @@ fn Sha2_64(comptime params: Sha2Params64) type { // Append message length. var i: usize = 1; var len = d.total_len >> 5; - d.buf[127] = u8(d.total_len & 0x1f) << 3; + d.buf[127] = @intCast(u8, d.total_len & 0x1f) << 3; while (i < 16) : (i += 1) { - d.buf[127 - i] = u8(len & 0xff); + d.buf[127 - i] = @intCast(u8, len & 0xff); len >>= 8; } diff --git a/std/debug/index.zig b/std/debug/index.zig index fb1dac537c..198e0f90f6 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -554,7 +554,7 @@ const LineNumberProgram = struct { const file_name = try os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name); errdefer self.file_entries.allocator.free(file_name); return LineInfo{ - .line = if (self.prev_line >= 0) usize(self.prev_line) else 0, + .line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0, .column = self.prev_column, .file_name = file_name, .allocator = self.file_entries.allocator, @@ -1070,7 +1070,7 @@ fn readULeb128(in_stream: var) !u64 { var operand: u64 = undefined; - if (@shlWithOverflow(u64, byte & 0b01111111, u6(shift), &operand)) return error.InvalidDebugInfo; + if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; result |= operand; @@ -1089,13 +1089,13 @@ fn readILeb128(in_stream: var) !i64 { var operand: i64 = undefined; - if (@shlWithOverflow(i64, byte & 0b01111111, u6(shift), &operand)) return error.InvalidDebugInfo; + if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; result |= operand; shift += 7; if ((byte & 0b10000000) == 0) { - if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << u6(shift)); + if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift)); return result; } } diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index a906b714ab..a5fb692857 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -29,11 +29,11 @@ pub fn roundToPrecision(float_decimal: *FloatDecimal, precision: usize, mode: Ro switch (mode) { RoundMode.Decimal => { if (float_decimal.exp >= 0) { - round_digit = precision + usize(float_decimal.exp); + round_digit = precision + @intCast(usize, float_decimal.exp); } else { // if a small negative exp, then adjust we need to offset by the number // of leading zeros that will occur. - const min_exp_required = usize(-float_decimal.exp); + const min_exp_required = @intCast(usize, -float_decimal.exp); if (precision > min_exp_required) { round_digit = precision - min_exp_required; } @@ -107,16 +107,16 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { // normalize the midpoint const e = math.frexp(val).exponent; - var exp = i16(math.floor(307 + f64(e) * 0.30103)); + var exp = @floatToInt(i16, math.floor(307 + @intToFloat(f64, e) * 0.30103)); if (exp < 20) { exp = 20; - } else if (usize(exp) >= lookup_table.len) { - exp = i16(lookup_table.len - 1); + } else if (@intCast(usize, exp) >= lookup_table.len) { + exp = @intCast(i16, lookup_table.len - 1); } - var mid = lookup_table[usize(exp)]; + var mid = lookup_table[@intCast(usize, exp)]; mid = hpProd(mid, val); - const lten = lookup_table[usize(exp)].val; + const lten = lookup_table[@intCast(usize, exp)].val; exp -= 307; @@ -168,25 +168,25 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { // the 0-index for this extra digit. var buf_index: usize = 1; while (true) { - var hdig = u8(math.floor(high.val)); - if ((high.val == f64(hdig)) and (high.off < 0)) hdig -= 1; + var hdig = @floatToInt(u8, math.floor(high.val)); + if ((high.val == @intToFloat(f64, hdig)) and (high.off < 0)) hdig -= 1; - var ldig = u8(math.floor(low.val)); - if ((low.val == f64(ldig)) and (low.off < 0)) ldig -= 1; + var ldig = @floatToInt(u8, math.floor(low.val)); + if ((low.val == @intToFloat(f64, ldig)) and (low.off < 0)) ldig -= 1; if (ldig != hdig) break; buffer[buf_index] = hdig + '0'; buf_index += 1; - high.val -= f64(hdig); - low.val -= f64(ldig); + high.val -= @intToFloat(f64, hdig); + low.val -= @intToFloat(f64, ldig); hpMul10(&high); hpMul10(&low); } const tmp = (high.val + low.val) / 2.0; - var mdig = u8(math.floor(tmp + 0.5)); - if ((f64(mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1; + var mdig = @floatToInt(u8, math.floor(tmp + 0.5)); + if ((@intToFloat(f64, mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1; buffer[buf_index] = mdig + '0'; buf_index += 1; @@ -304,7 +304,7 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { assert((val > 9.007199254740992e15) and val < (3.40282366920938e38)); - var mid = u128(val); + var mid = @floatToInt(u128, val); var low: u128 = mid - fpeint((fpnext(val) - val) / 2.0); var high: u128 = mid + fpeint((val - fpprev(val)) / 2.0); @@ -314,11 +314,11 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { low -= 1; } - var l64 = u64(low % pow19); - const lf = u64((low / pow19) % pow19); + var l64 = @intCast(u64, low % pow19); + const lf = @intCast(u64, (low / pow19) % pow19); - var h64 = u64(high % pow19); - const hf = u64((high / pow19) % pow19); + var h64 = @intCast(u64, high % pow19); + const hf = @intCast(u64, (high / pow19) % pow19); if (lf != hf) { l64 = lf; @@ -348,7 +348,7 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { return FloatDecimal{ .digits = buffer[0..buf_index], - .exp = i32(buf_index) + mi, + .exp = @intCast(i32, buf_index) + mi, }; } @@ -359,33 +359,33 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { fn errolFixed(val: f64, buffer: []u8) FloatDecimal { assert((val >= 16.0) and (val < 9.007199254740992e15)); - const u = u64(val); - const n = f64(u); + const u = @floatToInt(u64, val); + const n = @intToFloat(f64, u); var mid = val - n; var lo = ((fpprev(val) - n) + mid) / 2.0; var hi = ((fpnext(val) - n) + mid) / 2.0; var buf_index = u64toa(u, buffer); - var exp = i32(buf_index); + var exp = @intCast(i32, buf_index); var j = buf_index; buffer[j] = 0; if (mid != 0.0) { while (mid != 0.0) { lo *= 10.0; - const ldig = i32(lo); - lo -= f64(ldig); + const ldig = @floatToInt(i32, lo); + lo -= @intToFloat(f64, ldig); mid *= 10.0; - const mdig = i32(mid); - mid -= f64(mdig); + const mdig = @floatToInt(i32, mid); + mid -= @intToFloat(f64, mdig); hi *= 10.0; - const hdig = i32(hi); - hi -= f64(hdig); + const hdig = @floatToInt(i32, hi); + hi -= @intToFloat(f64, hdig); - buffer[j] = u8(mdig + '0'); + buffer[j] = @intCast(u8, mdig + '0'); j += 1; if (hdig != ldig or j > 50) break; @@ -452,7 +452,7 @@ fn u64toa(value_param: u64, buffer: []u8) usize { var buf_index: usize = 0; if (value < kTen8) { - const v = u32(value); + const v = @intCast(u32, value); if (v < 10000) { const d1: u32 = (v / 100) << 1; const d2: u32 = (v % 100) << 1; @@ -507,8 +507,8 @@ fn u64toa(value_param: u64, buffer: []u8) usize { buf_index += 1; } } else if (value < kTen16) { - const v0: u32 = u32(value / kTen8); - const v1: u32 = u32(value % kTen8); + const v0: u32 = @intCast(u32, value / kTen8); + const v1: u32 = @intCast(u32, value % kTen8); const b0: u32 = v0 / 10000; const c0: u32 = v0 % 10000; @@ -578,11 +578,11 @@ fn u64toa(value_param: u64, buffer: []u8) usize { buffer[buf_index] = c_digits_lut[d8 + 1]; buf_index += 1; } else { - const a = u32(value / kTen16); // 1 to 1844 + const a = @intCast(u32, value / kTen16); // 1 to 1844 value %= kTen16; if (a < 10) { - buffer[buf_index] = '0' + u8(a); + buffer[buf_index] = '0' + @intCast(u8, a); buf_index += 1; } else if (a < 100) { const i: u32 = a << 1; @@ -591,7 +591,7 @@ fn u64toa(value_param: u64, buffer: []u8) usize { buffer[buf_index] = c_digits_lut[i + 1]; buf_index += 1; } else if (a < 1000) { - buffer[buf_index] = '0' + u8(a / 100); + buffer[buf_index] = '0' + @intCast(u8, a / 100); buf_index += 1; const i: u32 = (a % 100) << 1; @@ -612,8 +612,8 @@ fn u64toa(value_param: u64, buffer: []u8) usize { buf_index += 1; } - const v0 = u32(value / kTen8); - const v1 = u32(value % kTen8); + const v0 = @intCast(u32, value / kTen8); + const v1 = @intCast(u32, value % kTen8); const b0: u32 = v0 / 10000; const c0: u32 = v0 % 10000; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 90d3a559c4..f4dfa0e324 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -5,6 +5,7 @@ const assert = debug.assert; const mem = std.mem; const builtin = @import("builtin"); const errol = @import("errol/index.zig"); +const lossyCast = std.math.lossyCast; const max_int_digits = 65; @@ -463,7 +464,7 @@ pub fn formatFloatDecimal( errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); // exp < 0 means the leading is always 0 as errol result is normalized. - var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0; + var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); @@ -492,7 +493,7 @@ pub fn formatFloatDecimal( // Zero-fill until we reach significant digits or run out of precision. if (float_decimal.exp <= 0) { - const zero_digit_count = usize(-float_decimal.exp); + const zero_digit_count = @intCast(usize, -float_decimal.exp); const zeros_to_print = math.min(zero_digit_count, precision); var i: usize = 0; @@ -521,7 +522,7 @@ pub fn formatFloatDecimal( } } else { // exp < 0 means the leading is always 0 as errol result is normalized. - var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0; + var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); @@ -547,7 +548,7 @@ pub fn formatFloatDecimal( // Zero-fill until we reach significant digits or run out of precision. if (float_decimal.exp < 0) { - const zero_digit_count = usize(-float_decimal.exp); + const zero_digit_count = @intCast(usize, -float_decimal.exp); var i: usize = 0; while (i < zero_digit_count) : (i += 1) { @@ -578,7 +579,7 @@ pub fn formatBytes( 1024 => math.min(math.log2(value) / 10, mags_iec.len - 1), else => unreachable, }; - const new_value = f64(value) / math.pow(f64, f64(radix), f64(magnitude)); + const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude)); const suffix = switch (radix) { 1000 => mags_si[magnitude], 1024 => mags_iec[magnitude], @@ -628,15 +629,15 @@ fn formatIntSigned( if (value < 0) { const minus_sign: u8 = '-'; try output(context, (*[1]u8)(&minus_sign)[0..]); - const new_value = uint(-(value + 1)) + 1; + const new_value = @intCast(uint, -(value + 1)) + 1; const new_width = if (width == 0) 0 else (width - 1); return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); } else if (width == 0) { - return formatIntUnsigned(uint(value), base, uppercase, width, context, Errors, output); + return formatIntUnsigned(@intCast(uint, value), base, uppercase, width, context, Errors, output); } else { const plus_sign: u8 = '+'; try output(context, (*[1]u8)(&plus_sign)[0..]); - const new_value = uint(value); + const new_value = @intCast(uint, value); const new_width = if (width == 0) 0 else (width - 1); return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); } @@ -660,7 +661,7 @@ fn formatIntUnsigned( while (true) { const digit = a % base; index -= 1; - buf[index] = digitToChar(u8(digit), uppercase); + buf[index] = digitToChar(@intCast(u8, digit), uppercase); a /= base; if (a == 0) break; } diff --git a/std/hash/crc.zig b/std/hash/crc.zig index ec831cdc2e..c455140785 100644 --- a/std/hash/crc.zig +++ b/std/hash/crc.zig @@ -26,7 +26,7 @@ pub fn Crc32WithPoly(comptime poly: u32) type { var tables: [8][256]u32 = undefined; for (tables[0]) |*e, i| { - var crc = u32(i); + var crc = @intCast(u32, i); var j: usize = 0; while (j < 8) : (j += 1) { if (crc & 1 == 1) { @@ -122,7 +122,7 @@ pub fn Crc32SmallWithPoly(comptime poly: u32) type { var table: [16]u32 = undefined; for (table) |*e, i| { - var crc = u32(i * 16); + var crc = @intCast(u32, i * 16); var j: usize = 0; while (j < 8) : (j += 1) { if (crc & 1 == 1) { diff --git a/std/hash/siphash.zig b/std/hash/siphash.zig index 8a90308a46..cdad77e59e 100644 --- a/std/hash/siphash.zig +++ b/std/hash/siphash.zig @@ -81,7 +81,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) // Remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); d.msg_len +%= @truncate(u8, b.len); } @@ -233,7 +233,7 @@ test "siphash64-2-4 sanity" { var buffer: [64]u8 = undefined; for (vectors) |vector, i| { - buffer[i] = u8(i); + buffer[i] = @intCast(u8, i); const expected = mem.readInt(vector, u64, Endian.Little); debug.assert(siphash.hash(test_key, buffer[0..i]) == expected); @@ -312,7 +312,7 @@ test "siphash128-2-4 sanity" { var buffer: [64]u8 = undefined; for (vectors) |vector, i| { - buffer[i] = u8(i); + buffer[i] = @intCast(u8, i); const expected = mem.readInt(vector, u128, Endian.Little); debug.assert(siphash.hash(test_key, buffer[0..i]) == expected); diff --git a/std/heap.zig b/std/heap.zig index 172bc24118..2a2c8c0b59 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -408,7 +408,7 @@ fn testAllocator(allocator: *mem.Allocator) !void { for (slice) |*item, i| { item.* = try allocator.create(i32); - item.*.* = i32(i); + item.*.* = @intCast(i32, i); } for (slice) |item, i| { diff --git a/std/json.zig b/std/json.zig index 8bbee981e3..2930cd21bb 100644 --- a/std/json.zig +++ b/std/json.zig @@ -180,7 +180,7 @@ pub const StreamingParser = struct { pub fn fromInt(x: var) State { debug.assert(x == 0 or x == 1); const T = @TagType(State); - return State(T(x)); + return State(@intCast(T, x)); } }; diff --git a/std/math/acos.zig b/std/math/acos.zig index 284f73fc91..54844e8f6e 100644 --- a/std/math/acos.zig +++ b/std/math/acos.zig @@ -95,12 +95,12 @@ fn acos64(x: f64) f64 { const pio2_lo: f64 = 6.12323399573676603587e-17; const ux = @bitCast(u64, x); - const hx = u32(ux >> 32); + const hx = @intCast(u32, ux >> 32); const ix = hx & 0x7FFFFFFF; // |x| >= 1 or nan if (ix >= 0x3FF00000) { - const lx = u32(ux & 0xFFFFFFFF); + const lx = @intCast(u32, ux & 0xFFFFFFFF); // acos(1) = 0, acos(-1) = pi if ((ix - 0x3FF00000) | lx == 0) { diff --git a/std/math/asin.zig b/std/math/asin.zig index 2ee3aa6048..30b3a57e32 100644 --- a/std/math/asin.zig +++ b/std/math/asin.zig @@ -87,12 +87,12 @@ fn asin64(x: f64) f64 { const pio2_lo: f64 = 6.12323399573676603587e-17; const ux = @bitCast(u64, x); - const hx = u32(ux >> 32); + const hx = @intCast(u32, ux >> 32); const ix = hx & 0x7FFFFFFF; // |x| >= 1 or nan if (ix >= 0x3FF00000) { - const lx = u32(ux & 0xFFFFFFFF); + const lx = @intCast(u32, ux & 0xFFFFFFFF); // asin(1) = +-pi/2 with inexact if ((ix - 0x3FF00000) | lx == 0) { diff --git a/std/math/atan.zig b/std/math/atan.zig index 7eceef98e3..6ca94dd84a 100644 --- a/std/math/atan.zig +++ b/std/math/atan.zig @@ -138,7 +138,7 @@ fn atan64(x_: f64) f64 { var x = x_; var ux = @bitCast(u64, x); - var ix = u32(ux >> 32); + var ix = @intCast(u32, ux >> 32); const sign = ix >> 31; ix &= 0x7FFFFFFF; @@ -159,7 +159,7 @@ fn atan64(x_: f64) f64 { // |x| < 2^(-27) if (ix < 0x3E400000) { if (ix < 0x00100000) { - math.forceEval(f32(x)); + math.forceEval(@floatCast(f32, x)); } return x; } diff --git a/std/math/atan2.zig b/std/math/atan2.zig index f0a8e67c46..b3e45ba045 100644 --- a/std/math/atan2.zig +++ b/std/math/atan2.zig @@ -124,12 +124,12 @@ fn atan2_64(y: f64, x: f64) f64 { } var ux = @bitCast(u64, x); - var ix = u32(ux >> 32); - var lx = u32(ux & 0xFFFFFFFF); + var ix = @intCast(u32, ux >> 32); + var lx = @intCast(u32, ux & 0xFFFFFFFF); var uy = @bitCast(u64, y); - var iy = u32(uy >> 32); - var ly = u32(uy & 0xFFFFFFFF); + var iy = @intCast(u32, uy >> 32); + var ly = @intCast(u32, uy & 0xFFFFFFFF); // x = 1.0 if ((ix -% 0x3FF00000) | lx == 0) { diff --git a/std/math/atanh.zig b/std/math/atanh.zig index 8ca0cc85bc..4ae8a66bc0 100644 --- a/std/math/atanh.zig +++ b/std/math/atanh.zig @@ -62,7 +62,7 @@ fn atanh_64(x: f64) f64 { if (e < 0x3FF - 32) { // underflow if (e == 0) { - math.forceEval(f32(y)); + math.forceEval(@floatCast(f32, y)); } } // |x| < 0.5 diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 5e15cfb895..21d9347c01 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -135,7 +135,7 @@ pub const Int = struct { self.positive = value >= 0; self.len = 0; - var w_value: UT = if (value < 0) UT(-value) else UT(value); + var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value); if (info.bits <= Limb.bit_count) { self.limbs[0] = Limb(w_value); @@ -198,7 +198,7 @@ pub const Int = struct { var r: UT = 0; if (@sizeOf(UT) <= @sizeOf(Limb)) { - r = UT(self.limbs[0]); + r = @intCast(UT, self.limbs[0]); } else { for (self.limbs[0..self.len]) |_, ri| { const limb = self.limbs[self.len - ri - 1]; @@ -210,7 +210,7 @@ pub const Int = struct { if (!T.is_signed) { return if (self.positive) r else error.NegativeIntoUnsigned; } else { - return if (self.positive) T(r) else -T(r); + return if (self.positive) @intCast(T, r) else -@intCast(T, r); } }, else => { @@ -295,7 +295,7 @@ pub const Int = struct { for (self.limbs[0..self.len]) |limb| { var shift: usize = 0; while (shift < Limb.bit_count) : (shift += base_shift) { - const r = u8((limb >> Log2Limb(shift)) & Limb(base - 1)); + const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & Limb(base - 1)); const ch = try digitToChar(r, base); try digits.append(ch); } @@ -329,7 +329,7 @@ pub const Int = struct { var r_word = r.limbs[0]; var i: usize = 0; while (i < digits_per_limb) : (i += 1) { - const ch = try digitToChar(u8(r_word % base), base); + const ch = try digitToChar(@intCast(u8, r_word % base), base); r_word /= base; try digits.append(ch); } @@ -340,7 +340,7 @@ pub const Int = struct { var r_word = q.limbs[0]; while (r_word != 0) { - const ch = try digitToChar(u8(r_word % base), base); + const ch = try digitToChar(@intCast(u8, r_word % base), base); r_word /= base; try digits.append(ch); } @@ -801,7 +801,7 @@ pub const Int = struct { q.limbs[i - t - 1] = @maxValue(Limb); } else { const num = (DoubleLimb(x.limbs[i]) << Limb.bit_count) | DoubleLimb(x.limbs[i - 1]); - const z = Limb(num / DoubleLimb(y.limbs[t])); + const z = @intCast(Limb, num / DoubleLimb(y.limbs[t])); q.limbs[i - t - 1] = if (z > @maxValue(Limb)) @maxValue(Limb) else Limb(z); } @@ -860,7 +860,7 @@ pub const Int = struct { debug.assert(r.len >= a.len + (shift / Limb.bit_count) + 1); const limb_shift = shift / Limb.bit_count + 1; - const interior_limb_shift = Log2Limb(shift % Limb.bit_count); + const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count); var carry: Limb = 0; var i: usize = 0; @@ -869,7 +869,7 @@ pub const Int = struct { const dst_i = src_i + limb_shift; const src_digit = a[src_i]; - r[dst_i] = carry | @inlineCall(math.shr, Limb, src_digit, Limb.bit_count - Limb(interior_limb_shift)); + r[dst_i] = carry | @inlineCall(math.shr, Limb, src_digit, Limb.bit_count - @intCast(Limb, interior_limb_shift)); carry = (src_digit << interior_limb_shift); } @@ -898,7 +898,7 @@ pub const Int = struct { debug.assert(r.len >= a.len - (shift / Limb.bit_count)); const limb_shift = shift / Limb.bit_count; - const interior_limb_shift = Log2Limb(shift % Limb.bit_count); + const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count); var carry: Limb = 0; var i: usize = 0; @@ -908,7 +908,7 @@ pub const Int = struct { const src_digit = a[src_i]; r[dst_i] = carry | (src_digit >> interior_limb_shift); - carry = @inlineCall(math.shl, Limb, src_digit, Limb.bit_count - Limb(interior_limb_shift)); + carry = @inlineCall(math.shl, Limb, src_digit, Limb.bit_count - @intCast(Limb, interior_limb_shift)); } } diff --git a/std/math/cbrt.zig b/std/math/cbrt.zig index cd3b71ca8a..c067c5155a 100644 --- a/std/math/cbrt.zig +++ b/std/math/cbrt.zig @@ -54,7 +54,7 @@ fn cbrt32(x: f32) f32 { r = t * t * t; t = t * (f64(x) + x + r) / (x + r + r); - return f32(t); + return @floatCast(f32, t); } fn cbrt64(x: f64) f64 { @@ -69,7 +69,7 @@ fn cbrt64(x: f64) f64 { const P4: f64 = 0.145996192886612446982; var u = @bitCast(u64, x); - var hx = u32(u >> 32) & 0x7FFFFFFF; + var hx = @intCast(u32, u >> 32) & 0x7FFFFFFF; // cbrt(nan, inf) = itself if (hx >= 0x7FF00000) { @@ -79,7 +79,7 @@ fn cbrt64(x: f64) f64 { // cbrt to ~5bits if (hx < 0x00100000) { u = @bitCast(u64, x * 0x1.0p54); - hx = u32(u >> 32) & 0x7FFFFFFF; + hx = @intCast(u32, u >> 32) & 0x7FFFFFFF; // cbrt(0) is itself if (hx == 0) { diff --git a/std/math/ceil.zig b/std/math/ceil.zig index a189bb66d2..1c429504e8 100644 --- a/std/math/ceil.zig +++ b/std/math/ceil.zig @@ -20,7 +20,7 @@ pub fn ceil(x: var) @typeOf(x) { fn ceil32(x: f32) f32 { var u = @bitCast(u32, x); - var e = i32((u >> 23) & 0xFF) - 0x7F; + var e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F; var m: u32 = undefined; // TODO: Shouldn't need this explicit check. @@ -31,7 +31,7 @@ fn ceil32(x: f32) f32 { if (e >= 23) { return x; } else if (e >= 0) { - m = u32(0x007FFFFF) >> u5(e); + m = u32(0x007FFFFF) >> @intCast(u5, e); if (u & m == 0) { return x; } diff --git a/std/math/complex/atan.zig b/std/math/complex/atan.zig index 9bfe5fe724..de60f2546d 100644 --- a/std/math/complex/atan.zig +++ b/std/math/complex/atan.zig @@ -4,7 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; -pub fn atan(z: var) Complex(@typeOf(z.re)) { +pub fn atan(z: var) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { f32 => atan32(z), @@ -25,11 +25,11 @@ fn redupif32(x: f32) f32 { t -= 0.5; } - const u = f32(i32(t)); + const u = @intToFloat(f32, @floatToInt(i32, t)); return ((x - u * DP1) - u * DP2) - t * DP3; } -fn atan32(z: *const Complex(f32)) Complex(f32) { +fn atan32(z: Complex(f32)) Complex(f32) { const maxnum = 1.0e38; const x = z.re; @@ -74,11 +74,11 @@ fn redupif64(x: f64) f64 { t -= 0.5; } - const u = f64(i64(t)); + const u = @intToFloat(f64, @floatToInt(i64, t)); return ((x - u * DP1) - u * DP2) - t * DP3; } -fn atan64(z: *const Complex(f64)) Complex(f64) { +fn atan64(z: Complex(f64)) Complex(f64) { const maxnum = 1.0e308; const x = z.re; diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig index c2f9a47b8d..a2e31631ea 100644 --- a/std/math/complex/cosh.zig +++ b/std/math/complex/cosh.zig @@ -83,12 +83,12 @@ fn cosh64(z: *const Complex(f64)) Complex(f64) { const y = z.im; const fx = @bitCast(u64, x); - const hx = u32(fx >> 32); + const hx = @intCast(u32, fx >> 32); const lx = @truncate(u32, fx); const ix = hx & 0x7fffffff; const fy = @bitCast(u64, y); - const hy = u32(fy >> 32); + const hy = @intCast(u32, fy >> 32); const ly = @truncate(u32, fy); const iy = hy & 0x7fffffff; diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig index 44c354f246..48fb132d97 100644 --- a/std/math/complex/exp.zig +++ b/std/math/complex/exp.zig @@ -6,7 +6,7 @@ const Complex = cmath.Complex; const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; -pub fn exp(z: var) Complex(@typeOf(z.re)) { +pub fn exp(z: var) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { @@ -16,7 +16,7 @@ pub fn exp(z: var) Complex(@typeOf(z.re)) { }; } -fn exp32(z: *const Complex(f32)) Complex(f32) { +fn exp32(z: Complex(f32)) Complex(f32) { @setFloatMode(this, @import("builtin").FloatMode.Strict); const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955 @@ -63,7 +63,7 @@ fn exp32(z: *const Complex(f32)) Complex(f32) { } } -fn exp64(z: *const Complex(f64)) Complex(f64) { +fn exp64(z: Complex(f64)) Complex(f64) { const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710 const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2 diff --git a/std/math/complex/ldexp.zig b/std/math/complex/ldexp.zig index a56c2ef2eb..e919ef6bec 100644 --- a/std/math/complex/ldexp.zig +++ b/std/math/complex/ldexp.zig @@ -4,7 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; -pub fn ldexp_cexp(z: var, expt: i32) Complex(@typeOf(z.re)) { +pub fn ldexp_cexp(z: var, expt: i32) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { @@ -20,11 +20,12 @@ fn frexp_exp32(x: f32, expt: *i32) f32 { const exp_x = math.exp(x - kln2); const hx = @bitCast(u32, exp_x); - expt.* = i32(hx >> 23) - (0x7f + 127) + k; + // TODO zig should allow this cast implicitly because it should know the value is in range + expt.* = @intCast(i32, hx >> 23) - (0x7f + 127) + k; return @bitCast(f32, (hx & 0x7fffff) | ((0x7f + 127) << 23)); } -fn ldexp_cexp32(z: *const Complex(f32), expt: i32) Complex(f32) { +fn ldexp_cexp32(z: Complex(f32), expt: i32) Complex(f32) { var ex_expt: i32 = undefined; const exp_x = frexp_exp32(z.re, &ex_expt); const exptf = expt + ex_expt; @@ -45,16 +46,16 @@ fn frexp_exp64(x: f64, expt: *i32) f64 { const exp_x = math.exp(x - kln2); const fx = @bitCast(u64, x); - const hx = u32(fx >> 32); + const hx = @intCast(u32, fx >> 32); const lx = @truncate(u32, fx); - expt.* = i32(hx >> 20) - (0x3ff + 1023) + k; + expt.* = @intCast(i32, hx >> 20) - (0x3ff + 1023) + k; const high_word = (hx & 0xfffff) | ((0x3ff + 1023) << 20); return @bitCast(f64, (u64(high_word) << 32) | lx); } -fn ldexp_cexp64(z: *const Complex(f64), expt: i32) Complex(f64) { +fn ldexp_cexp64(z: Complex(f64), expt: i32) Complex(f64) { var ex_expt: i32 = undefined; const exp_x = frexp_exp64(z.re, &ex_expt); const exptf = i64(expt + ex_expt); diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig index 3d196bfd50..ab23c5c74d 100644 --- a/std/math/complex/sinh.zig +++ b/std/math/complex/sinh.zig @@ -6,7 +6,7 @@ const Complex = cmath.Complex; const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; -pub fn sinh(z: var) Complex(@typeOf(z.re)) { +pub fn sinh(z: var) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { f32 => sinh32(z), @@ -15,7 +15,7 @@ pub fn sinh(z: var) Complex(@typeOf(z.re)) { }; } -fn sinh32(z: *const Complex(f32)) Complex(f32) { +fn sinh32(z: Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -78,17 +78,17 @@ fn sinh32(z: *const Complex(f32)) Complex(f32) { return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y)); } -fn sinh64(z: *const Complex(f64)) Complex(f64) { +fn sinh64(z: Complex(f64)) Complex(f64) { const x = z.re; const y = z.im; const fx = @bitCast(u64, x); - const hx = u32(fx >> 32); + const hx = @intCast(u32, fx >> 32); const lx = @truncate(u32, fx); const ix = hx & 0x7fffffff; const fy = @bitCast(u64, y); - const hy = u32(fy >> 32); + const hy = @intCast(u32, fy >> 32); const ly = @truncate(u32, fy); const iy = hy & 0x7fffffff; diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig index caa3bd2868..47367816f7 100644 --- a/std/math/complex/sqrt.zig +++ b/std/math/complex/sqrt.zig @@ -49,10 +49,16 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { if (dx >= 0) { const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5); - return Complex(f32).new(f32(t), f32(dy / (2.0 * t))); + return Complex(f32).new( + @floatCast(f32, t), + @floatCast(f32, dy / (2.0 * t)), + ); } else { const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); - return Complex(f32).new(f32(math.fabs(y) / (2.0 * t)), f32(math.copysign(f64, t, y))); + return Complex(f32).new( + @floatCast(f32, math.fabs(y) / (2.0 * t)), + @floatCast(f32, math.copysign(f64, t, y)), + ); } } diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig index 1d754838a3..e48d438783 100644 --- a/std/math/complex/tanh.zig +++ b/std/math/complex/tanh.zig @@ -4,7 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; -pub fn tanh(z: var) Complex(@typeOf(z.re)) { +pub fn tanh(z: var) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { f32 => tanh32(z), @@ -13,7 +13,7 @@ pub fn tanh(z: var) Complex(@typeOf(z.re)) { }; } -fn tanh32(z: *const Complex(f32)) Complex(f32) { +fn tanh32(z: Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -51,12 +51,14 @@ fn tanh32(z: *const Complex(f32)) Complex(f32) { return Complex(f32).new((beta * rho * s) / den, t / den); } -fn tanh64(z: *const Complex(f64)) Complex(f64) { +fn tanh64(z: Complex(f64)) Complex(f64) { const x = z.re; const y = z.im; const fx = @bitCast(u64, x); - const hx = u32(fx >> 32); + // TODO: zig should allow this conversion implicitly because it can notice that the value necessarily + // fits in range. + const hx = @intCast(u32, fx >> 32); const lx = @truncate(u32, fx); const ix = hx & 0x7fffffff; diff --git a/std/math/cos.zig b/std/math/cos.zig index 5e5ec4f1cb..71d5e4a8f6 100644 --- a/std/math/cos.zig +++ b/std/math/cos.zig @@ -55,7 +55,7 @@ fn cos32(x_: f32) f32 { } var y = math.floor(x * m4pi); - var j = i64(y); + var j = @floatToInt(i64, y); if (j & 1 == 1) { j += 1; @@ -106,7 +106,7 @@ fn cos64(x_: f64) f64 { } var y = math.floor(x * m4pi); - var j = i64(y); + var j = @floatToInt(i64, y); if (j & 1 == 1) { j += 1; diff --git a/std/math/cosh.zig b/std/math/cosh.zig index fa46219986..52beafb642 100644 --- a/std/math/cosh.zig +++ b/std/math/cosh.zig @@ -49,7 +49,7 @@ fn cosh32(x: f32) f32 { fn cosh64(x: f64) f64 { const u = @bitCast(u64, x); - const w = u32(u >> 32); + const w = @intCast(u32, u >> 32); const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); // TODO: Shouldn't need this explicit check. diff --git a/std/math/exp.zig b/std/math/exp.zig index 76dc47d04b..d6185d4f0b 100644 --- a/std/math/exp.zig +++ b/std/math/exp.zig @@ -29,7 +29,7 @@ fn exp32(x_: f32) f32 { var x = x_; var hx = @bitCast(u32, x); - const sign = i32(hx >> 31); + const sign = @intCast(i32, hx >> 31); hx &= 0x7FFFFFFF; if (math.isNan(x)) { @@ -63,12 +63,12 @@ fn exp32(x_: f32) f32 { if (hx > 0x3EB17218) { // |x| > 1.5 * ln2 if (hx > 0x3F851592) { - k = i32(invln2 * x + half[usize(sign)]); + k = @floatToInt(i32, invln2 * x + half[@intCast(usize, sign)]); } else { k = 1 - sign - sign; } - const fk = f32(k); + const fk = @intToFloat(f32, k); hi = x - fk * ln2hi; lo = fk * ln2lo; x = hi - lo; @@ -110,7 +110,7 @@ fn exp64(x_: f64) f64 { var x = x_; var ux = @bitCast(u64, x); var hx = ux >> 32; - const sign = i32(hx >> 31); + const sign = @intCast(i32, hx >> 31); hx &= 0x7FFFFFFF; if (math.isNan(x)) { @@ -148,12 +148,12 @@ fn exp64(x_: f64) f64 { if (hx > 0x3EB17218) { // |x| >= 1.5 * ln2 if (hx > 0x3FF0A2B2) { - k = i32(invln2 * x + half[usize(sign)]); + k = @floatToInt(i32, invln2 * x + half[@intCast(usize, sign)]); } else { k = 1 - sign - sign; } - const dk = f64(k); + const dk = @intToFloat(f64, k); hi = x - dk * ln2hi; lo = dk * ln2lo; x = hi - lo; diff --git a/std/math/exp2.zig b/std/math/exp2.zig index 62a3eb85b6..90ea088181 100644 --- a/std/math/exp2.zig +++ b/std/math/exp2.zig @@ -38,8 +38,8 @@ const exp2ft = []const f64{ fn exp2_32(x: f32) f32 { @setFloatMode(this, @import("builtin").FloatMode.Strict); - const tblsiz = u32(exp2ft.len); - const redux: f32 = 0x1.8p23 / f32(tblsiz); + const tblsiz = @intCast(u32, exp2ft.len); + const redux: f32 = 0x1.8p23 / @intToFloat(f32, tblsiz); const P1: f32 = 0x1.62e430p-1; const P2: f32 = 0x1.ebfbe0p-3; const P3: f32 = 0x1.c6b348p-5; @@ -89,7 +89,7 @@ fn exp2_32(x: f32) f32 { var r: f64 = exp2ft[i0]; const t: f64 = r * z; r = r + t * (P1 + z * P2) + t * (z * z) * (P3 + z * P4); - return f32(r * uk); + return @floatCast(f32, r * uk); } const exp2dt = []f64{ @@ -355,8 +355,8 @@ const exp2dt = []f64{ fn exp2_64(x: f64) f64 { @setFloatMode(this, @import("builtin").FloatMode.Strict); - const tblsiz = u32(exp2dt.len / 2); - const redux: f64 = 0x1.8p52 / f64(tblsiz); + const tblsiz = @intCast(u32, exp2dt.len / 2); + const redux: f64 = 0x1.8p52 / @intToFloat(f64, tblsiz); const P1: f64 = 0x1.62e42fefa39efp-1; const P2: f64 = 0x1.ebfbdff82c575p-3; const P3: f64 = 0x1.c6b08d704a0a6p-5; @@ -364,7 +364,7 @@ fn exp2_64(x: f64) f64 { const P5: f64 = 0x1.5d88003875c74p-10; const ux = @bitCast(u64, x); - const ix = u32(ux >> 32) & 0x7FFFFFFF; + const ix = @intCast(u32, ux >> 32) & 0x7FFFFFFF; // TODO: This should be handled beneath. if (math.isNan(x)) { @@ -386,7 +386,7 @@ fn exp2_64(x: f64) f64 { if (ux >> 63 != 0) { // underflow if (x <= -1075 or x - 0x1.0p52 + 0x1.0p52 != x) { - math.forceEval(f32(-0x1.0p-149 / x)); + math.forceEval(@floatCast(f32, -0x1.0p-149 / x)); } if (x <= -1075) { return 0; diff --git a/std/math/expm1.zig b/std/math/expm1.zig index 11af1b215f..438e44ccce 100644 --- a/std/math/expm1.zig +++ b/std/math/expm1.zig @@ -78,8 +78,8 @@ fn expm1_32(x_: f32) f32 { kf += 0.5; } - k = i32(kf); - const t = f32(k); + k = @floatToInt(i32, kf); + const t = @intToFloat(f32, k); hi = x - t * ln2_hi; lo = t * ln2_lo; } @@ -123,7 +123,7 @@ fn expm1_32(x_: f32) f32 { } } - const twopk = @bitCast(f32, u32((0x7F +% k) << 23)); + const twopk = @bitCast(f32, @intCast(u32, (0x7F +% k) << 23)); if (k < 0 or k > 56) { var y = x - e + 1.0; @@ -136,7 +136,7 @@ fn expm1_32(x_: f32) f32 { return y - 1.0; } - const uf = @bitCast(f32, u32(0x7F -% k) << 23); + const uf = @bitCast(f32, @intCast(u32, 0x7F -% k) << 23); if (k < 23) { return (x - e + (1 - uf)) * twopk; } else { @@ -158,7 +158,7 @@ fn expm1_64(x_: f64) f64 { var x = x_; const ux = @bitCast(u64, x); - const hx = u32(ux >> 32) & 0x7FFFFFFF; + const hx = @intCast(u32, ux >> 32) & 0x7FFFFFFF; const sign = ux >> 63; if (math.isNegativeInf(x)) { @@ -207,8 +207,8 @@ fn expm1_64(x_: f64) f64 { kf += 0.5; } - k = i32(kf); - const t = f64(k); + k = @floatToInt(i32, kf); + const t = @intToFloat(f64, k); hi = x - t * ln2_hi; lo = t * ln2_lo; } @@ -219,7 +219,7 @@ fn expm1_64(x_: f64) f64 { // |x| < 2^(-54) else if (hx < 0x3C900000) { if (hx < 0x00100000) { - math.forceEval(f32(x)); + math.forceEval(@floatCast(f32, x)); } return x; } else { @@ -252,7 +252,7 @@ fn expm1_64(x_: f64) f64 { } } - const twopk = @bitCast(f64, u64(0x3FF +% k) << 52); + const twopk = @bitCast(f64, @intCast(u64, 0x3FF +% k) << 52); if (k < 0 or k > 56) { var y = x - e + 1.0; @@ -265,7 +265,7 @@ fn expm1_64(x_: f64) f64 { return y - 1.0; } - const uf = @bitCast(f64, u64(0x3FF -% k) << 52); + const uf = @bitCast(f64, @intCast(u64, 0x3FF -% k) << 52); if (k < 20) { return (x - e + (1 - uf)) * twopk; } else { diff --git a/std/math/floor.zig b/std/math/floor.zig index 7d5364787f..79d1097d08 100644 --- a/std/math/floor.zig +++ b/std/math/floor.zig @@ -20,7 +20,7 @@ pub fn floor(x: var) @typeOf(x) { fn floor32(x: f32) f32 { var u = @bitCast(u32, x); - const e = i32((u >> 23) & 0xFF) - 0x7F; + const e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F; var m: u32 = undefined; // TODO: Shouldn't need this explicit check. @@ -33,7 +33,7 @@ fn floor32(x: f32) f32 { } if (e >= 0) { - m = u32(0x007FFFFF) >> u5(e); + m = u32(0x007FFFFF) >> @intCast(u5, e); if (u & m == 0) { return x; } diff --git a/std/math/fma.zig b/std/math/fma.zig index 3e9214f35b..21faf4118d 100644 --- a/std/math/fma.zig +++ b/std/math/fma.zig @@ -17,10 +17,10 @@ fn fma32(x: f32, y: f32, z: f32) f32 { const e = (u >> 52) & 0x7FF; if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or xy_z - xy == z) { - return f32(xy_z); + return @floatCast(f32, xy_z); } else { // TODO: Handle inexact case with double-rounding - return f32(xy_z); + return @floatCast(f32, xy_z); } } @@ -124,7 +124,7 @@ fn add_and_denorm(a: f64, b: f64, scale: i32) f64 { var sum = dd_add(a, b); if (sum.lo != 0) { var uhii = @bitCast(u64, sum.hi); - const bits_lost = -i32((uhii >> 52) & 0x7FF) - scale + 1; + const bits_lost = -@intCast(i32, (uhii >> 52) & 0x7FF) - scale + 1; if ((bits_lost != 1) == (uhii & 1 != 0)) { const uloi = @bitCast(u64, sum.lo); uhii += 1 - (((uhii ^ uloi) >> 62) & 2); diff --git a/std/math/frexp.zig b/std/math/frexp.zig index b58af0a9bc..dfc790fdd9 100644 --- a/std/math/frexp.zig +++ b/std/math/frexp.zig @@ -30,7 +30,7 @@ fn frexp32(x: f32) frexp32_result { var result: frexp32_result = undefined; var y = @bitCast(u32, x); - const e = i32(y >> 23) & 0xFF; + const e = @intCast(i32, y >> 23) & 0xFF; if (e == 0) { if (x != 0) { @@ -67,7 +67,7 @@ fn frexp64(x: f64) frexp64_result { var result: frexp64_result = undefined; var y = @bitCast(u64, x); - const e = i32(y >> 52) & 0x7FF; + const e = @intCast(i32, y >> 52) & 0x7FF; if (e == 0) { if (x != 0) { diff --git a/std/math/hypot.zig b/std/math/hypot.zig index 494df22ba6..f834f422e6 100644 --- a/std/math/hypot.zig +++ b/std/math/hypot.zig @@ -49,7 +49,7 @@ fn hypot32(x: f32, y: f32) f32 { yy *= 0x1.0p-90; } - return z * math.sqrt(f32(f64(x) * x + f64(y) * y)); + return z * math.sqrt(@floatCast(f32, f64(x) * x + f64(y) * y)); } fn sq(hi: *f64, lo: *f64, x: f64) void { diff --git a/std/math/ilogb.zig b/std/math/ilogb.zig index f1f33aff55..a24f580a32 100644 --- a/std/math/ilogb.zig +++ b/std/math/ilogb.zig @@ -23,7 +23,7 @@ const fp_ilogb0 = fp_ilogbnan; fn ilogb32(x: f32) i32 { var u = @bitCast(u32, x); - var e = i32((u >> 23) & 0xFF); + var e = @intCast(i32, (u >> 23) & 0xFF); // TODO: We should be able to merge this with the lower check. if (math.isNan(x)) { @@ -59,7 +59,7 @@ fn ilogb32(x: f32) i32 { fn ilogb64(x: f64) i32 { var u = @bitCast(u64, x); - var e = i32((u >> 52) & 0x7FF); + var e = @intCast(i32, (u >> 52) & 0x7FF); if (math.isNan(x)) { return @maxValue(i32); diff --git a/std/math/index.zig b/std/math/index.zig index 99e5b4ecf1..176293be74 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -227,7 +227,7 @@ pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T { /// A negative shift amount results in a right shift. pub fn shl(comptime T: type, a: T, shift_amt: var) T { const abs_shift_amt = absCast(shift_amt); - const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else Log2Int(T)(abs_shift_amt); + const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); if (@typeOf(shift_amt).is_signed) { if (shift_amt >= 0) { @@ -251,7 +251,7 @@ test "math.shl" { /// A negative shift amount results in a lefft shift. pub fn shr(comptime T: type, a: T, shift_amt: var) T { const abs_shift_amt = absCast(shift_amt); - const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else Log2Int(T)(abs_shift_amt); + const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); if (@typeOf(shift_amt).is_signed) { if (shift_amt >= 0) { @@ -473,9 +473,9 @@ fn testRem() void { /// Result is an unsigned integer. pub fn absCast(x: var) @IntType(false, @typeOf(x).bit_count) { const uint = @IntType(false, @typeOf(x).bit_count); - if (x >= 0) return uint(x); + if (x >= 0) return @intCast(uint, x); - return uint(-(x + 1)) + 1; + return @intCast(uint, -(x + 1)) + 1; } test "math.absCast" { @@ -499,7 +499,7 @@ pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) { if (x == -@minValue(int)) return @minValue(int); - return -int(x); + return -@intCast(int, x); } test "math.negateCast" { @@ -522,7 +522,7 @@ pub fn cast(comptime T: type, x: var) (error{Overflow}!T) { } else if (@minValue(@typeOf(x)) < @minValue(T) and x < @minValue(T)) { return error.Overflow; } else { - return T(x); + return @intCast(T, x); } } @@ -565,7 +565,7 @@ test "math.floorPowerOfTwo" { pub fn log2_int(comptime T: type, x: T) Log2Int(T) { assert(x != 0); - return Log2Int(T)(T.bit_count - 1 - @clz(x)); + return @intCast(Log2Int(T), T.bit_count - 1 - @clz(x)); } pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) { @@ -597,3 +597,14 @@ fn testFloorPowerOfTwo() void { assert(floorPowerOfTwo(u4, 8) == 8); assert(floorPowerOfTwo(u4, 9) == 8); } + +pub fn lossyCast(comptime T: type, value: var) T { + switch (@typeInfo(@typeOf(value))) { + builtin.TypeId.Int => return @intToFloat(T, value), + builtin.TypeId.Float => return @floatCast(T, value), + builtin.TypeId.ComptimeInt => return T(value), + builtin.TypeId.ComptimeFloat => return T(value), + else => @compileError("bad type"), + } +} + diff --git a/std/math/ln.zig b/std/math/ln.zig index 3fd75977b9..e78cc379e0 100644 --- a/std/math/ln.zig +++ b/std/math/ln.zig @@ -71,7 +71,7 @@ pub fn ln_32(x_: f32) f32 { // x into [sqrt(2) / 2, sqrt(2)] ix += 0x3F800000 - 0x3F3504F3; - k += i32(ix >> 23) - 0x7F; + k += @intCast(i32, ix >> 23) - 0x7F; ix = (ix & 0x007FFFFF) + 0x3F3504F3; x = @bitCast(f32, ix); @@ -83,7 +83,7 @@ pub fn ln_32(x_: f32) f32 { const t2 = z * (Lg1 + w * Lg3); const R = t2 + t1; const hfsq = 0.5 * f * f; - const dk = f32(k); + const dk = @intToFloat(f32, k); return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi; } @@ -103,7 +103,7 @@ pub fn ln_64(x_: f64) f64 { var x = x_; var ix = @bitCast(u64, x); - var hx = u32(ix >> 32); + var hx = @intCast(u32, ix >> 32); var k: i32 = 0; if (hx < 0x00100000 or hx >> 31 != 0) { @@ -119,7 +119,7 @@ pub fn ln_64(x_: f64) f64 { // subnormal, scale x k -= 54; x *= 0x1.0p54; - hx = u32(@bitCast(u64, ix) >> 32); + hx = @intCast(u32, @bitCast(u64, ix) >> 32); } else if (hx >= 0x7FF00000) { return x; } else if (hx == 0x3FF00000 and ix << 32 == 0) { @@ -128,7 +128,7 @@ pub fn ln_64(x_: f64) f64 { // x into [sqrt(2) / 2, sqrt(2)] hx += 0x3FF00000 - 0x3FE6A09E; - k += i32(hx >> 20) - 0x3FF; + k += @intCast(i32, hx >> 20) - 0x3FF; hx = (hx & 0x000FFFFF) + 0x3FE6A09E; ix = (u64(hx) << 32) | (ix & 0xFFFFFFFF); x = @bitCast(f64, ix); @@ -141,7 +141,7 @@ pub fn ln_64(x_: f64) f64 { const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); const R = t2 + t1; - const dk = f64(k); + const dk = @intToFloat(f64, k); return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi; } diff --git a/std/math/log.zig b/std/math/log.zig index 2c876081d8..20b6d055e8 100644 --- a/std/math/log.zig +++ b/std/math/log.zig @@ -13,22 +13,23 @@ pub fn log(comptime T: type, base: T, x: T) T { return math.ln(x); } + const float_base = math.lossyCast(f64, base); switch (@typeId(T)) { TypeId.ComptimeFloat => { - return @typeOf(1.0)(math.ln(f64(x)) / math.ln(f64(base))); + return @typeOf(1.0)(math.ln(f64(x)) / math.ln(float_base)); }, TypeId.ComptimeInt => { - return @typeOf(1)(math.floor(math.ln(f64(x)) / math.ln(f64(base)))); + return @typeOf(1)(math.floor(math.ln(f64(x)) / math.ln(float_base))); }, builtin.TypeId.Int => { // TODO implement integer log without using float math - return T(math.floor(math.ln(f64(x)) / math.ln(f64(base)))); + return @floatToInt(T, math.floor(math.ln(@intToFloat(f64, x)) / math.ln(float_base))); }, builtin.TypeId.Float => { switch (T) { - f32 => return f32(math.ln(f64(x)) / math.ln(f64(base))), - f64 => return math.ln(x) / math.ln(f64(base)), + f32 => return @floatCast(f32, math.ln(f64(x)) / math.ln(float_base)), + f64 => return math.ln(x) / math.ln(float_base), else => @compileError("log not implemented for " ++ @typeName(T)), } }, diff --git a/std/math/log10.zig b/std/math/log10.zig index c444add9ac..a93ce48fb7 100644 --- a/std/math/log10.zig +++ b/std/math/log10.zig @@ -28,7 +28,7 @@ pub fn log10(x: var) @typeOf(x) { return @typeOf(1)(math.floor(log10_64(f64(x)))); }, TypeId.Int => { - return T(math.floor(log10_64(f64(x)))); + return @floatToInt(T, math.floor(log10_64(@intToFloat(f64, x)))); }, else => @compileError("log10 not implemented for " ++ @typeName(T)), } @@ -71,7 +71,7 @@ pub fn log10_32(x_: f32) f32 { // x into [sqrt(2) / 2, sqrt(2)] ix += 0x3F800000 - 0x3F3504F3; - k += i32(ix >> 23) - 0x7F; + k += @intCast(i32, ix >> 23) - 0x7F; ix = (ix & 0x007FFFFF) + 0x3F3504F3; x = @bitCast(f32, ix); @@ -89,7 +89,7 @@ pub fn log10_32(x_: f32) f32 { u &= 0xFFFFF000; hi = @bitCast(f32, u); const lo = f - hi - hfsq + s * (hfsq + R); - const dk = f32(k); + const dk = @intToFloat(f32, k); return dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi + hi * ivln10hi + dk * log10_2hi; } @@ -109,7 +109,7 @@ pub fn log10_64(x_: f64) f64 { var x = x_; var ix = @bitCast(u64, x); - var hx = u32(ix >> 32); + var hx = @intCast(u32, ix >> 32); var k: i32 = 0; if (hx < 0x00100000 or hx >> 31 != 0) { @@ -125,7 +125,7 @@ pub fn log10_64(x_: f64) f64 { // subnormal, scale x k -= 54; x *= 0x1.0p54; - hx = u32(@bitCast(u64, x) >> 32); + hx = @intCast(u32, @bitCast(u64, x) >> 32); } else if (hx >= 0x7FF00000) { return x; } else if (hx == 0x3FF00000 and ix << 32 == 0) { @@ -134,7 +134,7 @@ pub fn log10_64(x_: f64) f64 { // x into [sqrt(2) / 2, sqrt(2)] hx += 0x3FF00000 - 0x3FE6A09E; - k += i32(hx >> 20) - 0x3FF; + k += @intCast(i32, hx >> 20) - 0x3FF; hx = (hx & 0x000FFFFF) + 0x3FE6A09E; ix = (u64(hx) << 32) | (ix & 0xFFFFFFFF); x = @bitCast(f64, ix); @@ -157,7 +157,7 @@ pub fn log10_64(x_: f64) f64 { // val_hi + val_lo ~ log10(1 + f) + k * log10(2) var val_hi = hi * ivln10hi; - const dk = f64(k); + const dk = @intToFloat(f64, k); const y = dk * log10_2hi; var val_lo = dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi; diff --git a/std/math/log1p.zig b/std/math/log1p.zig index 57efdaf51c..7d3be6bb49 100644 --- a/std/math/log1p.zig +++ b/std/math/log1p.zig @@ -68,7 +68,7 @@ fn log1p_32(x: f32) f32 { const uf = 1 + x; var iu = @bitCast(u32, uf); iu += 0x3F800000 - 0x3F3504F3; - k = i32(iu >> 23) - 0x7F; + k = @intCast(i32, iu >> 23) - 0x7F; // correction to avoid underflow in c / u if (k < 25) { @@ -90,7 +90,7 @@ fn log1p_32(x: f32) f32 { const t2 = z * (Lg1 + w * Lg3); const R = t2 + t1; const hfsq = 0.5 * f * f; - const dk = f32(k); + const dk = @intToFloat(f32, k); return s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi; } @@ -107,7 +107,7 @@ fn log1p_64(x: f64) f64 { const Lg7: f64 = 1.479819860511658591e-01; var ix = @bitCast(u64, x); - var hx = u32(ix >> 32); + var hx = @intCast(u32, ix >> 32); var k: i32 = 1; var c: f64 = undefined; var f: f64 = undefined; @@ -145,9 +145,9 @@ fn log1p_64(x: f64) f64 { if (k != 0) { const uf = 1 + x; const hu = @bitCast(u64, uf); - var iu = u32(hu >> 32); + var iu = @intCast(u32, hu >> 32); iu += 0x3FF00000 - 0x3FE6A09E; - k = i32(iu >> 20) - 0x3FF; + k = @intCast(i32, iu >> 20) - 0x3FF; // correction to avoid underflow in c / u if (k < 54) { @@ -170,7 +170,7 @@ fn log1p_64(x: f64) f64 { const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); const R = t2 + t1; - const dk = f64(k); + const dk = @intToFloat(f64, k); return s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi; } diff --git a/std/math/log2.zig b/std/math/log2.zig index 2530519941..858f6ffa02 100644 --- a/std/math/log2.zig +++ b/std/math/log2.zig @@ -75,7 +75,7 @@ pub fn log2_32(x_: f32) f32 { // x into [sqrt(2) / 2, sqrt(2)] ix += 0x3F800000 - 0x3F3504F3; - k += i32(ix >> 23) - 0x7F; + k += @intCast(i32, ix >> 23) - 0x7F; ix = (ix & 0x007FFFFF) + 0x3F3504F3; x = @bitCast(f32, ix); @@ -93,7 +93,7 @@ pub fn log2_32(x_: f32) f32 { u &= 0xFFFFF000; hi = @bitCast(f32, u); const lo = f - hi - hfsq + s * (hfsq + R); - return (lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + f32(k); + return (lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + @intToFloat(f32, k); } pub fn log2_64(x_: f64) f64 { @@ -109,7 +109,7 @@ pub fn log2_64(x_: f64) f64 { var x = x_; var ix = @bitCast(u64, x); - var hx = u32(ix >> 32); + var hx = @intCast(u32, ix >> 32); var k: i32 = 0; if (hx < 0x00100000 or hx >> 31 != 0) { @@ -125,7 +125,7 @@ pub fn log2_64(x_: f64) f64 { // subnormal, scale x k -= 54; x *= 0x1.0p54; - hx = u32(@bitCast(u64, x) >> 32); + hx = @intCast(u32, @bitCast(u64, x) >> 32); } else if (hx >= 0x7FF00000) { return x; } else if (hx == 0x3FF00000 and ix << 32 == 0) { @@ -134,7 +134,7 @@ pub fn log2_64(x_: f64) f64 { // x into [sqrt(2) / 2, sqrt(2)] hx += 0x3FF00000 - 0x3FE6A09E; - k += i32(hx >> 20) - 0x3FF; + k += @intCast(i32, hx >> 20) - 0x3FF; hx = (hx & 0x000FFFFF) + 0x3FE6A09E; ix = (u64(hx) << 32) | (ix & 0xFFFFFFFF); x = @bitCast(f64, ix); @@ -159,7 +159,7 @@ pub fn log2_64(x_: f64) f64 { var val_lo = (lo + hi) * ivln2lo + lo * ivln2hi; // spadd(val_hi, val_lo, y) - const y = f64(k); + const y = @intToFloat(f64, k); const ww = y + val_hi; val_lo += (y - ww) + val_hi; val_hi = ww; diff --git a/std/math/modf.zig b/std/math/modf.zig index a6606ce34c..b6dd78f022 100644 --- a/std/math/modf.zig +++ b/std/math/modf.zig @@ -29,7 +29,7 @@ fn modf32(x: f32) modf32_result { var result: modf32_result = undefined; const u = @bitCast(u32, x); - const e = i32((u >> 23) & 0xFF) - 0x7F; + const e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F; const us = u & 0x80000000; // TODO: Shouldn't need this. @@ -57,7 +57,7 @@ fn modf32(x: f32) modf32_result { return result; } - const mask = u32(0x007FFFFF) >> u5(e); + const mask = u32(0x007FFFFF) >> @intCast(u5, e); if (u & mask == 0) { result.ipart = x; result.fpart = @bitCast(f32, us); @@ -74,7 +74,7 @@ fn modf64(x: f64) modf64_result { var result: modf64_result = undefined; const u = @bitCast(u64, x); - const e = i32((u >> 52) & 0x7FF) - 0x3FF; + const e = @intCast(i32, (u >> 52) & 0x7FF) - 0x3FF; const us = u & (1 << 63); if (math.isInf(x)) { @@ -101,7 +101,7 @@ fn modf64(x: f64) modf64_result { return result; } - const mask = u64(@maxValue(u64) >> 12) >> u6(e); + const mask = u64(@maxValue(u64) >> 12) >> @intCast(u6, e); if (u & mask == 0) { result.ipart = x; result.fpart = @bitCast(f64, us); diff --git a/std/math/pow.zig b/std/math/pow.zig index dfe4fc09d6..7fc334c06b 100644 --- a/std/math/pow.zig +++ b/std/math/pow.zig @@ -146,7 +146,7 @@ pub fn pow(comptime T: type, x: T, y: T) T { var xe = r2.exponent; var x1 = r2.significand; - var i = i32(yi); + var i = @floatToInt(i32, yi); while (i != 0) : (i >>= 1) { if (i & 1 == 1) { a1 *= x1; @@ -171,7 +171,7 @@ pub fn pow(comptime T: type, x: T, y: T) T { fn isOddInteger(x: f64) bool { const r = math.modf(x); - return r.fpart == 0.0 and i64(r.ipart) & 1 == 1; + return r.fpart == 0.0 and @floatToInt(i64, r.ipart) & 1 == 1; } test "math.pow" { diff --git a/std/math/scalbn.zig b/std/math/scalbn.zig index deb5d989d2..f72c7e866f 100644 --- a/std/math/scalbn.zig +++ b/std/math/scalbn.zig @@ -37,7 +37,7 @@ fn scalbn32(x: f32, n_: i32) f32 { } } - const u = u32(n +% 0x7F) << 23; + const u = @intCast(u32, n +% 0x7F) << 23; return y * @bitCast(f32, u); } @@ -67,7 +67,7 @@ fn scalbn64(x: f64, n_: i32) f64 { } } - const u = u64(n +% 0x3FF) << 52; + const u = @intCast(u64, n +% 0x3FF) << 52; return y * @bitCast(f64, u); } diff --git a/std/math/sin.zig b/std/math/sin.zig index 21c324e444..3796d74812 100644 --- a/std/math/sin.zig +++ b/std/math/sin.zig @@ -60,7 +60,7 @@ fn sin32(x_: f32) f32 { } var y = math.floor(x * m4pi); - var j = i64(y); + var j = @floatToInt(i64, y); if (j & 1 == 1) { j += 1; @@ -112,7 +112,7 @@ fn sin64(x_: f64) f64 { } var y = math.floor(x * m4pi); - var j = i64(y); + var j = @floatToInt(i64, y); if (j & 1 == 1) { j += 1; diff --git a/std/math/sinh.zig b/std/math/sinh.zig index 85c9ae979b..bb3af280ab 100644 --- a/std/math/sinh.zig +++ b/std/math/sinh.zig @@ -57,7 +57,7 @@ fn sinh64(x: f64) f64 { @setFloatMode(this, @import("builtin").FloatMode.Strict); const u = @bitCast(u64, x); - const w = u32(u >> 32); + const w = @intCast(u32, u >> 32); const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); if (x == 0.0 or math.isNan(x)) { diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig index 7a3ddb3b96..599008acff 100644 --- a/std/math/sqrt.zig +++ b/std/math/sqrt.zig @@ -99,7 +99,7 @@ fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) { } const ResultType = @IntType(false, T.bit_count / 2); - return ResultType(res); + return @intCast(ResultType, res); } test "math.sqrt_int" { diff --git a/std/math/tan.zig b/std/math/tan.zig index f578cf8156..ff3ed06186 100644 --- a/std/math/tan.zig +++ b/std/math/tan.zig @@ -53,7 +53,7 @@ fn tan32(x_: f32) f32 { } var y = math.floor(x * m4pi); - var j = i64(y); + var j = @floatToInt(i64, y); if (j & 1 == 1) { j += 1; @@ -102,7 +102,7 @@ fn tan64(x_: f64) f64 { } var y = math.floor(x * m4pi); - var j = i64(y); + var j = @floatToInt(i64, y); if (j & 1 == 1) { j += 1; diff --git a/std/math/tanh.zig b/std/math/tanh.zig index c1f5a0ca46..6204b2a374 100644 --- a/std/math/tanh.zig +++ b/std/math/tanh.zig @@ -68,7 +68,7 @@ fn tanh32(x: f32) f32 { fn tanh64(x: f64) f64 { const u = @bitCast(u64, x); - const w = u32(u >> 32); + const w = @intCast(u32, u >> 32); const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); var t: f64 = undefined; @@ -100,7 +100,7 @@ fn tanh64(x: f64) f64 { } // |x| is subnormal else { - math.forceEval(f32(x)); + math.forceEval(@floatCast(f32, x)); t = x; } diff --git a/std/math/trunc.zig b/std/math/trunc.zig index 54aa6943f7..92d5bfebc5 100644 --- a/std/math/trunc.zig +++ b/std/math/trunc.zig @@ -19,7 +19,7 @@ pub fn trunc(x: var) @typeOf(x) { fn trunc32(x: f32) f32 { const u = @bitCast(u32, x); - var e = i32(((u >> 23) & 0xFF)) - 0x7F + 9; + var e = @intCast(i32, ((u >> 23) & 0xFF)) - 0x7F + 9; var m: u32 = undefined; if (e >= 23 + 9) { @@ -29,7 +29,7 @@ fn trunc32(x: f32) f32 { e = 1; } - m = u32(@maxValue(u32)) >> u5(e); + m = u32(@maxValue(u32)) >> @intCast(u5, e); if (u & m == 0) { return x; } else { @@ -40,7 +40,7 @@ fn trunc32(x: f32) f32 { fn trunc64(x: f64) f64 { const u = @bitCast(u64, x); - var e = i32(((u >> 52) & 0x7FF)) - 0x3FF + 12; + var e = @intCast(i32, ((u >> 52) & 0x7FF)) - 0x3FF + 12; var m: u64 = undefined; if (e >= 52 + 12) { @@ -50,7 +50,7 @@ fn trunc64(x: f64) f64 { e = 1; } - m = u64(@maxValue(u64)) >> u6(e); + m = u64(@maxValue(u64)) >> @intCast(u6, e); if (u & m == 0) { return x; } else { diff --git a/std/mem.zig b/std/mem.zig index 10b3eb8fef..b02589b0dd 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -334,7 +334,7 @@ pub fn readInt(bytes: []const u8, comptime T: type, endian: builtin.Endian) T { builtin.Endian.Little => { const ShiftType = math.Log2Int(T); for (bytes) |b, index| { - result = result | (T(b) << ShiftType(index * 8)); + result = result | (T(b) << @intCast(ShiftType, index * 8)); } }, } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 1e3a732498..3a0fa7f461 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -413,7 +413,7 @@ pub const ChildProcess = struct { } // we are the parent - const pid = i32(pid_result); + const pid = @intCast(i32, pid_result); if (self.stdin_behavior == StdIo.Pipe) { self.stdin = os.File.openHandle(stdin_pipe[1]); } else { diff --git a/std/os/darwin.zig b/std/os/darwin.zig index a835959103..15e5608343 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -290,7 +290,7 @@ pub fn WIFSIGNALED(x: i32) bool { /// Get the errno from a syscall return value, or 0 for no error. pub fn getErrno(r: usize) usize { const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0; + return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; } pub fn close(fd: i32) usize { @@ -339,7 +339,14 @@ pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { } pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap(@ptrCast(*c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); + const ptr_result = c.mmap( + @ptrCast(*c_void, address), + length, + @bitCast(c_int, @intCast(c_uint, prot)), + @bitCast(c_int, c_uint(flags)), + fd, + offset, + ); const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); return errnoWrap(isize_result); } diff --git a/std/os/file.zig b/std/os/file.zig index 7e05501831..055f185121 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -266,7 +266,7 @@ pub const File = struct { pub fn getEndPos(self: *File) !usize { if (is_posix) { const stat = try os.posixFStat(self.handle); - return usize(stat.size); + return @intCast(usize, stat.size); } else if (is_windows) { var file_size: windows.LARGE_INTEGER = undefined; if (windows.GetFileSizeEx(self.handle, &file_size) == 0) { @@ -277,7 +277,7 @@ pub const File = struct { } if (file_size < 0) return error.Overflow; - return math.cast(usize, u64(file_size)); + return math.cast(usize, @intCast(u64, file_size)); } else { @compileError("TODO support getEndPos on this OS"); } @@ -343,7 +343,7 @@ pub const File = struct { } else if (is_windows) { var index: usize = 0; while (index < buffer.len) { - const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); + const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); var amt_read: windows.DWORD = undefined; if (windows.ReadFile(self.handle, @ptrCast(*c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) { const err = windows.GetLastError(); diff --git a/std/os/index.zig b/std/os/index.zig index fb4605fce0..f1c3ab2128 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -126,7 +126,7 @@ pub fn getRandomBytes(buf: []u8) !void { } defer _ = windows.CryptReleaseContext(hCryptProv, 0); - if (windows.CryptGenRandom(hCryptProv, windows.DWORD(buf.len), buf.ptr) == 0) { + if (windows.CryptGenRandom(hCryptProv, @intCast(windows.DWORD, buf.len), buf.ptr) == 0) { const err = windows.GetLastError(); return switch (err) { else => unexpectedErrorWindows(err), @@ -343,7 +343,7 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { else => return unexpectedErrorPosix(err), } } - return i32(result); + return @intCast(i32, result); } } @@ -586,7 +586,7 @@ pub fn getCwd(allocator: *Allocator) ![]u8 { errdefer allocator.free(buf); while (true) { - const result = windows.GetCurrentDirectoryA(windows.WORD(buf.len), buf.ptr); + const result = windows.GetCurrentDirectoryA(@intCast(windows.WORD, buf.len), buf.ptr); if (result == 0) { const err = windows.GetLastError(); @@ -2019,7 +2019,7 @@ pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { const rc = posix.socket(domain, socket_type, protocol); const err = posix.getErrno(rc); switch (err) { - 0 => return i32(rc), + 0 => return @intCast(i32, rc), posix.EACCES => return PosixSocketError.PermissionDenied, posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported, posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, @@ -2183,7 +2183,7 @@ pub fn posixAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError! const rc = posix.accept4(fd, addr, &sockaddr_size, flags); const err = posix.getErrno(rc); switch (err) { - 0 => return i32(rc), + 0 => return @intCast(i32, rc), posix.EINTR => continue, else => return unexpectedErrorPosix(err), @@ -2226,7 +2226,7 @@ pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 { const rc = posix.epoll_create1(flags); const err = posix.getErrno(rc); switch (err) { - 0 => return i32(rc), + 0 => return @intCast(i32, rc), else => return unexpectedErrorPosix(err), posix.EINVAL => return LinuxEpollCreateError.InvalidSyscall, @@ -2296,7 +2296,7 @@ pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: *linux.epoll_event) Lin pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize { while (true) { - const rc = posix.epoll_wait(epfd, events.ptr, u32(events.len), timeout); + const rc = posix.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); const err = posix.getErrno(rc); switch (err) { 0 => return rc, @@ -2661,7 +2661,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread posix.EAGAIN => return SpawnThreadError.SystemResources, posix.EPERM => unreachable, posix.EINVAL => unreachable, - else => return unexpectedErrorPosix(usize(err)), + else => return unexpectedErrorPosix(@intCast(usize, err)), } } else if (builtin.os == builtin.Os.linux) { // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 0e77371ec2..65aa659c82 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -642,7 +642,7 @@ pub fn WIFEXITED(s: i32) bool { return WTERMSIG(s) == 0; } pub fn WIFSTOPPED(s: i32) bool { - return (u16)(((unsigned(s) & 0xffff) *% 0x10001) >> 8) > 0x7f00; + return @intCast(u16, ((unsigned(s) & 0xffff) *% 0x10001) >> 8) > 0x7f00; } pub fn WIFSIGNALED(s: i32) bool { return (unsigned(s) & 0xffff) -% 1 < 0xff; @@ -658,11 +658,11 @@ pub const winsize = extern struct { /// Get the errno from a syscall return value, or 0 for no error. pub fn getErrno(r: usize) usize { const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0; + return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; } pub fn dup2(old: i32, new: i32) usize { - return syscall2(SYS_dup2, usize(old), usize(new)); + return syscall2(SYS_dup2, @intCast(usize, old), @intCast(usize, new)); } // TODO https://github.com/ziglang/zig/issues/265 @@ -693,12 +693,12 @@ pub fn getcwd(buf: [*]u8, size: usize) usize { } pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { - return syscall3(SYS_getdents, usize(fd), @ptrToInt(dirp), count); + return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count); } pub fn isatty(fd: i32) bool { var wsz: winsize = undefined; - return syscall3(SYS_ioctl, usize(fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; + return syscall3(SYS_ioctl, @intCast(usize, fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; } // TODO https://github.com/ziglang/zig/issues/265 @@ -727,7 +727,7 @@ pub fn umount2(special: [*]const u8, flags: u32) usize { } pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd), @bitCast(usize, offset)); + return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, @intCast(usize, fd), @bitCast(usize, offset)); } pub fn munmap(address: usize, length: usize) usize { @@ -735,7 +735,7 @@ pub fn munmap(address: usize, length: usize) usize { } pub fn read(fd: i32, buf: [*]u8, count: usize) usize { - return syscall3(SYS_read, usize(fd), @ptrToInt(buf), count); + return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count); } // TODO https://github.com/ziglang/zig/issues/265 @@ -749,7 +749,7 @@ pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { } pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { - return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset); + return syscall4(SYS_pread, @intCast(usize, fd), @ptrToInt(buf), count, offset); } // TODO https://github.com/ziglang/zig/issues/265 @@ -766,11 +766,11 @@ pub fn pipe2(fd: *[2]i32, flags: usize) usize { } pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { - return syscall3(SYS_write, usize(fd), @ptrToInt(buf), count); + return syscall3(SYS_write, @intCast(usize, fd), @ptrToInt(buf), count); } pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { - return syscall4(SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset); + return syscall4(SYS_pwrite, @intCast(usize, fd), @ptrToInt(buf), count, offset); } // TODO https://github.com/ziglang/zig/issues/265 @@ -790,7 +790,7 @@ pub fn create(path: [*]const u8, perm: usize) usize { // TODO https://github.com/ziglang/zig/issues/265 pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { - return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode); + return syscall4(SYS_openat, @intCast(usize, dirfd), @ptrToInt(path), flags, mode); } /// See also `clone` (from the arch-specific include) @@ -804,11 +804,11 @@ pub fn clone2(flags: usize, child_stack_ptr: usize) usize { } pub fn close(fd: i32) usize { - return syscall1(SYS_close, usize(fd)); + return syscall1(SYS_close, @intCast(usize, fd)); } pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize { - return syscall3(SYS_lseek, usize(fd), @bitCast(usize, offset), ref_pos); + return syscall3(SYS_lseek, @intCast(usize, fd), @bitCast(usize, offset), ref_pos); } pub fn exit(status: i32) noreturn { @@ -817,11 +817,11 @@ pub fn exit(status: i32) noreturn { } pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { - return syscall3(SYS_getrandom, @ptrToInt(buf), count, usize(flags)); + return syscall3(SYS_getrandom, @ptrToInt(buf), count, @intCast(usize, flags)); } pub fn kill(pid: i32, sig: i32) usize { - return syscall2(SYS_kill, @bitCast(usize, isize(pid)), usize(sig)); + return syscall2(SYS_kill, @bitCast(usize, isize(pid)), @intCast(usize, sig)); } // TODO https://github.com/ziglang/zig/issues/265 @@ -999,8 +999,8 @@ pub const empty_sigset = []usize{0} ** sigset_t.len; pub fn raise(sig: i32) usize { var set: sigset_t = undefined; blockAppSignals(&set); - const tid = i32(syscall0(SYS_gettid)); - const ret = syscall2(SYS_tkill, usize(tid), usize(sig)); + const tid = @intCast(i32, syscall0(SYS_gettid)); + const ret = syscall2(SYS_tkill, @intCast(usize, tid), @intCast(usize, sig)); restoreSignals(&set); return ret; } @@ -1019,12 +1019,12 @@ fn restoreSignals(set: *sigset_t) void { pub fn sigaddset(set: *sigset_t, sig: u6) void { const s = sig - 1; - (set.*)[usize(s) / usize.bit_count] |= usize(1) << (s & (usize.bit_count - 1)); + (set.*)[@intCast(usize, s) / usize.bit_count] |= @intCast(usize, 1) << (s & (usize.bit_count - 1)); } pub fn sigismember(set: *const sigset_t, sig: u6) bool { const s = sig - 1; - return ((set.*)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0; + return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0; } pub const in_port_t = u16; @@ -1057,11 +1057,11 @@ pub const iovec = extern struct { }; pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - return syscall3(SYS_getsockname, usize(fd), @ptrToInt(addr), @ptrToInt(len)); + return syscall3(SYS_getsockname, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len)); } pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len)); + return syscall3(SYS_getpeername, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len)); } pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { @@ -1069,47 +1069,47 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { } pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { - return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen)); + return syscall5(SYS_setsockopt, @intCast(usize, fd), level, optname, @intCast(usize, optval), @ptrToInt(optlen)); } pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { - return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); + return syscall5(SYS_getsockopt, @intCast(usize, fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); } pub fn sendmsg(fd: i32, msg: *const msghdr, flags: u32) usize { - return syscall3(SYS_sendmsg, usize(fd), @ptrToInt(msg), flags); + return syscall3(SYS_sendmsg, @intCast(usize, fd), @ptrToInt(msg), flags); } pub fn connect(fd: i32, addr: *const sockaddr, len: socklen_t) usize { - return syscall3(SYS_connect, usize(fd), @ptrToInt(addr), usize(len)); + return syscall3(SYS_connect, @intCast(usize, fd), @ptrToInt(addr), @intCast(usize, len)); } pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { - return syscall3(SYS_recvmsg, usize(fd), @ptrToInt(msg), flags); + return syscall3(SYS_recvmsg, @intCast(usize, fd), @ptrToInt(msg), flags); } pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { - return syscall6(SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); + return syscall6(SYS_recvfrom, @intCast(usize, fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); } pub fn shutdown(fd: i32, how: i32) usize { - return syscall2(SYS_shutdown, usize(fd), usize(how)); + return syscall2(SYS_shutdown, @intCast(usize, fd), @intCast(usize, how)); } pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { - return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len)); + return syscall3(SYS_bind, @intCast(usize, fd), @ptrToInt(addr), @intCast(usize, len)); } pub fn listen(fd: i32, backlog: u32) usize { - return syscall2(SYS_listen, usize(fd), backlog); + return syscall2(SYS_listen, @intCast(usize, fd), backlog); } pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { - return syscall6(SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen)); + return syscall6(SYS_sendto, @intCast(usize, fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen)); } pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { - return syscall4(SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(*fd[0])); + return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(*fd[0])); } pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { @@ -1117,11 +1117,11 @@ pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { } pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { - return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags); + return syscall4(SYS_accept4, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len), flags); } pub fn fstat(fd: i32, stat_buf: *Stat) usize { - return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf)); + return syscall2(SYS_fstat, @intCast(usize, fd), @ptrToInt(stat_buf)); } // TODO https://github.com/ziglang/zig/issues/265 @@ -1214,15 +1214,15 @@ pub fn epoll_create1(flags: usize) usize { } pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { - return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev)); + return syscall4(SYS_epoll_ctl, @intCast(usize, epoll_fd), @intCast(usize, op), @intCast(usize, fd), @ptrToInt(ev)); } pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { - return syscall4(SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout)); + return syscall4(SYS_epoll_wait, @intCast(usize, epoll_fd), @ptrToInt(events), @intCast(usize, maxevents), @intCast(usize, timeout)); } pub fn timerfd_create(clockid: i32, flags: u32) usize { - return syscall2(SYS_timerfd_create, usize(clockid), usize(flags)); + return syscall2(SYS_timerfd_create, @intCast(usize, clockid), @intCast(usize, flags)); } pub const itimerspec = extern struct { @@ -1231,11 +1231,11 @@ pub const itimerspec = extern struct { }; pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { - return syscall2(SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value)); + return syscall2(SYS_timerfd_gettime, @intCast(usize, fd), @ptrToInt(curr_value)); } pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { - return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value)); + return syscall4(SYS_timerfd_settime, @intCast(usize, fd), @intCast(usize, flags), @ptrToInt(new_value), @ptrToInt(old_value)); } pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330; @@ -1345,7 +1345,7 @@ pub const cap_user_data_t = extern struct { }; pub fn unshare(flags: usize) usize { - return syscall1(SYS_unshare, usize(flags)); + return syscall1(SYS_unshare, @intCast(usize, flags)); } pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize { diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig index 948a3ac96b..e7dae3a584 100644 --- a/std/os/linux/test.zig +++ b/std/os/linux/test.zig @@ -21,7 +21,7 @@ test "timer" { .it_value = time_interval, }; - err = linux.timerfd_settime(i32(timer_fd), 0, &new_time, null); + err = linux.timerfd_settime(@intCast(i32, timer_fd), 0, &new_time, null); assert(err == 0); var event = linux.epoll_event{ @@ -29,12 +29,12 @@ test "timer" { .data = linux.epoll_data{ .ptr = 0 }, }; - err = linux.epoll_ctl(i32(epoll_fd), linux.EPOLL_CTL_ADD, i32(timer_fd), &event); + err = linux.epoll_ctl(@intCast(i32, epoll_fd), linux.EPOLL_CTL_ADD, @intCast(i32, timer_fd), &event); assert(err == 0); const events_one: linux.epoll_event = undefined; var events = []linux.epoll_event{events_one} ** 8; // TODO implicit cast from *[N]T to [*]T - err = linux.epoll_wait(i32(epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1); + err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1); } diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig index cbd0cd1df5..a78e3370e6 100644 --- a/std/os/linux/vdso.zig +++ b/std/os/linux/vdso.zig @@ -62,8 +62,8 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { var i: usize = 0; while (i < hashtab[1]) : (i += 1) { - if (0 == (u32(1) << u5(syms[i].st_info & 0xf) & OK_TYPES)) continue; - if (0 == (u32(1) << u5(syms[i].st_info >> 4) & OK_BINDS)) continue; + if (0 == (u32(1) << @intCast(u5, syms[i].st_info & 0xf) & OK_TYPES)) continue; + if (0 == (u32(1) << @intCast(u5, syms[i].st_info >> 4) & OK_BINDS)) continue; if (0 == syms[i].st_shndx) continue; if (!mem.eql(u8, name, cstr.toSliceConst(strings + syms[i].st_name))) continue; if (maybe_versym) |versym| { diff --git a/std/os/time.zig b/std/os/time.zig index ffb506cd7d..73ba5bba82 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -14,12 +14,12 @@ pub const epoch = @import("epoch.zig"); pub fn sleep(seconds: usize, nanoseconds: usize) void { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { - posixSleep(u63(seconds), u63(nanoseconds)); + posixSleep(@intCast(u63, seconds), @intCast(u63, nanoseconds)); }, Os.windows => { const ns_per_ms = ns_per_s / ms_per_s; const milliseconds = seconds * ms_per_s + nanoseconds / ns_per_ms; - windows.Sleep(windows.DWORD(milliseconds)); + windows.Sleep(@intCast(windows.DWORD, milliseconds)); }, else => @compileError("Unsupported OS"), } @@ -83,8 +83,8 @@ fn milliTimestampDarwin() u64 { var tv: darwin.timeval = undefined; var err = darwin.gettimeofday(&tv, null); debug.assert(err == 0); - const sec_ms = u64(tv.tv_sec) * ms_per_s; - const usec_ms = @divFloor(u64(tv.tv_usec), us_per_s / ms_per_s); + const sec_ms = @intCast(u64, tv.tv_sec) * ms_per_s; + const usec_ms = @divFloor(@intCast(u64, tv.tv_usec), us_per_s / ms_per_s); return u64(sec_ms) + u64(usec_ms); } @@ -95,8 +95,8 @@ fn milliTimestampPosix() u64 { var ts: posix.timespec = undefined; const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts); debug.assert(err == 0); - const sec_ms = u64(ts.tv_sec) * ms_per_s; - const nsec_ms = @divFloor(u64(ts.tv_nsec), ns_per_s / ms_per_s); + const sec_ms = @intCast(u64, ts.tv_sec) * ms_per_s; + const nsec_ms = @divFloor(@intCast(u64, ts.tv_nsec), ns_per_s / ms_per_s); return sec_ms + nsec_ms; } @@ -162,13 +162,13 @@ pub const Timer = struct { var freq: i64 = undefined; var err = windows.QueryPerformanceFrequency(&freq); if (err == windows.FALSE) return error.TimerUnsupported; - self.frequency = u64(freq); + self.frequency = @intCast(u64, freq); self.resolution = @divFloor(ns_per_s, self.frequency); var start_time: i64 = undefined; err = windows.QueryPerformanceCounter(&start_time); debug.assert(err != windows.FALSE); - self.start_time = u64(start_time); + self.start_time = @intCast(u64, start_time); }, Os.linux => { //On Linux, seccomp can do arbitrary things to our ability to call @@ -184,12 +184,12 @@ pub const Timer = struct { posix.EINVAL => return error.TimerUnsupported, else => return std.os.unexpectedErrorPosix(errno), } - self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); + self.resolution = @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); result = posix.clock_gettime(monotonic_clock_id, &ts); errno = posix.getErrno(result); if (errno != 0) return std.os.unexpectedErrorPosix(errno); - self.start_time = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); + self.start_time = @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); }, Os.macosx, Os.ios => { darwin.mach_timebase_info(&self.frequency); @@ -236,7 +236,7 @@ pub const Timer = struct { var result: i64 = undefined; var err = windows.QueryPerformanceCounter(&result); debug.assert(err != windows.FALSE); - return u64(result); + return @intCast(u64, result); } fn clockDarwin() u64 { @@ -247,7 +247,7 @@ pub const Timer = struct { var ts: posix.timespec = undefined; var result = posix.clock_gettime(monotonic_clock_id, &ts); debug.assert(posix.getErrno(result) == 0); - return u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); + return @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); } }; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 88a9e7952e..cb4788ba17 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -42,7 +42,7 @@ pub const WriteError = error{ }; pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { - if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { + if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), @intCast(u32, bytes.len), null, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, @@ -68,7 +68,12 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { const size = @sizeOf(windows.FILE_NAME_INFO); var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = []u8{0} ** (size + windows.MAX_PATH); - if (windows.GetFileInformationByHandleEx(handle, windows.FileNameInfo, @ptrCast(*c_void, &name_info_bytes[0]), u32(name_info_bytes.len)) == 0) { + if (windows.GetFileInformationByHandleEx( + handle, + windows.FileNameInfo, + @ptrCast(*c_void, &name_info_bytes[0]), + @intCast(u32, name_info_bytes.len), + ) == 0) { return true; } diff --git a/std/rand/index.zig b/std/rand/index.zig index 3a1a559cd9..13694f4c09 100644 --- a/std/rand/index.zig +++ b/std/rand/index.zig @@ -55,16 +55,16 @@ pub const Random = struct { if (T.is_signed) { const uint = @IntType(false, T.bit_count); if (start >= 0 and end >= 0) { - return T(r.range(uint, uint(start), uint(end))); + return @intCast(T, r.range(uint, @intCast(uint, start), @intCast(uint, end))); } else if (start < 0 and end < 0) { // Can't overflow because the range is over signed ints return math.negateCast(r.range(uint, math.absCast(end), math.absCast(start)) + 1) catch unreachable; } else if (start < 0 and end >= 0) { - const end_uint = uint(end); + const end_uint = @intCast(uint, end); const total_range = math.absCast(start) + end_uint; const value = r.range(uint, 0, total_range); const result = if (value < end_uint) x: { - break :x T(value); + break :x @intCast(T, value); } else if (value == end_uint) x: { break :x start; } else x: { @@ -213,9 +213,9 @@ pub const Pcg = struct { self.s = l *% default_multiplier +% (self.i | 1); const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27); - const rot = u32(l >> 59); + const rot = @intCast(u32, l >> 59); - return (xor_s >> u5(rot)) | (xor_s << u5((0 -% rot) & 31)); + return (xor_s >> @intCast(u5, rot)) | (xor_s << @intCast(u5, (0 -% rot) & 31)); } fn seed(self: *Pcg, init_s: u64) void { @@ -322,7 +322,7 @@ pub const Xoroshiro128 = struct { inline for (table) |entry| { var b: usize = 0; while (b < 64) : (b += 1) { - if ((entry & (u64(1) << u6(b))) != 0) { + if ((entry & (u64(1) << @intCast(u6, b))) != 0) { s0 ^= self.s[0]; s1 ^= self.s[1]; } @@ -667,13 +667,13 @@ test "Random range" { } fn testRange(r: *Random, start: i32, end: i32) void { - const count = usize(end - start); + const count = @intCast(usize, end - start); var values_buffer = []bool{false} ** 20; const values = values_buffer[0..count]; var i: usize = 0; while (i < count) { const value = r.range(i32, start, end); - const index = usize(value - start); + const index = @intCast(usize, value - start); if (!values[index]) { i += 1; values[index] = true; diff --git a/std/segmented_list.zig b/std/segmented_list.zig index 9f10f4d44a..6e3f32e9d6 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -104,7 +104,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } pub fn deinit(self: *Self) void { - self.freeShelves(ShelfIndex(self.dynamic_segments.len), 0); + self.freeShelves(@intCast(ShelfIndex, self.dynamic_segments.len), 0); self.allocator.free(self.dynamic_segments); self.* = undefined; } @@ -158,7 +158,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type /// Only grows capacity, or retains current capacity pub fn growCapacity(self: *Self, new_capacity: usize) !void { const new_cap_shelf_count = shelfCount(new_capacity); - const old_shelf_count = ShelfIndex(self.dynamic_segments.len); + const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); if (new_cap_shelf_count > old_shelf_count) { self.dynamic_segments = try self.allocator.realloc([*]T, self.dynamic_segments, new_cap_shelf_count); var i = old_shelf_count; @@ -175,7 +175,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type /// Only shrinks capacity or retains current capacity pub fn shrinkCapacity(self: *Self, new_capacity: usize) void { if (new_capacity <= prealloc_item_count) { - const len = ShelfIndex(self.dynamic_segments.len); + const len = @intCast(ShelfIndex, self.dynamic_segments.len); self.freeShelves(len, 0); self.allocator.free(self.dynamic_segments); self.dynamic_segments = [][*]T{}; @@ -183,7 +183,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } const new_cap_shelf_count = shelfCount(new_capacity); - const old_shelf_count = ShelfIndex(self.dynamic_segments.len); + const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); assert(new_cap_shelf_count <= old_shelf_count); if (new_cap_shelf_count == old_shelf_count) { return; @@ -338,7 +338,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { { var i: usize = 0; while (i < 100) : (i += 1) { - try list.push(i32(i + 1)); + try list.push(@intCast(i32, i + 1)); assert(list.len == i + 1); } } @@ -346,7 +346,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { { var i: usize = 0; while (i < 100) : (i += 1) { - assert(list.at(i).* == i32(i + 1)); + assert(list.at(i).* == @intCast(i32, i + 1)); } } diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index dd37f1edb6..5c8a330a92 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -80,7 +80,7 @@ extern fn main(c_argc: i32, c_argv: [*][*]u8, c_envp: [*]?[*]u8) i32 { var env_count: usize = 0; while (c_envp[env_count] != null) : (env_count += 1) {} const envp = @ptrCast([*][*]u8, c_envp)[0..env_count]; - return callMainWithArgs(usize(c_argc), c_argv, envp); + return callMainWithArgs(@intCast(usize, c_argc), c_argv, envp); } fn callMain() u8 { diff --git a/std/special/builtin.zig b/std/special/builtin.zig index e97b0a89e4..07e735d931 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -135,9 +135,9 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { const mask = if (T == f32) 0xff else 0x7ff; var ux = @bitCast(uint, x); var uy = @bitCast(uint, y); - var ex = i32((ux >> digits) & mask); - var ey = i32((uy >> digits) & mask); - const sx = if (T == f32) u32(ux & 0x80000000) else i32(ux >> bits_minus_1); + var ex = @intCast(i32, (ux >> digits) & mask); + var ey = @intCast(i32, (uy >> digits) & mask); + const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1); var i: uint = undefined; if (uy << 1 == 0 or isNan(uint, uy) or ex == mask) @@ -156,7 +156,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { ex -= 1; i <<= 1; }) {} - ux <<= log2uint(@bitCast(u32, -ex + 1)); + ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1)); } else { ux &= @maxValue(uint) >> exp_bits; ux |= 1 << digits; @@ -167,7 +167,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { ey -= 1; i <<= 1; }) {} - uy <<= log2uint(@bitCast(u32, -ey + 1)); + uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1)); } else { uy &= @maxValue(uint) >> exp_bits; uy |= 1 << digits; @@ -199,12 +199,12 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { ux -%= 1 << digits; ux |= uint(@bitCast(u32, ex)) << digits; } else { - ux >>= log2uint(@bitCast(u32, -ex + 1)); + ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1)); } if (T == f32) { ux |= sx; } else { - ux |= uint(sx) << bits_minus_1; + ux |= @intCast(uint, sx) << bits_minus_1; } return @bitCast(T, ux); } @@ -227,8 +227,8 @@ export fn sqrt(x: f64) f64 { const sign: u32 = 0x80000000; const u = @bitCast(u64, x); - var ix0 = u32(u >> 32); - var ix1 = u32(u & 0xFFFFFFFF); + var ix0 = @intCast(u32, u >> 32); + var ix1 = @intCast(u32, u & 0xFFFFFFFF); // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan if (ix0 & 0x7FF00000 == 0x7FF00000) { @@ -245,7 +245,7 @@ export fn sqrt(x: f64) f64 { } // normalize x - var m = i32(ix0 >> 20); + var m = @intCast(i32, ix0 >> 20); if (m == 0) { // subnormal while (ix0 == 0) { @@ -259,9 +259,9 @@ export fn sqrt(x: f64) f64 { while (ix0 & 0x00100000 == 0) : (i += 1) { ix0 <<= 1; } - m -= i32(i) - 1; - ix0 |= ix1 >> u5(32 - i); - ix1 <<= u5(i); + m -= @intCast(i32, i) - 1; + ix0 |= ix1 >> @intCast(u5, 32 - i); + ix1 <<= @intCast(u5, i); } // unbias exponent @@ -345,10 +345,10 @@ export fn sqrt(x: f64) f64 { // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same // behaviour at least. - var iix0 = i32(ix0); + var iix0 = @intCast(i32, ix0); iix0 = iix0 +% (m << 20); - const uz = (u64(iix0) << 32) | ix1; + const uz = (@intCast(u64, iix0) << 32) | ix1; return @bitCast(f64, uz); } diff --git a/std/special/compiler_rt/divti3.zig b/std/special/compiler_rt/divti3.zig index 60460ea62d..712cccba82 100644 --- a/std/special/compiler_rt/divti3.zig +++ b/std/special/compiler_rt/divti3.zig @@ -13,7 +13,7 @@ pub extern fn __divti3(a: i128, b: i128) i128 { const r = udivmod(u128, @bitCast(u128, an), @bitCast(u128, bn), null); const s = s_a ^ s_b; - return (i128(r) ^ s) -% s; + return (@bitCast(i128, r) ^ s) -% s; } pub extern fn __divti3_windows_x86_64(a: *const i128, b: *const i128) void { diff --git a/std/special/compiler_rt/fixuint.zig b/std/special/compiler_rt/fixuint.zig index bd9b2fc395..48d63ed914 100644 --- a/std/special/compiler_rt/fixuint.zig +++ b/std/special/compiler_rt/fixuint.zig @@ -32,14 +32,14 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t const aAbs: rep_t = aRep & absMask; const sign = if ((aRep & signBit) != 0) i32(-1) else i32(1); - const exponent = i32(aAbs >> significandBits) - exponentBias; + const exponent = @intCast(i32, aAbs >> significandBits) - exponentBias; const significand: rep_t = (aAbs & significandMask) | implicitBit; // If either the value or the exponent is negative, the result is zero. if (sign == -1 or exponent < 0) return 0; // If the value is too large for the integer type, saturate. - if (c_uint(exponent) >= fixuint_t.bit_count) return ~fixuint_t(0); + if (@intCast(c_uint, exponent) >= fixuint_t.bit_count) return ~fixuint_t(0); // If 0 <= exponent < significandBits, right shift to get the result. // Otherwise, shift left. @@ -47,11 +47,11 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t // TODO this is a workaround for the mysterious "integer cast truncated bits" // happening on the next line @setRuntimeSafety(false); - return fixuint_t(significand >> Log2Int(rep_t)(significandBits - exponent)); + return @intCast(fixuint_t, significand >> @intCast(Log2Int(rep_t), significandBits - exponent)); } else { // TODO this is a workaround for the mysterious "integer cast truncated bits" // happening on the next line @setRuntimeSafety(false); - return fixuint_t(significand) << Log2Int(fixuint_t)(exponent - significandBits); + return @intCast(fixuint_t, significand) << @intCast(Log2Int(fixuint_t), exponent - significandBits); } } diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index f952730353..dc95aa23f2 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -292,7 +292,7 @@ extern fn __udivmodsi4(a: u32, b: u32, rem: *u32) u32 { @setRuntimeSafety(is_test); const d = __udivsi3(a, b); - rem.* = u32(i32(a) -% (i32(d) * i32(b))); + rem.* = @bitCast(u32, @bitCast(i32, a) -% (@bitCast(i32, d) * @bitCast(i32, b))); return d; } @@ -316,12 +316,12 @@ extern fn __udivsi3(n: u32, d: u32) u32 { sr += 1; // 1 <= sr <= n_uword_bits - 1 // Not a special case - var q: u32 = n << u5(n_uword_bits - sr); - var r: u32 = n >> u5(sr); + var q: u32 = n << @intCast(u5, n_uword_bits - sr); + var r: u32 = n >> @intCast(u5, sr); var carry: u32 = 0; while (sr > 0) : (sr -= 1) { // r:q = ((r:q) << 1) | carry - r = (r << 1) | (q >> u5(n_uword_bits - 1)); + r = (r << 1) | (q >> @intCast(u5, n_uword_bits - 1)); q = (q << 1) | carry; // carry = 0; // if (r.all >= d.all) @@ -329,8 +329,8 @@ extern fn __udivsi3(n: u32, d: u32) u32 { // r.all -= d.all; // carry = 1; // } - const s = i32(d -% r -% 1) >> u5(n_uword_bits - 1); - carry = u32(s & 1); + const s = @intCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1); + carry = @intCast(u32, s & 1); r -= d & @bitCast(u32, s); } q = (q << 1) | carry; diff --git a/std/special/compiler_rt/udivmod.zig b/std/special/compiler_rt/udivmod.zig index 894dd02239..e6b4ee0482 100644 --- a/std/special/compiler_rt/udivmod.zig +++ b/std/special/compiler_rt/udivmod.zig @@ -71,7 +71,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: r[high] = n[high] & (d[high] - 1); rem.* = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 } - return n[high] >> Log2SingleInt(@ctz(d[high])); + return n[high] >> @intCast(Log2SingleInt, @ctz(d[high])); } // K K // --- @@ -88,10 +88,10 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // 1 <= sr <= SingleInt.bit_count - 1 // q.all = a << (DoubleInt.bit_count - sr); q[low] = 0; - q[high] = n[low] << Log2SingleInt(SingleInt.bit_count - sr); + q[high] = n[low] << @intCast(Log2SingleInt, SingleInt.bit_count - sr); // r.all = a >> sr; - r[high] = n[high] >> Log2SingleInt(sr); - r[low] = (n[high] << Log2SingleInt(SingleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr)); + r[high] = n[high] >> @intCast(Log2SingleInt, sr); + r[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr)); } else { // d[low] != 0 if (d[high] == 0) { @@ -107,8 +107,8 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: return a; } sr = @ctz(d[low]); - q[high] = n[high] >> Log2SingleInt(sr); - q[low] = (n[high] << Log2SingleInt(SingleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr)); + q[high] = n[high] >> @intCast(Log2SingleInt, sr); + q[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr)); return @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &q[0]).*; // TODO issue #421 } // K X @@ -126,15 +126,15 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: } else if (sr < SingleInt.bit_count) { // 2 <= sr <= SingleInt.bit_count - 1 q[low] = 0; - q[high] = n[low] << Log2SingleInt(SingleInt.bit_count - sr); - r[high] = n[high] >> Log2SingleInt(sr); - r[low] = (n[high] << Log2SingleInt(SingleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr)); + q[high] = n[low] << @intCast(Log2SingleInt, SingleInt.bit_count - sr); + r[high] = n[high] >> @intCast(Log2SingleInt, sr); + r[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr)); } else { // SingleInt.bit_count + 1 <= sr <= DoubleInt.bit_count - 1 - q[low] = n[low] << Log2SingleInt(DoubleInt.bit_count - sr); - q[high] = (n[high] << Log2SingleInt(DoubleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr - SingleInt.bit_count)); + q[low] = n[low] << @intCast(Log2SingleInt, DoubleInt.bit_count - sr); + q[high] = (n[high] << @intCast(Log2SingleInt, DoubleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr - SingleInt.bit_count)); r[high] = 0; - r[low] = n[high] >> Log2SingleInt(sr - SingleInt.bit_count); + r[low] = n[high] >> @intCast(Log2SingleInt, sr - SingleInt.bit_count); } } else { // K X @@ -158,9 +158,9 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: r[high] = 0; r[low] = n[high]; } else { - r[high] = n[high] >> Log2SingleInt(sr); - r[low] = (n[high] << Log2SingleInt(SingleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr)); - q[high] = n[low] << Log2SingleInt(SingleInt.bit_count - sr); + r[high] = n[high] >> @intCast(Log2SingleInt, sr); + r[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr)); + q[high] = n[low] << @intCast(Log2SingleInt, SingleInt.bit_count - sr); } } } @@ -184,8 +184,8 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // carry = 1; // } r_all = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 - const s: SignedDoubleInt = SignedDoubleInt(b -% r_all -% 1) >> (DoubleInt.bit_count - 1); - carry = u32(s & 1); + const s: SignedDoubleInt = @intCast(SignedDoubleInt, b -% r_all -% 1) >> (DoubleInt.bit_count - 1); + carry = @intCast(u32, s & 1); r_all -= b & @bitCast(DoubleInt, s); r = @ptrCast(*[2]SingleInt, &r_all).*; // TODO issue #421 } diff --git a/std/unicode.zig b/std/unicode.zig index ec808ca4fe..9c329acc68 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -35,22 +35,22 @@ pub fn utf8Encode(c: u32, out: []u8) !u3 { // - Increasing the initial shift by 6 each time // - Each time after the first shorten the shifted // value to a max of 0b111111 (63) - 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range + 1 => out[0] = @intCast(u8, c), // Can just do 0 + codepoint for initial range 2 => { - out[0] = u8(0b11000000 | (c >> 6)); - out[1] = u8(0b10000000 | (c & 0b111111)); + out[0] = @intCast(u8, 0b11000000 | (c >> 6)); + out[1] = @intCast(u8, 0b10000000 | (c & 0b111111)); }, 3 => { if (0xd800 <= c and c <= 0xdfff) return error.Utf8CannotEncodeSurrogateHalf; - out[0] = u8(0b11100000 | (c >> 12)); - out[1] = u8(0b10000000 | ((c >> 6) & 0b111111)); - out[2] = u8(0b10000000 | (c & 0b111111)); + out[0] = @intCast(u8, 0b11100000 | (c >> 12)); + out[1] = @intCast(u8, 0b10000000 | ((c >> 6) & 0b111111)); + out[2] = @intCast(u8, 0b10000000 | (c & 0b111111)); }, 4 => { - out[0] = u8(0b11110000 | (c >> 18)); - out[1] = u8(0b10000000 | ((c >> 12) & 0b111111)); - out[2] = u8(0b10000000 | ((c >> 6) & 0b111111)); - out[3] = u8(0b10000000 | (c & 0b111111)); + out[0] = @intCast(u8, 0b11110000 | (c >> 18)); + out[1] = @intCast(u8, 0b10000000 | ((c >> 12) & 0b111111)); + out[2] = @intCast(u8, 0b10000000 | ((c >> 6) & 0b111111)); + out[3] = @intCast(u8, 0b10000000 | (c & 0b111111)); }, else => unreachable, } diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 4534529f36..79f1871b64 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -1128,7 +1128,7 @@ pub const Tokenizer = struct { // check utf8-encoded character. const length = std.unicode.utf8ByteSequenceLength(c0) catch return 1; if (self.index + length > self.buffer.len) { - return u3(self.buffer.len - self.index); + return @intCast(u3, self.buffer.len - self.index); } const bytes = self.buffer[self.index .. self.index + length]; switch (length) { diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 4c216010eb..7035740c54 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -343,7 +343,7 @@ fn testPeerErrorAndArray2(x: u8) error![]const u8 { test "explicit cast float number literal to integer if no fraction component" { const x = i32(1e4); assert(x == 10000); - const y = i32(f32(1e4)); + const y = @floatToInt(i32, f32(1e4)); assert(y == 10000); } @@ -398,3 +398,19 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" { const x: [*]const ?[*]const u8 = &window_name; assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); } + +test "@intCast comptime_int" { + const result = @intCast(i32, 1234); + assert(@typeOf(result) == i32); + assert(result == 1234); +} + +test "@floatCast comptime_int and comptime_float" { + const result = @floatCast(f32, 1234); + assert(@typeOf(result) == f32); + assert(result == 1234.0); + + const result2 = @floatCast(f32, 1234.0); + assert(@typeOf(result) == f32); + assert(result == 1234.0); +} diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 5c78d73092..6a02a47784 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -99,7 +99,7 @@ test "int to enum" { testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) void { - assert(IntToEnumNumber(u3(x)) == IntToEnumNumber.Three); + assert(IntToEnumNumber(@intCast(u3, x)) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 08d3f3a841..6c919e17a6 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -5,7 +5,7 @@ const builtin = @import("builtin"); test "compile time recursion" { assert(some_data.len == 21); } -var some_data: [usize(fibonacci(7))]u8 = undefined; +var some_data: [@intCast(usize, fibonacci(7))]u8 = undefined; fn fibonacci(x: i32) i32 { if (x <= 1) return 1; return fibonacci(x - 1) + fibonacci(x - 2); @@ -356,7 +356,7 @@ const global_array = x: { test "compile-time downcast when the bits fit" { comptime { const spartan_count: u16 = 255; - const byte = u8(spartan_count); + const byte = @intCast(u8, spartan_count); assert(byte == 255); } } @@ -440,7 +440,7 @@ test "binary math operator in partially inlined function" { var b: [16]u8 = undefined; for (b) |*r, i| - r.* = u8(i + 1); + r.* = @intCast(u8, i + 1); copyWithPartialInline(s[0..], b[0..]); assert(s[0] == 0x1020304); @@ -480,7 +480,7 @@ fn generateTable(comptime T: type) [1010]T { var res: [1010]T = undefined; var i: usize = 0; while (i < 1010) : (i += 1) { - res[i] = T(i); + res[i] = @intCast(T, i); } return res; } diff --git a/test/cases/fn.zig b/test/cases/fn.zig index 12f22bfc35..47f7d5e688 100644 --- a/test/cases/fn.zig +++ b/test/cases/fn.zig @@ -80,7 +80,7 @@ test "function pointers" { fn4, }; for (fns) |f, i| { - assert(f() == u32(i) + 5); + assert(f() == @intCast(u32, i) + 5); } } fn fn1() u32 { diff --git a/test/cases/for.zig b/test/cases/for.zig index bdbab312f6..59d90c1b85 100644 --- a/test/cases/for.zig +++ b/test/cases/for.zig @@ -46,7 +46,7 @@ test "basic for loop" { buf_index += 1; } for (array) |item, index| { - buffer[buf_index] = u8(index); + buffer[buf_index] = @intCast(u8, index); buf_index += 1; } const unknown_size: []const u8 = array; @@ -55,7 +55,7 @@ test "basic for loop" { buf_index += 1; } for (unknown_size) |item, index| { - buffer[buf_index] = u8(index); + buffer[buf_index] = @intCast(u8, index); buf_index += 1; } diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 6952611a8c..94a2ba6336 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -365,14 +365,14 @@ test "runtime struct initialization of bitfield" { .y = x1, }; const s2 = Nibbles{ - .x = u4(x2), - .y = u4(x2), + .x = @intCast(u4, x2), + .y = @intCast(u4, x2), }; assert(s1.x == x1); assert(s1.y == x1); - assert(s2.x == u4(x2)); - assert(s2.y == u4(x2)); + assert(s2.x == @intCast(u4, x2)); + assert(s2.y == @intCast(u4, x2)); } var x1 = u4(1); -- cgit v1.2.3 From 3c12ba718029adac707eeb6e86399f71b74c892e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 17 Jun 2018 04:32:57 -0400 Subject: update test cases --- test/compare_output.zig | 4 ++-- test/compile_errors.zig | 8 ++++---- test/runtime_safety.zig | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/compare_output.zig b/test/compare_output.zig index eec077ef85..b60b844b1c 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -331,8 +331,8 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ } \\ const small: f32 = 3.25; \\ const x: f64 = small; - \\ const y = i32(x); - \\ const z = f64(y); + \\ const y = @floatToInt(i32, x); + \\ const z = @intToFloat(f64, y); \\ _ = c.printf(c"%.2f\n%d\n%.2f\n%.2f\n", x, y, z, f64(-0.4)); \\ return 0; \\} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index ed46e43066..b51a6e9761 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2931,10 +2931,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "cast negative value to unsigned integer", \\comptime { \\ const value: i32 = -1; - \\ const unsigned = u32(value); + \\ const unsigned = @intCast(u32, value); \\} , - ".tmp_source.zig:3:25: error: attempt to cast negative value to unsigned integer", + ".tmp_source.zig:3:22: error: attempt to cast negative value to unsigned integer", ); cases.add( @@ -2963,10 +2963,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "compile-time integer cast truncates bits", \\comptime { \\ const spartan_count: u16 = 300; - \\ const byte = u8(spartan_count); + \\ const byte = @intCast(u8, spartan_count); \\} , - ".tmp_source.zig:3:20: error: cast from 'u16' to 'u8' truncates bits", + ".tmp_source.zig:3:18: error: cast from 'u16' to 'u8' truncates bits", ); cases.add( diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 61eba9458e..96384066e5 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -188,7 +188,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ if (x == 0) return error.Whatever; \\} \\fn shorten_cast(x: i32) i8 { - \\ return i8(x); + \\ return @intCast(i8, x); \\} ); @@ -201,7 +201,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ if (x == 0) return error.Whatever; \\} \\fn unsigned_cast(x: i32) u32 { - \\ return u32(x); + \\ return @intCast(u32, x); \\} ); -- cgit v1.2.3 From 74ccf56a4b1da78b6cd6b0ac34dd6ded1e15b155 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 17 Jun 2018 12:33:24 -0400 Subject: update more tests --- example/hello_world/hello_libc.zig | 2 +- test/compare_output.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig index f64beda40f..60a1f76871 100644 --- a/example/hello_world/hello_libc.zig +++ b/example/hello_world/hello_libc.zig @@ -8,7 +8,7 @@ const c = @cImport({ const msg = c"Hello, world!\n"; export fn main(argc: c_int, argv: **u8) c_int { - if (c.printf(msg) != c_int(c.strlen(msg))) return -1; + if (c.printf(msg) != @intCast(c_int, c.strlen(msg))) return -1; return 0; } diff --git a/test/compare_output.zig b/test/compare_output.zig index b60b844b1c..a18a78b419 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -299,7 +299,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\export fn main() c_int { \\ var array = []u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; \\ - \\ c.qsort(@ptrCast(?*c_void, array[0..].ptr), c_ulong(array.len), @sizeOf(i32), compare_fn); + \\ c.qsort(@ptrCast(?*c_void, array[0..].ptr), @intCast(c_ulong, array.len), @sizeOf(i32), compare_fn); \\ \\ for (array) |item, i| { \\ if (item != i) { -- cgit v1.2.3 From e5956f23ca702b79a3a4b0f0440a2fe88e0231e5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 17 Jun 2018 12:47:27 -0400 Subject: add target C int type information for msp430 target closes #1125 --- src/target.cpp | 50 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/target.cpp b/src/target.cpp index bd4aa4d4c2..c717c533df 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -686,21 +686,41 @@ static int get_arch_pointer_bit_width(ZigLLVM_ArchType arch) { uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { switch (target->os) { case OsFreestanding: - switch (id) { - case CIntTypeShort: - case CIntTypeUShort: - return 16; - case CIntTypeInt: - case CIntTypeUInt: - return 32; - case CIntTypeLong: - case CIntTypeULong: - return get_arch_pointer_bit_width(target->arch.arch); - case CIntTypeLongLong: - case CIntTypeULongLong: - return 64; - case CIntTypeCount: - zig_unreachable(); + switch (target->arch.arch) { + case ZigLLVM_msp430: + switch (id) { + case CIntTypeShort: + case CIntTypeUShort: + return 16; + case CIntTypeInt: + case CIntTypeUInt: + return 16; + case CIntTypeLong: + case CIntTypeULong: + return get_arch_pointer_bit_width(target->arch.arch); + case CIntTypeLongLong: + case CIntTypeULongLong: + return 64; + case CIntTypeCount: + zig_unreachable(); + } + default: + switch (id) { + case CIntTypeShort: + case CIntTypeUShort: + return 16; + case CIntTypeInt: + case CIntTypeUInt: + return 32; + case CIntTypeLong: + case CIntTypeULong: + return get_arch_pointer_bit_width(target->arch.arch); + case CIntTypeLongLong: + case CIntTypeULongLong: + return 64; + case CIntTypeCount: + zig_unreachable(); + } } case OsLinux: case OsMacOSX: -- cgit v1.2.3 From 906ed059ce7c5c9acb5088d38d9da76ad5c93407 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 00:52:55 -0400 Subject: update std.DynLib to use @intCast --- std/dynamic_library.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig index 8fe5f7f818..ed190f7deb 100644 --- a/std/dynamic_library.zig +++ b/std/dynamic_library.zig @@ -16,7 +16,7 @@ pub const DynLib = struct { const fd = try std.os.posixOpen(allocator, path, 0, linux.O_RDONLY | linux.O_CLOEXEC); errdefer std.os.close(fd); - const size = usize((try std.os.posixFStat(fd)).size); + const size = @intCast(usize, (try std.os.posixFStat(fd)).size); const addr = linux.mmap( null, @@ -126,8 +126,8 @@ pub const ElfLib = struct { var i: usize = 0; while (i < self.hashtab[1]) : (i += 1) { - if (0 == (u32(1) << u5(self.syms[i].st_info & 0xf) & OK_TYPES)) continue; - if (0 == (u32(1) << u5(self.syms[i].st_info >> 4) & OK_BINDS)) continue; + if (0 == (u32(1) << @intCast(u5, self.syms[i].st_info & 0xf) & OK_TYPES)) continue; + if (0 == (u32(1) << @intCast(u5, self.syms[i].st_info >> 4) & OK_BINDS)) continue; if (0 == self.syms[i].st_shndx) continue; if (!mem.eql(u8, name, cstr.toSliceConst(self.strings + self.syms[i].st_name))) continue; if (maybe_versym) |versym| { -- cgit v1.2.3 From 92a36040b1d0882c1e9439b998d3b855e84b9f2c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 01:03:45 -0400 Subject: msp430 target: c_long is always 32 bits closes #1125 --- src/target.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target.cpp b/src/target.cpp index c717c533df..91d36c5109 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -697,7 +697,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { return 16; case CIntTypeLong: case CIntTypeULong: - return get_arch_pointer_bit_width(target->arch.arch); + return 32; case CIntTypeLongLong: case CIntTypeULongLong: return 64; -- cgit v1.2.3 From d52ef95f77478582e5c479cc2bd0a4ecb5591933 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 01:09:51 -0400 Subject: disable failing macos test. see #1126 I'm unable to reproduce the failure on my mac laptop more investigation required --- test/build_examples.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/build_examples.zig b/test/build_examples.zig index b48fcbb698..79192c3e9a 100644 --- a/test/build_examples.zig +++ b/test/build_examples.zig @@ -13,7 +13,10 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void { cases.addBuildFile("example/shared_library/build.zig"); cases.addBuildFile("example/mix_o_files/build.zig"); } - cases.addBuildFile("test/standalone/issue_339/build.zig"); + if (builtin.os != builtin.Os.macosx) { + // TODO https://github.com/ziglang/zig/issues/1126 + cases.addBuildFile("test/standalone/issue_339/build.zig"); + } cases.addBuildFile("test/standalone/issue_794/build.zig"); cases.addBuildFile("test/standalone/pkg_import/build.zig"); cases.addBuildFile("test/standalone/use_alias/build.zig"); -- cgit v1.2.3 From 4210f1f6a0f55fb7c7b287ac691582752340f79d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 03:07:16 -0400 Subject: remove bool to int syntax. add @boolToInt add missing docs See #1061 --- doc/langref.html.in | 90 ++++++++++++++++++++++++++-------- src/all_types.hpp | 8 +++ src/codegen.cpp | 2 + src/ir.cpp | 60 ++++++++++++++++++++--- src/ir_print.cpp | 9 ++++ std/fmt/errol/index.zig | 4 +- std/math/big/int.zig | 18 +++---- std/special/compiler_rt/comparetf2.zig | 2 +- test/cases/bool.zig | 8 +-- 9 files changed, 158 insertions(+), 43 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 35ca9a13b4..7c1f9b53d9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4560,6 +4560,19 @@ comptime {

    {#see_also|Alignment#} {#header_close#} + + {#header_open|@boolToInt#} +
    @boolToInt(value: bool) u1
    +

    + Converts true to u1(1) and false to + u1(0). +

    +

    + If the value is known at compile-time, the return type is comptime_int + instead of u1. +

    + {#header_close#} + {#header_open|@cDefine#}
    @cDefine(comptime name: []u8, value)

    @@ -4834,21 +4847,6 @@ test "main" { Creates a symbol in the output object file.

    {#header_close#} - {#header_open|@tagName#} -
    @tagName(value: var) []const u8
    -

    - Converts an enum value or union value to a slice of bytes representing the name. -

    - {#header_close#} - {#header_open|@TagType#} -
    @TagType(T: type) type
    -

    - For an enum, returns the integer type that is used to store the enumeration value. -

    -

    - For a union, returns the enum type that is used to store the tag value. -

    - {#header_close#} {#header_open|@errorName#}
    @errorName(err: error) []u8

    @@ -4883,6 +4881,12 @@ test "main" {

    {#see_also|Compile Variables#} {#header_close#} + + {#header_open|@field#} +
    @field(lhs: var, comptime field_name: []const u8) (field)
    +

    Preforms field access equivalent to lhs.->field_name-<.

    + {#header_close#} + {#header_open|@fieldParentPtr#}
    @fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8,
         field_ptr: *T) *ParentType
    @@ -4890,6 +4894,23 @@ test "main" { Given a pointer to a field, returns the base pointer of a struct.

    {#header_close#} + + {#header_open|@floatCast#} +
    @floatCast(comptime DestType: type, value: var) DestType
    +

    + Convert from one float type to another. This cast is safe, but may cause the + numeric value to lose precision. +

    + {#header_close#} + + {#header_open|@floatToInt#} +
    @floatToInt(comptime DestType: type, float: var) DestType
    +

    + Converts the integer part of a floating point number to the destination type. + To convert the other way, use {#link|@intToFloat#}. This cast is always safe. +

    + {#header_close#} + {#header_open|@frameAddress#}
    @frameAddress()

    @@ -4944,12 +4965,30 @@ fn add(a: i32, b: i32) i32 { return a + b; }

    {#see_also|@noInlineCall#} {#header_close#} + + {#header_open|@intCast#} +
    @intCast(comptime DestType: type, int: var) DestType
    +

    + Converts an integer to another integer while keeping the same numerical value. + Attempting to convert a number which is out of range of the destination type results in + {#link|Undefined Behavior#}. +

    + {#header_close#} + + {#header_open|@intToFloat#} +
    @intToFloat(comptime DestType: type, int: var) DestType
    +

    + Converts an integer to the closest floating point representation. To convert the other way, use {#link|@floatToInt#}. This cast is always safe. +

    + {#header_close#} + {#header_open|@intToPtr#}
    @intToPtr(comptime DestType: type, int: usize) DestType

    Converts an integer to a pointer. To convert the other way, use {#link|@ptrToInt#}.

    {#header_close#} + {#header_open|@IntType#}
    @IntType(comptime is_signed: bool, comptime bit_count: u8) type

    @@ -4987,10 +5026,6 @@ fn add(a: i32, b: i32) i32 { return a + b; } It does not include functions, variables, or constants.

    {#header_close#} - {#header_open|@field#} -
    @field(lhs: var, comptime field_name: []const u8) (field)
    -

    Preforms field access equivalent to lhs.->field_name-<.

    - {#header_close#} {#header_open|@memberType#}
    @memberType(comptime T: type, comptime index: usize) type

    Returns the field type of a struct or union.

    @@ -5370,6 +5405,21 @@ pub const FloatMode = enum { If no overflow or underflow occurs, returns false.

    {#header_close#} + {#header_open|@tagName#} +
    @tagName(value: var) []const u8
    +

    + Converts an enum value or union value to a slice of bytes representing the name. +

    + {#header_close#} + {#header_open|@TagType#} +
    @TagType(T: type) type
    +

    + For an enum, returns the integer type that is used to store the enumeration value. +

    +

    + For a union, returns the enum type that is used to store the tag value. +

    + {#header_close#} {#header_open|@truncate#}
    @truncate(comptime T: type, integer) T

    @@ -6665,7 +6715,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index bc2fe07c18..d94dfa0fcb 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1361,6 +1361,7 @@ enum BuiltinFnId { BuiltinFnIdFloatCast, BuiltinFnIdIntToFloat, BuiltinFnIdFloatToInt, + BuiltinFnIdBoolToInt, BuiltinFnIdIntType, BuiltinFnIdSetCold, BuiltinFnIdSetRuntimeSafety, @@ -2048,6 +2049,7 @@ enum IrInstructionId { IrInstructionIdFloatCast, IrInstructionIdIntToFloat, IrInstructionIdFloatToInt, + IrInstructionIdBoolToInt, IrInstructionIdIntType, IrInstructionIdBoolNot, IrInstructionIdMemset, @@ -2668,6 +2670,12 @@ struct IrInstructionFloatToInt { IrInstruction *target; }; +struct IrInstructionBoolToInt { + IrInstruction base; + + IrInstruction *target; +}; + struct IrInstructionIntType { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 4108cbbd68..84335d4e06 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4726,6 +4726,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdFloatCast: case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: + case IrInstructionIdBoolToInt: zig_unreachable(); case IrInstructionIdReturn: @@ -6318,6 +6319,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdFloatCast, "floatCast", 2); create_builtin_fn(g, BuiltinFnIdIntToFloat, "intToFloat", 2); create_builtin_fn(g, BuiltinFnIdFloatToInt, "floatToInt", 2); + create_builtin_fn(g, BuiltinFnIdBoolToInt, "boolToInt", 1); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int diff --git a/src/ir.cpp b/src/ir.cpp index 0b847fc4e4..e6339a72f6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -476,6 +476,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatToInt *) { return IrInstructionIdFloatToInt; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolToInt *) { + return IrInstructionIdBoolToInt; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) { return IrInstructionIdIntType; } @@ -1959,6 +1963,15 @@ static IrInstruction *ir_build_float_to_int(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } +static IrInstruction *ir_build_bool_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { + IrInstructionBoolToInt *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *is_signed, IrInstruction *bit_count) { IrInstructionIntType *instruction = ir_build_instruction(irb, scope, source_node); instruction->is_signed = is_signed; @@ -4071,6 +4084,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } + case BuiltinFnIdBoolToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *result = ir_build_bool_to_int(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval); + } case BuiltinFnIdIntType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -10055,13 +10078,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } - // explicit cast from bool to int - if (wanted_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdBool) - { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBoolToInt, false); - } - // explicit widening conversion if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdInt && @@ -17605,6 +17621,33 @@ static TypeTableEntry *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrIns return dest_type; } +static TypeTableEntry *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstructionBoolToInt *instruction) { + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (target->value.type->id != TypeTableEntryIdBool) { + ir_add_error(ira, instruction->target, buf_sprintf("expected bool, found '%s'", + buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + if (instr_is_comptime(target)) { + bool is_true; + if (!ir_resolve_bool(ira, target, &is_true)) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + bigint_init_unsigned(&out_val->data.x_bigint, is_true ? 1 : 0); + return ira->codegen->builtin_types.entry_num_lit_int; + } + + TypeTableEntry *u1_type = get_int_type(ira->codegen, false, 1); + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, u1_type, CastOpBoolToInt, false); + ir_link_new_instruction(result, &instruction->base); + return u1_type; +} + static TypeTableEntry *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstructionIntType *instruction) { IrInstruction *is_signed_value = instruction->is_signed->other; bool is_signed; @@ -20143,6 +20186,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction); case IrInstructionIdFloatToInt: return ir_analyze_instruction_float_to_int(ira, (IrInstructionFloatToInt *)instruction); + case IrInstructionIdBoolToInt: + return ir_analyze_instruction_bool_to_int(ira, (IrInstructionBoolToInt *)instruction); case IrInstructionIdIntType: return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction); case IrInstructionIdBoolNot: @@ -20490,6 +20535,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdFloatCast: case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: + case IrInstructionIdBoolToInt: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b5722c52fa..cb91720180 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -680,6 +680,12 @@ static void ir_print_float_to_int(IrPrint *irp, IrInstructionFloatToInt *instruc fprintf(irp->f, ")"); } +static void ir_print_bool_to_int(IrPrint *irp, IrInstructionBoolToInt *instruction) { + fprintf(irp->f, "@boolToInt("); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) { fprintf(irp->f, "@IntType("); ir_print_other_instruction(irp, instruction->is_signed); @@ -1461,6 +1467,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFloatToInt: ir_print_float_to_int(irp, (IrInstructionFloatToInt *)instruction); break; + case IrInstructionIdBoolToInt: + ir_print_bool_to_int(irp, (IrInstructionBoolToInt *)instruction); + break; case IrInstructionIdIntType: ir_print_int_type(irp, (IrInstructionIntType *)instruction); break; diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index a5fb692857..3222913107 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -329,7 +329,7 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { var mi: i32 = mismatch10(l64, h64); var x: u64 = 1; { - var i = i32(lf == hf); + var i: i32 = @boolToInt(lf == hf); while (i < mi) : (i += 1) { x *= 10; } @@ -341,7 +341,7 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { var buf_index = u64toa(m64, buffer) - 1; if (mi != 0) { - buffer[buf_index - 1] += u8(buffer[buf_index] >= '5'); + buffer[buf_index - 1] += @boolToInt(buffer[buf_index] >= '5'); } else { buf_index += 1; } diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 21d9347c01..29673538eb 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -117,7 +117,7 @@ pub const Int = struct { fn bitcount(self: Int) usize { const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); - return usize(!self.positive) + u_bit_count; + return usize(@boolToInt(!self.positive)) + u_bit_count; } pub fn sizeInBase(self: Int, base: usize) usize { @@ -499,13 +499,13 @@ pub const Int = struct { while (i < b.len) : (i += 1) { var c: Limb = 0; - c += Limb(@addWithOverflow(Limb, a[i], b[i], &r[i])); - c += Limb(@addWithOverflow(Limb, r[i], carry, &r[i])); + c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i])); + c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); carry = c; } while (i < a.len) : (i += 1) { - carry = Limb(@addWithOverflow(Limb, a[i], carry, &r[i])); + carry = @boolToInt(@addWithOverflow(Limb, a[i], carry, &r[i])); } r[i] = carry; @@ -577,13 +577,13 @@ pub const Int = struct { while (i < b.len) : (i += 1) { var c: Limb = 0; - c += Limb(@subWithOverflow(Limb, a[i], b[i], &r[i])); - c += Limb(@subWithOverflow(Limb, r[i], borrow, &r[i])); + c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i])); + c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i])); borrow = c; } while (i < a.len) : (i += 1) { - borrow = Limb(@subWithOverflow(Limb, a[i], borrow, &r[i])); + borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i])); } debug.assert(borrow == 0); @@ -624,7 +624,7 @@ pub const Int = struct { var r1: Limb = undefined; // r1 = a + *carry - const c1 = Limb(@addWithOverflow(Limb, a, carry.*, &r1)); + const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1)); // r2 = b * c // @@ -639,7 +639,7 @@ pub const Int = struct { const c2 = @truncate(Limb, bc >> Limb.bit_count); // r1 = r1 + r2 - const c3 = Limb(@addWithOverflow(Limb, r1, r2, &r1)); + const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1)); // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then // c2 is at least <= @maxValue(Limb) - 2. diff --git a/std/special/compiler_rt/comparetf2.zig b/std/special/compiler_rt/comparetf2.zig index d63b7a7c92..479ba51962 100644 --- a/std/special/compiler_rt/comparetf2.zig +++ b/std/special/compiler_rt/comparetf2.zig @@ -91,5 +91,5 @@ pub extern fn __unordtf2(a: f128, b: f128) c_int { const aAbs = @bitCast(rep_t, a) & absMask; const bAbs = @bitCast(rep_t, b) & absMask; - return c_int(aAbs > infRep or bAbs > infRep); + return @boolToInt(aAbs > infRep or bAbs > infRep); } diff --git a/test/cases/bool.zig b/test/cases/bool.zig index 07d30454ee..3e4ac9c1cf 100644 --- a/test/cases/bool.zig +++ b/test/cases/bool.zig @@ -8,14 +8,14 @@ test "bool literals" { test "cast bool to int" { const t = true; const f = false; - assert(i32(t) == i32(1)); - assert(i32(f) == i32(0)); + assert(@boolToInt(t) == u32(1)); + assert(@boolToInt(f) == u32(0)); nonConstCastBoolToInt(t, f); } fn nonConstCastBoolToInt(t: bool, f: bool) void { - assert(i32(t) == i32(1)); - assert(i32(f) == i32(0)); + assert(@boolToInt(t) == u32(1)); + assert(@boolToInt(f) == u32(0)); } test "bool cmp" { -- cgit v1.2.3 From e6b69151c0d30d2df95d1f92b65784eac7d186d9 Mon Sep 17 00:00:00 2001 From: Bodie Solomon Date: Sun, 17 Jun 2018 14:35:00 -0400 Subject: Fix 1117: Use realpath in stage1 Darwin os_self_exe_path Issue: https://github.com/ziglang/zig/issues/1117 The macOS stage1 Zig compiler should look in Zig's real absolute path for the Zig stdlib, but os_self_exe_path looks in its path as returned by _NSGetExecutablePath, which may be a symlink. This means that a symlinked Zig cannot find the Zig stdlib. This patch fixes the issue by resolving the _NSGetExecutablePath result to the real path using realpath() before copying the result to the output path. --- src/os.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/os.cpp b/src/os.cpp index 97462bd658..75e6dd8658 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -989,12 +989,28 @@ int os_self_exe_path(Buf *out_path) { } #elif defined(ZIG_OS_DARWIN) + // How long is the executable's path? uint32_t u32_len = 0; int ret1 = _NSGetExecutablePath(nullptr, &u32_len); assert(ret1 != 0); - buf_resize(out_path, u32_len); - int ret2 = _NSGetExecutablePath(buf_ptr(out_path), &u32_len); + + // Allocate a buffer for this path. + Buf *path_tmp = buf_alloc_fixed(u32_len); + // Fill the buffer with the path, which may be a symlink. + int ret2 = _NSGetExecutablePath(buf_ptr(path_tmp), &u32_len); assert(ret2 == 0); + + // Make a buffer with room for the real path. + Buf *resolve_tmp = buf_alloc_fixed(PATH_MAX); + + // Fill it with the real resolved path. + char *real_path = realpath(buf_ptr(path_tmp), buf_ptr(resolve_tmp)); + // IEEE Std 1003.1-2017: realpath() shall return a pointer to the + // buffer containing the resolved name. + assert(real_path == buf_ptr(resolve_tmp)); + + // Resize out_path and copy the resulting resolved absolute path. + buf_init_from_buf(out_path, resolve_tmp); return 0; #elif defined(ZIG_OS_LINUX) buf_resize(out_path, 256); -- cgit v1.2.3 From 045682289243c6186363e984babc706c3ed93152 Mon Sep 17 00:00:00 2001 From: Bodie Solomon Date: Mon, 18 Jun 2018 07:01:59 -0400 Subject: Fix 1117: Tweak realpath logic to use out_path as scratch space --- src/os.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/os.cpp b/src/os.cpp index 75e6dd8658..0e3c3f9a60 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -994,23 +994,23 @@ int os_self_exe_path(Buf *out_path) { int ret1 = _NSGetExecutablePath(nullptr, &u32_len); assert(ret1 != 0); - // Allocate a buffer for this path. - Buf *path_tmp = buf_alloc_fixed(u32_len); - // Fill the buffer with the path, which may be a symlink. - int ret2 = _NSGetExecutablePath(buf_ptr(path_tmp), &u32_len); + // Make room for the executable path and resolved path in out_path, + // then subslice it for convenience. + buf_resize(out_path, u32_len + PATH_MAX); + Buf *tmp = buf_slice(out_path, 0, u32_len); + Buf *resolved = buf_slice(out_path, u32_len, u32_len + PATH_MAX); + + // Fill the executable path. + int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len); assert(ret2 == 0); - // Make a buffer with room for the real path. - Buf *resolve_tmp = buf_alloc_fixed(PATH_MAX); + // Resolve the real path from that. + char *real_path = realpath(buf_ptr(tmp), buf_ptr(resolved)); + assert(real_path == buf_ptr(resolved)); - // Fill it with the real resolved path. - char *real_path = realpath(buf_ptr(path_tmp), buf_ptr(resolve_tmp)); - // IEEE Std 1003.1-2017: realpath() shall return a pointer to the - // buffer containing the resolved name. - assert(real_path == buf_ptr(resolve_tmp)); - - // Resize out_path and copy the resulting resolved absolute path. - buf_init_from_buf(out_path, resolve_tmp); + // Write the real path back into the beginning of out_path, resize. + buf_init_from_buf(out_path, resolved); + assert(buf_len(out_path) == buf_len(resolved)); return 0; #elif defined(ZIG_OS_LINUX) buf_resize(out_path, 256); -- cgit v1.2.3 From c7057bd25b2a99e5ac61963efd588f5fe4ba93c6 Mon Sep 17 00:00:00 2001 From: Bodie Solomon Date: Mon, 18 Jun 2018 07:37:26 -0400 Subject: Fix 1117: Revise realpath scratch logic --- src/os.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/os.cpp b/src/os.cpp index 0e3c3f9a60..75f748e2f0 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -994,23 +994,24 @@ int os_self_exe_path(Buf *out_path) { int ret1 = _NSGetExecutablePath(nullptr, &u32_len); assert(ret1 != 0); - // Make room for the executable path and resolved path in out_path, - // then subslice it for convenience. - buf_resize(out_path, u32_len + PATH_MAX); - Buf *tmp = buf_slice(out_path, 0, u32_len); - Buf *resolved = buf_slice(out_path, u32_len, u32_len + PATH_MAX); + // Make a buffer having room for the temp path. + Buf *tmp = buf_alloc_fixed(u32_len); // Fill the executable path. int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len); assert(ret2 == 0); // Resolve the real path from that. - char *real_path = realpath(buf_ptr(tmp), buf_ptr(resolved)); - assert(real_path == buf_ptr(resolved)); + buf_resize(out_path, PATH_MAX); + char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); + assert(real_path == buf_ptr(out_path)); + + // Deallocate our scratch space. + buf_deinit(tmp); + + // Resize out_path for the correct length. + buf_resize(out_path, strlen(buf_ptr(out_path))); - // Write the real path back into the beginning of out_path, resize. - buf_init_from_buf(out_path, resolved); - assert(buf_len(out_path) == buf_len(resolved)); return 0; #elif defined(ZIG_OS_LINUX) buf_resize(out_path, 256); -- cgit v1.2.3 From d49d6f0cde782f4ec3c1d623d58644c3e51a6ce9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 11:04:18 -0400 Subject: fix compiler crash when using @intToFloat with float literal closes #1132 --- src/ir.cpp | 6 ++++++ test/cases/cast.zig | 6 ++++++ test/compile_errors.zig | 8 ++++++++ 3 files changed, 20 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index e6339a72f6..a312b501ab 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -17602,6 +17602,12 @@ static TypeTableEntry *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrIns if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; + if (target->value.type->id != TypeTableEntryIdInt && target->value.type->id != TypeTableEntryIdComptimeInt) { + ir_add_error(ira, instruction->target, buf_sprintf("expected int type, found '%s'", + buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpIntToFloat, false); ir_link_new_instruction(result, &instruction->base); return dest_type; diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 7035740c54..f1e49c6d1f 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -414,3 +414,9 @@ test "@floatCast comptime_int and comptime_float" { assert(@typeOf(result) == f32); assert(result == 1234.0); } + +test "comptime_int @intToFloat" { + const result = @intToFloat(f32, 1234); + assert(@typeOf(result) == f32); + assert(result == 1234.0); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index b51a6e9761..23337ca479 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,14 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "non int passed to @intToFloat", + \\export fn entry() void { + \\ const x = @intToFloat(f32, 1.1); + \\} + , + ".tmp_source.zig:2:32: error: expected int type, found 'comptime_float'", + ); cases.add( "use implicit casts to assign null to non-nullable pointer", \\export fn entry() void { -- cgit v1.2.3 From 8fd7cc11e167c0b23892d6f22841bb6856d0f499 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 11:12:15 -0400 Subject: disallow opaque as a return type of fn type syntax closes #1115 --- src/analyze.cpp | 1 + src/ir.cpp | 5 +++++ test/compile_errors.zig | 10 ++++++++++ 3 files changed, 16 insertions(+) diff --git a/src/analyze.cpp b/src/analyze.cpp index 758bc1a045..10cdb0af6f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1022,6 +1022,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { ensure_complete_type(g, fn_type_id->return_type); if (type_is_invalid(fn_type_id->return_type)) return g->builtin_types.entry_invalid; + assert(fn_type_id->return_type->id != TypeTableEntryIdOpaque); } else { zig_panic("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"); } diff --git a/src/ir.cpp b/src/ir.cpp index a312b501ab..c75a3ae7c1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18676,6 +18676,11 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc fn_type_id.return_type = ir_resolve_type(ira, return_type_value); if (type_is_invalid(fn_type_id.return_type)) return ira->codegen->builtin_types.entry_invalid; + if (fn_type_id.return_type->id == TypeTableEntryIdOpaque) { + ir_add_error(ira, instruction->return_type, + buf_sprintf("return type cannot be opaque")); + return ira->codegen->builtin_types.entry_invalid; + } if (fn_type_id.cc == CallingConventionAsync) { if (instruction->async_allocator_type_value == nullptr) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 23337ca479..8c5abaaccc 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "use c_void as return type of fn ptr", + \\export fn entry() void { + \\ const a: fn () c_void = undefined; + \\} + , + ".tmp_source.zig:2:20: error: return type cannot be opaque", + ); + cases.add( "non int passed to @intToFloat", \\export fn entry() void { @@ -9,6 +18,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:2:32: error: expected int type, found 'comptime_float'", ); + cases.add( "use implicit casts to assign null to non-nullable pointer", \\export fn entry() void { -- cgit v1.2.3 From 48985a7e684ff32eb5d419a89eac3b92c08c3ee9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 12:02:30 -0400 Subject: langref: add docs for void see #367 --- doc/langref.html.in | 90 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 6 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 7c1f9b53d9..f1ae2bafaa 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1651,7 +1651,7 @@ fn foo(bytes: []u8) u32 {

    @ptrCast(*u32, f32(12.34)).*

    Instead, use {#link|@bitCast#}:

    @bitCast(u32, f32(12.34))
    -

    As an added benefit, the @bitcast version works at compile-time.

    +

    As an added benefit, the @bitCast version works at compile-time.

    {#see_also|Slices|Memory#} {#header_close#} {#header_close#} @@ -3551,13 +3551,91 @@ const optional_value: ?i32 = null;

    TODO: ptrcast builtin

    TODO: explain number literals vs concrete types

    {#header_close#} + {#header_open|void#} -

    TODO: assigning void has no codegen

    -

    TODO: hashmap with void becomes a set

    -

    TODO: difference between c_void and void

    -

    TODO: void is the default return value of functions

    -

    TODO: functions require assigning the return value

    +

    + void represents a type that has no value. Code that makes use of void values is + not included in the final generated code: +

    + {#code_begin|syntax#} +export fn entry() void { + var x: void = {}; + var y: void = {}; + x = y; +} + {#code_end#} +

    When this turns into LLVM IR, there is no code generated in the body of entry, + even in debug mode. For example, on x86_64:

    +
    0000000000000010 <entry>:
    +  10:	55                   	push   %rbp
    +  11:	48 89 e5             	mov    %rsp,%rbp
    +  14:	5d                   	pop    %rbp
    +  15:	c3                   	retq   
    +

    These assembly instructions do not have any code associated with the void values - + they only perform the function call prologue and epilog.

    +

    + void can be useful for instantiating generic types. For example, given a + Map(Key, Value), one can pass void for the Value + type to make it into a Set: +

    + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +test "turn HashMap into a set with void" { + var map = std.HashMap(i32, void, hash_i32, eql_i32).init(std.debug.global_allocator); + defer map.deinit(); + + _ = try map.put(1, {}); + _ = try map.put(2, {}); + + assert(map.contains(2)); + assert(!map.contains(3)); + + _ = map.remove(2); + assert(!map.contains(2)); +} + +fn hash_i32(x: i32) u32 { + return @bitCast(u32, x); +} + +fn eql_i32(a: i32, b: i32) bool { + return a == b; +} + {#code_end#} +

    Note that this is different than using a dummy value for the hash map value. + By using void as the type of the value, the hash map entry type has no value field, and + thus the hash map takes up less space. Further, all the code that deals with storing and loading the + value is deleted, as seen above. +

    +

    + void is distinct from c_void, which is defined like this: + pub const c_void = @OpaqueType();. + void has a known size of 0 bytes, and c_void has an unknown, but non-zero, size. +

    +

    + Expressions of type void are the only ones whose value can be ignored. For example: +

    + {#code_begin|test_err|expression value is ignored#} +test "ignoring expression value" { + foo(); +} + +fn foo() i32 { + return 1234; +} + {#code_end#} +

    However, if the expression has type void:

    + {#code_begin|test#} +test "ignoring expression value" { + foo(); +} + +fn foo() void {} + {#code_end#} {#header_close#} + {#header_open|this#}

    TODO: example of this referring to Self struct

    TODO: example of this referring to recursion function

    -- cgit v1.2.3 From 4ce36a64755b4b2ca8bb28e3ac91b23dfe90ade8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 12:18:39 -0400 Subject: adjust logic for finding the path to zig executable on darwin --- src/os.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/os.cpp b/src/os.cpp index 75f748e2f0..14e9effb1e 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1004,10 +1004,10 @@ int os_self_exe_path(Buf *out_path) { // Resolve the real path from that. buf_resize(out_path, PATH_MAX); char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); - assert(real_path == buf_ptr(out_path)); - - // Deallocate our scratch space. - buf_deinit(tmp); + if (!real_path) { + buf_init_from_buf(out_path, tmp); + return 0; + } // Resize out_path for the correct length. buf_resize(out_path, strlen(buf_ptr(out_path))); -- cgit v1.2.3 From cd4676a2338430d9e424f297b8b576143c5be180 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 12:54:03 -0400 Subject: stage1: update darwin code to workaround old libc bug See #1128 --- src/os.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/os.cpp b/src/os.cpp index 14e9effb1e..b7d2fd1de0 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -994,15 +994,15 @@ int os_self_exe_path(Buf *out_path) { int ret1 = _NSGetExecutablePath(nullptr, &u32_len); assert(ret1 != 0); - // Make a buffer having room for the temp path. Buf *tmp = buf_alloc_fixed(u32_len); // Fill the executable path. int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len); assert(ret2 == 0); - // Resolve the real path from that. - buf_resize(out_path, PATH_MAX); + // According to libuv project, PATH_MAX*2 works around a libc bug where + // the resolved path is sometimes bigger than PATH_MAX. + buf_resize(out_path, PATH_MAX*2); char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); if (!real_path) { buf_init_from_buf(out_path, tmp); -- cgit v1.2.3 From 1ca90b585692c9611c64412844d2f3a7b3e11340 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 13:55:03 -0400 Subject: zig fmt: support directories zig fmt accepts any number of file paths. For each one, if it is a file, then it formats the file. If it is a directory, then zig recursively scans the directory, formatting all files that end in `.zig`. it maintains a map of paths that have been seen already, to avoid softlink loops. closes #1068 --- src-self-hosted/main.zig | 63 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index ffe23d2ffe..f7f38130b5 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -700,6 +700,36 @@ const args_fmt_spec = []Flag{ }), }; +const Fmt = struct { + seen: std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8), + queue: std.LinkedList([]const u8), + any_error: bool, + + // file_path must outlive Fmt + fn addToQueue(self: *Fmt, file_path: []const u8) !void { + const new_node = try self.seen.allocator.construct(std.LinkedList([]const u8).Node{ + .prev = undefined, + .next = undefined, + .data = file_path, + }); + + if (try self.seen.put(file_path, {})) |_| return; + + self.queue.append(new_node); + } + + fn addDirToQueue(self: *Fmt, file_path: []const u8) !void { + var dir = try std.os.Dir.open(self.seen.allocator, file_path); + defer dir.close(); + while (try dir.next()) |entry| { + if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { + const full_path = try os.path.join(self.seen.allocator, file_path, entry.name); + try self.addToQueue(full_path); + } + } + } +}; + fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_fmt_spec, args); defer flags.deinit(); @@ -728,21 +758,38 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { } }; - var fmt_errors = false; + var fmt = Fmt{ + .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator), + .queue = std.LinkedList([]const u8).init(), + .any_error = false, + }; + for (flags.positionals.toSliceConst()) |file_path| { + try fmt.addToQueue(file_path); + } + + while (fmt.queue.popFirst()) |node| { + const file_path = node.data; + var file = try os.File.openRead(allocator, file_path); defer file.close(); - const source_code = io.readFileAlloc(allocator, file_path) catch |err| { - try stderr.print("unable to open '{}': {}\n", file_path, err); - fmt_errors = true; - continue; + const source_code = io.readFileAlloc(allocator, file_path) catch |err| switch (err) { + error.IsDir => { + try fmt.addDirToQueue(file_path); + continue; + }, + else => { + try stderr.print("unable to open '{}': {}\n", file_path, err); + fmt.any_error = true; + continue; + }, }; defer allocator.free(source_code); var tree = std.zig.parse(allocator, source_code) catch |err| { try stderr.print("error parsing file '{}': {}\n", file_path, err); - fmt_errors = true; + fmt.any_error = true; continue; }; defer tree.deinit(); @@ -755,7 +802,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { try errmsg.printToFile(&stderr_file, msg, color); } if (tree.errors.len != 0) { - fmt_errors = true; + fmt.any_error = true; continue; } @@ -769,7 +816,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { } } - if (fmt_errors) { + if (fmt.any_error) { os.exit(1); } } -- cgit v1.2.3 From 5d705fc6e35e75a604d3dbbb377ab01bf2b2b575 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 15:01:42 -0400 Subject: remove error set casting syntax. add `@errSetCast` See #1061 --- doc/langref.html.in | 31 ++++++++++++++++++------ src/all_types.hpp | 9 +++++++ src/codegen.cpp | 2 ++ src/ir.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++------ src/ir_print.cpp | 11 +++++++++ test/cases/error.zig | 4 ++-- 6 files changed, 109 insertions(+), 16 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index f1ae2bafaa..48f525fedc 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4919,12 +4919,15 @@ test "main" {

    {#see_also|@import#} {#header_close#} - {#header_open|@export#} -
    @export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8
    + + {#header_open|@errSetCast#} +
    @errSetCast(comptime T: DestType, value: var) DestType

    - Creates a symbol in the output object file. + Converts an error value from one error set to another error set. Attempting to convert an error + which is not in the destination error set results in {#link|Undefined Behavior#}.

    {#header_close#} + {#header_open|@errorName#}
    @errorName(err: error) []u8

    @@ -4949,6 +4952,14 @@ test "main" { stack trace object. Otherwise returns `null`.

    {#header_close#} + + {#header_open|@export#} +
    @export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8
    +

    + Creates a symbol in the output object file. +

    + {#header_close#} + {#header_open|@fence#}
    @fence(order: AtomicOrder)

    @@ -5817,10 +5828,10 @@ pub fn build(b: &Builder) void { {#header_open|Undefined Behavior#}

    Zig has many instances of undefined behavior. If undefined behavior is - detected at compile-time, Zig emits an error. Most undefined behavior that - cannot be detected at compile-time can be detected at runtime. In these cases, - Zig has safety checks. Safety checks can be disabled on a per-block basis - with {#link|setRuntimeSafety#}. The {#link|ReleaseFast#} + detected at compile-time, Zig emits a compile error and refuses to continue. + Most undefined behavior that cannot be detected at compile-time can be detected + at runtime. In these cases, Zig has safety checks. Safety checks can be disabled + on a per-block basis with {#link|setRuntimeSafety#}. The {#link|ReleaseFast#} build mode disables all safety checks in order to facilitate optimizations.

    @@ -6101,6 +6112,11 @@ comptime {

    TODO

    {#header_close#} + + {#header_open|Invalid Error Set Cast#} +

    TODO

    + {#header_close#} + {#header_open|Incorrect Pointer Alignment#}

    TODO

    @@ -6109,6 +6125,7 @@ comptime {

    TODO

    {#header_close#} + {#header_close#} {#header_open|Memory#}

    TODO: explain no default allocator in zig

    diff --git a/src/all_types.hpp b/src/all_types.hpp index d94dfa0fcb..732af239e2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1359,6 +1359,7 @@ enum BuiltinFnId { BuiltinFnIdTruncate, BuiltinFnIdIntCast, BuiltinFnIdFloatCast, + BuiltinFnIdErrSetCast, BuiltinFnIdIntToFloat, BuiltinFnIdFloatToInt, BuiltinFnIdBoolToInt, @@ -2121,6 +2122,7 @@ enum IrInstructionId { IrInstructionIdMergeErrRetTraces, IrInstructionIdMarkErrRetTracePtr, IrInstructionIdSqrt, + IrInstructionIdErrSetCast, }; struct IrInstruction { @@ -2656,6 +2658,13 @@ struct IrInstructionFloatCast { IrInstruction *target; }; +struct IrInstructionErrSetCast { + IrInstruction base; + + IrInstruction *dest_type; + IrInstruction *target; +}; + struct IrInstructionIntToFloat { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 84335d4e06..585881a9a5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4727,6 +4727,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: + case IrInstructionIdErrSetCast: zig_unreachable(); case IrInstructionIdReturn: @@ -6356,6 +6357,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); + create_builtin_fn(g, BuiltinFnIdErrSetCast, "errSetCast", 2); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index c75a3ae7c1..e5ba4114cf 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -468,6 +468,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatCast *) { return IrInstructionIdFloatCast; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionErrSetCast *) { + return IrInstructionIdErrSetCast; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToFloat *) { return IrInstructionIdIntToFloat; } @@ -1941,6 +1945,17 @@ static IrInstruction *ir_build_float_cast(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } +static IrInstruction *ir_build_err_set_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { + IrInstructionErrSetCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_int_to_float(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { IrInstructionIntToFloat *instruction = ir_build_instruction(irb, scope, source_node); instruction->dest_type = dest_type; @@ -4054,6 +4069,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } + case BuiltinFnIdErrSetCast: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } case BuiltinFnIdIntToFloat: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -10104,13 +10134,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } - // explicit error set cast - if (wanted_type->id == TypeTableEntryIdErrorSet && - actual_type->id == TypeTableEntryIdErrorSet) - { - return ir_analyze_err_set_cast(ira, source_instr, value, wanted_type); - } - // explicit cast from [N]T to []const T if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) { TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; @@ -17593,6 +17616,34 @@ static TypeTableEntry *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstr return dest_type; } +static TypeTableEntry *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstructionErrSetCast *instruction) { + TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + if (dest_type->id != TypeTableEntryIdErrorSet) { + ir_add_error(ira, instruction->dest_type, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (target->value.type->id != TypeTableEntryIdErrorSet) { + ir_add_error(ira, instruction->target, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *result = ir_analyze_err_set_cast(ira, &instruction->base, target, dest_type); + if (type_is_invalid(result->value.type)) + return ira->codegen->builtin_types.entry_invalid; + ir_link_new_instruction(result, &instruction->base); + return dest_type; +} + static TypeTableEntry *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); if (type_is_invalid(dest_type)) @@ -20193,6 +20244,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_int_cast(ira, (IrInstructionIntCast *)instruction); case IrInstructionIdFloatCast: return ir_analyze_instruction_float_cast(ira, (IrInstructionFloatCast *)instruction); + case IrInstructionIdErrSetCast: + return ir_analyze_instruction_err_set_cast(ira, (IrInstructionErrSetCast *)instruction); case IrInstructionIdIntToFloat: return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction); case IrInstructionIdFloatToInt: @@ -20544,6 +20597,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAtomicLoad: case IrInstructionIdIntCast: case IrInstructionIdFloatCast: + case IrInstructionIdErrSetCast: case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index cb91720180..2667c246a5 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -664,6 +664,14 @@ static void ir_print_float_cast(IrPrint *irp, IrInstructionFloatCast *instructio fprintf(irp->f, ")"); } +static void ir_print_err_set_cast(IrPrint *irp, IrInstructionErrSetCast *instruction) { + fprintf(irp->f, "@errSetCast("); + ir_print_other_instruction(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_int_to_float(IrPrint *irp, IrInstructionIntToFloat *instruction) { fprintf(irp->f, "@intToFloat("); ir_print_other_instruction(irp, instruction->dest_type); @@ -1461,6 +1469,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFloatCast: ir_print_float_cast(irp, (IrInstructionFloatCast *)instruction); break; + case IrInstructionIdErrSetCast: + ir_print_err_set_cast(irp, (IrInstructionErrSetCast *)instruction); + break; case IrInstructionIdIntToFloat: ir_print_int_to_float(irp, (IrInstructionIntToFloat *)instruction); break; diff --git a/test/cases/error.zig b/test/cases/error.zig index 693631fe2d..95890d8384 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -124,8 +124,8 @@ const Set2 = error{ }; fn testExplicitErrorSetCast(set1: Set1) void { - var x = Set2(set1); - var y = Set1(x); + var x = @errSetCast(Set2, set1); + var y = @errSetCast(Set1, x); assert(y == error.A); } -- cgit v1.2.3 From 1aafbae5be518309b4c2194cdc24e22642514519 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 17:25:29 -0400 Subject: remove []u8 casting syntax. add `@bytesToSlice` and `@sliceToBytes` See #1061 --- doc/langref.html.in | 45 +++++++--- src/all_types.hpp | 28 +++++++ src/codegen.cpp | 4 + src/ir.cpp | 217 ++++++++++++++++++++++++++++++++++++------------ src/ir_print.cpp | 20 +++++ std/heap.zig | 2 +- std/macho.zig | 2 +- std/mem.zig | 12 +-- std/net.zig | 2 +- std/os/windows/util.zig | 2 +- test/cases/align.zig | 2 +- test/cases/cast.zig | 8 +- test/cases/misc.zig | 4 +- test/cases/struct.zig | 4 +- test/compile_errors.zig | 21 ++--- 15 files changed, 277 insertions(+), 96 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 48f525fedc..9a3d5e5f17 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1456,8 +1456,7 @@ test "pointer array access" { // Taking an address of an individual element gives a // pointer to a single item. This kind of pointer // does not support pointer arithmetic. - - var array = []u8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + var array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; const ptr = &array[2]; assert(@typeOf(ptr) == *u8); @@ -1469,7 +1468,7 @@ test "pointer array access" { test "pointer slicing" { // In Zig, we prefer using slices over null-terminated pointers. // You can turn an array into a slice using slice syntax: - var array = []u8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + var array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; const slice = array[2..4]; assert(slice.len == 2); @@ -1541,13 +1540,13 @@ test "pointer casting" { // To convert one pointer type to another, use @ptrCast. This is an unsafe // operation that Zig cannot protect you against. Use @ptrCast only when other // conversions are not possible. - const bytes align(@alignOf(u32)) = []u8{0x12, 0x12, 0x12, 0x12}; + const bytes align(@alignOf(u32)) = []u8{ 0x12, 0x12, 0x12, 0x12 }; const u32_ptr = @ptrCast(*const u32, &bytes[0]); assert(u32_ptr.* == 0x12121212); // Even this example is contrived - there are better ways to do the above than // pointer casting. For example, using a slice narrowing cast: - const u32_value = ([]const u32)(bytes[0..])[0]; + const u32_value = @bytesToSlice(u32, bytes[0..])[0]; assert(u32_value == 0x12121212); // And even another way, the most straightforward way to do it: @@ -1630,13 +1629,13 @@ test "function alignment" { const assert = @import("std").debug.assert; test "pointer alignment safety" { - var array align(4) = []u32{0x11111111, 0x11111111}; - const bytes = ([]u8)(array[0..]); + var array align(4) = []u32{ 0x11111111, 0x11111111 }; + const bytes = @sliceToBytes(array[0..]); assert(foo(bytes) == 0x11111111); } fn foo(bytes: []u8) u32 { const slice4 = bytes[1..5]; - const int_slice = ([]u32)(@alignCast(4, slice4)); + const int_slice = @bytesToSlice(u32, @alignCast(4, slice4)); return int_slice[0]; } {#code_end#} @@ -1728,8 +1727,8 @@ test "slice pointer" { test "slice widening" { // Zig supports slice widening and slice narrowing. Cast a slice of u8 // to a slice of anything else, and Zig will perform the length conversion. - const array align(@alignOf(u32)) = []u8{0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13}; - const slice = ([]const u32)(array[0..]); + const array align(@alignOf(u32)) = []u8{ 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13 }; + const slice = @bytesToSlice(u32, array[0..]); assert(slice.len == 2); assert(slice[0] == 0x12121212); assert(slice[1] == 0x13131313); @@ -4651,6 +4650,18 @@ comptime {

    {#header_close#} + {#header_open|@bytesToSlice#} +
    @bytesToSlice(comptime Element: type, bytes: []u8) []Element
    +

    + Converts a slice of bytes or array of bytes into a slice of Element. + The resulting slice has the same {#link|pointer|Pointers#} properties as the parameter. +

    +

    + Attempting to convert a number of bytes with a length that does not evenly divide into a slice of + elements results in {#link|Undefined Behavior#}. +

    + {#header_close#} + {#header_open|@cDefine#}
    @cDefine(comptime name: []u8, value)

    @@ -5467,8 +5478,9 @@ pub const FloatMode = enum {

    {#see_also|@shlExact|@shlWithOverflow#} {#header_close#} + {#header_open|@sizeOf#} -
    @sizeOf(comptime T: type) (number literal)
    +
    @sizeOf(comptime T: type) comptime_int

    This function returns the number of bytes it takes to store T in memory.

    @@ -5476,6 +5488,15 @@ pub const FloatMode = enum { The result is a target-specific compile time constant.

    {#header_close#} + + {#header_open|@sliceToBytes#} +
    @sliceToBytes(value: var) []u8
    +

    + Converts a slice or array to a slice of u8. The resulting slice has the same + {#link|pointer|Pointers#} properties as the parameter. +

    + {#header_close#} + {#header_open|@sqrt#}
    @sqrt(comptime T: type, value: T) T

    @@ -6810,7 +6831,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index 732af239e2..d8432232c2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -234,6 +234,16 @@ enum RuntimeHintPtr { RuntimeHintPtrNonStack, }; +enum RuntimeHintSliceId { + RuntimeHintSliceIdUnknown, + RuntimeHintSliceIdLen, +}; + +struct RuntimeHintSlice { + enum RuntimeHintSliceId id; + uint64_t len; +}; + struct ConstGlobalRefs { LLVMValueRef llvm_value; LLVMValueRef llvm_global; @@ -270,6 +280,7 @@ struct ConstExprValue { RuntimeHintErrorUnion rh_error_union; RuntimeHintOptional rh_maybe; RuntimeHintPtr rh_ptr; + RuntimeHintSlice rh_slice; } data; }; @@ -1360,6 +1371,8 @@ enum BuiltinFnId { BuiltinFnIdIntCast, BuiltinFnIdFloatCast, BuiltinFnIdErrSetCast, + BuiltinFnIdToBytes, + BuiltinFnIdFromBytes, BuiltinFnIdIntToFloat, BuiltinFnIdFloatToInt, BuiltinFnIdBoolToInt, @@ -2123,6 +2136,8 @@ enum IrInstructionId { IrInstructionIdMarkErrRetTracePtr, IrInstructionIdSqrt, IrInstructionIdErrSetCast, + IrInstructionIdToBytes, + IrInstructionIdFromBytes, }; struct IrInstruction { @@ -2665,6 +2680,19 @@ struct IrInstructionErrSetCast { IrInstruction *target; }; +struct IrInstructionToBytes { + IrInstruction base; + + IrInstruction *target; +}; + +struct IrInstructionFromBytes { + IrInstruction base; + + IrInstruction *dest_child_type; + IrInstruction *target; +}; + struct IrInstructionIntToFloat { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 585881a9a5..1bc9a17804 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4728,6 +4728,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: case IrInstructionIdErrSetCast: + case IrInstructionIdFromBytes: + case IrInstructionIdToBytes: zig_unreachable(); case IrInstructionIdReturn: @@ -6358,6 +6360,8 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); create_builtin_fn(g, BuiltinFnIdErrSetCast, "errSetCast", 2); + create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1); + create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index e5ba4114cf..c5ca12af1e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -472,6 +472,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrSetCast *) { return IrInstructionIdErrSetCast; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionToBytes *) { + return IrInstructionIdToBytes; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFromBytes *) { + return IrInstructionIdFromBytes; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToFloat *) { return IrInstructionIdIntToFloat; } @@ -1956,6 +1964,26 @@ static IrInstruction *ir_build_err_set_cast(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } +static IrInstruction *ir_build_to_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { + IrInstructionToBytes *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_from_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_child_type, IrInstruction *target) { + IrInstructionFromBytes *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_child_type = dest_child_type; + instruction->target = target; + + ir_ref_instruction(dest_child_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_int_to_float(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { IrInstructionIntToFloat *instruction = ir_build_instruction(irb, scope, source_node); instruction->dest_type = dest_type; @@ -4084,6 +4112,31 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } + case BuiltinFnIdFromBytes: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_from_bytes(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdToBytes: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *result = ir_build_to_bytes(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval); + } case BuiltinFnIdIntToFloat: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -9103,11 +9156,6 @@ static bool is_container(TypeTableEntry *type) { type->id == TypeTableEntryIdUnion; } -static bool is_u8(TypeTableEntry *type) { - return type->id == TypeTableEntryIdInt && - !type->data.integral.is_signed && type->data.integral.bit_count == 8; -} - static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb, IrInstruction *ref_old_instruction) { assert(old_bb); @@ -9661,6 +9709,8 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s IrInstruction *result = ir_build_slice(&ira->new_irb, source_instr->scope, source_instr->source_node, array_ptr, start, end, false); result->value.type = wanted_type; + result->value.data.rh_slice.id = RuntimeHintSliceIdLen; + result->value.data.rh_slice.len = array_type->data.array.len; ir_add_alloca(ira, result, result->value.type); return result; @@ -10103,7 +10153,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ira->codegen->invalid_instruction; } - // explicit match or non-const to const + // perfect match or non-const to const if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node, false).id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } @@ -10214,52 +10264,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from []T to []u8 or []u8 to []T - if (is_slice(wanted_type) && is_slice(actual_type)) { - TypeTableEntry *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; - TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; - if ((is_u8(wanted_ptr_type->data.pointer.child_type) || is_u8(actual_ptr_type->data.pointer.child_type)) && - (wanted_ptr_type->data.pointer.is_const || !actual_ptr_type->data.pointer.is_const)) - { - uint32_t src_align_bytes = get_ptr_align(actual_ptr_type); - uint32_t dest_align_bytes = get_ptr_align(wanted_ptr_type); - - if (dest_align_bytes > src_align_bytes) { - ErrorMsg *msg = ir_add_error(ira, source_instr, - buf_sprintf("cast increases pointer alignment")); - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), src_align_bytes)); - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), dest_align_bytes)); - return ira->codegen->invalid_instruction; - } - - if (!ir_emit_global_runtime_side_effect(ira, source_instr)) - return ira->codegen->invalid_instruction; - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpResizeSlice, true); - } - } - - // explicit cast from [N]u8 to []const T - if (is_slice(wanted_type) && - wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const && - actual_type->id == TypeTableEntryIdArray && - is_u8(actual_type->data.array.child_type)) - { - if (!ir_emit_global_runtime_side_effect(ira, source_instr)) - return ira->codegen->invalid_instruction; - uint64_t child_type_size = type_size(ira->codegen, - wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type); - if (actual_type->data.array.len % child_type_size == 0) { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBytesToSlice, true); - } else { - ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("unable to convert %s to %s: size mismatch", - buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name))); - return ira->codegen->invalid_instruction; - } - } - // explicit *[N]T to [*]T if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenUnknown && @@ -17644,6 +17648,109 @@ static TypeTableEntry *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrIns return dest_type; } +static TypeTableEntry *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) { + TypeTableEntry *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->other); + if (type_is_invalid(dest_child_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + bool src_ptr_const; + bool src_ptr_volatile; + uint32_t src_ptr_align; + if (target->value.type->id == TypeTableEntryIdPointer) { + src_ptr_const = target->value.type->data.pointer.is_const; + src_ptr_volatile = target->value.type->data.pointer.is_volatile; + src_ptr_align = target->value.type->data.pointer.alignment; + } else if (is_slice(target->value.type)) { + TypeTableEntry *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; + src_ptr_const = src_ptr_type->data.pointer.is_const; + src_ptr_volatile = src_ptr_type->data.pointer.is_volatile; + src_ptr_align = src_ptr_type->data.pointer.alignment; + } else { + src_ptr_const = true; + src_ptr_volatile = false; + src_ptr_align = get_abi_alignment(ira->codegen, target->value.type); + } + + TypeTableEntry *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_child_type, + src_ptr_const, src_ptr_volatile, PtrLenUnknown, + src_ptr_align, 0, 0); + TypeTableEntry *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); + + TypeTableEntry *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + src_ptr_const, src_ptr_volatile, PtrLenUnknown, + src_ptr_align, 0, 0); + TypeTableEntry *u8_slice = get_slice_type(ira->codegen, u8_ptr); + + IrInstruction *casted_value = ir_implicit_cast(ira, target, u8_slice); + if (type_is_invalid(casted_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + bool have_known_len = false; + uint64_t known_len; + + if (instr_is_comptime(casted_value)) { + ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad); + if (!val) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *len_val = &val->data.x_struct.fields[slice_len_index]; + if (value_is_comptime(len_val)) { + known_len = bigint_as_unsigned(&len_val->data.x_bigint); + have_known_len = true; + } + } + + if (casted_value->value.data.rh_slice.id == RuntimeHintSliceIdLen) { + known_len = casted_value->value.data.rh_slice.len; + have_known_len = true; + } + + if (have_known_len) { + uint64_t child_type_size = type_size(ira->codegen, dest_child_type); + uint64_t remainder = known_len % child_type_size; + if (remainder != 0) { + ErrorMsg *msg = ir_add_error(ira, &instruction->base, + buf_sprintf("unable to convert [%" ZIG_PRI_u64 "]u8 to %s: size mismatch", + known_len, buf_ptr(&dest_slice_type->name))); + add_error_note(ira->codegen, msg, instruction->dest_child_type->source_node, + buf_sprintf("%s has size %" ZIG_PRI_u64 "; remaining bytes: %" ZIG_PRI_u64, + buf_ptr(&dest_child_type->name), child_type_size, remainder)); + return ira->codegen->builtin_types.entry_invalid; + } + } + + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, casted_value, dest_slice_type, CastOpResizeSlice, true); + ir_link_new_instruction(result, &instruction->base); + return dest_slice_type; +} + +static TypeTableEntry *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToBytes *instruction) { + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (!is_slice(target->value.type)) { + ir_add_error(ira, instruction->target, + buf_sprintf("expected slice, found '%s'", buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + TypeTableEntry *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; + + TypeTableEntry *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + src_ptr_type->data.pointer.is_const, src_ptr_type->data.pointer.is_volatile, PtrLenUnknown, + src_ptr_type->data.pointer.alignment, 0, 0); + TypeTableEntry *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); + + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); + ir_link_new_instruction(result, &instruction->base); + return dest_slice_type; +} + static TypeTableEntry *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); if (type_is_invalid(dest_type)) @@ -20246,6 +20353,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_float_cast(ira, (IrInstructionFloatCast *)instruction); case IrInstructionIdErrSetCast: return ir_analyze_instruction_err_set_cast(ira, (IrInstructionErrSetCast *)instruction); + case IrInstructionIdFromBytes: + return ir_analyze_instruction_from_bytes(ira, (IrInstructionFromBytes *)instruction); + case IrInstructionIdToBytes: + return ir_analyze_instruction_to_bytes(ira, (IrInstructionToBytes *)instruction); case IrInstructionIdIntToFloat: return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction); case IrInstructionIdFloatToInt: @@ -20601,6 +20712,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: + case IrInstructionIdFromBytes: + case IrInstructionIdToBytes: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 2667c246a5..1b35ecf57f 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -672,6 +672,20 @@ static void ir_print_err_set_cast(IrPrint *irp, IrInstructionErrSetCast *instruc fprintf(irp->f, ")"); } +static void ir_print_from_bytes(IrPrint *irp, IrInstructionFromBytes *instruction) { + fprintf(irp->f, "@bytesToSlice("); + ir_print_other_instruction(irp, instruction->dest_child_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_to_bytes(IrPrint *irp, IrInstructionToBytes *instruction) { + fprintf(irp->f, "@sliceToBytes("); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_int_to_float(IrPrint *irp, IrInstructionIntToFloat *instruction) { fprintf(irp->f, "@intToFloat("); ir_print_other_instruction(irp, instruction->dest_type); @@ -1472,6 +1486,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdErrSetCast: ir_print_err_set_cast(irp, (IrInstructionErrSetCast *)instruction); break; + case IrInstructionIdFromBytes: + ir_print_from_bytes(irp, (IrInstructionFromBytes *)instruction); + break; + case IrInstructionIdToBytes: + ir_print_to_bytes(irp, (IrInstructionToBytes *)instruction); + break; case IrInstructionIdIntToFloat: ir_print_int_to_float(irp, (IrInstructionIntToFloat *)instruction); break; diff --git a/std/heap.zig b/std/heap.zig index 2a2c8c0b59..c948818e3d 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -221,7 +221,7 @@ pub const ArenaAllocator = struct { if (len >= actual_min_size) break; } const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len); - const buf_node_slice = ([]BufNode)(buf[0..@sizeOf(BufNode)]); + const buf_node_slice = @bytesToSlice(BufNode, buf[0..@sizeOf(BufNode)]); const buf_node = &buf_node_slice[0]; buf_node.* = BufNode{ .data = buf, diff --git a/std/macho.zig b/std/macho.zig index 64f78ae4a3..fe5409ad4d 100644 --- a/std/macho.zig +++ b/std/macho.zig @@ -161,7 +161,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable } fn readNoEof(in: *io.FileInStream, comptime T: type, result: []T) !void { - return in.stream.readNoEof(([]u8)(result)); + return in.stream.readNoEof(@sliceToBytes(result)); } fn readOneNoEof(in: *io.FileInStream, comptime T: type, result: *T) !void { return readNoEof(in, T, (*[1]T)(result)[0..]); diff --git a/std/mem.zig b/std/mem.zig index b02589b0dd..55844b88db 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -70,7 +70,7 @@ pub const Allocator = struct { for (byte_slice) |*byte| { byte.* = undefined; } - return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); + return @bytesToSlice(T, @alignCast(alignment, byte_slice)); } pub fn realloc(self: *Allocator, comptime T: type, old_mem: []T, n: usize) ![]T { @@ -86,7 +86,7 @@ pub const Allocator = struct { return ([*]align(alignment) T)(undefined)[0..0]; } - const old_byte_slice = ([]u8)(old_mem); + const old_byte_slice = @sliceToBytes(old_mem); const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment); assert(byte_slice.len == byte_count); @@ -96,7 +96,7 @@ pub const Allocator = struct { byte.* = undefined; } } - return ([]T)(@alignCast(alignment, byte_slice)); + return @bytesToSlice(T, @alignCast(alignment, byte_slice)); } /// Reallocate, but `n` must be less than or equal to `old_mem.len`. @@ -118,13 +118,13 @@ pub const Allocator = struct { // n <= old_mem.len and the multiplication didn't overflow for that operation. const byte_count = @sizeOf(T) * n; - const byte_slice = self.reallocFn(self, ([]u8)(old_mem), byte_count, alignment) catch unreachable; + const byte_slice = self.reallocFn(self, @sliceToBytes(old_mem), byte_count, alignment) catch unreachable; assert(byte_slice.len == byte_count); - return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); + return @bytesToSlice(T, @alignCast(alignment, byte_slice)); } pub fn free(self: *Allocator, memory: var) void { - const bytes = ([]const u8)(memory); + const bytes = @sliceToBytes(memory); if (bytes.len == 0) return; const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr)); self.freeFn(self, non_const_ptr[0..bytes.len]); diff --git a/std/net.zig b/std/net.zig index f21611ff91..8c1aeb92d7 100644 --- a/std/net.zig +++ b/std/net.zig @@ -68,7 +68,7 @@ pub const Address = struct { pub fn parseIp4(buf: []const u8) !u32 { var result: u32 = undefined; - const out_ptr = ([]u8)((*[1]u32)(&result)[0..]); + const out_ptr = @sliceToBytes((*[1]u32)(&result)[0..]); var x: u8 = 0; var index: u8 = 0; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index cb4788ba17..45b205451d 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -79,7 +79,7 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]); const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)]; - const name_wide = ([]u16)(name_bytes); + const name_wide = @bytesToSlice(u16, name_bytes); return mem.indexOf(u16, name_wide, []u16{ 'm', 's', 'y', 's', '-' }) != null or mem.indexOf(u16, name_wide, []u16{ '-', 'p', 't', 'y' }) != null; } diff --git a/test/cases/align.zig b/test/cases/align.zig index 682c185e86..64f0788efc 100644 --- a/test/cases/align.zig +++ b/test/cases/align.zig @@ -90,7 +90,7 @@ fn testBytesAlignSlice(b: u8) void { b, b, }; - const slice = ([]u32)(bytes[0..]); + const slice: []u32 = @bytesToSlice(u32, bytes[0..]); assert(slice[0] == 0x33333333); } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index f1e49c6d1f..0b79f0df48 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -372,7 +372,7 @@ test "const slice widen cast" { 0x12, }; - const u32_value = ([]const u32)(bytes[0..])[0]; + const u32_value = @bytesToSlice(u32, bytes[0..])[0]; assert(u32_value == 0x12121212); assert(@bitCast(u32, bytes) == 0x12121212); @@ -420,3 +420,9 @@ test "comptime_int @intToFloat" { assert(@typeOf(result) == f32); assert(result == 1234.0); } + +test "@bytesToSlice keeps pointer alignment" { + var bytes = []u8{ 0x01, 0x02, 0x03, 0x04 }; + const numbers = @bytesToSlice(u32, bytes[0..]); + comptime assert(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); +} diff --git a/test/cases/misc.zig b/test/cases/misc.zig index beb0d6d456..89c441e7f9 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -422,14 +422,14 @@ test "cast slice to u8 slice" { 4, }; const big_thing_slice: []i32 = big_thing_array[0..]; - const bytes = ([]u8)(big_thing_slice); + const bytes = @sliceToBytes(big_thing_slice); assert(bytes.len == 4 * 4); bytes[4] = 0; bytes[5] = 0; bytes[6] = 0; bytes[7] = 0; assert(big_thing_slice[1] == 0); - const big_thing_again = ([]align(1) i32)(bytes); + const big_thing_again = @bytesToSlice(i32, bytes); assert(big_thing_again[2] == 3); big_thing_again[2] = -1; assert(bytes[8] == @maxValue(u8)); diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 94a2ba6336..2941ecb56a 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -302,7 +302,7 @@ test "packed array 24bits" { var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); bytes[bytes.len - 1] = 0xaa; - const ptr = &([]FooArray24Bits)(bytes[0 .. bytes.len - 1])[0]; + const ptr = &@bytesToSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; assert(ptr.a == 0); assert(ptr.b[0].field == 0); assert(ptr.b[1].field == 0); @@ -351,7 +351,7 @@ test "aligned array of packed struct" { } var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); - const ptr = &([]FooArrayOfAligned)(bytes[0..bytes.len])[0]; + const ptr = &@bytesToSlice(FooArrayOfAligned, bytes[0..bytes.len])[0]; assert(ptr.a[0].a == 0xbb); assert(ptr.a[0].b == 0xbb); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 8c5abaaccc..2f4a22553b 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -404,10 +404,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\const Set2 = error {A, C}; \\comptime { \\ var x = Set1.B; - \\ var y = Set2(x); + \\ var y = @errSetCast(Set2, x); \\} , - ".tmp_source.zig:5:17: error: error.B not a member of error set 'Set2'", + ".tmp_source.zig:5:13: error: error.B not a member of error set 'Set2'", ); cases.add( @@ -2086,10 +2086,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "convert fixed size array to slice with invalid size", \\export fn f() void { \\ var array: [5]u8 = undefined; - \\ var foo = ([]const u32)(array)[0]; + \\ var foo = @bytesToSlice(u32, array)[0]; \\} , - ".tmp_source.zig:3:28: error: unable to convert [5]u8 to []const u32: size mismatch", + ".tmp_source.zig:3:15: error: unable to convert [5]u8 to []align(1) const u32: size mismatch", + ".tmp_source.zig:3:29: note: u32 has size 4; remaining bytes: 1", ); cases.add( @@ -3239,18 +3240,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:26: note: '*u32' has alignment 4", ); - cases.add( - "increase pointer alignment in slice resize", - \\export fn entry() u32 { - \\ var bytes = []u8{0x01, 0x02, 0x03, 0x04}; - \\ return ([]u32)(bytes[0..])[0]; - \\} - , - ".tmp_source.zig:3:19: error: cast increases pointer alignment", - ".tmp_source.zig:3:19: note: '[]u8' has alignment 1", - ".tmp_source.zig:3:19: note: '[]u32' has alignment 4", - ); - cases.add( "@alignCast expects pointer or slice", \\export fn entry() void { -- cgit v1.2.3 From a430853a48a5e4dbcd0094c632957e28898159f3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 17:43:01 -0400 Subject: standard library fixes --- src/ir.cpp | 2 +- std/cstr.zig | 2 +- std/os/index.zig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index c5ca12af1e..3ed9f66947 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10215,7 +10215,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from [N]T to &const []const N + // explicit cast from [N]T to &const []const T if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.is_const && is_slice(wanted_type->data.pointer.child_type) && diff --git a/std/cstr.zig b/std/cstr.zig index d9106769c1..e83d5a39e9 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -79,7 +79,7 @@ pub const NullTerminated2DArray = struct { errdefer allocator.free(buf); var write_index = index_size; - const index_buf = ([]?[*]u8)(buf); + const index_buf = @bytesToSlice(?[*]u8, buf); var i: usize = 0; for (slices) |slice| { diff --git a/std/os/index.zig b/std/os/index.zig index f1c3ab2128..dd0d4e2ea1 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1805,7 +1805,7 @@ pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 { const buf = try allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes); errdefer allocator.free(buf); - const result_slice_list = ([][]u8)(buf[0..slice_list_bytes]); + const result_slice_list = @bytesToSlice([]u8, buf[0..slice_list_bytes]); const result_contents = buf[slice_list_bytes..]; mem.copy(u8, result_contents, contents_slice); -- cgit v1.2.3 From 626b73e8beeaae1cab23f883f877d89d64bbfa39 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 18:48:29 -0400 Subject: remove error to/from int casting syntax; add `@errorToInt`/`@intToError` See #1061 --- doc/langref.html.in | 48 +++++++++++++++++++++++++++---- src/all_types.hpp | 2 ++ src/codegen.cpp | 2 ++ src/ir.cpp | 74 +++++++++++++++++++++++++++++++++++++----------- std/os/child_process.zig | 4 +-- test/cases/cast.zig | 4 +-- test/cases/error.zig | 10 +++---- test/cases/type_info.zig | 2 +- test/compile_errors.zig | 38 ++++++++++++------------- test/runtime_safety.zig | 14 ++++----- 10 files changed, 138 insertions(+), 60 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 9a3d5e5f17..24bf6e1b16 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4658,7 +4658,7 @@ comptime {

    Attempting to convert a number of bytes with a length that does not evenly divide into a slice of - elements results in {#link|Undefined Behavior#}. + elements results in safety-protected {#link|Undefined Behavior#}.

    {#header_close#} @@ -4935,7 +4935,7 @@ test "main" {
    @errSetCast(comptime T: DestType, value: var) DestType

    Converts an error value from one error set to another error set. Attempting to convert an error - which is not in the destination error set results in {#link|Undefined Behavior#}. + which is not in the destination error set results in safety-protected {#link|Undefined Behavior#}.

    {#header_close#} @@ -4955,6 +4955,7 @@ test "main" { error name table will be generated.

    {#header_close#} + {#header_open|@errorReturnTrace#}
    @errorReturnTrace() ?*builtin.StackTrace

    @@ -4964,6 +4965,25 @@ test "main" {

    {#header_close#} + {#header_open|@errorToInt#} +
    @errorToInt(err: var) @IntType(false, @sizeOf(error) * 8)
    +

    + Supports the following types: +

    +
      +
    • error unions
    • +
    • E!void
    • +
    +

    + Converts an error to the integer representation of an error. +

    +

    + It is generally recommended to avoid this + cast, as the integer representation of an error is not stable across source code changes. +

    + {#see_also|@intToError#} + {#header_close#} + {#header_open|@export#}
    @export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8

    @@ -5071,8 +5091,24 @@ fn add(a: i32, b: i32) i32 { return a + b; }

    Converts an integer to another integer while keeping the same numerical value. Attempting to convert a number which is out of range of the destination type results in - {#link|Undefined Behavior#}. + safety-protected {#link|Undefined Behavior#}. +

    + {#header_close#} + + {#header_open|@intToError#} +
    @intToError(value: @IntType(false, @sizeOf(error) * 8)) error
    +

    + Converts from the integer representation of an error into the global error set type. +

    +

    + It is generally recommended to avoid this + cast, as the integer representation of an error is not stable across source code changes. +

    +

    + Attempting to convert an integer that does not correspond to any error results in + safety-protected {#link|Undefined Behavior#}.

    + {#see_also|@errorToInt#} {#header_close#} {#header_open|@intToFloat#} @@ -6123,8 +6159,8 @@ fn getNumberOrFail() !i32 { {#code_begin|test_err|integer value 11 represents no error#} comptime { const err = error.AnError; - const number = u32(err) + 10; - const invalid_err = error(number); + const number = @errorToInt(err) + 10; + const invalid_err = @intToError(number); } {#code_end#}

    At runtime crashes with the message invalid error code and a stack trace.

    @@ -6831,7 +6867,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index d8432232c2..e1a4ed7510 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1376,6 +1376,8 @@ enum BuiltinFnId { BuiltinFnIdIntToFloat, BuiltinFnIdFloatToInt, BuiltinFnIdBoolToInt, + BuiltinFnIdErrToInt, + BuiltinFnIdIntToErr, BuiltinFnIdIntType, BuiltinFnIdSetCold, BuiltinFnIdSetRuntimeSafety, diff --git a/src/codegen.cpp b/src/codegen.cpp index 1bc9a17804..c9b4ade4c6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6323,6 +6323,8 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdIntToFloat, "intToFloat", 2); create_builtin_fn(g, BuiltinFnIdFloatToInt, "floatToInt", 2); create_builtin_fn(g, BuiltinFnIdBoolToInt, "boolToInt", 1); + create_builtin_fn(g, BuiltinFnIdErrToInt, "errorToInt", 1); + create_builtin_fn(g, BuiltinFnIdIntToErr, "intToError", 1); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int diff --git a/src/ir.cpp b/src/ir.cpp index 3ed9f66947..350c80017e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4167,6 +4167,26 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } + case BuiltinFnIdErrToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *result = ir_build_err_to_int(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdIntToErr: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *result = ir_build_int_to_err(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval); + } case BuiltinFnIdBoolToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -10465,21 +10485,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); } - // explicit cast from T!void to integer type which can fit it - bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion && - !type_has_bits(actual_type->data.error_union.payload_type); - bool actual_type_is_err_set = actual_type->id == TypeTableEntryIdErrorSet; - if ((actual_type_is_void_err || actual_type_is_err_set) && wanted_type->id == TypeTableEntryIdInt) { - return ir_analyze_err_to_int(ira, source_instr, value, wanted_type); - } - - // explicit cast from integer to error set - if (wanted_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdInt && - !actual_type->data.integral.is_signed) - { - return ir_analyze_int_to_err(ira, source_instr, value, wanted_type); - } - // explicit cast from integer to enum type with no payload if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdEnum) { return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type); @@ -17785,6 +17790,39 @@ static TypeTableEntry *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrIns return dest_type; } +static TypeTableEntry *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstructionErrToInt *instruction) { + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *casted_target; + if (target->value.type->id == TypeTableEntryIdErrorSet) { + casted_target = target; + } else { + casted_target = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_global_error_set); + if (type_is_invalid(casted_target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *result = ir_analyze_err_to_int(ira, &instruction->base, casted_target, ira->codegen->err_tag_type); + ir_link_new_instruction(result, &instruction->base); + return result->value.type; +} + +static TypeTableEntry *ir_analyze_instruction_int_to_err(IrAnalyze *ira, IrInstructionIntToErr *instruction) { + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *casted_target = ir_implicit_cast(ira, target, ira->codegen->err_tag_type); + if (type_is_invalid(casted_target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_analyze_int_to_err(ira, &instruction->base, casted_target, ira->codegen->builtin_types.entry_global_error_set); + ir_link_new_instruction(result, &instruction->base); + return result->value.type; +} + static TypeTableEntry *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstructionBoolToInt *instruction) { IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) @@ -20229,8 +20267,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi case IrInstructionIdInvalid: case IrInstructionIdWidenOrShorten: case IrInstructionIdIntToEnum: - case IrInstructionIdIntToErr: - case IrInstructionIdErrToInt: case IrInstructionIdStructInit: case IrInstructionIdUnionInit: case IrInstructionIdStructFieldPtr: @@ -20491,6 +20527,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction); case IrInstructionIdSqrt: return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction); + case IrInstructionIdIntToErr: + return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction); + case IrInstructionIdErrToInt: + return ir_analyze_instruction_err_to_int(ira, (IrInstructionErrToInt *)instruction); } zig_unreachable(); } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 3a0fa7f461..da5e708555 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -318,7 +318,7 @@ pub const ChildProcess = struct { // Here we potentially return the fork child's error // from the parent pid. if (err_int != @maxValue(ErrInt)) { - return SpawnError(err_int); + return @errSetCast(SpawnError, @intToError(err_int)); } return statusToTerm(status); @@ -756,7 +756,7 @@ fn destroyPipe(pipe: *const [2]i32) void { // Child of fork calls this to report an error to the fork parent. // Then the child exits. fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn { - _ = writeIntFd(fd, ErrInt(err)); + _ = writeIntFd(fd, ErrInt(@errorToInt(err))); posix.exit(1); } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 0b79f0df48..156835bcb3 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -140,8 +140,8 @@ test "explicit cast from integer to error type" { comptime testCastIntToErr(error.ItBroke); } fn testCastIntToErr(err: error) void { - const x = usize(err); - const y = error(x); + const x = @errorToInt(err); + const y = @intToError(x); assert(error.ItBroke == y); } diff --git a/test/cases/error.zig b/test/cases/error.zig index 95890d8384..45971fd40d 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -31,8 +31,8 @@ test "@errorName" { } test "error values" { - const a = i32(error.err1); - const b = i32(error.err2); + const a = @errorToInt(error.err1); + const b = @errorToInt(error.err2); assert(a != b); } @@ -147,14 +147,14 @@ test "syntax: optional operator in front of error union operator" { } test "comptime err to int of error set with only 1 possible value" { - testErrToIntWithOnePossibleValue(error.A, u32(error.A)); - comptime testErrToIntWithOnePossibleValue(error.A, u32(error.A)); + testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); + comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); } fn testErrToIntWithOnePossibleValue( x: error{A}, comptime value: u32, ) void { - if (u32(x) != value) { + if (@errorToInt(x) != value) { @compileError("bad"); } } diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index 1bc58b14e1..46fb3852f3 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -130,7 +130,7 @@ fn testErrorSet() void { assert(TypeId(error_set_info) == TypeId.ErrorSet); assert(error_set_info.ErrorSet.errors.len == 3); assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); - assert(error_set_info.ErrorSet.errors[2].value == usize(TestErrorSet.Third)); + assert(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third)); const error_union_info = @typeInfo(TestErrorSet!usize); assert(TypeId(error_union_info) == TypeId.ErrorUnion); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2f4a22553b..866b303082 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -467,25 +467,34 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( "int to err global invalid number", - \\const Set1 = error{A, B}; + \\const Set1 = error{ + \\ A, + \\ B, + \\}; \\comptime { - \\ var x: usize = 3; - \\ var y = error(x); + \\ var x: u16 = 3; + \\ var y = @intToError(x); \\} , - ".tmp_source.zig:4:18: error: integer value 3 represents no error", + ".tmp_source.zig:7:13: error: integer value 3 represents no error", ); cases.add( "int to err non global invalid number", - \\const Set1 = error{A, B}; - \\const Set2 = error{A, C}; + \\const Set1 = error{ + \\ A, + \\ B, + \\}; + \\const Set2 = error{ + \\ A, + \\ C, + \\}; \\comptime { - \\ var x = usize(Set1.B); - \\ var y = Set2(x); + \\ var x = @errorToInt(Set1.B); + \\ var y = @errSetCast(Set2, @intToError(x)); \\} , - ".tmp_source.zig:5:17: error: integer value 2 represents no error in 'Set2'", + ".tmp_source.zig:11:13: error: error.B not a member of error set 'Set2'", ); cases.add( @@ -2612,17 +2621,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:2:21: error: expected pointer, found 'usize'", ); - cases.add( - "too many error values to cast to small integer", - \\const Error = error { A, B, C, D, E, F, G, H }; - \\fn foo(e: Error) u2 { - \\ return u2(e); - \\} - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , - ".tmp_source.zig:3:14: error: too many error values to fit in 'u2'", - ); - cases.add( "asm at compile time", \\comptime { diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 96384066e5..ea5beafe8d 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -175,7 +175,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ if (x.len == 0) return error.Whatever; \\} \\fn widenSlice(slice: []align(1) const u8) []align(1) const i32 { - \\ return ([]align(1) const i32)(slice); + \\ return @bytesToSlice(i32, slice); \\} ); @@ -227,12 +227,12 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\pub fn main() void { \\ _ = bar(9999); \\} - \\fn bar(x: u32) error { - \\ return error(x); + \\fn bar(x: u16) error { + \\ return @intToError(x); \\} ); - cases.addRuntimeSafety("cast integer to non-global error set and no match", + cases.addRuntimeSafety("@errSetCast error not present in destination", \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} @@ -242,7 +242,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ _ = foo(Set1.B); \\} \\fn foo(set1: Set1) Set2 { - \\ return Set2(set1); + \\ return @errSetCast(Set2, set1); \\} ); @@ -252,12 +252,12 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\pub fn main() !void { \\ var array align(4) = []u32{0x11111111, 0x11111111}; - \\ const bytes = ([]u8)(array[0..]); + \\ const bytes = @sliceToBytes(array[0..]); \\ if (foo(bytes) != 0x11111111) return error.Wrong; \\} \\fn foo(bytes: []u8) u32 { \\ const slice4 = bytes[1..5]; - \\ const int_slice = ([]u32)(@alignCast(4, slice4)); + \\ const int_slice = @bytesToSlice(u32, @alignCast(4, slice4)); \\ return int_slice[0]; \\} ); -- cgit v1.2.3 From a3ddd0826bd9c799768c0c707de72c21befa742a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Jun 2018 03:50:38 -0400 Subject: remove enum to/from int casting syntax; add `@enumToInt`/`@intToEnum` see #1061 --- doc/langref.html.in | 34 +++++++++++--- src/all_types.hpp | 10 ++++ src/codegen.cpp | 3 ++ src/ir.cpp | 118 ++++++++++++++++++++++++++++++++++++++++++------ src/ir_print.cpp | 14 ++++++ std/json.zig | 2 +- test/cases/enum.zig | 16 +++---- test/cases/union.zig | 4 +- test/compile_errors.zig | 24 ++-------- 9 files changed, 174 insertions(+), 51 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 24bf6e1b16..19f023f6e1 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1900,9 +1900,9 @@ const Value = enum(u2) { // Now you can cast between u2 and Value. // The ordinal value starts from 0, counting up for each member. test "enum ordinal value" { - assert(u2(Value.Zero) == 0); - assert(u2(Value.One) == 1); - assert(u2(Value.Two) == 2); + assert(@enumToInt(Value.Zero) == 0); + assert(@enumToInt(Value.One) == 1); + assert(@enumToInt(Value.Two) == 2); } // You can override the ordinal value for an enum. @@ -1912,9 +1912,9 @@ const Value2 = enum(u32) { Million = 1000000, }; test "set enum ordinal value" { - assert(u32(Value2.Hundred) == 100); - assert(u32(Value2.Thousand) == 1000); - assert(u32(Value2.Million) == 1000000); + assert(@enumToInt(Value2.Hundred) == 100); + assert(@enumToInt(Value2.Thousand) == 1000); + assert(@enumToInt(Value2.Million) == 1000000); } // Enums can have methods, the same as structs and unions. @@ -4931,6 +4931,14 @@ test "main" { {#see_also|@import#} {#header_close#} + {#header_open|@enumToInt#} +
    @enumToInt(enum_value: var) var
    +

    + Converts an enumeration value into its integer tag type. +

    + {#see_also|@intToEnum#} + {#header_close#} + {#header_open|@errSetCast#}
    @errSetCast(comptime T: DestType, value: var) DestType

    @@ -5095,6 +5103,18 @@ fn add(a: i32, b: i32) i32 { return a + b; }

    {#header_close#} + {#header_open|@intToEnum#} +
    @intToEnum(comptime DestType: type, int_value: @TagType(DestType)) DestType
    +

    + Converts an integer into an {#link|enum#} value. +

    +

    + Attempting to convert an integer which represents no value in the chosen enum type invokes + safety-checked {#link|Undefined Behavior#}. +

    + {#see_also|@enumToInt#} + {#header_close#} + {#header_open|@intToError#}
    @intToError(value: @IntType(false, @sizeOf(error) * 8)) error

    @@ -6867,7 +6887,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index e1a4ed7510..41c9fe18c3 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1378,6 +1378,8 @@ enum BuiltinFnId { BuiltinFnIdBoolToInt, BuiltinFnIdErrToInt, BuiltinFnIdIntToErr, + BuiltinFnIdEnumToInt, + BuiltinFnIdIntToEnum, BuiltinFnIdIntType, BuiltinFnIdSetCold, BuiltinFnIdSetRuntimeSafety, @@ -2092,6 +2094,7 @@ enum IrInstructionId { IrInstructionIdIntToPtr, IrInstructionIdPtrToInt, IrInstructionIdIntToEnum, + IrInstructionIdEnumToInt, IrInstructionIdIntToErr, IrInstructionIdErrToInt, IrInstructionIdCheckSwitchProngs, @@ -2905,6 +2908,13 @@ struct IrInstructionIntToPtr { struct IrInstructionIntToEnum { IrInstruction base; + IrInstruction *dest_type; + IrInstruction *target; +}; + +struct IrInstructionEnumToInt { + IrInstruction base; + IrInstruction *target; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index c9b4ade4c6..21520e0dd0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4730,6 +4730,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdErrSetCast: case IrInstructionIdFromBytes: case IrInstructionIdToBytes: + case IrInstructionIdEnumToInt: zig_unreachable(); case IrInstructionIdReturn: @@ -6325,6 +6326,8 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdBoolToInt, "boolToInt", 1); create_builtin_fn(g, BuiltinFnIdErrToInt, "errorToInt", 1); create_builtin_fn(g, BuiltinFnIdIntToErr, "intToError", 1); + create_builtin_fn(g, BuiltinFnIdEnumToInt, "enumToInt", 1); + create_builtin_fn(g, BuiltinFnIdIntToEnum, "intToEnum", 2); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int diff --git a/src/ir.cpp b/src/ir.cpp index 350c80017e..a8599e7aae 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -600,6 +600,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToEnum *) { return IrInstructionIdIntToEnum; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumToInt *) { + return IrInstructionIdEnumToInt; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToErr *) { return IrInstructionIdIntToErr; } @@ -2378,10 +2382,26 @@ static IrInstruction *ir_build_ptr_to_int(IrBuilder *irb, Scope *scope, AstNode } static IrInstruction *ir_build_int_to_enum(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *target) + IrInstruction *dest_type, IrInstruction *target) { IrInstructionIntToEnum *instruction = ir_build_instruction( irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + + + +static IrInstruction *ir_build_enum_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *target) +{ + IrInstructionEnumToInt *instruction = ir_build_instruction( + irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); @@ -4708,6 +4728,31 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo // this value does not mean anything since we passed non-null values for other arg AtomicOrderMonotonic); } + case BuiltinFnIdIntToEnum: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_int_to_enum(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdEnumToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *result = ir_build_enum_to_int(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval); + } } zig_unreachable(); } @@ -9951,7 +9996,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour } IrInstruction *result = ir_build_int_to_enum(&ira->new_irb, source_instr->scope, - source_instr->source_node, target); + source_instr->source_node, nullptr, target); result->value.type = wanted_type; return result; } @@ -10485,16 +10530,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); } - // explicit cast from integer to enum type with no payload - if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdEnum) { - return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type); - } - - // explicit cast from enum type with no payload to integer - if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdEnum) { - return ir_analyze_enum_to_int(ira, source_instr, value, wanted_type); - } - // explicit cast from union to the enum type of the union if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) { type_ensure_zero_bits_known(ira->codegen, actual_type); @@ -20262,11 +20297,63 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction return result->value.type; } +static TypeTableEntry *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) { + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (target->value.type->id != TypeTableEntryIdEnum) { + ir_add_error(ira, instruction->target, + buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + type_ensure_zero_bits_known(ira->codegen, target->value.type); + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + TypeTableEntry *tag_type = target->value.type->data.enumeration.tag_int_type; + + IrInstruction *result = ir_analyze_enum_to_int(ira, &instruction->base, target, tag_type); + ir_link_new_instruction(result, &instruction->base); + return result->value.type; +} + +static TypeTableEntry *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) { + IrInstruction *dest_type_value = instruction->dest_type->other; + TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + if (dest_type->id != TypeTableEntryIdEnum) { + ir_add_error(ira, instruction->dest_type, + buf_sprintf("expected enum, found type '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + type_ensure_zero_bits_known(ira->codegen, dest_type); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + TypeTableEntry *tag_type = dest_type->data.enumeration.tag_int_type; + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *casted_target = ir_implicit_cast(ira, target, tag_type); + if (type_is_invalid(casted_target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_analyze_int_to_enum(ira, &instruction->base, casted_target, dest_type); + ir_link_new_instruction(result, &instruction->base); + return result->value.type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: case IrInstructionIdWidenOrShorten: - case IrInstructionIdIntToEnum: case IrInstructionIdStructInit: case IrInstructionIdUnionInit: case IrInstructionIdStructFieldPtr: @@ -20531,6 +20618,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction); case IrInstructionIdErrToInt: return ir_analyze_instruction_err_to_int(ira, (IrInstructionErrToInt *)instruction); + case IrInstructionIdIntToEnum: + return ir_analyze_instruction_int_to_enum(ira, (IrInstructionIntToEnum *)instruction); + case IrInstructionIdEnumToInt: + return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction); } zig_unreachable(); } @@ -20754,6 +20845,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdBoolToInt: case IrInstructionIdFromBytes: case IrInstructionIdToBytes: + case IrInstructionIdEnumToInt: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 1b35ecf57f..5e5a71382c 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -928,6 +928,17 @@ static void ir_print_int_to_ptr(IrPrint *irp, IrInstructionIntToPtr *instruction static void ir_print_int_to_enum(IrPrint *irp, IrInstructionIntToEnum *instruction) { fprintf(irp->f, "@intToEnum("); + if (instruction->dest_type == nullptr) { + fprintf(irp->f, "(null)"); + } else { + ir_print_other_instruction(irp, instruction->dest_type); + } + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_enum_to_int(IrPrint *irp, IrInstructionEnumToInt *instruction) { + fprintf(irp->f, "@enumToInt("); ir_print_other_instruction(irp, instruction->target); fprintf(irp->f, ")"); } @@ -1717,6 +1728,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdAtomicLoad: ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); break; + case IrInstructionIdEnumToInt: + ir_print_enum_to_int(irp, (IrInstructionEnumToInt *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/std/json.zig b/std/json.zig index 2930cd21bb..8986034fb4 100644 --- a/std/json.zig +++ b/std/json.zig @@ -180,7 +180,7 @@ pub const StreamingParser = struct { pub fn fromInt(x: var) State { debug.assert(x == 0 or x == 1); const T = @TagType(State); - return State(@intCast(T, x)); + return @intToEnum(State, @intCast(T, x)); } }; diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 6a02a47784..50edfda536 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -92,14 +92,14 @@ test "enum to int" { } fn shouldEqual(n: Number, expected: u3) void { - assert(u3(n) == expected); + assert(@enumToInt(n) == expected); } test "int to enum" { testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) void { - assert(IntToEnumNumber(@intCast(u3, x)) == IntToEnumNumber.Three); + assert(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, @@ -768,7 +768,7 @@ test "casting enum to its tag type" { } fn testCastEnumToTagType(value: Small2) void { - assert(u2(value) == 1); + assert(@enumToInt(value) == 1); } const MultipleChoice = enum(u32) { @@ -784,7 +784,7 @@ test "enum with specified tag values" { } fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void { - assert(u32(x) == 60); + assert(@enumToInt(x) == 60); assert(1234 == switch (x) { MultipleChoice.A => 1, MultipleChoice.B => 2, @@ -811,7 +811,7 @@ test "enum with specified and unspecified tag values" { } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(u32(x) == 1000); + assert(@enumToInt(x) == 1000); assert(1234 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, @@ -826,8 +826,8 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { } test "cast integer literal to enum" { - assert(MultipleChoice2(0) == MultipleChoice2.Unspecified1); - assert(MultipleChoice2(40) == MultipleChoice2.B); + assert(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); + assert(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); } const EnumWithOneMember = enum { @@ -865,7 +865,7 @@ const EnumWithTagValues = enum(u4) { D = 1 << 3, }; test "enum with tag values don't require parens" { - assert(u4(EnumWithTagValues.C) == 0b0100); + assert(@enumToInt(EnumWithTagValues.C) == 0b0100); } test "enum with 1 field but explicit tag type should still have the tag type" { diff --git a/test/cases/union.zig b/test/cases/union.zig index bdcbbdb452..78b2dc8dd7 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -126,7 +126,7 @@ const MultipleChoice = union(enum(u32)) { test "simple union(enum(u32))" { var x = MultipleChoice.C; assert(x == MultipleChoice.C); - assert(u32(@TagType(MultipleChoice)(x)) == 60); + assert(@enumToInt(@TagType(MultipleChoice)(x)) == 60); } const MultipleChoice2 = union(enum(u32)) { @@ -148,7 +148,7 @@ test "union(enum(u32)) with specified and unspecified tag values" { } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: *const MultipleChoice2) void { - assert(u32(@TagType(MultipleChoice2)(x.*)) == 60); + assert(@enumToInt(@TagType(MultipleChoice2)(x.*)) == 60); assert(1123 == switch (x.*) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 866b303082..609e3f103f 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3709,22 +3709,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:9:22: error: expected type 'u2', found 'Small'", ); - cases.add( - "explicitly casting enum to non tag type", - \\const Small = enum(u2) { - \\ One, - \\ Two, - \\ Three, - \\ Four, - \\}; - \\ - \\export fn entry() void { - \\ var x = u3(Small.Two); - \\} - , - ".tmp_source.zig:9:15: error: enum to integer cast to 'u3' instead of its tag type, 'u2'", - ); - cases.add( "explicitly casting non tag type to enum", \\const Small = enum(u2) { @@ -3736,10 +3720,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() void { \\ var y = u3(3); - \\ var x = Small(y); + \\ var x = @intToEnum(Small, y); \\} , - ".tmp_source.zig:10:18: error: integer to enum cast from 'u3' instead of its tag type, 'u2'", + ".tmp_source.zig:10:31: error: expected type 'u2', found 'u3'", ); cases.add( @@ -4020,10 +4004,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ B = 11, \\}; \\export fn entry() void { - \\ var x = Foo(0); + \\ var x = @intToEnum(Foo, 0); \\} , - ".tmp_source.zig:6:16: error: enum 'Foo' has no tag matching integer value 0", + ".tmp_source.zig:6:13: error: enum 'Foo' has no tag matching integer value 0", ".tmp_source.zig:1:13: note: 'Foo' declared here", ); -- cgit v1.2.3 From 13923132363b26b9982951fd79f01dbab0046e81 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 19 Jun 2018 17:45:19 +0300 Subject: @typeInfo now uses optional types instead of @typeOf(undefined) --- doc/langref.html.in | 8 +++--- src/codegen.cpp | 10 +++---- src/ir.cpp | 75 +++++++++++++++++++++++++++++++----------------- test/cases/type_info.zig | 16 +++++------ 4 files changed, 65 insertions(+), 44 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index f1ae2bafaa..551f3ff769 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5684,20 +5684,20 @@ pub const TypeInfo = union(TypeId) { pub const FnArg = struct { is_generic: bool, is_noalias: bool, - arg_type: type, + arg_type: ?type, }; pub const Fn = struct { calling_convention: CallingConvention, is_generic: bool, is_var_args: bool, - return_type: type, - async_allocator_type: type, + return_type: ?type, + async_allocator_type: ?type, args: []FnArg, }; pub const Promise = struct { - child: type, + child: ?type, }; pub const Definition = struct { diff --git a/src/codegen.cpp b/src/codegen.cpp index 84335d4e06..dcb5e8cb46 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6627,7 +6627,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { "\n" " pub const Union = struct {\n" " layout: ContainerLayout,\n" - " tag_type: type,\n" + " tag_type: ?type,\n" " fields: []UnionField,\n" " defs: []Definition,\n" " };\n" @@ -6644,20 +6644,20 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " pub const FnArg = struct {\n" " is_generic: bool,\n" " is_noalias: bool,\n" - " arg_type: type,\n" + " arg_type: ?type,\n" " };\n" "\n" " pub const Fn = struct {\n" " calling_convention: CallingConvention,\n" " is_generic: bool,\n" " is_var_args: bool,\n" - " return_type: type,\n" - " async_allocator_type: type,\n" + " return_type: ?type,\n" + " async_allocator_type: ?type,\n" " args: []FnArg,\n" " };\n" "\n" " pub const Promise = struct {\n" - " child: type,\n" + " child: ?type,\n" " };\n" "\n" " pub const Definition = struct {\n" diff --git a/src/ir.cpp b/src/ir.cpp index c75a3ae7c1..433637513e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16722,16 +16722,20 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; - // @TODO ?type instead of using @typeOf(undefined) when we have no type. - // child: type + // child: ?type ensure_field_index(result->type, "child", 0); fields[0].special = ConstValSpecialStatic; - fields[0].type = ira->codegen->builtin_types.entry_type; + fields[0].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.promise.result_type == nullptr) - fields[0].data.x_type = ira->codegen->builtin_types.entry_undef; - else - fields[0].data.x_type = type_entry->data.promise.result_type; + fields[0].data.x_optional = nullptr; + else { + ConstExprValue *child_type = create_const_vals(1); + child_type->special = ConstValSpecialStatic; + child_type->type = ira->codegen->builtin_types.entry_type; + child_type->data.x_type = type_entry->data.promise.result_type; + fields[0].data.x_optional = child_type; + } break; } @@ -16872,19 +16876,23 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t fields[0].special = ConstValSpecialStatic; fields[0].type = ir_type_info_get_type(ira, "ContainerLayout"); bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.unionation.layout); - // tag_type: type + // tag_type: ?type ensure_field_index(result->type, "tag_type", 1); fields[1].special = ConstValSpecialStatic; - fields[1].type = ira->codegen->builtin_types.entry_type; - // @TODO ?type instead of using @typeOf(undefined) when we have no type. + fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); + AstNode *union_decl_node = type_entry->data.unionation.decl_node; if (union_decl_node->data.container_decl.auto_enum || union_decl_node->data.container_decl.init_arg_expr != nullptr) { - fields[1].data.x_type = type_entry->data.unionation.tag_type; + ConstExprValue *tag_type = create_const_vals(1); + tag_type->special = ConstValSpecialStatic; + tag_type->type = ira->codegen->builtin_types.entry_type; + tag_type->data.x_type = type_entry->data.unionation.tag_type; + fields[1].data.x_optional = tag_type; } else - fields[1].data.x_type = ira->codegen->builtin_types.entry_undef; + fields[1].data.x_optional = nullptr; // fields: []TypeInfo.UnionField ensure_field_index(result->type, "fields", 2); @@ -16913,7 +16921,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].special = ConstValSpecialStatic; inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type); - if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) { + if (fields[1].data.x_optional == nullptr) { inner_fields[1].data.x_optional = nullptr; } else { inner_fields[1].data.x_optional = create_const_vals(1); @@ -17022,8 +17030,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ConstExprValue *fields = create_const_vals(6); result->data.x_struct.fields = fields; - // @TODO Fix type = undefined with ?type - // calling_convention: TypeInfo.CallingConvention ensure_field_index(result->type, "calling_convention", 0); fields[0].special = ConstValSpecialStatic; @@ -17041,22 +17047,32 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t fields[2].special = ConstValSpecialStatic; fields[2].type = ira->codegen->builtin_types.entry_bool; fields[2].data.x_bool = type_entry->data.fn.fn_type_id.is_var_args; - // return_type: type + // return_type: ?type ensure_field_index(result->type, "return_type", 3); fields[3].special = ConstValSpecialStatic; - fields[3].type = ira->codegen->builtin_types.entry_type; + fields[3].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.return_type == nullptr) - fields[3].data.x_type = ira->codegen->builtin_types.entry_undef; - else - fields[3].data.x_type = type_entry->data.fn.fn_type_id.return_type; + fields[3].data.x_optional = nullptr; + else { + ConstExprValue *return_type = create_const_vals(1); + return_type->special = ConstValSpecialStatic; + return_type->type = ira->codegen->builtin_types.entry_type; + return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type; + fields[3].data.x_optional = return_type; + } // async_allocator_type: type ensure_field_index(result->type, "async_allocator_type", 4); fields[4].special = ConstValSpecialStatic; - fields[4].type = ira->codegen->builtin_types.entry_type; + fields[4].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.async_allocator_type == nullptr) - fields[4].data.x_type = ira->codegen->builtin_types.entry_undef; - else - fields[4].data.x_type = type_entry->data.fn.fn_type_id.async_allocator_type; + fields[4].data.x_optional = nullptr; + else { + ConstExprValue *async_alloc_type = create_const_vals(1); + async_alloc_type->special = ConstValSpecialStatic; + async_alloc_type->type = ira->codegen->builtin_types.entry_type; + async_alloc_type->data.x_type = type_entry->data.fn.fn_type_id.async_allocator_type; + fields[4].data.x_optional = async_alloc_type; + } // args: []TypeInfo.FnArg TypeTableEntry *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg"); size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count - @@ -17090,12 +17106,17 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].type = ira->codegen->builtin_types.entry_bool; inner_fields[1].data.x_bool = fn_param_info->is_noalias; inner_fields[2].special = ConstValSpecialStatic; - inner_fields[2].type = ira->codegen->builtin_types.entry_type; + inner_fields[2].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (arg_is_generic) - inner_fields[2].data.x_type = ira->codegen->builtin_types.entry_undef; - else - inner_fields[2].data.x_type = fn_param_info->type; + inner_fields[2].data.x_optional = nullptr; + else { + ConstExprValue *arg_type = create_const_vals(1); + arg_type->special = ConstValSpecialStatic; + arg_type->type = ira->codegen->builtin_types.entry_type; + arg_type->data.x_type = fn_param_info->type; + inner_fields[2].data.x_optional = arg_type; + } fn_arg_val->data.x_struct.fields = inner_fields; fn_arg_val->data.x_struct.parent.id = ConstParentIdArray; diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index 1bc58b14e1..ab4f3678e8 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -107,11 +107,11 @@ test "type info: promise info" { fn testPromise() void { const null_promise_info = @typeInfo(promise); assert(TypeId(null_promise_info) == TypeId.Promise); - assert(null_promise_info.Promise.child == @typeOf(undefined)); + assert(null_promise_info.Promise.child == null); const promise_info = @typeInfo(promise->usize); assert(TypeId(promise_info) == TypeId.Promise); - assert(promise_info.Promise.child == usize); + assert(promise_info.Promise.child.? == usize); } test "type info: error set, error union info" { @@ -165,7 +165,7 @@ fn testUnion() void { const typeinfo_info = @typeInfo(TypeInfo); assert(TypeId(typeinfo_info) == TypeId.Union); assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(typeinfo_info.Union.tag_type == TypeId); + assert(typeinfo_info.Union.tag_type.? == TypeId); assert(typeinfo_info.Union.fields.len == 25); assert(typeinfo_info.Union.fields[4].enum_field != null); assert(typeinfo_info.Union.fields[4].enum_field.?.value == 4); @@ -179,7 +179,7 @@ fn testUnion() void { const notag_union_info = @typeInfo(TestNoTagUnion); assert(TypeId(notag_union_info) == TypeId.Union); - assert(notag_union_info.Union.tag_type == @typeOf(undefined)); + assert(notag_union_info.Union.tag_type == null); assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); assert(notag_union_info.Union.fields.len == 2); assert(notag_union_info.Union.fields[0].enum_field == null); @@ -191,7 +191,7 @@ fn testUnion() void { const extern_union_info = @typeInfo(TestExternUnion); assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); - assert(extern_union_info.Union.tag_type == @typeOf(undefined)); + assert(extern_union_info.Union.tag_type == null); assert(extern_union_info.Union.fields[0].enum_field == null); assert(extern_union_info.Union.fields[0].field_type == *c_void); } @@ -238,13 +238,13 @@ fn testFunction() void { assert(fn_info.Fn.is_generic); assert(fn_info.Fn.args.len == 2); assert(fn_info.Fn.is_var_args); - assert(fn_info.Fn.return_type == @typeOf(undefined)); - assert(fn_info.Fn.async_allocator_type == @typeOf(undefined)); + assert(fn_info.Fn.return_type == null); + assert(fn_info.Fn.async_allocator_type == null); const test_instance: TestStruct = undefined; const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); assert(TypeId(bound_fn_info) == TypeId.BoundFn); - assert(bound_fn_info.BoundFn.args[0].arg_type == *const TestStruct); + assert(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); } fn foo(comptime a: usize, b: bool, args: ...) usize { -- cgit v1.2.3 From 811539f8ee88366fea744ecfe251413b5dd774cc Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 19 Jun 2018 17:49:48 +0300 Subject: Added missing ?type in docs. --- doc/langref.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 551f3ff769..769f6d586b 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5667,7 +5667,7 @@ pub const TypeInfo = union(TypeId) { pub const Union = struct { layout: ContainerLayout, - tag_type: type, + tag_type: ?type, fields: []UnionField, defs: []Definition, }; -- cgit v1.2.3 From 0b92d689d0e64164bb8908c807db9338d59c41ce Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Jun 2018 12:16:59 -0400 Subject: update langref --- doc/langref.html.in | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/langref.html.in b/doc/langref.html.in index 7a9355747f..4070ef0ac0 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5688,10 +5688,17 @@ pub const TypeInfo = union(TypeId) { }; pub const Pointer = struct { + size: Size, is_const: bool, is_volatile: bool, alignment: u32, child: type, + + pub const Size = enum { + One, + Many, + Slice, + }; }; pub const Array = struct { -- cgit v1.2.3 From c7804277bf390eeba368e3565b2aff0cf96f86b0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Jun 2018 16:06:10 -0400 Subject: `@floatToInt` now has safety-checked undefined behavior when the integer part does not fit in the destination integer type * Also fix incorrect safety triggered for integer casting an `i32` to a `u7`. closes #1138 * adds compiler-rt function: `__floatuntidf` --- CMakeLists.txt | 1 + doc/langref.html.in | 9 +++- src/all_types.hpp | 1 + src/codegen.cpp | 36 +++++++++++-- src/ir.cpp | 25 +++++++-- std/math/expm1.zig | 8 +++ std/special/compiler_rt/floatuntidf.zig | 60 +++++++++++++++++++++ std/special/compiler_rt/floatuntidf_test.zig | 81 ++++++++++++++++++++++++++++ std/special/compiler_rt/index.zig | 2 + test/cases/cast.zig | 21 +++++++- test/compile_errors.zig | 17 ++++++ test/runtime_safety.zig | 39 ++++++++++++++ 12 files changed, 291 insertions(+), 9 deletions(-) create mode 100644 std/special/compiler_rt/floatuntidf.zig create mode 100644 std/special/compiler_rt/floatuntidf_test.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index e502901bd2..030398d71c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -568,6 +568,7 @@ set(ZIG_STD_FILES "special/compiler_rt/fixunstfdi.zig" "special/compiler_rt/fixunstfsi.zig" "special/compiler_rt/fixunstfti.zig" + "special/compiler_rt/floatuntidf.zig" "special/compiler_rt/muloti4.zig" "special/compiler_rt/index.zig" "special/compiler_rt/udivmod.zig" diff --git a/doc/langref.html.in b/doc/langref.html.in index 4070ef0ac0..1bd28f9c3e 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5035,8 +5035,12 @@ test "main" {

    @floatToInt(comptime DestType: type, float: var) DestType

    Converts the integer part of a floating point number to the destination type. - To convert the other way, use {#link|@intToFloat#}. This cast is always safe.

    +

    + If the integer part of the floating point number cannot fit in the destination type, + it invokes safety-checked {#link|Undefined Behavior#}. +

    + {#see_also|@intToFloat#} {#header_close#} {#header_open|@frameAddress#} @@ -6207,7 +6211,10 @@ comptime { {#header_close#} {#header_open|Wrong Union Field Access#}

    TODO

    + {#header_close#} + {#header_open|Out of Bounds Float To Integer Cast#} +

    TODO

    {#header_close#} {#header_close#} diff --git a/src/all_types.hpp b/src/all_types.hpp index 41c9fe18c3..12e054cbeb 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1434,6 +1434,7 @@ enum PanicMsgId { PanicMsgIdIncorrectAlignment, PanicMsgIdBadUnionField, PanicMsgIdBadEnumValue, + PanicMsgIdFloatToInt, PanicMsgIdCount, }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 5f7fd60392..20268d636a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -865,6 +865,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { return buf_create_from_str("access of inactive union field"); case PanicMsgIdBadEnumValue: return buf_create_from_str("invalid enum value"); + case PanicMsgIdFloatToInt: + return buf_create_from_str("integer part of floating point value out of bounds"); } zig_unreachable(); } @@ -1671,7 +1673,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, T return trunc_val; } LLVMValueRef orig_val; - if (actual_type->data.integral.is_signed) { + if (wanted_type->data.integral.is_signed) { orig_val = LLVMBuildSExt(g->builder, trunc_val, actual_type->type_ref, ""); } else { orig_val = LLVMBuildZExt(g->builder, trunc_val, actual_type->type_ref, ""); @@ -2546,15 +2548,41 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, } else { return LLVMBuildUIToFP(g->builder, expr_val, wanted_type->type_ref, ""); } - case CastOpFloatToInt: + case CastOpFloatToInt: { assert(wanted_type->id == TypeTableEntryIdInt); ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &cast_instruction->base)); + + bool want_safety = ir_want_runtime_safety(g, &cast_instruction->base); + + LLVMValueRef result; if (wanted_type->data.integral.is_signed) { - return LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, ""); + result = LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, ""); } else { - return LLVMBuildFPToUI(g->builder, expr_val, wanted_type->type_ref, ""); + result = LLVMBuildFPToUI(g->builder, expr_val, wanted_type->type_ref, ""); } + if (want_safety) { + LLVMValueRef back_to_float; + if (wanted_type->data.integral.is_signed) { + back_to_float = LLVMBuildSIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); + } else { + back_to_float = LLVMBuildUIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); + } + LLVMValueRef difference = LLVMBuildFSub(g->builder, expr_val, back_to_float, ""); + LLVMValueRef one_pos = LLVMConstReal(LLVMTypeOf(expr_val), 1.0f); + LLVMValueRef one_neg = LLVMConstReal(LLVMTypeOf(expr_val), -1.0f); + LLVMValueRef ok_bit_pos = LLVMBuildFCmp(g->builder, LLVMRealOLT, difference, one_pos, ""); + LLVMValueRef ok_bit_neg = LLVMBuildFCmp(g->builder, LLVMRealOGT, difference, one_neg, ""); + LLVMValueRef ok_bit = LLVMBuildAnd(g->builder, ok_bit_pos, ok_bit_neg, ""); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "FloatCheckOk"); + LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "FloatCheckFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, bad_block); + LLVMPositionBuilderAtEnd(g->builder, bad_block); + gen_safety_crash(g, PanicMsgIdFloatToInt); + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + return result; + } case CastOpBoolToInt: assert(wanted_type->id == TypeTableEntryIdInt); assert(actual_type->id == TypeTableEntryIdBool); diff --git a/src/ir.cpp b/src/ir.cpp index 55da8ad944..1fe078dd3f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9057,7 +9057,8 @@ static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_ } } -static void eval_const_expr_implicit_cast(CastOp cast_op, +static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_instr, + CastOp cast_op, ConstExprValue *other_val, TypeTableEntry *other_type, ConstExprValue *const_val, TypeTableEntry *new_type) { @@ -9129,6 +9130,20 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, } case CastOpFloatToInt: float_init_bigint(&const_val->data.x_bigint, other_val); + if (new_type->id == TypeTableEntryIdInt) { + if (!bigint_fits_in_bits(&const_val->data.x_bigint, new_type->data.integral.bit_count, + new_type->data.integral.is_signed)) + { + Buf *int_buf = buf_alloc(); + bigint_append_buf(int_buf, &const_val->data.x_bigint, 10); + + ir_add_error(ira, source_instr, + buf_sprintf("integer value '%s' cannot be stored in type '%s'", + buf_ptr(int_buf), buf_ptr(&new_type->name))); + return false; + } + } + const_val->special = ConstValSpecialStatic; break; case CastOpBoolToInt: @@ -9136,6 +9151,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, const_val->special = ConstValSpecialStatic; break; } + return true; } static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type, CastOp cast_op, bool need_alloca) @@ -9145,8 +9161,11 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst { IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - eval_const_expr_implicit_cast(cast_op, &value->value, value->value.type, - &result->value, wanted_type); + if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type, + &result->value, wanted_type)) + { + return ira->codegen->invalid_instruction; + } return result; } else { IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, cast_op); diff --git a/std/math/expm1.zig b/std/math/expm1.zig index 438e44ccce..6fa0194b32 100644 --- a/std/math/expm1.zig +++ b/std/math/expm1.zig @@ -20,6 +20,10 @@ pub fn expm1(x: var) @typeOf(x) { fn expm1_32(x_: f32) f32 { @setFloatMode(this, builtin.FloatMode.Strict); + + if (math.isNan(x_)) + return math.nan(f32); + const o_threshold: f32 = 8.8721679688e+01; const ln2_hi: f32 = 6.9313812256e-01; const ln2_lo: f32 = 9.0580006145e-06; @@ -146,6 +150,10 @@ fn expm1_32(x_: f32) f32 { fn expm1_64(x_: f64) f64 { @setFloatMode(this, builtin.FloatMode.Strict); + + if (math.isNan(x_)) + return math.nan(f64); + const o_threshold: f64 = 7.09782712893383973096e+02; const ln2_hi: f64 = 6.93147180369123816490e-01; const ln2_lo: f64 = 1.90821492927058770002e-10; diff --git a/std/special/compiler_rt/floatuntidf.zig b/std/special/compiler_rt/floatuntidf.zig new file mode 100644 index 0000000000..3aabcb7b8a --- /dev/null +++ b/std/special/compiler_rt/floatuntidf.zig @@ -0,0 +1,60 @@ +const builtin = @import("builtin"); +const is_test = builtin.is_test; + +const DBL_MANT_DIG = 53; + +pub extern fn __floatuntidf(arg: u128) f64 { + @setRuntimeSafety(is_test); + + if (arg == 0) + return 0.0; + + var a = arg; + const N: u32 = @sizeOf(u128) * 8; + const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits + var e: i32 = sd -% 1; // exponent + if (sd > DBL_MANT_DIG) { + // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + // 12345678901234567890123456 + // 1 = msb 1 bit + // P = bit DBL_MANT_DIG-1 bits to the right of 1 + // Q = bit DBL_MANT_DIG bits to the right of 1 + // R = "or" of all bits to the right of Q + switch (sd) { + DBL_MANT_DIG + 1 => { + a <<= 1; + }, + DBL_MANT_DIG + 2 => {}, + else => { + const shift_amt = @bitCast(i32, N +% (DBL_MANT_DIG + 2)) -% sd; + const shift_amt_u7 = @intCast(u7, shift_amt); + a = (a >> @intCast(u7, sd -% (DBL_MANT_DIG + 2))) | + @boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0); + }, + } + // finish + a |= @boolToInt((a & 4) != 0); // Or P into R + a +%= 1; // round - this step may add a significant bit + a >>= 2; // dump Q and R + // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits + if ((a & (u128(1) << DBL_MANT_DIG)) != 0) { + a >>= 1; + e +%= 1; + } + // a is now rounded to DBL_MANT_DIG bits + } else { + a <<= @intCast(u7, DBL_MANT_DIG -% sd); + // a is now rounded to DBL_MANT_DIG bits + } + + const high: u64 = @bitCast(u32, (e +% 1023) << 20) | // exponent + (@truncate(u32, a >> 32) & 0x000FFFFF); // mantissa-high + const low = @truncate(u32, a); // mantissa-low + + return @bitCast(f64, low | (high << 32)); +} + +test "import floatuntidf" { + _ = @import("floatuntidf_test.zig"); +} diff --git a/std/special/compiler_rt/floatuntidf_test.zig b/std/special/compiler_rt/floatuntidf_test.zig new file mode 100644 index 0000000000..e2c79378e2 --- /dev/null +++ b/std/special/compiler_rt/floatuntidf_test.zig @@ -0,0 +1,81 @@ +const __floatuntidf = @import("floatuntidf.zig").__floatuntidf; +const assert = @import("std").debug.assert; + +fn test__floatuntidf(a: u128, expected: f64) void { + const x = __floatuntidf(a); + assert(x == expected); +} + +test "floatuntidf" { + test__floatuntidf(0, 0.0); + + test__floatuntidf(1, 1.0); + test__floatuntidf(2, 2.0); + test__floatuntidf(20, 20.0); + + test__floatuntidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + test__floatuntidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + test__floatuntidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + test__floatuntidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + + test__floatuntidf(make_ti(0x8000008000000000, 0), 0x1.000001p+127); + test__floatuntidf(make_ti(0x8000000000000800, 0), 0x1.0000000000001p+127); + test__floatuntidf(make_ti(0x8000010000000000, 0), 0x1.000002p+127); + test__floatuntidf(make_ti(0x8000000000001000, 0), 0x1.0000000000002p+127); + + test__floatuntidf(make_ti(0x8000000000000000, 0), 0x1.000000p+127); + test__floatuntidf(make_ti(0x8000000000000001, 0), 0x1.0000000000000002p+127); + + test__floatuntidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + test__floatuntidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + test__floatuntidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + test__floatuntidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + test__floatuntidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + test__floatuntidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + test__floatuntidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + test__floatuntidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + test__floatuntidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + test__floatuntidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + test__floatuntidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + test__floatuntidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); + test__floatuntidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); + test__floatuntidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); + test__floatuntidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); + test__floatuntidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + test__floatuntidf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121); + test__floatuntidf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121); + test__floatuntidf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121); + test__floatuntidf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121); + test__floatuntidf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); +} + +fn make_ti(high: u64, low: u64) u128 { + var result: u128 = high; + result <<= 64; + result |= low; + return result; +} diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index dc95aa23f2..6ad7768cb2 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -19,6 +19,8 @@ comptime { @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); + @export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage); + @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage); @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage); @export("__fixunssfti", @import("fixunssfti.zig").__fixunssfti, linkage); diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 156835bcb3..7b36bcd04a 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -340,11 +340,23 @@ fn testPeerErrorAndArray2(x: u8) error![]const u8 { }; } -test "explicit cast float number literal to integer if no fraction component" { +test "@floatToInt" { + testFloatToInts(); + comptime testFloatToInts(); +} + +fn testFloatToInts() void { const x = i32(1e4); assert(x == 10000); const y = @floatToInt(i32, f32(1e4)); assert(y == 10000); + expectFloatToInt(u8, 255.1, 255); + expectFloatToInt(i8, 127.2, 127); + expectFloatToInt(i8, -128.2, -128); +} + +fn expectFloatToInt(comptime T: type, f: f32, i: T) void { + assert(@floatToInt(T, f) == i); } test "cast u128 to f128 and back" { @@ -426,3 +438,10 @@ test "@bytesToSlice keeps pointer alignment" { const numbers = @bytesToSlice(u32, bytes[0..]); comptime assert(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); } + +test "@intCast i32 to u7" { + var x: u128 = @maxValue(u128); + var y: i32 = 120; + var z = x >> @intCast(u7, y); + assert(z == 0xff); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 609e3f103f..39107b4a21 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,23 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "@floatToInt comptime safety", + \\comptime { + \\ _ = @floatToInt(i8, f32(-129.1)); + \\} + \\comptime { + \\ _ = @floatToInt(u8, f32(-1.1)); + \\} + \\comptime { + \\ _ = @floatToInt(u8, f32(256.1)); + \\} + , + ".tmp_source.zig:2:9: error: integer value '-129' cannot be stored in type 'i8'", + ".tmp_source.zig:5:9: error: integer value '-1' cannot be stored in type 'u8'", + ".tmp_source.zig:8:9: error: integer value '256' cannot be stored in type 'u8'", + ); + cases.add( "use c_void as return type of fn ptr", \\export fn entry() void { diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index ea5beafe8d..a7e8d6dc0e 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -1,6 +1,45 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { + cases.addRuntimeSafety("@floatToInt cannot fit - negative to unsigned", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\pub fn main() void { + \\ baz(bar(-1.1)); + \\} + \\fn bar(a: f32) u8 { + \\ return @floatToInt(u8, a); + \\} + \\fn baz(a: u8) void { } + ); + + cases.addRuntimeSafety("@floatToInt cannot fit - negative out of range", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\pub fn main() void { + \\ baz(bar(-129.1)); + \\} + \\fn bar(a: f32) i8 { + \\ return @floatToInt(i8, a); + \\} + \\fn baz(a: i8) void { } + ); + + cases.addRuntimeSafety("@floatToInt cannot fit - positive out of range", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\pub fn main() void { + \\ baz(bar(256.2)); + \\} + \\fn bar(a: f32) u8 { + \\ return @floatToInt(u8, a); + \\} + \\fn baz(a: u8) void { } + ); + cases.addRuntimeSafety("calling panic", \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); -- cgit v1.2.3 From ee525c92a4c3bafa8f30c46e0303e0bca8f81860 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Jun 2018 17:21:08 -0400 Subject: langref: organize docs for inline loops and add note about when to use it --- doc/langref.html.in | 57 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 1bd28f9c3e..bdc33cb808 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2355,11 +2355,18 @@ fn eventuallyErrorSequence() error!u32 { break :blk numbers_left; }; } + {#code_end#} + + {#header_open|inline while#} +

    + While loops can be inlined. This causes the loop to be unrolled, which + allows the code to do some things which only work at compile time, + such as use types as first class values. +

    + {#code_begin|test#} +const assert = @import("std").debug.assert; test "inline while loop" { - // While loops can be inlined. This causes the loop to be unrolled, which - // allows the code to do some things which only work at compile time, - // such as use types as first class values. comptime var i = 0; var sum: usize = 0; inline while (i < 3) : (i += 1) { @@ -2378,6 +2385,16 @@ fn typeNameLength(comptime T: type) usize { return @typeName(T).len; } {#code_end#} +

    + It is recommended to use inline loops only for one of these reasons: +

    +
      +
    • You need the loop to execute at {#link|comptime#} for the semantics to work.
    • +
    • + You have a benchmark to prove that forcibly unrolling the loop in this way is measurably faster. +
    • +
    + {#header_close#} {#see_also|if|Optionals|Errors|comptime|unreachable#} {#header_close#} {#header_open|for#} @@ -2445,15 +2462,20 @@ test "for else" { break :blk sum; }; } - + {#code_end#} + {#header_open|inline for#} +

    + For loops can be inlined. This causes the loop to be unrolled, which + allows the code to do some things which only work at compile time, + such as use types as first class values. + The capture value and iterator value of inlined for loops are + compile-time known. +

    + {#code_begin|test#} +const assert = @import("std").debug.assert; test "inline for loop" { const nums = []i32{2, 4, 6}; - // For loops can be inlined. This causes the loop to be unrolled, which - // allows the code to do some things which only work at compile time, - // such as use types as first class values. - // The capture value and iterator value of inlined for loops are - // compile-time known. var sum: usize = 0; inline for (nums) |i| { const T = switch (i) { @@ -2471,6 +2493,16 @@ fn typeNameLength(comptime T: type) usize { return @typeName(T).len; } {#code_end#} +

    + It is recommended to use inline loops only for one of these reasons: +

    +
      +
    • You need the loop to execute at {#link|comptime#} for the semantics to work.
    • +
    • + You have a benchmark to prove that forcibly unrolling the loop in this way is measurably faster. +
    • +
    + {#header_close#} {#see_also|while|comptime|Arrays|Slices#} {#header_close#} {#header_open|if#} @@ -4222,13 +4254,8 @@ pub fn main() void { task in userland. It does so without introducing another language on top of Zig, such as a macro language or a preprocessor language. It's Zig all the way down.

    -

    TODO: suggestion to not use inline unless necessary

    - {#header_close#} {#header_close#} - {#header_open|inline#} -

    TODO: inline while

    -

    TODO: inline for

    -

    TODO: suggestion to not use inline unless necessary

    + {#see_also|inline while|inline for#} {#header_close#} {#header_open|Assembly#}

    TODO: example of inline assembly

    -- cgit v1.2.3 From 42db807f375d0c8fe248eebcb4eaeed71b43075d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Jun 2018 18:51:46 -0400 Subject: remove redundant implicit casting code and introduce better type mismatch errors closes #1061 --- src/ir.cpp | 564 +++++++++++++++--------------------------------- test/compile_errors.zig | 19 +- 2 files changed, 189 insertions(+), 394 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 1fe078dd3f..ba2a9b3fe2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -65,12 +65,7 @@ enum ConstCastResultId { ConstCastResultIdNullWrapPtr, }; -struct ConstCastErrSetMismatch { - ZigList missing_errors; -}; - struct ConstCastOnly; - struct ConstCastArg { size_t arg_index; ConstCastOnly *child; @@ -80,15 +75,22 @@ struct ConstCastArgNoAlias { size_t arg_index; }; +struct ConstCastOptionalMismatch; +struct ConstCastPointerMismatch; +struct ConstCastSliceMismatch; +struct ConstCastErrUnionErrSetMismatch; +struct ConstCastErrUnionPayloadMismatch; +struct ConstCastErrSetMismatch; + struct ConstCastOnly { ConstCastResultId id; union { - ConstCastErrSetMismatch error_set; - ConstCastOnly *pointer_child; - ConstCastOnly *slice_child; - ConstCastOnly *optional_child; - ConstCastOnly *error_union_payload; - ConstCastOnly *error_union_error_set; + ConstCastErrSetMismatch *error_set_mismatch; + ConstCastPointerMismatch *pointer_mismatch; + ConstCastSliceMismatch *slice_mismatch; + ConstCastOptionalMismatch *optional; + ConstCastErrUnionPayloadMismatch *error_union_payload; + ConstCastErrUnionErrSetMismatch *error_union_error_set; ConstCastOnly *return_type; ConstCastOnly *async_allocator_type; ConstCastOnly *null_wrap_ptr_child; @@ -97,6 +99,39 @@ struct ConstCastOnly { } data; }; +struct ConstCastOptionalMismatch { + ConstCastOnly child; + TypeTableEntry *wanted_child; + TypeTableEntry *actual_child; +}; + +struct ConstCastPointerMismatch { + ConstCastOnly child; + TypeTableEntry *wanted_child; + TypeTableEntry *actual_child; +}; + +struct ConstCastSliceMismatch { + ConstCastOnly child; + TypeTableEntry *wanted_child; + TypeTableEntry *actual_child; +}; + +struct ConstCastErrUnionErrSetMismatch { + ConstCastOnly child; + TypeTableEntry *wanted_err_set; + TypeTableEntry *actual_err_set; +}; + +struct ConstCastErrUnionPayloadMismatch { + ConstCastOnly child; + TypeTableEntry *wanted_payload; + TypeTableEntry *actual_payload; +}; + +struct ConstCastErrSetMismatch { + ZigList missing_errors; +}; static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope); static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval); @@ -7972,8 +8007,10 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const); if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdPointerChild; - result.data.pointer_child = allocate_nonzero(1); - *result.data.pointer_child = child; + result.data.pointer_mismatch = allocate_nonzero(1); + result.data.pointer_mismatch->child = child; + result.data.pointer_mismatch->wanted_child = wanted_type->data.pointer.child_type; + result.data.pointer_mismatch->actual_child = actual_type->data.pointer.child_type; } return result; } @@ -7992,8 +8029,10 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const); if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdSliceChild; - result.data.slice_child = allocate_nonzero(1); - *result.data.slice_child = child; + result.data.slice_mismatch = allocate_nonzero(1); + result.data.slice_mismatch->child = child; + result.data.slice_mismatch->actual_child = actual_ptr_type->data.pointer.child_type; + result.data.slice_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type; } return result; } @@ -8005,8 +8044,10 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry actual_type->data.maybe.child_type, source_node, wanted_is_mutable); if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdOptionalChild; - result.data.optional_child = allocate_nonzero(1); - *result.data.optional_child = child; + result.data.optional = allocate_nonzero(1); + result.data.optional->child = child; + result.data.optional->wanted_child = wanted_type->data.maybe.child_type; + result.data.optional->actual_child = actual_type->data.maybe.child_type; } return result; } @@ -8017,16 +8058,20 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry actual_type->data.error_union.payload_type, source_node, wanted_is_mutable); if (payload_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdErrorUnionPayload; - result.data.error_union_payload = allocate_nonzero(1); - *result.data.error_union_payload = payload_child; + result.data.error_union_payload = allocate_nonzero(1); + result.data.error_union_payload->child = payload_child; + result.data.error_union_payload->wanted_payload = wanted_type->data.error_union.payload_type; + result.data.error_union_payload->actual_payload = actual_type->data.error_union.payload_type; return result; } ConstCastOnly error_set_child = types_match_const_cast_only(ira, wanted_type->data.error_union.err_set_type, actual_type->data.error_union.err_set_type, source_node, wanted_is_mutable); if (error_set_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdErrorUnionErrorSet; - result.data.error_union_error_set = allocate_nonzero(1); - *result.data.error_union_error_set = error_set_child; + result.data.error_union_error_set = allocate_nonzero(1); + result.data.error_union_error_set->child = error_set_child; + result.data.error_union_error_set->wanted_err_set = wanted_type->data.error_union.err_set_type; + result.data.error_union_error_set->actual_err_set = actual_type->data.error_union.err_set_type; return result; } return result; @@ -8068,8 +8113,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry if (error_entry == nullptr) { if (result.id == ConstCastResultIdOk) { result.id = ConstCastResultIdErrSet; + result.data.error_set_mismatch = allocate(1); } - result.data.error_set.missing_errors.append(contained_error_entry); + result.data.error_set_mismatch->missing_errors.append(contained_error_entry); } } free(errors); @@ -8164,325 +8210,6 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry return result; } -enum ImplicitCastMatchResult { - ImplicitCastMatchResultNo, - ImplicitCastMatchResultYes, - ImplicitCastMatchResultReportedError, -}; - -static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *wanted_type, - TypeTableEntry *actual_type, IrInstruction *value) -{ - AstNode *source_node = value->source_node; - ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, - source_node, false); - if (const_cast_result.id == ConstCastResultIdOk) { - return ImplicitCastMatchResultYes; - } - - // if we got here with error sets, make an error showing the incompatibilities - ZigList *missing_errors = nullptr; - if (const_cast_result.id == ConstCastResultIdErrSet) { - missing_errors = &const_cast_result.data.error_set.missing_errors; - } - if (const_cast_result.id == ConstCastResultIdErrorUnionErrorSet) { - if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSet) { - missing_errors = &const_cast_result.data.error_union_error_set->data.error_set.missing_errors; - } else if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSetGlobal) { - ErrorMsg *msg = ir_add_error(ira, value, - buf_sprintf("expected '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); - add_error_note(ira->codegen, msg, value->source_node, - buf_sprintf("unable to cast global error set into smaller set")); - return ImplicitCastMatchResultReportedError; - } - } else if (const_cast_result.id == ConstCastResultIdErrSetGlobal) { - ErrorMsg *msg = ir_add_error(ira, value, - buf_sprintf("expected '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); - add_error_note(ira->codegen, msg, value->source_node, - buf_sprintf("unable to cast global error set into smaller set")); - return ImplicitCastMatchResultReportedError; - } - if (missing_errors != nullptr) { - ErrorMsg *msg = ir_add_error(ira, value, - buf_sprintf("expected '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); - for (size_t i = 0; i < missing_errors->length; i += 1) { - ErrorTableEntry *error_entry = missing_errors->at(i); - add_error_note(ira->codegen, msg, error_entry->decl_node, - buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name))); - } - - return ImplicitCastMatchResultReportedError; - } - - // implicit conversion from ?T to ?U - if (wanted_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdOptional) { - ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, wanted_type->data.maybe.child_type, - actual_type->data.maybe.child_type, value); - if (res != ImplicitCastMatchResultNo) - return res; - } - - // implicit conversion from non maybe type to maybe type - if (wanted_type->id == TypeTableEntryIdOptional) { - ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, wanted_type->data.maybe.child_type, - actual_type, value); - if (res != ImplicitCastMatchResultNo) - return res; - } - - // implicit conversion from null literal to maybe type - if (wanted_type->id == TypeTableEntryIdOptional && - actual_type->id == TypeTableEntryIdNull) - { - return ImplicitCastMatchResultYes; - } - - // implicit T to U!T - if (wanted_type->id == TypeTableEntryIdErrorUnion && - ir_types_match_with_implicit_cast(ira, wanted_type->data.error_union.payload_type, actual_type, value)) - { - return ImplicitCastMatchResultYes; - } - - // implicit conversion from error set to error union type - if (wanted_type->id == TypeTableEntryIdErrorUnion && - actual_type->id == TypeTableEntryIdErrorSet) - { - return ImplicitCastMatchResultYes; - } - - // implicit conversion from T to U!?T - if (wanted_type->id == TypeTableEntryIdErrorUnion && - wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional && - ir_types_match_with_implicit_cast(ira, - wanted_type->data.error_union.payload_type->data.maybe.child_type, - actual_type, value)) - { - return ImplicitCastMatchResultYes; - } - - // implicit widening conversion - if (wanted_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdInt && - wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && - wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count) - { - return ImplicitCastMatchResultYes; - } - - // small enough unsigned ints can get casted to large enough signed ints - if (wanted_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed && - actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed && - wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count) - { - return ImplicitCastMatchResultYes; - } - - // implicit float widening conversion - if (wanted_type->id == TypeTableEntryIdFloat && - actual_type->id == TypeTableEntryIdFloat && - wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) - { - return ImplicitCastMatchResultYes; - } - - // implicit [N]T to []const T - if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) { - TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; - assert(ptr_type->id == TypeTableEntryIdPointer); - - if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, - source_node, false).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - } - - // implicit &const [N]T to []const T - if (is_slice(wanted_type) && - actual_type->id == TypeTableEntryIdPointer && - actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.is_const && - actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) - { - TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; - assert(ptr_type->id == TypeTableEntryIdPointer); - - TypeTableEntry *array_type = actual_type->data.pointer.child_type; - - if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, - source_node, false).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - } - - // implicit [N]T to &const []const T - if (wanted_type->id == TypeTableEntryIdPointer && - wanted_type->data.pointer.is_const && - wanted_type->data.pointer.ptr_len == PtrLenSingle && - is_slice(wanted_type->data.pointer.child_type) && - actual_type->id == TypeTableEntryIdArray) - { - TypeTableEntry *ptr_type = - wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; - assert(ptr_type->id == TypeTableEntryIdPointer); - if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, - actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - } - - // implicit *[N]T to [*]T - if (wanted_type->id == TypeTableEntryIdPointer && - wanted_type->data.pointer.ptr_len == PtrLenUnknown && - actual_type->id == TypeTableEntryIdPointer && - actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.child_type->id == TypeTableEntryIdArray && - types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node, - !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - - // implicit *[N]T to []T - if (is_slice(wanted_type) && - actual_type->id == TypeTableEntryIdPointer && - actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) - { - TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; - assert(slice_ptr_type->id == TypeTableEntryIdPointer); - if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node, - !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - } - - // implicit [N]T to ?[]const T - if (wanted_type->id == TypeTableEntryIdOptional && - is_slice(wanted_type->data.maybe.child_type) && - actual_type->id == TypeTableEntryIdArray) - { - TypeTableEntry *ptr_type = - wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; - assert(ptr_type->id == TypeTableEntryIdPointer); - if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, - actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - } - - - // implicit number literal to typed number - // implicit number literal to &const integer - if (actual_type->id == TypeTableEntryIdComptimeFloat || - actual_type->id == TypeTableEntryIdComptimeInt) - { - if (wanted_type->id == TypeTableEntryIdPointer && - wanted_type->data.pointer.ptr_len == PtrLenSingle && - wanted_type->data.pointer.is_const) - { - if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.pointer.child_type, false)) { - return ImplicitCastMatchResultYes; - } else { - return ImplicitCastMatchResultReportedError; - } - } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, false)) { - return ImplicitCastMatchResultYes; - } else { - return ImplicitCastMatchResultReportedError; - } - } - - // implicit typed number to integer or float literal. - // works when the number is known - if (value->value.special == ConstValSpecialStatic) { - if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) { - return ImplicitCastMatchResultYes; - } else if (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdComptimeFloat) { - return ImplicitCastMatchResultYes; - } - } - - // implicit union to its enum tag type - if (wanted_type->id == TypeTableEntryIdEnum && actual_type->id == TypeTableEntryIdUnion && - (actual_type->data.unionation.decl_node->data.container_decl.auto_enum || - actual_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) - { - type_ensure_zero_bits_known(ira->codegen, actual_type); - if (actual_type->data.unionation.tag_type == wanted_type) { - return ImplicitCastMatchResultYes; - } - } - - // implicit enum to union which has the enum as the tag type - if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum && - (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum || - wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) - { - type_ensure_zero_bits_known(ira->codegen, wanted_type); - if (wanted_type->data.unionation.tag_type == actual_type) { - return ImplicitCastMatchResultYes; - } - } - - // implicit enum to &const union which has the enum as the tag type - if (actual_type->id == TypeTableEntryIdEnum && - wanted_type->id == TypeTableEntryIdPointer && - wanted_type->data.pointer.ptr_len == PtrLenSingle) - { - TypeTableEntry *union_type = wanted_type->data.pointer.child_type; - if (union_type->data.unionation.decl_node->data.container_decl.auto_enum || - union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr) - { - type_ensure_zero_bits_known(ira->codegen, union_type); - if (union_type->data.unionation.tag_type == actual_type) { - return ImplicitCastMatchResultYes; - } - } - } - - // implicit T to *T where T is zero bits - if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && - types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type, source_node, false).id == ConstCastResultIdOk) - { - type_ensure_zero_bits_known(ira->codegen, actual_type); - if (!type_has_bits(actual_type)) { - return ImplicitCastMatchResultYes; - } - } - - // implicit undefined literal to anything - if (actual_type->id == TypeTableEntryIdUndefined) { - return ImplicitCastMatchResultYes; - } - - // implicitly take a const pointer to something - if (!type_requires_comptime(actual_type)) { - TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (wanted_type->id == TypeTableEntryIdPointer && - wanted_type->data.pointer.ptr_len == PtrLenSingle && - types_match_const_cast_only(ira, wanted_type, const_ptr_actual, - source_node, false).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - } - - return ImplicitCastMatchResultNo; -} - static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *errors_count) { size_t old_errors_count = *errors_count; *errors_count = g->errors_by_index.length; @@ -10227,6 +9954,83 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou return result; } +static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCastOnly *cast_result, + ErrorMsg *parent_msg) +{ + switch (cast_result->id) { + case ConstCastResultIdOk: + zig_unreachable(); + case ConstCastResultIdOptionalChild: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("optional type child '%s' cannot cast into optional type child '%s'", + buf_ptr(&cast_result->data.optional->actual_child->name), + buf_ptr(&cast_result->data.optional->wanted_child->name))); + report_recursive_error(ira, source_node, &cast_result->data.optional->child, msg); + break; + } + case ConstCastResultIdErrorUnionErrorSet: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("error set '%s' cannot cast into error set '%s'", + buf_ptr(&cast_result->data.error_union_error_set->actual_err_set->name), + buf_ptr(&cast_result->data.error_union_error_set->wanted_err_set->name))); + report_recursive_error(ira, source_node, &cast_result->data.error_union_error_set->child, msg); + break; + } + case ConstCastResultIdErrSet: { + ZigList *missing_errors = &cast_result->data.error_set_mismatch->missing_errors; + for (size_t i = 0; i < missing_errors->length; i += 1) { + ErrorTableEntry *error_entry = missing_errors->at(i); + add_error_note(ira->codegen, parent_msg, error_entry->decl_node, + buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name))); + } + break; + } + case ConstCastResultIdErrSetGlobal: { + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("cannot cast global error set into smaller set")); + break; + } + case ConstCastResultIdPointerChild: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("pointer type child '%s' cannot cast into pointer type child '%s'", + buf_ptr(&cast_result->data.pointer_mismatch->actual_child->name), + buf_ptr(&cast_result->data.pointer_mismatch->wanted_child->name))); + report_recursive_error(ira, source_node, &cast_result->data.pointer_mismatch->child, msg); + break; + } + case ConstCastResultIdSliceChild: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("slice type child '%s' cannot cast into slice type child '%s'", + buf_ptr(&cast_result->data.slice_mismatch->actual_child->name), + buf_ptr(&cast_result->data.slice_mismatch->wanted_child->name))); + report_recursive_error(ira, source_node, &cast_result->data.slice_mismatch->child, msg); + break; + } + case ConstCastResultIdErrorUnionPayload: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("error union payload '%s' cannot cast into error union payload '%s'", + buf_ptr(&cast_result->data.error_union_payload->actual_payload->name), + buf_ptr(&cast_result->data.error_union_payload->wanted_payload->name))); + report_recursive_error(ira, source_node, &cast_result->data.error_union_payload->child, msg); + break; + } + case ConstCastResultIdFnAlign: // TODO + case ConstCastResultIdFnCC: // TODO + case ConstCastResultIdFnVarArgs: // TODO + case ConstCastResultIdFnIsGeneric: // TODO + case ConstCastResultIdFnReturnType: // TODO + case ConstCastResultIdFnArgCount: // TODO + case ConstCastResultIdFnGenericArgCount: // TODO + case ConstCastResultIdFnArg: // TODO + case ConstCastResultIdFnArgNoAlias: // TODO + case ConstCastResultIdType: // TODO + case ConstCastResultIdUnresolvedInferredErrSet: // TODO + case ConstCastResultIdAsyncAllocatorType: // TODO + case ConstCastResultIdNullWrapPtr: // TODO + break; + } +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, TypeTableEntry *wanted_type, IrInstruction *value) { @@ -10238,11 +10042,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // perfect match or non-const to const - if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node, false).id == ConstCastResultIdOk) { + ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, + source_node, false); + if (const_cast_result.id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } - // explicit widening conversion + // widening conversion if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && @@ -10259,7 +10065,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } - // explicit float widening conversion + // float widening conversion if (wanted_type->id == TypeTableEntryIdFloat && actual_type->id == TypeTableEntryIdFloat && wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) @@ -10268,7 +10074,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } - // explicit cast from [N]T to []const T + // cast from [N]T to []const T if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) { TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); @@ -10280,7 +10086,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from &const [N]T to []const T + // cast from &const [N]T to []const T if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.is_const && @@ -10299,7 +10105,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from [N]T to &const []const T + // cast from [N]T to &const []const T if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.is_const && is_slice(wanted_type->data.pointer.child_type) && @@ -10324,7 +10130,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from [N]T to ?[]const N + // cast from [N]T to ?[]const N if (wanted_type->id == TypeTableEntryIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == TypeTableEntryIdArray) @@ -10348,7 +10154,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit *[N]T to [*]T + // *[N]T to [*]T if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenUnknown && actual_type->id == TypeTableEntryIdPointer && @@ -10362,7 +10168,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); } - // explicit *[N]T to []T + // *[N]T to []T if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && @@ -10379,7 +10185,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } - // explicit cast from T to ?T + // cast from T to ?T // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism if (wanted_type->id == TypeTableEntryIdOptional) { TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; @@ -10411,14 +10217,14 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from null literal to maybe type + // cast from null literal to maybe type if (wanted_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdNull) { return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); } - // explicit cast from child type of error type to error type + // cast from child type of error type to error type if (wanted_type->id == TypeTableEntryIdErrorUnion) { if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node, false).id == ConstCastResultIdOk) @@ -10435,7 +10241,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from [N]T to E![]const T + // cast from [N]T to E![]const T if (wanted_type->id == TypeTableEntryIdErrorUnion && is_slice(wanted_type->data.error_union.payload_type) && actual_type->id == TypeTableEntryIdArray) @@ -10459,14 +10265,14 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from error set to error union type + // cast from error set to error union type if (wanted_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorSet) { return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type); } - // explicit cast from T to E!?T + // cast from T to E!?T if (wanted_type->id == TypeTableEntryIdErrorUnion && wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional && actual_type->id != TypeTableEntryIdOptional) @@ -10489,8 +10295,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from number literal to another type - // explicit cast from number literal to *const integer + // cast from number literal to another type + // cast from number literal to *const integer if (actual_type->id == TypeTableEntryIdComptimeFloat || actual_type->id == TypeTableEntryIdComptimeInt) { @@ -10540,7 +10346,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from typed number to integer or float literal. + // cast from typed number to integer or float literal. // works when the number is known at compile time if (instr_is_comptime(value) && ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) || @@ -10549,7 +10355,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); } - // explicit cast from union to the enum type of the union + // cast from union to the enum type of the union if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) { type_ensure_zero_bits_known(ira->codegen, actual_type); if (type_is_invalid(actual_type)) @@ -10560,7 +10366,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit enum to union which has the enum as the tag type + // enum to union which has the enum as the tag type if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum && (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum || wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) @@ -10571,7 +10377,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit enum to &const union which has the enum as the tag type + // enum to &const union which has the enum as the tag type if (actual_type->id == TypeTableEntryIdEnum && wanted_type->id == TypeTableEntryIdPointer) { TypeTableEntry *union_type = wanted_type->data.pointer.child_type; if (union_type->data.unionation.decl_node->data.container_decl.auto_enum || @@ -10592,7 +10398,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from *T to *[1]T + // cast from *T to *[1]T if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle) { @@ -10616,7 +10422,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from T to *T where T is zero bits + // cast from T to *T where T is zero bits if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) @@ -10631,12 +10437,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } - // explicit cast from undefined to anything + // cast from undefined to anything if (actual_type->id == TypeTableEntryIdUndefined) { return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); } - // explicit cast from something to const pointer of it + // cast from something to const pointer of it if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node, false).id == ConstCastResultIdOk) { @@ -10644,10 +10450,11 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("invalid cast from type '%s' to '%s'", - buf_ptr(&actual_type->name), - buf_ptr(&wanted_type->name))); + ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("expected type '%s', found '%s'", + buf_ptr(&wanted_type->name), + buf_ptr(&actual_type->name))); + report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg); return ira->codegen->invalid_instruction; } @@ -10664,22 +10471,7 @@ static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, Typ if (value->value.type->id == TypeTableEntryIdUnreachable) return value; - ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, expected_type, value->value.type, value); - switch (result) { - case ImplicitCastMatchResultNo: - ir_add_error(ira, value, - buf_sprintf("expected type '%s', found '%s'", - buf_ptr(&expected_type->name), - buf_ptr(&value->value.type->name))); - return ira->codegen->invalid_instruction; - - case ImplicitCastMatchResultYes: - return ir_analyze_cast(ira, value, expected_type, value); - case ImplicitCastMatchResultReportedError: - return ira->codegen->invalid_instruction; - } - - zig_unreachable(); + return ir_analyze_cast(ira, value, expected_type, value); } static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 39107b4a21..17896f9ab9 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -100,7 +100,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var rule_set = try Foo.init(); \\} , - ".tmp_source.zig:2:13: error: invalid cast from type 'type' to 'i32'", + ".tmp_source.zig:2:13: error: expected type 'i32', found 'type'", ); cases.add( @@ -122,7 +122,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "invalid deref on switch target", + "nested error set mismatch", \\const NextError = error{NextError}; \\const OtherError = error{OutOfMemory}; \\ @@ -134,7 +134,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return null; \\} , - ".tmp_source.zig:5:34: error: expected 'NextError!i32', found 'OtherError!i32'", + ".tmp_source.zig:5:34: error: expected type '?NextError!i32', found '?OtherError!i32'", + ".tmp_source.zig:5:34: note: optional type child 'OtherError!i32' cannot cast into optional type child 'NextError!i32'", + ".tmp_source.zig:5:34: note: error set 'OtherError' cannot cast into error set 'NextError'", ".tmp_source.zig:2:26: note: 'error.OutOfMemory' not a member of destination error set", ); @@ -437,8 +439,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.B; \\} , - ".tmp_source.zig:3:35: error: expected 'SmallErrorSet!i32', found 'error!i32'", - ".tmp_source.zig:3:35: note: unable to cast global error set into smaller set", + ".tmp_source.zig:3:35: error: expected type 'SmallErrorSet!i32', found 'error!i32'", + ".tmp_source.zig:3:35: note: error set 'error' cannot cast into error set 'SmallErrorSet'", + ".tmp_source.zig:3:35: note: cannot cast global error set into smaller set", ); cases.add( @@ -451,8 +454,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.B; \\} , - ".tmp_source.zig:3:31: error: expected 'SmallErrorSet', found 'error'", - ".tmp_source.zig:3:31: note: unable to cast global error set into smaller set", + ".tmp_source.zig:3:31: error: expected type 'SmallErrorSet', found 'error'", + ".tmp_source.zig:3:31: note: cannot cast global error set into smaller set", ); cases.add( @@ -478,7 +481,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: Set2 = set1; \\} , - ".tmp_source.zig:7:19: error: expected 'Set2', found 'Set1'", + ".tmp_source.zig:7:19: error: expected type 'Set2', found 'Set1'", ".tmp_source.zig:1:23: note: 'error.B' not a member of destination error set", ); -- cgit v1.2.3 From 7c99c30bf406342a45833963ce630bb104aef00e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Jun 2018 19:35:35 -0400 Subject: fix calling method with comptime pass-by-non-copyign-value self arg closes #1124 --- src/analyze.cpp | 11 +++++++++++ test/cases/eval.zig | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/analyze.cpp b/src/analyze.cpp index 10cdb0af6f..479abef16a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1470,6 +1470,17 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } + if (param_node->data.param_decl.type != nullptr) { + TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); + if (type_is_invalid(type_entry)) { + return g->builtin_types.entry_invalid; + } + FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; + param_info->type = type_entry; + param_info->is_noalias = param_node->data.param_decl.is_noalias; + fn_type_id.next_param_index += 1; + } + return get_generic_fn_type(g, &fn_type_id); } else if (param_is_var_args) { if (fn_type_id.cc == CallingConventionC) { diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 6c919e17a6..756ffe339a 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -623,3 +623,17 @@ test "function which returns struct with type field causes implicit comptime" { const ty = wrap(i32).T; assert(ty == i32); } + +test "call method with comptime pass-by-non-copying-value self parameter" { + const S = struct { + a: u8, + + fn b(comptime s: this) u8 { + return s.a; + } + }; + + const s = S{ .a = 2 }; + var b = s.b(); + assert(b == 2); +} -- cgit v1.2.3 From 55193cb13bbc69350474f6a66728319b41149274 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 20 Jun 2018 06:45:45 -0400 Subject: fix runtime fn ptr equality codegen closes #1140 --- src/codegen.cpp | 10 +++++----- test/cases/misc.zig | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 20268d636a..c2406f0838 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2246,12 +2246,12 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, } else if (type_entry->id == TypeTableEntryIdInt) { LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, type_entry->data.integral.is_signed); return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); - } else if (type_entry->id == TypeTableEntryIdEnum) { - LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); - return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); - } else if (type_entry->id == TypeTableEntryIdErrorSet || + } else if (type_entry->id == TypeTableEntryIdEnum || + type_entry->id == TypeTableEntryIdErrorSet || type_entry->id == TypeTableEntryIdPointer || - type_entry->id == TypeTableEntryIdBool) + type_entry->id == TypeTableEntryIdBool || + type_entry->id == TypeTableEntryIdPromise || + type_entry->id == TypeTableEntryIdFn) { LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 89c441e7f9..d539f79a57 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -701,3 +701,8 @@ test "comptime cast fn to ptr" { const addr2 = @ptrCast(*const u8, emptyFn); comptime assert(addr1 == addr2); } + +test "equality compare fn ptrs" { + var a = emptyFn; + assert(a == a); +} -- cgit v1.2.3 From 457c0f0a7e424177e7d8d00f4429da292b7c67d5 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 21 Jun 2018 00:39:19 +0900 Subject: std.mem: remove allocator create in favor of construct; ref #733 --- std/mem.zig | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/std/mem.zig b/std/mem.zig index 55844b88db..b3c3e59cec 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -32,15 +32,7 @@ pub const Allocator = struct { freeFn: fn (self: *Allocator, old_mem: []u8) void, /// Call destroy with the result - pub fn create(self: *Allocator, comptime T: type) !*T { - if (@sizeOf(T) == 0) return *{}; - const slice = try self.alloc(T, 1); - return &slice[0]; - } - - /// Call destroy with the result - /// TODO once #733 is solved, this will replace create - pub fn construct(self: *Allocator, init: var) Error!*@typeOf(init) { + pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) { const T = @typeOf(init); if (@sizeOf(T) == 0) return &{}; const slice = try self.alloc(T, 1); @@ -49,6 +41,13 @@ pub const Allocator = struct { return ptr; } + /// Alias of create + /// Call destroy with the result + pub fn construct(self: *Allocator, init: var) Error!*@typeOf(init) { + debug.warn("std.mem.Allocator.construct is deprecated;\n"); + return self.create(init); + } + /// `ptr` should be the return value of `construct` or `create` pub fn destroy(self: *Allocator, ptr: var) void { const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr)); -- cgit v1.2.3 From 71db8df5480f4d849480267574cd5491066e3868 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 21 Jun 2018 00:40:21 +0900 Subject: std: update stdlib to match updated allocator create signature; ref #733 --- src-self-hosted/module.zig | 24 +++++++------- std/atomic/queue.zig | 6 ++-- std/atomic/stack.zig | 6 ++-- std/build.zig | 55 +++++++++++-------------------- std/debug/index.zig | 17 ++++------ std/heap.zig | 2 +- std/io.zig | 8 ++--- std/linked_list.zig | 6 +++- std/os/child_process.zig | 9 ++--- std/os/index.zig | 7 ++-- test/tests.zig | 82 +++++++++++++++++++++------------------------- 11 files changed, 100 insertions(+), 122 deletions(-) diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 575105f25f..997ef5eed2 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -110,11 +110,12 @@ pub const Module = struct { parent: ?*CliPkg, pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg { - var pkg = try allocator.create(CliPkg); - pkg.name = name; - pkg.path = path; - pkg.children = ArrayList(*CliPkg).init(allocator); - pkg.parent = parent; + var pkg = try allocator.create(CliPkg{ + .name = name, + .path = path, + .children = ArrayList(*CliPkg).init(allocator), + .parent = parent + }); return pkg; } @@ -139,10 +140,7 @@ pub const Module = struct { const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory; errdefer c.LLVMDisposeBuilder(builder); - const module_ptr = try allocator.create(Module); - errdefer allocator.destroy(module_ptr); - - module_ptr.* = Module{ + const module_ptr = try allocator.create(Module{ .allocator = allocator, .name = name_buffer, .root_src_path = root_src_path, @@ -196,7 +194,8 @@ pub const Module = struct { .test_filters = [][]const u8{}, .test_name_prefix = null, .emit_file_type = Emit.Binary, - }; + }); + errdefer allocator.destroy(module_ptr); return module_ptr; } @@ -279,13 +278,12 @@ pub const Module = struct { } } - const link_lib = try self.allocator.create(LinkLib); - link_lib.* = LinkLib{ + const link_lib = try self.allocator.create(LinkLib{ .name = name, .path = null, .provided_explicitly = provided_explicitly, .symbols = ArrayList([]u8).init(self.allocator), - }; + }); try self.link_libs_list.append(link_lib); if (is_libc) { self.libc_link_lib = link_lib; diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 3dc64dbea2..5b810f95ac 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -114,8 +114,10 @@ fn startPuts(ctx: *Context) u8 { while (put_count != 0) : (put_count -= 1) { std.os.time.sleep(0, 1); // let the os scheduler be our fuzz const x = @bitCast(i32, r.random.scalar(u32)); - const node = ctx.allocator.create(Queue(i32).Node) catch unreachable; - node.data = x; + const node = ctx.allocator.create(Queue(i32).Node{ + .next = null, + .data = x + }) catch unreachable; ctx.queue.put(node); _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); } diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 9e81d89257..2272be4a92 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -117,8 +117,10 @@ fn startPuts(ctx: *Context) u8 { while (put_count != 0) : (put_count -= 1) { std.os.time.sleep(0, 1); // let the os scheduler be our fuzz const x = @bitCast(i32, r.random.scalar(u32)); - const node = ctx.allocator.create(Stack(i32).Node) catch unreachable; - node.data = x; + const node = ctx.allocator.create(Stack(i32).Node{ + .next = null, + .data = x + }) catch unreachable; ctx.stack.push(node); _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); } diff --git a/std/build.zig b/std/build.zig index 92454a183a..99de9b5197 100644 --- a/std/build.zig +++ b/std/build.zig @@ -158,8 +158,7 @@ pub const Builder = struct { } pub fn addTest(self: *Builder, root_src: []const u8) *TestStep { - const test_step = self.allocator.create(TestStep) catch unreachable; - test_step.* = TestStep.init(self, root_src); + const test_step = self.allocator.create(TestStep.init(self, root_src)) catch unreachable; return test_step; } @@ -191,21 +190,18 @@ pub const Builder = struct { } pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep { - const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; - write_file_step.* = WriteFileStep.init(self, file_path, data); + const write_file_step = self.allocator.create(WriteFileStep.init(self, file_path, data)) catch unreachable; return write_file_step; } pub fn addLog(self: *Builder, comptime format: []const u8, args: ...) *LogStep { const data = self.fmt(format, args); - const log_step = self.allocator.create(LogStep) catch unreachable; - log_step.* = LogStep.init(self, data); + const log_step = self.allocator.create(LogStep.init(self, data)) catch unreachable; return log_step; } pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep { - const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable; - remove_dir_step.* = RemoveDirStep.init(self, dir_path); + const remove_dir_step = self.allocator.create(RemoveDirStep.init(self, dir_path)) catch unreachable; return remove_dir_step; } @@ -404,11 +400,10 @@ pub const Builder = struct { } pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step { - const step_info = self.allocator.create(TopLevelStep) catch unreachable; - step_info.* = TopLevelStep{ + const step_info = self.allocator.create(TopLevelStep{ .step = Step.initNoOp(name, self.allocator), .description = description, - }; + }) catch unreachable; self.top_level_steps.append(step_info) catch unreachable; return &step_info.step; } @@ -598,8 +593,7 @@ pub const Builder = struct { const full_dest_path = os.path.resolve(self.allocator, self.prefix, dest_rel_path) catch unreachable; self.pushInstalledFile(full_dest_path); - const install_step = self.allocator.create(InstallFileStep) catch unreachable; - install_step.* = InstallFileStep.init(self, src_path, full_dest_path); + const install_step = self.allocator.create(InstallFileStep.init(self, src_path, full_dest_path)) catch unreachable; return install_step; } @@ -837,51 +831,43 @@ pub const LibExeObjStep = struct { }; pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8, ver: *const Version) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, ver); + const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Lib, false, ver)) catch unreachable; return self; } pub fn createCSharedLibrary(builder: *Builder, name: []const u8, version: *const Version) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initC(builder, name, Kind.Lib, version, false); + const self = builder.allocator.create(initC(builder, name, Kind.Lib, version, false)) catch unreachable; return self; } pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0)); + const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0))) catch unreachable; return self; } pub fn createCStaticLibrary(builder: *Builder, name: []const u8) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true); + const self = builder.allocator.create(initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true)) catch unreachable; return self; } pub fn createObject(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); + const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0))) catch unreachable; return self; } pub fn createCObject(builder: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false); + const self = builder.allocator.create(initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false)) catch unreachable; self.object_src = src; return self; } pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Exe, false, builder.version(0, 0, 0)); + const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Exe, false, builder.version(0, 0, 0))) catch unreachable; return self; } pub fn createCExecutable(builder: *Builder, name: []const u8) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false); + const self = builder.allocator.create(initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false)) catch unreachable; return self; } @@ -1748,14 +1734,14 @@ pub const CommandStep = struct { /// ::argv is copied. pub fn create(builder: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) *CommandStep { - const self = builder.allocator.create(CommandStep) catch unreachable; - self.* = CommandStep{ + const self = builder.allocator.create(CommandStep{ .builder = builder, .step = Step.init(argv[0], builder.allocator, make), .argv = builder.allocator.alloc([]u8, argv.len) catch unreachable, .cwd = cwd, .env_map = env_map, - }; + }) catch unreachable; + mem.copy([]const u8, self.argv, argv); self.step.name = self.argv[0]; return self; @@ -1778,18 +1764,17 @@ const InstallArtifactStep = struct { const Self = this; pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self { - const self = builder.allocator.create(Self) catch unreachable; const dest_dir = switch (artifact.kind) { LibExeObjStep.Kind.Obj => unreachable, LibExeObjStep.Kind.Exe => builder.exe_dir, LibExeObjStep.Kind.Lib => builder.lib_dir, }; - self.* = Self{ + const self = builder.allocator.create(Self{ .builder = builder, .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make), .artifact = artifact, .dest_file = os.path.join(builder.allocator, dest_dir, artifact.out_filename) catch unreachable, - }; + }) catch unreachable; self.step.dependOn(&artifact.step); builder.pushInstalledFile(self.dest_file); if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) { diff --git a/std/debug/index.zig b/std/debug/index.zig index 198e0f90f6..19cee3c65d 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -249,9 +249,7 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace { switch (builtin.object_format) { builtin.ObjectFormat.elf => { - const st = try allocator.create(ElfStackTrace); - errdefer allocator.destroy(st); - st.* = ElfStackTrace{ + const st = try allocator.create(ElfStackTrace{ .self_exe_file = undefined, .elf = undefined, .debug_info = undefined, @@ -261,7 +259,8 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace { .debug_ranges = null, .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator), .compile_unit_list = ArrayList(CompileUnit).init(allocator), - }; + }); + errdefer allocator.destroy(st); st.self_exe_file = try os.openSelfExe(); errdefer st.self_exe_file.close(); @@ -280,11 +279,10 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace { var exe_file = try os.openSelfExe(); defer exe_file.close(); - const st = try allocator.create(ElfStackTrace); + const st = try allocator.create(ElfStackTrace{ + .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)) + }); errdefer allocator.destroy(st); - - st.* = ElfStackTrace{ .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)) }; - return st; }, builtin.ObjectFormat.coff => { @@ -974,8 +972,7 @@ fn scanAllCompileUnits(st: *ElfStackTrace) !void { try st.self_exe_file.seekTo(compile_unit_pos); - const compile_unit_die = try st.allocator().create(Die); - compile_unit_die.* = try parseDie(st, abbrev_table, is_64); + const compile_unit_die = try st.allocator().create( try parseDie(st, abbrev_table, is_64) ); if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; diff --git a/std/heap.zig b/std/heap.zig index c948818e3d..7fc00cd0a4 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -407,7 +407,7 @@ fn testAllocator(allocator: *mem.Allocator) !void { var slice = try allocator.alloc(*i32, 100); for (slice) |*item, i| { - item.* = try allocator.create(i32); + item.* = try allocator.create(i32(0)); item.*.* = @intCast(i32, i); } diff --git a/std/io.zig b/std/io.zig index cfe1a7f585..1c468f6f4f 100644 --- a/std/io.zig +++ b/std/io.zig @@ -414,14 +414,12 @@ pub const BufferedAtomicFile = struct { pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile { // TODO with well defined copy elision we don't need this allocation - var self = try allocator.create(BufferedAtomicFile); - errdefer allocator.destroy(self); - - self.* = BufferedAtomicFile{ + var self = try allocator.create(BufferedAtomicFile{ .atomic_file = undefined, .file_stream = undefined, .buffered_stream = undefined, - }; + }); + errdefer allocator.destroy(self); self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.default_file_mode); errdefer self.atomic_file.deinit(); diff --git a/std/linked_list.zig b/std/linked_list.zig index 9e32b7d9da..f4f7aab752 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -193,7 +193,11 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// A pointer to the new node. pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node { comptime assert(!isIntrusive()); - return allocator.create(Node); + return allocator.create(Node{ + .prev = null, + .next = null, + .data = undefined + }); } /// Deallocate a node. diff --git a/std/os/child_process.zig b/std/os/child_process.zig index da5e708555..693129eea8 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -85,10 +85,7 @@ pub const ChildProcess = struct { /// First argument in argv is the executable. /// On success must call deinit. pub fn init(argv: []const []const u8, allocator: *mem.Allocator) !*ChildProcess { - const child = try allocator.create(ChildProcess); - errdefer allocator.destroy(child); - - child.* = ChildProcess{ + const child = try allocator.create(ChildProcess{ .allocator = allocator, .argv = argv, .pid = undefined, @@ -109,8 +106,8 @@ pub const ChildProcess = struct { .stdin_behavior = StdIo.Inherit, .stdout_behavior = StdIo.Inherit, .stderr_behavior = StdIo.Inherit, - }; - + }); + errdefer allocator.destroy(child); return child; } diff --git a/std/os/index.zig b/std/os/index.zig index dd0d4e2ea1..7b69bd0b36 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2582,8 +2582,11 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory; errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; - const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; - outer_context.inner = context; + const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext{ + .thread = undefined, + .inner = context + }) catch unreachable; + outer_context.thread.data.heap_handle = heap_handle; outer_context.thread.data.alloc_start = bytes_ptr; diff --git a/test/tests.zig b/test/tests.zig index b66441f628..66eb2d93a0 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -48,13 +48,12 @@ const test_targets = []TestTarget{ const max_stdout_size = 1 * 1024 * 1024; // 1 MB pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(CompareOutputContext) catch unreachable; - cases.* = CompareOutputContext{ + const cases = b.allocator.create(CompareOutputContext{ .b = b, .step = b.step("test-compare-output", "Run the compare output tests"), .test_index = 0, .test_filter = test_filter, - }; + }) catch unreachable; compare_output.addCases(cases); @@ -62,13 +61,12 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8) *build } pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(CompareOutputContext) catch unreachable; - cases.* = CompareOutputContext{ + const cases = b.allocator.create(CompareOutputContext{ .b = b, .step = b.step("test-runtime-safety", "Run the runtime safety tests"), .test_index = 0, .test_filter = test_filter, - }; + }) catch unreachable; runtime_safety.addCases(cases); @@ -76,13 +74,12 @@ pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8) *build } pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(CompileErrorContext) catch unreachable; - cases.* = CompileErrorContext{ + const cases = b.allocator.create(CompileErrorContext{ .b = b, .step = b.step("test-compile-errors", "Run the compile error tests"), .test_index = 0, .test_filter = test_filter, - }; + }) catch unreachable; compile_errors.addCases(cases); @@ -90,13 +87,12 @@ pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8) *build. } pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(BuildExamplesContext) catch unreachable; - cases.* = BuildExamplesContext{ + const cases = b.allocator.create(BuildExamplesContext{ .b = b, .step = b.step("test-build-examples", "Build the examples"), .test_index = 0, .test_filter = test_filter, - }; + }) catch unreachable; build_examples.addCases(cases); @@ -104,13 +100,12 @@ pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build. } pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(CompareOutputContext) catch unreachable; - cases.* = CompareOutputContext{ + const cases = b.allocator.create(CompareOutputContext{ .b = b, .step = b.step("test-asm-link", "Run the assemble and link tests"), .test_index = 0, .test_filter = test_filter, - }; + }) catch unreachable; assemble_and_link.addCases(cases); @@ -118,13 +113,12 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8) *bui } pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(TranslateCContext) catch unreachable; - cases.* = TranslateCContext{ + const cases = b.allocator.create(TranslateCContext{ .b = b, .step = b.step("test-translate-c", "Run the C transation tests"), .test_index = 0, .test_filter = test_filter, - }; + }) catch unreachable; translate_c.addCases(cases); @@ -132,13 +126,12 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St } pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(GenHContext) catch unreachable; - cases.* = GenHContext{ + const cases = b.allocator.create(GenHContext{ .b = b, .step = b.step("test-gen-h", "Run the C header file generation tests"), .test_index = 0, .test_filter = test_filter, - }; + }) catch unreachable; gen_h.addCases(cases); @@ -240,8 +233,7 @@ pub const CompareOutputContext = struct { pub fn create(context: *CompareOutputContext, exe_path: []const u8, name: []const u8, expected_output: []const u8, cli_args: []const []const u8) *RunCompareOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(RunCompareOutputStep) catch unreachable; - ptr.* = RunCompareOutputStep{ + const ptr = allocator.create(RunCompareOutputStep{ .context = context, .exe_path = exe_path, .name = name, @@ -249,7 +241,7 @@ pub const CompareOutputContext = struct { .test_index = context.test_index, .step = build.Step.init("RunCompareOutput", allocator, make), .cli_args = cli_args, - }; + }) catch unreachable; context.test_index += 1; return ptr; } @@ -328,14 +320,14 @@ pub const CompareOutputContext = struct { pub fn create(context: *CompareOutputContext, exe_path: []const u8, name: []const u8) *RuntimeSafetyRunStep { const allocator = context.b.allocator; - const ptr = allocator.create(RuntimeSafetyRunStep) catch unreachable; - ptr.* = RuntimeSafetyRunStep{ + const ptr = allocator.create(RuntimeSafetyRunStep{ .context = context, .exe_path = exe_path, .name = name, .test_index = context.test_index, .step = build.Step.init("RuntimeSafetyRun", allocator, make), - }; + }) catch unreachable; + context.test_index += 1; return ptr; } @@ -543,15 +535,15 @@ pub const CompileErrorContext = struct { pub fn create(context: *CompileErrorContext, name: []const u8, case: *const TestCase, build_mode: Mode) *CompileCmpOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(CompileCmpOutputStep) catch unreachable; - ptr.* = CompileCmpOutputStep{ + const ptr = allocator.create(CompileCmpOutputStep{ .step = build.Step.init("CompileCmpOutput", allocator, make), .context = context, .name = name, .test_index = context.test_index, .case = case, .build_mode = build_mode, - }; + }) catch unreachable; + context.test_index += 1; return ptr; } @@ -662,14 +654,14 @@ pub const CompileErrorContext = struct { } pub fn create(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { - const tc = self.b.allocator.create(TestCase) catch unreachable; - tc.* = TestCase{ + const tc = self.b.allocator.create(TestCase{ .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), .expected_errors = ArrayList([]const u8).init(self.b.allocator), .link_libc = false, .is_exe = false, - }; + }) catch unreachable; + tc.addSourceFile(".tmp_source.zig", source); comptime var arg_i = 0; inline while (arg_i < expected_lines.len) : (arg_i += 1) { @@ -829,14 +821,14 @@ pub const TranslateCContext = struct { pub fn create(context: *TranslateCContext, name: []const u8, case: *const TestCase) *TranslateCCmpOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(TranslateCCmpOutputStep) catch unreachable; - ptr.* = TranslateCCmpOutputStep{ + const ptr = allocator.create(TranslateCCmpOutputStep{ .step = build.Step.init("ParseCCmpOutput", allocator, make), .context = context, .name = name, .test_index = context.test_index, .case = case, - }; + }) catch unreachable; + context.test_index += 1; return ptr; } @@ -936,13 +928,13 @@ pub const TranslateCContext = struct { } pub fn create(self: *TranslateCContext, allow_warnings: bool, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { - const tc = self.b.allocator.create(TestCase) catch unreachable; - tc.* = TestCase{ + const tc = self.b.allocator.create(TestCase{ .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), .expected_lines = ArrayList([]const u8).init(self.b.allocator), .allow_warnings = allow_warnings, - }; + }) catch unreachable; + tc.addSourceFile(filename, source); comptime var arg_i = 0; inline while (arg_i < expected_lines.len) : (arg_i += 1) { @@ -1023,15 +1015,15 @@ pub const GenHContext = struct { pub fn create(context: *GenHContext, h_path: []const u8, name: []const u8, case: *const TestCase) *GenHCmpOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; - ptr.* = GenHCmpOutputStep{ + const ptr = allocator.create(GenHCmpOutputStep{ .step = build.Step.init("ParseCCmpOutput", allocator, make), .context = context, .h_path = h_path, .name = name, .test_index = context.test_index, .case = case, - }; + }) catch unreachable; + context.test_index += 1; return ptr; } @@ -1070,12 +1062,12 @@ pub const GenHContext = struct { } pub fn create(self: *GenHContext, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { - const tc = self.b.allocator.create(TestCase) catch unreachable; - tc.* = TestCase{ + const tc = self.b.allocator.create(TestCase{ .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), .expected_lines = ArrayList([]const u8).init(self.b.allocator), - }; + }) catch unreachable; + tc.addSourceFile(filename, source); comptime var arg_i = 0; inline while (arg_i < expected_lines.len) : (arg_i += 1) { -- cgit v1.2.3 From 4b46af481086ad253d43d149d7ee54f17e6570dc Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 21 Jun 2018 01:39:48 +0900 Subject: std.mem.Allocator.construct: remove deprecation warning; --- std/mem.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/std/mem.zig b/std/mem.zig index b3c3e59cec..e99bf9e146 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -44,7 +44,6 @@ pub const Allocator = struct { /// Alias of create /// Call destroy with the result pub fn construct(self: *Allocator, init: var) Error!*@typeOf(init) { - debug.warn("std.mem.Allocator.construct is deprecated;\n"); return self.create(init); } -- cgit v1.2.3 From 6bd861006377ba192d5bd108a0de1e94f32c2454 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 21 Jun 2018 01:40:25 +0900 Subject: std.mem.Allocator.construct: improve formatting; --- std/mem.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/std/mem.zig b/std/mem.zig index e99bf9e146..18fc386b47 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -31,7 +31,7 @@ pub const Allocator = struct { /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` freeFn: fn (self: *Allocator, old_mem: []u8) void, - /// Call destroy with the result + /// Call `destroy` with the result pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) { const T = @typeOf(init); if (@sizeOf(T) == 0) return &{}; @@ -41,8 +41,8 @@ pub const Allocator = struct { return ptr; } - /// Alias of create - /// Call destroy with the result + /// Alias of `create` + /// Call `destroy` with the result pub fn construct(self: *Allocator, init: var) Error!*@typeOf(init) { return self.create(init); } -- cgit v1.2.3 From e891f9cd9dc52b4bc63ae63115822fbf5e724348 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 20 Jun 2018 17:16:27 -0400 Subject: zig fmt --- std/atomic/queue.zig | 2 +- std/atomic/stack.zig | 2 +- std/debug/index.zig | 6 ++---- std/linked_list.zig | 6 +++--- std/mem.zig | 2 +- std/os/index.zig | 4 ++-- test/cases/null.zig | 2 +- 7 files changed, 11 insertions(+), 13 deletions(-) diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 5b810f95ac..308cd6c736 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -116,7 +116,7 @@ fn startPuts(ctx: *Context) u8 { const x = @bitCast(i32, r.random.scalar(u32)); const node = ctx.allocator.create(Queue(i32).Node{ .next = null, - .data = x + .data = x, }) catch unreachable; ctx.queue.put(node); _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 2272be4a92..011ab3254f 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -119,7 +119,7 @@ fn startPuts(ctx: *Context) u8 { const x = @bitCast(i32, r.random.scalar(u32)); const node = ctx.allocator.create(Stack(i32).Node{ .next = null, - .data = x + .data = x, }) catch unreachable; ctx.stack.push(node); _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); diff --git a/std/debug/index.zig b/std/debug/index.zig index 19cee3c65d..57b2dfc300 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -279,9 +279,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace { var exe_file = try os.openSelfExe(); defer exe_file.close(); - const st = try allocator.create(ElfStackTrace{ - .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)) - }); + const st = try allocator.create(ElfStackTrace{ .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)) }); errdefer allocator.destroy(st); return st; }, @@ -972,7 +970,7 @@ fn scanAllCompileUnits(st: *ElfStackTrace) !void { try st.self_exe_file.seekTo(compile_unit_pos); - const compile_unit_die = try st.allocator().create( try parseDie(st, abbrev_table, is_64) ); + const compile_unit_die = try st.allocator().create(try parseDie(st, abbrev_table, is_64)); if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; diff --git a/std/linked_list.zig b/std/linked_list.zig index f4f7aab752..128ccdf4b1 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -194,9 +194,9 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node { comptime assert(!isIntrusive()); return allocator.create(Node{ - .prev = null, - .next = null, - .data = undefined + .prev = null, + .next = null, + .data = undefined, }); } diff --git a/std/mem.zig b/std/mem.zig index 18fc386b47..29f5f924c8 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -44,7 +44,7 @@ pub const Allocator = struct { /// Alias of `create` /// Call `destroy` with the result pub fn construct(self: *Allocator, init: var) Error!*@typeOf(init) { - return self.create(init); + return self.create(init); } /// `ptr` should be the return value of `construct` or `create` diff --git a/std/os/index.zig b/std/os/index.zig index 7b69bd0b36..ce29106810 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2583,8 +2583,8 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext{ - .thread = undefined, - .inner = context + .thread = undefined, + .inner = context, }) catch unreachable; outer_context.thread.data.heap_handle = heap_handle; diff --git a/test/cases/null.zig b/test/cases/null.zig index d2a9aaed55..c86dd34b06 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -146,7 +146,7 @@ test "null with default unwrap" { test "optional types" { comptime { - const opt_type_struct = StructWithOptionalType { .t=u8, }; + const opt_type_struct = StructWithOptionalType{ .t = u8 }; assert(opt_type_struct.t != null and opt_type_struct.t.? == u8); } } -- cgit v1.2.3 From 85f928f8bff8c033f6ef0104d68b033669cb36e4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 20 Jun 2018 17:33:29 -0400 Subject: remove std.mem.Allocator.construct and other fixups --- src-self-hosted/errmsg.zig | 2 +- src-self-hosted/main.zig | 2 +- src-self-hosted/module.zig | 13 +++- std/atomic/queue.zig | 2 +- std/atomic/stack.zig | 2 +- std/heap.zig | 3 +- std/linked_list.zig | 6 +- std/mem.zig | 8 +-- std/os/index.zig | 13 ++-- std/zig/parse.zig | 150 ++++++++++++++++++++++----------------------- 10 files changed, 101 insertions(+), 100 deletions(-) diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index 32d2450aac..b6fd78d8f6 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -35,7 +35,7 @@ pub fn createFromParseError( var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; try parse_error.render(&tree.tokens, out_stream); - const msg = try allocator.construct(Msg{ + const msg = try allocator.create(Msg{ .tree = tree, .path = path, .text = text_buf.toOwnedSlice(), diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index f7f38130b5..45e6bb742a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -707,7 +707,7 @@ const Fmt = struct { // file_path must outlive Fmt fn addToQueue(self: *Fmt, file_path: []const u8) !void { - const new_node = try self.seen.allocator.construct(std.LinkedList([]const u8).Node{ + const new_node = try self.seen.allocator.create(std.LinkedList([]const u8).Node{ .prev = undefined, .next = undefined, .data = file_path, diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 997ef5eed2..5f02f1a832 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -114,7 +114,7 @@ pub const Module = struct { .name = name, .path = path, .children = ArrayList(*CliPkg).init(allocator), - .parent = parent + .parent = parent, }); return pkg; } @@ -127,7 +127,16 @@ pub const Module = struct { } }; - pub fn create(allocator: *mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: *const Target, kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !*Module { + pub fn create( + allocator: *mem.Allocator, + name: []const u8, + root_src_path: ?[]const u8, + target: *const Target, + kind: Kind, + build_mode: builtin.Mode, + zig_lib_dir: []const u8, + cache_dir: []const u8, + ) !*Module { var name_buffer = try Buffer.init(allocator, name); errdefer name_buffer.deinit(); diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 308cd6c736..16dc9f6cc3 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -115,7 +115,7 @@ fn startPuts(ctx: *Context) u8 { std.os.time.sleep(0, 1); // let the os scheduler be our fuzz const x = @bitCast(i32, r.random.scalar(u32)); const node = ctx.allocator.create(Queue(i32).Node{ - .next = null, + .next = undefined, .data = x, }) catch unreachable; ctx.queue.put(node); diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 011ab3254f..d74bee8e8b 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -118,7 +118,7 @@ fn startPuts(ctx: *Context) u8 { std.os.time.sleep(0, 1); // let the os scheduler be our fuzz const x = @bitCast(i32, r.random.scalar(u32)); const node = ctx.allocator.create(Stack(i32).Node{ - .next = null, + .next = undefined, .data = x, }) catch unreachable; ctx.stack.push(node); diff --git a/std/heap.zig b/std/heap.zig index 7fc00cd0a4..41d7802fdd 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -407,8 +407,7 @@ fn testAllocator(allocator: *mem.Allocator) !void { var slice = try allocator.alloc(*i32, 100); for (slice) |*item, i| { - item.* = try allocator.create(i32(0)); - item.*.* = @intCast(i32, i); + item.* = try allocator.create(@intCast(i32, i)); } for (slice) |item, i| { diff --git a/std/linked_list.zig b/std/linked_list.zig index 128ccdf4b1..62cd5ca2bb 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -193,11 +193,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// A pointer to the new node. pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node { comptime assert(!isIntrusive()); - return allocator.create(Node{ - .prev = null, - .next = null, - .data = undefined, - }); + return allocator.create(Node(undefined)); } /// Deallocate a node. diff --git a/std/mem.zig b/std/mem.zig index 29f5f924c8..ba59faf711 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -41,13 +41,7 @@ pub const Allocator = struct { return ptr; } - /// Alias of `create` - /// Call `destroy` with the result - pub fn construct(self: *Allocator, init: var) Error!*@typeOf(init) { - return self.create(init); - } - - /// `ptr` should be the return value of `construct` or `create` + /// `ptr` should be the return value of `create` pub fn destroy(self: *Allocator, ptr: var) void { const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr)); self.freeFn(self, non_const_ptr[0..@sizeOf(@typeOf(ptr).Child)]); diff --git a/std/os/index.zig b/std/os/index.zig index ce29106810..52b36c351c 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2468,7 +2468,7 @@ pub const Thread = struct { data: Data, pub const use_pthreads = is_posix and builtin.link_libc; - const Data = if (use_pthreads) + pub const Data = if (use_pthreads) struct { handle: c.pthread_t, stack_addr: usize, @@ -2583,13 +2583,16 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext{ - .thread = undefined, + .thread = Thread{ + .data = Thread.Data{ + .heap_handle = heap_handle, + .alloc_start = bytes_ptr, + .handle = undefined, + }, + }, .inner = context, }) catch unreachable; - outer_context.thread.data.heap_handle = heap_handle; - outer_context.thread.data.alloc_start = bytes_ptr; - const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse { const err = windows.GetLastError(); diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 877b81c527..9f0371d4da 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -17,7 +17,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { defer stack.deinit(); const arena = &tree_arena.allocator; - const root_node = try arena.construct(ast.Node.Root{ + const root_node = try arena.create(ast.Node.Root{ .base = ast.Node{ .id = ast.Node.Id.Root }, .decls = ast.Node.Root.DeclList.init(arena), .doc_comments = null, @@ -65,14 +65,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Keyword_test => { stack.append(State.TopLevel) catch unreachable; - const block = try arena.construct(ast.Node.Block{ + const block = try arena.create(ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = undefined, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, }); - const test_node = try arena.construct(ast.Node.TestDecl{ + const test_node = try arena.create(ast.Node.TestDecl{ .base = ast.Node{ .id = ast.Node.Id.TestDecl }, .doc_comments = comments, .test_token = token_index, @@ -109,14 +109,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_comptime => { - const block = try arena.construct(ast.Node.Block{ + const block = try arena.create(ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = undefined, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, }); - const node = try arena.construct(ast.Node.Comptime{ + const node = try arena.create(ast.Node.Comptime{ .base = ast.Node{ .id = ast.Node.Id.Comptime }, .comptime_token = token_index, .expr = &block.base, @@ -225,7 +225,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { return tree; } - const node = try arena.construct(ast.Node.Use{ + const node = try arena.create(ast.Node.Use{ .base = ast.Node{ .id = ast.Node.Id.Use }, .use_token = token_index, .visib_token = ctx.visib_token, @@ -266,7 +266,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { - const fn_proto = try arena.construct(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, @@ -298,7 +298,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_async => { - const async_node = try arena.construct(ast.Node.AsyncAttribute{ + const async_node = try arena.create(ast.Node.AsyncAttribute{ .base = ast.Node{ .id = ast.Node.Id.AsyncAttribute }, .async_token = token_index, .allocator_type = null, @@ -330,7 +330,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.TopLevelExternOrField => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |identifier| { - const node = try arena.construct(ast.Node.StructField{ + const node = try arena.create(ast.Node.StructField{ .base = ast.Node{ .id = ast.Node.Id.StructField }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, @@ -375,7 +375,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; - const node = try arena.construct(ast.Node.ContainerDecl{ + const node = try arena.create(ast.Node.ContainerDecl{ .base = ast.Node{ .id = ast.Node.Id.ContainerDecl }, .layout_token = ctx.layout_token, .kind_token = switch (token_ptr.id) { @@ -448,7 +448,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Identifier => { switch (tree.tokens.at(container_decl.kind_token).id) { Token.Id.Keyword_struct => { - const node = try arena.construct(ast.Node.StructField{ + const node = try arena.create(ast.Node.StructField{ .base = ast.Node{ .id = ast.Node.Id.StructField }, .doc_comments = comments, .visib_token = null, @@ -464,7 +464,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_union => { - const node = try arena.construct(ast.Node.UnionTag{ + const node = try arena.create(ast.Node.UnionTag{ .base = ast.Node{ .id = ast.Node.Id.UnionTag }, .name_token = token_index, .type_expr = null, @@ -480,7 +480,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_enum => { - const node = try arena.construct(ast.Node.EnumTag{ + const node = try arena.create(ast.Node.EnumTag{ .base = ast.Node{ .id = ast.Node.Id.EnumTag }, .name_token = token_index, .value = null, @@ -562,7 +562,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.VarDecl => |ctx| { - const var_decl = try arena.construct(ast.Node.VarDecl{ + const var_decl = try arena.create(ast.Node.VarDecl{ .base = ast.Node{ .id = ast.Node.Id.VarDecl }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, @@ -660,7 +660,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LBrace => { - const block = try arena.construct(ast.Node.Block{ + const block = try arena.create(ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, @@ -712,7 +712,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { // TODO: this is a special case. Remove this when #760 is fixed if (token_ptr.id == Token.Id.Keyword_error) { if (tok_it.peek().?.id == Token.Id.LBrace) { - const error_type_node = try arena.construct(ast.Node.ErrorType{ + const error_type_node = try arena.create(ast.Node.ErrorType{ .base = ast.Node{ .id = ast.Node.Id.ErrorType }, .token = token_index, }); @@ -733,7 +733,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { if (eatToken(&tok_it, &tree, Token.Id.RParen)) |_| { continue; } - const param_decl = try arena.construct(ast.Node.ParamDecl{ + const param_decl = try arena.create(ast.Node.ParamDecl{ .base = ast.Node{ .id = ast.Node.Id.ParamDecl }, .comptime_token = null, .noalias_token = null, @@ -819,7 +819,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LBrace => { - const block = try arena.construct(ast.Node.Block{ + const block = try arena.create(ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = ctx.label, .lbrace = token_index, @@ -853,7 +853,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_suspend => { - const node = try arena.construct(ast.Node.Suspend{ + const node = try arena.create(ast.Node.Suspend{ .base = ast.Node{ .id = ast.Node.Id.Suspend }, .label = ctx.label, .suspend_token = token_index, @@ -925,7 +925,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { } }, State.While => |ctx| { - const node = try arena.construct(ast.Node.While{ + const node = try arena.create(ast.Node.While{ .base = ast.Node{ .id = ast.Node.Id.While }, .label = ctx.label, .inline_token = ctx.inline_token, @@ -954,7 +954,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.For => |ctx| { - const node = try arena.construct(ast.Node.For{ + const node = try arena.create(ast.Node.For{ .base = ast.Node{ .id = ast.Node.Id.For }, .label = ctx.label, .inline_token = ctx.inline_token, @@ -975,7 +975,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.Else => |dest| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_else)) |else_token| { - const node = try arena.construct(ast.Node.Else{ + const node = try arena.create(ast.Node.Else{ .base = ast.Node{ .id = ast.Node.Id.Else }, .else_token = else_token, .payload = null, @@ -1038,7 +1038,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { - const node = try arena.construct(ast.Node.Defer{ + const node = try arena.create(ast.Node.Defer{ .base = ast.Node{ .id = ast.Node.Id.Defer }, .defer_token = token_index, .kind = switch (token_ptr.id) { @@ -1056,7 +1056,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBrace => { - const inner_block = try arena.construct(ast.Node.Block{ + const inner_block = try arena.create(ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, @@ -1124,7 +1124,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.AsmOutput{ + const node = try arena.create(ast.Node.AsmOutput{ .base = ast.Node{ .id = ast.Node.Id.AsmOutput }, .lbracket = lbracket_index, .symbolic_name = undefined, @@ -1178,7 +1178,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.AsmInput{ + const node = try arena.create(ast.Node.AsmInput{ .base = ast.Node{ .id = ast.Node.Id.AsmInput }, .lbracket = lbracket_index, .symbolic_name = undefined, @@ -1243,7 +1243,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.FieldInitializer{ + const node = try arena.create(ast.Node.FieldInitializer{ .base = ast.Node{ .id = ast.Node.Id.FieldInitializer }, .period_token = undefined, .name_token = undefined, @@ -1332,7 +1332,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { } const comments = try eatDocComments(arena, &tok_it, &tree); - const node = try arena.construct(ast.Node.SwitchCase{ + const node = try arena.create(ast.Node.SwitchCase{ .base = ast.Node{ .id = ast.Node.Id.SwitchCase }, .items = ast.Node.SwitchCase.ItemList.init(arena), .payload = null, @@ -1369,7 +1369,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (token_ptr.id == Token.Id.Keyword_else) { - const else_node = try arena.construct(ast.Node.SwitchElse{ + const else_node = try arena.create(ast.Node.SwitchElse{ .base = ast.Node{ .id = ast.Node.Id.SwitchElse }, .token = token_index, }); @@ -1468,7 +1468,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.ExternType => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_fn)) |fn_token| { - const fn_proto = try arena.construct(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = ctx.comments, .visib_token = null, @@ -1641,7 +1641,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.Payload{ + const node = try arena.create(ast.Node.Payload{ .base = ast.Node{ .id = ast.Node.Id.Payload }, .lpipe = token_index, .error_symbol = undefined, @@ -1677,7 +1677,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.PointerPayload{ + const node = try arena.create(ast.Node.PointerPayload{ .base = ast.Node{ .id = ast.Node.Id.PointerPayload }, .lpipe = token_index, .ptr_token = null, @@ -1720,7 +1720,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.PointerIndexPayload{ + const node = try arena.create(ast.Node.PointerIndexPayload{ .base = ast.Node{ .id = ast.Node.Id.PointerIndexPayload }, .lpipe = token_index, .ptr_token = null, @@ -1754,7 +1754,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { - const node = try arena.construct(ast.Node.ControlFlowExpression{ + const node = try arena.create(ast.Node.ControlFlowExpression{ .base = ast.Node{ .id = ast.Node.Id.ControlFlowExpression }, .ltoken = token_index, .kind = undefined, @@ -1783,7 +1783,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { - const node = try arena.construct(ast.Node.PrefixOp{ + const node = try arena.create(ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = switch (token_ptr.id) { @@ -1817,7 +1817,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = ellipsis3, @@ -1842,7 +1842,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToAssignment(token_ptr.id)) |ass_id| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, @@ -1872,7 +1872,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, @@ -1904,7 +1904,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = or_token, @@ -1928,7 +1928,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = and_token, @@ -1955,7 +1955,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToComparison(token_ptr.id)) |comp_id| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, @@ -1982,7 +1982,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = pipe, @@ -2006,7 +2006,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = caret, @@ -2030,7 +2030,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = ampersand, @@ -2057,7 +2057,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, @@ -2087,7 +2087,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToAddition(token_ptr.id)) |add_id| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, @@ -2117,7 +2117,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToMultiply(token_ptr.id)) |mult_id| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, @@ -2145,7 +2145,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (tok_it.peek().?.id == Token.Id.Period) { - const node = try arena.construct(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena) }, @@ -2164,7 +2164,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena) }, @@ -2193,7 +2193,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| { - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = bang, @@ -2212,7 +2212,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| { - var node = try arena.construct(ast.Node.PrefixOp{ + var node = try arena.create(ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = prefix_id, @@ -2222,7 +2222,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { // Treat '**' token as two pointer types if (token_ptr.id == Token.Id.AsteriskAsterisk) { - const child = try arena.construct(ast.Node.PrefixOp{ + const child = try arena.create(ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = prefix_id, @@ -2246,7 +2246,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.SuffixOpExpressionBegin => |opt_ctx| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_async)) |async_token| { - const async_node = try arena.construct(ast.Node.AsyncAttribute{ + const async_node = try arena.create(ast.Node.AsyncAttribute{ .base = ast.Node{ .id = ast.Node.Id.AsyncAttribute }, .async_token = async_token, .allocator_type = null, @@ -2277,7 +2277,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LParen => { - const node = try arena.construct(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ @@ -2301,7 +2301,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBracket => { - const node = try arena.construct(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ .ArrayAccess = undefined }, @@ -2316,7 +2316,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, Token.Id.Period => { if (eatToken(&tok_it, &tree, Token.Id.Asterisk)) |asterisk_token| { - const node = try arena.construct(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op.Deref, @@ -2327,7 +2327,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } if (eatToken(&tok_it, &tree, Token.Id.QuestionMark)) |question_token| { - const node = try arena.construct(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op.UnwrapOptional, @@ -2337,7 +2337,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; continue; } - const node = try arena.construct(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, @@ -2397,7 +2397,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_promise => { - const node = try arena.construct(ast.Node.PromiseType{ + const node = try arena.create(ast.Node.PromiseType{ .base = ast.Node{ .id = ast.Node.Id.PromiseType }, .promise_token = token.index, .result = null, @@ -2423,7 +2423,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LParen => { - const node = try arena.construct(ast.Node.GroupedExpression{ + const node = try arena.create(ast.Node.GroupedExpression{ .base = ast.Node{ .id = ast.Node.Id.GroupedExpression }, .lparen = token.index, .expr = undefined, @@ -2441,7 +2441,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Builtin => { - const node = try arena.construct(ast.Node.BuiltinCall{ + const node = try arena.create(ast.Node.BuiltinCall{ .base = ast.Node{ .id = ast.Node.Id.BuiltinCall }, .builtin_token = token.index, .params = ast.Node.BuiltinCall.ParamList.init(arena), @@ -2460,7 +2460,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBracket => { - const node = try arena.construct(ast.Node.PrefixOp{ + const node = try arena.create(ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token.index, .op = undefined, @@ -2519,7 +2519,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_fn => { - const fn_proto = try arena.construct(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = null, .visib_token = null, @@ -2540,7 +2540,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_proto = try arena.construct(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = null, .visib_token = null, @@ -2567,7 +2567,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_asm => { - const node = try arena.construct(ast.Node.Asm{ + const node = try arena.create(ast.Node.Asm{ .base = ast.Node{ .id = ast.Node.Id.Asm }, .asm_token = token.index, .volatile_token = null, @@ -2629,7 +2629,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.construct(ast.Node.ErrorSetDecl{ + const node = try arena.create(ast.Node.ErrorSetDecl{ .base = ast.Node{ .id = ast.Node.Id.ErrorSetDecl }, .error_token = ctx.error_token, .decls = ast.Node.ErrorSetDecl.DeclList.init(arena), @@ -2695,7 +2695,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { return tree; } - const node = try arena.construct(ast.Node.ErrorTag{ + const node = try arena.create(ast.Node.ErrorTag{ .base = ast.Node{ .id = ast.Node.Id.ErrorTag }, .doc_comments = comments, .name_token = ident_token_index, @@ -3032,7 +3032,7 @@ fn pushDocComment(arena: *mem.Allocator, line_comment: TokenIndex, result: *?*as if (result.*) |comment_node| { break :blk comment_node; } else { - const comment_node = try arena.construct(ast.Node.DocComment{ + const comment_node = try arena.create(ast.Node.DocComment{ .base = ast.Node{ .id = ast.Node.Id.DocComment }, .lines = ast.Node.DocComment.LineList.init(arena), }); @@ -3061,7 +3061,7 @@ fn parseStringLiteral(arena: *mem.Allocator, tok_it: *ast.Tree.TokenList.Iterato return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base; }, Token.Id.MultilineStringLiteralLine => { - const node = try arena.construct(ast.Node.MultilineStringLiteral{ + const node = try arena.create(ast.Node.MultilineStringLiteral{ .base = ast.Node{ .id = ast.Node.Id.MultilineStringLiteral }, .lines = ast.Node.MultilineStringLiteral.LineList.init(arena), }); @@ -3089,7 +3089,7 @@ fn parseStringLiteral(arena: *mem.Allocator, tok_it: *ast.Tree.TokenList.Iterato fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *const OptionalCtx, token_ptr: *const Token, token_index: TokenIndex) !bool { switch (token_ptr.id) { Token.Id.Keyword_suspend => { - const node = try arena.construct(ast.Node.Suspend{ + const node = try arena.create(ast.Node.Suspend{ .base = ast.Node{ .id = ast.Node.Id.Suspend }, .label = null, .suspend_token = token_index, @@ -3103,7 +3103,7 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *con return true; }, Token.Id.Keyword_if => { - const node = try arena.construct(ast.Node.If{ + const node = try arena.create(ast.Node.If{ .base = ast.Node{ .id = ast.Node.Id.If }, .if_token = token_index, .condition = undefined, @@ -3144,7 +3144,7 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *con return true; }, Token.Id.Keyword_switch => { - const node = try arena.construct(ast.Node.Switch{ + const node = try arena.create(ast.Node.Switch{ .base = ast.Node{ .id = ast.Node.Id.Switch }, .switch_token = token_index, .expr = undefined, @@ -3166,7 +3166,7 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *con return true; }, Token.Id.Keyword_comptime => { - const node = try arena.construct(ast.Node.Comptime{ + const node = try arena.create(ast.Node.Comptime{ .base = ast.Node{ .id = ast.Node.Id.Comptime }, .comptime_token = token_index, .expr = undefined, @@ -3178,7 +3178,7 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *con return true; }, Token.Id.LBrace => { - const block = try arena.construct(ast.Node.Block{ + const block = try arena.create(ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, @@ -3318,7 +3318,7 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { } fn createLiteral(arena: *mem.Allocator, comptime T: type, token_index: TokenIndex) !*T { - return arena.construct(T{ + return arena.create(T{ .base = ast.Node{ .id = ast.Node.typeToId(T) }, .token = token_index, }); -- cgit v1.2.3 From eb6a8e6a3bba061c4a7fb18f53976e1fb683c3d4 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 20 Jun 2018 21:51:18 +0200 Subject: fix f128 remainder division bug The modulo operation computed rem(b+rem(a,b), b) which produces -1 for a=1 and b=2. Switch to a - b * trunc(a/b) which produces the expected result, 1. closes #1137 --- src/ir.cpp | 12 +++++++++--- test/cases/math.zig | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index ba2a9b3fe2..950d051492 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7710,6 +7710,14 @@ static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal } } +// c = a - b * trunc(a / b) +static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* c) { + f128M_div(a, b, c); + f128M_roundToInt(c, softfloat_round_min, true, c); + f128M_mul(b, c, c); + f128M_sub(a, c, c); +} + static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; @@ -7724,9 +7732,7 @@ static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal out_val->data.x_f64 = fmod(fmod(op1->data.x_f64, op2->data.x_f64) + op2->data.x_f64, op2->data.x_f64); return; case 128: - f128M_rem(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); - f128M_add(&out_val->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); - f128M_rem(&out_val->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); + zig_f128M_mod(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); diff --git a/test/cases/math.zig b/test/cases/math.zig index 0bf99cff0e..08388d3df8 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -434,6 +434,20 @@ test "comptime float rem int" { } } +test "remainder division" { + comptime remdiv(f32); + comptime remdiv(f64); + comptime remdiv(f128); + remdiv(f32); + remdiv(f64); + remdiv(f128); +} + +fn remdiv(comptime T: type) void { + assert(T(1) == T(1) % T(2)); + assert(T(1) == T(7) % T(3)); +} + test "@sqrt" { testSqrt(f64, 12.0); comptime testSqrt(f64, 12.0); -- cgit v1.2.3 From f50c0c664f3bd7a56c0274ffecae28e2b41c8d20 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 21 Jun 2018 17:13:53 +1200 Subject: Add float repr bit extraction functions --- std/math/index.zig | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/std/math/index.zig b/std/math/index.zig index 176293be74..04133dc1dc 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -183,6 +183,32 @@ test "math" { _ = @import("big/index.zig"); } +pub fn floatMantissaBits(comptime T: type) comptime_int { + assert(@typeId(T) == builtin.TypeId.Float); + + return switch (T.bit_count) { + 16 => 10, + 32 => 23, + 64 => 52, + 80 => 64, + 128 => 112, + else => @compileError("unknown floating point type " ++ @typeName(T)), + }; +} + +pub fn floatExponentBits(comptime T: type) comptime_int { + assert(@typeId(T) == builtin.TypeId.Float); + + return switch (T.bit_count) { + 16 => 5, + 32 => 8, + 64 => 11, + 80 => 15, + 128 => 15, + else => @compileError("unknown floating point type " ++ @typeName(T)), + }; +} + pub fn min(x: var, y: var) @typeOf(x + y) { return if (x < y) x else y; } @@ -607,4 +633,3 @@ pub fn lossyCast(comptime T: type, value: var) T { else => @compileError("bad type"), } } - -- cgit v1.2.3 From 0ab4afbf4236e6823be6b8845b7e9fa77f7c61f9 Mon Sep 17 00:00:00 2001 From: Isaac Hier Date: Thu, 21 Jun 2018 08:14:26 -0400 Subject: Fix increment operation for bigint -1 --- src/bigint.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/bigint.cpp b/src/bigint.cpp index 367ae79b6c..088703402d 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1683,10 +1683,20 @@ void bigint_incr(BigInt *x) { bigint_init_unsigned(x, 1); return; } - - if (x->digit_count == 1 && x->data.digit != UINT64_MAX) { - x->data.digit += 1; - return; + + if (x->digit_count == 1) { + if (x->is_negative) { + if (x->data.digit != 0) { + x->data.digit -= 1; + } + return; + } + else { + if (x->data.digit != UINT64_MAX) { + x->data.digit += 1; + } + return; + } } BigInt copy; -- cgit v1.2.3 From eeda1a1396eb2732e8fe9234bd13d21ac334cfac Mon Sep 17 00:00:00 2001 From: Isaac Hier Date: Thu, 21 Jun 2018 08:17:08 -0400 Subject: Fix logic --- src/bigint.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/bigint.cpp b/src/bigint.cpp index 088703402d..bb227a7c3d 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1685,16 +1685,11 @@ void bigint_incr(BigInt *x) { } if (x->digit_count == 1) { - if (x->is_negative) { - if (x->data.digit != 0) { - x->data.digit -= 1; - } + if (x->is_negative && x->data.digit != 0) { + x->data.digit -= 1; return; - } - else { - if (x->data.digit != UINT64_MAX) { - x->data.digit += 1; - } + } else if (!x->is_negative && x->data.digit != UINT64_MAX) { + x->data.digit += 1; return; } } -- cgit v1.2.3 From f1207a8e744617d62e5c535cf05764cdf2cfd99e Mon Sep 17 00:00:00 2001 From: Isaac Hier Date: Thu, 21 Jun 2018 08:32:05 -0400 Subject: Add test case --- test/behavior.zig | 1 + test/cases/bugs/1111.zig | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 test/cases/bugs/1111.zig diff --git a/test/behavior.zig b/test/behavior.zig index 096c07b2e0..b494c623e2 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -13,6 +13,7 @@ comptime { _ = @import("cases/bugs/656.zig"); _ = @import("cases/bugs/828.zig"); _ = @import("cases/bugs/920.zig"); + _ = @import("cases/bugs/1111.zig"); _ = @import("cases/byval_arg_var.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); diff --git a/test/cases/bugs/1111.zig b/test/cases/bugs/1111.zig new file mode 100644 index 0000000000..51ce90af52 --- /dev/null +++ b/test/cases/bugs/1111.zig @@ -0,0 +1,12 @@ +const Foo = extern enum { + Bar = -1, +}; + +test "issue 1111 fixed" { + const v = Foo.Bar; + + switch(v) { + Foo.Bar => return, + else => return, + } +} -- cgit v1.2.3 From 5f38d6e2e97829ed74f06a96b5d07a2c68516063 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Jun 2018 14:43:55 -0400 Subject: add casting docs, __extenddftf2, and __extendsftf2 --- CMakeLists.txt | 1 + doc/langref.html.in | 171 ++++++++++++++++++++++++--- src/ir.cpp | 6 +- std/special/compiler_rt/extendXfYf2.zig | 87 ++++++++++++++ std/special/compiler_rt/extendXfYf2_test.zig | 108 +++++++++++++++++ std/special/compiler_rt/index.zig | 2 + test/behavior.zig | 1 + test/cases/widening.zig | 26 ++++ 8 files changed, 384 insertions(+), 18 deletions(-) create mode 100644 std/special/compiler_rt/extendXfYf2.zig create mode 100644 std/special/compiler_rt/extendXfYf2_test.zig create mode 100644 test/cases/widening.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 030398d71c..99de2328d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -558,6 +558,7 @@ set(ZIG_STD_FILES "special/compiler_rt/aullrem.zig" "special/compiler_rt/comparetf2.zig" "special/compiler_rt/divti3.zig" + "special/compiler_rt/extendXfYf2.zig" "special/compiler_rt/fixuint.zig" "special/compiler_rt/fixunsdfdi.zig" "special/compiler_rt/fixunsdfsi.zig" diff --git a/doc/langref.html.in b/doc/langref.html.in index bdc33cb808..b76fc69385 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -3573,14 +3573,161 @@ const optional_value: ?i32 = null; {#header_close#} {#header_close#} {#header_open|Casting#} -

    TODO: explain implicit vs explicit casting

    -

    TODO: resolve peer types builtin

    -

    TODO: truncate builtin

    -

    TODO: bitcast builtin

    -

    TODO: int to ptr builtin

    -

    TODO: ptr to int builtin

    -

    TODO: ptrcast builtin

    -

    TODO: explain number literals vs concrete types

    +

    + A type cast converts a value of one type to another. + Zig has {#link|Implicit Casts#} for conversions that are known to be completely safe and unambiguous, + and {#link|Explicit Casts#} for conversions that one would not want to happen on accident. + There is also a third kind of type conversion called {#link|Peer Type Resolution#} for + the case when a result type must be decided given multiple operand types. +

    + {#header_open|Implicit Casts#} +

    + An implicit cast occurs when one type is expected, but different type is provided: +

    + {#code_begin|test#} +test "implicit cast - variable declaration" { + var a: u8 = 1; + var b: u16 = a; +} + +test "implicit cast - function call" { + var a: u8 = 1; + foo(a); +} + +fn foo(b: u16) void {} + +test "implicit cast - invoke a type as a function" { + var a: u8 = 1; + var b = u16(a); +} + {#code_end#} + {#header_open|Implicit Cast: Stricter Qualification#} +

    + Values which have the same representation at runtime can be cast to increase the strictness + of the qualifiers, no matter how nested the qualifiers are: +

    +
      +
    • const - non-const to const is allowed
    • +
    • volatile - non-volatile to volatile is allowed
    • +
    • align - bigger to smaller alignment is allowed
    • +
    • {#link|error sets|Error Set Type#} to supersets is allowed
    • +
    +

    + These casts are no-ops at runtime since the value representation does not change. +

    + {#code_begin|test#} +test "implicit cast - const qualification" { + var a: i32 = 1; + var b: *i32 = &a; + foo(b); +} + +fn foo(a: *const i32) void {} + {#code_end#} +

    + In addition, pointers implicitly cast to const optional pointers: +

    + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; + +test "cast *[1][*]const u8 to [*]const ?[*]const u8" { + const window_name = [1][*]const u8{c"window name"}; + const x: [*]const ?[*]const u8 = &window_name; + assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); +} + {#code_end#} + {#header_close#} + {#header_open|Implicit Cast: Integer and Float Widening#} +

    + {#link|Integers#} implicitly cast to integer types which can represent every value of the old type, and likewise + {#link|Floats#} implicitly cast to float types which can represent every value of the old type. +

    + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; + +test "integer widening" { + var a: u8 = 250; + var b: u16 = a; + var c: u32 = b; + var d: u64 = c; + var e: u64 = d; + var f: u128 = e; + assert(f == a); +} + +test "implicit unsigned integer to signed integer" { + var a: u8 = 250; + var b: i16 = a; + assert(b == 250); +} + +test "float widening" { + var a: f32 = 12.34; + var b: f64 = a; + var c: f128 = b; + assert(c == a); +} + {#code_end#} + {#header_close#} + {#header_open|Implicit Cast: Arrays#} +

    TODO: [N]T to []const T

    +

    TODO: *const [N]T to []const T

    +

    TODO: [N]T to *const []const T

    +

    TODO: [N]T to ?[]const T

    +

    TODO: *[N]T to []T

    +

    TODO: *[N]T to [*]T

    +

    TODO: *T to *[1]T

    +

    TODO: [N]T to E![]const T

    + {#header_close#} + {#header_open|Implicit Cast: Optionals#} +

    TODO: T to ?T

    +

    TODO: T to E!?T

    +

    TODO: null to ?T

    + {#header_close#} + {#header_open|Implicit Cast: T to E!T#} +

    TODO

    + {#header_close#} + {#header_open|Implicit Cast: E to E!T#} +

    TODO

    + {#header_close#} + {#header_open|Implicit Cast: comptime_int to *const integer#} +

    TODO

    + {#header_close#} + {#header_open|Implicit Cast: comptime_float to *const float#} +

    TODO

    + {#header_close#} + {#header_open|Implicit Cast: compile-time known numbers#} +

    TODO

    + {#header_close#} + {#header_open|Implicit Cast: union to enum#} +

    TODO

    + {#header_close#} + {#header_open|Implicit Cast: enum to union#} +

    TODO

    + {#header_close#} + {#header_open|Implicit Cast: T to *T when @sizeOf(T) == 0#} +

    TODO

    + {#header_close#} + {#header_open|Implicit Cast: undefined#} +

    TODO

    + {#header_close#} + {#header_open|Implicit Cast: T to *const T#} +

    TODO

    + {#header_close#} + {#header_close#} + + {#header_open|Explicit Casts#} +

    TODO

    + {#header_close#} + + {#header_open|Peer Type Resolution#} +

    TODO

    + {#header_close#} {#header_close#} {#header_open|void#} @@ -5522,12 +5669,6 @@ pub const FloatMode = enum {

    {#see_also|Compile Variables#} {#header_close#} - {#header_open|@setGlobalSection#} -
    @setGlobalSection(global_variable_name, comptime section_name: []const u8) bool
    -

    - Puts the global variable in the specified section. -

    - {#header_close#} {#header_open|@shlExact#}
    @shlExact(value: T, shift_amt: Log2T) T

    @@ -6928,7 +7069,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/ir.cpp b/src/ir.cpp index 950d051492..c6078e755d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10092,7 +10092,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from &const [N]T to []const T + // cast from *const [N]T to []const T if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.is_const && @@ -10111,7 +10111,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from [N]T to &const []const T + // cast from [N]T to *const []const T if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.is_const && is_slice(wanted_type->data.pointer.child_type) && @@ -10136,7 +10136,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from [N]T to ?[]const N + // cast from [N]T to ?[]const T if (wanted_type->id == TypeTableEntryIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == TypeTableEntryIdArray) diff --git a/std/special/compiler_rt/extendXfYf2.zig b/std/special/compiler_rt/extendXfYf2.zig new file mode 100644 index 0000000000..6fa8cf4654 --- /dev/null +++ b/std/special/compiler_rt/extendXfYf2.zig @@ -0,0 +1,87 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const is_test = builtin.is_test; + +pub extern fn __extenddftf2(a: f64) f128 { + return extendXfYf2(f128, f64, a); +} + +pub extern fn __extendsftf2(a: f32) f128 { + return extendXfYf2(f128, f32, a); +} + +const CHAR_BIT = 8; + +pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { + const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits); + const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits); + const srcSigBits = std.math.floatMantissaBits(src_t); + const dstSigBits = std.math.floatMantissaBits(dst_t); + const SrcShift = std.math.Log2Int(src_rep_t); + const DstShift = std.math.Log2Int(dst_rep_t); + + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const srcBits: i32 = @sizeOf(src_t) * CHAR_BIT; + const srcExpBits: i32 = srcBits - srcSigBits - 1; + const srcInfExp: i32 = (1 << srcExpBits) - 1; + const srcExpBias: i32 = srcInfExp >> 1; + + const srcMinNormal: src_rep_t = src_rep_t(1) << srcSigBits; + const srcInfinity: src_rep_t = src_rep_t(@bitCast(u32, srcInfExp)) << srcSigBits; + const srcSignMask: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits +% srcExpBits); + const srcAbsMask: src_rep_t = srcSignMask -% 1; + const srcQNaN: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits -% 1); + const srcNaNCode: src_rep_t = srcQNaN -% 1; + + const dstBits: i32 = @sizeOf(dst_t) * CHAR_BIT; + const dstExpBits: i32 = dstBits - dstSigBits - 1; + const dstInfExp: i32 = (1 << dstExpBits) - 1; + const dstExpBias: i32 = dstInfExp >> 1; + + const dstMinNormal: dst_rep_t = dst_rep_t(1) << dstSigBits; + + // Break a into a sign and representation of the absolute value + const aRep: src_rep_t = @bitCast(src_rep_t, a); + const aAbs: src_rep_t = aRep & srcAbsMask; + const sign: src_rep_t = aRep & srcSignMask; + var absResult: dst_rep_t = undefined; + + // If @sizeOf(src_rep_t) < @sizeOf(int), the subtraction result is promoted + // to (signed) int. To avoid that, explicitly cast to src_rep_t. + if ((src_rep_t)(aAbs -% srcMinNormal) < srcInfinity -% srcMinNormal) { + // a is a normal number. + // Extend to the destination type by shifting the significand and + // exponent into the proper position and rebiasing the exponent. + absResult = dst_rep_t(aAbs) << (dstSigBits -% srcSigBits); + absResult += dst_rep_t(@bitCast(u32, dstExpBias -% srcExpBias)) << dstSigBits; + } else if (aAbs >= srcInfinity) { + // a is NaN or infinity. + // Conjure the result by beginning with infinity, then setting the qNaN + // bit (if needed) and right-aligning the rest of the trailing NaN + // payload field. + absResult = dst_rep_t(@bitCast(u32, dstInfExp)) << dstSigBits; + absResult |= (dst_rep_t)(aAbs & srcQNaN) << (dstSigBits - srcSigBits); + absResult |= (dst_rep_t)(aAbs & srcNaNCode) << (dstSigBits - srcSigBits); + } else if (aAbs != 0) { + // a is denormal. + // renormalize the significand and clear the leading bit, then insert + // the correct adjusted exponent in the destination type. + const scale: i32 = @clz(aAbs) - @clz(srcMinNormal); + absResult = dst_rep_t(aAbs) << @intCast(DstShift, dstSigBits - srcSigBits + scale); + absResult ^= dstMinNormal; + const resultExponent: i32 = dstExpBias - srcExpBias - scale + 1; + absResult |= dst_rep_t(@bitCast(u32, resultExponent)) << @intCast(DstShift, dstSigBits); + } else { + // a is zero. + absResult = 0; + } + + // Apply the signbit to (dst_t)abs(a). + const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << @intCast(DstShift, dstBits - srcBits); + return @bitCast(dst_t, result); +} + +test "import extendXfYf2" { + _ = @import("extendXfYf2_test.zig"); +} diff --git a/std/special/compiler_rt/extendXfYf2_test.zig b/std/special/compiler_rt/extendXfYf2_test.zig new file mode 100644 index 0000000000..84fb410fbb --- /dev/null +++ b/std/special/compiler_rt/extendXfYf2_test.zig @@ -0,0 +1,108 @@ +const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; +const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2; +const assert = @import("std").debug.assert; + +fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void { + const x = __extenddftf2(a); + + const rep = @bitCast(u128, x); + const hi = @intCast(u64, rep >> 64); + const lo = @truncate(u64, rep); + + if (hi == expectedHi and lo == expectedLo) + return; + + // test other possible NaN representation(signal NaN) + if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) { + if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and + ((hi & 0xffffffffffff) > 0 or lo > 0)) + { + return; + } + } + + @panic("__extenddftf2 test failure"); +} + +fn test__extendsftf2(a: f32, expectedHi: u64, expectedLo: u64) void { + const x = __extendsftf2(a); + + const rep = @bitCast(u128, x); + const hi = @intCast(u64, rep >> 64); + const lo = @truncate(u64, rep); + + if (hi == expectedHi and lo == expectedLo) + return; + + // test other possible NaN representation(signal NaN) + if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) { + if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and + ((hi & 0xffffffffffff) > 0 or lo > 0)) + { + return; + } + } + + @panic("__extendsftf2 test failure"); +} + +test "extenddftf2" { + // qNaN + test__extenddftf2(makeQNaN64(), 0x7fff800000000000, 0x0); + + // NaN + test__extenddftf2(makeNaN64(0x7100000000000), 0x7fff710000000000, 0x0); + + // inf + test__extenddftf2(makeInf64(), 0x7fff000000000000, 0x0); + + // zero + test__extenddftf2(0.0, 0x0, 0x0); + + test__extenddftf2(0x1.23456789abcdefp+5, 0x400423456789abcd, 0xf000000000000000); + + test__extenddftf2(0x1.edcba987654321fp-9, 0x3ff6edcba9876543, 0x2000000000000000); + + test__extenddftf2(0x1.23456789abcdefp+45, 0x402c23456789abcd, 0xf000000000000000); + + test__extenddftf2(0x1.edcba987654321fp-45, 0x3fd2edcba9876543, 0x2000000000000000); +} + +test "extendsftf2" { + // qNaN + test__extendsftf2(makeQNaN32(), 0x7fff800000000000, 0x0); + // NaN + test__extendsftf2(makeNaN32(0x410000), 0x7fff820000000000, 0x0); + // inf + test__extendsftf2(makeInf32(), 0x7fff000000000000, 0x0); + // zero + test__extendsftf2(0.0, 0x0, 0x0); + test__extendsftf2(0x1.23456p+5, 0x4004234560000000, 0x0); + test__extendsftf2(0x1.edcbap-9, 0x3ff6edcba0000000, 0x0); + test__extendsftf2(0x1.23456p+45, 0x402c234560000000, 0x0); + test__extendsftf2(0x1.edcbap-45, 0x3fd2edcba0000000, 0x0); +} + +fn makeQNaN64() f64 { + return @bitCast(f64, u64(0x7ff8000000000000)); +} + +fn makeInf64() f64 { + return @bitCast(f64, u64(0x7ff0000000000000)); +} + +fn makeNaN64(rand: u64) f64 { + return @bitCast(f64, 0x7ff0000000000000 | (rand & 0xfffffffffffff)); +} + +fn makeQNaN32() f32 { + return @bitCast(f32, u32(0x7fc00000)); +} + +fn makeNaN32(rand: u32) f32 { + return @bitCast(f32, 0x7f800000 | (rand & 0x7fffff)); +} + +fn makeInf32() f32 { + return @bitCast(f32, u32(0x7f800000)); +} diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index 6ad7768cb2..c96e1587f8 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -20,6 +20,8 @@ comptime { @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); @export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage); + @export("__extenddftf2", @import("extendXfYf2.zig").__extenddftf2, linkage); + @export("__extendsftf2", @import("extendXfYf2.zig").__extendsftf2, linkage); @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage); @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage); diff --git a/test/behavior.zig b/test/behavior.zig index b494c623e2..3a2f706ad4 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -59,4 +59,5 @@ comptime { _ = @import("cases/var_args.zig"); _ = @import("cases/void.zig"); _ = @import("cases/while.zig"); + _ = @import("cases/widening.zig"); } diff --git a/test/cases/widening.zig b/test/cases/widening.zig new file mode 100644 index 0000000000..18c12806d3 --- /dev/null +++ b/test/cases/widening.zig @@ -0,0 +1,26 @@ +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; + +test "integer widening" { + var a: u8 = 250; + var b: u16 = a; + var c: u32 = b; + var d: u64 = c; + var e: u64 = d; + var f: u128 = e; + assert(f == a); +} + +test "implicit unsigned integer to signed integer" { + var a: u8 = 250; + var b: i16 = a; + assert(b == 250); +} + +test "float widening" { + var a: f32 = 12.34; + var b: f64 = a; + var c: f128 = b; + assert(c == a); +} -- cgit v1.2.3 From 459d72f8736ebd8372b9050c17e5f3bc00092573 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Jun 2018 17:41:49 -0400 Subject: fix compiler crash for invalid enum closes #1079 closes #1147 --- src/analyze.cpp | 5 +++-- test/compile_errors.zig | 13 +++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 479abef16a..5160a19e81 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2318,8 +2318,9 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { return; if (enum_type->data.enumeration.zero_bits_loop_flag) { - enum_type->data.enumeration.zero_bits_known = true; - enum_type->data.enumeration.zero_bits_loop_flag = false; + add_node_error(g, enum_type->data.enumeration.decl_node, + buf_sprintf("'%s' depends on itself", buf_ptr(&enum_type->name))); + enum_type->data.enumeration.is_invalid = true; return; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 17896f9ab9..2247f0af96 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,19 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "enum field value references enum", + \\pub const Foo = extern enum { + \\ A = Foo.B, + \\ C = D, + \\}; + \\export fn entry() void { + \\ var s: Foo = Foo.E; + \\} + , + ".tmp_source.zig:1:17: error: 'Foo' depends on itself", + ); + cases.add( "@floatToInt comptime safety", \\comptime { -- cgit v1.2.3 From 8866bef92c8b674c2a444c94326c57984597ab05 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 22 Jun 2018 01:49:32 -0400 Subject: clean up self hosted main. delete unsupported commands --- src-self-hosted/arg.zig | 4 +- src-self-hosted/main.zig | 546 ++++++++++----------------------------------- src-self-hosted/module.zig | 24 -- test/cases/bugs/1111.zig | 6 +- 4 files changed, 121 insertions(+), 459 deletions(-) diff --git a/src-self-hosted/arg.zig b/src-self-hosted/arg.zig index dc89483213..2ab44e5fdf 100644 --- a/src-self-hosted/arg.zig +++ b/src-self-hosted/arg.zig @@ -168,7 +168,7 @@ pub const Args = struct { } // e.g. --names value1 value2 value3 - pub fn many(self: *Args, name: []const u8) ?[]const []const u8 { + pub fn many(self: *Args, name: []const u8) []const []const u8 { if (self.flags.get(name)) |entry| { switch (entry.value) { FlagArg.Many => |inner| { @@ -177,7 +177,7 @@ pub const Args = struct { else => @panic("attempted to retrieve flag with wrong type"), } } else { - return null; + return []const []const u8{}; } } }; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 45e6bb742a..6dabddaefb 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -26,15 +26,11 @@ const usage = \\ \\Commands: \\ - \\ build Build project from build.zig \\ build-exe [source] Create executable from source or object files \\ build-lib [source] Create library from source or object files \\ build-obj [source] Create object from source or assembly \\ fmt [source] Parse file and render in canonical zig format - \\ run [source] Create executable and run immediately \\ targets List available compilation targets - \\ test [source] Create and run a test build - \\ translate-c [source] Convert c code to zig code \\ version Print version number and exit \\ zen Print zen of zig and exit \\ @@ -47,7 +43,7 @@ const Command = struct { }; pub fn main() !void { - var allocator = std.heap.c_allocator; + const allocator = std.heap.c_allocator; var stdout_file = try std.io.getStdOut(); var stdout_out_stream = std.io.FileOutStream.init(&stdout_file); @@ -58,18 +54,16 @@ pub fn main() !void { stderr = &stderr_out_stream.stream; const args = try os.argsAlloc(allocator); - defer os.argsFree(allocator, args); + // TODO I'm getting unreachable code here, which shouldn't happen + //defer os.argsFree(allocator, args); if (args.len <= 1) { + try stderr.write("expected command argument\n\n"); try stderr.write(usage); os.exit(1); } const commands = []Command{ - Command{ - .name = "build", - .exec = cmdBuild, - }, Command{ .name = "build-exe", .exec = cmdBuildExe, @@ -86,22 +80,10 @@ pub fn main() !void { .name = "fmt", .exec = cmdFmt, }, - Command{ - .name = "run", - .exec = cmdRun, - }, Command{ .name = "targets", .exec = cmdTargets, }, - Command{ - .name = "test", - .exec = cmdTest, - }, - Command{ - .name = "translate-c", - .exec = cmdTranslateC, - }, Command{ .name = "version", .exec = cmdVersion, @@ -124,177 +106,15 @@ pub fn main() !void { for (commands) |command| { if (mem.eql(u8, command.name, args[1])) { - try command.exec(allocator, args[2..]); - return; + return command.exec(allocator, args[2..]); } } try stderr.print("unknown command: {}\n\n", args[1]); try stderr.write(usage); + os.exit(1); } -// cmd:build /////////////////////////////////////////////////////////////////////////////////////// - -const usage_build = - \\usage: zig build - \\ - \\General Options: - \\ --help Print this help and exit - \\ --init Generate a build.zig template - \\ --build-file [file] Override path to build.zig - \\ --cache-dir [path] Override path to cache directory - \\ --verbose Print commands before executing them - \\ --prefix [path] Override default install prefix - \\ - \\Project-Specific Options: - \\ - \\ Project-specific options become available when the build file is found. - \\ - \\Advanced Options: - \\ --build-file [file] Override path to build.zig - \\ --cache-dir [path] Override path to cache directory - \\ --verbose-tokenize Enable compiler debug output for tokenization - \\ --verbose-ast Enable compiler debug output for parsing into an AST - \\ --verbose-link Enable compiler debug output for linking - \\ --verbose-ir Enable compiler debug output for Zig IR - \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR - \\ --verbose-cimport Enable compiler debug output for C imports - \\ - \\ -; - -const args_build_spec = []Flag{ - Flag.Bool("--help"), - Flag.Bool("--init"), - Flag.Arg1("--build-file"), - Flag.Arg1("--cache-dir"), - Flag.Bool("--verbose"), - Flag.Arg1("--prefix"), - - Flag.Arg1("--build-file"), - Flag.Arg1("--cache-dir"), - Flag.Bool("--verbose-tokenize"), - Flag.Bool("--verbose-ast"), - Flag.Bool("--verbose-link"), - Flag.Bool("--verbose-ir"), - Flag.Bool("--verbose-llvm-ir"), - Flag.Bool("--verbose-cimport"), -}; - -const missing_build_file = - \\No 'build.zig' file found. - \\ - \\Initialize a 'build.zig' template file with `zig build --init`, - \\or build an executable directly with `zig build-exe $FILENAME.zig`. - \\ - \\See: `zig build --help` or `zig help` for more options. - \\ -; - -fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void { - var flags = try Args.parse(allocator, args_build_spec, args); - defer flags.deinit(); - - if (flags.present("help")) { - try stderr.write(usage_build); - os.exit(0); - } - - const zig_lib_dir = try introspect.resolveZigLibDir(allocator); - defer allocator.free(zig_lib_dir); - - const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std"); - defer allocator.free(zig_std_dir); - - const special_dir = try os.path.join(allocator, zig_std_dir, "special"); - defer allocator.free(special_dir); - - const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig"); - defer allocator.free(build_runner_path); - - const build_file = flags.single("build-file") orelse "build.zig"; - const build_file_abs = try os.path.resolve(allocator, ".", build_file); - defer allocator.free(build_file_abs); - - const build_file_exists = os.File.access(allocator, build_file_abs, os.default_file_mode) catch false; - - if (flags.present("init")) { - if (build_file_exists) { - try stderr.print("build.zig already exists\n"); - os.exit(1); - } - - // need a new scope for proper defer scope finalization on exit - { - const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig"); - defer allocator.free(build_template_path); - - try os.copyFile(allocator, build_template_path, build_file_abs); - try stderr.print("wrote build.zig template\n"); - } - - os.exit(0); - } - - if (!build_file_exists) { - try stderr.write(missing_build_file); - os.exit(1); - } - - // TODO: Invoke build.zig entrypoint directly? - var zig_exe_path = try os.selfExePath(allocator); - defer allocator.free(zig_exe_path); - - var build_args = ArrayList([]const u8).init(allocator); - defer build_args.deinit(); - - const build_file_basename = os.path.basename(build_file_abs); - const build_file_dirname = os.path.dirname(build_file_abs) orelse "."; - - var full_cache_dir: []u8 = undefined; - if (flags.single("cache-dir")) |cache_dir| { - full_cache_dir = try os.path.resolve(allocator, ".", cache_dir, full_cache_dir); - } else { - full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache"); - } - defer allocator.free(full_cache_dir); - - const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build"); - defer allocator.free(path_to_build_exe); - - try build_args.append(path_to_build_exe); - try build_args.append(zig_exe_path); - try build_args.append(build_file_dirname); - try build_args.append(full_cache_dir); - - var proc = try os.ChildProcess.init(build_args.toSliceConst(), allocator); - defer proc.deinit(); - - var term = try proc.spawnAndWait(); - switch (term) { - os.ChildProcess.Term.Exited => |status| { - if (status != 0) { - try stderr.print("{} exited with status {}\n", build_args.at(0), status); - os.exit(1); - } - }, - os.ChildProcess.Term.Signal => |signal| { - try stderr.print("{} killed by signal {}\n", build_args.at(0), signal); - os.exit(1); - }, - os.ChildProcess.Term.Stopped => |signal| { - try stderr.print("{} stopped by signal {}\n", build_args.at(0), signal); - os.exit(1); - }, - os.ChildProcess.Term.Unknown => |status| { - try stderr.print("{} encountered unknown failure {}\n", build_args.at(0), status); - os.exit(1); - }, - } -} - -// cmd:build-exe /////////////////////////////////////////////////////////////////////////////////// - const usage_build_generic = \\usage: zig build-exe [file] \\ zig build-lib [file] @@ -315,8 +135,11 @@ const usage_build_generic = \\ --output-h [file] Override generated header file path \\ --pkg-begin [name] [path] Make package available to import and push current pkg \\ --pkg-end Pop current pkg - \\ --release-fast Build with optimizations on and safety off - \\ --release-safe Build with optimizations on and safety on + \\ --mode [mode] Set the build mode + \\ debug (default) optimizations off, safety on + \\ release-fast optimizations on, safety off + \\ release-safe optimizations on, safety on + \\ release-small optimize for small binary, safety off \\ --static Output will be statically linked \\ --strip Exclude debug symbols \\ --target-arch [name] Specify target architecture @@ -367,6 +190,12 @@ const args_build_generic = []Flag{ "off", "on", }), + Flag.Option("--mode", []const []const u8{ + "debug", + "release-fast", + "release-safe", + "release-small", + }), Flag.ArgMergeN("--assembly", 1), Flag.Arg1("--cache-dir"), @@ -383,8 +212,6 @@ const args_build_generic = []Flag{ // NOTE: Parsed manually after initial check Flag.ArgN("--pkg-begin", 2), Flag.Bool("--pkg-end"), - Flag.Bool("--release-fast"), - Flag.Bool("--release-safe"), Flag.Bool("--static"), Flag.Bool("--strip"), Flag.Arg1("--target-arch"), @@ -431,16 +258,25 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo defer flags.deinit(); if (flags.present("help")) { - try stderr.write(usage_build_generic); + try stdout.write(usage_build_generic); os.exit(0); } - var build_mode = builtin.Mode.Debug; - if (flags.present("release-fast")) { - build_mode = builtin.Mode.ReleaseFast; - } else if (flags.present("release-safe")) { - build_mode = builtin.Mode.ReleaseSafe; - } + const build_mode = blk: { + if (flags.single("mode")) |mode_flag| { + if (mem.eql(u8, mode_flag, "debug")) { + break :blk builtin.Mode.Debug; + } else if (mem.eql(u8, mode_flag, "release-fast")) { + break :blk builtin.Mode.ReleaseFast; + } else if (mem.eql(u8, mode_flag, "release-safe")) { + break :blk builtin.Mode.ReleaseSafe; + } else if (mem.eql(u8, mode_flag, "release-small")) { + break :blk builtin.Mode.ReleaseSmall; + } else unreachable; + } else { + break :blk builtin.Mode.Debug; + } + }; const color = blk: { if (flags.single("color")) |color_flag| { @@ -456,20 +292,21 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo } }; - var emit_type = Module.Emit.Binary; - if (flags.single("emit")) |emit_flag| { - if (mem.eql(u8, emit_flag, "asm")) { - emit_type = Module.Emit.Assembly; - } else if (mem.eql(u8, emit_flag, "bin")) { - emit_type = Module.Emit.Binary; - } else if (mem.eql(u8, emit_flag, "llvm-ir")) { - emit_type = Module.Emit.LlvmIr; + const emit_type = blk: { + if (flags.single("emit")) |emit_flag| { + if (mem.eql(u8, emit_flag, "asm")) { + break :blk Module.Emit.Assembly; + } else if (mem.eql(u8, emit_flag, "bin")) { + break :blk Module.Emit.Binary; + } else if (mem.eql(u8, emit_flag, "llvm-ir")) { + break :blk Module.Emit.LlvmIr; + } else unreachable; } else { - unreachable; + break :blk Module.Emit.Binary; } - } + }; - var cur_pkg = try Module.CliPkg.init(allocator, "", "", null); // TODO: Need a path, name? + var cur_pkg = try CliPkg.init(allocator, "", "", null); defer cur_pkg.deinit(); var i: usize = 0; @@ -482,15 +319,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo i += 1; const new_pkg_path = args[i]; - var new_cur_pkg = try Module.CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg); + var new_cur_pkg = try CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg); try cur_pkg.children.append(new_cur_pkg); cur_pkg = new_cur_pkg; } else if (mem.eql(u8, "--pkg-end", arg_name)) { - if (cur_pkg.parent == null) { + if (cur_pkg.parent) |parent| { + cur_pkg = parent; + } else { try stderr.print("encountered --pkg-end with no matching --pkg-begin\n"); os.exit(1); } - cur_pkg = cur_pkg.parent.?; } } @@ -499,43 +337,42 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo os.exit(1); } - var in_file: ?[]const u8 = undefined; - switch (flags.positionals.len) { - 0 => { - try stderr.write("--name [name] not provided and unable to infer\n"); - os.exit(1); - }, - 1 => { - in_file = flags.positionals.at(0); - }, + const provided_name = flags.single("name"); + const root_source_file = switch (flags.positionals.len) { + 0 => null, + 1 => flags.positionals.at(0), else => { - try stderr.write("only one zig input file is accepted during build\n"); + try stderr.print("unexpected extra parameter: {}\n", flags.positionals.at(1)); os.exit(1); }, - } + }; - const basename = os.path.basename(in_file.?); - var it = mem.split(basename, "."); - const root_name = it.next() orelse { - try stderr.write("file name cannot be empty\n"); - os.exit(1); + const root_name = if (provided_name) |n| n else blk: { + if (root_source_file) |file| { + const basename = os.path.basename(file); + var it = mem.split(basename, "."); + break :blk it.next() orelse basename; + } else { + try stderr.write("--name [name] not provided and unable to infer\n"); + os.exit(1); + } }; - const asm_a = flags.many("assembly"); - const obj_a = flags.many("object"); - if (in_file == null and (obj_a == null or obj_a.?.len == 0) and (asm_a == null or asm_a.?.len == 0)) { + const assembly_files = flags.many("assembly"); + const link_objects = flags.many("object"); + if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) { try stderr.write("Expected source file argument or at least one --object or --assembly argument\n"); os.exit(1); } - if (out_type == Module.Kind.Obj and (obj_a != null and obj_a.?.len != 0)) { + if (out_type == Module.Kind.Obj and link_objects.len != 0) { try stderr.write("When building an object file, --object arguments are invalid\n"); os.exit(1); } - const zig_root_source_file = in_file; - - const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") orelse "zig-cache"[0..]) catch { + const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..]; + const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch { + try stderr.print("invalid cache dir: {}\n", rel_cache_dir); os.exit(1); }; defer allocator.free(full_cache_dir); @@ -546,7 +383,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo var module = try Module.create( allocator, root_name, - zig_root_source_file, + root_source_file, Target.Native, out_type, build_mode, @@ -561,24 +398,21 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo module.is_test = false; - if (flags.single("linker-script")) |linker_script| { - module.linker_script = linker_script; - } - + module.linker_script = flags.single("linker-script"); module.each_lib_rpath = flags.present("each-lib-rpath"); var clang_argv_buf = ArrayList([]const u8).init(allocator); defer clang_argv_buf.deinit(); - if (flags.many("mllvm")) |mllvm_flags| { - for (mllvm_flags) |mllvm| { - try clang_argv_buf.append("-mllvm"); - try clang_argv_buf.append(mllvm); - } - module.llvm_argv = mllvm_flags; - module.clang_argv = clang_argv_buf.toSliceConst(); + const mllvm_flags = flags.many("mllvm"); + for (mllvm_flags) |mllvm| { + try clang_argv_buf.append("-mllvm"); + try clang_argv_buf.append(mllvm); } + module.llvm_argv = mllvm_flags; + module.clang_argv = clang_argv_buf.toSliceConst(); + module.strip = flags.present("strip"); module.is_static = flags.present("static"); @@ -610,18 +444,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo module.verbose_cimport = flags.present("verbose-cimport"); module.err_color = color; - - if (flags.many("library-path")) |lib_dirs| { - module.lib_dirs = lib_dirs; - } - - if (flags.many("framework")) |frameworks| { - module.darwin_frameworks = frameworks; - } - - if (flags.many("rpath")) |rpath_list| { - module.rpath_list = rpath_list; - } + module.lib_dirs = flags.many("library-path"); + module.darwin_frameworks = flags.many("framework"); + module.rpath_list = flags.many("rpath"); if (flags.single("output-h")) |output_h| { module.out_h_path = output_h; @@ -644,41 +469,25 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo } module.emit_file_type = emit_type; - if (flags.many("object")) |objects| { - module.link_objects = objects; - } - if (flags.many("assembly")) |assembly_files| { - module.assembly_files = assembly_files; - } + module.link_objects = link_objects; + module.assembly_files = assembly_files; try module.build(); - try module.link(flags.single("out-file") orelse null); - - if (flags.present("print-timing-info")) { - // codegen_print_timing_info(g, stderr); - } - - try stderr.print("building {}: {}\n", @tagName(out_type), in_file); + try module.link(flags.single("out-file")); } fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void { - try buildOutputType(allocator, args, Module.Kind.Exe); + return buildOutputType(allocator, args, Module.Kind.Exe); } -// cmd:build-lib /////////////////////////////////////////////////////////////////////////////////// - fn cmdBuildLib(allocator: *Allocator, args: []const []const u8) !void { - try buildOutputType(allocator, args, Module.Kind.Lib); + return buildOutputType(allocator, args, Module.Kind.Lib); } -// cmd:build-obj /////////////////////////////////////////////////////////////////////////////////// - fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void { - try buildOutputType(allocator, args, Module.Kind.Obj); + return buildOutputType(allocator, args, Module.Kind.Obj); } -// cmd:fmt ///////////////////////////////////////////////////////////////////////////////////////// - const usage_fmt = \\usage: zig fmt [file]... \\ @@ -735,7 +544,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { defer flags.deinit(); if (flags.present("help")) { - try stderr.write(usage_fmt); + try stdout.write(usage_fmt); os.exit(0); } @@ -863,162 +672,16 @@ fn cmdTargets(allocator: *Allocator, args: []const []const u8) !void { } } -// cmd:version ///////////////////////////////////////////////////////////////////////////////////// - fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void { try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING)); } -// cmd:test //////////////////////////////////////////////////////////////////////////////////////// - -const usage_test = - \\usage: zig test [file]... - \\ - \\Options: - \\ --help Print this help and exit - \\ - \\ -; - const args_test_spec = []Flag{Flag.Bool("--help")}; -fn cmdTest(allocator: *Allocator, args: []const []const u8) !void { - var flags = try Args.parse(allocator, args_build_spec, args); - defer flags.deinit(); - - if (flags.present("help")) { - try stderr.write(usage_test); - os.exit(0); - } - - if (flags.positionals.len != 1) { - try stderr.write("expected exactly one zig source file\n"); - os.exit(1); - } - - // compile the test program into the cache and run - - // NOTE: May be overlap with buildOutput, take the shared part out. - try stderr.print("testing file {}\n", flags.positionals.at(0)); -} - -// cmd:run ///////////////////////////////////////////////////////////////////////////////////////// - -// Run should be simple and not expose the full set of arguments provided by build-exe. If specific -// build requirements are need, the user should `build-exe` then `run` manually. -const usage_run = - \\usage: zig run [file] -- - \\ - \\Options: - \\ --help Print this help and exit - \\ - \\ -; - -const args_run_spec = []Flag{Flag.Bool("--help")}; - -fn cmdRun(allocator: *Allocator, args: []const []const u8) !void { - var compile_args = args; - var runtime_args: []const []const u8 = []const []const u8{}; - - for (args) |argv, i| { - if (mem.eql(u8, argv, "--")) { - compile_args = args[0..i]; - runtime_args = args[i + 1 ..]; - break; - } - } - var flags = try Args.parse(allocator, args_run_spec, compile_args); - defer flags.deinit(); - - if (flags.present("help")) { - try stderr.write(usage_run); - os.exit(0); - } - - if (flags.positionals.len != 1) { - try stderr.write("expected exactly one zig source file\n"); - os.exit(1); - } - - try stderr.print("runtime args:\n"); - for (runtime_args) |cargs| { - try stderr.print("{}\n", cargs); - } -} - -// cmd:translate-c ///////////////////////////////////////////////////////////////////////////////// - -const usage_translate_c = - \\usage: zig translate-c [file] - \\ - \\Options: - \\ --help Print this help and exit - \\ --enable-timing-info Print timing diagnostics - \\ --output [path] Output file to write generated zig file (default: stdout) - \\ - \\ -; - -const args_translate_c_spec = []Flag{ - Flag.Bool("--help"), - Flag.Bool("--enable-timing-info"), - Flag.Arg1("--libc-include-dir"), - Flag.Arg1("--output"), -}; - -fn cmdTranslateC(allocator: *Allocator, args: []const []const u8) !void { - var flags = try Args.parse(allocator, args_translate_c_spec, args); - defer flags.deinit(); - - if (flags.present("help")) { - try stderr.write(usage_translate_c); - os.exit(0); - } - - if (flags.positionals.len != 1) { - try stderr.write("expected exactly one c source file\n"); - os.exit(1); - } - - // set up codegen - - const zig_root_source_file = null; - - // NOTE: translate-c shouldn't require setting up the full codegen instance as it does in - // the C++ compiler. - - // codegen_create(g); - // codegen_set_out_name(g, null); - // codegen_translate_c(g, flags.positional.at(0)) - - var output_stream = stdout; - if (flags.single("output")) |output_file| { - var file = try os.File.openWrite(allocator, output_file); - defer file.close(); - - var file_stream = io.FileOutStream.init(&file); - // TODO: Not being set correctly, still stdout - output_stream = &file_stream.stream; - } - - // ast_render(g, output_stream, g->root_import->root, 4); - try output_stream.write("pub const example = 10;\n"); - - if (flags.present("enable-timing-info")) { - // codegen_print_timing_info(g, stdout); - try stderr.write("printing timing info for translate-c\n"); - } -} - -// cmd:help //////////////////////////////////////////////////////////////////////////////////////// - fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void { - try stderr.write(usage); + try stdout.write(usage); } -// cmd:zen ///////////////////////////////////////////////////////////////////////////////////////// - const info_zen = \\ \\ * Communicate intent precisely. @@ -1040,8 +703,6 @@ fn cmdZen(allocator: *Allocator, args: []const []const u8) !void { try stdout.write(info_zen); } -// cmd:internal //////////////////////////////////////////////////////////////////////////////////// - const usage_internal = \\usage: zig internal [subcommand] \\ @@ -1095,3 +756,28 @@ fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void { std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB), ); } + +const CliPkg = struct { + name: []const u8, + path: []const u8, + children: ArrayList(*CliPkg), + parent: ?*CliPkg, + + pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg { + var pkg = try allocator.create(CliPkg{ + .name = name, + .path = path, + .children = ArrayList(*CliPkg).init(allocator), + .parent = parent, + }); + return pkg; + } + + pub fn deinit(self: *CliPkg) void { + for (self.children.toSliceConst()) |child| { + child.deinit(); + } + self.children.deinit(); + } +}; + diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 5f02f1a832..4da46cd38c 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -103,30 +103,6 @@ pub const Module = struct { LlvmIr, }; - pub const CliPkg = struct { - name: []const u8, - path: []const u8, - children: ArrayList(*CliPkg), - parent: ?*CliPkg, - - pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg { - var pkg = try allocator.create(CliPkg{ - .name = name, - .path = path, - .children = ArrayList(*CliPkg).init(allocator), - .parent = parent, - }); - return pkg; - } - - pub fn deinit(self: *CliPkg) void { - for (self.children.toSliceConst()) |child| { - child.deinit(); - } - self.children.deinit(); - } - }; - pub fn create( allocator: *mem.Allocator, name: []const u8, diff --git a/test/cases/bugs/1111.zig b/test/cases/bugs/1111.zig index 51ce90af52..f62107f9a3 100644 --- a/test/cases/bugs/1111.zig +++ b/test/cases/bugs/1111.zig @@ -5,8 +5,8 @@ const Foo = extern enum { test "issue 1111 fixed" { const v = Foo.Bar; - switch(v) { - Foo.Bar => return, - else => return, + switch (v) { + Foo.Bar => return, + else => return, } } -- cgit v1.2.3 From 3290e728339e49765b1adda78f173befb9fc12bf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Jun 2018 11:52:18 -0400 Subject: std.zig.ast: fix incorrect impl of FnProto.firstToken closes #1151 --- std/zig/ast.zig | 1 + std/zig/parser_test.zig | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 4246a50861..63518c5182 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -858,6 +858,7 @@ pub const Node = struct { pub fn firstToken(self: *FnProto) TokenIndex { if (self.visib_token) |visib_token| return visib_token; + if (self.async_attr) |async_attr| return async_attr.firstToken(); if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token; assert(self.lib_name == null); if (self.cc_token) |cc_token| return cc_token; diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 09ea8aa1a1..21259bec3c 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,12 @@ +test "zig fmt: preserve space between async fn definitions" { + try testCanonical( + \\async fn a() void {} + \\ + \\async fn b() void {} + \\ + ); +} + test "zig fmt: comment to disable/enable zig fmt first" { try testCanonical( \\// Test trailing comma syntax -- cgit v1.2.3 From 8e714289cac55fd6f793cf21cea5fa1930edb985 Mon Sep 17 00:00:00 2001 From: Isaac Hier Date: Sun, 24 Jun 2018 20:27:18 -0400 Subject: Fix os_path_join for case where dirname is empty --- src/os.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/os.cpp b/src/os.cpp index b7d2fd1de0..d52295950d 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -225,6 +225,11 @@ void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname) { } void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { + if (buf_len(dirname) == 0) { + buf_init_from_buf(out_full_path, basename); + return; + } + buf_init_from_buf(out_full_path, dirname); uint8_t c = *(buf_ptr(out_full_path) + buf_len(out_full_path) - 1); if (!os_is_sep(c)) -- cgit v1.2.3 From af95e1557214df4a1a34a712efc2f8dafb502c82 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Jun 2018 15:10:11 -0400 Subject: rename get_maybe_type to get_optional_type --- src/all_types.hpp | 2 +- src/analyze.cpp | 12 ++++++------ src/analyze.hpp | 2 +- src/ir.cpp | 38 +++++++++++++++++++------------------- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 12e054cbeb..019dcb182e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1233,7 +1233,7 @@ struct TypeTableEntry { // use these fields to make sure we don't duplicate type table entries for the same type TypeTableEntry *pointer_parent[2]; // [0 - mut, 1 - const] - TypeTableEntry *maybe_parent; + TypeTableEntry *optional_parent; TypeTableEntry *promise_parent; TypeTableEntry *promise_frame_parent; // If we generate a constant name value for this type, we memoize it here. diff --git a/src/analyze.cpp b/src/analyze.cpp index 5160a19e81..c018ee4e92 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -482,7 +482,7 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) return return_type->promise_frame_parent; } - TypeTableEntry *awaiter_handle_type = get_maybe_type(g, g->builtin_types.entry_promise); + TypeTableEntry *awaiter_handle_type = get_optional_type(g, g->builtin_types.entry_promise); TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false); ZigList field_names = {}; @@ -513,9 +513,9 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) return entry; } -TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { - if (child_type->maybe_parent) { - TypeTableEntry *entry = child_type->maybe_parent; +TypeTableEntry *get_optional_type(CodeGen *g, TypeTableEntry *child_type) { + if (child_type->optional_parent) { + TypeTableEntry *entry = child_type->optional_parent; return entry; } else { ensure_complete_type(g, child_type); @@ -592,7 +592,7 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { entry->data.maybe.child_type = child_type; - child_type->maybe_parent = entry; + child_type->optional_parent = entry; return entry; } } @@ -2996,7 +2996,7 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { return wrong_panic_prototype(g, proto_node, fn_type); } - TypeTableEntry *optional_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g)); + TypeTableEntry *optional_ptr_to_stack_trace_type = get_optional_type(g, get_ptr_to_stack_trace_type(g)); if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) { return wrong_panic_prototype(g, proto_node, fn_type); } diff --git a/src/analyze.hpp b/src/analyze.hpp index 88e06b2390..c2730197e2 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -24,7 +24,7 @@ TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits); TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type); TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type); TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id); -TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type); +TypeTableEntry *get_optional_type(CodeGen *g, TypeTableEntry *child_type); TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size); TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type); TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, diff --git a/src/ir.cpp b/src/ir.cpp index c6078e755d..1930bbb248 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3044,7 +3044,7 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value); IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, - get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); + get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); // TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig IrInstruction *replacement_value = irb->exec->coro_handle; IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node, @@ -6654,7 +6654,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle); IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, - get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); + get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, parent_scope, node, promise_type_val, awaiter_field_ptr, nullptr, irb->exec->coro_handle, nullptr, AtomicRmwOp_xchg, AtomicOrderSeqCst); @@ -6988,7 +6988,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec VariableTableEntry *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, - get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); + get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var); @@ -8762,7 +8762,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else if (prev_inst->value.type->id == TypeTableEntryIdOptional) { return prev_inst->value.type; } else { - return get_maybe_type(ira->codegen, prev_inst->value.type); + return get_optional_type(ira->codegen, prev_inst->value.type); } } else { return prev_inst->value.type; @@ -12127,7 +12127,7 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, { if (instruction->optional == IrInstructionErrorReturnTrace::Null) { TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); - TypeTableEntry *optional_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); + TypeTableEntry *optional_type = get_optional_type(ira->codegen, ptr_to_stack_trace_type); if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); assert(get_codegen_ptr_type(optional_type) != nullptr); @@ -13105,7 +13105,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdPromise: { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); - out_val->data.x_type = get_maybe_type(ira->codegen, type_entry); + out_val->data.x_type = get_optional_type(ira->codegen, type_entry); return ira->codegen->builtin_types.entry_type; } case TypeTableEntryIdUnreachable: @@ -16326,7 +16326,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); - fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); + fn_def_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { fn_def_fields[6].data.x_optional = create_const_vals(1); ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); @@ -16609,7 +16609,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t // child: ?type ensure_field_index(result->type, "child", 0); fields[0].special = ConstValSpecialStatic; - fields[0].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[0].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.promise.result_type == nullptr) fields[0].data.x_optional = nullptr; @@ -16763,7 +16763,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t // tag_type: ?type ensure_field_index(result->type, "tag_type", 1); fields[1].special = ConstValSpecialStatic; - fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); AstNode *union_decl_node = type_entry->data.unionation.decl_node; if (union_decl_node->data.container_decl.auto_enum || @@ -16803,7 +16803,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ConstExprValue *inner_fields = create_const_vals(3); inner_fields[1].special = ConstValSpecialStatic; - inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type); + inner_fields[1].type = get_optional_type(ira->codegen, type_info_enum_field_type); if (fields[1].data.x_optional == nullptr) { inner_fields[1].data.x_optional = nullptr; @@ -16874,7 +16874,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ConstExprValue *inner_fields = create_const_vals(3); inner_fields[1].special = ConstValSpecialStatic; - inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize); + inner_fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_usize); if (!type_has_bits(struct_field->type_entry)) { inner_fields[1].data.x_optional = nullptr; @@ -16934,7 +16934,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t // return_type: ?type ensure_field_index(result->type, "return_type", 3); fields[3].special = ConstValSpecialStatic; - fields[3].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[3].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.return_type == nullptr) fields[3].data.x_optional = nullptr; else { @@ -16947,7 +16947,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t // async_allocator_type: type ensure_field_index(result->type, "async_allocator_type", 4); fields[4].special = ConstValSpecialStatic; - fields[4].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[4].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.async_allocator_type == nullptr) fields[4].data.x_optional = nullptr; else { @@ -16990,7 +16990,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].type = ira->codegen->builtin_types.entry_bool; inner_fields[1].data.x_bool = fn_param_info->is_noalias; inner_fields[2].special = ConstValSpecialStatic; - inner_fields[2].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); + inner_fields[2].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (arg_is_generic) inner_fields[2].data.x_optional = nullptr; @@ -17342,7 +17342,7 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, operand_type, success_order, failure_order); - result->value.type = get_maybe_type(ira->codegen, operand_type); + result->value.type = get_optional_type(ira->codegen, operand_type); ir_link_new_instruction(result, &instruction->base); ir_add_alloca(ira, result, result->value.type); return result->value.type; @@ -19013,7 +19013,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 old_align_bytes = ptr_type->data.pointer.alignment; TypeTableEntry *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes); - result_type = get_maybe_type(ira->codegen, better_ptr_type); + result_type = get_optional_type(ira->codegen, better_ptr_type); } else if (target_type->id == TypeTableEntryIdOptional && target_type->data.maybe.child_type->id == TypeTableEntryIdFn) { @@ -19021,7 +19021,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 old_align_bytes = fn_type_id.alignment; fn_type_id.alignment = align_bytes; TypeTableEntry *fn_type = get_fn_type(ira->codegen, &fn_type_id); - result_type = get_maybe_type(ira->codegen, fn_type); + result_type = get_optional_type(ira->codegen, fn_type); } else if (is_slice(target_type)) { TypeTableEntry *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry; old_align_bytes = slice_ptr_type->data.pointer.alignment; @@ -19782,7 +19782,7 @@ static TypeTableEntry *ir_analyze_instruction_coro_free(IrAnalyze *ira, IrInstru instruction->base.source_node, coro_id, coro_handle); ir_link_new_instruction(result, &instruction->base); TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); - result->value.type = get_maybe_type(ira->codegen, ptr_type); + result->value.type = get_optional_type(ira->codegen, ptr_type); return result->value.type; } @@ -19850,7 +19850,7 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, instruction->base.source_node, alloc_fn, coro_size); ir_link_new_instruction(result, &instruction->base); TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); - result->value.type = get_maybe_type(ira->codegen, u8_ptr_type); + result->value.type = get_optional_type(ira->codegen, u8_ptr_type); return result->value.type; } -- cgit v1.2.3 From 11ca38a4e9c637bf6ff635f4f62634edaf89f853 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Jun 2018 15:27:41 -0400 Subject: fix crash for optional pointer to empty struct closes #1153 --- src/ir.cpp | 3 ++- test/behavior.zig | 1 + test/cases/optional.zig | 9 +++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 test/cases/optional.zig diff --git a/src/ir.cpp b/src/ir.cpp index 1930bbb248..76178f2437 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7985,9 +7985,10 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry // * and [*] can do a const-cast-only to ?* and ?[*], respectively // but not if there is a mutable parent pointer + // and not if the pointer is zero bits if (!wanted_is_mutable && wanted_type->id == TypeTableEntryIdOptional && wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer && - actual_type->id == TypeTableEntryIdPointer) + actual_type->id == TypeTableEntryIdPointer && type_has_bits(actual_type)) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, actual_type, source_node, wanted_is_mutable); diff --git a/test/behavior.zig b/test/behavior.zig index 3a2f706ad4..3766ed4305 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -35,6 +35,7 @@ comptime { _ = @import("cases/math.zig"); _ = @import("cases/merge_error_sets.zig"); _ = @import("cases/misc.zig"); + _ = @import("cases/optional.zig"); _ = @import("cases/namespace_depends_on_compile_var/index.zig"); _ = @import("cases/new_stack_call.zig"); _ = @import("cases/null.zig"); diff --git a/test/cases/optional.zig b/test/cases/optional.zig new file mode 100644 index 0000000000..0129252dab --- /dev/null +++ b/test/cases/optional.zig @@ -0,0 +1,9 @@ +const assert = @import("std").debug.assert; + +pub const EmptyStruct = struct {}; + +test "optional pointer to size zero struct" { + var e = EmptyStruct{}; + var o: ?*EmptyStruct = &e; + assert(o != null); +} -- cgit v1.2.3 From 4de60dde6ed734acbc428887866ae3d528abbd37 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Jun 2018 15:48:42 -0400 Subject: langref: explicit cast section --- doc/langref.html.in | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index b76fc69385..8e24a4be2c 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -3602,6 +3602,10 @@ test "implicit cast - invoke a type as a function" { var b = u16(a); } {#code_end#} +

    + Implicit casts are only allowed when it is completely unambiguous how to get from one type to another, + and the transformation is guaranteed to be safe. +

    {#header_open|Implicit Cast: Stricter Qualification#}

    Values which have the same representation at runtime can be cast to increase the strictness @@ -3722,7 +3726,32 @@ test "float widening" { {#header_close#} {#header_open|Explicit Casts#} -

    TODO

    +

    + Explicit casts are performed via {#link|Builtin Functions#}. + Some explicit casts are safe; some are not. + Some explicit casts perform language-level assertions; some do not. + Some explicit casts are no-ops at runtime; some are not. +

    +
      +
    • {#link|@bitCast#} - change type but maintain bit representation
    • +
    • {#link|@alignCast#} - make a pointer have more alignment
    • +
    • {#link|@boolToInt#} - convert true to 1 and false to 0
    • +
    • {#link|@bytesToSlice#} - convert a slice of bytes to a slice of another type
    • +
    • {#link|@enumToInt#} - obtain the integer tag value of an enum or tagged union
    • +
    • {#link|@errSetCast#} - convert to a smaller error set
    • +
    • {#link|@errorToInt#} - obtain the integer value of an error code
    • +
    • {#link|@floatCast#} - convert a larger float to a smaller float
    • +
    • {#link|@floatToInt#} - obtain the integer part of a float value
    • +
    • {#link|@intCast#} - convert between integer types
    • +
    • {#link|@intToEnum#} - obtain an enum value based on its integer tag value
    • +
    • {#link|@intToError#} - obtain an error code based on its integer value
    • +
    • {#link|@intToFloat#} - convert an integer to a float value
    • +
    • {#link|@intToPtr#} - convert an address to a pointer
    • +
    • {#link|@ptrCast#} - convert between pointer types
    • +
    • {#link|@ptrToInt#} - obtain the address of a pointer
    • +
    • {#link|@sliceToBytes#} - convert a slice of anything to a slice of bytes
    • +
    • {#link|@truncate#} - convert between integer types, chopping off bits
    • +
    {#header_close#} {#header_open|Peer Type Resolution#} -- cgit v1.2.3 From 0ebc7b66e6fe721d84f169ae714bbff7e82aa738 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 27 Jun 2018 16:20:04 +0200 Subject: scope variables in floating point cast tests Fixes a bug where the result of a @floatCast wasn't actually checked; it was checking the result from the previous @floatCast. --- test/cases/cast.zig | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 7b36bcd04a..4209d87c1a 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -418,19 +418,24 @@ test "@intCast comptime_int" { } test "@floatCast comptime_int and comptime_float" { - const result = @floatCast(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); - - const result2 = @floatCast(f32, 1234.0); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + { + const result = @floatCast(f32, 1234); + assert(@typeOf(result) == f32); + assert(result == 1234.0); + } + { + const result = @floatCast(f32, 1234.0); + assert(@typeOf(result) == f32); + assert(result == 1234.0); + } } test "comptime_int @intToFloat" { - const result = @intToFloat(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + { + const result = @intToFloat(f32, 1234); + assert(@typeOf(result) == f32); + assert(result == 1234.0); + } } test "@bytesToSlice keeps pointer alignment" { -- cgit v1.2.3 From 1f45075a0e1d86fa110011f6cedbef61a9f6f056 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 27 Jun 2018 16:20:04 +0200 Subject: dry floating-point type definitions --- src/codegen.cpp | 57 ++++++++++++++------------------------------------------- 1 file changed, 14 insertions(+), 43 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index c2406f0838..abec5a8ec7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6177,58 +6177,29 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_usize = entry; } } - { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); - entry->type_ref = LLVMFloatType(); - buf_init_from_str(&entry->name, "f32"); - entry->data.floating.bit_count = 32; - - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); - entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - debug_size_in_bits, - ZigLLVMEncoding_DW_ATE_float()); - g->builtin_types.entry_f32 = entry; - g->primitive_type_table.put(&entry->name, entry); - } - { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); - entry->type_ref = LLVMDoubleType(); - buf_init_from_str(&entry->name, "f64"); - entry->data.floating.bit_count = 64; - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); - entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - debug_size_in_bits, - ZigLLVMEncoding_DW_ATE_float()); - g->builtin_types.entry_f64 = entry; - g->primitive_type_table.put(&entry->name, entry); - } - { + auto add_fp_entry = [] (CodeGen *g, + const char *name, + uint32_t bit_count, + LLVMTypeRef type_ref, + TypeTableEntry **field) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); - entry->type_ref = LLVMFP128Type(); - buf_init_from_str(&entry->name, "f128"); - entry->data.floating.bit_count = 128; + entry->type_ref = type_ref; + buf_init_from_str(&entry->name, name); + entry->data.floating.bit_count = bit_count; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), debug_size_in_bits, ZigLLVMEncoding_DW_ATE_float()); - g->builtin_types.entry_f128 = entry; + *field = entry; g->primitive_type_table.put(&entry->name, entry); - } - { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); - entry->type_ref = LLVMX86FP80Type(); - buf_init_from_str(&entry->name, "c_longdouble"); - entry->data.floating.bit_count = 80; + }; + add_fp_entry(g, "f32", 32, LLVMFloatType(), &g->builtin_types.entry_f32); + add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64); + add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128); + add_fp_entry(g, "c_longdouble", 80, LLVMX86FP80Type(), &g->builtin_types.entry_c_longdouble); - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); - entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - debug_size_in_bits, - ZigLLVMEncoding_DW_ATE_float()); - g->builtin_types.entry_c_longdouble = entry; - g->primitive_type_table.put(&entry->name, entry); - } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid); entry->type_ref = LLVMVoidType(); -- cgit v1.2.3 From fd75e73ee9818f12fd81d8fdb3cb949c492d664a Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 27 Jun 2018 16:20:04 +0200 Subject: add f16 type Add support for half-precision floating point operations. Introduce `__extendhfsf2` and `__truncsfhf2` in std/special/compiler_rt. Add `__gnu_h2f_ieee` and `__gnu_f2h_ieee` as aliases that are used in Windows builds. The logic in std/special/compiler_rt/extendXfYf2.zig has been reworked and can now operate on 16 bits floating point types. `extendXfYf2()` and `truncXfYf2()` are marked `inline` to work around a not entirely understood stack alignment issue on Windows when calling the f16 versions of the builtins. closes #1122 --- CMakeLists.txt | 16 +++ src/all_types.hpp | 2 + src/analyze.cpp | 15 +++ src/bigfloat.cpp | 8 ++ src/bigfloat.hpp | 2 + src/codegen.cpp | 4 + src/ir.cpp | 151 ++++++++++++++++++++++++++- src/util.hpp | 19 ++++ std/special/compiler_rt/extendXfYf2.zig | 56 +++++----- std/special/compiler_rt/extendXfYf2_test.zig | 46 ++++++++ std/special/compiler_rt/index.zig | 5 + std/special/compiler_rt/truncXfYf2.zig | 111 ++++++++++++++++++++ std/special/compiler_rt/truncXfYf2_test.zig | 64 ++++++++++++ test/cases/cast.zig | 28 ++++- test/cases/math.zig | 12 ++- test/cases/misc.zig | 1 + 16 files changed, 505 insertions(+), 35 deletions(-) create mode 100644 std/special/compiler_rt/truncXfYf2.zig create mode 100644 std/special/compiler_rt/truncXfYf2_test.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 99de2328d2..789da4a8a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,12 +261,15 @@ endif() set(EMBEDDED_SOFTFLOAT_SOURCES "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/f128M_isSignalingNaN.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF128M.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF16UI.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF32UI.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF64UI.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f128MToCommonNaN.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f16UIToCommonNaN.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f32UIToCommonNaN.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f64UIToCommonNaN.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNF128M.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNF16UI.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/softfloat_raiseFlags.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_add.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_div.c" @@ -293,8 +296,20 @@ set(EMBEDDED_SOFTFLOAT_SOURCES "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui32_r_minMag.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui64.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui64_r_minMag.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_add.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_div.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_eq.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_lt.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_mul.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_rem.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_roundToInt.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_sqrt.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_sub.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_f128M.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_f64.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f32_to_f128M.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_f128M.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_f16.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_add256M.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addCarryM.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addComplCarryM.c" @@ -572,6 +587,7 @@ set(ZIG_STD_FILES "special/compiler_rt/floatuntidf.zig" "special/compiler_rt/muloti4.zig" "special/compiler_rt/index.zig" + "special/compiler_rt/truncXfYf2.zig" "special/compiler_rt/udivmod.zig" "special/compiler_rt/udivmoddi4.zig" "special/compiler_rt/udivmodti4.zig" diff --git a/src/all_types.hpp b/src/all_types.hpp index 019dcb182e..5d449491c8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -258,6 +258,7 @@ struct ConstExprValue { // populated if special == ConstValSpecialStatic BigInt x_bigint; BigFloat x_bigfloat; + float16_t x_f16; float x_f32; double x_f64; float128_t x_f128; @@ -1598,6 +1599,7 @@ struct CodeGen { TypeTableEntry *entry_i128; TypeTableEntry *entry_isize; TypeTableEntry *entry_usize; + TypeTableEntry *entry_f16; TypeTableEntry *entry_f32; TypeTableEntry *entry_f64; TypeTableEntry *entry_f128; diff --git a/src/analyze.cpp b/src/analyze.cpp index c018ee4e92..25cc1c79d0 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4668,6 +4668,13 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { } case TypeTableEntryIdFloat: switch (const_val->type->data.floating.bit_count) { + case 16: + { + uint16_t result; + static_assert(sizeof(result) == sizeof(const_val->data.x_f16), ""); + memcpy(&result, &const_val->data.x_f16, sizeof(result)); + return result * 65537u; + } case 32: { uint32_t result; @@ -5128,6 +5135,9 @@ void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double va bigfloat_init_64(&const_val->data.x_bigfloat, value); } else if (type->id == TypeTableEntryIdFloat) { switch (type->data.floating.bit_count) { + case 16: + const_val->data.x_f16 = zig_double_to_f16(value); + break; case 32: const_val->data.x_f32 = value; break; @@ -5441,6 +5451,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { case TypeTableEntryIdFloat: assert(a->type->data.floating.bit_count == b->type->data.floating.bit_count); switch (a->type->data.floating.bit_count) { + case 16: + return f16_eq(a->data.x_f16, b->data.x_f16); case 32: return a->data.x_f32 == b->data.x_f32; case 64: @@ -5614,6 +5626,9 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { return; case TypeTableEntryIdFloat: switch (type_entry->data.floating.bit_count) { + case 16: + buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16)); + return; case 32: buf_appendf(buf, "%f", const_val->data.x_f32); return; diff --git a/src/bigfloat.cpp b/src/bigfloat.cpp index dcb6db61db..cc442fa3b7 100644 --- a/src/bigfloat.cpp +++ b/src/bigfloat.cpp @@ -18,6 +18,10 @@ void bigfloat_init_128(BigFloat *dest, float128_t x) { dest->value = x; } +void bigfloat_init_16(BigFloat *dest, float16_t x) { + f16_to_f128M(x, &dest->value); +} + void bigfloat_init_32(BigFloat *dest, float x) { float32_t f32_val; memcpy(&f32_val, &x, sizeof(float)); @@ -146,6 +150,10 @@ Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2) { } } +float16_t bigfloat_to_f16(const BigFloat *bigfloat) { + return f128M_to_f16(&bigfloat->value); +} + float bigfloat_to_f32(const BigFloat *bigfloat) { float32_t f32_value = f128M_to_f32(&bigfloat->value); float result; diff --git a/src/bigfloat.hpp b/src/bigfloat.hpp index e212c30c87..c6ae567945 100644 --- a/src/bigfloat.hpp +++ b/src/bigfloat.hpp @@ -22,6 +22,7 @@ struct BigFloat { struct Buf; +void bigfloat_init_16(BigFloat *dest, float16_t x); void bigfloat_init_32(BigFloat *dest, float x); void bigfloat_init_64(BigFloat *dest, double x); void bigfloat_init_128(BigFloat *dest, float128_t x); @@ -29,6 +30,7 @@ void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x); void bigfloat_init_bigint(BigFloat *dest, const BigInt *op); int bigfloat_init_buf_base10(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len); +float16_t bigfloat_to_f16(const BigFloat *bigfloat); float bigfloat_to_f32(const BigFloat *bigfloat); double bigfloat_to_f64(const BigFloat *bigfloat); float128_t bigfloat_to_f128(const BigFloat *bigfloat); diff --git a/src/codegen.cpp b/src/codegen.cpp index abec5a8ec7..4419f4fc84 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -17,6 +17,7 @@ #include "os.hpp" #include "translate_c.hpp" #include "target.hpp" +#include "util.hpp" #include "zig_llvm.h" #include @@ -5211,6 +5212,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c const_val->data.x_err_set->value, false); case TypeTableEntryIdFloat: switch (type_entry->data.floating.bit_count) { + case 16: + return LLVMConstReal(type_entry->type_ref, zig_f16_to_double(const_val->data.x_f16)); case 32: return LLVMConstReal(type_entry->type_ref, const_val->data.x_f32); case 64: @@ -6195,6 +6198,7 @@ static void define_builtin_types(CodeGen *g) { *field = entry; g->primitive_type_table.put(&entry->name, entry); }; + add_fp_entry(g, "f16", 16, LLVMHalfType(), &g->builtin_types.entry_f16); add_fp_entry(g, "f32", 32, LLVMFloatType(), &g->builtin_types.entry_f32); add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64); add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128); diff --git a/src/ir.cpp b/src/ir.cpp index 76178f2437..694f912145 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11,9 +11,10 @@ #include "ir.hpp" #include "ir_print.hpp" #include "os.hpp" -#include "translate_c.hpp" #include "range_set.hpp" #include "softfloat.hpp" +#include "translate_c.hpp" +#include "util.hpp" struct IrExecContext { ConstExprValue *mem_slot_list; @@ -7238,6 +7239,11 @@ static bool float_has_fraction(ConstExprValue *const_val) { return bigfloat_has_fraction(&const_val->data.x_bigfloat); } else if (const_val->type->id == TypeTableEntryIdFloat) { switch (const_val->type->data.floating.bit_count) { + case 16: + { + float16_t floored = f16_roundToInt(const_val->data.x_f16, softfloat_round_minMag, false); + return !f16_eq(floored, const_val->data.x_f16); + } case 32: return floorf(const_val->data.x_f32) != const_val->data.x_f32; case 64: @@ -7261,6 +7267,9 @@ static void float_append_buf(Buf *buf, ConstExprValue *const_val) { bigfloat_append_buf(buf, &const_val->data.x_bigfloat); } else if (const_val->type->id == TypeTableEntryIdFloat) { switch (const_val->type->data.floating.bit_count) { + case 16: + buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16)); + break; case 32: buf_appendf(buf, "%f", const_val->data.x_f32); break; @@ -7296,6 +7305,17 @@ static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) { bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat); } else if (const_val->type->id == TypeTableEntryIdFloat) { switch (const_val->type->data.floating.bit_count) { + case 16: + { + double x = zig_f16_to_double(const_val->data.x_f16); + if (x >= 0) { + bigint_init_unsigned(bigint, (uint64_t)x); + } else { + bigint_init_unsigned(bigint, (uint64_t)-x); + bigint->is_negative = true; + } + break; + } case 32: if (const_val->data.x_f32 >= 0) { bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f32)); @@ -7332,6 +7352,9 @@ static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = bigfloat_to_f16(bigfloat); + break; case 32: dest_val->data.x_f32 = bigfloat_to_f32(bigfloat); break; @@ -7349,11 +7372,39 @@ static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { } } +static void float_init_f16(ConstExprValue *dest_val, float16_t x) { + if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { + bigfloat_init_16(&dest_val->data.x_bigfloat, x); + } else if (dest_val->type->id == TypeTableEntryIdFloat) { + switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = x; + break; + case 32: + dest_val->data.x_f32 = zig_f16_to_double(x); + break; + case 64: + dest_val->data.x_f64 = zig_f16_to_double(x); + break; + case 128: + f16_to_f128M(x, &dest_val->data.x_f128); + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + static void float_init_f32(ConstExprValue *dest_val, float x) { if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_32(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = zig_double_to_f16(x); + break; case 32: dest_val->data.x_f32 = x; break; @@ -7380,6 +7431,9 @@ static void float_init_f64(ConstExprValue *dest_val, double x) { bigfloat_init_64(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = zig_double_to_f16(x); + break; case 32: dest_val->data.x_f32 = x; break; @@ -7406,6 +7460,9 @@ static void float_init_f128(ConstExprValue *dest_val, float128_t x) { bigfloat_init_128(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = f128M_to_f16(&x); + break; case 32: { float32_t f32_val = f128M_to_f32(&x); @@ -7436,6 +7493,9 @@ static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) float_init_bigfloat(dest_val, &src_val->data.x_bigfloat); } else if (src_val->type->id == TypeTableEntryIdFloat) { switch (src_val->type->data.floating.bit_count) { + case 16: + float_init_f16(dest_val, src_val->data.x_f16); + break; case 32: float_init_f32(dest_val, src_val->data.x_f32); break; @@ -7459,6 +7519,14 @@ static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) { return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + if (f16_lt(op1->data.x_f16, op2->data.x_f16)) { + return CmpLT; + } else if (f16_lt(op2->data.x_f16, op1->data.x_f16)) { + return CmpGT; + } else { + return CmpEQ; + } case 32: if (op1->data.x_f32 > op2->data.x_f32) { return CmpGT; @@ -7496,6 +7564,17 @@ static Cmp float_cmp_zero(ConstExprValue *op) { return bigfloat_cmp_zero(&op->data.x_bigfloat); } else if (op->type->id == TypeTableEntryIdFloat) { switch (op->type->data.floating.bit_count) { + case 16: + { + const float16_t zero = zig_double_to_f16(0); + if (f16_lt(op->data.x_f16, zero)) { + return CmpLT; + } else if (f16_lt(zero, op->data.x_f16)) { + return CmpGT; + } else { + return CmpEQ; + } + } case 32: if (op->data.x_f32 < 0.0) { return CmpLT; @@ -7537,6 +7616,9 @@ static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_add(op1->data.x_f16, op2->data.x_f16); + return; case 32: out_val->data.x_f32 = op1->data.x_f32 + op2->data.x_f32; return; @@ -7561,6 +7643,9 @@ static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_sub(op1->data.x_f16, op2->data.x_f16); + return; case 32: out_val->data.x_f32 = op1->data.x_f32 - op2->data.x_f32; return; @@ -7585,6 +7670,9 @@ static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_mul(op1->data.x_f16, op2->data.x_f16); + return; case 32: out_val->data.x_f32 = op1->data.x_f32 * op2->data.x_f32; return; @@ -7609,6 +7697,9 @@ static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); + return; case 32: out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; return; @@ -7633,6 +7724,19 @@ static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstE bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + { + double a = zig_f16_to_double(op1->data.x_f16); + double b = zig_f16_to_double(op2->data.x_f16); + double c = a / b; + if (c >= 0.0) { + c = floor(c); + } else { + c = ceil(c); + } + out_val->data.x_f16 = zig_double_to_f16(c); + return; + } case 32: out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; if (out_val->data.x_f32 >= 0.0) { @@ -7668,6 +7772,10 @@ static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstE bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); + out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_min, false); + return; case 32: out_val->data.x_f32 = floorf(op1->data.x_f32 / op2->data.x_f32); return; @@ -7693,6 +7801,9 @@ static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_rem(op1->data.x_f16, op2->data.x_f16); + return; case 32: out_val->data.x_f32 = fmodf(op1->data.x_f32, op2->data.x_f32); return; @@ -7710,6 +7821,16 @@ static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal } } +// c = a - b * trunc(a / b) +static float16_t zig_f16_mod(float16_t a, float16_t b) { + float16_t c; + c = f16_div(a, b); + c = f16_roundToInt(c, softfloat_round_min, true); + c = f16_mul(b, c); + c = f16_sub(a, c); + return c; +} + // c = a - b * trunc(a / b) static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* c) { f128M_div(a, b, c); @@ -7725,6 +7846,9 @@ static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = zig_f16_mod(op1->data.x_f16, op2->data.x_f16); + return; case 32: out_val->data.x_f32 = fmodf(fmodf(op1->data.x_f32, op2->data.x_f32) + op2->data.x_f32, op2->data.x_f32); return; @@ -7748,6 +7872,12 @@ static void float_negate(ConstExprValue *out_val, ConstExprValue *op) { bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat); } else if (op->type->id == TypeTableEntryIdFloat) { switch (op->type->data.floating.bit_count) { + case 16: + { + const float16_t zero = zig_double_to_f16(0); + out_val->data.x_f16 = f16_sub(zero, op->data.x_f16); + return; + } case 32: out_val->data.x_f32 = -op->data.x_f32; return; @@ -7770,6 +7900,9 @@ static void float_negate(ConstExprValue *out_val, ConstExprValue *op) { void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) { if (op->type->id == TypeTableEntryIdFloat) { switch (op->type->data.floating.bit_count) { + case 16: + memcpy(buf, &op->data.x_f16, 2); // TODO wrong when compiler is big endian + return; case 32: memcpy(buf, &op->data.x_f32, 4); // TODO wrong when compiler is big endian return; @@ -7790,6 +7923,9 @@ void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) { void float_read_ieee597(ConstExprValue *val, uint8_t *buf, bool is_big_endian) { if (val->type->id == TypeTableEntryIdFloat) { switch (val->type->data.floating.bit_count) { + case 16: + memcpy(&val->data.x_f16, buf, 2); // TODO wrong when compiler is big endian + return; case 32: memcpy(&val->data.x_f32, buf, 4); // TODO wrong when compiler is big endian return; @@ -8817,6 +8953,9 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ if (other_val->type->id == TypeTableEntryIdComptimeFloat) { assert(new_type->id == TypeTableEntryIdFloat); switch (new_type->data.floating.bit_count) { + case 16: + const_val->data.x_f16 = bigfloat_to_f16(&other_val->data.x_bigfloat); + break; case 32: const_val->data.x_f32 = bigfloat_to_f32(&other_val->data.x_bigfloat); break; @@ -8847,6 +8986,9 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ BigFloat bigfloat; bigfloat_init_bigint(&bigfloat, &other_val->data.x_bigint); switch (new_type->data.floating.bit_count) { + case 16: + const_val->data.x_f16 = bigfloat_to_f16(&bigfloat); + break; case 32: const_val->data.x_f32 = bigfloat_to_f32(&bigfloat); break; @@ -20104,6 +20246,9 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat); } else if (float_type->id == TypeTableEntryIdFloat) { switch (float_type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_sqrt(val->data.x_f16); + break; case 32: out_val->data.x_f32 = sqrtf(val->data.x_f32); break; @@ -20124,7 +20269,9 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction } assert(float_type->id == TypeTableEntryIdFloat); - if (float_type->data.floating.bit_count != 32 && float_type->data.floating.bit_count != 64) { + if (float_type->data.floating.bit_count != 16 && + float_type->data.floating.bit_count != 32 && + float_type->data.floating.bit_count != 64) { ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name))); return ira->codegen->builtin_types.entry_invalid; } diff --git a/src/util.hpp b/src/util.hpp index 52baab7ace..b0402137bd 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -31,6 +31,8 @@ #endif +#include "softfloat.hpp" + #define BREAKPOINT __asm("int $0x03") ATTRIBUTE_COLD @@ -165,4 +167,21 @@ static inline uint8_t log2_u64(uint64_t x) { return (63 - clzll(x)); } +static inline float16_t zig_double_to_f16(double x) { + float64_t y; + static_assert(sizeof(x) == sizeof(y), ""); + memcpy(&y, &x, sizeof(x)); + return f64_to_f16(y); +} + + +// Return value is safe to coerce to float even when |x| is NaN or Infinity. +static inline double zig_f16_to_double(float16_t x) { + float64_t y = f16_to_f64(x); + double z; + static_assert(sizeof(y) == sizeof(z), ""); + memcpy(&z, &y, sizeof(y)); + return z; +} + #endif diff --git a/std/special/compiler_rt/extendXfYf2.zig b/std/special/compiler_rt/extendXfYf2.zig index 6fa8cf4654..099e27b74a 100644 --- a/std/special/compiler_rt/extendXfYf2.zig +++ b/std/special/compiler_rt/extendXfYf2.zig @@ -10,9 +10,13 @@ pub extern fn __extendsftf2(a: f32) f128 { return extendXfYf2(f128, f32, a); } +pub extern fn __extendhfsf2(a: u16) f32 { + return extendXfYf2(f32, f16, @bitCast(f16, a)); +} + const CHAR_BIT = 8; -pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { +inline fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits); const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits); const srcSigBits = std.math.floatMantissaBits(src_t); @@ -22,22 +26,22 @@ pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { // Various constants whose values follow from the type parameters. // Any reasonable optimizer will fold and propagate all of these. - const srcBits: i32 = @sizeOf(src_t) * CHAR_BIT; - const srcExpBits: i32 = srcBits - srcSigBits - 1; - const srcInfExp: i32 = (1 << srcExpBits) - 1; - const srcExpBias: i32 = srcInfExp >> 1; + const srcBits = @sizeOf(src_t) * CHAR_BIT; + const srcExpBits = srcBits - srcSigBits - 1; + const srcInfExp = (1 << srcExpBits) - 1; + const srcExpBias = srcInfExp >> 1; - const srcMinNormal: src_rep_t = src_rep_t(1) << srcSigBits; - const srcInfinity: src_rep_t = src_rep_t(@bitCast(u32, srcInfExp)) << srcSigBits; - const srcSignMask: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits +% srcExpBits); - const srcAbsMask: src_rep_t = srcSignMask -% 1; - const srcQNaN: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits -% 1); - const srcNaNCode: src_rep_t = srcQNaN -% 1; + const srcMinNormal = 1 << srcSigBits; + const srcInfinity = srcInfExp << srcSigBits; + const srcSignMask = 1 << (srcSigBits + srcExpBits); + const srcAbsMask = srcSignMask - 1; + const srcQNaN = 1 << (srcSigBits - 1); + const srcNaNCode = srcQNaN - 1; - const dstBits: i32 = @sizeOf(dst_t) * CHAR_BIT; - const dstExpBits: i32 = dstBits - dstSigBits - 1; - const dstInfExp: i32 = (1 << dstExpBits) - 1; - const dstExpBias: i32 = dstInfExp >> 1; + const dstBits = @sizeOf(dst_t) * CHAR_BIT; + const dstExpBits = dstBits - dstSigBits - 1; + const dstInfExp = (1 << dstExpBits) - 1; + const dstExpBias = dstInfExp >> 1; const dstMinNormal: dst_rep_t = dst_rep_t(1) << dstSigBits; @@ -47,38 +51,36 @@ pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { const sign: src_rep_t = aRep & srcSignMask; var absResult: dst_rep_t = undefined; - // If @sizeOf(src_rep_t) < @sizeOf(int), the subtraction result is promoted - // to (signed) int. To avoid that, explicitly cast to src_rep_t. - if ((src_rep_t)(aAbs -% srcMinNormal) < srcInfinity -% srcMinNormal) { + if (aAbs -% srcMinNormal < srcInfinity - srcMinNormal) { // a is a normal number. // Extend to the destination type by shifting the significand and // exponent into the proper position and rebiasing the exponent. - absResult = dst_rep_t(aAbs) << (dstSigBits -% srcSigBits); - absResult += dst_rep_t(@bitCast(u32, dstExpBias -% srcExpBias)) << dstSigBits; + absResult = dst_rep_t(aAbs) << (dstSigBits - srcSigBits); + absResult += (dstExpBias - srcExpBias) << dstSigBits; } else if (aAbs >= srcInfinity) { // a is NaN or infinity. // Conjure the result by beginning with infinity, then setting the qNaN // bit (if needed) and right-aligning the rest of the trailing NaN // payload field. - absResult = dst_rep_t(@bitCast(u32, dstInfExp)) << dstSigBits; - absResult |= (dst_rep_t)(aAbs & srcQNaN) << (dstSigBits - srcSigBits); - absResult |= (dst_rep_t)(aAbs & srcNaNCode) << (dstSigBits - srcSigBits); + absResult = dstInfExp << dstSigBits; + absResult |= dst_rep_t(aAbs & srcQNaN) << (dstSigBits - srcSigBits); + absResult |= dst_rep_t(aAbs & srcNaNCode) << (dstSigBits - srcSigBits); } else if (aAbs != 0) { // a is denormal. // renormalize the significand and clear the leading bit, then insert // the correct adjusted exponent in the destination type. - const scale: i32 = @clz(aAbs) - @clz(srcMinNormal); + const scale: u32 = @clz(aAbs) - @clz(src_rep_t(srcMinNormal)); absResult = dst_rep_t(aAbs) << @intCast(DstShift, dstSigBits - srcSigBits + scale); absResult ^= dstMinNormal; - const resultExponent: i32 = dstExpBias - srcExpBias - scale + 1; - absResult |= dst_rep_t(@bitCast(u32, resultExponent)) << @intCast(DstShift, dstSigBits); + const resultExponent: u32 = dstExpBias - srcExpBias - scale + 1; + absResult |= @intCast(dst_rep_t, resultExponent) << dstSigBits; } else { // a is zero. absResult = 0; } // Apply the signbit to (dst_t)abs(a). - const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << @intCast(DstShift, dstBits - srcBits); + const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << (dstBits - srcBits); return @bitCast(dst_t, result); } diff --git a/std/special/compiler_rt/extendXfYf2_test.zig b/std/special/compiler_rt/extendXfYf2_test.zig index 84fb410fbb..0168de12a5 100644 --- a/std/special/compiler_rt/extendXfYf2_test.zig +++ b/std/special/compiler_rt/extendXfYf2_test.zig @@ -1,4 +1,5 @@ const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; +const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2; const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2; const assert = @import("std").debug.assert; @@ -24,6 +25,22 @@ fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void { @panic("__extenddftf2 test failure"); } +fn test__extendhfsf2(a: u16, expected: u32) void { + const x = __extendhfsf2(a); + const rep = @bitCast(u32, x); + + if (rep == expected) { + if (rep & 0x7fffffff > 0x7f800000) { + return; // NaN is always unequal. + } + if (x == @bitCast(f32, expected)) { + return; + } + } + + @panic("__extendhfsf2 test failure"); +} + fn test__extendsftf2(a: f32, expectedHi: u64, expectedLo: u64) void { const x = __extendsftf2(a); @@ -68,6 +85,35 @@ test "extenddftf2" { test__extenddftf2(0x1.edcba987654321fp-45, 0x3fd2edcba9876543, 0x2000000000000000); } +test "extendhfsf2" { + test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN + test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN + + test__extendhfsf2(0, 0); // 0 + test__extendhfsf2(0x8000, 0x80000000); // -0 + + test__extendhfsf2(0x7c00, 0x7f800000); // inf + test__extendhfsf2(0xfc00, 0xff800000); // -inf + + test__extendhfsf2(0x0001, 0x33800000); // denormal (min), 2**-24 + test__extendhfsf2(0x8001, 0xb3800000); // denormal (min), -2**-24 + + test__extendhfsf2(0x03ff, 0x387fc000); // denormal (max), 2**-14 - 2**-24 + test__extendhfsf2(0x83ff, 0xb87fc000); // denormal (max), -2**-14 + 2**-24 + + test__extendhfsf2(0x0400, 0x38800000); // normal (min), 2**-14 + test__extendhfsf2(0x8400, 0xb8800000); // normal (min), -2**-14 + + test__extendhfsf2(0x7bff, 0x477fe000); // normal (max), 65504 + test__extendhfsf2(0xfbff, 0xc77fe000); // normal (max), -65504 + + test__extendhfsf2(0x3c01, 0x3f802000); // normal, 1 + 2**-10 + test__extendhfsf2(0xbc01, 0xbf802000); // normal, -1 - 2**-10 + + test__extendhfsf2(0x3555, 0x3eaaa000); // normal, approx. 1/3 + test__extendhfsf2(0xb555, 0xbeaaa000); // normal, approx. -1/3 +} + test "extendsftf2" { // qNaN test__extendsftf2(makeQNaN32(), 0x7fff800000000000, 0x0); diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index c96e1587f8..fda8d9d8af 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -15,6 +15,8 @@ comptime { @export("__lttf2", @import("comparetf2.zig").__letf2, linkage); @export("__netf2", @import("comparetf2.zig").__letf2, linkage); @export("__gttf2", @import("comparetf2.zig").__getf2, linkage); + @export("__gnu_h2f_ieee", @import("extendXfYf2.zig").__extendhfsf2, linkage); + @export("__gnu_f2h_ieee", @import("truncXfYf2.zig").__truncsfhf2, linkage); } @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); @@ -22,6 +24,9 @@ comptime { @export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage); @export("__extenddftf2", @import("extendXfYf2.zig").__extenddftf2, linkage); @export("__extendsftf2", @import("extendXfYf2.zig").__extendsftf2, linkage); + @export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage); + + @export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage); @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage); @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage); diff --git a/std/special/compiler_rt/truncXfYf2.zig b/std/special/compiler_rt/truncXfYf2.zig new file mode 100644 index 0000000000..f08c6ae34f --- /dev/null +++ b/std/special/compiler_rt/truncXfYf2.zig @@ -0,0 +1,111 @@ +const std = @import("std"); + +pub extern fn __truncsfhf2(a: f32) u16 { + return @bitCast(u16, truncXfYf2(f16, f32, a)); +} + +const CHAR_BIT = 8; + +inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { + const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits); + const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits); + const srcSigBits = std.math.floatMantissaBits(src_t); + const dstSigBits = std.math.floatMantissaBits(dst_t); + const SrcShift = std.math.Log2Int(src_rep_t); + const DstShift = std.math.Log2Int(dst_rep_t); + + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const srcBits = @sizeOf(src_t) * CHAR_BIT; + const srcExpBits = srcBits - srcSigBits - 1; + const srcInfExp = (1 << srcExpBits) - 1; + const srcExpBias = srcInfExp >> 1; + + const srcMinNormal = 1 << srcSigBits; + const srcSignificandMask = srcMinNormal - 1; + const srcInfinity = srcInfExp << srcSigBits; + const srcSignMask = 1 << (srcSigBits + srcExpBits); + const srcAbsMask = srcSignMask - 1; + const roundMask = (1 << (srcSigBits - dstSigBits)) - 1; + const halfway = 1 << (srcSigBits - dstSigBits - 1); + const srcQNaN = 1 << (srcSigBits - 1); + const srcNaNCode = srcQNaN - 1; + + const dstBits = @sizeOf(dst_t) * CHAR_BIT; + const dstExpBits = dstBits - dstSigBits - 1; + const dstInfExp = (1 << dstExpBits) - 1; + const dstExpBias = dstInfExp >> 1; + + const underflowExponent = srcExpBias + 1 - dstExpBias; + const overflowExponent = srcExpBias + dstInfExp - dstExpBias; + const underflow = underflowExponent << srcSigBits; + const overflow = overflowExponent << srcSigBits; + + const dstQNaN = 1 << (dstSigBits - 1); + const dstNaNCode = dstQNaN - 1; + + // Break a into a sign and representation of the absolute value + const aRep: src_rep_t = @bitCast(src_rep_t, a); + const aAbs: src_rep_t = aRep & srcAbsMask; + const sign: src_rep_t = aRep & srcSignMask; + var absResult: dst_rep_t = undefined; + + if (aAbs -% underflow < aAbs -% overflow) { + // The exponent of a is within the range of normal numbers in the + // destination format. We can convert by simply right-shifting with + // rounding and adjusting the exponent. + absResult = @truncate(dst_rep_t, aAbs >> (srcSigBits - dstSigBits)); + absResult -%= dst_rep_t(srcExpBias - dstExpBias) << dstSigBits; + + const roundBits: src_rep_t = aAbs & roundMask; + if (roundBits > halfway) { + // Round to nearest + absResult += 1; + } else if (roundBits == halfway) { + // Ties to even + absResult += absResult & 1; + } + } else if (aAbs > srcInfinity) { + // a is NaN. + // Conjure the result by beginning with infinity, setting the qNaN + // bit and inserting the (truncated) trailing NaN field. + absResult = @intCast(dst_rep_t, dstInfExp) << dstSigBits; + absResult |= dstQNaN; + absResult |= @intCast(dst_rep_t, ((aAbs & srcNaNCode) >> (srcSigBits - dstSigBits)) & dstNaNCode); + } else if (aAbs >= overflow) { + // a overflows to infinity. + absResult = @intCast(dst_rep_t, dstInfExp) << dstSigBits; + } else { + // a underflows on conversion to the destination type or is an exact + // zero. The result may be a denormal or zero. Extract the exponent + // to get the shift amount for the denormalization. + const aExp: u32 = aAbs >> srcSigBits; + const shift: u32 = srcExpBias - dstExpBias - aExp + 1; + + const significand: src_rep_t = (aRep & srcSignificandMask) | srcMinNormal; + + // Right shift by the denormalization amount with sticky. + if (shift > srcSigBits) { + absResult = 0; + } else { + const sticky: src_rep_t = significand << @intCast(SrcShift, srcBits - shift); + const denormalizedSignificand: src_rep_t = significand >> @intCast(SrcShift, shift) | sticky; + absResult = @intCast(dst_rep_t, denormalizedSignificand >> (srcSigBits - dstSigBits)); + const roundBits: src_rep_t = denormalizedSignificand & roundMask; + if (roundBits > halfway) { + // Round to nearest + absResult += 1; + } else if (roundBits == halfway) { + // Ties to even + absResult += absResult & 1; + } + } + } + + const result: dst_rep_t align(@alignOf(dst_t)) = absResult | @truncate(dst_rep_t, sign >> @intCast(SrcShift, srcBits - dstBits)); + return @bitCast(dst_t, result); +} + +test "import truncXfYf2" { + _ = @import("truncXfYf2_test.zig"); +} diff --git a/std/special/compiler_rt/truncXfYf2_test.zig b/std/special/compiler_rt/truncXfYf2_test.zig new file mode 100644 index 0000000000..e4dae7b5b0 --- /dev/null +++ b/std/special/compiler_rt/truncXfYf2_test.zig @@ -0,0 +1,64 @@ +const __truncsfhf2 = @import("truncXfYf2.zig").__truncsfhf2; + +fn test__truncsfhf2(a: u32, expected: u16) void { + const actual = __truncsfhf2(@bitCast(f32, a)); + + if (actual == expected) { + return; + } + + @panic("__truncsfhf2 test failure"); +} + +test "truncsfhf2" { + test__truncsfhf2(0x7fc00000, 0x7e00); // qNaN + test__truncsfhf2(0x7fe00000, 0x7f00); // sNaN + + test__truncsfhf2(0, 0); // 0 + test__truncsfhf2(0x80000000, 0x8000); // -0 + + test__truncsfhf2(0x7f800000, 0x7c00); // inf + test__truncsfhf2(0xff800000, 0xfc00); // -inf + + test__truncsfhf2(0x477ff000, 0x7c00); // 65520 -> inf + test__truncsfhf2(0xc77ff000, 0xfc00); // -65520 -> -inf + + test__truncsfhf2(0x71cc3892, 0x7c00); // 0x1.987124876876324p+100 -> inf + test__truncsfhf2(0xf1cc3892, 0xfc00); // -0x1.987124876876324p+100 -> -inf + + test__truncsfhf2(0x38800000, 0x0400); // normal (min), 2**-14 + test__truncsfhf2(0xb8800000, 0x8400); // normal (min), -2**-14 + + test__truncsfhf2(0x477fe000, 0x7bff); // normal (max), 65504 + test__truncsfhf2(0xc77fe000, 0xfbff); // normal (max), -65504 + + test__truncsfhf2(0x477fe100, 0x7bff); // normal, 65505 -> 65504 + test__truncsfhf2(0xc77fe100, 0xfbff); // normal, -65505 -> -65504 + + test__truncsfhf2(0x477fef00, 0x7bff); // normal, 65519 -> 65504 + test__truncsfhf2(0xc77fef00, 0xfbff); // normal, -65519 -> -65504 + + test__truncsfhf2(0x3f802000, 0x3c01); // normal, 1 + 2**-10 + test__truncsfhf2(0xbf802000, 0xbc01); // normal, -1 - 2**-10 + + test__truncsfhf2(0x3eaaa000, 0x3555); // normal, approx. 1/3 + test__truncsfhf2(0xbeaaa000, 0xb555); // normal, approx. -1/3 + + test__truncsfhf2(0x40490fdb, 0x4248); // normal, 3.1415926535 + test__truncsfhf2(0xc0490fdb, 0xc248); // normal, -3.1415926535 + + test__truncsfhf2(0x45cc3892, 0x6e62); // normal, 0x1.987124876876324p+12 + + test__truncsfhf2(0x3f800000, 0x3c00); // normal, 1 + test__truncsfhf2(0x38800000, 0x0400); // normal, 0x1.0p-14 + + test__truncsfhf2(0x33800000, 0x0001); // denormal (min), 2**-24 + test__truncsfhf2(0xb3800000, 0x8001); // denormal (min), -2**-24 + + test__truncsfhf2(0x387fc000, 0x03ff); // denormal (max), 2**-14 - 2**-24 + test__truncsfhf2(0xb87fc000, 0x83ff); // denormal (max), -2**-14 + 2**-24 + + test__truncsfhf2(0x35800000, 0x0010); // denormal, 0x1.0p-20 + test__truncsfhf2(0x33280000, 0x0001); // denormal, 0x1.5p-25 -> 0x1.0p-24 + test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero +} diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 4209d87c1a..5688d90e11 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -350,13 +350,16 @@ fn testFloatToInts() void { assert(x == 10000); const y = @floatToInt(i32, f32(1e4)); assert(y == 10000); - expectFloatToInt(u8, 255.1, 255); - expectFloatToInt(i8, 127.2, 127); - expectFloatToInt(i8, -128.2, -128); + expectFloatToInt(f16, 255.1, u8, 255); + expectFloatToInt(f16, 127.2, i8, 127); + expectFloatToInt(f16, -128.2, i8, -128); + expectFloatToInt(f32, 255.1, u8, 255); + expectFloatToInt(f32, 127.2, i8, 127); + expectFloatToInt(f32, -128.2, i8, -128); } -fn expectFloatToInt(comptime T: type, f: f32, i: T) void { - assert(@floatToInt(T, f) == i); +fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void { + assert(@floatToInt(I, f) == i); } test "cast u128 to f128 and back" { @@ -418,6 +421,16 @@ test "@intCast comptime_int" { } test "@floatCast comptime_int and comptime_float" { + { + const result = @floatCast(f16, 1234); + assert(@typeOf(result) == f16); + assert(result == 1234.0); + } + { + const result = @floatCast(f16, 1234.0); + assert(@typeOf(result) == f16); + assert(result == 1234.0); + } { const result = @floatCast(f32, 1234); assert(@typeOf(result) == f32); @@ -431,6 +444,11 @@ test "@floatCast comptime_int and comptime_float" { } test "comptime_int @intToFloat" { + { + const result = @intToFloat(f16, 1234); + assert(@typeOf(result) == f16); + assert(result == 1234.0); + } { const result = @intToFloat(f32, 1234); assert(@typeOf(result) == f32); diff --git a/test/cases/math.zig b/test/cases/math.zig index 08388d3df8..1807e5a1b0 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -6,15 +6,20 @@ test "division" { } fn testDivision() void { assert(div(u32, 13, 3) == 4); + assert(div(f16, 1.0, 2.0) == 0.5); assert(div(f32, 1.0, 2.0) == 0.5); assert(divExact(u32, 55, 11) == 5); assert(divExact(i32, -55, 11) == -5); + assert(divExact(f16, 55.0, 11.0) == 5.0); + assert(divExact(f16, -55.0, 11.0) == -5.0); assert(divExact(f32, 55.0, 11.0) == 5.0); assert(divExact(f32, -55.0, 11.0) == -5.0); assert(divFloor(i32, 5, 3) == 1); assert(divFloor(i32, -5, 3) == -2); + assert(divFloor(f16, 5.0, 3.0) == 1.0); + assert(divFloor(f16, -5.0, 3.0) == -2.0); assert(divFloor(f32, 5.0, 3.0) == 1.0); assert(divFloor(f32, -5.0, 3.0) == -2.0); assert(divFloor(i32, -0x80000000, -2) == 0x40000000); @@ -24,6 +29,8 @@ fn testDivision() void { assert(divTrunc(i32, 5, 3) == 1); assert(divTrunc(i32, -5, 3) == -1); + assert(divTrunc(f16, 5.0, 3.0) == 1.0); + assert(divTrunc(f16, -5.0, 3.0) == -1.0); assert(divTrunc(f32, 5.0, 3.0) == 1.0); assert(divTrunc(f32, -5.0, 3.0) == -1.0); @@ -435,10 +442,11 @@ test "comptime float rem int" { } test "remainder division" { + comptime remdiv(f16); comptime remdiv(f32); comptime remdiv(f64); comptime remdiv(f128); - remdiv(f32); + remdiv(f16); remdiv(f64); remdiv(f128); } @@ -453,6 +461,8 @@ test "@sqrt" { comptime testSqrt(f64, 12.0); testSqrt(f32, 13.0); comptime testSqrt(f32, 13.0); + testSqrt(f16, 13.0); + comptime testSqrt(f16, 13.0); const x = 14.0; const y = x * x; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index d539f79a57..0f181a7b4e 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -53,6 +53,7 @@ test "@IntType builtin" { } test "floating point primitive bit counts" { + assert(f16.bit_count == 16); assert(f32.bit_count == 32); assert(f64.bit_count == 64); } -- cgit v1.2.3 From 440c1d52b4053c40c58c05116c8f6d9da8e35eed Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 27 Jun 2018 16:20:04 +0200 Subject: simplify comptime floating-point @divTrunc Replace a conditional ceil/floor call with an unconditional trunc call. --- src/ir.cpp | 29 +++++------------------------ test/cases/math.zig | 2 ++ 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 694f912145..6e424980f8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7725,33 +7725,14 @@ static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstE } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: - { - double a = zig_f16_to_double(op1->data.x_f16); - double b = zig_f16_to_double(op2->data.x_f16); - double c = a / b; - if (c >= 0.0) { - c = floor(c); - } else { - c = ceil(c); - } - out_val->data.x_f16 = zig_double_to_f16(c); - return; - } + out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); + out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_minMag, false); + return; case 32: - out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; - if (out_val->data.x_f32 >= 0.0) { - out_val->data.x_f32 = floorf(out_val->data.x_f32); - } else { - out_val->data.x_f32 = ceilf(out_val->data.x_f32); - } + out_val->data.x_f32 = truncf(op1->data.x_f32 / op2->data.x_f32); return; case 64: - out_val->data.x_f64 = op1->data.x_f64 / op2->data.x_f64; - if (out_val->data.x_f64 >= 0.0) { - out_val->data.x_f64 = floor(out_val->data.x_f64); - } else { - out_val->data.x_f64 = ceil(out_val->data.x_f64); - } + out_val->data.x_f64 = trunc(op1->data.x_f64 / op2->data.x_f64); return; case 128: f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); diff --git a/test/cases/math.zig b/test/cases/math.zig index 1807e5a1b0..5931c5de31 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -33,6 +33,8 @@ fn testDivision() void { assert(divTrunc(f16, -5.0, 3.0) == -1.0); assert(divTrunc(f32, 5.0, 3.0) == 1.0); assert(divTrunc(f32, -5.0, 3.0) == -1.0); + assert(divTrunc(f64, 5.0, 3.0) == 1.0); + assert(divTrunc(f64, -5.0, 3.0) == -1.0); comptime { assert( -- cgit v1.2.3 From 3e94347e6152dd9a88f4bceb46bbc245ed7cef98 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 27 Jun 2018 11:30:15 -0500 Subject: Fix up some std.rand syntax #1161 (#1162) * Fix old syntax in rand Ziggurat somehow did not get updated to latest syntax * Fix broken float casts f32 float casts somehow not updated to latest syntax --- std/rand/index.zig | 4 ++-- std/rand/ziggurat.zig | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/std/rand/index.zig b/std/rand/index.zig index 13694f4c09..7daa558f13 100644 --- a/std/rand/index.zig +++ b/std/rand/index.zig @@ -116,7 +116,7 @@ pub const Random = struct { pub fn floatNorm(r: *Random, comptime T: type) T { const value = ziggurat.next_f64(r, ziggurat.NormDist); switch (T) { - f32 => return f32(value), + f32 => return @floatCast(f32, value), f64 => return value, else => @compileError("unknown floating point type"), } @@ -128,7 +128,7 @@ pub const Random = struct { pub fn floatExp(r: *Random, comptime T: type) T { const value = ziggurat.next_f64(r, ziggurat.ExpDist); switch (T) { - f32 => return f32(value), + f32 => return @floatCast(f32, value), f64 => return value, else => @compileError("unknown floating point type"), } diff --git a/std/rand/ziggurat.zig b/std/rand/ziggurat.zig index 774d3bd52a..f7a1359f17 100644 --- a/std/rand/ziggurat.zig +++ b/std/rand/ziggurat.zig @@ -84,12 +84,12 @@ fn ZigTableGen( for (tables.x[2..256]) |*entry, i| { const last = tables.x[2 + i - 1]; - *entry = f_inv(v / last + f(last)); + entry.* = f_inv(v / last + f(last)); } tables.x[256] = 0; for (tables.f[0..]) |*entry, i| { - *entry = f(tables.x[i]); + entry.* = f(tables.x[i]); } return tables; @@ -160,3 +160,7 @@ test "ziggurant exp dist sanity" { _ = prng.random.floatExp(f64); } } + +test "ziggurat table gen" { + const table = NormDist; +} -- cgit v1.2.3 From 6f88ecc9b6ca4249912b7a9cffbad6d9b8819bc2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Jun 2018 12:59:12 -0400 Subject: add f16 to langref --- doc/langref.html.in | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 8e24a4be2c..dbb4ea9806 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -367,6 +367,11 @@ pub fn main() void { for ABI compatibility with C + + f16 + float + 16-bit floating point (10-bit mantissa) IEEE-754-2008 binary16 + f32 float @@ -654,6 +659,7 @@ fn divide(a: i32, b: i32) i32 { {#header_open|Floats#}

    Zig has the following floating point types:

      +
    • f16 - IEEE-754-2008 binary16
    • f32 - IEEE-754-2008 binary32
    • f64 - IEEE-754-2008 binary64
    • f128 - IEEE-754-2008 binary128
    • @@ -3671,10 +3677,11 @@ test "implicit unsigned integer to signed integer" { } test "float widening" { - var a: f32 = 12.34; - var b: f64 = a; - var c: f128 = b; - assert(c == a); + var a: f16 = 12.34; + var b: f32 = a; + var c: f64 = b; + var d: f128 = c; + assert(d == a); } {#code_end#} {#header_close#} -- cgit v1.2.3 From 19961c50e4db10fc4ada428928a7f5d1a2966da6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Jun 2018 13:15:55 -0400 Subject: fix comptime @tagName crashing sometimes closes #1118 --- src/analyze.cpp | 1 + src/ir.cpp | 3 +++ test/cases/eval.zig | 5 +++++ test/cases/widening.zig | 9 +++++---- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 25cc1c79d0..d5e69de1eb 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3728,6 +3728,7 @@ TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt } TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag) { + assert(enum_type->data.enumeration.zero_bits_known); for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *field = &enum_type->data.enumeration.fields[i]; if (bigint_cmp(&field->value, tag) == CmpEQ) { diff --git a/src/ir.cpp b/src/ir.cpp index 6e424980f8..9ba01d1411 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16053,6 +16053,9 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn assert(target->value.type->id == TypeTableEntryIdEnum); if (instr_is_comptime(target)) { + type_ensure_zero_bits_known(ira->codegen, target->value.type); + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint); ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 756ffe339a..83d2e80176 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -637,3 +637,8 @@ test "call method with comptime pass-by-non-copying-value self parameter" { var b = s.b(); assert(b == 2); } + +test "@tagName of @typeId" { + const str = @tagName(@typeId(u8)); + assert(std.mem.eql(u8, str, "Int")); +} diff --git a/test/cases/widening.zig b/test/cases/widening.zig index 18c12806d3..cf6ab4ca0f 100644 --- a/test/cases/widening.zig +++ b/test/cases/widening.zig @@ -19,8 +19,9 @@ test "implicit unsigned integer to signed integer" { } test "float widening" { - var a: f32 = 12.34; - var b: f64 = a; - var c: f128 = b; - assert(c == a); + var a: f16 = 12.34; + var b: f32 = a; + var c: f64 = b; + var d: f128 = c; + assert(d == a); } -- cgit v1.2.3 From 2fa588e81d60cfe319446bd0483c6bf296f40c40 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Jun 2018 18:45:21 -0400 Subject: fix coroutine accessing freed memory closes #1164 --- src/analyze.cpp | 2 +- src/ir.cpp | 17 ++++++++++++++--- test/cases/coroutines.zig | 41 ++++++++++++++++++++++++++++++++--------- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index d5e69de1eb..3c81d9ff9a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5583,7 +5583,7 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeT return; } case ConstPtrSpecialHardCodedAddr: - buf_appendf(buf, "(*%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name), + buf_appendf(buf, "(%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->name), const_val->data.x_ptr.data.hard_coded_addr.addr); return; case ConstPtrSpecialDiscard: diff --git a/src/ir.cpp b/src/ir.cpp index 9ba01d1411..98ed53d839 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7112,6 +7112,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr); } + // Before we destroy the coroutine frame, we need to load the target promise into + // a register or local variable which does not get spilled into the frame, + // otherwise llvm tries to access memory inside the destroyed frame. + IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, + irb->exec->await_handle_var_ptr, false); + IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block); @@ -7126,6 +7132,14 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec incoming_values[1] = const_bool_true; IrInstruction *resume_awaiter = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); + IrBasicBlock **merge_incoming_blocks = allocate(2); + IrInstruction **merge_incoming_values = allocate(2); + merge_incoming_blocks[0] = irb->exec->coro_final_cleanup_block; + merge_incoming_values[0] = ir_build_const_undefined(irb, scope, node); + merge_incoming_blocks[1] = irb->exec->coro_normal_final; + merge_incoming_values[1] = await_handle_in_block; + IrInstruction *awaiter_handle = ir_build_phi(irb, scope, node, 2, merge_incoming_blocks, merge_incoming_values); + Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, ImplicitAllocatorIdLocalVar); @@ -7152,9 +7166,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, resume_block); - IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, - irb->exec->await_handle_var_ptr, false); - IrInstruction *awaiter_handle = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); ir_build_coro_resume(irb, scope, node, awaiter_handle); ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false); } diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 4d2aa54a69..b3899b306b 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -5,7 +5,10 @@ const assert = std.debug.assert; var x: i32 = 1; test "create a coroutine and cancel it" { - const p = try async simpleAsyncFn(); + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = try async<&da.allocator> simpleAsyncFn(); comptime assert(@typeOf(p) == promise->void); cancel p; assert(x == 2); @@ -17,8 +20,11 @@ async fn simpleAsyncFn() void { } test "coroutine suspend, resume, cancel" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + seq('a'); - const p = try async testAsyncSeq(); + const p = try async<&da.allocator> testAsyncSeq(); seq('c'); resume p; seq('f'); @@ -43,7 +49,10 @@ fn seq(c: u8) void { } test "coroutine suspend with block" { - const p = try async testSuspendBlock(); + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = try async<&da.allocator> testSuspendBlock(); std.debug.assert(!result); resume a_promise; std.debug.assert(result); @@ -64,8 +73,11 @@ var await_a_promise: promise = undefined; var await_final_result: i32 = 0; test "coroutine await" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + await_seq('a'); - const p = async await_amain() catch unreachable; + const p = async<&da.allocator> await_amain() catch unreachable; await_seq('f'); resume await_a_promise; await_seq('i'); @@ -100,8 +112,11 @@ fn await_seq(c: u8) void { var early_final_result: i32 = 0; test "coroutine await early return" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + early_seq('a'); - const p = async early_amain() catch unreachable; + const p = async<&da.allocator> early_amain() catch unreachable; early_seq('f'); assert(early_final_result == 1234); assert(std.mem.eql(u8, early_points, "abcdef")); @@ -146,7 +161,9 @@ test "async function with dot syntax" { suspend; } }; - const p = try async S.foo(); + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = try async<&da.allocator> S.foo(); cancel p; assert(S.y == 2); } @@ -157,7 +174,9 @@ test "async fn pointer in a struct field" { bar: async<*std.mem.Allocator> fn (*i32) void, }; var foo = Foo{ .bar = simpleAsyncFn2 }; - const p = (async foo.bar(&data)) catch unreachable; + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = (async<&da.allocator> foo.bar(&data)) catch unreachable; assert(data == 2); cancel p; assert(data == 4); @@ -169,7 +188,9 @@ async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { } test "async fn with inferred error set" { - const p = (async failing()) catch unreachable; + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = (async<&da.allocator> failing()) catch unreachable; resume p; cancel p; } @@ -181,7 +202,9 @@ async fn failing() !void { test "error return trace across suspend points - early return" { const p = nonFailing(); resume p; - const p2 = try async printTrace(p); + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p2 = try async<&da.allocator> printTrace(p); cancel p2; } -- cgit v1.2.3 From 4a35d7eeebec3f345e2482bc189f07c19dcf6f8b Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 28 Jun 2018 20:12:03 +1200 Subject: Correct hex-float parsing Unblocks #495. --- src/tokenizer.cpp | 11 +++++++++-- test/cases/math.zig | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 2950b4eb49..f7f41af8a6 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -357,12 +357,19 @@ static void end_float_token(Tokenize *t) { // Mask the sign bit to 0 since always non-negative lex const uint64_t exp_mask = 0xffffull << exp_shift; - if (shift >= 64) { + // must be special-cased to avoid undefined behavior on shift == 64 + if (shift == 128) { + f_bits.repr[0] = 0; + f_bits.repr[1] = sig_bits[0]; + } else if (shift == 0) { + f_bits.repr[0] = sig_bits[0]; + f_bits.repr[1] = sig_bits[1]; + } else if (shift >= 64) { f_bits.repr[0] = 0; f_bits.repr[1] = sig_bits[0] << (shift - 64); } else { f_bits.repr[0] = sig_bits[0] << shift; - f_bits.repr[1] = ((sig_bits[1] << shift) | (sig_bits[0] >> (64 - shift))); + f_bits.repr[1] = (sig_bits[1] << shift) | (sig_bits[0] >> (64 - shift)); } f_bits.repr[1] &= ~exp_mask; diff --git a/test/cases/math.zig b/test/cases/math.zig index 5931c5de31..195ada15dd 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -296,6 +296,14 @@ test "quad hex float literal parsing in range" { const d = 0x1.edcbff8ad76ab5bf46463233214fp-435; } +test "quad hex float literal parsing accurate" { + const a: f128 = 0x1.1111222233334444555566667777p+0; + + // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. + const expected: u128 = 0x3fff1111222233334444555566667777; + assert(@bitCast(u128, a) == expected); +} + test "hex float literal within range" { const a = 0x1.0p16383; const b = 0x0.1p16387; -- cgit v1.2.3 From 3ec38b249446d1a51391e263fbb8303af52e6751 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Thu, 28 Jun 2018 10:34:37 +0200 Subject: Implement const_values_equal for array type * This allows arrays to be passed by value at comptime --- src/analyze.cpp | 15 +++++++++++++-- test/cases/array.zig | 8 ++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 5160a19e81..e9b74a9c26 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5458,8 +5458,19 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { case TypeTableEntryIdPointer: case TypeTableEntryIdFn: return const_values_equal_ptr(a, b); - case TypeTableEntryIdArray: - zig_panic("TODO"); + case TypeTableEntryIdArray: { + assert(a->type->data.array.len == b->type->data.array.len); + size_t len = a->type->data.array.len; + ConstExprValue *a_elems = a->data.x_array.s_none.elements; + ConstExprValue *b_elems = b->data.x_array.s_none.elements; + + for (size_t i = 0; i < len; ++i) { + if (!const_values_equal(&a_elems[i], &b_elems[i])) + return false; + } + + return true; + } case TypeTableEntryIdStruct: for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) { ConstExprValue *field_a = &a->data.x_struct.fields[i]; diff --git a/test/cases/array.zig b/test/cases/array.zig index b481261b4f..b72491bcc0 100644 --- a/test/cases/array.zig +++ b/test/cases/array.zig @@ -152,3 +152,11 @@ fn testImplicitCastSingleItemPtr() void { slice[0] += 1; assert(byte == 101); } + +fn testArrayByValAtComptime(b: [2]u8) u8 { return b[0]; } + +test "comptime evalutating function that takes array by value" { + const arr = []u8{0,1}; + _ = comptime testArrayByValAtComptime(arr); + _ = comptime testArrayByValAtComptime(arr); +} -- cgit v1.2.3 From b1128b18d5395d85f1c483d8b35e33c57be80722 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Fri, 29 Jun 2018 08:41:16 +0200 Subject: Assert that array is not ConstArraySpecialUndef in const_values_equal --- src/analyze.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/analyze.cpp b/src/analyze.cpp index e9b74a9c26..b3a302a1d4 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5460,6 +5460,9 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return const_values_equal_ptr(a, b); case TypeTableEntryIdArray: { assert(a->type->data.array.len == b->type->data.array.len); + assert(a->data.x_array.special != ConstArraySpecialUndef); + assert(b->data.x_array.special != ConstArraySpecialUndef); + size_t len = a->type->data.array.len; ConstExprValue *a_elems = a->data.x_array.s_none.elements; ConstExprValue *b_elems = b->data.x_array.s_none.elements; -- cgit v1.2.3 From 4c3f27ce1ea17b5236a022971ebace73a02b7c2b Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Fri, 29 Jun 2018 10:21:43 +0200 Subject: ir_resolve_const now checks recursivly for undef values --- src/analyze.cpp | 135 ++++++++++++++++++++++++++++++++++++++++++++++++ src/analyze.hpp | 1 + src/ir.cpp | 11 +++- test/compile_errors.zig | 15 ++++++ 4 files changed, 160 insertions(+), 2 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index b3a302a1d4..068ea48c0a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5288,6 +5288,141 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_ return const_val; } +bool contains_comptime_undefined_value(ConstExprValue *value) { + assert(value->special != ConstValSpecialRuntime); + if (value->special == ConstValSpecialUndef) + return true; + + switch (value->type->id) { + case TypeTableEntryIdInvalid: + zig_unreachable(); + + case TypeTableEntryIdPointer: { + ConstPtrValue *ptr = &value->data.x_ptr; + if (ptr->mut == ConstPtrMutRuntimeVar) + return false; + + switch (ptr->special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + return contains_comptime_undefined_value(ptr->data.ref.pointee); + case ConstPtrSpecialBaseArray: { + size_t index = ptr->data.base_array.elem_index; + ConstExprValue *arr = ptr->data.base_array.array_val; + if (arr->special == ConstValSpecialUndef) + return true; + if (arr->data.x_array.special == ConstArraySpecialUndef) + return true; + + return contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[index]); + } + case ConstPtrSpecialBaseStruct: { + size_t index = ptr->data.base_struct.field_index; + ConstExprValue *str = ptr->data.base_struct.struct_val; + if (str->special == ConstValSpecialUndef) + return true; + + return contains_comptime_undefined_value(&str->data.x_struct.fields[index]); + } + case ConstPtrSpecialFunction: // TODO: Can a fn ptr have an undefined value? + case ConstPtrSpecialDiscard: + case ConstPtrSpecialHardCodedAddr: + return false; + } + } + case TypeTableEntryIdArray: { + ConstArrayValue *arr = &value->data.x_array; + if (arr->special == ConstArraySpecialUndef) + return true; + + for (size_t i = 0; i < value->type->data.array.len; ++i) { + if (contains_comptime_undefined_value(&arr->s_none.elements[i])) + return true; + } + return false; + } + case TypeTableEntryIdStruct: { + ConstStructValue *str = &value->data.x_struct; + if (value->type->data.structure.is_slice) { + ConstExprValue *len = &str->fields[slice_len_index]; + ConstExprValue *ptr = &str->fields[slice_ptr_index]; + if (len->special == ConstValSpecialUndef) + return true; + if (ptr->special == ConstValSpecialUndef) + return true; + + switch (ptr->data.x_ptr.special) { + case ConstPtrSpecialRef: + return contains_comptime_undefined_value(ptr->data.x_ptr.data.ref.pointee); + case ConstPtrSpecialBaseArray: { + size_t offset = ptr->data.x_ptr.data.base_array.elem_index; + ConstExprValue *arr = ptr->data.x_ptr.data.base_array.array_val; + if (arr->special == ConstValSpecialUndef) + return true; + if (arr->data.x_array.special == ConstArraySpecialUndef) + return true; + + uint64_t slice_len = bigint_as_unsigned(&len->data.x_bigint); + for (size_t i = 0; i < slice_len; ++i) { + if (contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[i + offset])) + return true; + } + + return false; + } + case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialInvalid: + case ConstPtrSpecialFunction: + case ConstPtrSpecialDiscard: + case ConstPtrSpecialHardCodedAddr: + zig_unreachable(); + } + } + + for (size_t i = 0; i < value->type->data.structure.src_field_count; ++i) { + if (contains_comptime_undefined_value(&str->fields[i])) + return true; + } + return false; + } + case TypeTableEntryIdOptional: + if (value->data.x_optional == nullptr) + return false; + + return contains_comptime_undefined_value(value->data.x_optional); + case TypeTableEntryIdErrorUnion: + // TODO: Can error union error be undefined? + if (value->data.x_err_union.err != nullptr) + return false; + + return contains_comptime_undefined_value(value->data.x_err_union.payload); + case TypeTableEntryIdUnion: + return contains_comptime_undefined_value(value->data.x_union.payload); + + case TypeTableEntryIdArgTuple: + case TypeTableEntryIdVoid: + case TypeTableEntryIdBool: + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: + case TypeTableEntryIdUndefined: + case TypeTableEntryIdNull: + case TypeTableEntryIdErrorSet: + case TypeTableEntryIdEnum: + case TypeTableEntryIdFn: + case TypeTableEntryIdNamespace: + case TypeTableEntryIdBlock: + case TypeTableEntryIdBoundFn: + case TypeTableEntryIdMetaType: + case TypeTableEntryIdOpaque: + case TypeTableEntryIdPromise: + return false; + } + zig_unreachable(); +} void init_const_undefined(CodeGen *g, ConstExprValue *const_val) { TypeTableEntry *wanted_type = const_val->type; diff --git a/src/analyze.hpp b/src/analyze.hpp index 88e06b2390..100f85d4d9 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -93,6 +93,7 @@ void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry); void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry); void complete_enum(CodeGen *g, TypeTableEntry *enum_type); bool ir_get_var_is_comptime(VariableTableEntry *var); +bool contains_comptime_undefined_value(ConstExprValue *value); bool const_values_equal(ConstExprValue *a, ConstExprValue *b); void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max); void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigInt *bigint, bool is_max); diff --git a/src/ir.cpp b/src/ir.cpp index c6078e755d..2cce4a5044 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9148,8 +9148,15 @@ enum UndefAllowed { static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) { switch (value->value.special) { - case ConstValSpecialStatic: - return &value->value; + case ConstValSpecialStatic: { + ConstExprValue *res = &value->value; + if (undef_allowed == UndefBad && contains_comptime_undefined_value(res)) { + ir_add_error(ira, value, buf_sprintf("use of undefined value")); + return nullptr; + } + + return res; + } case ConstValSpecialRuntime: ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression")); return nullptr; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2247f0af96..8749f5b560 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -4124,4 +4124,19 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", ); + + cases.add( + "Trying to pass undefined array to function taking comptime array by value", + \\fn a(comptime b: [2]u8) u8 { return b[0]; } + \\ + \\test "" { + \\ const arr: [2]u8 = undefined; + \\ _ = a(arr); + \\} + , + ".tmp_source.zig:5:11: error: use of undefined value", + ); + + + } -- cgit v1.2.3 From 58b1692182dc2f8da5b535f59e9a89cfab10a7b6 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Fri, 29 Jun 2018 11:34:38 +0200 Subject: contains_comptime_undefined_value should not follow pointers --- src/analyze.cpp | 72 +-------------------------------------------------------- 1 file changed, 1 insertion(+), 71 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 068ea48c0a..4c200888d8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5296,41 +5296,6 @@ bool contains_comptime_undefined_value(ConstExprValue *value) { switch (value->type->id) { case TypeTableEntryIdInvalid: zig_unreachable(); - - case TypeTableEntryIdPointer: { - ConstPtrValue *ptr = &value->data.x_ptr; - if (ptr->mut == ConstPtrMutRuntimeVar) - return false; - - switch (ptr->special) { - case ConstPtrSpecialInvalid: - zig_unreachable(); - case ConstPtrSpecialRef: - return contains_comptime_undefined_value(ptr->data.ref.pointee); - case ConstPtrSpecialBaseArray: { - size_t index = ptr->data.base_array.elem_index; - ConstExprValue *arr = ptr->data.base_array.array_val; - if (arr->special == ConstValSpecialUndef) - return true; - if (arr->data.x_array.special == ConstArraySpecialUndef) - return true; - - return contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[index]); - } - case ConstPtrSpecialBaseStruct: { - size_t index = ptr->data.base_struct.field_index; - ConstExprValue *str = ptr->data.base_struct.struct_val; - if (str->special == ConstValSpecialUndef) - return true; - - return contains_comptime_undefined_value(&str->data.x_struct.fields[index]); - } - case ConstPtrSpecialFunction: // TODO: Can a fn ptr have an undefined value? - case ConstPtrSpecialDiscard: - case ConstPtrSpecialHardCodedAddr: - return false; - } - } case TypeTableEntryIdArray: { ConstArrayValue *arr = &value->data.x_array; if (arr->special == ConstArraySpecialUndef) @@ -5344,42 +5309,6 @@ bool contains_comptime_undefined_value(ConstExprValue *value) { } case TypeTableEntryIdStruct: { ConstStructValue *str = &value->data.x_struct; - if (value->type->data.structure.is_slice) { - ConstExprValue *len = &str->fields[slice_len_index]; - ConstExprValue *ptr = &str->fields[slice_ptr_index]; - if (len->special == ConstValSpecialUndef) - return true; - if (ptr->special == ConstValSpecialUndef) - return true; - - switch (ptr->data.x_ptr.special) { - case ConstPtrSpecialRef: - return contains_comptime_undefined_value(ptr->data.x_ptr.data.ref.pointee); - case ConstPtrSpecialBaseArray: { - size_t offset = ptr->data.x_ptr.data.base_array.elem_index; - ConstExprValue *arr = ptr->data.x_ptr.data.base_array.array_val; - if (arr->special == ConstValSpecialUndef) - return true; - if (arr->data.x_array.special == ConstArraySpecialUndef) - return true; - - uint64_t slice_len = bigint_as_unsigned(&len->data.x_bigint); - for (size_t i = 0; i < slice_len; ++i) { - if (contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[i + offset])) - return true; - } - - return false; - } - case ConstPtrSpecialBaseStruct: - case ConstPtrSpecialInvalid: - case ConstPtrSpecialFunction: - case ConstPtrSpecialDiscard: - case ConstPtrSpecialHardCodedAddr: - zig_unreachable(); - } - } - for (size_t i = 0; i < value->type->data.structure.src_field_count; ++i) { if (contains_comptime_undefined_value(&str->fields[i])) return true; @@ -5400,6 +5329,7 @@ bool contains_comptime_undefined_value(ConstExprValue *value) { case TypeTableEntryIdUnion: return contains_comptime_undefined_value(value->data.x_union.payload); + case TypeTableEntryIdPointer: case TypeTableEntryIdArgTuple: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: -- cgit v1.2.3 From 0874a5ba77a1d049a0e9e7f9f249605c109a731c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 29 Jun 2018 14:45:01 -0400 Subject: std.atomic.queue - document limitation and add MPSC queue --- CMakeLists.txt | 3 +- std/atomic/index.zig | 8 +- std/atomic/queue.zig | 139 ------------------------------ std/atomic/queue_mpmc.zig | 214 ++++++++++++++++++++++++++++++++++++++++++++++ std/atomic/queue_mpsc.zig | 143 +++++++++++++++++++++++++++++++ 5 files changed, 364 insertions(+), 143 deletions(-) delete mode 100644 std/atomic/queue.zig create mode 100644 std/atomic/queue_mpmc.zig create mode 100644 std/atomic/queue_mpsc.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 789da4a8a6..4838aeb797 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -431,7 +431,8 @@ set(ZIG_CPP_SOURCES set(ZIG_STD_FILES "array_list.zig" "atomic/index.zig" - "atomic/queue.zig" + "atomic/queue_mpmc.zig" + "atomic/queue_mpsc.zig" "atomic/stack.zig" "base64.zig" "buf_map.zig" diff --git a/std/atomic/index.zig b/std/atomic/index.zig index 9d556a6415..c0ea5be183 100644 --- a/std/atomic/index.zig +++ b/std/atomic/index.zig @@ -1,7 +1,9 @@ pub const Stack = @import("stack.zig").Stack; -pub const Queue = @import("queue.zig").Queue; +pub const QueueMpsc = @import("queue_mpsc.zig").QueueMpsc; +pub const QueueMpmc = @import("queue_mpmc.zig").QueueMpmc; test "std.atomic" { - _ = @import("stack.zig").Stack; - _ = @import("queue.zig").Queue; + _ = @import("stack.zig"); + _ = @import("queue_mpsc.zig"); + _ = @import("queue_mpmc.zig"); } diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig deleted file mode 100644 index 16dc9f6cc3..0000000000 --- a/std/atomic/queue.zig +++ /dev/null @@ -1,139 +0,0 @@ -const builtin = @import("builtin"); -const AtomicOrder = builtin.AtomicOrder; -const AtomicRmwOp = builtin.AtomicRmwOp; - -/// Many reader, many writer, non-allocating, thread-safe, lock-free -pub fn Queue(comptime T: type) type { - return struct { - head: *Node, - tail: *Node, - root: Node, - - pub const Self = this; - - pub const Node = struct { - next: ?*Node, - data: T, - }; - - // TODO: well defined copy elision: https://github.com/ziglang/zig/issues/287 - pub fn init(self: *Self) void { - self.root.next = null; - self.head = &self.root; - self.tail = &self.root; - } - - pub fn put(self: *Self, node: *Node) void { - node.next = null; - - const tail = @atomicRmw(*Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); - _ = @atomicRmw(?*Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); - } - - pub fn get(self: *Self) ?*Node { - var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst); - while (true) { - const node = head.next orelse return null; - head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return node; - } - } - }; -} - -const std = @import("std"); -const Context = struct { - allocator: *std.mem.Allocator, - queue: *Queue(i32), - put_sum: isize, - get_sum: isize, - get_count: usize, - puts_done: u8, // TODO make this a bool -}; - -// TODO add lazy evaluated build options and then put puts_per_thread behind -// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor -// CI we would use a less aggressive setting since at 1 core, while we still -// want this test to pass, we need a smaller value since there is so much thrashing -// we would also use a less aggressive setting when running in valgrind -const puts_per_thread = 500; -const put_thread_count = 3; - -test "std.atomic.queue" { - var direct_allocator = std.heap.DirectAllocator.init(); - defer direct_allocator.deinit(); - - var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024); - defer direct_allocator.allocator.free(plenty_of_memory); - - var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); - var a = &fixed_buffer_allocator.allocator; - - var queue: Queue(i32) = undefined; - queue.init(); - var context = Context{ - .allocator = a, - .queue = &queue, - .put_sum = 0, - .get_sum = 0, - .puts_done = 0, - .get_count = 0, - }; - - var putters: [put_thread_count]*std.os.Thread = undefined; - for (putters) |*t| { - t.* = try std.os.spawnThread(&context, startPuts); - } - var getters: [put_thread_count]*std.os.Thread = undefined; - for (getters) |*t| { - t.* = try std.os.spawnThread(&context, startGets); - } - - for (putters) |t| - t.wait(); - _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - for (getters) |t| - t.wait(); - - if (context.put_sum != context.get_sum) { - std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum); - } - - if (context.get_count != puts_per_thread * put_thread_count) { - std.debug.panic( - "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}", - context.get_count, - u32(puts_per_thread), - u32(put_thread_count), - ); - } -} - -fn startPuts(ctx: *Context) u8 { - var put_count: usize = puts_per_thread; - var r = std.rand.DefaultPrng.init(0xdeadbeef); - while (put_count != 0) : (put_count -= 1) { - std.os.time.sleep(0, 1); // let the os scheduler be our fuzz - const x = @bitCast(i32, r.random.scalar(u32)); - const node = ctx.allocator.create(Queue(i32).Node{ - .next = undefined, - .data = x, - }) catch unreachable; - ctx.queue.put(node); - _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); - } - return 0; -} - -fn startGets(ctx: *Context) u8 { - while (true) { - const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1; - - while (ctx.queue.get()) |node| { - std.os.time.sleep(0, 1); // let the os scheduler be our fuzz - _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); - _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); - } - - if (last) return 0; - } -} diff --git a/std/atomic/queue_mpmc.zig b/std/atomic/queue_mpmc.zig new file mode 100644 index 0000000000..7ffc9f9ccb --- /dev/null +++ b/std/atomic/queue_mpmc.zig @@ -0,0 +1,214 @@ +const builtin = @import("builtin"); +const AtomicOrder = builtin.AtomicOrder; +const AtomicRmwOp = builtin.AtomicRmwOp; + +/// Many producer, many consumer, non-allocating, thread-safe, lock-free +/// This implementation has a crippling limitation - it hangs onto node +/// memory for 1 extra get() and 1 extra put() operation - when get() returns a node, that +/// node must not be freed until both the next get() and the next put() completes. +pub fn QueueMpmc(comptime T: type) type { + return struct { + head: *Node, + tail: *Node, + root: Node, + + pub const Self = this; + + pub const Node = struct { + next: ?*Node, + data: T, + }; + + /// TODO: well defined copy elision: https://github.com/ziglang/zig/issues/287 + pub fn init(self: *Self) void { + self.root.next = null; + self.head = &self.root; + self.tail = &self.root; + } + + pub fn put(self: *Self, node: *Node) void { + node.next = null; + + const tail = @atomicRmw(*Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); + _ = @atomicRmw(?*Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); + } + + /// node must not be freed until both the next get() and the next put() complete + pub fn get(self: *Self) ?*Node { + var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst); + while (true) { + const node = head.next orelse return null; + head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return node; + } + } + + ///// This is a debug function that is not thread-safe. + pub fn dump(self: *Self) void { + std.debug.warn("head: "); + dumpRecursive(self.head, 0); + std.debug.warn("tail: "); + dumpRecursive(self.tail, 0); + } + + fn dumpRecursive(optional_node: ?*Node, indent: usize) void { + var stderr_file = std.io.getStdErr() catch return; + const stderr = &std.io.FileOutStream.init(&stderr_file).stream; + stderr.writeByteNTimes(' ', indent) catch return; + if (optional_node) |node| { + std.debug.warn("0x{x}={}\n", @ptrToInt(node), node.data); + dumpRecursive(node.next, indent + 1); + } else { + std.debug.warn("(null)\n"); + } + } + }; +} + +const std = @import("std"); +const assert = std.debug.assert; + +const Context = struct { + allocator: *std.mem.Allocator, + queue: *QueueMpmc(i32), + put_sum: isize, + get_sum: isize, + get_count: usize, + puts_done: u8, // TODO make this a bool +}; + +// TODO add lazy evaluated build options and then put puts_per_thread behind +// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor +// CI we would use a less aggressive setting since at 1 core, while we still +// want this test to pass, we need a smaller value since there is so much thrashing +// we would also use a less aggressive setting when running in valgrind +const puts_per_thread = 500; +const put_thread_count = 3; + +test "std.atomic.queue_mpmc" { + var direct_allocator = std.heap.DirectAllocator.init(); + defer direct_allocator.deinit(); + + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024); + defer direct_allocator.allocator.free(plenty_of_memory); + + var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); + var a = &fixed_buffer_allocator.allocator; + + var queue: QueueMpmc(i32) = undefined; + queue.init(); + var context = Context{ + .allocator = a, + .queue = &queue, + .put_sum = 0, + .get_sum = 0, + .puts_done = 0, + .get_count = 0, + }; + + var putters: [put_thread_count]*std.os.Thread = undefined; + for (putters) |*t| { + t.* = try std.os.spawnThread(&context, startPuts); + } + var getters: [put_thread_count]*std.os.Thread = undefined; + for (getters) |*t| { + t.* = try std.os.spawnThread(&context, startGets); + } + + for (putters) |t| + t.wait(); + _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + for (getters) |t| + t.wait(); + + if (context.put_sum != context.get_sum) { + std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum); + } + + if (context.get_count != puts_per_thread * put_thread_count) { + std.debug.panic( + "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}", + context.get_count, + u32(puts_per_thread), + u32(put_thread_count), + ); + } +} + +fn startPuts(ctx: *Context) u8 { + var put_count: usize = puts_per_thread; + var r = std.rand.DefaultPrng.init(0xdeadbeef); + while (put_count != 0) : (put_count -= 1) { + std.os.time.sleep(0, 1); // let the os scheduler be our fuzz + const x = @bitCast(i32, r.random.scalar(u32)); + const node = ctx.allocator.create(QueueMpmc(i32).Node{ + .next = undefined, + .data = x, + }) catch unreachable; + ctx.queue.put(node); + _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); + } + return 0; +} + +fn startGets(ctx: *Context) u8 { + while (true) { + const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1; + + while (ctx.queue.get()) |node| { + std.os.time.sleep(0, 1); // let the os scheduler be our fuzz + _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); + _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); + } + + if (last) return 0; + } +} + +test "std.atomic.queue_mpmc single-threaded" { + var queue: QueueMpmc(i32) = undefined; + queue.init(); + + var node_0 = QueueMpmc(i32).Node{ + .data = 0, + .next = undefined, + }; + queue.put(&node_0); + + var node_1 = QueueMpmc(i32).Node{ + .data = 1, + .next = undefined, + }; + queue.put(&node_1); + + assert(queue.get().?.data == 0); + + var node_2 = QueueMpmc(i32).Node{ + .data = 2, + .next = undefined, + }; + queue.put(&node_2); + + var node_3 = QueueMpmc(i32).Node{ + .data = 3, + .next = undefined, + }; + queue.put(&node_3); + + assert(queue.get().?.data == 1); + + assert(queue.get().?.data == 2); + + var node_4 = QueueMpmc(i32).Node{ + .data = 4, + .next = undefined, + }; + queue.put(&node_4); + + assert(queue.get().?.data == 3); + // if we were to set node_3.next to null here, it would cause this test + // to fail. this demonstrates the limitation of hanging on to extra memory. + + assert(queue.get().?.data == 4); + + assert(queue.get() == null); +} diff --git a/std/atomic/queue_mpsc.zig b/std/atomic/queue_mpsc.zig new file mode 100644 index 0000000000..66eb4573df --- /dev/null +++ b/std/atomic/queue_mpsc.zig @@ -0,0 +1,143 @@ +const std = @import("std"); +const assert = std.debug.assert; +const builtin = @import("builtin"); +const AtomicOrder = builtin.AtomicOrder; +const AtomicRmwOp = builtin.AtomicRmwOp; + +/// Many producer, single consumer, non-allocating, thread-safe, lock-free +pub fn QueueMpsc(comptime T: type) type { + return struct { + inboxes: [2]std.atomic.Stack(T), + outbox: std.atomic.Stack(T), + inbox_index: usize, + + pub const Self = this; + + pub const Node = std.atomic.Stack(T).Node; + + pub fn init() Self { + return Self{ + .inboxes = []std.atomic.Stack(T){ + std.atomic.Stack(T).init(), + std.atomic.Stack(T).init(), + }, + .outbox = std.atomic.Stack(T).init(), + .inbox_index = 0, + }; + } + + pub fn put(self: *Self, node: *Node) void { + const inbox_index = @atomicLoad(usize, &self.inbox_index, AtomicOrder.SeqCst); + const inbox = &self.inboxes[inbox_index]; + inbox.push(node); + } + + pub fn get(self: *Self) ?*Node { + if (self.outbox.pop()) |node| { + return node; + } + const prev_inbox_index = @atomicRmw(usize, &self.inbox_index, AtomicRmwOp.Xor, 0x1, AtomicOrder.SeqCst); + const prev_inbox = &self.inboxes[prev_inbox_index]; + while (prev_inbox.pop()) |node| { + self.outbox.push(node); + } + return self.outbox.pop(); + } + }; +} + +const Context = struct { + allocator: *std.mem.Allocator, + queue: *QueueMpsc(i32), + put_sum: isize, + get_sum: isize, + get_count: usize, + puts_done: u8, // TODO make this a bool +}; + +// TODO add lazy evaluated build options and then put puts_per_thread behind +// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor +// CI we would use a less aggressive setting since at 1 core, while we still +// want this test to pass, we need a smaller value since there is so much thrashing +// we would also use a less aggressive setting when running in valgrind +const puts_per_thread = 500; +const put_thread_count = 3; + +test "std.atomic.queue_mpsc" { + var direct_allocator = std.heap.DirectAllocator.init(); + defer direct_allocator.deinit(); + + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024); + defer direct_allocator.allocator.free(plenty_of_memory); + + var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); + var a = &fixed_buffer_allocator.allocator; + + var queue = QueueMpsc(i32).init(); + var context = Context{ + .allocator = a, + .queue = &queue, + .put_sum = 0, + .get_sum = 0, + .puts_done = 0, + .get_count = 0, + }; + + var putters: [put_thread_count]*std.os.Thread = undefined; + for (putters) |*t| { + t.* = try std.os.spawnThread(&context, startPuts); + } + var getters: [1]*std.os.Thread = undefined; + for (getters) |*t| { + t.* = try std.os.spawnThread(&context, startGets); + } + + for (putters) |t| + t.wait(); + _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + for (getters) |t| + t.wait(); + + if (context.put_sum != context.get_sum) { + std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum); + } + + if (context.get_count != puts_per_thread * put_thread_count) { + std.debug.panic( + "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}", + context.get_count, + u32(puts_per_thread), + u32(put_thread_count), + ); + } +} + +fn startPuts(ctx: *Context) u8 { + var put_count: usize = puts_per_thread; + var r = std.rand.DefaultPrng.init(0xdeadbeef); + while (put_count != 0) : (put_count -= 1) { + std.os.time.sleep(0, 1); // let the os scheduler be our fuzz + const x = @bitCast(i32, r.random.scalar(u32)); + const node = ctx.allocator.create(QueueMpsc(i32).Node{ + .next = undefined, + .data = x, + }) catch unreachable; + ctx.queue.put(node); + _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); + } + return 0; +} + +fn startGets(ctx: *Context) u8 { + while (true) { + const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1; + + while (ctx.queue.get()) |node| { + std.os.time.sleep(0, 1); // let the os scheduler be our fuzz + _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); + _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); + } + + if (last) return 0; + } +} -- cgit v1.2.3 From f1c56f7f225f2f3054abd8c9e6330a0f1e20b2a6 Mon Sep 17 00:00:00 2001 From: isaachier Date: Fri, 29 Jun 2018 14:52:25 -0400 Subject: Clarify reason implicit cast does not work for large RHS (#1168) * Clarify reason implicit cast does not work for large RHS --- src/ir.cpp | 20 ++++++++++++++++++++ test/compile_errors.zig | 12 ++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 98ed53d839..a450b6d14e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11432,6 +11432,26 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * } else { TypeTableEntry *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, op1->value.type->data.integral.bit_count - 1); + if (bin_op_instruction->op_id == IrBinOpBitShiftLeftLossy && + op2->value.type->id == TypeTableEntryIdComptimeInt) { + if (!bigint_fits_in_bits(&op2->value.data.x_bigint, + shift_amt_type->data.integral.bit_count, + op2->value.data.x_bigint.is_negative)) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &op2->value.data.x_bigint, 10); + ErrorMsg* msg = ir_add_error(ira, + &bin_op_instruction->base, + buf_sprintf("RHS of shift is too large for LHS type")); + add_error_note( + ira->codegen, + msg, + op2->source_node, + buf_sprintf("value %s cannot fit into type %s", + buf_ptr(val_buf), + buf_ptr(&shift_amt_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + } casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); if (casted_op2 == ira->codegen->invalid_instruction) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2247f0af96..cfe4a2ef5f 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1677,6 +1677,18 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:1:16: error: integer value 300 cannot be implicitly casted to type 'u8'", ); + cases.add( + "invalid shift amount error", + \\const x : u8 = 2; + \\fn f() u16 { + \\ return x << 8; + \\} + \\export fn entry() u16 { return f(); } + , + ".tmp_source.zig:3:14: error: RHS of shift is too large for LHS type", + ".tmp_source.zig:3:17: note: value 8 cannot fit into type u3", + ); + cases.add( "incompatible number literals", \\const x = 2 == 2.0; -- cgit v1.2.3 From 03f66825d6b6922da250cdb99c2996455541e0f9 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 29 Jun 2018 23:28:42 +0200 Subject: support --emit in 'test' command Support the `--emit` switch in `zig --emit asm test file.zig`. The command fails because no tests run (no executable is created) but it emits the requested file. That seems like a good tradeoff. --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 0fe12bb0cb..a409778a78 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -924,6 +924,8 @@ int main(int argc, char **argv) { codegen_print_timing_report(g, stdout); return EXIT_SUCCESS; } else if (cmd == CmdTest) { + codegen_set_emit_file_type(g, emit_file_type); + ZigTarget native; get_native_target(&native); -- cgit v1.2.3 From 61df5bc142253b0d33b77bceecfaef4c767e7feb Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 30 Jun 2018 01:44:54 +0200 Subject: add std.math f16 constants refs #1122 --- std/math/index.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/std/math/index.zig b/std/math/index.zig index 04133dc1dc..e987274f71 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -19,6 +19,12 @@ pub const f32_max = 3.40282346638528859812e+38; pub const f32_epsilon = 1.1920928955078125e-07; pub const f32_toint = 1.0 / f32_epsilon; +pub const f16_true_min = 0.000000059604644775390625; // 2**-24 +pub const f16_min = 0.00006103515625; // 2**-14 +pub const f16_max = 65504; +pub const f16_epsilon = 0.0009765625; // 2**-10 +pub const f16_toint = 1.0 / f16_epsilon; + pub const nan_u32 = u32(0x7F800001); pub const nan_f32 = @bitCast(f32, nan_u32); -- cgit v1.2.3 From 27b02413dc3dacc7d784fe84ff8ba6cb0361842d Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 30 Jun 2018 01:44:54 +0200 Subject: add std.math f16 nan support refs #1122 --- std/math/index.zig | 3 +++ std/math/isnan.zig | 6 ++++++ std/math/nan.zig | 2 ++ std/special/builtin.zig | 4 +++- std/special/compiler_rt/extendXfYf2_test.zig | 1 + 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/std/math/index.zig b/std/math/index.zig index e987274f71..df8271fbcf 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -25,6 +25,9 @@ pub const f16_max = 65504; pub const f16_epsilon = 0.0009765625; // 2**-10 pub const f16_toint = 1.0 / f16_epsilon; +pub const nan_u16 = u16(0x7C01); +pub const nan_f16 = @bitCast(f16, nan_u16); + pub const nan_u32 = u32(0x7F800001); pub const nan_f32 = @bitCast(f32, nan_u32); diff --git a/std/math/isnan.zig b/std/math/isnan.zig index 67971e3d0c..ef3002d8e1 100644 --- a/std/math/isnan.zig +++ b/std/math/isnan.zig @@ -5,6 +5,10 @@ const assert = std.debug.assert; pub fn isNan(x: var) bool { const T = @typeOf(x); switch (T) { + f16 => { + const bits = @bitCast(u16, x); + return (bits & 0x7fff) > 0x7c00; + }, f32 => { const bits = @bitCast(u32, x); return bits & 0x7FFFFFFF > 0x7F800000; @@ -26,8 +30,10 @@ pub fn isSignalNan(x: var) bool { } test "math.isNan" { + assert(isNan(math.nan(f16))); assert(isNan(math.nan(f32))); assert(isNan(math.nan(f64))); + assert(!isNan(f16(1.0))); assert(!isNan(f32(1.0))); assert(!isNan(f64(1.0))); } diff --git a/std/math/nan.zig b/std/math/nan.zig index 22461711d0..2cbcbee81b 100644 --- a/std/math/nan.zig +++ b/std/math/nan.zig @@ -2,6 +2,7 @@ const math = @import("index.zig"); pub fn nan(comptime T: type) T { return switch (T) { + f16 => @bitCast(f16, math.nan_u16), f32 => @bitCast(f32, math.nan_u32), f64 => @bitCast(f64, math.nan_u64), else => @compileError("nan not implemented for " ++ @typeName(T)), @@ -12,6 +13,7 @@ pub fn nan(comptime T: type) T { // representation in the future when required. pub fn snan(comptime T: type) T { return switch (T) { + f16 => @bitCast(f16, math.nan_u16), f32 => @bitCast(f32, math.nan_u32), f64 => @bitCast(f64, math.nan_u64), else => @compileError("snan not implemented for " ++ @typeName(T)), diff --git a/std/special/builtin.zig b/std/special/builtin.zig index 07e735d931..56e578030b 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -210,7 +210,9 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { } fn isNan(comptime T: type, bits: T) bool { - if (T == u32) { + if (T == u16) { + return (bits & 0x7fff) > 0x7c00; + } else if (T == u32) { return (bits & 0x7fffffff) > 0x7f800000; } else if (T == u64) { return (bits & (@maxValue(u64) >> 1)) > (u64(0x7ff) << 52); diff --git a/std/special/compiler_rt/extendXfYf2_test.zig b/std/special/compiler_rt/extendXfYf2_test.zig index 0168de12a5..185c83a0ef 100644 --- a/std/special/compiler_rt/extendXfYf2_test.zig +++ b/std/special/compiler_rt/extendXfYf2_test.zig @@ -88,6 +88,7 @@ test "extenddftf2" { test "extendhfsf2" { test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN + test__extendhfsf2(0x7c01, 0x7f802000); // sNaN test__extendhfsf2(0, 0); // 0 test__extendhfsf2(0x8000, 0x80000000); // -0 -- cgit v1.2.3 From a36d7b613185c28c508bcc7b0e8b580a0ffd5e28 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 30 Jun 2018 01:44:54 +0200 Subject: add std.math f16 inf support refs #1122 --- std/math/index.zig | 3 +++ std/math/inf.zig | 2 +- std/math/isinf.zig | 22 ++++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/std/math/index.zig b/std/math/index.zig index df8271fbcf..1926943cc6 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -28,6 +28,9 @@ pub const f16_toint = 1.0 / f16_epsilon; pub const nan_u16 = u16(0x7C01); pub const nan_f16 = @bitCast(f16, nan_u16); +pub const inf_u16 = u16(0x7C00); +pub const inf_f16 = @bitCast(f16, inf_u16); + pub const nan_u32 = u32(0x7F800001); pub const nan_f32 = @bitCast(f32, nan_u32); diff --git a/std/math/inf.zig b/std/math/inf.zig index bde90b2be1..62f5ef7c0d 100644 --- a/std/math/inf.zig +++ b/std/math/inf.zig @@ -1,9 +1,9 @@ const std = @import("../index.zig"); const math = std.math; -const assert = std.debug.assert; pub fn inf(comptime T: type) T { return switch (T) { + f16 => @bitCast(f16, math.inf_u16), f32 => @bitCast(f32, math.inf_u32), f64 => @bitCast(f64, math.inf_u64), else => @compileError("inf not implemented for " ++ @typeName(T)), diff --git a/std/math/isinf.zig b/std/math/isinf.zig index a976fb73d2..cf68b5769c 100644 --- a/std/math/isinf.zig +++ b/std/math/isinf.zig @@ -5,6 +5,10 @@ const assert = std.debug.assert; pub fn isInf(x: var) bool { const T = @typeOf(x); switch (T) { + f16 => { + const bits = @bitCast(u16, x); + return bits & 0x7FFF == 0x7C00; + }, f32 => { const bits = @bitCast(u32, x); return bits & 0x7FFFFFFF == 0x7F800000; @@ -22,6 +26,9 @@ pub fn isInf(x: var) bool { pub fn isPositiveInf(x: var) bool { const T = @typeOf(x); switch (T) { + f16 => { + return @bitCast(u16, x) == 0x7C00; + }, f32 => { return @bitCast(u32, x) == 0x7F800000; }, @@ -37,6 +44,9 @@ pub fn isPositiveInf(x: var) bool { pub fn isNegativeInf(x: var) bool { const T = @typeOf(x); switch (T) { + f16 => { + return @bitCast(u16, x) == 0xFC00; + }, f32 => { return @bitCast(u32, x) == 0xFF800000; }, @@ -50,10 +60,14 @@ pub fn isNegativeInf(x: var) bool { } test "math.isInf" { + assert(!isInf(f16(0.0))); + assert(!isInf(f16(-0.0))); assert(!isInf(f32(0.0))); assert(!isInf(f32(-0.0))); assert(!isInf(f64(0.0))); assert(!isInf(f64(-0.0))); + assert(isInf(math.inf(f16))); + assert(isInf(-math.inf(f16))); assert(isInf(math.inf(f32))); assert(isInf(-math.inf(f32))); assert(isInf(math.inf(f64))); @@ -61,10 +75,14 @@ test "math.isInf" { } test "math.isPositiveInf" { + assert(!isPositiveInf(f16(0.0))); + assert(!isPositiveInf(f16(-0.0))); assert(!isPositiveInf(f32(0.0))); assert(!isPositiveInf(f32(-0.0))); assert(!isPositiveInf(f64(0.0))); assert(!isPositiveInf(f64(-0.0))); + assert(isPositiveInf(math.inf(f16))); + assert(!isPositiveInf(-math.inf(f16))); assert(isPositiveInf(math.inf(f32))); assert(!isPositiveInf(-math.inf(f32))); assert(isPositiveInf(math.inf(f64))); @@ -72,10 +90,14 @@ test "math.isPositiveInf" { } test "math.isNegativeInf" { + assert(!isNegativeInf(f16(0.0))); + assert(!isNegativeInf(f16(-0.0))); assert(!isNegativeInf(f32(0.0))); assert(!isNegativeInf(f32(-0.0))); assert(!isNegativeInf(f64(0.0))); assert(!isNegativeInf(f64(-0.0))); + assert(!isNegativeInf(math.inf(f16))); + assert(isNegativeInf(-math.inf(f16))); assert(!isNegativeInf(math.inf(f32))); assert(isNegativeInf(-math.inf(f32))); assert(!isNegativeInf(math.inf(f64))); -- cgit v1.2.3 From 30b75ae3539128cf7f12ef84db15ad91f54f0de2 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 30 Jun 2018 01:44:54 +0200 Subject: add std.math f16 isfinite support refs #1122 --- std/math/isfinite.zig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/std/math/isfinite.zig b/std/math/isfinite.zig index 37ead03bba..3a5d4f01bb 100644 --- a/std/math/isfinite.zig +++ b/std/math/isfinite.zig @@ -5,6 +5,10 @@ const assert = std.debug.assert; pub fn isFinite(x: var) bool { const T = @typeOf(x); switch (T) { + f16 => { + const bits = @bitCast(u16, x); + return bits & 0x7FFF < 0x7C00; + }, f32 => { const bits = @bitCast(u32, x); return bits & 0x7FFFFFFF < 0x7F800000; @@ -20,10 +24,14 @@ pub fn isFinite(x: var) bool { } test "math.isFinite" { + assert(isFinite(f16(0.0))); + assert(isFinite(f16(-0.0))); assert(isFinite(f32(0.0))); assert(isFinite(f32(-0.0))); assert(isFinite(f64(0.0))); assert(isFinite(f64(-0.0))); + assert(!isFinite(math.inf(f16))); + assert(!isFinite(-math.inf(f16))); assert(!isFinite(math.inf(f32))); assert(!isFinite(-math.inf(f32))); assert(!isFinite(math.inf(f64))); -- cgit v1.2.3 From f36b095b5ff2176f357d84a9924740324b069765 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 30 Jun 2018 01:44:54 +0200 Subject: add std.math f16 isnormal support refs #1122 --- std/math/isnormal.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/std/math/isnormal.zig b/std/math/isnormal.zig index d5c1061cb1..22109936c4 100644 --- a/std/math/isnormal.zig +++ b/std/math/isnormal.zig @@ -5,6 +5,10 @@ const assert = std.debug.assert; pub fn isNormal(x: var) bool { const T = @typeOf(x); switch (T) { + f16 => { + const bits = @bitCast(u16, x); + return (bits + 1024) & 0x7FFF >= 2048; + }, f32 => { const bits = @bitCast(u32, x); return (bits + 0x00800000) & 0x7FFFFFFF >= 0x01000000; @@ -20,8 +24,13 @@ pub fn isNormal(x: var) bool { } test "math.isNormal" { + assert(!isNormal(math.nan(f16))); assert(!isNormal(math.nan(f32))); assert(!isNormal(math.nan(f64))); + assert(!isNormal(f16(0))); + assert(!isNormal(f32(0))); + assert(!isNormal(f64(0))); + assert(isNormal(f16(1.0))); assert(isNormal(f32(1.0))); assert(isNormal(f64(1.0))); } -- cgit v1.2.3 From 1abc9252922f242b801fe88533b482a18724237d Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 30 Jun 2018 01:44:54 +0200 Subject: add std.math f16 fabs support refs #1122 --- std/math/fabs.zig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/std/math/fabs.zig b/std/math/fabs.zig index 821624e1bc..ae8f9616a8 100644 --- a/std/math/fabs.zig +++ b/std/math/fabs.zig @@ -10,12 +10,19 @@ const assert = std.debug.assert; pub fn fabs(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { + f16 => fabs16(x), f32 => fabs32(x), f64 => fabs64(x), else => @compileError("fabs not implemented for " ++ @typeName(T)), }; } +fn fabs16(x: f16) f16 { + var u = @bitCast(u16, x); + u &= 0x7FFF; + return @bitCast(f16, u); +} + fn fabs32(x: f32) f32 { var u = @bitCast(u32, x); u &= 0x7FFFFFFF; @@ -29,10 +36,16 @@ fn fabs64(x: f64) f64 { } test "math.fabs" { + assert(fabs(f16(1.0)) == fabs16(1.0)); assert(fabs(f32(1.0)) == fabs32(1.0)); assert(fabs(f64(1.0)) == fabs64(1.0)); } +test "math.fabs16" { + assert(fabs16(1.0) == 1.0); + assert(fabs16(-1.0) == 1.0); +} + test "math.fabs32" { assert(fabs32(1.0) == 1.0); assert(fabs32(-1.0) == 1.0); @@ -43,6 +56,12 @@ test "math.fabs64" { assert(fabs64(-1.0) == 1.0); } +test "math.fabs16.special" { + assert(math.isPositiveInf(fabs(math.inf(f16)))); + assert(math.isPositiveInf(fabs(-math.inf(f16)))); + assert(math.isNan(fabs(math.nan(f16)))); +} + test "math.fabs32.special" { assert(math.isPositiveInf(fabs(math.inf(f32)))); assert(math.isPositiveInf(fabs(-math.inf(f32)))); -- cgit v1.2.3 From d293f1a0edf0e2e42f03ad416c7f5d649776ae88 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 30 Jun 2018 01:44:54 +0200 Subject: add std.math f16 floor support refs #1122 --- std/math/floor.zig | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ std/math/index.zig | 5 +++++ 2 files changed, 55 insertions(+) diff --git a/std/math/floor.zig b/std/math/floor.zig index 79d1097d08..0858598eea 100644 --- a/std/math/floor.zig +++ b/std/math/floor.zig @@ -12,12 +12,47 @@ const math = std.math; pub fn floor(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { + f16 => floor16(x), f32 => floor32(x), f64 => floor64(x), else => @compileError("floor not implemented for " ++ @typeName(T)), }; } +fn floor16(x: f16) f16 { + var u = @bitCast(u16, x); + const e = @intCast(i16, (u >> 10) & 31) - 15; + var m: u16 = undefined; + + // TODO: Shouldn't need this explicit check. + if (x == 0.0) { + return x; + } + + if (e >= 10) { + return x; + } + + if (e >= 0) { + m = u16(1023) >> @intCast(u4, e); + if (u & m == 0) { + return x; + } + math.forceEval(x + 0x1.0p120); + if (u >> 15 != 0) { + u += m; + } + return @bitCast(f16, u & ~m); + } else { + math.forceEval(x + 0x1.0p120); + if (u >> 15 == 0) { + return 0.0; + } else { + return -1.0; + } + } +} + fn floor32(x: f32) f32 { var u = @bitCast(u32, x); const e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F; @@ -84,10 +119,17 @@ fn floor64(x: f64) f64 { } test "math.floor" { + assert(floor(f16(1.3)) == floor16(1.3)); assert(floor(f32(1.3)) == floor32(1.3)); assert(floor(f64(1.3)) == floor64(1.3)); } +test "math.floor16" { + assert(floor16(1.3) == 1.0); + assert(floor16(-1.3) == -2.0); + assert(floor16(0.2) == 0.0); +} + test "math.floor32" { assert(floor32(1.3) == 1.0); assert(floor32(-1.3) == -2.0); @@ -100,6 +142,14 @@ test "math.floor64" { assert(floor64(0.2) == 0.0); } +test "math.floor16.special" { + assert(floor16(0.0) == 0.0); + assert(floor16(-0.0) == -0.0); + assert(math.isPositiveInf(floor16(math.inf(f16)))); + assert(math.isNegativeInf(floor16(-math.inf(f16)))); + assert(math.isNan(floor16(math.nan(f16)))); +} + test "math.floor32.special" { assert(floor32(0.0) == 0.0); assert(floor32(-0.0) == -0.0); diff --git a/std/math/index.zig b/std/math/index.zig index 1926943cc6..17b66f5568 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -56,6 +56,11 @@ pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool { pub fn forceEval(value: var) void { const T = @typeOf(value); switch (T) { + f16 => { + var x: f16 = undefined; + const p = @ptrCast(*volatile f16, &x); + p.* = x; + }, f32 => { var x: f32 = undefined; const p = @ptrCast(*volatile f32, &x); -- cgit v1.2.3 From ca444e6191138e6f9977cf2fbceb071134ed6aff Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 30 Jun 2018 01:44:54 +0200 Subject: add std.math f16 copysign support refs #1122 --- std/math/copysign.zig | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/std/math/copysign.zig b/std/math/copysign.zig index 4ca8f82f4b..8c71dcb0bc 100644 --- a/std/math/copysign.zig +++ b/std/math/copysign.zig @@ -4,12 +4,22 @@ const assert = std.debug.assert; pub fn copysign(comptime T: type, x: T, y: T) T { return switch (T) { + f16 => copysign16(x, y), f32 => copysign32(x, y), f64 => copysign64(x, y), else => @compileError("copysign not implemented for " ++ @typeName(T)), }; } +fn copysign16(x: f16, y: f16) f16 { + const ux = @bitCast(u16, x); + const uy = @bitCast(u16, y); + + const h1 = ux & (@maxValue(u16) / 2); + const h2 = uy & (u16(1) << 15); + return @bitCast(f16, h1 | h2); +} + fn copysign32(x: f32, y: f32) f32 { const ux = @bitCast(u32, x); const uy = @bitCast(u32, y); @@ -29,10 +39,18 @@ fn copysign64(x: f64, y: f64) f64 { } test "math.copysign" { + assert(copysign(f16, 1.0, 1.0) == copysign16(1.0, 1.0)); assert(copysign(f32, 1.0, 1.0) == copysign32(1.0, 1.0)); assert(copysign(f64, 1.0, 1.0) == copysign64(1.0, 1.0)); } +test "math.copysign16" { + assert(copysign16(5.0, 1.0) == 5.0); + assert(copysign16(5.0, -1.0) == -5.0); + assert(copysign16(-5.0, -1.0) == -5.0); + assert(copysign16(-5.0, 1.0) == 5.0); +} + test "math.copysign32" { assert(copysign32(5.0, 1.0) == 5.0); assert(copysign32(5.0, -1.0) == -5.0); -- cgit v1.2.3 From be361790645971446b64dabf21c27a595a40c8fd Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 30 Jun 2018 01:44:54 +0200 Subject: add std.math f16 signbit support refs #1122 --- std/math/signbit.zig | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/std/math/signbit.zig b/std/math/signbit.zig index a0191bed5c..8c6829dfcd 100644 --- a/std/math/signbit.zig +++ b/std/math/signbit.zig @@ -5,12 +5,18 @@ const assert = std.debug.assert; pub fn signbit(x: var) bool { const T = @typeOf(x); return switch (T) { + f16 => signbit16(x), f32 => signbit32(x), f64 => signbit64(x), else => @compileError("signbit not implemented for " ++ @typeName(T)), }; } +fn signbit16(x: f16) bool { + const bits = @bitCast(u16, x); + return bits >> 15 != 0; +} + fn signbit32(x: f32) bool { const bits = @bitCast(u32, x); return bits >> 31 != 0; @@ -22,10 +28,16 @@ fn signbit64(x: f64) bool { } test "math.signbit" { + assert(signbit(f16(4.0)) == signbit16(4.0)); assert(signbit(f32(4.0)) == signbit32(4.0)); assert(signbit(f64(4.0)) == signbit64(4.0)); } +test "math.signbit16" { + assert(!signbit16(4.0)); + assert(signbit16(-3.0)); +} + test "math.signbit32" { assert(!signbit32(4.0)); assert(signbit32(-3.0)); -- cgit v1.2.3 From 30cfc0ab2c56ad73dca9b9935731d10010c93b32 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 30 Jun 2018 01:44:54 +0200 Subject: test std.math f16 sqrt support refs #1122 --- std/math/sqrt.zig | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig index 599008acff..e12ecf9683 100644 --- a/std/math/sqrt.zig +++ b/std/math/sqrt.zig @@ -31,10 +31,25 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ } test "math.sqrt" { + assert(sqrt(f16(0.0)) == @sqrt(f16, 0.0)); assert(sqrt(f32(0.0)) == @sqrt(f32, 0.0)); assert(sqrt(f64(0.0)) == @sqrt(f64, 0.0)); } +test "math.sqrt16" { + const epsilon = 0.000001; + + assert(@sqrt(f16, 0.0) == 0.0); + assert(math.approxEq(f16, @sqrt(f16, 2.0), 1.414214, epsilon)); + assert(math.approxEq(f16, @sqrt(f16, 3.6), 1.897367, epsilon)); + assert(@sqrt(f16, 4.0) == 2.0); + assert(math.approxEq(f16, @sqrt(f16, 7.539840), 2.745877, epsilon)); + assert(math.approxEq(f16, @sqrt(f16, 19.230934), 4.385309, epsilon)); + assert(@sqrt(f16, 64.0) == 8.0); + assert(math.approxEq(f16, @sqrt(f16, 64.1), 8.006248, epsilon)); + assert(math.approxEq(f16, @sqrt(f16, 8942.230469), 94.563370, epsilon)); +} + test "math.sqrt32" { const epsilon = 0.000001; @@ -63,6 +78,14 @@ test "math.sqrt64" { assert(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon)); } +test "math.sqrt16.special" { + assert(math.isPositiveInf(@sqrt(f16, math.inf(f16)))); + assert(@sqrt(f16, 0.0) == 0.0); + assert(@sqrt(f16, -0.0) == -0.0); + assert(math.isNan(@sqrt(f16, -1.0))); + assert(math.isNan(@sqrt(f16, math.nan(f16)))); +} + test "math.sqrt32.special" { assert(math.isPositiveInf(@sqrt(f32, math.inf(f32)))); assert(@sqrt(f32, 0.0) == 0.0); -- cgit v1.2.3 From 25bbb1a8ff7074a56b4da98de24a549e223d0009 Mon Sep 17 00:00:00 2001 From: Jay Weisskopf Date: Fri, 29 Jun 2018 22:22:04 -0400 Subject: Fix version detection for out-of-source builds Git was called in the build directory and not the source directory. This works fine when the build directory resides within the source repository, but doesn't work for out-of-source builds. Example: ``` ~/zigbuild$ cmake ../zig fatal: not a git repository (or any of the parent directories): .git Configuring zig version 0.2.0+ ``` Use Git's `-C ` flag to always point to the source directory so that it doesn't matter where the build directory lives. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4838aeb797..e8873d2e67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ set(ZIG_VERSION "${ZIG_VERSION_MAJOR}.${ZIG_VERSION_MINOR}.${ZIG_VERSION_PATCH}" find_program(GIT_EXE NAMES git) if(GIT_EXE) execute_process( - COMMAND ${GIT_EXE} name-rev HEAD --tags --name-only --no-undefined --always + COMMAND ${GIT_EXE} -C ${CMAKE_SOURCE_DIR} name-rev HEAD --tags --name-only --no-undefined --always OUTPUT_VARIABLE ZIG_GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE) if(ZIG_GIT_REV MATCHES "\\^0$") -- cgit v1.2.3 From 379950f81debb1e4df4e69511fbebd61911013b4 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 28 Jun 2018 20:26:35 +1200 Subject: compiler_rt: Add trunc f128 narrowing functions --- std/special/compiler_rt/truncXfYf2.zig | 16 +++- std/special/compiler_rt/truncXfYf2_test.zig | 138 +++++++++++++++++++++------- 2 files changed, 115 insertions(+), 39 deletions(-) diff --git a/std/special/compiler_rt/truncXfYf2.zig b/std/special/compiler_rt/truncXfYf2.zig index f08c6ae34f..04b815e86b 100644 --- a/std/special/compiler_rt/truncXfYf2.zig +++ b/std/special/compiler_rt/truncXfYf2.zig @@ -4,7 +4,13 @@ pub extern fn __truncsfhf2(a: f32) u16 { return @bitCast(u16, truncXfYf2(f16, f32, a)); } -const CHAR_BIT = 8; +pub extern fn __trunctfsf2(a: f128) f32 { + return truncXfYf2(f32, f128, a); +} + +pub extern fn __trunctfdf2(a: f128) f64 { + return truncXfYf2(f64, f128, a); +} inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits); @@ -16,7 +22,7 @@ inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t // Various constants whose values follow from the type parameters. // Any reasonable optimizer will fold and propagate all of these. - const srcBits = @sizeOf(src_t) * CHAR_BIT; + const srcBits = src_t.bit_count; const srcExpBits = srcBits - srcSigBits - 1; const srcInfExp = (1 << srcExpBits) - 1; const srcExpBias = srcInfExp >> 1; @@ -31,7 +37,7 @@ inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t const srcQNaN = 1 << (srcSigBits - 1); const srcNaNCode = srcQNaN - 1; - const dstBits = @sizeOf(dst_t) * CHAR_BIT; + const dstBits = dst_t.bit_count; const dstExpBits = dstBits - dstSigBits - 1; const dstInfExp = (1 << dstExpBits) - 1; const dstExpBias = dstInfExp >> 1; @@ -79,8 +85,8 @@ inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t // a underflows on conversion to the destination type or is an exact // zero. The result may be a denormal or zero. Extract the exponent // to get the shift amount for the denormalization. - const aExp: u32 = aAbs >> srcSigBits; - const shift: u32 = srcExpBias - dstExpBias - aExp + 1; + const aExp = aAbs >> srcSigBits; + const shift = srcExpBias - dstExpBias - aExp + 1; const significand: src_rep_t = (aRep & srcSignificandMask) | srcMinNormal; diff --git a/std/special/compiler_rt/truncXfYf2_test.zig b/std/special/compiler_rt/truncXfYf2_test.zig index e4dae7b5b0..c4bf2db733 100644 --- a/std/special/compiler_rt/truncXfYf2_test.zig +++ b/std/special/compiler_rt/truncXfYf2_test.zig @@ -11,54 +11,124 @@ fn test__truncsfhf2(a: u32, expected: u16) void { } test "truncsfhf2" { - test__truncsfhf2(0x7fc00000, 0x7e00); // qNaN - test__truncsfhf2(0x7fe00000, 0x7f00); // sNaN + test__truncsfhf2(0x7fc00000, 0x7e00); // qNaN + test__truncsfhf2(0x7fe00000, 0x7f00); // sNaN - test__truncsfhf2(0, 0); // 0 - test__truncsfhf2(0x80000000, 0x8000); // -0 + test__truncsfhf2(0, 0); // 0 + test__truncsfhf2(0x80000000, 0x8000); // -0 - test__truncsfhf2(0x7f800000, 0x7c00); // inf - test__truncsfhf2(0xff800000, 0xfc00); // -inf + test__truncsfhf2(0x7f800000, 0x7c00); // inf + test__truncsfhf2(0xff800000, 0xfc00); // -inf - test__truncsfhf2(0x477ff000, 0x7c00); // 65520 -> inf - test__truncsfhf2(0xc77ff000, 0xfc00); // -65520 -> -inf + test__truncsfhf2(0x477ff000, 0x7c00); // 65520 -> inf + test__truncsfhf2(0xc77ff000, 0xfc00); // -65520 -> -inf - test__truncsfhf2(0x71cc3892, 0x7c00); // 0x1.987124876876324p+100 -> inf - test__truncsfhf2(0xf1cc3892, 0xfc00); // -0x1.987124876876324p+100 -> -inf + test__truncsfhf2(0x71cc3892, 0x7c00); // 0x1.987124876876324p+100 -> inf + test__truncsfhf2(0xf1cc3892, 0xfc00); // -0x1.987124876876324p+100 -> -inf - test__truncsfhf2(0x38800000, 0x0400); // normal (min), 2**-14 - test__truncsfhf2(0xb8800000, 0x8400); // normal (min), -2**-14 + test__truncsfhf2(0x38800000, 0x0400); // normal (min), 2**-14 + test__truncsfhf2(0xb8800000, 0x8400); // normal (min), -2**-14 - test__truncsfhf2(0x477fe000, 0x7bff); // normal (max), 65504 - test__truncsfhf2(0xc77fe000, 0xfbff); // normal (max), -65504 + test__truncsfhf2(0x477fe000, 0x7bff); // normal (max), 65504 + test__truncsfhf2(0xc77fe000, 0xfbff); // normal (max), -65504 - test__truncsfhf2(0x477fe100, 0x7bff); // normal, 65505 -> 65504 - test__truncsfhf2(0xc77fe100, 0xfbff); // normal, -65505 -> -65504 + test__truncsfhf2(0x477fe100, 0x7bff); // normal, 65505 -> 65504 + test__truncsfhf2(0xc77fe100, 0xfbff); // normal, -65505 -> -65504 - test__truncsfhf2(0x477fef00, 0x7bff); // normal, 65519 -> 65504 - test__truncsfhf2(0xc77fef00, 0xfbff); // normal, -65519 -> -65504 + test__truncsfhf2(0x477fef00, 0x7bff); // normal, 65519 -> 65504 + test__truncsfhf2(0xc77fef00, 0xfbff); // normal, -65519 -> -65504 - test__truncsfhf2(0x3f802000, 0x3c01); // normal, 1 + 2**-10 - test__truncsfhf2(0xbf802000, 0xbc01); // normal, -1 - 2**-10 + test__truncsfhf2(0x3f802000, 0x3c01); // normal, 1 + 2**-10 + test__truncsfhf2(0xbf802000, 0xbc01); // normal, -1 - 2**-10 - test__truncsfhf2(0x3eaaa000, 0x3555); // normal, approx. 1/3 - test__truncsfhf2(0xbeaaa000, 0xb555); // normal, approx. -1/3 + test__truncsfhf2(0x3eaaa000, 0x3555); // normal, approx. 1/3 + test__truncsfhf2(0xbeaaa000, 0xb555); // normal, approx. -1/3 - test__truncsfhf2(0x40490fdb, 0x4248); // normal, 3.1415926535 - test__truncsfhf2(0xc0490fdb, 0xc248); // normal, -3.1415926535 + test__truncsfhf2(0x40490fdb, 0x4248); // normal, 3.1415926535 + test__truncsfhf2(0xc0490fdb, 0xc248); // normal, -3.1415926535 - test__truncsfhf2(0x45cc3892, 0x6e62); // normal, 0x1.987124876876324p+12 + test__truncsfhf2(0x45cc3892, 0x6e62); // normal, 0x1.987124876876324p+12 - test__truncsfhf2(0x3f800000, 0x3c00); // normal, 1 - test__truncsfhf2(0x38800000, 0x0400); // normal, 0x1.0p-14 + test__truncsfhf2(0x3f800000, 0x3c00); // normal, 1 + test__truncsfhf2(0x38800000, 0x0400); // normal, 0x1.0p-14 - test__truncsfhf2(0x33800000, 0x0001); // denormal (min), 2**-24 - test__truncsfhf2(0xb3800000, 0x8001); // denormal (min), -2**-24 + test__truncsfhf2(0x33800000, 0x0001); // denormal (min), 2**-24 + test__truncsfhf2(0xb3800000, 0x8001); // denormal (min), -2**-24 - test__truncsfhf2(0x387fc000, 0x03ff); // denormal (max), 2**-14 - 2**-24 - test__truncsfhf2(0xb87fc000, 0x83ff); // denormal (max), -2**-14 + 2**-24 + test__truncsfhf2(0x387fc000, 0x03ff); // denormal (max), 2**-14 - 2**-24 + test__truncsfhf2(0xb87fc000, 0x83ff); // denormal (max), -2**-14 + 2**-24 - test__truncsfhf2(0x35800000, 0x0010); // denormal, 0x1.0p-20 - test__truncsfhf2(0x33280000, 0x0001); // denormal, 0x1.5p-25 -> 0x1.0p-24 - test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero + test__truncsfhf2(0x35800000, 0x0010); // denormal, 0x1.0p-20 + test__truncsfhf2(0x33280000, 0x0001); // denormal, 0x1.5p-25 -> 0x1.0p-24 + test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero +} + +const __trunctfsf2 = @import("truncXfYf2.zig").__trunctfsf2; + +fn test__trunctfsf2(a: f128, expected: u32) void { + const x = __trunctfsf2(a); + + const rep = @bitCast(u32, x); + if (rep == expected) { + return; + } + // test other possible NaN representation(signal NaN) + else if (expected == 0x7fc00000) { + if ((rep & 0x7f800000) == 0x7f800000 and (rep & 0x7fffff) > 0) { + return; + } + } + + @panic("__trunctfsf2 test failure"); +} + +test "trunctfsf2" { + // qnan + test__trunctfsf2(@bitCast(f128, u128(0x7fff800000000000 << 64)), 0x7fc00000); + // nan + test__trunctfsf2(@bitCast(f128, u128((0x7fff000000000000 | (0x810000000000 & 0xffffffffffff)) << 64)), 0x7fc08000); + // inf + test__trunctfsf2(@bitCast(f128, u128(0x7fff000000000000 << 64)), 0x7f800000); + // zero + test__trunctfsf2(0.0, 0x0); + + test__trunctfsf2(0x1.23a2abb4a2ddee355f36789abcdep+5, 0x4211d156); + test__trunctfsf2(0x1.e3d3c45bd3abfd98b76a54cc321fp-9, 0x3b71e9e2); + test__trunctfsf2(0x1.234eebb5faa678f4488693abcdefp+4534, 0x7f800000); + test__trunctfsf2(0x1.edcba9bb8c76a5a43dd21f334634p-435, 0x0); +} + +const __trunctfdf2 = @import("truncXfYf2.zig").__trunctfdf2; + +fn test__trunctfdf2(a: f128, expected: u64) void { + const x = __trunctfdf2(a); + + const rep = @bitCast(u64, x); + if (rep == expected) { + return; + } + // test other possible NaN representation(signal NaN) + else if (expected == 0x7ff8000000000000) { + if ((rep & 0x7ff0000000000000) == 0x7ff0000000000000 and (rep & 0xfffffffffffff) > 0) { + return; + } + } + + @panic("__trunctfsf2 test failure"); +} + +test "trunctfdf2" { + // qnan + test__trunctfdf2(@bitCast(f128, u128(0x7fff800000000000 << 64)), 0x7ff8000000000000); + // nan + test__trunctfdf2(@bitCast(f128, u128((0x7fff000000000000 | (0x810000000000 & 0xffffffffffff)) << 64)), 0x7ff8100000000000); + // inf + test__trunctfdf2(@bitCast(f128, u128(0x7fff000000000000 << 64)), 0x7ff0000000000000); + // zero + test__trunctfdf2(0.0, 0x0); + + test__trunctfdf2(0x1.af23456789bbaaab347645365cdep+5, 0x404af23456789bbb); + test__trunctfdf2(0x1.dedafcff354b6ae9758763545432p-9, 0x3f6dedafcff354b7); + test__trunctfdf2(0x1.2f34dd5f437e849b4baab754cdefp+4534, 0x7ff0000000000000); + test__trunctfdf2(0x1.edcbff8ad76ab5bf46463233214fp-435, 0x24cedcbff8ad76ab); } -- cgit v1.2.3 From c32b2e45efb0d0ced14c76d5221b2db636e40246 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 28 Jun 2018 20:40:11 +1200 Subject: compiler_rt: Add floatuntisf --- std/special/compiler_rt/floatuntisf.zig | 59 +++++++++++++++++++++++ std/special/compiler_rt/floatuntisf_test.zig | 72 ++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 std/special/compiler_rt/floatuntisf.zig create mode 100644 std/special/compiler_rt/floatuntisf_test.zig diff --git a/std/special/compiler_rt/floatuntisf.zig b/std/special/compiler_rt/floatuntisf.zig new file mode 100644 index 0000000000..e83affd87c --- /dev/null +++ b/std/special/compiler_rt/floatuntisf.zig @@ -0,0 +1,59 @@ +const builtin = @import("builtin"); +const is_test = builtin.is_test; + +const FLT_MANT_DIG = 24; + +pub extern fn __floatuntisf(arg: u128) f32 { + @setRuntimeSafety(is_test); + + if (arg == 0) + return 0.0; + + var a = arg; + const N: u32 = @sizeOf(u128) * 8; + const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits + var e: i32 = sd -% 1; // exponent + if (sd > FLT_MANT_DIG) { + // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + // 12345678901234567890123456 + // 1 = msb 1 bit + // P = bit FLT_MANT_DIG-1 bits to the right of 1 + // Q = bit FLT_MANT_DIG bits to the right of 1 + // R = "or" of all bits to the right of Q + switch (sd) { + FLT_MANT_DIG + 1 => { + a <<= 1; + }, + FLT_MANT_DIG + 2 => {}, + else => { + const shift_amt = @bitCast(i32, N +% (FLT_MANT_DIG + 2)) -% sd; + const shift_amt_u7 = @intCast(u7, shift_amt); + a = (a >> @intCast(u7, sd -% (FLT_MANT_DIG + 2))) | + @boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0); + }, + } + // finish + a |= @boolToInt((a & 4) != 0); // Or P into R + a +%= 1; // round - this step may add a significant bit + a >>= 2; // dump Q and R + // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits + if ((a & (u128(1) << FLT_MANT_DIG)) != 0) { + a >>= 1; + e +%= 1; + } + // a is now rounded to FLT_MANT_DIG bits + } else { + a <<= @intCast(u7, FLT_MANT_DIG -% sd); + // a is now rounded to FLT_MANT_DIG bits + } + + const high = @bitCast(u32, (e +% 127) << 23); // exponent + const low = @truncate(u32, a) & 0x007fffff; // mantissa + + return @bitCast(f32, high | low); +} + +test "import floatuntisf" { + _ = @import("floatuntisf_test.zig"); +} diff --git a/std/special/compiler_rt/floatuntisf_test.zig b/std/special/compiler_rt/floatuntisf_test.zig new file mode 100644 index 0000000000..7f84c1f963 --- /dev/null +++ b/std/special/compiler_rt/floatuntisf_test.zig @@ -0,0 +1,72 @@ +const __floatuntisf = @import("floatuntisf.zig").__floatuntisf; +const assert = @import("std").debug.assert; + +fn test__floatuntisf(a: u128, expected: f32) void { + const x = __floatuntisf(a); + assert(x == expected); +} + +test "floatuntisf" { + test__floatuntisf(0, 0.0); + + test__floatuntisf(1, 1.0); + test__floatuntisf(2, 2.0); + test__floatuntisf(20, 20.0); + + test__floatuntisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + test__floatuntisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + + test__floatuntisf(make_ti(0x8000008000000000, 0), 0x1.000001p+127); + test__floatuntisf(make_ti(0x8000000000000800, 0), 0x1.0p+127); + test__floatuntisf(make_ti(0x8000010000000000, 0), 0x1.000002p+127); + + test__floatuntisf(make_ti(0x8000000000000000, 0), 0x1.000000p+127); + + test__floatuntisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + test__floatuntisf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + test__floatuntisf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + + test__floatuntisf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + + test__floatuntisf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + test__floatuntisf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + test__floatuntisf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + test__floatuntisf(0xFFFFFFFFFFFFFFFE, 0x1p+64); + test__floatuntisf(0xFFFFFFFFFFFFFFFF, 0x1p+64); + + test__floatuntisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + test__floatuntisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); + test__floatuntisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); + test__floatuntisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); + test__floatuntisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); + test__floatuntisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); + + test__floatuntisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); + test__floatuntisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); + test__floatuntisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); + test__floatuntisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); + test__floatuntisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); + + test__floatuntisf(make_ti(0x0000000000001FED, 0xCB90000000000001), 0x1.FEDCBAp+76); + test__floatuntisf(make_ti(0x0000000000001FED, 0xCBA0000000000000), 0x1.FEDCBAp+76); + test__floatuntisf(make_ti(0x0000000000001FED, 0xCBAFFFFFFFFFFFFF), 0x1.FEDCBAp+76); + test__floatuntisf(make_ti(0x0000000000001FED, 0xCBB0000000000000), 0x1.FEDCBCp+76); + test__floatuntisf(make_ti(0x0000000000001FED, 0xCBB0000000000001), 0x1.FEDCBCp+76); + test__floatuntisf(make_ti(0x0000000000001FED, 0xCBBFFFFFFFFFFFFF), 0x1.FEDCBCp+76); + test__floatuntisf(make_ti(0x0000000000001FED, 0xCBC0000000000000), 0x1.FEDCBCp+76); + test__floatuntisf(make_ti(0x0000000000001FED, 0xCBC0000000000001), 0x1.FEDCBCp+76); + test__floatuntisf(make_ti(0x0000000000001FED, 0xCBD0000000000000), 0x1.FEDCBCp+76); + test__floatuntisf(make_ti(0x0000000000001FED, 0xCBD0000000000001), 0x1.FEDCBEp+76); + test__floatuntisf(make_ti(0x0000000000001FED, 0xCBDFFFFFFFFFFFFF), 0x1.FEDCBEp+76); + test__floatuntisf(make_ti(0x0000000000001FED, 0xCBE0000000000000), 0x1.FEDCBEp+76); +} + +fn make_ti(high: u64, low: u64) u128 { + var result: u128 = high; + result <<= 64; + result |= low; + return result; +} -- cgit v1.2.3 From 61ebfe6603c8fef26008683f96b62dab2c502429 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 28 Jun 2018 21:12:47 +1200 Subject: compiler_rt: Add floatunditf and floatunsitf --- std/special/compiler_rt/floatunditf.zig | 28 +++++++++++++++++++++++ std/special/compiler_rt/floatunditf_test.zig | 33 ++++++++++++++++++++++++++++ std/special/compiler_rt/floatunsitf.zig | 29 ++++++++++++++++++++++++ std/special/compiler_rt/floatunsitf_test.zig | 29 ++++++++++++++++++++++++ std/special/compiler_rt/truncXfYf2.zig | 4 ++-- 5 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 std/special/compiler_rt/floatunditf.zig create mode 100644 std/special/compiler_rt/floatunditf_test.zig create mode 100644 std/special/compiler_rt/floatunsitf.zig create mode 100644 std/special/compiler_rt/floatunsitf_test.zig diff --git a/std/special/compiler_rt/floatunditf.zig b/std/special/compiler_rt/floatunditf.zig new file mode 100644 index 0000000000..b8b097581f --- /dev/null +++ b/std/special/compiler_rt/floatunditf.zig @@ -0,0 +1,28 @@ +const builtin = @import("builtin"); +const is_test = builtin.is_test; +const std = @import("../../index.zig"); + +pub extern fn __floatunditf(a: u128) f128 { + @setRuntimeSafety(is_test); + + if (a == 0) { + return 0; + } + + const mantissa_bits = std.math.floatMantissaBits(f128); + const exponent_bits = std.math.floatExponentBits(f128); + const exponent_bias = (1 << (exponent_bits - 1)) - 1; + const implicit_bit = 1 << mantissa_bits; + + const exp = (u128.bit_count - 1) - @clz(a); + const shift = mantissa_bits - @intCast(u7, exp); + + var result: u128 = (a << shift) ^ implicit_bit; + result += (@intCast(u128, exp) + exponent_bias) << mantissa_bits; + + return @bitCast(f128, result); +} + +test "import floatunditf" { + _ = @import("floatunditf_test.zig"); +} diff --git a/std/special/compiler_rt/floatunditf_test.zig b/std/special/compiler_rt/floatunditf_test.zig new file mode 100644 index 0000000000..8533c75070 --- /dev/null +++ b/std/special/compiler_rt/floatunditf_test.zig @@ -0,0 +1,33 @@ +const __floatunditf = @import("floatunditf.zig").__floatunditf; +const assert = @import("std").debug.assert; + +fn test__floatunditf(a: u128, expected_hi: u64, expected_lo: u64) void { + const x = __floatunditf(a); + + const x_repr = @bitCast(u128, x); + const x_hi = @intCast(u64, x_repr >> 64); + const x_lo = @truncate(u64, x_repr); + + if (x_hi == expected_hi and x_lo == expected_lo) { + return; + } + // nan repr + else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { + if ((x_hi & 0x7fff000000000000) == 0x7fff000000000000 and ((x_hi & 0xffffffffffff) > 0 or x_lo > 0)) { + return; + } + } + + @panic("__floatunditf test failure"); +} + +test "floatunditf" { + test__floatunditf(0xffffffffffffffff, 0x403effffffffffff, 0xfffe000000000000); + test__floatunditf(0xfffffffffffffffe, 0x403effffffffffff, 0xfffc000000000000); + test__floatunditf(0x8000000000000000, 0x403e000000000000, 0x0); + test__floatunditf(0x7fffffffffffffff, 0x403dffffffffffff, 0xfffc000000000000); + test__floatunditf(0x123456789abcdef1, 0x403b23456789abcd, 0xef10000000000000); + test__floatunditf(0x2, 0x4000000000000000, 0x0); + test__floatunditf(0x1, 0x3fff000000000000, 0x0); + test__floatunditf(0x0, 0x0, 0x0); +} diff --git a/std/special/compiler_rt/floatunsitf.zig b/std/special/compiler_rt/floatunsitf.zig new file mode 100644 index 0000000000..dfee826b32 --- /dev/null +++ b/std/special/compiler_rt/floatunsitf.zig @@ -0,0 +1,29 @@ +const builtin = @import("builtin"); +const is_test = builtin.is_test; +const std = @import("../../index.zig"); + +pub extern fn __floatunsitf(a: u64) f128 { + @setRuntimeSafety(is_test); + + if (a == 0) { + return 0; + } + + const mantissa_bits = std.math.floatMantissaBits(f128); + const exponent_bits = std.math.floatExponentBits(f128); + const exponent_bias = (1 << (exponent_bits - 1)) - 1; + const implicit_bit = 1 << mantissa_bits; + + const exp = (u64.bit_count - 1) - @clz(a); + const shift = mantissa_bits - @intCast(u7, exp); + + // TODO: @bitCast alignment error + var result align(16) = (@intCast(u128, a) << shift) ^ implicit_bit; + result += (@intCast(u128, exp) + exponent_bias) << mantissa_bits; + + return @bitCast(f128, result); +} + +test "import floatunsitf" { + _ = @import("floatunsitf_test.zig"); +} diff --git a/std/special/compiler_rt/floatunsitf_test.zig b/std/special/compiler_rt/floatunsitf_test.zig new file mode 100644 index 0000000000..06f54cde03 --- /dev/null +++ b/std/special/compiler_rt/floatunsitf_test.zig @@ -0,0 +1,29 @@ +const __floatunsitf = @import("floatunsitf.zig").__floatunsitf; +const assert = @import("std").debug.assert; + +fn test__floatunsitf(a: u64, expected_hi: u64, expected_lo: u64) void { + const x = __floatunsitf(a); + + const x_repr = @bitCast(u128, x); + const x_hi = @intCast(u64, x_repr >> 64); + const x_lo = @truncate(u64, x_repr); + + if (x_hi == expected_hi and x_lo == expected_lo) { + return; + } + // nan repr + else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { + if ((x_hi & 0x7fff000000000000) == 0x7fff000000000000 and ((x_hi & 0xffffffffffff) > 0 or x_lo > 0)) { + return; + } + } + + @panic("__floatunsitf test failure"); +} + +test "floatunsitf" { + test__floatunsitf(0x7fffffff, 0x401dfffffffc0000, 0x0); + test__floatunsitf(0, 0x0, 0x0); + test__floatunsitf(0xffffffff, 0x401efffffffe0000, 0x0); + test__floatunsitf(0x12345678, 0x401b234567800000, 0x0); +} diff --git a/std/special/compiler_rt/truncXfYf2.zig b/std/special/compiler_rt/truncXfYf2.zig index 04b815e86b..5cb2f61568 100644 --- a/std/special/compiler_rt/truncXfYf2.zig +++ b/std/special/compiler_rt/truncXfYf2.zig @@ -85,8 +85,8 @@ inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t // a underflows on conversion to the destination type or is an exact // zero. The result may be a denormal or zero. Extract the exponent // to get the shift amount for the denormalization. - const aExp = aAbs >> srcSigBits; - const shift = srcExpBias - dstExpBias - aExp + 1; + const aExp = @intCast(u32, aAbs >> srcSigBits); + const shift = @intCast(u32, srcExpBias - dstExpBias - aExp + 1); const significand: src_rep_t = (aRep & srcSignificandMask) | srcMinNormal; -- cgit v1.2.3 From cb7bdc2da1b2d7a3e78b272928ced77ccdd12148 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 28 Jun 2018 21:37:58 +1200 Subject: compiler_rt: Add floatuntitf --- std/special/compiler_rt/floatuntitf.zig | 60 +++++++++++++++++ std/special/compiler_rt/floatuntitf_test.zig | 99 ++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 std/special/compiler_rt/floatuntitf.zig create mode 100644 std/special/compiler_rt/floatuntitf_test.zig diff --git a/std/special/compiler_rt/floatuntitf.zig b/std/special/compiler_rt/floatuntitf.zig new file mode 100644 index 0000000000..a64dd96a34 --- /dev/null +++ b/std/special/compiler_rt/floatuntitf.zig @@ -0,0 +1,60 @@ +const builtin = @import("builtin"); +const is_test = builtin.is_test; + +const LDBL_MANT_DIG = 113; + +pub extern fn __floatuntitf(arg: u128) f128 { + @setRuntimeSafety(is_test); + + if (arg == 0) + return 0.0; + + var a = arg; + const N: u32 = @sizeOf(u128) * 8; + const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits + var e: i32 = sd -% 1; // exponent + if (sd > LDBL_MANT_DIG) { + // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + // 12345678901234567890123456 + // 1 = msb 1 bit + // P = bit LDBL_MANT_DIG-1 bits to the right of 1 + // Q = bit LDBL_MANT_DIG bits to the right of 1 + // R = "or" of all bits to the right of Q + switch (sd) { + LDBL_MANT_DIG + 1 => { + a <<= 1; + }, + LDBL_MANT_DIG + 2 => {}, + else => { + const shift_amt = @bitCast(i32, N +% (LDBL_MANT_DIG + 2)) -% sd; + const shift_amt_u7 = @intCast(u7, shift_amt); + a = (a >> @intCast(u7, sd -% (LDBL_MANT_DIG + 2))) | + @boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0); + }, + } + // finish + a |= @boolToInt((a & 4) != 0); // Or P into R + a +%= 1; // round - this step may add a significant bit + a >>= 2; // dump Q and R + // a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits + if ((a & (u128(1) << LDBL_MANT_DIG)) != 0) { + a >>= 1; + e +%= 1; + } + // a is now rounded to LDBL_MANT_DIG bits + } else { + a <<= @intCast(u7, LDBL_MANT_DIG -% sd); + // a is now rounded to LDBL_MANT_DIG bits + } + + const high: u128 = (@intCast(u64, (e +% 16383)) << 48) | // exponent + (@truncate(u64, a >> 64) & 0x0000ffffffffffff); // mantissa-high + const low = @truncate(u64, a); // mantissa-low + + return @bitCast(f128, low | (high << 64)); +} + +test "import floatuntitf" { + _ = @import("floatuntitf_test.zig"); +} diff --git a/std/special/compiler_rt/floatuntitf_test.zig b/std/special/compiler_rt/floatuntitf_test.zig new file mode 100644 index 0000000000..8e67fee108 --- /dev/null +++ b/std/special/compiler_rt/floatuntitf_test.zig @@ -0,0 +1,99 @@ +const __floatuntitf = @import("floatuntitf.zig").__floatuntitf; +const assert = @import("std").debug.assert; + +fn test__floatuntitf(a: u128, expected: f128) void { + const x = __floatuntitf(a); + assert(x == expected); +} + +test "floatuntitf" { + test__floatuntitf(0, 0.0); + + test__floatuntitf(1, 1.0); + test__floatuntitf(2, 2.0); + test__floatuntitf(20, 20.0); + + test__floatuntitf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + test__floatuntitf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + test__floatuntitf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + test__floatuntitf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + test__floatuntitf(0x7FFFFFFFFFFFFFFF, 0xF.FFFFFFFFFFFFFFEp+59); + test__floatuntitf(0xFFFFFFFFFFFFFFFE, 0xF.FFFFFFFFFFFFFFEp+60); + test__floatuntitf(0xFFFFFFFFFFFFFFFF, 0xF.FFFFFFFFFFFFFFFp+60); + + test__floatuntitf(0x8000008000000000, 0x8.000008p+60); + test__floatuntitf(0x8000000000000800, 0x8.0000000000008p+60); + test__floatuntitf(0x8000010000000000, 0x8.00001p+60); + test__floatuntitf(0x8000000000001000, 0x8.000000000001p+60); + + test__floatuntitf(0x8000000000000000, 0x8p+60); + test__floatuntitf(0x8000000000000001, 0x8.000000000000001p+60); + + test__floatuntitf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + test__floatuntitf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + test__floatuntitf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + test__floatuntitf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + test__floatuntitf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + test__floatuntitf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + test__floatuntitf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + test__floatuntitf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + test__floatuntitf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + test__floatuntitf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + test__floatuntitf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + test__floatuntitf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + test__floatuntitf(0x023479FD0E092DA1, 0x1.1A3CFE870496D08p+57); + test__floatuntitf(0x023479FD0E092DB0, 0x1.1A3CFE870496D8p+57); + test__floatuntitf(0x023479FD0E092DB8, 0x1.1A3CFE870496DCp+57); + test__floatuntitf(0x023479FD0E092DB6, 0x1.1A3CFE870496DBp+57); + test__floatuntitf(0x023479FD0E092DBF, 0x1.1A3CFE870496DF8p+57); + test__floatuntitf(0x023479FD0E092DC1, 0x1.1A3CFE870496E08p+57); + test__floatuntitf(0x023479FD0E092DC7, 0x1.1A3CFE870496E38p+57); + test__floatuntitf(0x023479FD0E092DC8, 0x1.1A3CFE870496E4p+57); + test__floatuntitf(0x023479FD0E092DCF, 0x1.1A3CFE870496E78p+57); + test__floatuntitf(0x023479FD0E092DD0, 0x1.1A3CFE870496E8p+57); + test__floatuntitf(0x023479FD0E092DD1, 0x1.1A3CFE870496E88p+57); + test__floatuntitf(0x023479FD0E092DD8, 0x1.1A3CFE870496ECp+57); + test__floatuntitf(0x023479FD0E092DDF, 0x1.1A3CFE870496EF8p+57); + test__floatuntitf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + test__floatuntitf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + test__floatuntitf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496D08p+121); + test__floatuntitf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496D8p+121); + test__floatuntitf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496DCp+121); + test__floatuntitf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496DBp+121); + test__floatuntitf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496DF8p+121); + test__floatuntitf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496E08p+121); + test__floatuntitf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496E38p+121); + test__floatuntitf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496E4p+121); + test__floatuntitf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496E78p+121); + test__floatuntitf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496E8p+121); + test__floatuntitf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496E88p+121); + test__floatuntitf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496ECp+121); + test__floatuntitf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496EF8p+121); + test__floatuntitf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); + + test__floatuntitf(make_ti(0, 0xFFFFFFFFFFFFFFFF), 0x1.FFFFFFFFFFFFFFFEp+63); + + test__floatuntitf(make_ti(0xFFFFFFFFFFFFFFFF, 0x0000000000000000), 0x1.FFFFFFFFFFFFFFFEp+127); + test__floatuntitf(make_ti(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), 0x1.0000000000000000p+128); + + test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC2801), 0x1.23456789ABCDEF0123456789ABC3p+124); + test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3000), 0x1.23456789ABCDEF0123456789ABC3p+124); + test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC37FF), 0x1.23456789ABCDEF0123456789ABC3p+124); + test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3800), 0x1.23456789ABCDEF0123456789ABC4p+124); + test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4000), 0x1.23456789ABCDEF0123456789ABC4p+124); + test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC47FF), 0x1.23456789ABCDEF0123456789ABC4p+124); + test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4800), 0x1.23456789ABCDEF0123456789ABC4p+124); + test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4801), 0x1.23456789ABCDEF0123456789ABC5p+124); + test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC57FF), 0x1.23456789ABCDEF0123456789ABC5p+124); +} + +fn make_ti(high: u64, low: u64) u128 { + var result: u128 = high; + result <<= 64; + result |= low; + return result; +} -- cgit v1.2.3 From e19fc4a0a3899ce1a3cb476617b9c19b605877bb Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 28 Jun 2018 21:41:35 +1200 Subject: compiler_rt: Add missing exports --- std/special/compiler_rt/index.zig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index fda8d9d8af..0eca064dda 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -21,12 +21,20 @@ comptime { @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); + @export("__floatunditf", @import("floatunditf.zig").__floatunditf, linkage); + @export("__floatunsitf", @import("floatunsitf.zig").__floatunsitf, linkage); + + @export("__floatuntitf", @import("floatuntitf.zig").__floatuntitf, linkage); @export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage); + @export("__floatuntisf", @import("floatuntisf.zig").__floatuntisf, linkage); + @export("__extenddftf2", @import("extendXfYf2.zig").__extenddftf2, linkage); @export("__extendsftf2", @import("extendXfYf2.zig").__extendsftf2, linkage); @export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage); @export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage); + @export("__trunctfdf2", @import("truncXfYf2.zig").__trunctfdf2, linkage); + @export("__trunctfsf2", @import("truncXfYf2.zig").__trunctfsf2, linkage); @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage); @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage); -- cgit v1.2.3 From 53fef94b9fb2b04d208a0671aa58e90f93f412cf Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 28 Jun 2018 23:14:40 +1200 Subject: compiler_rt: Add missing install targets --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4838aeb797..9957a740cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -585,7 +585,11 @@ set(ZIG_STD_FILES "special/compiler_rt/fixunstfdi.zig" "special/compiler_rt/fixunstfsi.zig" "special/compiler_rt/fixunstfti.zig" + "special/compiler_rt/floatunditf.zig" + "special/compiler_rt/floatunsitf.zig" "special/compiler_rt/floatuntidf.zig" + "special/compiler_rt/floatuntisf.zig" + "special/compiler_rt/floatuntitf.zig" "special/compiler_rt/muloti4.zig" "special/compiler_rt/index.zig" "special/compiler_rt/truncXfYf2.zig" -- cgit v1.2.3 From 814a34f263cbfeabbf7a898b3b70fa781baaccac Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 30 Jun 2018 19:57:17 +1200 Subject: compiler_rt: Add floattitf/floattidf/floattisf --- std/special/compiler_rt/floattidf.zig | 69 +++++++++++++++++++++ std/special/compiler_rt/floattidf_test.zig | 84 ++++++++++++++++++++++++++ std/special/compiler_rt/floattisf.zig | 69 +++++++++++++++++++++ std/special/compiler_rt/floattisf_test.zig | 60 +++++++++++++++++++ std/special/compiler_rt/floattitf.zig | 69 +++++++++++++++++++++ std/special/compiler_rt/floattitf_test.zig | 96 ++++++++++++++++++++++++++++++ std/special/compiler_rt/index.zig | 4 ++ 7 files changed, 451 insertions(+) create mode 100644 std/special/compiler_rt/floattidf.zig create mode 100644 std/special/compiler_rt/floattidf_test.zig create mode 100644 std/special/compiler_rt/floattisf.zig create mode 100644 std/special/compiler_rt/floattisf_test.zig create mode 100644 std/special/compiler_rt/floattitf.zig create mode 100644 std/special/compiler_rt/floattitf_test.zig diff --git a/std/special/compiler_rt/floattidf.zig b/std/special/compiler_rt/floattidf.zig new file mode 100644 index 0000000000..8a627fdc04 --- /dev/null +++ b/std/special/compiler_rt/floattidf.zig @@ -0,0 +1,69 @@ +const builtin = @import("builtin"); +const is_test = builtin.is_test; + +const DBL_MANT_DIG = 53; + +pub extern fn __floattidf(arg: i128) f64 { + @setRuntimeSafety(is_test); + + if (arg == 0) + return 0.0; + + var ai = arg; + const N: u32 = 128; + const si = ai >> @intCast(u7, (N - 1)); + ai = ((ai ^ si) -% si); + var a = @bitCast(u128, ai); + + const sd = @bitCast(i32, N - @clz(a)); // number of significant digits + var e: i32 = sd - 1; // exponent + if (sd > DBL_MANT_DIG) { + // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + // 12345678901234567890123456 + // 1 = msb 1 bit + // P = bit DBL_MANT_DIG-1 bits to the right of 1 + // Q = bit DBL_MANT_DIG bits to the right of 1 + // R = "or" of all bits to the right of Q + switch (sd) { + DBL_MANT_DIG + 1 => { + a <<= 1; + }, + DBL_MANT_DIG + 2 => {}, + else => { + const shift1_amt = @intCast(i32, sd - (DBL_MANT_DIG + 2)); + const shift1_amt_u7 = @intCast(u7, shift1_amt); + + const shift2_amt = @intCast(i32, N + (DBL_MANT_DIG + 2)) - sd; + const shift2_amt_u7 = @intCast(u7, shift2_amt); + + a = (a >> shift1_amt_u7) | @boolToInt((a & (@intCast(u128, @maxValue(u128)) >> shift2_amt_u7)) != 0); + }, + } + // finish + a |= @boolToInt((a & 4) != 0); // Or P into R + a +%= 1; // round - this step may add a significant bit + a >>= 2; // dump Q and R + // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits + if ((a & (u128(1) << DBL_MANT_DIG)) != 0) { + a >>= 1; + e +%= 1; + } + // a is now rounded to DBL_MANT_DIG bits + } else { + a <<= @intCast(u7, DBL_MANT_DIG - sd); + // a is now rounded to DBL_MANT_DIG bits + } + + const s = @bitCast(u128, arg) >> (128 - 32); + const high: u64 = (@intCast(u64, s) & 0x80000000) | // sign + (@intCast(u32, (e + 1023)) << 20) | // exponent + (@truncate(u32, a >> 32) & 0x000fffff); // mantissa-high + const low: u64 = @truncate(u32, a); // mantissa-low + + return @bitCast(f64, low | (high << 32)); +} + +test "import floattidf" { + _ = @import("floattidf_test.zig"); +} diff --git a/std/special/compiler_rt/floattidf_test.zig b/std/special/compiler_rt/floattidf_test.zig new file mode 100644 index 0000000000..25dc595052 --- /dev/null +++ b/std/special/compiler_rt/floattidf_test.zig @@ -0,0 +1,84 @@ +const __floattidf = @import("floattidf.zig").__floattidf; +const assert = @import("std").debug.assert; + +fn test__floattidf(a: i128, expected: f64) void { + const x = __floattidf(a); + assert(x == expected); +} + +test "floattidf" { + test__floattidf(0, 0.0); + + test__floattidf(1, 1.0); + test__floattidf(2, 2.0); + test__floattidf(20, 20.0); + test__floattidf(-1, -1.0); + test__floattidf(-2, -2.0); + test__floattidf(-20, -20.0); + + test__floattidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + test__floattidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + test__floattidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + test__floattidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + + test__floattidf(make_ti(0x8000008000000000, 0), -0x1.FFFFFEp+126); + test__floattidf(make_ti(0x8000000000000800, 0), -0x1.FFFFFFFFFFFFEp+126); + test__floattidf(make_ti(0x8000010000000000, 0), -0x1.FFFFFCp+126); + test__floattidf(make_ti(0x8000000000001000, 0), -0x1.FFFFFFFFFFFFCp+126); + + test__floattidf(make_ti(0x8000000000000000, 0), -0x1.000000p+127); + test__floattidf(make_ti(0x8000000000000001, 0), -0x1.000000p+127); + + test__floattidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + test__floattidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + test__floattidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + test__floattidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + test__floattidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + test__floattidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + test__floattidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + test__floattidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + test__floattidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + test__floattidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + test__floattidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + test__floattidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + test__floattidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); + test__floattidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); + test__floattidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); + test__floattidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); + test__floattidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); + test__floattidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); + test__floattidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); + test__floattidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); + test__floattidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); + test__floattidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); + test__floattidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); + test__floattidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); + test__floattidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); + test__floattidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + test__floattidf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + test__floattidf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121); + test__floattidf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121); + test__floattidf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121); + test__floattidf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121); + test__floattidf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121); + test__floattidf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121); + test__floattidf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121); + test__floattidf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121); + test__floattidf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121); + test__floattidf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121); + test__floattidf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121); + test__floattidf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121); + test__floattidf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121); + test__floattidf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); +} + +fn make_ti(high: u64, low: u64) i128 { + var result: u128 = high; + result <<= 64; + result |= low; + return @bitCast(i128, result); +} diff --git a/std/special/compiler_rt/floattisf.zig b/std/special/compiler_rt/floattisf.zig new file mode 100644 index 0000000000..cbdd30418f --- /dev/null +++ b/std/special/compiler_rt/floattisf.zig @@ -0,0 +1,69 @@ +const builtin = @import("builtin"); +const is_test = builtin.is_test; + +const FLT_MANT_DIG = 24; + +pub extern fn __floattisf(arg: i128) f32 { + @setRuntimeSafety(is_test); + + if (arg == 0) + return 0.0; + + var ai = arg; + const N: u32 = 128; + const si = ai >> @intCast(u7, (N - 1)); + ai = ((ai ^ si) -% si); + var a = @bitCast(u128, ai); + + const sd = @bitCast(i32, N - @clz(a)); // number of significant digits + var e: i32 = sd - 1; // exponent + + if (sd > FLT_MANT_DIG) { + // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + // 12345678901234567890123456 + // 1 = msb 1 bit + // P = bit FLT_MANT_DIG-1 bits to the right of 1 + // Q = bit FLT_MANT_DIG bits to the right of 1 + // R = "or" of all bits to the right of Q + switch (sd) { + FLT_MANT_DIG + 1 => { + a <<= 1; + }, + FLT_MANT_DIG + 2 => {}, + else => { + const shift1_amt = @intCast(i32, sd - (FLT_MANT_DIG + 2)); + const shift1_amt_u7 = @intCast(u7, shift1_amt); + + const shift2_amt = @intCast(i32, N + (FLT_MANT_DIG + 2)) - sd; + const shift2_amt_u7 = @intCast(u7, shift2_amt); + + a = (a >> shift1_amt_u7) | @boolToInt((a & (@intCast(u128, @maxValue(u128)) >> shift2_amt_u7)) != 0); + }, + } + // finish + a |= @boolToInt((a & 4) != 0); // Or P into R + a +%= 1; // round - this step may add a significant bit + a >>= 2; // dump Q and R + // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits + if ((a & (u128(1) << FLT_MANT_DIG)) != 0) { + a >>= 1; + e +%= 1; + } + // a is now rounded to FLT_MANT_DIG bits + } else { + a <<= @intCast(u7, FLT_MANT_DIG - sd); + // a is now rounded to FLT_MANT_DIG bits + } + + const s = @bitCast(u128, arg) >> (128 - 32); + const r = (@intCast(u32, s) & 0x80000000) | // sign + (@intCast(u32, (e + 127)) << 23) | // exponent + (@truncate(u32, a) & 0x007fffff); // mantissa-high + + return @bitCast(f32, r); +} + +test "import floattisf" { + _ = @import("floattisf_test.zig"); +} diff --git a/std/special/compiler_rt/floattisf_test.zig b/std/special/compiler_rt/floattisf_test.zig new file mode 100644 index 0000000000..ecb8eac60a --- /dev/null +++ b/std/special/compiler_rt/floattisf_test.zig @@ -0,0 +1,60 @@ +const __floattisf = @import("floattisf.zig").__floattisf; +const assert = @import("std").debug.assert; + +fn test__floattisf(a: i128, expected: f32) void { + const x = __floattisf(a); + assert(x == expected); +} + +test "floattisf" { + test__floattisf(0, 0.0); + + test__floattisf(1, 1.0); + test__floattisf(2, 2.0); + test__floattisf(-1, -1.0); + test__floattisf(-2, -2.0); + + test__floattisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + test__floattisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + + test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000008000000000), -0x1.FFFFFEp+62); + test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000010000000000), -0x1.FFFFFCp+62); + + test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000000000000000), -0x1.000000p+63); + test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000000000000001), -0x1.000000p+63); + + test__floattisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + test__floattisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); + test__floattisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); + test__floattisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); + test__floattisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); + test__floattisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); + + test__floattisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); + test__floattisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); + test__floattisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); + test__floattisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); + test__floattisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); + + test__floattisf(make_ti(0x0007FB72E8000000, 0), 0x1.FEDCBAp+114); + + test__floattisf(make_ti(0x0007FB72EA000000, 0), 0x1.FEDCBAp+114); + test__floattisf(make_ti(0x0007FB72EB000000, 0), 0x1.FEDCBAp+114); + test__floattisf(make_ti(0x0007FB72EBFFFFFF, 0), 0x1.FEDCBAp+114); + test__floattisf(make_ti(0x0007FB72EC000000, 0), 0x1.FEDCBCp+114); + test__floattisf(make_ti(0x0007FB72E8000001, 0), 0x1.FEDCBAp+114); + + test__floattisf(make_ti(0x0007FB72E6000000, 0), 0x1.FEDCBAp+114); + test__floattisf(make_ti(0x0007FB72E7000000, 0), 0x1.FEDCBAp+114); + test__floattisf(make_ti(0x0007FB72E7FFFFFF, 0), 0x1.FEDCBAp+114); + test__floattisf(make_ti(0x0007FB72E4000001, 0), 0x1.FEDCBAp+114); + test__floattisf(make_ti(0x0007FB72E4000000, 0), 0x1.FEDCB8p+114); +} + +fn make_ti(high: u64, low: u64) i128 { + var result: u128 = high; + result <<= 64; + result |= low; + return @bitCast(i128, result); +} diff --git a/std/special/compiler_rt/floattitf.zig b/std/special/compiler_rt/floattitf.zig new file mode 100644 index 0000000000..531634e494 --- /dev/null +++ b/std/special/compiler_rt/floattitf.zig @@ -0,0 +1,69 @@ +const builtin = @import("builtin"); +const is_test = builtin.is_test; + +const LDBL_MANT_DIG = 113; + +pub extern fn __floattitf(arg: i128) f128 { + @setRuntimeSafety(is_test); + + if (arg == 0) + return 0.0; + + var ai = arg; + const N: u32 = 128; + const si = ai >> @intCast(u7, (N - 1)); + ai = ((ai ^ si) -% si); + var a = @bitCast(u128, ai); + + const sd = @bitCast(i32, N - @clz(a)); // number of significant digits + var e: i32 = sd - 1; // exponent + if (sd > LDBL_MANT_DIG) { + // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + // 12345678901234567890123456 + // 1 = msb 1 bit + // P = bit LDBL_MANT_DIG-1 bits to the right of 1 + // Q = bit LDBL_MANT_DIG bits to the right of 1 + // R = "or" of all bits to the right of Q + switch (sd) { + LDBL_MANT_DIG + 1 => { + a <<= 1; + }, + LDBL_MANT_DIG + 2 => {}, + else => { + const shift1_amt = @intCast(i32, sd - (LDBL_MANT_DIG + 2)); + const shift1_amt_u7 = @intCast(u7, shift1_amt); + + const shift2_amt = @intCast(i32, N + (LDBL_MANT_DIG + 2)) - sd; + const shift2_amt_u7 = @intCast(u7, shift2_amt); + + a = (a >> shift1_amt_u7) | @boolToInt((a & (@intCast(u128, @maxValue(u128)) >> shift2_amt_u7)) != 0); + }, + } + // finish + a |= @boolToInt((a & 4) != 0); // Or P into R + a +%= 1; // round - this step may add a significant bit + a >>= 2; // dump Q and R + // a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits + if ((a & (u128(1) << LDBL_MANT_DIG)) != 0) { + a >>= 1; + e +%= 1; + } + // a is now rounded to LDBL_MANT_DIG bits + } else { + a <<= @intCast(u7, LDBL_MANT_DIG - sd); + // a is now rounded to LDBL_MANT_DIG bits + } + + const s = @bitCast(u128, arg) >> (128 - 64); + const high: u128 = (@intCast(u64, s) & 0x8000000000000000) | // sign + (@intCast(u64, (e + 16383)) << 48) | // exponent + (@truncate(u64, a >> 64) & 0x0000ffffffffffff); // mantissa-high + const low = @truncate(u64, a); // mantissa-low + + return @bitCast(f128, low | (high << 64)); +} + +test "import floattitf" { + _ = @import("floattitf_test.zig"); +} diff --git a/std/special/compiler_rt/floattitf_test.zig b/std/special/compiler_rt/floattitf_test.zig new file mode 100644 index 0000000000..da2ccc8b35 --- /dev/null +++ b/std/special/compiler_rt/floattitf_test.zig @@ -0,0 +1,96 @@ +const __floattitf = @import("floattitf.zig").__floattitf; +const assert = @import("std").debug.assert; + +fn test__floattitf(a: i128, expected: f128) void { + const x = __floattitf(a); + assert(x == expected); +} + +test "floattitf" { + test__floattitf(0, 0.0); + + test__floattitf(1, 1.0); + test__floattitf(2, 2.0); + test__floattitf(20, 20.0); + test__floattitf(-1, -1.0); + test__floattitf(-2, -2.0); + test__floattitf(-20, -20.0); + + test__floattitf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + test__floattitf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + test__floattitf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + test__floattitf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + + test__floattitf(make_ti(0x8000008000000000, 0), -0x1.FFFFFEp+126); + test__floattitf(make_ti(0x8000000000000800, 0), -0x1.FFFFFFFFFFFFEp+126); + test__floattitf(make_ti(0x8000010000000000, 0), -0x1.FFFFFCp+126); + test__floattitf(make_ti(0x8000000000001000, 0), -0x1.FFFFFFFFFFFFCp+126); + + test__floattitf(make_ti(0x8000000000000000, 0), -0x1.000000p+127); + test__floattitf(make_ti(0x8000000000000001, 0), -0x1.FFFFFFFFFFFFFFFCp+126); + + test__floattitf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + test__floattitf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + test__floattitf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + test__floattitf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + test__floattitf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + test__floattitf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + test__floattitf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + test__floattitf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + test__floattitf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + test__floattitf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + test__floattitf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + test__floattitf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + test__floattitf(0x023479FD0E092DA1, 0x1.1A3CFE870496D08p+57); + test__floattitf(0x023479FD0E092DB0, 0x1.1A3CFE870496D8p+57); + test__floattitf(0x023479FD0E092DB8, 0x1.1A3CFE870496DCp+57); + test__floattitf(0x023479FD0E092DB6, 0x1.1A3CFE870496DBp+57); + test__floattitf(0x023479FD0E092DBF, 0x1.1A3CFE870496DF8p+57); + test__floattitf(0x023479FD0E092DC1, 0x1.1A3CFE870496E08p+57); + test__floattitf(0x023479FD0E092DC7, 0x1.1A3CFE870496E38p+57); + test__floattitf(0x023479FD0E092DC8, 0x1.1A3CFE870496E4p+57); + test__floattitf(0x023479FD0E092DCF, 0x1.1A3CFE870496E78p+57); + test__floattitf(0x023479FD0E092DD0, 0x1.1A3CFE870496E8p+57); + test__floattitf(0x023479FD0E092DD1, 0x1.1A3CFE870496E88p+57); + test__floattitf(0x023479FD0E092DD8, 0x1.1A3CFE870496ECp+57); + test__floattitf(0x023479FD0E092DDF, 0x1.1A3CFE870496EF8p+57); + test__floattitf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + test__floattitf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + test__floattitf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496D08p+121); + test__floattitf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496D8p+121); + test__floattitf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496DCp+121); + test__floattitf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496DBp+121); + test__floattitf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496DF8p+121); + test__floattitf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496E08p+121); + test__floattitf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496E38p+121); + test__floattitf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496E4p+121); + test__floattitf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496E78p+121); + test__floattitf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496E8p+121); + test__floattitf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496E88p+121); + test__floattitf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496ECp+121); + test__floattitf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496EF8p+121); + test__floattitf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); + + test__floattitf(make_ti(0, 0xFFFFFFFFFFFFFFFF), 0x1.FFFFFFFFFFFFFFFEp+63); + + test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC2801), 0x1.23456789ABCDEF0123456789ABC3p+124); + test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3000), 0x1.23456789ABCDEF0123456789ABC3p+124); + test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC37FF), 0x1.23456789ABCDEF0123456789ABC3p+124); + test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3800), 0x1.23456789ABCDEF0123456789ABC4p+124); + test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4000), 0x1.23456789ABCDEF0123456789ABC4p+124); + test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC47FF), 0x1.23456789ABCDEF0123456789ABC4p+124); + test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4800), 0x1.23456789ABCDEF0123456789ABC4p+124); + test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4801), 0x1.23456789ABCDEF0123456789ABC5p+124); + test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC57FF), 0x1.23456789ABCDEF0123456789ABC5p+124); +} + +fn make_ti(high: u64, low: u64) i128 { + var result: u128 = high; + result <<= 64; + result |= low; + return @bitCast(i128, result); +} diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index 0eca064dda..54a461d0f1 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -21,6 +21,10 @@ comptime { @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); + @export("__floattitf", @import("floattitf.zig").__floattitf, linkage); + @export("__floattidf", @import("floattidf.zig").__floattidf, linkage); + @export("__floattisf", @import("floattisf.zig").__floattisf, linkage); + @export("__floatunditf", @import("floatunditf.zig").__floatunditf, linkage); @export("__floatunsitf", @import("floatunsitf.zig").__floatunsitf, linkage); -- cgit v1.2.3 From 9f48b2ab48bcd6cfba45b8dfc60d0ad9633b294e Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 30 Jun 2018 20:06:30 +1200 Subject: compiler_rt: Remove wrapping add/sub operators where unneeded Closes #495. --- std/special/compiler_rt/floattidf.zig | 4 ++-- std/special/compiler_rt/floattisf.zig | 4 ++-- std/special/compiler_rt/floattitf.zig | 4 ++-- std/special/compiler_rt/floatunditf.zig | 2 +- std/special/compiler_rt/floatunsitf.zig | 2 +- std/special/compiler_rt/floatuntidf.zig | 16 ++++++++-------- std/special/compiler_rt/floatuntisf.zig | 16 ++++++++-------- std/special/compiler_rt/floatuntitf.zig | 16 ++++++++-------- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/std/special/compiler_rt/floattidf.zig b/std/special/compiler_rt/floattidf.zig index 8a627fdc04..2a24c64efe 100644 --- a/std/special/compiler_rt/floattidf.zig +++ b/std/special/compiler_rt/floattidf.zig @@ -42,12 +42,12 @@ pub extern fn __floattidf(arg: i128) f64 { } // finish a |= @boolToInt((a & 4) != 0); // Or P into R - a +%= 1; // round - this step may add a significant bit + a += 1; // round - this step may add a significant bit a >>= 2; // dump Q and R // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits if ((a & (u128(1) << DBL_MANT_DIG)) != 0) { a >>= 1; - e +%= 1; + e += 1; } // a is now rounded to DBL_MANT_DIG bits } else { diff --git a/std/special/compiler_rt/floattisf.zig b/std/special/compiler_rt/floattisf.zig index cbdd30418f..4618a86444 100644 --- a/std/special/compiler_rt/floattisf.zig +++ b/std/special/compiler_rt/floattisf.zig @@ -43,12 +43,12 @@ pub extern fn __floattisf(arg: i128) f32 { } // finish a |= @boolToInt((a & 4) != 0); // Or P into R - a +%= 1; // round - this step may add a significant bit + a += 1; // round - this step may add a significant bit a >>= 2; // dump Q and R // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits if ((a & (u128(1) << FLT_MANT_DIG)) != 0) { a >>= 1; - e +%= 1; + e += 1; } // a is now rounded to FLT_MANT_DIG bits } else { diff --git a/std/special/compiler_rt/floattitf.zig b/std/special/compiler_rt/floattitf.zig index 531634e494..4da2c145fa 100644 --- a/std/special/compiler_rt/floattitf.zig +++ b/std/special/compiler_rt/floattitf.zig @@ -42,12 +42,12 @@ pub extern fn __floattitf(arg: i128) f128 { } // finish a |= @boolToInt((a & 4) != 0); // Or P into R - a +%= 1; // round - this step may add a significant bit + a += 1; // round - this step may add a significant bit a >>= 2; // dump Q and R // a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits if ((a & (u128(1) << LDBL_MANT_DIG)) != 0) { a >>= 1; - e +%= 1; + e += 1; } // a is now rounded to LDBL_MANT_DIG bits } else { diff --git a/std/special/compiler_rt/floatunditf.zig b/std/special/compiler_rt/floatunditf.zig index b8b097581f..12d6a5613f 100644 --- a/std/special/compiler_rt/floatunditf.zig +++ b/std/special/compiler_rt/floatunditf.zig @@ -1,6 +1,6 @@ const builtin = @import("builtin"); const is_test = builtin.is_test; -const std = @import("../../index.zig"); +const std = @import("std"); pub extern fn __floatunditf(a: u128) f128 { @setRuntimeSafety(is_test); diff --git a/std/special/compiler_rt/floatunsitf.zig b/std/special/compiler_rt/floatunsitf.zig index dfee826b32..625f90a3d0 100644 --- a/std/special/compiler_rt/floatunsitf.zig +++ b/std/special/compiler_rt/floatunsitf.zig @@ -1,6 +1,6 @@ const builtin = @import("builtin"); const is_test = builtin.is_test; -const std = @import("../../index.zig"); +const std = @import("std"); pub extern fn __floatunsitf(a: u64) f128 { @setRuntimeSafety(is_test); diff --git a/std/special/compiler_rt/floatuntidf.zig b/std/special/compiler_rt/floatuntidf.zig index 3aabcb7b8a..1101733825 100644 --- a/std/special/compiler_rt/floatuntidf.zig +++ b/std/special/compiler_rt/floatuntidf.zig @@ -11,8 +11,8 @@ pub extern fn __floatuntidf(arg: u128) f64 { var a = arg; const N: u32 = @sizeOf(u128) * 8; - const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits - var e: i32 = sd -% 1; // exponent + const sd = @bitCast(i32, N - @clz(a)); // number of significant digits + var e: i32 = sd - 1; // exponent if (sd > DBL_MANT_DIG) { // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR @@ -27,28 +27,28 @@ pub extern fn __floatuntidf(arg: u128) f64 { }, DBL_MANT_DIG + 2 => {}, else => { - const shift_amt = @bitCast(i32, N +% (DBL_MANT_DIG + 2)) -% sd; + const shift_amt = @bitCast(i32, N + (DBL_MANT_DIG + 2)) - sd; const shift_amt_u7 = @intCast(u7, shift_amt); - a = (a >> @intCast(u7, sd -% (DBL_MANT_DIG + 2))) | + a = (a >> @intCast(u7, sd - (DBL_MANT_DIG + 2))) | @boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0); }, } // finish a |= @boolToInt((a & 4) != 0); // Or P into R - a +%= 1; // round - this step may add a significant bit + a += 1; // round - this step may add a significant bit a >>= 2; // dump Q and R // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits if ((a & (u128(1) << DBL_MANT_DIG)) != 0) { a >>= 1; - e +%= 1; + e += 1; } // a is now rounded to DBL_MANT_DIG bits } else { - a <<= @intCast(u7, DBL_MANT_DIG -% sd); + a <<= @intCast(u7, DBL_MANT_DIG - sd); // a is now rounded to DBL_MANT_DIG bits } - const high: u64 = @bitCast(u32, (e +% 1023) << 20) | // exponent + const high: u64 = @bitCast(u32, (e + 1023) << 20) | // exponent (@truncate(u32, a >> 32) & 0x000FFFFF); // mantissa-high const low = @truncate(u32, a); // mantissa-low diff --git a/std/special/compiler_rt/floatuntisf.zig b/std/special/compiler_rt/floatuntisf.zig index e83affd87c..f85c22578e 100644 --- a/std/special/compiler_rt/floatuntisf.zig +++ b/std/special/compiler_rt/floatuntisf.zig @@ -11,8 +11,8 @@ pub extern fn __floatuntisf(arg: u128) f32 { var a = arg; const N: u32 = @sizeOf(u128) * 8; - const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits - var e: i32 = sd -% 1; // exponent + const sd = @bitCast(i32, N - @clz(a)); // number of significant digits + var e: i32 = sd - 1; // exponent if (sd > FLT_MANT_DIG) { // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR @@ -27,28 +27,28 @@ pub extern fn __floatuntisf(arg: u128) f32 { }, FLT_MANT_DIG + 2 => {}, else => { - const shift_amt = @bitCast(i32, N +% (FLT_MANT_DIG + 2)) -% sd; + const shift_amt = @bitCast(i32, N + (FLT_MANT_DIG + 2)) - sd; const shift_amt_u7 = @intCast(u7, shift_amt); - a = (a >> @intCast(u7, sd -% (FLT_MANT_DIG + 2))) | + a = (a >> @intCast(u7, sd - (FLT_MANT_DIG + 2))) | @boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0); }, } // finish a |= @boolToInt((a & 4) != 0); // Or P into R - a +%= 1; // round - this step may add a significant bit + a += 1; // round - this step may add a significant bit a >>= 2; // dump Q and R // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits if ((a & (u128(1) << FLT_MANT_DIG)) != 0) { a >>= 1; - e +%= 1; + e += 1; } // a is now rounded to FLT_MANT_DIG bits } else { - a <<= @intCast(u7, FLT_MANT_DIG -% sd); + a <<= @intCast(u7, FLT_MANT_DIG - sd); // a is now rounded to FLT_MANT_DIG bits } - const high = @bitCast(u32, (e +% 127) << 23); // exponent + const high = @bitCast(u32, (e + 127) << 23); // exponent const low = @truncate(u32, a) & 0x007fffff; // mantissa return @bitCast(f32, high | low); diff --git a/std/special/compiler_rt/floatuntitf.zig b/std/special/compiler_rt/floatuntitf.zig index a64dd96a34..6354c89287 100644 --- a/std/special/compiler_rt/floatuntitf.zig +++ b/std/special/compiler_rt/floatuntitf.zig @@ -11,8 +11,8 @@ pub extern fn __floatuntitf(arg: u128) f128 { var a = arg; const N: u32 = @sizeOf(u128) * 8; - const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits - var e: i32 = sd -% 1; // exponent + const sd = @bitCast(i32, N - @clz(a)); // number of significant digits + var e: i32 = sd - 1; // exponent if (sd > LDBL_MANT_DIG) { // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR @@ -27,28 +27,28 @@ pub extern fn __floatuntitf(arg: u128) f128 { }, LDBL_MANT_DIG + 2 => {}, else => { - const shift_amt = @bitCast(i32, N +% (LDBL_MANT_DIG + 2)) -% sd; + const shift_amt = @bitCast(i32, N + (LDBL_MANT_DIG + 2)) - sd; const shift_amt_u7 = @intCast(u7, shift_amt); - a = (a >> @intCast(u7, sd -% (LDBL_MANT_DIG + 2))) | + a = (a >> @intCast(u7, sd - (LDBL_MANT_DIG + 2))) | @boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0); }, } // finish a |= @boolToInt((a & 4) != 0); // Or P into R - a +%= 1; // round - this step may add a significant bit + a += 1; // round - this step may add a significant bit a >>= 2; // dump Q and R // a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits if ((a & (u128(1) << LDBL_MANT_DIG)) != 0) { a >>= 1; - e +%= 1; + e += 1; } // a is now rounded to LDBL_MANT_DIG bits } else { - a <<= @intCast(u7, LDBL_MANT_DIG -% sd); + a <<= @intCast(u7, LDBL_MANT_DIG - sd); // a is now rounded to LDBL_MANT_DIG bits } - const high: u128 = (@intCast(u64, (e +% 16383)) << 48) | // exponent + const high: u128 = (@intCast(u64, (e + 16383)) << 48) | // exponent (@truncate(u64, a >> 64) & 0x0000ffffffffffff); // mantissa-high const low = @truncate(u64, a); // mantissa-low -- cgit v1.2.3 From 951512f5ae52e41d3f2bdcb9a533668bcca3a9cd Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 30 Jun 2018 20:11:21 +1200 Subject: compiler_rt: Add CMake entries --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9957a740cf..87c0351059 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -590,6 +590,9 @@ set(ZIG_STD_FILES "special/compiler_rt/floatuntidf.zig" "special/compiler_rt/floatuntisf.zig" "special/compiler_rt/floatuntitf.zig" + "special/compiler_rt/floattidf.zig" + "special/compiler_rt/floattisf.zig" + "special/compiler_rt/floattitf.zig" "special/compiler_rt/muloti4.zig" "special/compiler_rt/index.zig" "special/compiler_rt/truncXfYf2.zig" -- cgit v1.2.3 From 887c97742f86071ffab2a79443d48c4153b63ad6 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 30 Jun 2018 20:15:02 +1200 Subject: Alignment fix and allow rudimentary f128 float printing --- std/fmt/index.zig | 2 +- std/special/compiler_rt/floatunditf.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index f4dfa0e324..bf12e86fef 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -327,7 +327,7 @@ pub fn formatFloatScientific( comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, ) Errors!void { - var x = f64(value); + var x = @floatCast(f64, value); // Errol doesn't handle these special cases. if (math.signbit(x)) { diff --git a/std/special/compiler_rt/floatunditf.zig b/std/special/compiler_rt/floatunditf.zig index 12d6a5613f..afc545448a 100644 --- a/std/special/compiler_rt/floatunditf.zig +++ b/std/special/compiler_rt/floatunditf.zig @@ -17,7 +17,7 @@ pub extern fn __floatunditf(a: u128) f128 { const exp = (u128.bit_count - 1) - @clz(a); const shift = mantissa_bits - @intCast(u7, exp); - var result: u128 = (a << shift) ^ implicit_bit; + var result: u128 align(16) = (a << shift) ^ implicit_bit; result += (@intCast(u128, exp) + exponent_bias) << mantissa_bits; return @bitCast(f128, result); -- cgit v1.2.3 From 616fe798c801baa5fa7238f5fc576a5090938999 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 30 Jun 2018 17:35:05 +0200 Subject: Revert "contains_comptime_undefined_value should not follow pointers" This reverts commit 58b1692182dc2f8da5b535f59e9a89cfab10a7b6. --- src/analyze.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 4c200888d8..068ea48c0a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5296,6 +5296,41 @@ bool contains_comptime_undefined_value(ConstExprValue *value) { switch (value->type->id) { case TypeTableEntryIdInvalid: zig_unreachable(); + + case TypeTableEntryIdPointer: { + ConstPtrValue *ptr = &value->data.x_ptr; + if (ptr->mut == ConstPtrMutRuntimeVar) + return false; + + switch (ptr->special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + return contains_comptime_undefined_value(ptr->data.ref.pointee); + case ConstPtrSpecialBaseArray: { + size_t index = ptr->data.base_array.elem_index; + ConstExprValue *arr = ptr->data.base_array.array_val; + if (arr->special == ConstValSpecialUndef) + return true; + if (arr->data.x_array.special == ConstArraySpecialUndef) + return true; + + return contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[index]); + } + case ConstPtrSpecialBaseStruct: { + size_t index = ptr->data.base_struct.field_index; + ConstExprValue *str = ptr->data.base_struct.struct_val; + if (str->special == ConstValSpecialUndef) + return true; + + return contains_comptime_undefined_value(&str->data.x_struct.fields[index]); + } + case ConstPtrSpecialFunction: // TODO: Can a fn ptr have an undefined value? + case ConstPtrSpecialDiscard: + case ConstPtrSpecialHardCodedAddr: + return false; + } + } case TypeTableEntryIdArray: { ConstArrayValue *arr = &value->data.x_array; if (arr->special == ConstArraySpecialUndef) @@ -5309,6 +5344,42 @@ bool contains_comptime_undefined_value(ConstExprValue *value) { } case TypeTableEntryIdStruct: { ConstStructValue *str = &value->data.x_struct; + if (value->type->data.structure.is_slice) { + ConstExprValue *len = &str->fields[slice_len_index]; + ConstExprValue *ptr = &str->fields[slice_ptr_index]; + if (len->special == ConstValSpecialUndef) + return true; + if (ptr->special == ConstValSpecialUndef) + return true; + + switch (ptr->data.x_ptr.special) { + case ConstPtrSpecialRef: + return contains_comptime_undefined_value(ptr->data.x_ptr.data.ref.pointee); + case ConstPtrSpecialBaseArray: { + size_t offset = ptr->data.x_ptr.data.base_array.elem_index; + ConstExprValue *arr = ptr->data.x_ptr.data.base_array.array_val; + if (arr->special == ConstValSpecialUndef) + return true; + if (arr->data.x_array.special == ConstArraySpecialUndef) + return true; + + uint64_t slice_len = bigint_as_unsigned(&len->data.x_bigint); + for (size_t i = 0; i < slice_len; ++i) { + if (contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[i + offset])) + return true; + } + + return false; + } + case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialInvalid: + case ConstPtrSpecialFunction: + case ConstPtrSpecialDiscard: + case ConstPtrSpecialHardCodedAddr: + zig_unreachable(); + } + } + for (size_t i = 0; i < value->type->data.structure.src_field_count; ++i) { if (contains_comptime_undefined_value(&str->fields[i])) return true; @@ -5329,7 +5400,6 @@ bool contains_comptime_undefined_value(ConstExprValue *value) { case TypeTableEntryIdUnion: return contains_comptime_undefined_value(value->data.x_union.payload); - case TypeTableEntryIdPointer: case TypeTableEntryIdArgTuple: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: -- cgit v1.2.3 From 01bd5c46e177ae59f72197063c374e845eea3ff3 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 30 Jun 2018 17:35:06 +0200 Subject: Revert "ir_resolve_const now checks recursivly for undef values" This reverts commit 4c3f27ce1ea17b5236a022971ebace73a02b7c2b. --- src/analyze.cpp | 135 ------------------------------------------------ src/analyze.hpp | 1 - src/ir.cpp | 11 +--- test/compile_errors.zig | 15 ------ 4 files changed, 2 insertions(+), 160 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 068ea48c0a..b3a302a1d4 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5288,141 +5288,6 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_ return const_val; } -bool contains_comptime_undefined_value(ConstExprValue *value) { - assert(value->special != ConstValSpecialRuntime); - if (value->special == ConstValSpecialUndef) - return true; - - switch (value->type->id) { - case TypeTableEntryIdInvalid: - zig_unreachable(); - - case TypeTableEntryIdPointer: { - ConstPtrValue *ptr = &value->data.x_ptr; - if (ptr->mut == ConstPtrMutRuntimeVar) - return false; - - switch (ptr->special) { - case ConstPtrSpecialInvalid: - zig_unreachable(); - case ConstPtrSpecialRef: - return contains_comptime_undefined_value(ptr->data.ref.pointee); - case ConstPtrSpecialBaseArray: { - size_t index = ptr->data.base_array.elem_index; - ConstExprValue *arr = ptr->data.base_array.array_val; - if (arr->special == ConstValSpecialUndef) - return true; - if (arr->data.x_array.special == ConstArraySpecialUndef) - return true; - - return contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[index]); - } - case ConstPtrSpecialBaseStruct: { - size_t index = ptr->data.base_struct.field_index; - ConstExprValue *str = ptr->data.base_struct.struct_val; - if (str->special == ConstValSpecialUndef) - return true; - - return contains_comptime_undefined_value(&str->data.x_struct.fields[index]); - } - case ConstPtrSpecialFunction: // TODO: Can a fn ptr have an undefined value? - case ConstPtrSpecialDiscard: - case ConstPtrSpecialHardCodedAddr: - return false; - } - } - case TypeTableEntryIdArray: { - ConstArrayValue *arr = &value->data.x_array; - if (arr->special == ConstArraySpecialUndef) - return true; - - for (size_t i = 0; i < value->type->data.array.len; ++i) { - if (contains_comptime_undefined_value(&arr->s_none.elements[i])) - return true; - } - return false; - } - case TypeTableEntryIdStruct: { - ConstStructValue *str = &value->data.x_struct; - if (value->type->data.structure.is_slice) { - ConstExprValue *len = &str->fields[slice_len_index]; - ConstExprValue *ptr = &str->fields[slice_ptr_index]; - if (len->special == ConstValSpecialUndef) - return true; - if (ptr->special == ConstValSpecialUndef) - return true; - - switch (ptr->data.x_ptr.special) { - case ConstPtrSpecialRef: - return contains_comptime_undefined_value(ptr->data.x_ptr.data.ref.pointee); - case ConstPtrSpecialBaseArray: { - size_t offset = ptr->data.x_ptr.data.base_array.elem_index; - ConstExprValue *arr = ptr->data.x_ptr.data.base_array.array_val; - if (arr->special == ConstValSpecialUndef) - return true; - if (arr->data.x_array.special == ConstArraySpecialUndef) - return true; - - uint64_t slice_len = bigint_as_unsigned(&len->data.x_bigint); - for (size_t i = 0; i < slice_len; ++i) { - if (contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[i + offset])) - return true; - } - - return false; - } - case ConstPtrSpecialBaseStruct: - case ConstPtrSpecialInvalid: - case ConstPtrSpecialFunction: - case ConstPtrSpecialDiscard: - case ConstPtrSpecialHardCodedAddr: - zig_unreachable(); - } - } - - for (size_t i = 0; i < value->type->data.structure.src_field_count; ++i) { - if (contains_comptime_undefined_value(&str->fields[i])) - return true; - } - return false; - } - case TypeTableEntryIdOptional: - if (value->data.x_optional == nullptr) - return false; - - return contains_comptime_undefined_value(value->data.x_optional); - case TypeTableEntryIdErrorUnion: - // TODO: Can error union error be undefined? - if (value->data.x_err_union.err != nullptr) - return false; - - return contains_comptime_undefined_value(value->data.x_err_union.payload); - case TypeTableEntryIdUnion: - return contains_comptime_undefined_value(value->data.x_union.payload); - - case TypeTableEntryIdArgTuple: - case TypeTableEntryIdVoid: - case TypeTableEntryIdBool: - case TypeTableEntryIdUnreachable: - case TypeTableEntryIdInt: - case TypeTableEntryIdFloat: - case TypeTableEntryIdComptimeFloat: - case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefined: - case TypeTableEntryIdNull: - case TypeTableEntryIdErrorSet: - case TypeTableEntryIdEnum: - case TypeTableEntryIdFn: - case TypeTableEntryIdNamespace: - case TypeTableEntryIdBlock: - case TypeTableEntryIdBoundFn: - case TypeTableEntryIdMetaType: - case TypeTableEntryIdOpaque: - case TypeTableEntryIdPromise: - return false; - } - zig_unreachable(); -} void init_const_undefined(CodeGen *g, ConstExprValue *const_val) { TypeTableEntry *wanted_type = const_val->type; diff --git a/src/analyze.hpp b/src/analyze.hpp index 100f85d4d9..88e06b2390 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -93,7 +93,6 @@ void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry); void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry); void complete_enum(CodeGen *g, TypeTableEntry *enum_type); bool ir_get_var_is_comptime(VariableTableEntry *var); -bool contains_comptime_undefined_value(ConstExprValue *value); bool const_values_equal(ConstExprValue *a, ConstExprValue *b); void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max); void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigInt *bigint, bool is_max); diff --git a/src/ir.cpp b/src/ir.cpp index 2cce4a5044..c6078e755d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9148,15 +9148,8 @@ enum UndefAllowed { static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) { switch (value->value.special) { - case ConstValSpecialStatic: { - ConstExprValue *res = &value->value; - if (undef_allowed == UndefBad && contains_comptime_undefined_value(res)) { - ir_add_error(ira, value, buf_sprintf("use of undefined value")); - return nullptr; - } - - return res; - } + case ConstValSpecialStatic: + return &value->value; case ConstValSpecialRuntime: ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression")); return nullptr; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 8749f5b560..2247f0af96 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -4124,19 +4124,4 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", ); - - cases.add( - "Trying to pass undefined array to function taking comptime array by value", - \\fn a(comptime b: [2]u8) u8 { return b[0]; } - \\ - \\test "" { - \\ const arr: [2]u8 = undefined; - \\ _ = a(arr); - \\} - , - ".tmp_source.zig:5:11: error: use of undefined value", - ); - - - } -- cgit v1.2.3 From ecd5e60be9cab03449e0d40a770c5a0c5582198d Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 30 Jun 2018 20:50:09 +0200 Subject: Expanded the list of operators that catch undefined values at comptime --- src/ir.cpp | 134 +++++++++++---- test/compile_errors.zig | 420 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 523 insertions(+), 31 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index c6078e755d..0f1e632299 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10773,10 +10773,15 @@ static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *op1_val = &casted_op1->value; - ConstExprValue *op2_val = &casted_op2->value; - if (op1_val->special != ConstValSpecialRuntime && op2_val->special != ConstValSpecialRuntime) { + if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); + ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; assert(casted_op1->value.type->id == TypeTableEntryIdBool); assert(casted_op2->value.type->id == TypeTableEntryIdBool); @@ -10926,9 +10931,14 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp } } - ConstExprValue *op1_val = &op1->value; - ConstExprValue *op2_val = &op2->value; - if (value_is_comptime(op1_val) && value_is_comptime(op2_val)) { + if (instr_is_comptime(op1) && instr_is_comptime(op2)) { + ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + bool answer; bool are_equal = op1_val->data.x_err_set->value == op2_val->data.x_err_set->value; if (op_id == IrBinOpCmpEq) { @@ -11017,10 +11027,15 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *op1_val = &casted_op1->value; - ConstExprValue *op2_val = &casted_op2->value; bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type); - if (one_possible_value || (value_is_comptime(op1_val) && value_is_comptime(op2_val))) { + if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { + ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + bool answer; if (resolved_type->id == TypeTableEntryIdComptimeFloat || resolved_type->id == TypeTableEntryIdFloat) { Cmp cmp_result = float_cmp(op1_val, op2_val); @@ -11048,11 +11063,17 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp if (resolved_type->id == TypeTableEntryIdInt && !resolved_type->data.integral.is_signed) { ConstExprValue *known_left_val; IrBinOp flipped_op_id; - if (value_is_comptime(op1_val)) { - known_left_val = op1_val; + if (instr_is_comptime(casted_op1)) { + known_left_val = ir_resolve_const(ira, casted_op1, UndefBad); + if (known_left_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + flipped_op_id = op_id; - } else if (value_is_comptime(op2_val)) { - known_left_val = op2_val; + } else if (instr_is_comptime(casted_op2)) { + known_left_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (known_left_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + if (op_id == IrBinOpCmpLessThan) { flipped_op_id = IrBinOpCmpGreaterThan; } else if (op_id == IrBinOpCmpGreaterThan) { @@ -11304,8 +11325,14 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * } if (instr_is_comptime(op1) && instr_is_comptime(casted_op2)) { - ConstExprValue *op1_val = &op1->value; - ConstExprValue *op2_val = &casted_op2->value; + ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *result_instruction = ir_get_const(ira, &bin_op_instruction->base); ir_link_new_instruction(result_instruction, &bin_op_instruction->base); ConstExprValue *out_val = &result_instruction->value; @@ -11384,7 +11411,15 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (is_signed_div) { bool ok = false; if (instr_is_comptime(op1) && instr_is_comptime(op2)) { - if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) { + ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + + if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ) { // the division by zero error will be caught later, but we don't have a // division function ambiguity problem. op_id = IrBinOpDivTrunc; @@ -11392,8 +11427,8 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp } else { BigInt trunc_result; BigInt floor_result; - bigint_div_trunc(&trunc_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint); - bigint_div_floor(&floor_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint); + bigint_div_trunc(&trunc_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + bigint_div_floor(&floor_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); if (bigint_cmp(&trunc_result, &floor_result) == CmpEQ) { ok = true; op_id = IrBinOpDivTrunc; @@ -11414,7 +11449,15 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (is_signed_div && (is_int || is_float)) { bool ok = false; if (instr_is_comptime(op1) && instr_is_comptime(op2)) { + ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + if (is_int) { + ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) { // the division by zero error will be caught later, but we don't // have a remainder function ambiguity problem @@ -11422,14 +11465,19 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp } else { BigInt rem_result; BigInt mod_result; - bigint_rem(&rem_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint); - bigint_mod(&mod_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint); + bigint_rem(&rem_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + bigint_mod(&mod_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ; } } else { IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + if (float_cmp_zero(&casted_op2->value) == CmpEQ) { // the division by zero error will be caught later, but we don't // have a remainder function ambiguity problem @@ -11437,8 +11485,8 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp } else { ConstExprValue rem_result; ConstExprValue mod_result; - float_rem(&rem_result, &op1->value, &casted_op2->value); - float_mod(&mod_result, &op1->value, &casted_op2->value); + float_rem(&rem_result, op1_val, op2_val); + float_mod(&mod_result, op1_val, op2_val); ok = float_cmp(&rem_result, &mod_result) == CmpEQ; } } @@ -11496,8 +11544,13 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp return ira->codegen->builtin_types.entry_invalid; if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { - ConstExprValue *op1_val = &casted_op1->value; - ConstExprValue *op2_val = &casted_op2->value; + ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *result_instruction = ir_get_const(ira, &bin_op_instruction->base); ir_link_new_instruction(result_instruction, &bin_op_instruction->base); ConstExprValue *out_val = &result_instruction->value; @@ -11672,9 +11725,16 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp * out_val->data.x_ptr.data.base_array.array_val = out_array_val; out_val->data.x_ptr.data.base_array.elem_index = 0; } - out_array_val->data.x_array.s_none.elements = create_const_vals(new_len); + if (op1_array_val->data.x_array.special == ConstArraySpecialUndef && + op2_array_val->data.x_array.special == ConstArraySpecialUndef) { + out_array_val->data.x_array.special = ConstArraySpecialUndef; + return result_type; + } + + out_array_val->data.x_array.s_none.elements = create_const_vals(new_len); expand_undef_array(ira->codegen, op1_array_val); + expand_undef_array(ira->codegen, op2_array_val); size_t next_index = 0; for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) { @@ -11726,10 +11786,14 @@ static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + if (array_val->data.x_array.special == ConstArraySpecialUndef) { + out_val->data.x_array.special = ConstArraySpecialUndef; - out_val->data.x_array.s_none.elements = create_const_vals(new_array_len); + TypeTableEntry *child_type = array_type->data.array.child_type; + return get_array_type(ira->codegen, child_type, new_array_len); + } - expand_undef_array(ira->codegen, array_val); + out_val->data.x_array.s_none.elements = create_const_vals(new_array_len); uint64_t i = 0; for (uint64_t x = 0; x < mult_amt; x += 1) { @@ -13056,7 +13120,11 @@ static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp // one of the ptr instructions if (instr_is_comptime(value)) { - ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value); + ConstExprValue *comptime_value = ir_resolve_const(ira, value, UndefBad); + if (comptime_value == nullptr) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *pointee = const_ptr_pointee(ira->codegen, comptime_value); if (pointee->type == child_type) { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); copy_const_val(out_val, pointee, value->value.data.x_ptr.mut == ConstPtrMutComptimeConst); @@ -13173,7 +13241,7 @@ static TypeTableEntry *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *ins if (expr_type->id == TypeTableEntryIdInt) { if (instr_is_comptime(value)) { ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad); - if (!target_const_val) + if (target_const_val == nullptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); @@ -17750,9 +17818,13 @@ static TypeTableEntry *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstruc if (type_is_invalid(casted_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - if (casted_value->value.special != ConstValSpecialRuntime) { + if (instr_is_comptime(casted_value)) { + ConstExprValue *value = ir_resolve_const(ira, casted_value, UndefBad); + if (value == nullptr) + return ira->codegen->builtin_types.entry_invalid; + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_bool = !casted_value->value.data.x_bool; + out_val->data.x_bool = !value->data.x_bool; return bool_type; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2247f0af96..2562424ee0 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1893,6 +1893,426 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:1:15: error: use of undefined value", ); + cases.add( + "div on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a / a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "div assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a /= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "mod on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a % a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "mod assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a %= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "add on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a + a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "add assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a += a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "add wrap on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a +% a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "add wrap assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a +%= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "sub on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a - a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "sub assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a -= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "sub wrap on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a -% a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "sub wrap assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a -%= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "mult on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a * a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "mult assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a *= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "mult wrap on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a *% a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "mult wrap assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a *%= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "shift left on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a << 2; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "shift left assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a <<= 2; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "shift right on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a >> 2; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "shift left assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a >>= 2; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "bin and on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a & a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "bin and assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a &= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "bin or on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a | a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "bin or assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a |= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "bin xor on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a ^ a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "bin xor assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a ^= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "equal on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a == a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "not equal on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a != a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "greater than on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a > a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "greater than equal on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a >= a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "less than on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a < a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "less than equal on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a <= a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "and on undefined value", + \\comptime { + \\ var a: bool = undefined; + \\ _ = a and a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "or on undefined value", + \\comptime { + \\ var a: bool = undefined; + \\ _ = a or a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "negate on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = -a; + \\} + , + ".tmp_source.zig:3:10: error: use of undefined value", + ); + + cases.add( + "negate wrap on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = -%a; + \\} + , + ".tmp_source.zig:3:11: error: use of undefined value", + ); + + cases.add( + "bin not on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = ~a; + \\} + , + ".tmp_source.zig:3:10: error: use of undefined value", + ); + + cases.add( + "bool not on undefined value", + \\comptime { + \\ var a: bool = undefined; + \\ _ = !a; + \\} + , + ".tmp_source.zig:3:10: error: use of undefined value", + ); + + cases.add( + "orelse on undefined value", + \\comptime { + \\ var a: ?bool = undefined; + \\ _ = a orelse false; + \\} + , + ".tmp_source.zig:3:11: error: use of undefined value", + ); + + cases.add( + "catch on undefined value", + \\comptime { + \\ var a: error!bool = undefined; + \\ _ = a catch |err| false; + \\} + , + ".tmp_source.zig:3:11: error: use of undefined value", + ); + + cases.add( + "deref on undefined value", + \\comptime { + \\ var a: *u8 = undefined; + \\ _ = a.*; + \\} + , + ".tmp_source.zig:3:11: error: use of undefined value", + ); + + cases.add( + "unwrap on undefined value", + \\comptime { + \\ var a: ?u8 = undefined; + \\ _ = a.?; + \\} + , + ".tmp_source.zig:3:11: error: use of undefined value", + ); + cases.add( "endless loop in function evaluation", \\const seventh_fib_number = fibbonaci(7); -- cgit v1.2.3 From 055e0fef4eaa2d8c6dcbd83180baa44d88be3b4d Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 30 Jun 2018 21:22:26 +0200 Subject: Avoid resolve_const in cmp when instr are not comptime --- src/ir.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 0f1e632299..ce2e333227 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11029,10 +11029,10 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type); if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { - ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + ConstExprValue *op2_val = one_possible_value ? &casted_op2->value : ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->builtin_types.entry_invalid; -- cgit v1.2.3 From b182151de5a215b97de3ef3742ebdc5af7e66119 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 30 Jun 2018 21:59:14 +0200 Subject: Fixed line numbers for tests --- test/compile_errors.zig | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2562424ee0..3b69fb633b 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2300,17 +2300,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a.*; \\} , - ".tmp_source.zig:3:11: error: use of undefined value", - ); - - cases.add( - "unwrap on undefined value", - \\comptime { - \\ var a: ?u8 = undefined; - \\ _ = a.?; - \\} - , - ".tmp_source.zig:3:11: error: use of undefined value", + ".tmp_source.zig:3:9: error: use of undefined value", ); cases.add( -- cgit v1.2.3 From e833a5a24c1ad58af0bb56e89dbcc0b9ec38c020 Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Sun, 1 Jul 2018 13:47:29 -0400 Subject: gitignore docgen test artifacts --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 20b208975a..5616da8e58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ zig-cache/ build/ build-*/ +docgen_tmp/ -- cgit v1.2.3 From 0206b76351a50e154181d5bff1d134656e412bfb Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Sun, 1 Jul 2018 22:03:51 -0400 Subject: syntax in build.zig example doc --- doc/langref.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index dbb4ea9806..15e04459bd 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6067,7 +6067,7 @@ pub const TypeInfo = union(TypeId) { {#code_begin|syntax#} const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const exe = b.addExecutable("example", "example.zig"); exe.setBuildMode(b.standardReleaseOptions()); b.default_step.dependOn(&exe.step); -- cgit v1.2.3 From 2759c7951da050d825cf765c4b660f5562fb01a4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 Jul 2018 14:10:27 -0400 Subject: always link against compiler_rt.o even when linking libc sometimes libgcc is missing things we need, so we always link compiler_rt and rely on weak linkage to allow libgcc to override. --- src/link.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/link.cpp b/src/link.cpp index a4631b1daf..2d9a79585f 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -325,10 +325,13 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); } - if (g->libc_link_lib == nullptr && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) { - Buf *builtin_o_path = build_o(g, "builtin"); - lj->args.append(buf_ptr(builtin_o_path)); + if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { + if (g->libc_link_lib == nullptr) { + Buf *builtin_o_path = build_o(g, "builtin"); + lj->args.append(buf_ptr(builtin_o_path)); + } + // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage Buf *compiler_rt_o_path = build_compiler_rt(g); lj->args.append(buf_ptr(compiler_rt_o_path)); } @@ -554,7 +557,7 @@ static void construct_linker_job_coff(LinkJob *lj) { lj->args.append(buf_ptr(builtin_o_path)); } - // msvc compiler_rt is missing some stuff, so we still build it and rely on LinkOnce + // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage Buf *compiler_rt_o_path = build_compiler_rt(g); lj->args.append(buf_ptr(compiler_rt_o_path)); } -- cgit v1.2.3 From a3f55aaf34f0a459c8aec4b35e55ad4534eaca30 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 29 Jun 2018 15:39:55 -0400 Subject: add event loop Channel abstraction This is akin to channels in Go, except: * implemented in userland * they are lock-free and thread-safe * they integrate with the userland event loop The self hosted compiler is changed to use a channel for events, and made to stay alive, watching files and performing builds when things change, however the main.zig file exits after 1 build. Note that nothing is actually built yet, it just parses the input and then declares that the build succeeded. Next items to do: * add windows and macos support for std.event.Loop * improve the event loop stop() operation * make the event loop multiplex coroutines onto kernel threads * watch source file for updates, and provide AST diffs (at least list the top level declaration changes) * top level declaration analysis --- src-self-hosted/main.zig | 37 +++++- src-self-hosted/module.zig | 135 ++++++++++++++++------ std/atomic/queue_mpsc.zig | 2 +- std/event.zig | 279 ++++++++++++++++++++++++++++++++++++++++++++- std/fmt/index.zig | 3 + std/heap.zig | 1 + 6 files changed, 416 insertions(+), 41 deletions(-) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 6dabddaefb..d17fc94c82 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); +const event = std.event; const os = std.os; const io = std.io; const mem = std.mem; @@ -43,6 +44,9 @@ const Command = struct { }; pub fn main() !void { + // This allocator needs to be thread-safe because we use it for the event.Loop + // which multiplexes coroutines onto kernel threads. + // libc allocator is guaranteed to have this property. const allocator = std.heap.c_allocator; var stdout_file = try std.io.getStdOut(); @@ -380,8 +384,10 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1); defer allocator.free(zig_lib_dir); + var loop = try event.Loop.init(allocator); + var module = try Module.create( - allocator, + &loop, root_name, root_source_file, Target.Native, @@ -471,9 +477,35 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo module.emit_file_type = emit_type; module.link_objects = link_objects; module.assembly_files = assembly_files; + module.link_out_file = flags.single("out-file"); try module.build(); - try module.link(flags.single("out-file")); + const process_build_events_handle = try async processBuildEvents(module, true); + defer cancel process_build_events_handle; + loop.run(); +} + +async fn processBuildEvents(module: *Module, watch: bool) void { + while (watch) { + // TODO directly awaiting async should guarantee memory allocation elision + const build_event = await (async module.events.get() catch unreachable); + + switch (build_event) { + Module.Event.Ok => { + std.debug.warn("Build succeeded\n"); + // for now we stop after 1 + module.loop.stop(); + return; + }, + Module.Event.Error => |err| { + std.debug.warn("build failed: {}\n", @errorName(err)); + @panic("TODO error return trace"); + }, + Module.Event.Fail => |errs| { + @panic("TODO print compile error messages"); + }, + } + } } fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void { @@ -780,4 +812,3 @@ const CliPkg = struct { self.children.deinit(); } }; - diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 4da46cd38c..4fac760790 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -11,9 +11,11 @@ const warn = std.debug.warn; const Token = std.zig.Token; const ArrayList = std.ArrayList; const errmsg = @import("errmsg.zig"); +const ast = std.zig.ast; +const event = std.event; pub const Module = struct { - allocator: *mem.Allocator, + loop: *event.Loop, name: Buffer, root_src_path: ?[]const u8, module: llvm.ModuleRef, @@ -76,6 +78,50 @@ pub const Module = struct { kind: Kind, + link_out_file: ?[]const u8, + events: *event.Channel(Event), + + // TODO handle some of these earlier and report them in a way other than error codes + pub const BuildError = error{ + OutOfMemory, + EndOfStream, + BadFd, + Io, + IsDir, + Unexpected, + SystemResources, + SharingViolation, + PathAlreadyExists, + FileNotFound, + AccessDenied, + PipeBusy, + FileTooBig, + SymLinkLoop, + ProcessFdQuotaExceeded, + NameTooLong, + SystemFdQuotaExceeded, + NoDevice, + PathNotFound, + NoSpaceLeft, + NotDir, + FileSystem, + OperationAborted, + IoPending, + BrokenPipe, + WouldBlock, + FileClosed, + DestinationAddressRequired, + DiskQuota, + InputOutput, + NoStdHandles, + }; + + pub const Event = union(enum) { + Ok, + Fail: []errmsg.Msg, + Error: BuildError, + }; + pub const DarwinVersionMin = union(enum) { None, MacOS: []const u8, @@ -104,7 +150,7 @@ pub const Module = struct { }; pub fn create( - allocator: *mem.Allocator, + loop: *event.Loop, name: []const u8, root_src_path: ?[]const u8, target: *const Target, @@ -113,7 +159,7 @@ pub const Module = struct { zig_lib_dir: []const u8, cache_dir: []const u8, ) !*Module { - var name_buffer = try Buffer.init(allocator, name); + var name_buffer = try Buffer.init(loop.allocator, name); errdefer name_buffer.deinit(); const context = c.LLVMContextCreate() orelse return error.OutOfMemory; @@ -125,8 +171,12 @@ pub const Module = struct { const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory; errdefer c.LLVMDisposeBuilder(builder); - const module_ptr = try allocator.create(Module{ - .allocator = allocator, + const events = try event.Channel(Event).create(loop, 0); + errdefer events.destroy(); + + return loop.allocator.create(Module{ + .loop = loop, + .events = events, .name = name_buffer, .root_src_path = root_src_path, .module = module, @@ -171,7 +221,7 @@ pub const Module = struct { .link_objects = [][]const u8{}, .windows_subsystem_windows = false, .windows_subsystem_console = false, - .link_libs_list = ArrayList(*LinkLib).init(allocator), + .link_libs_list = ArrayList(*LinkLib).init(loop.allocator), .libc_link_lib = null, .err_color = errmsg.Color.Auto, .darwin_frameworks = [][]const u8{}, @@ -179,9 +229,8 @@ pub const Module = struct { .test_filters = [][]const u8{}, .test_name_prefix = null, .emit_file_type = Emit.Binary, + .link_out_file = null, }); - errdefer allocator.destroy(module_ptr); - return module_ptr; } fn dump(self: *Module) void { @@ -189,58 +238,70 @@ pub const Module = struct { } pub fn destroy(self: *Module) void { + self.events.destroy(); c.LLVMDisposeBuilder(self.builder); c.LLVMDisposeModule(self.module); c.LLVMContextDispose(self.context); self.name.deinit(); - self.allocator.destroy(self); + self.a().destroy(self); } pub fn build(self: *Module) !void { if (self.llvm_argv.len != 0) { - var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.allocator, [][]const []const u8{ + var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{ [][]const u8{"zig (LLVM option parsing)"}, self.llvm_argv, }); defer c_compatible_args.deinit(); + // TODO this sets global state c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr); } + _ = try async self.buildAsync(); + } + + async fn buildAsync(self: *Module) void { + while (true) { + // TODO directly awaiting async should guarantee memory allocation elision + // TODO also async before suspending should guarantee memory allocation elision + (await (async self.addRootSrc() catch unreachable)) catch |err| { + await (async self.events.put(Event{ .Error = err }) catch unreachable); + return; + }; + await (async self.events.put(Event.Ok) catch unreachable); + } + } + + async fn addRootSrc(self: *Module) !void { const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); - const root_src_real_path = os.path.real(self.allocator, root_src_path) catch |err| { + const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| { try printError("unable to get real path '{}': {}", root_src_path, err); return err; }; - errdefer self.allocator.free(root_src_real_path); + errdefer self.a().free(root_src_real_path); - const source_code = io.readFileAlloc(self.allocator, root_src_real_path) catch |err| { + const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| { try printError("unable to open '{}': {}", root_src_real_path, err); return err; }; - errdefer self.allocator.free(source_code); - - warn("====input:====\n"); - - warn("{}", source_code); + errdefer self.a().free(source_code); - warn("====parse:====\n"); - - var tree = try std.zig.parse(self.allocator, source_code); + var tree = try std.zig.parse(self.a(), source_code); defer tree.deinit(); - var stderr_file = try std.io.getStdErr(); - var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file); - const out_stream = &stderr_file_out_stream.stream; - - warn("====fmt:====\n"); - _ = try std.zig.render(self.allocator, out_stream, &tree); - - warn("====ir:====\n"); - warn("TODO\n\n"); - - warn("====llvm ir:====\n"); - self.dump(); + //var it = tree.root_node.decls.iterator(); + //while (it.next()) |decl_ptr| { + // const decl = decl_ptr.*; + // switch (decl.id) { + // ast.Node.Comptime => @panic("TODO"), + // ast.Node.VarDecl => @panic("TODO"), + // ast.Node.UseDecl => @panic("TODO"), + // ast.Node.FnDef => @panic("TODO"), + // ast.Node.TestDecl => @panic("TODO"), + // else => unreachable, + // } + //} } pub fn link(self: *Module, out_file: ?[]const u8) !void { @@ -263,11 +324,11 @@ pub const Module = struct { } } - const link_lib = try self.allocator.create(LinkLib{ + const link_lib = try self.a().create(LinkLib{ .name = name, .path = null, .provided_explicitly = provided_explicitly, - .symbols = ArrayList([]u8).init(self.allocator), + .symbols = ArrayList([]u8).init(self.a()), }); try self.link_libs_list.append(link_lib); if (is_libc) { @@ -275,6 +336,10 @@ pub const Module = struct { } return link_lib; } + + fn a(self: Module) *mem.Allocator { + return self.loop.allocator; + } }; fn printError(comptime format: []const u8, args: ...) !void { diff --git a/std/atomic/queue_mpsc.zig b/std/atomic/queue_mpsc.zig index 66eb4573df..8030565d7a 100644 --- a/std/atomic/queue_mpsc.zig +++ b/std/atomic/queue_mpsc.zig @@ -1,4 +1,4 @@ -const std = @import("std"); +const std = @import("../index.zig"); const assert = std.debug.assert; const builtin = @import("builtin"); const AtomicOrder = builtin.AtomicOrder; diff --git a/std/event.zig b/std/event.zig index 0821c789b7..7f823bc732 100644 --- a/std/event.zig +++ b/std/event.zig @@ -4,6 +4,8 @@ const assert = std.debug.assert; const event = this; const mem = std.mem; const posix = std.os.posix; +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; pub const TcpServer = struct { handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void, @@ -95,16 +97,29 @@ pub const Loop = struct { allocator: *mem.Allocator, epollfd: i32, keep_running: bool, + next_tick_queue: std.atomic.QueueMpsc(promise), - fn init(allocator: *mem.Allocator) !Loop { + pub const NextTickNode = std.atomic.QueueMpsc(promise).Node; + + /// The allocator must be thread-safe because we use it for multiplexing + /// coroutines onto kernel threads. + pub fn init(allocator: *mem.Allocator) !Loop { const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC); + errdefer std.os.close(epollfd); + return Loop{ .keep_running = true, .allocator = allocator, .epollfd = epollfd, + .next_tick_queue = std.atomic.QueueMpsc(promise).init(), }; } + /// must call stop before deinit + pub fn deinit(self: *Loop) void { + std.os.close(self.epollfd); + } + pub fn addFd(self: *Loop, fd: i32, prom: promise) !void { var ev = std.os.linux.epoll_event{ .events = std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, @@ -126,11 +141,21 @@ pub const Loop = struct { pub fn stop(self: *Loop) void { // TODO make atomic self.keep_running = false; - // TODO activate an fd in the epoll set + // TODO activate an fd in the epoll set which should cancel all the promises + } + + /// bring your own linked list node. this means it can't fail. + pub fn onNextTick(self: *Loop, node: *NextTickNode) void { + self.next_tick_queue.put(node); } pub fn run(self: *Loop) void { while (self.keep_running) { + // TODO multiplex the next tick queue and the epoll event results onto a thread pool + while (self.next_tick_queue.get()) |node| { + resume node.data; + } + if (!self.keep_running) break; var events: [16]std.os.linux.epoll_event = undefined; const count = std.os.linuxEpollWait(self.epollfd, events[0..], -1); for (events[0..count]) |ev| { @@ -141,6 +166,215 @@ pub const Loop = struct { } }; +/// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size +/// when buffer is empty, consumers suspend and are resumed by producers +/// when buffer is full, producers suspend and are resumed by consumers +pub fn Channel(comptime T: type) type { + return struct { + loop: *Loop, + + getters: std.atomic.QueueMpsc(GetNode), + putters: std.atomic.QueueMpsc(PutNode), + get_count: usize, + put_count: usize, + dispatch_lock: u8, // TODO make this a bool + need_dispatch: u8, // TODO make this a bool + + // simple fixed size ring buffer + buffer_nodes: []T, + buffer_index: usize, + buffer_len: usize, + + const SelfChannel = this; + const GetNode = struct { + ptr: *T, + tick_node: *Loop.NextTickNode, + }; + const PutNode = struct { + data: T, + tick_node: *Loop.NextTickNode, + }; + + /// call destroy when done + pub fn create(loop: *Loop, capacity: usize) !*SelfChannel { + const buffer_nodes = try loop.allocator.alloc(T, capacity); + errdefer loop.allocator.free(buffer_nodes); + + const self = try loop.allocator.create(SelfChannel{ + .loop = loop, + .buffer_len = 0, + .buffer_nodes = buffer_nodes, + .buffer_index = 0, + .dispatch_lock = 0, + .need_dispatch = 0, + .getters = std.atomic.QueueMpsc(GetNode).init(), + .putters = std.atomic.QueueMpsc(PutNode).init(), + .get_count = 0, + .put_count = 0, + }); + errdefer loop.allocator.destroy(self); + + return self; + } + + /// must be called when all calls to put and get have suspended and no more calls occur + pub fn destroy(self: *SelfChannel) void { + while (self.getters.get()) |get_node| { + cancel get_node.data.tick_node.data; + } + while (self.putters.get()) |put_node| { + cancel put_node.data.tick_node.data; + } + self.loop.allocator.free(self.buffer_nodes); + self.loop.allocator.destroy(self); + } + + /// puts a data item in the channel. The promise completes when the value has been added to the + /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. + pub async fn put(self: *SelfChannel, data: T) void { + // TODO should be able to group memory allocation failure before first suspend point + // so that the async invocation catches it + var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined; + _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable; + + suspend |handle| { + var my_tick_node = Loop.NextTickNode{ + .next = undefined, + .data = handle, + }; + var queue_node = std.atomic.QueueMpsc(PutNode).Node{ + .data = PutNode{ + .tick_node = &my_tick_node, + .data = data, + }, + .next = undefined, + }; + self.putters.put(&queue_node); + _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + + self.loop.onNextTick(dispatch_tick_node_ptr); + } + } + + /// await this function to get an item from the channel. If the buffer is empty, the promise will + /// complete when the next item is put in the channel. + pub async fn get(self: *SelfChannel) T { + // TODO should be able to group memory allocation failure before first suspend point + // so that the async invocation catches it + var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined; + _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable; + + // TODO integrate this function with named return values + // so we can get rid of this extra result copy + var result: T = undefined; + var debug_handle: usize = undefined; + suspend |handle| { + debug_handle = @ptrToInt(handle); + var my_tick_node = Loop.NextTickNode{ + .next = undefined, + .data = handle, + }; + var queue_node = std.atomic.QueueMpsc(GetNode).Node{ + .data = GetNode{ + .ptr = &result, + .tick_node = &my_tick_node, + }, + .next = undefined, + }; + self.getters.put(&queue_node); + _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + + self.loop.onNextTick(dispatch_tick_node_ptr); + } + return result; + } + + async fn dispatch(self: *SelfChannel, tick_node_ptr: **Loop.NextTickNode) void { + // resumed by onNextTick + suspend |handle| { + var tick_node = Loop.NextTickNode{ + .data = handle, + .next = undefined, + }; + tick_node_ptr.* = &tick_node; + } + + // set the "need dispatch" flag + _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + + lock: while (true) { + // set the lock flag + const prev_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + if (prev_lock != 0) return; + + // clear the need_dispatch flag since we're about to do it + _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + while (true) { + one_dispatch: { + // later we correct these extra subtractions + var get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + var put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + + // transfer self.buffer to self.getters + while (self.buffer_len != 0) { + if (get_count == 0) break :one_dispatch; + + const get_node = &self.getters.get().?.data; + get_node.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len]; + self.loop.onNextTick(get_node.tick_node); + self.buffer_len -= 1; + + get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + + // direct transfer self.putters to self.getters + while (get_count != 0 and put_count != 0) { + const get_node = &self.getters.get().?.data; + const put_node = &self.putters.get().?.data; + + get_node.ptr.* = put_node.data; + self.loop.onNextTick(get_node.tick_node); + self.loop.onNextTick(put_node.tick_node); + + get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + + // transfer self.putters to self.buffer + while (self.buffer_len != self.buffer_nodes.len and put_count != 0) { + const put_node = &self.putters.get().?.data; + + self.buffer_nodes[self.buffer_index] = put_node.data; + self.loop.onNextTick(put_node.tick_node); + self.buffer_index +%= 1; + self.buffer_len += 1; + + put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + } + + // undo the extra subtractions + _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + + // clear need-dispatch flag + const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + if (need_dispatch != 0) continue; + + const my_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + assert(my_lock != 0); + + // we have to check again now that we unlocked + if (@atomicLoad(u8, &self.need_dispatch, AtomicOrder.SeqCst) != 0) continue :lock; + + return; + } + } + } + }; +} + pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File { var address = _address.*; // TODO https://github.com/ziglang/zig/issues/733 @@ -199,6 +433,7 @@ test "listen on a port, send bytes, receive bytes" { defer cancel p; loop.run(); } + async fn doAsyncTest(loop: *Loop, address: *const std.net.Address) void { errdefer @panic("test failure"); @@ -211,3 +446,43 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address) void { assert(mem.eql(u8, msg, "hello from server\n")); loop.stop(); } + +test "std.event.Channel" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const allocator = &da.allocator; + + var loop = try Loop.init(allocator); + defer loop.deinit(); + + const channel = try Channel(i32).create(&loop, 0); + defer channel.destroy(); + + const handle = try async testChannelGetter(&loop, channel); + defer cancel handle; + + const putter = try async testChannelPutter(channel); + defer cancel putter; + + loop.run(); +} + +async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void { + errdefer @panic("test failed"); + + const value1_promise = try async channel.get(); + const value1 = await value1_promise; + assert(value1 == 1234); + + const value2_promise = try async channel.get(); + const value2 = await value2_promise; + assert(value2 == 4567); + + loop.stop(); +} + +async fn testChannelPutter(channel: *Channel(i32)) void { + await (async channel.put(1234) catch @panic("out of memory")); + await (async channel.put(4567) catch @panic("out of memory")); +} diff --git a/std/fmt/index.zig b/std/fmt/index.zig index bf12e86fef..c3c17f5322 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -130,6 +130,9 @@ pub fn formatType( try output(context, "error."); return output(context, @errorName(value)); }, + builtin.TypeId.Promise => { + return format(context, Errors, output, "promise@{x}", @ptrToInt(value)); + }, builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) { builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) { builtin.TypeId.Array => |info| { diff --git a/std/heap.zig b/std/heap.zig index 41d7802fdd..2e02733da1 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -38,6 +38,7 @@ fn cFree(self: *Allocator, old_mem: []u8) void { } /// This allocator makes a syscall directly for every allocation and free. +/// TODO make this thread-safe. The windows implementation will need some atomics. pub const DirectAllocator = struct { allocator: Allocator, heap_handle: ?HeapHandle, -- cgit v1.2.3 From 96a6bc57d20cff5171437a483422f53b0231a03d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 Jul 2018 14:30:51 -0400 Subject: modify std.event.Loop to work for windows and macos --- std/event.zig | 67 ++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/std/event.zig b/std/event.zig index 7f823bc732..c6ac04a9d0 100644 --- a/std/event.zig +++ b/std/event.zig @@ -95,29 +95,56 @@ pub const TcpServer = struct { pub const Loop = struct { allocator: *mem.Allocator, - epollfd: i32, keep_running: bool, next_tick_queue: std.atomic.QueueMpsc(promise), + os_data: OsData, + + const OsData = switch (builtin.os) { + builtin.Os.linux => struct { + epollfd: i32, + }, + else => struct {}, + }; pub const NextTickNode = std.atomic.QueueMpsc(promise).Node; /// The allocator must be thread-safe because we use it for multiplexing /// coroutines onto kernel threads. pub fn init(allocator: *mem.Allocator) !Loop { - const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC); - errdefer std.os.close(epollfd); - - return Loop{ + var self = Loop{ .keep_running = true, .allocator = allocator, - .epollfd = epollfd, + .os_data = undefined, .next_tick_queue = std.atomic.QueueMpsc(promise).init(), }; + try self.initOsData(); + errdefer self.deinitOsData(); + + return self; } /// must call stop before deinit pub fn deinit(self: *Loop) void { - std.os.close(self.epollfd); + self.deinitOsData(); + } + + const InitOsDataError = std.os.LinuxEpollCreateError; + + fn initOsData(self: *Loop) InitOsDataError!void { + switch (builtin.os) { + builtin.Os.linux => { + self.os_data.epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC); + errdefer std.os.close(self.os_data.epollfd); + }, + else => {}, + } + } + + fn deinitOsData(self: *Loop) void { + switch (builtin.os) { + builtin.Os.linux => std.os.close(self.os_data.epollfd), + else => {}, + } } pub fn addFd(self: *Loop, fd: i32, prom: promise) !void { @@ -125,11 +152,11 @@ pub const Loop = struct { .events = std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(prom) }, }; - try std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev); + try std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev); } pub fn removeFd(self: *Loop, fd: i32) void { - std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; + std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; } async fn waitFd(self: *Loop, fd: i32) !void { defer self.removeFd(fd); @@ -156,12 +183,22 @@ pub const Loop = struct { resume node.data; } if (!self.keep_running) break; - var events: [16]std.os.linux.epoll_event = undefined; - const count = std.os.linuxEpollWait(self.epollfd, events[0..], -1); - for (events[0..count]) |ev| { - const p = @intToPtr(promise, ev.data.ptr); - resume p; - } + + self.dispatchOsEvents(); + } + } + + fn dispatchOsEvents(self: *Loop) void { + switch (builtin.os) { + builtin.Os.linux => { + var events: [16]std.os.linux.epoll_event = undefined; + const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); + for (events[0..count]) |ev| { + const p = @intToPtr(promise, ev.data.ptr); + resume p; + } + }, + else => {}, } } }; -- cgit v1.2.3 From 2da999372a1f7848af59b07fe14ef025354f4c51 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 Jul 2018 15:25:23 -0400 Subject: add another BuildError code --- src-self-hosted/module.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 4fac760790..c984610257 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -114,6 +114,7 @@ pub const Module = struct { DiskQuota, InputOutput, NoStdHandles, + Overflow, }; pub const Event = union(enum) { -- cgit v1.2.3 From 35463526cceb91243410bdab4d74e2d5b3c60f66 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 Jul 2018 15:49:49 -0400 Subject: add runtime safety for `@intToEnum`; add docs for runtime safety See #367 --- doc/langref.html.in | 225 ++++++++++++++++++++++++++++++++++++++++++------ src/codegen.cpp | 19 +++- test/runtime_safety.zig | 18 ++++ 3 files changed, 233 insertions(+), 29 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 15e04459bd..1da4205b89 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6144,7 +6144,14 @@ fn assert(ok: bool) void { if (!ok) unreachable; // assertion failure } {#code_end#} -

      At runtime crashes with the message reached unreachable code and a stack trace.

      +

      At runtime:

      + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + std.debug.assert(false); +} + {#code_end#} {#header_close#} {#header_open|Index out of Bounds#}

      At compile-time:

      @@ -6154,7 +6161,16 @@ comptime { const garbage = array[5]; } {#code_end#} -

      At runtime crashes with the message index out of bounds and a stack trace.

      +

      At runtime:

      + {#code_begin|exe_err#} +pub fn main() void { + var x = foo("hello"); +} + +fn foo(x: []const u8) u8 { + return x[5]; +} + {#code_end#} {#header_close#} {#header_open|Cast Negative Number to Unsigned Integer#}

      At compile-time:

      @@ -6164,10 +6180,18 @@ comptime { const unsigned = @intCast(u32, value); } {#code_end#} -

      At runtime crashes with the message attempt to cast negative value to unsigned integer and a stack trace.

      +

      At runtime:

      + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var value: i32 = -1; + var unsigned = @intCast(u32, value); + std.debug.warn("value: {}\n", unsigned); +} + {#code_end#}

      - If you are trying to obtain the maximum value of an unsigned integer, use @maxValue(T), - where T is the integer type, such as u32. + To obtain the maximum value of an unsigned integer, use {#link|@maxValue#}.

      {#header_close#} {#header_open|Cast Truncates Data#} @@ -6178,11 +6202,18 @@ comptime { const byte = @intCast(u8, spartan_count); } {#code_end#} -

      At runtime crashes with the message integer cast truncated bits and a stack trace.

      +

      At runtime:

      + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var spartan_count: u16 = 300; + const byte = @intCast(u8, spartan_count); + std.debug.warn("value: {}\n", byte); +} + {#code_end#}

      - If you are trying to truncate bits, use @truncate(T, value), - where T is the integer type, such as u32, and value - is the value you want to truncate. + To truncate bits, use {#link|@truncate#}.

      {#header_close#} {#header_open|Integer Overflow#} @@ -6194,9 +6225,9 @@ comptime {
    • - (negation)
    • * (multiplication)
    • / (division)
    • -
    • @divTrunc (division)
    • -
    • @divFloor (division)
    • -
    • @divExact (division)
    • +
    • {#link|@divTrunc#} (division)
    • +
    • {#link|@divFloor#} (division)
    • +
    • {#link|@divExact#} (division)

    Example with addition at compile-time:

    {#code_begin|test_err|operation caused overflow#} @@ -6205,7 +6236,16 @@ comptime { byte += 1; } {#code_end#} -

    At runtime crashes with the message integer overflow and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var byte: u8 = 255; + byte += 1; + std.debug.warn("value: {}\n", byte); +} + {#code_end#} {#header_close#} {#header_open|Standard Library Math Functions#}

    These functions provided by the standard library return possible errors.

    @@ -6240,13 +6280,13 @@ pub fn main() !void { occurred, as well as returning the overflowed bits:

      -
    • @addWithOverflow
    • -
    • @subWithOverflow
    • -
    • @mulWithOverflow
    • -
    • @shlWithOverflow
    • +
    • {#link|@addWithOverflow#}
    • +
    • {#link|@subWithOverflow#}
    • +
    • {#link|@mulWithOverflow#}
    • +
    • {#link|@shlWithOverflow#}

    - Example of @addWithOverflow: + Example of {#link|@addWithOverflow#}:

    {#code_begin|exe#} const warn = @import("std").debug.warn; @@ -6292,7 +6332,16 @@ comptime { const x = @shlExact(u8(0b01010101), 2); } {#code_end#} -

    At runtime crashes with the message left shift overflowed bits and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var x: u8 = 0b01010101; + var y = @shlExact(x, 2); + std.debug.warn("value: {}\n", y); +} + {#code_end#} {#header_close#} {#header_open|Exact Right Shift Overflow#}

    At compile-time:

    @@ -6301,7 +6350,16 @@ comptime { const x = @shrExact(u8(0b10101010), 2); } {#code_end#} -

    At runtime crashes with the message right shift overflowed bits and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var x: u8 = 0b10101010; + var y = @shrExact(x, 2); + std.debug.warn("value: {}\n", y); +} + {#code_end#} {#header_close#} {#header_open|Division by Zero#}

    At compile-time:

    @@ -6312,8 +6370,17 @@ comptime { const c = a / b; } {#code_end#} -

    At runtime crashes with the message division by zero and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); +pub fn main() void { + var a: u32 = 1; + var b: u32 = 0; + var c = a / b; + std.debug.warn("value: {}\n", c); +} + {#code_end#} {#header_close#} {#header_open|Remainder Division by Zero#}

    At compile-time:

    @@ -6324,14 +6391,57 @@ comptime { const c = a % b; } {#code_end#} -

    At runtime crashes with the message remainder division by zero and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); +pub fn main() void { + var a: u32 = 10; + var b: u32 = 0; + var c = a % b; + std.debug.warn("value: {}\n", c); +} + {#code_end#} {#header_close#} {#header_open|Exact Division Remainder#} -

    TODO

    +

    At compile-time:

    + {#code_begin|test_err|exact division had a remainder#} +comptime { + const a: u32 = 10; + const b: u32 = 3; + const c = @divExact(a, b); +} + {#code_end#} +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var a: u32 = 10; + var b: u32 = 3; + var c = @divExact(a, b); + std.debug.warn("value: {}\n", c); +} + {#code_end#} {#header_close#} {#header_open|Slice Widen Remainder#} -

    TODO

    +

    At compile-time:

    + {#code_begin|test_err|unable to convert#} +comptime { + var bytes = [5]u8{ 1, 2, 3, 4, 5 }; + var slice = @bytesToSlice(u32, bytes); +} + {#code_end#} +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var bytes = [5]u8{ 1, 2, 3, 4, 5 }; + var slice = @bytesToSlice(u32, bytes[0..]); + std.debug.warn("value: {}\n", slice[0]); +} + {#code_end#} {#header_close#} {#header_open|Attempt to Unwrap Null#}

    At compile-time:

    @@ -6341,7 +6451,16 @@ comptime { const number = optional_number.?; } {#code_end#} -

    At runtime crashes with the message attempt to unwrap null and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var optional_number: ?i32 = null; + var number = optional_number.?; + std.debug.warn("value: {}\n", number); +} + {#code_end#}

    One way to avoid this crash is to test for null instead of assuming non-null, with the if expression:

    {#code_begin|exe|test#} @@ -6356,6 +6475,7 @@ pub fn main() void { } } {#code_end#} + {#see_also|Optionals#} {#header_close#} {#header_open|Attempt to Unwrap Error#}

    At compile-time:

    @@ -6368,7 +6488,19 @@ fn getNumberOrFail() !i32 { return error.UnableToReturnNumber; } {#code_end#} -

    At runtime crashes with the message attempt to unwrap error: ErrorCode and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + const number = getNumberOrFail() catch unreachable; + std.debug.warn("value: {}\n", number); +} + +fn getNumberOrFail() !i32 { + return error.UnableToReturnNumber; +} + {#code_end#}

    One way to avoid this crash is to test for an error instead of assuming a successful result, with the if expression:

    {#code_begin|exe#} @@ -6388,6 +6520,7 @@ fn getNumberOrFail() !i32 { return error.UnableToReturnNumber; } {#code_end#} + {#see_also|Errors#} {#header_close#} {#header_open|Invalid Error Code#}

    At compile-time:

    @@ -6398,11 +6531,47 @@ comptime { const invalid_err = @intToError(number); } {#code_end#} -

    At runtime crashes with the message invalid error code and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var err = error.AnError; + var number = @errorToInt(err) + 500; + var invalid_err = @intToError(number); + std.debug.warn("value: {}\n", number); +} + {#code_end#} {#header_close#} {#header_open|Invalid Enum Cast#} -

    TODO

    +

    At compile-time:

    + {#code_begin|test_err|has no tag matching integer value 3#} +const Foo = enum { + A, + B, + C, +}; +comptime { + const a: u2 = 3; + const b = @intToEnum(Foo, a); +} + {#code_end#} +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +const Foo = enum { + A, + B, + C, +}; +pub fn main() void { + var a: u2 = 3; + var b = @intToEnum(Foo, a); + std.debug.warn("value: {}\n", @tagName(b)); +} + {#code_end#} {#header_close#} {#header_open|Invalid Error Set Cast#} diff --git a/src/codegen.cpp b/src/codegen.cpp index 4419f4fc84..9c37c174d6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2673,8 +2673,25 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, TypeTableEntry *tag_int_type = wanted_type->data.enumeration.tag_int_type; LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), + LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), instruction->target->value.type, tag_int_type, target_val); + + if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue"); + LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue"); + size_t field_count = wanted_type->data.enumeration.src_field_count; + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count); + for (size_t field_i = 0; field_i < field_count; field_i += 1) { + LLVMValueRef this_tag_int_value = bigint_to_llvm_const(tag_int_type->type_ref, + &wanted_type->data.enumeration.fields[field_i].value); + LLVMAddCase(switch_instr, this_tag_int_value, ok_value_block); + } + LLVMPositionBuilderAtEnd(g->builder, bad_value_block); + gen_safety_crash(g, PanicMsgIdBadEnumValue); + + LLVMPositionBuilderAtEnd(g->builder, ok_value_block); + } + return tag_int_value; } static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) { diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index a7e8d6dc0e..3d58dfe748 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -1,6 +1,24 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { + cases.addRuntimeSafety("@intToEnum - no matching tag value", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\const Foo = enum { + \\ A, + \\ B, + \\ C, + \\}; + \\pub fn main() void { + \\ baz(bar(3)); + \\} + \\fn bar(a: u2) Foo { + \\ return @intToEnum(Foo, a); + \\} + \\fn baz(a: Foo) void {} + ); + cases.addRuntimeSafety("@floatToInt cannot fit - negative to unsigned", \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); -- cgit v1.2.3 From 06e8c2e5194439ce5b66f18fcf60108604449957 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 Jul 2018 17:55:32 -0400 Subject: fix stage2 macos build See #1173 --- src-self-hosted/module.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index c984610257..cf27c826c8 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -115,6 +115,7 @@ pub const Module = struct { InputOutput, NoStdHandles, Overflow, + NotSupported, }; pub const Event = union(enum) { -- cgit v1.2.3 From 1eda86e1ad46493a996521b7a0c6bd59133960ae Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Tue, 3 Jul 2018 13:22:12 +1200 Subject: Clean up outstanding compiler_rt todos --- std/special/compiler_rt/comparetf2.zig | 4 ++-- std/special/compiler_rt/fixuint.zig | 6 ------ std/special/compiler_rt/fixunstfdi_test.zig | 21 ++++++++++----------- std/special/compiler_rt/fixunstfsi_test.zig | 4 ++-- std/special/compiler_rt/floatunsitf.zig | 2 +- 5 files changed, 15 insertions(+), 22 deletions(-) diff --git a/std/special/compiler_rt/comparetf2.zig b/std/special/compiler_rt/comparetf2.zig index 479ba51962..0912b71bd5 100644 --- a/std/special/compiler_rt/comparetf2.zig +++ b/std/special/compiler_rt/comparetf2.zig @@ -1,4 +1,4 @@ -// TODO https://github.com/ziglang/zig/issues/305 +// TODO https://github.com/ziglang/zig/issues/641 // and then make the return types of some of these functions the enum instead of c_int const LE_LESS = c_int(-1); const LE_EQUAL = c_int(0); @@ -56,7 +56,7 @@ pub extern fn __letf2(a: f128, b: f128) c_int { LE_GREATER; } -// TODO https://github.com/ziglang/zig/issues/305 +// TODO https://github.com/ziglang/zig/issues/641 // and then make the return types of some of these functions the enum instead of c_int const GE_LESS = c_int(-1); const GE_EQUAL = c_int(0); diff --git a/std/special/compiler_rt/fixuint.zig b/std/special/compiler_rt/fixuint.zig index 48d63ed914..55a113b368 100644 --- a/std/special/compiler_rt/fixuint.zig +++ b/std/special/compiler_rt/fixuint.zig @@ -44,14 +44,8 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t // If 0 <= exponent < significandBits, right shift to get the result. // Otherwise, shift left. if (exponent < significandBits) { - // TODO this is a workaround for the mysterious "integer cast truncated bits" - // happening on the next line - @setRuntimeSafety(false); return @intCast(fixuint_t, significand >> @intCast(Log2Int(rep_t), significandBits - exponent)); } else { - // TODO this is a workaround for the mysterious "integer cast truncated bits" - // happening on the next line - @setRuntimeSafety(false); return @intCast(fixuint_t, significand) << @intCast(Log2Int(fixuint_t), exponent - significandBits); } } diff --git a/std/special/compiler_rt/fixunstfdi_test.zig b/std/special/compiler_rt/fixunstfdi_test.zig index dd0869195a..6b1b9b7bd2 100644 --- a/std/special/compiler_rt/fixunstfdi_test.zig +++ b/std/special/compiler_rt/fixunstfdi_test.zig @@ -36,15 +36,14 @@ test "fixunstfdi" { test__fixunstfdi(-0x1.FFFFFFFFFFFFFp+62, 0); test__fixunstfdi(-0x1.FFFFFFFFFFFFEp+62, 0); - // TODO enable these tests when we can parse f128 float literals - //test__fixunstfdi(0x1.FFFFFFFFFFFFFFFEp+63, 0xFFFFFFFFFFFFFFFF); - //test__fixunstfdi(0x1.0000000000000002p+63, 0x8000000000000001); - //test__fixunstfdi(0x1.0000000000000000p+63, 0x8000000000000000); - //test__fixunstfdi(0x1.FFFFFFFFFFFFFFFCp+62, 0x7FFFFFFFFFFFFFFF); - //test__fixunstfdi(0x1.FFFFFFFFFFFFFFF8p+62, 0x7FFFFFFFFFFFFFFE); - //test__fixunstfdi(0x1.p+64, 0xFFFFFFFFFFFFFFFF); - - //test__fixunstfdi(-0x1.0000000000000000p+63, 0); - //test__fixunstfdi(-0x1.FFFFFFFFFFFFFFFCp+62, 0); - //test__fixunstfdi(-0x1.FFFFFFFFFFFFFFF8p+62, 0); + test__fixunstfdi(0x1.FFFFFFFFFFFFFFFEp+63, 0xFFFFFFFFFFFFFFFF); + test__fixunstfdi(0x1.0000000000000002p+63, 0x8000000000000001); + test__fixunstfdi(0x1.0000000000000000p+63, 0x8000000000000000); + test__fixunstfdi(0x1.FFFFFFFFFFFFFFFCp+62, 0x7FFFFFFFFFFFFFFF); + test__fixunstfdi(0x1.FFFFFFFFFFFFFFF8p+62, 0x7FFFFFFFFFFFFFFE); + test__fixunstfdi(0x1.p+64, 0xFFFFFFFFFFFFFFFF); + + test__fixunstfdi(-0x1.0000000000000000p+63, 0); + test__fixunstfdi(-0x1.FFFFFFFFFFFFFFFCp+62, 0); + test__fixunstfdi(-0x1.FFFFFFFFFFFFFFF8p+62, 0); } diff --git a/std/special/compiler_rt/fixunstfsi_test.zig b/std/special/compiler_rt/fixunstfsi_test.zig index f682191994..f47fcb3c86 100644 --- a/std/special/compiler_rt/fixunstfsi_test.zig +++ b/std/special/compiler_rt/fixunstfsi_test.zig @@ -11,9 +11,9 @@ const inf128 = @bitCast(f128, u128(0x7fff0000000000000000000000000000)); test "fixunstfsi" { test__fixunstfsi(inf128, 0xffffffff); test__fixunstfsi(0, 0x0); - //TODO test__fixunstfsi(0x1.23456789abcdefp+5, 0x24); + test__fixunstfsi(0x1.23456789abcdefp+5, 0x24); test__fixunstfsi(0x1.23456789abcdefp-3, 0x0); - //TODO test__fixunstfsi(0x1.23456789abcdefp+20, 0x123456); + test__fixunstfsi(0x1.23456789abcdefp+20, 0x123456); test__fixunstfsi(0x1.23456789abcdefp+40, 0xffffffff); test__fixunstfsi(0x1.23456789abcdefp+256, 0xffffffff); test__fixunstfsi(-0x1.23456789abcdefp+3, 0x0); diff --git a/std/special/compiler_rt/floatunsitf.zig b/std/special/compiler_rt/floatunsitf.zig index 625f90a3d0..19a5918bd0 100644 --- a/std/special/compiler_rt/floatunsitf.zig +++ b/std/special/compiler_rt/floatunsitf.zig @@ -17,7 +17,7 @@ pub extern fn __floatunsitf(a: u64) f128 { const exp = (u64.bit_count - 1) - @clz(a); const shift = mantissa_bits - @intCast(u7, exp); - // TODO: @bitCast alignment error + // TODO(#1148): @bitCast alignment error var result align(16) = (@intCast(u128, a) << shift) ^ implicit_bit; result += (@intCast(u128, exp) + exponent_bias) << mantissa_bits; -- cgit v1.2.3 From 27fc49f72c681c0643c2454fe0393abb913703f9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 3 Jul 2018 14:03:27 -0400 Subject: langref: improve docs for while and undefined closes #1190 --- doc/langref.html.in | 127 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 95 insertions(+), 32 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 1da4205b89..3cdcdc6e88 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -616,6 +616,17 @@ test "init with undefined" { assert(x == 1); } {#code_end#} +

    + undefined can be {#link|implicitly cast|Implicit Casts#} to any type. + Once this happens, it is no longer possible to detect that the value is undefined. + undefined means the value could be anything, even something that is nonsense + according to the type. Translated into English, undefined means "Not a meaningful + value. Using this value would be a bug. The value will be unused, or overwritten before being used." +

    +

    + In {#link|Debug#} mode, Zig writes 0xaa bytes to undefined memory. This is to catch + bugs early, and to help detect use of undefined memory in a debugger. +

    {#header_close#} {#header_close#} {#header_close#} @@ -2237,21 +2248,28 @@ test "switch inside function" { {#see_also|comptime|enum|@compileError|Compile Variables#} {#header_close#} {#header_open|while#} +

    + A while loop is used to repeatedly execute an expression until + some condition is no longer true. +

    {#code_begin|test|while#} const assert = @import("std").debug.assert; test "while basic" { - // A while loop is used to repeatedly execute an expression until - // some condition is no longer true. var i: usize = 0; while (i < 10) { i += 1; } assert(i == 10); } + {#code_end#} +

    + Use break to exit a while loop early. +

    + {#code_begin|test|while#} +const assert = @import("std").debug.assert; test "while break" { - // You can use break to exit a while loop early. var i: usize = 0; while (true) { if (i == 10) @@ -2260,9 +2278,14 @@ test "while break" { } assert(i == 10); } + {#code_end#} +

    + Use continue to jump back to the beginning of the loop. +

    + {#code_begin|test|while#} +const assert = @import("std").debug.assert; test "while continue" { - // You can use continue to jump back to the beginning of the loop. var i: usize = 0; while (true) { i += 1; @@ -2272,18 +2295,21 @@ test "while continue" { } assert(i == 10); } + {#code_end#} +

    + While loops support a continue expression which is executed when the loop + is continued. The continue keyword respects this expression. +

    + {#code_begin|test|while#} +const assert = @import("std").debug.assert; test "while loop continuation expression" { - // You can give an expression to the while loop to execute when - // the loop is continued. This is respected by the continue control flow. var i: usize = 0; while (i < 10) : (i += 1) {} assert(i == 10); } test "while loop continuation expression, more complicated" { - // More complex blocks can be used as an expression in the loop continue - // expression. var i1: usize = 1; var j1: usize = 1; while (i1 * j1 < 2000) : ({ i1 *= 2; j1 *= 3; }) { @@ -2291,6 +2317,20 @@ test "while loop continuation expression, more complicated" { assert(my_ij1 < 2000); } } + {#code_end#} +

    + While loops are expressions. The result of the expression is the + result of the else clause of a while loop, which is executed when + the condition of the while loop is tested as false. +

    +

    + break, like return, accepts a value + parameter. This is the result of the while expression. + When you break from a while loop, the else branch is not + evaluated. +

    + {#code_begin|test|while#} +const assert = @import("std").debug.assert; test "while else" { assert(rangeHasNumber(0, 10, 5)); @@ -2299,24 +2339,31 @@ test "while else" { fn rangeHasNumber(begin: usize, end: usize, number: usize) bool { var i = begin; - // While loops are expressions. The result of the expression is the - // result of the else clause of a while loop, which is executed when - // the condition of the while loop is tested as false. return while (i < end) : (i += 1) { if (i == number) { - // break expressions, like return expressions, accept a value - // parameter. This is the result of the while expression. - // When you break from a while loop, the else branch is not - // evaluated. break true; } } else false; } + {#code_end#} + {#header_open|while with Optionals#} +

    + Just like {#link|if#} expressions, while loops can take an optional as the + condition and capture the payload. When {#link|null#} is encountered the loop + exits. +

    +

    + When the |x| syntax is present on a while expression, + the while condition must have an {#link|Optional Type#}. +

    +

    + The else branch is allowed on optional iteration. In this case, it will + be executed on the first null value encountered. +

    + {#code_begin|test|while#} +const assert = @import("std").debug.assert; test "while null capture" { - // Just like if expressions, while loops can take an optional as the - // condition and capture the payload. When null is encountered the loop - // exits. var sum1: u32 = 0; numbers_left = 3; while (eventuallyNullSequence()) |value| { @@ -2324,8 +2371,6 @@ test "while null capture" { } assert(sum1 == 3); - // The else branch is allowed on optional iteration. In this case, it will - // be executed on the first null value encountered. var sum2: u32 = 0; numbers_left = 3; while (eventuallyNullSequence()) |value| { @@ -2333,18 +2378,6 @@ test "while null capture" { } else { assert(sum1 == 3); } - - // Just like if expressions, while loops can also take an error union as - // the condition and capture the payload or the error code. When the - // condition results in an error code the else branch is evaluated and - // the loop is finished. - var sum3: u32 = 0; - numbers_left = 3; - while (eventuallyErrorSequence()) |value| { - sum3 += value; - } else |err| { - assert(err == error.ReachedZero); - } } var numbers_left: u32 = undefined; @@ -2355,6 +2388,35 @@ fn eventuallyNullSequence() ?u32 { }; } + {#code_end#} + {#header_close#} + + {#header_open|while with Error Unions#} +

    + Just like {#link|if#} expressions, while loops can take an error union as + the condition and capture the payload or the error code. When the + condition results in an error code the else branch is evaluated and + the loop is finished. +

    +

    + When the else |x| syntax is present on a while expression, + the while condition must have an {#link|Error Union Type#}. +

    + {#code_begin|test|while#} +const assert = @import("std").debug.assert; + +test "while error union capture" { + var sum1: u32 = 0; + numbers_left = 3; + while (eventuallyErrorSequence()) |value| { + sum1 += value; + } else |err| { + assert(err == error.ReachedZero); + } +} + +var numbers_left: u32 = undefined; + fn eventuallyErrorSequence() error!u32 { return if (numbers_left == 0) error.ReachedZero else blk: { numbers_left -= 1; @@ -2362,6 +2424,7 @@ fn eventuallyErrorSequence() error!u32 { }; } {#code_end#} + {#header_close#} {#header_open|inline while#}

    -- cgit v1.2.3 From 291afcf75ab458e54a8ccd78dfd1531debfd2e40 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 3 Jul 2018 14:20:26 -0400 Subject: fix runtime libc detection depending on locale closes #1165 --- src/analyze.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 6f94deb9fd..ca582dfc4c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4417,22 +4417,14 @@ Buf *get_linux_libc_include_path(void) { } char *prev_newline = buf_ptr(out_stderr); ZigList search_paths = {}; - bool found_search_paths = false; for (;;) { char *newline = strchr(prev_newline, '\n'); if (newline == nullptr) { - zig_panic("unable to determine libc include path: bad output from C compiler command"); + break; } *newline = 0; - if (found_search_paths) { - if (strcmp(prev_newline, "End of search list.") == 0) { - break; - } + if (prev_newline[0] == ' ') { search_paths.append(prev_newline); - } else { - if (strcmp(prev_newline, "#include <...> search starts here:") == 0) { - found_search_paths = true; - } } prev_newline = newline + 1; } -- cgit v1.2.3 From 4f32b86142bec636b9d6be7b72d02b836120eb35 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Wed, 4 Jul 2018 11:29:02 +0200 Subject: Allow allocation of any 0 sized type (not just void) --- std/mem.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/mem.zig b/std/mem.zig index ba59faf711..bfd74669f2 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -34,7 +34,7 @@ pub const Allocator = struct { /// Call `destroy` with the result pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) { const T = @typeOf(init); - if (@sizeOf(T) == 0) return &{}; + if (@sizeOf(T) == 0) return &T{}; const slice = try self.alloc(T, 1); const ptr = &slice[0]; ptr.* = init; -- cgit v1.2.3 From 28821b5f31ec3a23d3a5def6ea0d242fdfede422 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Wed, 4 Jul 2018 11:35:29 +0200 Subject: Fixed last commit compiler error --- std/mem.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/mem.zig b/std/mem.zig index bfd74669f2..b52d3e9f68 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -34,7 +34,7 @@ pub const Allocator = struct { /// Call `destroy` with the result pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) { const T = @typeOf(init); - if (@sizeOf(T) == 0) return &T{}; + if (@sizeOf(T) == 0) return &(T{}); const slice = try self.alloc(T, 1); const ptr = &slice[0]; ptr.* = init; -- cgit v1.2.3 From 1d1868862863e8ce52bd77c100e02ae9e27ec274 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Wed, 4 Jul 2018 23:47:15 +1200 Subject: Do not normalize langref.html.in line endings See #1191. --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index f86311554f..e4a9c93776 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ *.zig text eol=lf +langref.html.in text eol=lf -- cgit v1.2.3 From 8c39cdc89f2ae7fc25c3856e7c4c6b4662ac8a80 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 3 Jul 2018 21:36:16 -0400 Subject: fix await on early return when return type is struct previously, await on an early return would try to access the destroyed coroutine frame; now it copies the result into a temporary variable before destroying the coroutine frame --- src/ir.cpp | 12 +++------ test/behavior.zig | 1 + test/cases/coroutine_await_struct.zig | 47 +++++++++++++++++++++++++++++++++++ test/cases/coroutines.zig | 4 +-- 4 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 test/cases/coroutine_await_struct.zig diff --git a/src/ir.cpp b/src/ir.cpp index 5df5c1d676..b40c2dc36d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6674,7 +6674,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast } Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name); + // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, + // because we're about to destroy the memory. So we store it into our result variable. IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr); + ir_build_store_ptr(irb, parent_scope, node, my_result_var_ptr, no_suspend_result); ir_build_cancel(irb, parent_scope, node, target_inst); ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); @@ -6696,17 +6699,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, resume_block); - IrInstruction *yes_suspend_result = ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr); ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, merge_block); - IrBasicBlock **incoming_blocks = allocate(2); - IrInstruction **incoming_values = allocate(2); - incoming_blocks[0] = resume_block; - incoming_values[0] = yes_suspend_result; - incoming_blocks[1] = no_suspend_block; - incoming_values[1] = no_suspend_result; - return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); + return ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr); } static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { diff --git a/test/behavior.zig b/test/behavior.zig index 3766ed4305..d47eb8fd6c 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -18,6 +18,7 @@ comptime { _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); _ = @import("cases/coroutines.zig"); + _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/defer.zig"); _ = @import("cases/enum.zig"); _ = @import("cases/enum_with_members.zig"); diff --git a/test/cases/coroutine_await_struct.zig b/test/cases/coroutine_await_struct.zig new file mode 100644 index 0000000000..56c526092d --- /dev/null +++ b/test/cases/coroutine_await_struct.zig @@ -0,0 +1,47 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; + +const Foo = struct { + x: i32, +}; + +var await_a_promise: promise = undefined; +var await_final_result = Foo{ .x = 0 }; + +test "coroutine await struct" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + await_seq('a'); + const p = async<&da.allocator> await_amain() catch unreachable; + await_seq('f'); + resume await_a_promise; + await_seq('i'); + assert(await_final_result.x == 1234); + assert(std.mem.eql(u8, await_points, "abcdefghi")); +} +async fn await_amain() void { + await_seq('b'); + const p = async await_another() catch unreachable; + await_seq('e'); + await_final_result = await p; + await_seq('h'); +} +async fn await_another() Foo { + await_seq('c'); + suspend |p| { + await_seq('d'); + await_a_promise = p; + } + await_seq('g'); + return Foo{ .x = 1234 }; +} + +var await_points = []u8{0} ** "abcdefghi".len; +var await_seq_index: usize = 0; + +fn await_seq(c: u8) void { + await_points[await_seq_index] = c; + await_seq_index += 1; +} diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index b3899b306b..f7f2af62a6 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -116,14 +116,14 @@ test "coroutine await early return" { defer da.deinit(); early_seq('a'); - const p = async<&da.allocator> early_amain() catch unreachable; + const p = async<&da.allocator> early_amain() catch @panic("out of memory"); early_seq('f'); assert(early_final_result == 1234); assert(std.mem.eql(u8, early_points, "abcdef")); } async fn early_amain() void { early_seq('b'); - const p = async early_another() catch unreachable; + const p = async early_another() catch @panic("out of memory"); early_seq('d'); early_final_result = await p; early_seq('e'); -- cgit v1.2.3 From 9395162a7c41689bcd1c0c48f9eabffc1485fc74 Mon Sep 17 00:00:00 2001 From: Isaac Hier Date: Mon, 2 Jul 2018 16:56:40 -0400 Subject: Debug enum issue --- src/ir.cpp | 1 + test/behavior.zig | 1 + test/cases/switch_usize_enum_prongs.zig | 11 +++++++++++ 3 files changed, 13 insertions(+) create mode 100644 test/cases/switch_usize_enum_prongs.zig diff --git a/src/ir.cpp b/src/ir.cpp index b40c2dc36d..c16f3c09b8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19149,6 +19149,7 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (!end_val) return ira->codegen->builtin_types.entry_invalid; + printf("%s\n", buf_ptr(&start_val->type->name)); assert(start_val->type->id == TypeTableEntryIdInt || start_val->type->id == TypeTableEntryIdComptimeInt); assert(end_val->type->id == TypeTableEntryIdInt || end_val->type->id == TypeTableEntryIdComptimeInt); AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, diff --git a/test/behavior.zig b/test/behavior.zig index d47eb8fd6c..803d4a5a08 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -52,6 +52,7 @@ comptime { _ = @import("cases/switch.zig"); _ = @import("cases/switch_prong_err_enum.zig"); _ = @import("cases/switch_prong_implicit_cast.zig"); + _ = @import("cases/switch_usize_enum_prongs.zig"); _ = @import("cases/syntax.zig"); _ = @import("cases/this.zig"); _ = @import("cases/try.zig"); diff --git a/test/cases/switch_usize_enum_prongs.zig b/test/cases/switch_usize_enum_prongs.zig new file mode 100644 index 0000000000..b49615e887 --- /dev/null +++ b/test/cases/switch_usize_enum_prongs.zig @@ -0,0 +1,11 @@ +const E = enum(usize) { One, Two }; + +test "aoeou" { + foo(1); +} + +fn foo(x: usize) void { + switch (x) { + E.One => {}, + } +} -- cgit v1.2.3 From 9cff23dbf9ff3da716a1c4397f9411eba09f6cac Mon Sep 17 00:00:00 2001 From: Isaac Hier Date: Wed, 4 Jul 2018 13:27:10 -0400 Subject: Fix assertion crash on enum switch values --- src/ir.cpp | 7 ++++++- test/behavior.zig | 1 - test/cases/switch_usize_enum_prongs.zig | 11 ----------- test/compile_errors.zig | 18 ++++++++++++++++++ 4 files changed, 24 insertions(+), 13 deletions(-) delete mode 100644 test/cases/switch_usize_enum_prongs.zig diff --git a/src/ir.cpp b/src/ir.cpp index c16f3c09b8..37d673bbd7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19149,9 +19149,14 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (!end_val) return ira->codegen->builtin_types.entry_invalid; - printf("%s\n", buf_ptr(&start_val->type->name)); + if (start_val->type->id == TypeTableEntryIdEnum) + return ira->codegen->builtin_types.entry_invalid; assert(start_val->type->id == TypeTableEntryIdInt || start_val->type->id == TypeTableEntryIdComptimeInt); + + if (end_val->type->id == TypeTableEntryIdEnum) + return ira->codegen->builtin_types.entry_invalid; assert(end_val->type->id == TypeTableEntryIdInt || end_val->type->id == TypeTableEntryIdComptimeInt); + AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, start_value->source_node); if (prev_node != nullptr) { diff --git a/test/behavior.zig b/test/behavior.zig index 803d4a5a08..d47eb8fd6c 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -52,7 +52,6 @@ comptime { _ = @import("cases/switch.zig"); _ = @import("cases/switch_prong_err_enum.zig"); _ = @import("cases/switch_prong_implicit_cast.zig"); - _ = @import("cases/switch_usize_enum_prongs.zig"); _ = @import("cases/syntax.zig"); _ = @import("cases/this.zig"); _ = @import("cases/try.zig"); diff --git a/test/cases/switch_usize_enum_prongs.zig b/test/cases/switch_usize_enum_prongs.zig deleted file mode 100644 index b49615e887..0000000000 --- a/test/cases/switch_usize_enum_prongs.zig +++ /dev/null @@ -1,11 +0,0 @@ -const E = enum(usize) { One, Two }; - -test "aoeou" { - foo(1); -} - -fn foo(x: usize) void { - switch (x) { - E.One => {}, - } -} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 7291a48a8f..8bd5480395 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -358,6 +358,24 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:14: note: other value is here", ); + + cases.add( + "invalid cast from integral type to enum", + \\const E = enum(usize) { One, Two }; + \\ + \\export fn entry() void { + \\ foo(1); + \\} + \\ + \\fn foo(x: usize) void { + \\ switch (x) { + \\ E.One => {}, + \\ } + \\} + , + ".tmp_source.zig:9:10: error: expected type 'usize', found 'E'" + ); + cases.add( "range operator in switch used on error set", \\export fn entry() void { -- cgit v1.2.3 From 1a5bd8888174ef2eb1881c1dd81d418b44625cc7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 6 Jul 2018 12:03:07 -0400 Subject: alternate implementation of previous commit This strategy adds another field to the SwitchBr instruction, which is the result of the CheckSwitchProngs instruction. The type of the result is void, and is unused, except that the SwitchBr instruction will not perform analysis if the CheckSwitchProngs instruction did not pass analysis. This allows the CheckSwitchProngs instruction to do implicit casting for its type checking, while preventing duplicate compile error messages. --- src/all_types.hpp | 1 + src/ir.cpp | 44 +++++++++++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 5d449491c8..4d97be468c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2193,6 +2193,7 @@ struct IrInstructionSwitchBr { size_t case_count; IrInstructionSwitchBrCase *cases; IrInstruction *is_comptime; + IrInstruction *switch_prongs_void; }; struct IrInstructionSwitchVar { diff --git a/src/ir.cpp b/src/ir.cpp index 37d673bbd7..204ebb332a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1719,7 +1719,8 @@ static IrInstruction *ir_build_ctz_from(IrBuilder *irb, IrInstruction *old_instr } static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value, - IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime) + IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, + IrInstruction *switch_prongs_void) { IrInstructionSwitchBr *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; @@ -1729,10 +1730,12 @@ static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode * instruction->case_count = case_count; instruction->cases = cases; instruction->is_comptime = is_comptime; + instruction->switch_prongs_void = switch_prongs_void; ir_ref_instruction(target_value, irb->current_basic_block); if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); ir_ref_bb(else_block); + if (switch_prongs_void) ir_ref_instruction(switch_prongs_void, irb->current_basic_block); for (size_t i = 0; i < case_count; i += 1) { ir_ref_instruction(cases[i].value, irb->current_basic_block); @@ -1744,10 +1747,10 @@ static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode * static IrInstruction *ir_build_switch_br_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *target_value, IrBasicBlock *else_block, size_t case_count, - IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime) + IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, IrInstruction *switch_prongs_void) { IrInstruction *new_instruction = ir_build_switch_br(irb, old_instruction->scope, old_instruction->source_node, - target_value, else_block, case_count, cases, is_comptime); + target_value, else_block, case_count, cases, is_comptime, switch_prongs_void); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } @@ -6035,13 +6038,13 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * } - ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length, + IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length, else_prong != nullptr); if (cases.length == 0) { ir_build_br(irb, scope, node, else_block, is_comptime); } else { - ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, is_comptime); + ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, is_comptime, switch_prongs_void); } if (!else_prong) { @@ -6692,7 +6695,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1); cases[1].block = cleanup_block; ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, - 2, cases, const_bool_false); + 2, cases, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); @@ -6773,7 +6776,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1)); cases[1].block = cleanup_block; ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, - 2, cases, const_bool_false)); + 2, cases, const_bool_false, nullptr)); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); @@ -7078,7 +7081,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec cases[0].block = invalid_resume_block; cases[1].value = ir_build_const_u8(irb, scope, node, 1); cases[1].block = irb->exec->coro_final_cleanup_block; - ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false); + ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_suspend_block); ir_build_coro_end(irb, scope, node); @@ -15297,6 +15300,13 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira, if (type_is_invalid(target_value->value.type)) return ir_unreach_error(ira); + if (switch_br_instruction->switch_prongs_void != nullptr) { + if (type_is_invalid(switch_br_instruction->switch_prongs_void->other->value.type)) { + return ir_unreach_error(ira); + } + } + + size_t case_count = switch_br_instruction->case_count; bool is_comptime; @@ -15387,7 +15397,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira, IrBasicBlock *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block, &switch_br_instruction->base); ir_build_switch_br_from(&ira->new_irb, &switch_br_instruction->base, - target_value, new_else_block, case_count, cases, nullptr); + target_value, new_else_block, case_count, cases, nullptr, nullptr); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } @@ -19136,27 +19146,27 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira IrInstruction *start_value = range->start->other; if (type_is_invalid(start_value->value.type)) return ira->codegen->builtin_types.entry_invalid; + IrInstruction *casted_start_value = ir_implicit_cast(ira, start_value, switch_type); + if (type_is_invalid(casted_start_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; IrInstruction *end_value = range->end->other; if (type_is_invalid(end_value->value.type)) return ira->codegen->builtin_types.entry_invalid; + IrInstruction *casted_end_value = ir_implicit_cast(ira, end_value, switch_type); + if (type_is_invalid(casted_end_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *start_val = ir_resolve_const(ira, start_value, UndefBad); + ConstExprValue *start_val = ir_resolve_const(ira, casted_start_value, UndefBad); if (!start_val) return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *end_val = ir_resolve_const(ira, end_value, UndefBad); + ConstExprValue *end_val = ir_resolve_const(ira, casted_end_value, UndefBad); if (!end_val) return ira->codegen->builtin_types.entry_invalid; - if (start_val->type->id == TypeTableEntryIdEnum) - return ira->codegen->builtin_types.entry_invalid; assert(start_val->type->id == TypeTableEntryIdInt || start_val->type->id == TypeTableEntryIdComptimeInt); - - if (end_val->type->id == TypeTableEntryIdEnum) - return ira->codegen->builtin_types.entry_invalid; assert(end_val->type->id == TypeTableEntryIdInt || end_val->type->id == TypeTableEntryIdComptimeInt); - AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, start_value->source_node); if (prev_node != nullptr) { -- cgit v1.2.3 From 6d793c0ea3679fe420199676e92e435c81617258 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 6 Jul 2018 16:20:31 -0400 Subject: langref: add more internal links --- doc/langref.html.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 3cdcdc6e88..5c1cc130ac 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -679,7 +679,7 @@ fn divide(a: i32, b: i32) i32 { {#header_open|Float Literals#}

    Float literals have type comptime_float which is guaranteed to hold at least all possible values - that the largest other floating point type can hold. Float literals implicitly cast to any other type. + that the largest other floating point type can hold. Float literals {#link|implicitly cast|Implicit Casts#} to any other type.

    {#code_begin|syntax#} const floating_point = 123.0E+77; @@ -1604,7 +1604,7 @@ test "variable alignment" { } } {#code_end#} -

    In the same way that a *i32 can be implicitly cast to a +

    In the same way that a *i32 can be {#link|implicitly cast|Implicit Casts#} to a *const i32, a pointer with a larger alignment can be implicitly cast to a pointer with a smaller alignment, but not vice versa.

    @@ -2968,7 +2968,7 @@ test "fn reflection" { However right now it is hard coded to be a u16. See #768.

    - You can implicitly cast an error from a subset to its superset: + You can {#link|implicitly cast|Implicit Casts#} an error from a subset to its superset:

    {#code_begin|test#} const std = @import("std"); @@ -3101,7 +3101,7 @@ test "parse u64" {

    Within the function definition, you can see some return statements that return an error, and at the bottom a return statement that returns a u64. - Both types implicitly cast to error!u64. + Both types {#link|implicitly cast|Implicit Casts#} to error!u64.

    What it looks like to use this function varies depending on what you're -- cgit v1.2.3 From 1cf7511dc9d449473748675a5e734e81ea7c85c2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 6 Jul 2018 16:20:46 -0400 Subject: add compile error notes for where struct definitions are closes #1202 --- src/analyze.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/analyze.hpp | 1 + src/ir.cpp | 47 ++++++++++++++++++++++++++++++++++++----------- test/compile_errors.zig | 42 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 110 insertions(+), 17 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index ca582dfc4c..643a85634e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -212,6 +212,43 @@ static uint8_t bits_needed_for_unsigned(uint64_t x) { return (upper >= x) ? base : (base + 1); } +AstNode *type_decl_node(TypeTableEntry *type_entry) { + switch (type_entry->id) { + case TypeTableEntryIdInvalid: + zig_unreachable(); + case TypeTableEntryIdStruct: + return type_entry->data.structure.decl_node; + case TypeTableEntryIdEnum: + return type_entry->data.enumeration.decl_node; + case TypeTableEntryIdUnion: + return type_entry->data.unionation.decl_node; + case TypeTableEntryIdOpaque: + case TypeTableEntryIdMetaType: + case TypeTableEntryIdVoid: + case TypeTableEntryIdBool: + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + case TypeTableEntryIdPointer: + case TypeTableEntryIdArray: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: + case TypeTableEntryIdUndefined: + case TypeTableEntryIdNull: + case TypeTableEntryIdOptional: + case TypeTableEntryIdErrorUnion: + case TypeTableEntryIdErrorSet: + case TypeTableEntryIdFn: + case TypeTableEntryIdNamespace: + case TypeTableEntryIdBlock: + case TypeTableEntryIdBoundFn: + case TypeTableEntryIdArgTuple: + case TypeTableEntryIdPromise: + return nullptr; + } + zig_unreachable(); +} + bool type_is_complete(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: diff --git a/src/analyze.hpp b/src/analyze.hpp index c2730197e2..5168509fe0 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -202,5 +202,6 @@ uint32_t get_coro_frame_align_bytes(CodeGen *g); bool fn_type_can_fail(FnTypeId *fn_type_id); bool type_can_fail(TypeTableEntry *type_entry); bool fn_eval_cacheable(Scope *scope, TypeTableEntry *return_type); +AstNode *type_decl_node(TypeTableEntry *type_entry); #endif diff --git a/src/ir.cpp b/src/ir.cpp index 204ebb332a..3ad7c77645 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -82,6 +82,7 @@ struct ConstCastSliceMismatch; struct ConstCastErrUnionErrSetMismatch; struct ConstCastErrUnionPayloadMismatch; struct ConstCastErrSetMismatch; +struct ConstCastTypeMismatch; struct ConstCastOnly { ConstCastResultId id; @@ -92,6 +93,7 @@ struct ConstCastOnly { ConstCastOptionalMismatch *optional; ConstCastErrUnionPayloadMismatch *error_union_payload; ConstCastErrUnionErrSetMismatch *error_union_error_set; + ConstCastTypeMismatch *type_mismatch; ConstCastOnly *return_type; ConstCastOnly *async_allocator_type; ConstCastOnly *null_wrap_ptr_child; @@ -100,6 +102,11 @@ struct ConstCastOnly { } data; }; +struct ConstCastTypeMismatch { + TypeTableEntry *wanted_type; + TypeTableEntry *actual_type; +}; + struct ConstCastOptionalMismatch { ConstCastOnly child; TypeTableEntry *wanted_child; @@ -8128,15 +8135,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry } // pointer const - if (wanted_type->id == TypeTableEntryIdPointer && - actual_type->id == TypeTableEntryIdPointer && - (actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) && - (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && - (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) && - actual_type->data.pointer.bit_offset == wanted_type->data.pointer.bit_offset && - actual_type->data.pointer.unaligned_bit_count == wanted_type->data.pointer.unaligned_bit_count && - actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment) - { + if (wanted_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const); if (child.id != ConstCastResultIdOk) { @@ -8145,8 +8144,17 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry result.data.pointer_mismatch->child = child; result.data.pointer_mismatch->wanted_child = wanted_type->data.pointer.child_type; result.data.pointer_mismatch->actual_child = actual_type->data.pointer.child_type; + return result; + } + if ((actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) && + (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && + (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) && + actual_type->data.pointer.bit_offset == wanted_type->data.pointer.bit_offset && + actual_type->data.pointer.unaligned_bit_count == wanted_type->data.pointer.unaligned_bit_count && + actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment) + { + return result; } - return result; } // slice const @@ -8341,6 +8349,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry } result.id = ConstCastResultIdType; + result.data.type_mismatch = allocate_nonzero(1); + result.data.type_mismatch->wanted_type = wanted_type; + result.data.type_mismatch->actual_type = actual_type; return result; } @@ -10154,6 +10165,21 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa report_recursive_error(ira, source_node, &cast_result->data.error_union_payload->child, msg); break; } + case ConstCastResultIdType: { + AstNode *wanted_decl_node = type_decl_node(cast_result->data.type_mismatch->wanted_type); + AstNode *actual_decl_node = type_decl_node(cast_result->data.type_mismatch->actual_type); + if (wanted_decl_node != nullptr) { + add_error_note(ira->codegen, parent_msg, wanted_decl_node, + buf_sprintf("%s declared here", + buf_ptr(&cast_result->data.type_mismatch->wanted_type->name))); + } + if (actual_decl_node != nullptr) { + add_error_note(ira->codegen, parent_msg, actual_decl_node, + buf_sprintf("%s declared here", + buf_ptr(&cast_result->data.type_mismatch->actual_type->name))); + } + break; + } case ConstCastResultIdFnAlign: // TODO case ConstCastResultIdFnCC: // TODO case ConstCastResultIdFnVarArgs: // TODO @@ -10163,7 +10189,6 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa case ConstCastResultIdFnGenericArgCount: // TODO case ConstCastResultIdFnArg: // TODO case ConstCastResultIdFnArgNoAlias: // TODO - case ConstCastResultIdType: // TODO case ConstCastResultIdUnresolvedInferredErrSet: // TODO case ConstCastResultIdAsyncAllocatorType: // TODO case ConstCastResultIdNullWrapPtr: // TODO diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 8bd5480395..d508c7c36c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,40 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addCase(x: { + const tc = cases.create( + "wrong same named struct", + \\const a = @import("a.zig"); + \\const b = @import("b.zig"); + \\ + \\export fn entry() void { + \\ var a1: a.Foo = undefined; + \\ bar(&a1); + \\} + \\ + \\fn bar(x: *b.Foo) void {} + , + ".tmp_source.zig:6:10: error: expected type '*Foo', found '*Foo'", + ".tmp_source.zig:6:10: note: pointer type child 'Foo' cannot cast into pointer type child 'Foo'", + "a.zig:1:17: note: Foo declared here", + "b.zig:1:17: note: Foo declared here", + ); + + tc.addSourceFile("a.zig", + \\pub const Foo = struct { + \\ x: i32, + \\}; + ); + + tc.addSourceFile("b.zig", + \\pub const Foo = struct { + \\ z: f64, + \\}; + ); + + break :x tc; + }); + cases.add( "enum field value references enum", \\pub const Foo = extern enum { @@ -358,9 +392,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:14: note: other value is here", ); - - cases.add( - "invalid cast from integral type to enum", + cases.add("invalid cast from integral type to enum", \\const E = enum(usize) { One, Two }; \\ \\export fn entry() void { @@ -372,9 +404,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ E.One => {}, \\ } \\} - , - ".tmp_source.zig:9:10: error: expected type 'usize', found 'E'" - ); + , ".tmp_source.zig:9:10: error: expected type 'usize', found 'E'"); cases.add( "range operator in switch used on error set", -- cgit v1.2.3 From 4ad4cd26541258a84faf97e9fe07a69fadc57c66 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 6 Jul 2018 17:27:44 -0400 Subject: fix iterating over a void slice closes #1203 --- src/codegen.cpp | 24 ++++++++++++++++++------ test/cases/void.zig | 12 ++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 9c37c174d6..26ee106959 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2992,18 +2992,26 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, ""); } else if (array_type->id == TypeTableEntryIdStruct) { assert(array_type->data.structure.is_slice); + if (!type_has_bits(instruction->base.value.type)) { + if (safety_check_on) { + assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMIntegerTypeKind); + add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, array_ptr); + } + return nullptr; + } + assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); if (safety_check_on) { - size_t len_index = array_type->data.structure.fields[1].gen_index; + size_t len_index = array_type->data.structure.fields[slice_len_index].gen_index; assert(len_index != SIZE_MAX); LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, ""); LLVMValueRef len = gen_load_untyped(g, len_ptr, 0, false, ""); add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len); } - size_t ptr_index = array_type->data.structure.fields[0].gen_index; + size_t ptr_index = array_type->data.structure.fields[slice_ptr_index].gen_index; assert(ptr_index != SIZE_MAX); LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); LLVMValueRef ptr = gen_load_untyped(g, ptr_ptr, 0, false, ""); @@ -3983,11 +3991,15 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); } - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, ""); - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + if (type_has_bits(array_type)) { + size_t gen_ptr_index = instruction->base.value.type->data.structure.fields[slice_ptr_index].gen_index; + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + } - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); + size_t gen_len_index = instruction->base.value.type->data.structure.fields[slice_len_index].gen_index; + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); gen_store_untyped(g, len_value, len_field_ptr, 0, false); diff --git a/test/cases/void.zig b/test/cases/void.zig index ef91690878..7121ac664b 100644 --- a/test/cases/void.zig +++ b/test/cases/void.zig @@ -16,3 +16,15 @@ test "compare void with void compile time known" { assert(foo.a == {}); } } + +test "iterate over a void slice" { + var j: usize = 0; + for (times(10)) |_, i| { + assert(i == j); + j += 1; + } +} + +fn times(n: usize) []const void { + return ([*]void)(undefined)[0..n]; +} -- cgit v1.2.3 From e19f0b5d9c26f4d309df9cfc84b7e5dc04b10ed5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 6 Jul 2018 18:24:09 -0400 Subject: remove outdated semantic analysis documentation --- doc/semantic_analysis.md | 74 ------------------------------------------------ 1 file changed, 74 deletions(-) delete mode 100644 doc/semantic_analysis.md diff --git a/doc/semantic_analysis.md b/doc/semantic_analysis.md deleted file mode 100644 index 6e860aac42..0000000000 --- a/doc/semantic_analysis.md +++ /dev/null @@ -1,74 +0,0 @@ -# How Semantic Analysis Works - -We start with a set of files. Typically the user only has one entry point file, -which imports the other files they want to use. However, the compiler may -choose to add more files to the compilation, for example bootstrap.zig which -contains the code that calls main. - -Our goal now is to treat everything that is marked with the `export` keyword -as a root node, and then parse and semantically analyze as little as possible -in order to fulfill these exports. - -So, some parts of the code very well may have uncaught semantic errors, but as -long as the code is not referenced in any way, the compiler will not complain -because the code may as well not exist. This is similar to the fact that code -excluded from compilation with an `#ifdef` in C is not analyzed. Avoiding -analyzing unused code will save compilation time - one of Zig's goals. - -So, for each file, we iterate over the top level declarations. The set of top -level declarations are: - - * Function Definition - * Global Variable Declaration - * Container Declaration (struct or enum) - * Error Value Declaration - * Use Declaration - -Each of these can have `export` attached to them except for error value -declarations and use declarations. - -When we see a top level declaration during this iteration, we determine its -unique name identifier within the file. For example, for a function definition, -the unique name identifier is simply its name. Using this name we add the top -level declaration to a map. - -If the top level declaration is exported, we add it to a set of exported top -level identifiers. - -If the top level declaration is a use declaration, we add it to a set of use -declarations. - -If the top level declaration is an error value declaration, we assign it a value -and increment the count of error values. - -After this preliminary iteration over the top level declarations, we iterate -over the use declarations and resolve them. To resolve a use declaration, we -analyze the associated expression, verify that its type is the namespace type, -and then add all the items from the namespace into the top level declaration -map for the current file. - -To analyze an expression, we recurse the abstract syntax tree of the -expression. Whenever we must look up a symbol, if the symbol exists already, -we can use it. Otherwise, we look it up in the top level declaration map. -If it exists, we can use it. Otherwise, we interrupt resolving this use -declaration to resolve the next one. If a dependency loop is detected, emit -an error. If all use declarations are resolved yet the symbol we need still -does not exist, emit an error. - -To analyze an `@import` expression, find the referenced file, parse it, and -add it to the set of files to perform semantic analysis on. - -Proceed through the rest of the use declarations the same way. - -If we make it through the use declarations without an error, then we have a -complete map of all globals that exist in the current file. - -Next we iterate over the set of exported top level declarations. - -If it's a function definition, add it to the set of exported function -definitions and resolve the function prototype only. Otherwise, resolve the -top level declaration completely. This may involve recursively resolving other -top level declarations that expressions depend on. - -Finally, iterate over the set of exported function definitions and analyze the -bodies. -- cgit v1.2.3 From d8295c188946b0f07d62420c2f08c940f70b03ac Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 7 Jul 2018 00:25:32 -0400 Subject: add @popCount intrinsic --- doc/langref.html.in | 15 +++++++++-- src/all_types.hpp | 12 +++++++++ src/analyze.cpp | 4 +++ src/bigint.cpp | 31 ++++++++++++++++++++++ src/bigint.hpp | 2 ++ src/codegen.cpp | 21 ++++++++++++++- src/ir.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ src/ir_print.cpp | 9 +++++++ test/behavior.zig | 7 ++--- test/cases/popcount.zig | 24 +++++++++++++++++ test/compile_errors.zig | 18 +++++++++++++ 11 files changed, 205 insertions(+), 6 deletions(-) create mode 100644 test/cases/popcount.zig diff --git a/doc/langref.html.in b/doc/langref.html.in index 5c1cc130ac..8eaffb64ad 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5013,7 +5013,7 @@ comptime {

    If x is zero, @clz returns T.bit_count.

    - + {#see_also|@ctz|@popCount#} {#header_close#} {#header_open|@cmpxchgStrong#}
    @cmpxchgStrong(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T
    @@ -5149,6 +5149,7 @@ test "main" {

    If x is zero, @ctz returns T.bit_count.

    + {#see_also|@clz|@popCount#} {#header_close#} {#header_open|@divExact#}
    @divExact(numerator: T, denominator: T) T
    @@ -5631,6 +5632,16 @@ test "call foo" { {#see_also|Root Source File#} {#header_close#} + {#header_open|@popCount#} +
    @popCount(integer: var) var
    +

    Counts the number of bits set in an integer.

    +

    + If integer is known at {#link|comptime#}, the return type is comptime_int. + Otherwise, the return type is an unsigned integer with the minimum number + of bits that can represent the bit count of the integer type. +

    + {#see_also|@ctz|@clz#} + {#header_close#} {#header_open|@ptrCast#}
    @ptrCast(comptime DestType: type, value: var) DestType

    @@ -7337,7 +7348,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz popCount import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index 4d97be468c..6dcf1894d8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1352,6 +1352,7 @@ enum BuiltinFnId { BuiltinFnIdCompileLog, BuiltinFnIdCtz, BuiltinFnIdClz, + BuiltinFnIdPopCount, BuiltinFnIdImport, BuiltinFnIdCImport, BuiltinFnIdErrName, @@ -1477,6 +1478,7 @@ bool type_id_eql(TypeId a, TypeId b); enum ZigLLVMFnId { ZigLLVMFnIdCtz, ZigLLVMFnIdClz, + ZigLLVMFnIdPopCount, ZigLLVMFnIdOverflowArithmetic, ZigLLVMFnIdFloor, ZigLLVMFnIdCeil, @@ -1499,6 +1501,9 @@ struct ZigLLVMFnKey { struct { uint32_t bit_count; } clz; + struct { + uint32_t bit_count; + } pop_count; struct { uint32_t bit_count; } floating; @@ -2050,6 +2055,7 @@ enum IrInstructionId { IrInstructionIdUnionTag, IrInstructionIdClz, IrInstructionIdCtz, + IrInstructionIdPopCount, IrInstructionIdImport, IrInstructionIdCImport, IrInstructionIdCInclude, @@ -2545,6 +2551,12 @@ struct IrInstructionClz { IrInstruction *value; }; +struct IrInstructionPopCount { + IrInstruction base; + + IrInstruction *value; +}; + struct IrInstructionUnionTag { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index 643a85634e..9b60f7374a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5976,6 +5976,8 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) { return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934; case ZigLLVMFnIdClz: return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817; + case ZigLLVMFnIdPopCount: + return (uint32_t)(x.data.clz.bit_count) * (uint32_t)101195049; case ZigLLVMFnIdFloor: return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168; case ZigLLVMFnIdCeil: @@ -5998,6 +6000,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { return a.data.ctz.bit_count == b.data.ctz.bit_count; case ZigLLVMFnIdClz: return a.data.clz.bit_count == b.data.clz.bit_count; + case ZigLLVMFnIdPopCount: + return a.data.pop_count.bit_count == b.data.pop_count.bit_count; case ZigLLVMFnIdFloor: case ZigLLVMFnIdCeil: case ZigLLVMFnIdSqrt: diff --git a/src/bigint.cpp b/src/bigint.cpp index bb227a7c3d..bf18b9a1bf 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1593,6 +1593,37 @@ void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base) { } } +size_t bigint_popcount_unsigned(const BigInt *bi) { + assert(!bi->is_negative); + if (bi->digit_count == 0) + return 0; + + size_t count = 0; + size_t bit_count = bi->digit_count * 64; + for (size_t i = 0; i < bit_count; i += 1) { + if (bit_at_index(bi, i)) + count += 1; + } + return count; +} + +size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) { + if (bit_count == 0) + return 0; + if (bi->digit_count == 0) + return 0; + + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, bi, bit_count); + + size_t count = 0; + for (size_t i = 0; i < bit_count; i += 1) { + if (bit_at_index(&twos_comp, i)) + count += 1; + } + return count; +} + size_t bigint_ctz(const BigInt *bi, size_t bit_count) { if (bit_count == 0) return 0; diff --git a/src/bigint.hpp b/src/bigint.hpp index 9f044c8722..48b222a227 100644 --- a/src/bigint.hpp +++ b/src/bigint.hpp @@ -81,6 +81,8 @@ void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base); size_t bigint_ctz(const BigInt *bi, size_t bit_count); size_t bigint_clz(const BigInt *bi, size_t bit_count); +size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count); +size_t bigint_popcount_unsigned(const BigInt *bi); size_t bigint_bits_needed(const BigInt *op); diff --git a/src/codegen.cpp b/src/codegen.cpp index 26ee106959..54e2da7d61 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3426,14 +3426,22 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, BuiltinFnId fn_id) { ZigLLVMFnKey key = {}; const char *fn_name; + uint32_t n_args; if (fn_id == BuiltinFnIdCtz) { fn_name = "cttz"; + n_args = 2; key.id = ZigLLVMFnIdCtz; key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count; } else if (fn_id == BuiltinFnIdClz) { fn_name = "ctlz"; + n_args = 2; key.id = ZigLLVMFnIdClz; key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count; + } else if (fn_id == BuiltinFnIdPopCount) { + fn_name = "ctpop"; + n_args = 1; + key.id = ZigLLVMFnIdPopCount; + key.data.pop_count.bit_count = (uint32_t)int_type->data.integral.bit_count; } else { zig_unreachable(); } @@ -3448,7 +3456,7 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui int_type->type_ref, LLVMInt1Type(), }; - LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, 2, false); + LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, n_args, false); LLVMValueRef fn_val = LLVMAddFunction(g->module, llvm_name, fn_type); assert(LLVMGetIntrinsicID(fn_val)); @@ -3481,6 +3489,14 @@ static LLVMValueRef ir_render_ctz(CodeGen *g, IrExecutable *executable, IrInstru return gen_widen_or_shorten(g, false, int_type, instruction->base.value.type, wrong_size_int); } +static LLVMValueRef ir_render_pop_count(CodeGen *g, IrExecutable *executable, IrInstructionPopCount *instruction) { + TypeTableEntry *int_type = instruction->value->value.type; + LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdPopCount); + LLVMValueRef operand = ir_llvm_value(g, instruction->value); + LLVMValueRef wrong_size_int = LLVMBuildCall(g->builder, fn_val, &operand, 1, ""); + return gen_widen_or_shorten(g, false, int_type, instruction->base.value.type, wrong_size_int); +} + static LLVMValueRef ir_render_switch_br(CodeGen *g, IrExecutable *executable, IrInstructionSwitchBr *instruction) { LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value); LLVMBasicBlockRef else_block = instruction->else_block->llvm_block; @@ -4831,6 +4847,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_clz(g, executable, (IrInstructionClz *)instruction); case IrInstructionIdCtz: return ir_render_ctz(g, executable, (IrInstructionCtz *)instruction); + case IrInstructionIdPopCount: + return ir_render_pop_count(g, executable, (IrInstructionPopCount *)instruction); case IrInstructionIdSwitchBr: return ir_render_switch_br(g, executable, (IrInstructionSwitchBr *)instruction); case IrInstructionIdPhi: @@ -6342,6 +6360,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdCUndef, "cUndef", 1); create_builtin_fn(g, BuiltinFnIdCtz, "ctz", 1); create_builtin_fn(g, BuiltinFnIdClz, "clz", 1); + create_builtin_fn(g, BuiltinFnIdPopCount, "popCount", 1); create_builtin_fn(g, BuiltinFnIdImport, "import", 1); create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1); create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1); diff --git a/src/ir.cpp b/src/ir.cpp index 3ad7c77645..98b1bd85ad 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -427,6 +427,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) { return IrInstructionIdCtz; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionPopCount *) { + return IrInstructionIdPopCount; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionTag *) { return IrInstructionIdUnionTag; } @@ -1725,6 +1729,15 @@ static IrInstruction *ir_build_ctz_from(IrBuilder *irb, IrInstruction *old_instr return new_instruction; } +static IrInstruction *ir_build_pop_count(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { + IrInstructionPopCount *instruction = ir_build_instruction(irb, scope, source_node); + instruction->value = value; + + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value, IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, IrInstruction *switch_prongs_void) @@ -3841,6 +3854,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *ctz = ir_build_ctz(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, ctz, lval); } + case BuiltinFnIdPopCount: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *instr = ir_build_pop_count(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, instr, lval); + } case BuiltinFnIdClz: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -15275,6 +15298,48 @@ static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionC } } +static TypeTableEntry *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstructionPopCount *instruction) { + IrInstruction *value = instruction->value->other; + if (type_is_invalid(value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (value->value.type->id != TypeTableEntryIdInt && value->value.type->id != TypeTableEntryIdComptimeInt) { + ir_add_error(ira, value, + buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + if (instr_is_comptime(value)) { + ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + if (!val) + return ira->codegen->builtin_types.entry_invalid; + if (bigint_cmp_zero(&val->data.x_bigint) != CmpLT) { + size_t result = bigint_popcount_unsigned(&val->data.x_bigint); + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + bigint_init_unsigned(&out_val->data.x_bigint, result); + return ira->codegen->builtin_types.entry_num_lit_int; + } + if (value->value.type->id == TypeTableEntryIdComptimeInt) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &val->data.x_bigint, 10); + ir_add_error(ira, &instruction->base, + buf_sprintf("@popCount on negative %s value %s", + buf_ptr(&value->value.type->name), buf_ptr(val_buf))); + return ira->codegen->builtin_types.entry_invalid; + } + size_t result = bigint_popcount_signed(&val->data.x_bigint, value->value.type->data.integral.bit_count); + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + bigint_init_unsigned(&out_val->data.x_bigint, result); + return ira->codegen->builtin_types.entry_num_lit_int; + } + + IrInstruction *result = ir_build_pop_count(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, value); + result->value.type = get_smallest_unsigned_int_type(ira->codegen, value->value.type->data.integral.bit_count); + ir_link_new_instruction(result, &instruction->base); + return result->value.type; +} + static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) { if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; @@ -20534,6 +20599,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: return ir_analyze_instruction_ctz(ira, (IrInstructionCtz *)instruction); + case IrInstructionIdPopCount: + return ir_analyze_instruction_pop_count(ira, (IrInstructionPopCount *)instruction); case IrInstructionIdSwitchBr: return ir_analyze_instruction_switch_br(ira, (IrInstructionSwitchBr *)instruction); case IrInstructionIdSwitchTarget: @@ -20892,6 +20959,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdUnwrapOptional: case IrInstructionIdClz: case IrInstructionIdCtz: + case IrInstructionIdPopCount: case IrInstructionIdSwitchVar: case IrInstructionIdSwitchTarget: case IrInstructionIdUnionTag: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 5e5a71382c..780cf9e756 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -501,6 +501,12 @@ static void ir_print_ctz(IrPrint *irp, IrInstructionCtz *instruction) { fprintf(irp->f, ")"); } +static void ir_print_pop_count(IrPrint *irp, IrInstructionPopCount *instruction) { + fprintf(irp->f, "@popCount("); + ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, ")"); +} + static void ir_print_switch_br(IrPrint *irp, IrInstructionSwitchBr *instruction) { fprintf(irp->f, "switch ("); ir_print_other_instruction(irp, instruction->target_value); @@ -1425,6 +1431,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdCtz: ir_print_ctz(irp, (IrInstructionCtz *)instruction); break; + case IrInstructionIdPopCount: + ir_print_pop_count(irp, (IrInstructionPopCount *)instruction); + break; case IrInstructionIdClz: ir_print_clz(irp, (IrInstructionClz *)instruction); break; diff --git a/test/behavior.zig b/test/behavior.zig index d47eb8fd6c..450dded56c 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -8,17 +8,17 @@ comptime { _ = @import("cases/atomics.zig"); _ = @import("cases/bitcast.zig"); _ = @import("cases/bool.zig"); + _ = @import("cases/bugs/1111.zig"); _ = @import("cases/bugs/394.zig"); _ = @import("cases/bugs/655.zig"); _ = @import("cases/bugs/656.zig"); _ = @import("cases/bugs/828.zig"); _ = @import("cases/bugs/920.zig"); - _ = @import("cases/bugs/1111.zig"); _ = @import("cases/byval_arg_var.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); - _ = @import("cases/coroutines.zig"); _ = @import("cases/coroutine_await_struct.zig"); + _ = @import("cases/coroutines.zig"); _ = @import("cases/defer.zig"); _ = @import("cases/enum.zig"); _ = @import("cases/enum_with_members.zig"); @@ -36,11 +36,12 @@ comptime { _ = @import("cases/math.zig"); _ = @import("cases/merge_error_sets.zig"); _ = @import("cases/misc.zig"); - _ = @import("cases/optional.zig"); _ = @import("cases/namespace_depends_on_compile_var/index.zig"); _ = @import("cases/new_stack_call.zig"); _ = @import("cases/null.zig"); + _ = @import("cases/optional.zig"); _ = @import("cases/pointers.zig"); + _ = @import("cases/popcount.zig"); _ = @import("cases/pub_enum/index.zig"); _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("cases/reflection.zig"); diff --git a/test/cases/popcount.zig b/test/cases/popcount.zig new file mode 100644 index 0000000000..7dc7f28c0e --- /dev/null +++ b/test/cases/popcount.zig @@ -0,0 +1,24 @@ +const assert = @import("std").debug.assert; + +test "@popCount" { + comptime testPopCount(); + testPopCount(); +} + +fn testPopCount() void { + { + var x: u32 = 0xaa; + assert(@popCount(x) == 4); + } + { + var x: u32 = 0xaaaaaaaa; + assert(@popCount(x) == 16); + } + { + var x: i16 = -1; + assert(@popCount(x) == 16); + } + comptime { + assert(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24); + } +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d508c7c36c..9071f0ad7e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,24 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "@popCount - non-integer", + \\export fn entry(x: f32) u32 { + \\ return @popCount(x); + \\} + , + ".tmp_source.zig:2:22: error: expected integer type, found 'f32'", + ); + + cases.add( + "@popCount - negative comptime_int", + \\comptime { + \\ _ = @popCount(-1); + \\} + , + ".tmp_source.zig:2:9: error: @popCount on negative comptime_int value -1", + ); + cases.addCase(x: { const tc = cases.create( "wrong same named struct", -- cgit v1.2.3 From eb326e15530dd6dca4ccbe7dbfde7bf048de813e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 Jul 2018 15:09:02 -0400 Subject: M:N threading * add std.atomic.QueueMpsc.isEmpty * make std.debug.global_allocator thread-safe * std.event.Loop: now you have to choose between - initSingleThreaded - initMultiThreaded * std.event.Loop multiplexes coroutines onto kernel threads * Remove std.event.Loop.stop. Instead the event loop run() function returns once there are no pending coroutines. * fix crash in ir.cpp for calling methods under some conditions * small progress self-hosted compiler, analyzing top level declarations * Introduce std.event.Lock for synchronizing coroutines * introduce std.event.Locked(T) for data that only 1 coroutine should modify at once. * make the self hosted compiler use multi threaded event loop * make std.heap.DirectAllocator thread-safe See #174 TODO: * call sched_getaffinity instead of hard coding thread pool size 4 * support for Windows and MacOS * #1194 * #1197 --- src-self-hosted/main.zig | 5 +- src-self-hosted/module.zig | 257 ++++++++++++++++++-- src/ir.cpp | 2 +- std/atomic/queue_mpsc.zig | 17 ++ std/debug/index.zig | 7 +- std/event.zig | 580 +++++++++++++++++++++++++++++++++++++++------ std/heap.zig | 30 +-- std/mem.zig | 2 +- std/os/index.zig | 39 ++- std/os/linux/index.zig | 8 + 10 files changed, 833 insertions(+), 114 deletions(-) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index d17fc94c82..fe94a4460a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -384,7 +384,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1); defer allocator.free(zig_lib_dir); - var loop = try event.Loop.init(allocator); + var loop: event.Loop = undefined; + try loop.initMultiThreaded(allocator); var module = try Module.create( &loop, @@ -493,8 +494,6 @@ async fn processBuildEvents(module: *Module, watch: bool) void { switch (build_event) { Module.Event.Ok => { std.debug.warn("Build succeeded\n"); - // for now we stop after 1 - module.loop.stop(); return; }, Module.Event.Error => |err| { diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index cf27c826c8..5ce1a7965a 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -2,6 +2,7 @@ const std = @import("std"); const os = std.os; const io = std.io; const mem = std.mem; +const Allocator = mem.Allocator; const Buffer = std.Buffer; const llvm = @import("llvm.zig"); const c = @import("c.zig"); @@ -13,6 +14,7 @@ const ArrayList = std.ArrayList; const errmsg = @import("errmsg.zig"); const ast = std.zig.ast; const event = std.event; +const assert = std.debug.assert; pub const Module = struct { loop: *event.Loop, @@ -81,6 +83,8 @@ pub const Module = struct { link_out_file: ?[]const u8, events: *event.Channel(Event), + exported_symbol_names: event.Locked(Decl.Table), + // TODO handle some of these earlier and report them in a way other than error codes pub const BuildError = error{ OutOfMemory, @@ -232,6 +236,7 @@ pub const Module = struct { .test_name_prefix = null, .emit_file_type = Emit.Binary, .link_out_file = null, + .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), }); } @@ -272,38 +277,91 @@ pub const Module = struct { return; }; await (async self.events.put(Event.Ok) catch unreachable); + // for now we stop after 1 + return; } } async fn addRootSrc(self: *Module) !void { const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); + // TODO async/await os.path.real const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| { try printError("unable to get real path '{}': {}", root_src_path, err); return err; }; errdefer self.a().free(root_src_real_path); + // TODO async/await readFileAlloc() const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| { try printError("unable to open '{}': {}", root_src_real_path, err); return err; }; errdefer self.a().free(source_code); - var tree = try std.zig.parse(self.a(), source_code); - defer tree.deinit(); - - //var it = tree.root_node.decls.iterator(); - //while (it.next()) |decl_ptr| { - // const decl = decl_ptr.*; - // switch (decl.id) { - // ast.Node.Comptime => @panic("TODO"), - // ast.Node.VarDecl => @panic("TODO"), - // ast.Node.UseDecl => @panic("TODO"), - // ast.Node.FnDef => @panic("TODO"), - // ast.Node.TestDecl => @panic("TODO"), - // else => unreachable, - // } - //} + var parsed_file = ParsedFile{ + .tree = try std.zig.parse(self.a(), source_code), + .realpath = root_src_real_path, + }; + errdefer parsed_file.tree.deinit(); + + const tree = &parsed_file.tree; + + // create empty struct for it + const decls = try Scope.Decls.create(self.a(), null); + errdefer decls.destroy(); + + var it = tree.root_node.decls.iterator(0); + while (it.next()) |decl_ptr| { + const decl = decl_ptr.*; + switch (decl.id) { + ast.Node.Id.Comptime => @panic("TODO"), + ast.Node.Id.VarDecl => @panic("TODO"), + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); + + const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { + @panic("TODO add compile error"); + //try self.addCompileError( + // &parsed_file, + // fn_proto.fn_token, + // fn_proto.fn_token + 1, + // "missing function name", + //); + continue; + }; + + const fn_decl = try self.a().create(Decl.Fn{ + .base = Decl{ + .id = Decl.Id.Fn, + .name = name, + .visib = parseVisibToken(tree, fn_proto.visib_token), + .resolution = Decl.Resolution.Unresolved, + }, + .value = Decl.Fn.Val{ .Unresolved = {} }, + .fn_proto = fn_proto, + }); + errdefer self.a().destroy(fn_decl); + + // TODO make this parallel + try await try async self.addTopLevelDecl(tree, &fn_decl.base); + }, + ast.Node.Id.TestDecl => @panic("TODO"), + else => unreachable, + } + } + } + + async fn addTopLevelDecl(self: *Module, tree: *ast.Tree, decl: *Decl) !void { + const is_export = decl.isExported(tree); + + { + const exported_symbol_names = await try async self.exported_symbol_names.acquire(); + defer exported_symbol_names.release(); + + if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { + @panic("TODO report compile error"); + } + } } pub fn link(self: *Module, out_file: ?[]const u8) !void { @@ -350,3 +408,172 @@ fn printError(comptime format: []const u8, args: ...) !void { const out_stream = &stderr_file_out_stream.stream; try out_stream.print(format, args); } + +fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib { + if (optional_token_index) |token_index| { + const token = tree.tokens.at(token_index); + assert(token.id == Token.Id.Keyword_pub); + return Visib.Pub; + } else { + return Visib.Private; + } +} + +pub const Scope = struct { + id: Id, + parent: ?*Scope, + + pub const Id = enum { + Decls, + Block, + }; + + pub const Decls = struct { + base: Scope, + table: Decl.Table, + + pub fn create(a: *Allocator, parent: ?*Scope) !*Decls { + const self = try a.create(Decls{ + .base = Scope{ + .id = Id.Decls, + .parent = parent, + }, + .table = undefined, + }); + errdefer a.destroy(self); + + self.table = Decl.Table.init(a); + errdefer self.table.deinit(); + + return self; + } + + pub fn destroy(self: *Decls) void { + self.table.deinit(); + self.table.allocator.destroy(self); + self.* = undefined; + } + }; + + pub const Block = struct { + base: Scope, + }; +}; + +pub const Visib = enum { + Private, + Pub, +}; + +pub const Decl = struct { + id: Id, + name: []const u8, + visib: Visib, + resolution: Resolution, + + pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); + + pub fn isExported(base: *const Decl, tree: *ast.Tree) bool { + switch (base.id) { + Id.Fn => { + const fn_decl = @fieldParentPtr(Fn, "base", base); + return fn_decl.isExported(tree); + }, + else => return false, + } + } + + pub const Resolution = enum { + Unresolved, + InProgress, + Invalid, + Ok, + }; + + pub const Id = enum { + Var, + Fn, + CompTime, + }; + + pub const Var = struct { + base: Decl, + }; + + pub const Fn = struct { + base: Decl, + value: Val, + fn_proto: *const ast.Node.FnProto, + + // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous + pub const Val = union { + Unresolved: void, + Ok: *Value.Fn, + }; + + pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { + return if (self.fn_proto.extern_export_inline_token) |tok_index| x: { + const token = tree.tokens.at(tok_index); + break :x switch (token.id) { + Token.Id.Extern => tree.tokenSlicePtr(token), + else => null, + }; + } else null; + } + + pub fn isExported(self: Fn, tree: *ast.Tree) bool { + if (self.fn_proto.extern_export_inline_token) |tok_index| { + const token = tree.tokens.at(tok_index); + return token.id == Token.Id.Keyword_export; + } else { + return false; + } + } + }; + + pub const CompTime = struct { + base: Decl, + }; +}; + +pub const Value = struct { + pub const Fn = struct {}; +}; + +pub const Type = struct { + id: Id, + + pub const Id = enum { + Type, + Void, + Bool, + NoReturn, + Int, + Float, + Pointer, + Array, + Struct, + ComptimeFloat, + ComptimeInt, + Undefined, + Null, + Optional, + ErrorUnion, + ErrorSet, + Enum, + Union, + Fn, + Opaque, + Promise, + }; + + pub const Struct = struct { + base: Type, + decls: *Scope.Decls, + }; +}; + +pub const ParsedFile = struct { + tree: ast.Tree, + realpath: []const u8, +}; diff --git a/src/ir.cpp b/src/ir.cpp index 98b1bd85ad..3fc8306339 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13278,7 +13278,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction FnTableEntry *fn_table_entry = fn_ref->value.data.x_bound_fn.fn; IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg; return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, - nullptr, first_arg_ptr, is_comptime, call_instruction->fn_inline); + fn_ref, first_arg_ptr, is_comptime, call_instruction->fn_inline); } else { ir_add_error_node(ira, fn_ref->source_node, buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name))); diff --git a/std/atomic/queue_mpsc.zig b/std/atomic/queue_mpsc.zig index 8030565d7a..bc0a94258b 100644 --- a/std/atomic/queue_mpsc.zig +++ b/std/atomic/queue_mpsc.zig @@ -15,6 +15,8 @@ pub fn QueueMpsc(comptime T: type) type { pub const Node = std.atomic.Stack(T).Node; + /// Not thread-safe. The call to init() must complete before any other functions are called. + /// No deinitialization required. pub fn init() Self { return Self{ .inboxes = []std.atomic.Stack(T){ @@ -26,12 +28,15 @@ pub fn QueueMpsc(comptime T: type) type { }; } + /// Fully thread-safe. put() may be called from any thread at any time. pub fn put(self: *Self, node: *Node) void { const inbox_index = @atomicLoad(usize, &self.inbox_index, AtomicOrder.SeqCst); const inbox = &self.inboxes[inbox_index]; inbox.push(node); } + /// Must be called by only 1 consumer at a time. Every call to get() and isEmpty() must complete before + /// the next call to get(). pub fn get(self: *Self) ?*Node { if (self.outbox.pop()) |node| { return node; @@ -43,6 +48,18 @@ pub fn QueueMpsc(comptime T: type) type { } return self.outbox.pop(); } + + /// Must be called by only 1 consumer at a time. Every call to get() and isEmpty() must complete before + /// the next call to isEmpty(). + pub fn isEmpty(self: *Self) bool { + if (!self.outbox.isEmpty()) return false; + const prev_inbox_index = @atomicRmw(usize, &self.inbox_index, AtomicRmwOp.Xor, 0x1, AtomicOrder.SeqCst); + const prev_inbox = &self.inboxes[prev_inbox_index]; + while (prev_inbox.pop()) |node| { + self.outbox.push(node); + } + return self.outbox.isEmpty(); + } }; } diff --git a/std/debug/index.zig b/std/debug/index.zig index 57b2dfc300..a5e1c313f0 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -11,6 +11,11 @@ const builtin = @import("builtin"); pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; +pub const runtime_safety = switch (builtin.mode) { + builtin.Mode.Debug, builtin.Mode.ReleaseSafe => true, + builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false, +}; + /// Tries to write to stderr, unbuffered, and ignores any error returned. /// Does not append a newline. /// TODO atomic/multithread support @@ -1098,7 +1103,7 @@ fn readILeb128(in_stream: var) !i64 { /// This should only be used in temporary test programs. pub const global_allocator = &global_fixed_allocator.allocator; -var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]); +var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]); var global_allocator_mem: [100 * 1024]u8 = undefined; // TODO make thread safe diff --git a/std/event.zig b/std/event.zig index c6ac04a9d0..2d69d0cb16 100644 --- a/std/event.zig +++ b/std/event.zig @@ -11,53 +11,69 @@ pub const TcpServer = struct { handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void, loop: *Loop, - sockfd: i32, + sockfd: ?i32, accept_coro: ?promise, listen_address: std.net.Address, waiting_for_emfile_node: PromiseNode, + listen_resume_node: event.Loop.ResumeNode, const PromiseNode = std.LinkedList(promise).Node; - pub fn init(loop: *Loop) !TcpServer { - const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); - errdefer std.os.close(sockfd); - + pub fn init(loop: *Loop) TcpServer { // TODO can't initialize handler coroutine here because we need well defined copy elision return TcpServer{ .loop = loop, - .sockfd = sockfd, + .sockfd = null, .accept_coro = null, .handleRequestFn = undefined, .waiting_for_emfile_node = undefined, .listen_address = undefined, + .listen_resume_node = event.Loop.ResumeNode{ + .id = event.Loop.ResumeNode.Id.Basic, + .handle = undefined, + }, }; } - pub fn listen(self: *TcpServer, address: *const std.net.Address, handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void) !void { + pub fn listen( + self: *TcpServer, + address: *const std.net.Address, + handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void, + ) !void { self.handleRequestFn = handleRequestFn; - try std.os.posixBind(self.sockfd, &address.os_addr); - try std.os.posixListen(self.sockfd, posix.SOMAXCONN); - self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd)); + const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); + errdefer std.os.close(sockfd); + self.sockfd = sockfd; + + try std.os.posixBind(sockfd, &address.os_addr); + try std.os.posixListen(sockfd, posix.SOMAXCONN); + self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(sockfd)); self.accept_coro = try async TcpServer.handler(self); errdefer cancel self.accept_coro.?; - try self.loop.addFd(self.sockfd, self.accept_coro.?); - errdefer self.loop.removeFd(self.sockfd); + self.listen_resume_node.handle = self.accept_coro.?; + try self.loop.addFd(sockfd, &self.listen_resume_node); + errdefer self.loop.removeFd(sockfd); + } + + /// Stop listening + pub fn close(self: *TcpServer) void { + self.loop.removeFd(self.sockfd.?); + std.os.close(self.sockfd.?); } pub fn deinit(self: *TcpServer) void { - self.loop.removeFd(self.sockfd); if (self.accept_coro) |accept_coro| cancel accept_coro; - std.os.close(self.sockfd); + if (self.sockfd) |sockfd| std.os.close(sockfd); } pub async fn handler(self: *TcpServer) void { while (true) { var accepted_addr: std.net.Address = undefined; - if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr, posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| { + if (std.os.posixAccept(self.sockfd.?, &accepted_addr.os_addr, posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| { var socket = std.os.File.openHandle(accepted_fd); _ = async self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) { error.OutOfMemory => { @@ -95,32 +111,65 @@ pub const TcpServer = struct { pub const Loop = struct { allocator: *mem.Allocator, - keep_running: bool, next_tick_queue: std.atomic.QueueMpsc(promise), os_data: OsData, + dispatch_lock: u8, // TODO make this a bool + pending_event_count: usize, + extra_threads: []*std.os.Thread, + final_resume_node: ResumeNode, - const OsData = switch (builtin.os) { - builtin.Os.linux => struct { - epollfd: i32, - }, - else => struct {}, + pub const NextTickNode = std.atomic.QueueMpsc(promise).Node; + + pub const ResumeNode = struct { + id: Id, + handle: promise, + + pub const Id = enum { + Basic, + Stop, + EventFd, + }; + + pub const EventFd = struct { + base: ResumeNode, + eventfd: i32, + }; }; - pub const NextTickNode = std.atomic.QueueMpsc(promise).Node; + /// After initialization, call run(). + /// TODO copy elision / named return values so that the threads referencing *Loop + /// have the correct pointer value. + fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void { + return self.initInternal(allocator, 1); + } /// The allocator must be thread-safe because we use it for multiplexing /// coroutines onto kernel threads. - pub fn init(allocator: *mem.Allocator) !Loop { - var self = Loop{ - .keep_running = true, + /// After initialization, call run(). + /// TODO copy elision / named return values so that the threads referencing *Loop + /// have the correct pointer value. + fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { + // TODO check the actual cpu core count + return self.initInternal(allocator, 4); + } + + /// Thread count is the total thread count. The thread pool size will be + /// max(thread_count - 1, 0) + fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void { + self.* = Loop{ + .pending_event_count = 0, .allocator = allocator, .os_data = undefined, .next_tick_queue = std.atomic.QueueMpsc(promise).init(), + .dispatch_lock = 1, // start locked so threads go directly into epoll wait + .extra_threads = undefined, + .final_resume_node = ResumeNode{ + .id = ResumeNode.Id.Stop, + .handle = undefined, + }, }; - try self.initOsData(); + try self.initOsData(thread_count); errdefer self.deinitOsData(); - - return self; } /// must call stop before deinit @@ -128,13 +177,70 @@ pub const Loop = struct { self.deinitOsData(); } - const InitOsDataError = std.os.LinuxEpollCreateError; + const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError || + std.os.SpawnThreadError || std.os.LinuxEpollCtlError; + + const wakeup_bytes = []u8{0x1} ** 8; - fn initOsData(self: *Loop) InitOsDataError!void { + fn initOsData(self: *Loop, thread_count: usize) InitOsDataError!void { switch (builtin.os) { builtin.Os.linux => { - self.os_data.epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC); + const extra_thread_count = thread_count - 1; + self.os_data.available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(); + self.os_data.eventfd_resume_nodes = try self.allocator.alloc( + std.atomic.Stack(ResumeNode.EventFd).Node, + extra_thread_count, + ); + errdefer self.allocator.free(self.os_data.eventfd_resume_nodes); + + errdefer { + while (self.os_data.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); + } + for (self.os_data.eventfd_resume_nodes) |*eventfd_node| { + eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ + .data = ResumeNode.EventFd{ + .base = ResumeNode{ + .id = ResumeNode.Id.EventFd, + .handle = undefined, + }, + .eventfd = try std.os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK), + }, + .next = undefined, + }; + self.os_data.available_eventfd_resume_nodes.push(eventfd_node); + } + + self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC); errdefer std.os.close(self.os_data.epollfd); + + self.os_data.final_eventfd = try std.os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK); + errdefer std.os.close(self.os_data.final_eventfd); + + self.os_data.final_eventfd_event = posix.epoll_event{ + .events = posix.EPOLLIN, + .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) }, + }; + try std.os.linuxEpollCtl( + self.os_data.epollfd, + posix.EPOLL_CTL_ADD, + self.os_data.final_eventfd, + &self.os_data.final_eventfd_event, + ); + self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count); + errdefer self.allocator.free(self.extra_threads); + + var extra_thread_index: usize = 0; + errdefer { + while (extra_thread_index != 0) { + extra_thread_index -= 1; + // writing 8 bytes to an eventfd cannot fail + std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + self.extra_threads[extra_thread_index].wait(); + } + } + while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { + self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); + } }, else => {}, } @@ -142,65 +248,154 @@ pub const Loop = struct { fn deinitOsData(self: *Loop) void { switch (builtin.os) { - builtin.Os.linux => std.os.close(self.os_data.epollfd), + builtin.Os.linux => { + std.os.close(self.os_data.final_eventfd); + while (self.os_data.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); + std.os.close(self.os_data.epollfd); + self.allocator.free(self.os_data.eventfd_resume_nodes); + self.allocator.free(self.extra_threads); + }, else => {}, } } - pub fn addFd(self: *Loop, fd: i32, prom: promise) !void { + /// resume_node must live longer than the promise that it holds a reference to. + pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + errdefer { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + try self.addFdNoCounter(fd, resume_node); + } + + fn addFdNoCounter(self: *Loop, fd: i32, resume_node: *ResumeNode) !void { var ev = std.os.linux.epoll_event{ .events = std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, - .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(prom) }, + .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) }, }; try std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev); } pub fn removeFd(self: *Loop, fd: i32) void { + self.removeFdNoCounter(fd); + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + + fn removeFdNoCounter(self: *Loop, fd: i32) void { std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; } - async fn waitFd(self: *Loop, fd: i32) !void { + + pub async fn waitFd(self: *Loop, fd: i32) !void { defer self.removeFd(fd); + var resume_node = ResumeNode{ + .id = ResumeNode.Id.Basic, + .handle = undefined, + }; suspend |p| { - try self.addFd(fd, p); + resume_node.handle = p; + try self.addFd(fd, &resume_node); } + var a = &resume_node; // TODO better way to explicitly put memory in coro frame } - pub fn stop(self: *Loop) void { - // TODO make atomic - self.keep_running = false; - // TODO activate an fd in the epoll set which should cancel all the promises - } - - /// bring your own linked list node. this means it can't fail. + /// Bring your own linked list node. This means it can't fail. pub fn onNextTick(self: *Loop, node: *NextTickNode) void { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); self.next_tick_queue.put(node); } pub fn run(self: *Loop) void { - while (self.keep_running) { - // TODO multiplex the next tick queue and the epoll event results onto a thread pool - while (self.next_tick_queue.get()) |node| { - resume node.data; - } - if (!self.keep_running) break; - - self.dispatchOsEvents(); + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + self.workerRun(); + for (self.extra_threads) |extra_thread| { + extra_thread.wait(); } } - fn dispatchOsEvents(self: *Loop) void { - switch (builtin.os) { - builtin.Os.linux => { - var events: [16]std.os.linux.epoll_event = undefined; - const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); - for (events[0..count]) |ev| { - const p = @intToPtr(promise, ev.data.ptr); - resume p; + fn workerRun(self: *Loop) void { + start_over: while (true) { + if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { + while (self.next_tick_queue.get()) |next_tick_node| { + const handle = next_tick_node.data; + if (self.next_tick_queue.isEmpty()) { + // last node, just resume it + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + } + + // non-last node, stick it in the epoll set so that + // other threads can get to it + if (self.os_data.available_eventfd_resume_nodes.pop()) |resume_stack_node| { + const eventfd_node = &resume_stack_node.data; + eventfd_node.base.handle = handle; + // the pending count is already accounted for + self.addFdNoCounter(eventfd_node.eventfd, &eventfd_node.base) catch |_| { + // fine, we didn't need it anyway + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + self.os_data.available_eventfd_resume_nodes.push(resume_stack_node); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + }; + } else { + // threads are too busy, can't add another eventfd to wake one up + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + } } - }, - else => {}, + + const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst); + if (pending_event_count == 0) { + // cause all the threads to stop + // writing 8 bytes to an eventfd cannot fail + std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + return; + } + + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + } + + // only process 1 event so we don't steal from other threads + var events: [1]std.os.linux.epoll_event = undefined; + const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); + for (events[0..count]) |ev| { + const resume_node = @intToPtr(*ResumeNode, ev.data.ptr); + const handle = resume_node.handle; + const resume_node_id = resume_node.id; + switch (resume_node_id) { + ResumeNode.Id.Basic => {}, + ResumeNode.Id.Stop => return, + ResumeNode.Id.EventFd => { + const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); + self.removeFdNoCounter(event_fd_node.eventfd); + const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); + self.os_data.available_eventfd_resume_nodes.push(stack_node); + }, + } + resume handle; + if (resume_node_id == ResumeNode.Id.EventFd) { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + } } } + + const OsData = switch (builtin.os) { + builtin.Os.linux => struct { + epollfd: i32, + // pre-allocated eventfds. all permanently active. + // this is how we send promises to be resumed on other threads. + available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd), + eventfd_resume_nodes: []std.atomic.Stack(ResumeNode.EventFd).Node, + final_eventfd: i32, + final_eventfd_event: posix.epoll_event, + }, + else => struct {}, + }; }; /// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size @@ -304,9 +499,7 @@ pub fn Channel(comptime T: type) type { // TODO integrate this function with named return values // so we can get rid of this extra result copy var result: T = undefined; - var debug_handle: usize = undefined; suspend |handle| { - debug_handle = @ptrToInt(handle); var my_tick_node = Loop.NextTickNode{ .next = undefined, .data = handle, @@ -438,9 +631,8 @@ test "listen on a port, send bytes, receive bytes" { const self = @fieldParentPtr(Self, "tcp_server", tcp_server); var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 defer socket.close(); - const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) { - error.OutOfMemory => @panic("unable to handle connection: out of memory"), - }; + // TODO guarantee elision of this allocation + const next_handler = async errorableHandler(self, _addr, socket) catch unreachable; (await next_handler) catch |err| { std.debug.panic("unable to handle connection: {}\n", err); }; @@ -461,17 +653,18 @@ test "listen on a port, send bytes, receive bytes" { const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable; const addr = std.net.Address.initIp4(ip4addr, 0); - var loop = try Loop.init(std.debug.global_allocator); - var server = MyServer{ .tcp_server = try TcpServer.init(&loop) }; + var loop: Loop = undefined; + try loop.initSingleThreaded(std.debug.global_allocator); + var server = MyServer{ .tcp_server = TcpServer.init(&loop) }; defer server.tcp_server.deinit(); try server.tcp_server.listen(addr, MyServer.handler); - const p = try async doAsyncTest(&loop, server.tcp_server.listen_address); + const p = try async doAsyncTest(&loop, server.tcp_server.listen_address, &server.tcp_server); defer cancel p; loop.run(); } -async fn doAsyncTest(loop: *Loop, address: *const std.net.Address) void { +async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *TcpServer) void { errdefer @panic("test failure"); var socket_file = try await try async event.connect(loop, address); @@ -481,7 +674,7 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address) void { const amt_read = try socket_file.read(buf[0..]); const msg = buf[0..amt_read]; assert(mem.eql(u8, msg, "hello from server\n")); - loop.stop(); + server.close(); } test "std.event.Channel" { @@ -490,7 +683,9 @@ test "std.event.Channel" { const allocator = &da.allocator; - var loop = try Loop.init(allocator); + var loop: Loop = undefined; + // TODO make a multi threaded test + try loop.initSingleThreaded(allocator); defer loop.deinit(); const channel = try Channel(i32).create(&loop, 0); @@ -515,11 +710,248 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void { const value2_promise = try async channel.get(); const value2 = await value2_promise; assert(value2 == 4567); - - loop.stop(); } async fn testChannelPutter(channel: *Channel(i32)) void { await (async channel.put(1234) catch @panic("out of memory")); await (async channel.put(4567) catch @panic("out of memory")); } + +/// Thread-safe async/await lock. +/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and +/// are resumed when the lock is released, in order. +pub const Lock = struct { + loop: *Loop, + shared_bit: u8, // TODO make this a bool + queue: Queue, + queue_empty_bit: u8, // TODO make this a bool + + const Queue = std.atomic.QueueMpsc(promise); + + pub const Held = struct { + lock: *Lock, + + pub fn release(self: Held) void { + // Resume the next item from the queue. + if (self.lock.queue.get()) |node| { + self.lock.loop.onNextTick(node); + return; + } + + // We need to release the lock. + _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + // There might be a queue item. If we know the queue is empty, we can be done, + // because the other actor will try to obtain the lock. + // But if there's a queue item, we are the actor which must loop and attempt + // to grab the lock again. + if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) { + return; + } + + while (true) { + const old_bit = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + if (old_bit != 0) { + // We did not obtain the lock. Great, the queue is someone else's problem. + return; + } + + // Resume the next item from the queue. + if (self.lock.queue.get()) |node| { + self.lock.loop.onNextTick(node); + return; + } + + // Release the lock again. + _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + // Find out if we can be done. + if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) { + return; + } + } + } + }; + + pub fn init(loop: *Loop) Lock { + return Lock{ + .loop = loop, + .shared_bit = 0, + .queue = Queue.init(), + .queue_empty_bit = 1, + }; + } + + /// Must be called when not locked. Not thread safe. + /// All calls to acquire() and release() must complete before calling deinit(). + pub fn deinit(self: *Lock) void { + assert(self.shared_bit == 0); + while (self.queue.get()) |node| cancel node.data; + } + + pub async fn acquire(self: *Lock) Held { + var my_tick_node: Loop.NextTickNode = undefined; + + s: suspend |handle| { + my_tick_node.data = handle; + self.queue.put(&my_tick_node); + + // At this point, we are in the queue, so we might have already been resumed and this coroutine + // frame might be destroyed. For the rest of the suspend block we cannot access the coroutine frame. + + // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor + // will attempt to grab the lock. + _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + while (true) { + const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + if (old_bit != 0) { + // We did not obtain the lock. Trust that our queue entry will resume us, and allow + // suspend to complete. + break; + } + // We got the lock. However we might have already been resumed from the queue. + if (self.queue.get()) |node| { + // Whether this node is us or someone else, we tail resume it. + resume node.data; + break; + } else { + // We already got resumed, and there are none left in the queue, which means that + // we aren't even supposed to hold the lock right now. + _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + _ = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + // There might be a queue item. If we know the queue is empty, we can be done, + // because the other actor will try to obtain the lock. + // But if there's a queue item, we are the actor which must loop and attempt + // to grab the lock again. + if (@atomicLoad(u8, &self.queue_empty_bit, AtomicOrder.SeqCst) == 1) { + break; + } else { + continue; + } + } + unreachable; + } + } + + // TODO this workaround to force my_tick_node to be in the coroutine frame should + // not be necessary + var trash1 = &my_tick_node; + + return Held{ .lock = self }; + } +}; + +/// Thread-safe async/await lock that protects one piece of data. +/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and +/// are resumed when the lock is released, in order. +pub fn Locked(comptime T: type) type { + return struct { + lock: Lock, + private_data: T, + + const Self = this; + + pub const HeldLock = struct { + value: *T, + held: Lock.Held, + + pub fn release(self: HeldLock) void { + self.held.release(); + } + }; + + pub fn init(loop: *Loop, data: T) Self { + return Self{ + .lock = Lock.init(loop), + .private_data = data, + }; + } + + pub fn deinit(self: *Self) void { + self.lock.deinit(); + } + + pub async fn acquire(self: *Self) HeldLock { + return HeldLock{ + // TODO guaranteed allocation elision + .held = await (async self.lock.acquire() catch unreachable), + .value = &self.private_data, + }; + } + }; +} + +test "std.event.Lock" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const allocator = &da.allocator; + + var loop: Loop = undefined; + try loop.initMultiThreaded(allocator); + defer loop.deinit(); + + var lock = Lock.init(&loop); + defer lock.deinit(); + + const handle = try async testLock(&loop, &lock); + defer cancel handle; + loop.run(); + + assert(mem.eql(i32, shared_test_data, [1]i32{3 * 10} ** 10)); +} + +async fn testLock(loop: *Loop, lock: *Lock) void { + const handle1 = async lockRunner(lock) catch @panic("out of memory"); + var tick_node1 = Loop.NextTickNode{ + .next = undefined, + .data = handle1, + }; + loop.onNextTick(&tick_node1); + + const handle2 = async lockRunner(lock) catch @panic("out of memory"); + var tick_node2 = Loop.NextTickNode{ + .next = undefined, + .data = handle2, + }; + loop.onNextTick(&tick_node2); + + const handle3 = async lockRunner(lock) catch @panic("out of memory"); + var tick_node3 = Loop.NextTickNode{ + .next = undefined, + .data = handle3, + }; + loop.onNextTick(&tick_node3); + + await handle1; + await handle2; + await handle3; + + // TODO this is to force tick node memory to be in the coro frame + // there should be a way to make it explicit where the memory is + var a = &tick_node1; + var b = &tick_node2; + var c = &tick_node3; +} + +var shared_test_data = [1]i32{0} ** 10; +var shared_test_index: usize = 0; + +async fn lockRunner(lock: *Lock) void { + suspend; // resumed by onNextTick + + var i: usize = 0; + while (i < 10) : (i += 1) { + const handle = await (async lock.acquire() catch @panic("out of memory")); + defer handle.release(); + + shared_test_index = 0; + while (shared_test_index < shared_test_data.len) : (shared_test_index += 1) { + shared_test_data[shared_test_index] = shared_test_data[shared_test_index] + 1; + } + } +} diff --git a/std/heap.zig b/std/heap.zig index 2e02733da1..bcace34afe 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -38,7 +38,7 @@ fn cFree(self: *Allocator, old_mem: []u8) void { } /// This allocator makes a syscall directly for every allocation and free. -/// TODO make this thread-safe. The windows implementation will need some atomics. +/// Thread-safe and lock-free. pub const DirectAllocator = struct { allocator: Allocator, heap_handle: ?HeapHandle, @@ -74,34 +74,34 @@ pub const DirectAllocator = struct { const alloc_size = if (alignment <= os.page_size) n else n + alignment; const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0); if (addr == p.MAP_FAILED) return error.OutOfMemory; - if (alloc_size == n) return @intToPtr([*]u8, addr)[0..n]; - var aligned_addr = addr & ~usize(alignment - 1); - aligned_addr += alignment; + const aligned_addr = (addr & ~usize(alignment - 1)) + alignment; - //We can unmap the unused portions of our mmap, but we must only - // pass munmap bytes that exist outside our allocated pages or it - // will happily eat us too + // We can unmap the unused portions of our mmap, but we must only + // pass munmap bytes that exist outside our allocated pages or it + // will happily eat us too. - //Since alignment > page_size, we are by definition on a page boundry + // Since alignment > page_size, we are by definition on a page boundary. const unused_start = addr; const unused_len = aligned_addr - 1 - unused_start; - var err = p.munmap(unused_start, unused_len); - debug.assert(p.getErrno(err) == 0); + const err = p.munmap(unused_start, unused_len); + assert(p.getErrno(err) == 0); - //It is impossible that there is an unoccupied page at the top of our - // mmap. + // It is impossible that there is an unoccupied page at the top of our + // mmap. return @intToPtr([*]u8, aligned_addr)[0..n]; }, Os.windows => { const amt = n + alignment + @sizeOf(usize); - const heap_handle = self.heap_handle orelse blk: { + const optional_heap_handle = @atomicLoad(?HeapHandle, ?self.heap_handle, builtin.AtomicOrder.SeqCst); + const heap_handle = optional_heap_handle orelse blk: { const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) orelse return error.OutOfMemory; - self.heap_handle = hh; - break :blk hh; + const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh; + _ = os.windows.HeapDestroy(hh); + break :blk other_hh; }; const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory; const root_addr = @ptrToInt(ptr); diff --git a/std/mem.zig b/std/mem.zig index b52d3e9f68..555e1e249d 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -6,7 +6,7 @@ const builtin = @import("builtin"); const mem = this; pub const Allocator = struct { - const Error = error{OutOfMemory}; + pub const Error = error{OutOfMemory}; /// Allocate byte_count bytes and return them in a slice, with the /// slice's pointer aligned at least to alignment bytes. diff --git a/std/os/index.zig b/std/os/index.zig index 52b36c351c..74a1b64f6e 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2309,6 +2309,30 @@ pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usiz } } +pub const LinuxEventFdError = error{ + InvalidFlagValue, + SystemResources, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + + Unexpected, +}; + +pub fn linuxEventFd(initval: u32, flags: u32) LinuxEventFdError!i32 { + const rc = posix.eventfd(initval, flags); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(i32, rc), + else => return unexpectedErrorPosix(err), + + posix.EINVAL => return LinuxEventFdError.InvalidFlagValue, + posix.EMFILE => return LinuxEventFdError.ProcessFdQuotaExceeded, + posix.ENFILE => return LinuxEventFdError.SystemFdQuotaExceeded, + posix.ENODEV => return LinuxEventFdError.SystemResources, + posix.ENOMEM => return LinuxEventFdError.SystemResources, + } +} + pub const PosixGetSockNameError = error{ /// Insufficient resources were available in the system to perform the operation. SystemResources, @@ -2605,10 +2629,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread const MainFuncs = struct { extern fn linuxThreadMain(ctx_addr: usize) u8 { - if (@sizeOf(Context) == 0) { - return startFn({}); - } else { - return startFn(@intToPtr(*const Context, ctx_addr).*); + const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*; + + switch (@typeId(@typeOf(startFn).ReturnType)) { + builtin.TypeId.Int => { + return startFn(arg); + }, + builtin.TypeId.Void => { + startFn(arg); + return 0; + }, + else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), } } extern fn posixThreadMain(ctx: ?*c_void) ?*c_void { diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 65aa659c82..1c15be4887 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -523,6 +523,10 @@ pub const CLONE_NEWPID = 0x20000000; pub const CLONE_NEWNET = 0x40000000; pub const CLONE_IO = 0x80000000; +pub const EFD_SEMAPHORE = 1; +pub const EFD_CLOEXEC = O_CLOEXEC; +pub const EFD_NONBLOCK = O_NONBLOCK; + pub const MS_RDONLY = 1; pub const MS_NOSUID = 2; pub const MS_NODEV = 4; @@ -1221,6 +1225,10 @@ pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout return syscall4(SYS_epoll_wait, @intCast(usize, epoll_fd), @ptrToInt(events), @intCast(usize, maxevents), @intCast(usize, timeout)); } +pub fn eventfd(count: u32, flags: u32) usize { + return syscall2(SYS_eventfd2, count, flags); +} + pub fn timerfd_create(clockid: i32, flags: u32) usize { return syscall2(SYS_timerfd_create, @intCast(usize, clockid), @intCast(usize, flags)); } -- cgit v1.2.3 From 57f36c420124b3b65d3036f10c4e8c675be29cf4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 Jul 2018 18:16:46 -0400 Subject: std.event.Loop: use EPOLLONESHOT to save 1 syscall when a thread pool worker accepts a coroutine to resume --- std/event.zig | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/std/event.zig b/std/event.zig index 2d69d0cb16..5fd87b8fdd 100644 --- a/std/event.zig +++ b/std/event.zig @@ -132,6 +132,7 @@ pub const Loop = struct { pub const EventFd = struct { base: ResumeNode, + epoll_op: u32, eventfd: i32, }; }; @@ -204,6 +205,7 @@ pub const Loop = struct { .handle = undefined, }, .eventfd = try std.os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK), + .epoll_op = posix.EPOLL_CTL_ADD, }, .next = undefined, }; @@ -265,15 +267,20 @@ pub const Loop = struct { errdefer { _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); } - try self.addFdNoCounter(fd, resume_node); + try self.modFd( + fd, + posix.EPOLL_CTL_ADD, + std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, + resume_node, + ); } - fn addFdNoCounter(self: *Loop, fd: i32, resume_node: *ResumeNode) !void { + pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void { var ev = std.os.linux.epoll_event{ - .events = std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, + .events = events, .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) }, }; - try std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev); + try std.os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev); } pub fn removeFd(self: *Loop, fd: i32) void { @@ -331,7 +338,8 @@ pub const Loop = struct { const eventfd_node = &resume_stack_node.data; eventfd_node.base.handle = handle; // the pending count is already accounted for - self.addFdNoCounter(eventfd_node.eventfd, &eventfd_node.base) catch |_| { + const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET; + self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch |_| { // fine, we didn't need it anyway _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); self.os_data.available_eventfd_resume_nodes.push(resume_stack_node); @@ -371,7 +379,7 @@ pub const Loop = struct { ResumeNode.Id.Stop => return, ResumeNode.Id.EventFd => { const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); - self.removeFdNoCounter(event_fd_node.eventfd); + event_fd_node.epoll_op = posix.EPOLL_CTL_MOD; const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); self.os_data.available_eventfd_resume_nodes.push(stack_node); }, @@ -902,7 +910,7 @@ test "std.event.Lock" { defer cancel handle; loop.run(); - assert(mem.eql(i32, shared_test_data, [1]i32{3 * 10} ** 10)); + assert(mem.eql(i32, shared_test_data, [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len)); } async fn testLock(loop: *Loop, lock: *Lock) void { @@ -945,7 +953,7 @@ async fn lockRunner(lock: *Lock) void { suspend; // resumed by onNextTick var i: usize = 0; - while (i < 10) : (i += 1) { + while (i < shared_test_data.len) : (i += 1) { const handle = await (async lock.acquire() catch @panic("out of memory")); defer handle.release(); -- cgit v1.2.3 From c15a6fa9d0e11398f65e8ecc1903e07f4c57add6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 7 Jul 2018 01:23:18 -0400 Subject: add std.os.cpuCount and have std.event.Loop use it for thread pool size --- std/event.zig | 4 +-- std/heap.zig | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ std/os/index.zig | 39 +++++++++++++++++++++++++++++ std/os/linux/index.zig | 4 +++ std/os/test.zig | 5 ++++ 5 files changed, 117 insertions(+), 2 deletions(-) diff --git a/std/event.zig b/std/event.zig index 5fd87b8fdd..f0c45f61bc 100644 --- a/std/event.zig +++ b/std/event.zig @@ -150,8 +150,8 @@ pub const Loop = struct { /// TODO copy elision / named return values so that the threads referencing *Loop /// have the correct pointer value. fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { - // TODO check the actual cpu core count - return self.initInternal(allocator, 4); + const core_count = try std.os.cpuCount(allocator); + return self.initInternal(allocator, core_count); } /// Thread count is the total thread count. The thread pool size will be diff --git a/std/heap.zig b/std/heap.zig index bcace34afe..6d3fd05cdb 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -361,6 +361,73 @@ pub const ThreadSafeFixedBufferAllocator = struct { fn free(allocator: *Allocator, bytes: []u8) void {} }; +pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) StackFallbackAllocator(size) { + return StackFallbackAllocator(size){ + .buffer = undefined, + .fallback_allocator = fallback_allocator, + .fixed_buffer_allocator = undefined, + .allocator = Allocator{ + .allocFn = StackFallbackAllocator(size).alloc, + .reallocFn = StackFallbackAllocator(size).realloc, + .freeFn = StackFallbackAllocator(size).free, + }, + }; +} + +pub fn StackFallbackAllocator(comptime size: usize) type { + return struct { + const Self = this; + + buffer: [size]u8, + allocator: Allocator, + fallback_allocator: *Allocator, + fixed_buffer_allocator: FixedBufferAllocator, + + pub fn get(self: *Self) *Allocator { + self.fixed_buffer_allocator = FixedBufferAllocator.init(self.buffer[0..]); + return &self.allocator; + } + + fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { + const self = @fieldParentPtr(Self, "allocator", allocator); + return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator.allocator, n, alignment) catch + self.fallback_allocator.allocFn(self.fallback_allocator, n, alignment); + } + + fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + const self = @fieldParentPtr(Self, "allocator", allocator); + const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and + @ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len; + if (in_buffer) { + return FixedBufferAllocator.realloc( + &self.fixed_buffer_allocator.allocator, + old_mem, + new_size, + alignment, + ) catch { + const result = try self.fallback_allocator.allocFn( + self.fallback_allocator, + new_size, + alignment, + ); + mem.copy(u8, result, old_mem); + return result; + }; + } + return self.fallback_allocator.reallocFn(self.fallback_allocator, old_mem, new_size, alignment); + } + + fn free(allocator: *Allocator, bytes: []u8) void { + const self = @fieldParentPtr(Self, "allocator", allocator); + const in_buffer = @ptrToInt(bytes.ptr) >= @ptrToInt(&self.buffer) and + @ptrToInt(bytes.ptr) < @ptrToInt(&self.buffer) + self.buffer.len; + if (!in_buffer) { + return self.fallback_allocator.freeFn(self.fallback_allocator, bytes); + } + } + }; +} + test "c_allocator" { if (builtin.link_libc) { var slice = c_allocator.alloc(u8, 50) catch return; diff --git a/std/os/index.zig b/std/os/index.zig index 74a1b64f6e..c36aae91da 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2748,3 +2748,42 @@ pub fn posixFStat(fd: i32) !posix.Stat { return stat; } + +pub const CpuCountError = error{ + OutOfMemory, + PermissionDenied, + Unexpected, +}; + +pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { + const usize_count = 16; + const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get(); + + var set = try allocator.alloc(usize, usize_count); + defer allocator.free(set); + + while (true) { + const rc = posix.sched_getaffinity(0, set); + const err = posix.getErrno(rc); + switch (err) { + 0 => { + if (rc < set.len * @sizeOf(usize)) { + const result = set[0 .. rc / @sizeOf(usize)]; + var sum: usize = 0; + for (result) |x| { + sum += @popCount(x); + } + return sum; + } else { + set = try allocator.realloc(usize, set, set.len * 2); + continue; + } + }, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.EPERM => return CpuCountError.PermissionDenied, + posix.ESRCH => unreachable, + else => return os.unexpectedErrorPosix(err), + } + } +} diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 1c15be4887..69bc30bad0 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -1197,6 +1197,10 @@ pub fn fremovexattr(fd: usize, name: [*]const u8) usize { return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); } +pub fn sched_getaffinity(pid: i32, set: []usize) usize { + return syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), set.len * @sizeOf(usize), @ptrToInt(set.ptr)); +} + pub const epoll_data = packed union { ptr: usize, fd: i32, diff --git a/std/os/test.zig b/std/os/test.zig index 5a977a569a..52e6ffdc1c 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -58,3 +58,8 @@ fn start2(ctx: *i32) u8 { _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); return 0; } + +test "cpu count" { + const cpu_count = try std.os.cpuCount(a); + assert(cpu_count >= 1); +} -- cgit v1.2.3 From ced3aae3b2371479c01b4abba42c751697185d7b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 7 Jul 2018 20:31:50 -0400 Subject: cleaner output from zig build when there are compile errors --- std/debug/index.zig | 62 +++++++++++++++++++++++++++++--------------- std/special/build_runner.zig | 9 ++++--- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/std/debug/index.zig b/std/debug/index.zig index 57b2dfc300..0e2a3a8d39 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -156,7 +156,7 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; }) { const return_address = stack_trace.instruction_addresses[frame_index]; - try printSourceAtAddress(debug_info, out_stream, return_address); + try printSourceAtAddress(debug_info, out_stream, return_address, tty_color); } } @@ -189,13 +189,11 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_ } }, } - try printSourceAtAddress(debug_info, out_stream, return_address); + try printSourceAtAddress(debug_info, out_stream, return_address, tty_color); } } -fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize) !void { - const ptr_hex = "0x{x}"; - +fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void { switch (builtin.os) { builtin.Os.windows => return error.UnsupportedDebugInfo, builtin.Os.macosx => { @@ -209,36 +207,58 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us .address = address, }; const symbol = debug_info.symbol_table.search(address) orelse &unknown; - try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address); + try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ "0x{x}" ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address); }, else => { const compile_unit = findCompileUnit(debug_info, address) catch { - try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", address); + if (tty_color) { + try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n ???\n\n", address); + } else { + try out_stream.print("???:?:?: 0x{x} in ??? (???)\n ???\n\n", address); + } return; }; const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| { defer line_info.deinit(); - try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", line_info.file_name, line_info.line, line_info.column, address, compile_unit_name); - if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { - if (line_info.column == 0) { - try out_stream.write("\n"); - } else { - { - var col_i: usize = 1; - while (col_i < line_info.column) : (col_i += 1) { - try out_stream.writeByte(' '); + if (tty_color) { + try out_stream.print( + WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n", + line_info.file_name, + line_info.line, + line_info.column, + address, + compile_unit_name, + ); + if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { + if (line_info.column == 0) { + try out_stream.write("\n"); + } else { + { + var col_i: usize = 1; + while (col_i < line_info.column) : (col_i += 1) { + try out_stream.writeByte(' '); + } } + try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); } - try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); + } else |err| switch (err) { + error.EndOfFile => {}, + else => return err, } - } else |err| switch (err) { - error.EndOfFile => {}, - else => return err, + } else { + try out_stream.print( + "{}:{}:{}: 0x{x} in ??? ({})\n", + line_info.file_name, + line_info.line, + line_info.column, + address, + compile_unit_name, + ); } } else |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { - try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name); + try out_stream.print("0x{x} in ??? ({})\n", address, compile_unit_name); }, else => return err, } diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index e4f04df6d0..2f073b3e98 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -122,10 +122,13 @@ pub fn main() !void { return usageAndErr(&builder, true, try stderr_stream); builder.make(targets.toSliceConst()) catch |err| { - if (err == error.InvalidStepName) { - return usageAndErr(&builder, true, try stderr_stream); + switch (err) { + error.InvalidStepName => { + return usageAndErr(&builder, true, try stderr_stream); + }, + error.UncleanExit => os.exit(1), + else => return err, } - return err; }; } -- cgit v1.2.3 From 410b4d9bdf8abb8dad2ac2e11038fe492b8be869 Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Sun, 8 Jul 2018 00:00:05 -0400 Subject: builder.addBuildOption --- std/build.zig | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/std/build.zig b/std/build.zig index 99de9b5197..24fa85383a 100644 --- a/std/build.zig +++ b/std/build.zig @@ -814,6 +814,7 @@ pub const LibExeObjStep = struct { out_h_filename: []const u8, assembly_files: ArrayList([]const u8), packages: ArrayList(Pkg), + build_options_contents: std.Buffer, // C only stuff source_files: ArrayList([]const u8), @@ -905,6 +906,7 @@ pub const LibExeObjStep = struct { .lib_paths = ArrayList([]const u8).init(builder.allocator), .object_src = undefined, .disable_libc = true, + .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, }; self.computeOutFileNames(); return self; @@ -945,6 +947,7 @@ pub const LibExeObjStep = struct { .out_h_filename = undefined, .assembly_files = undefined, .packages = undefined, + .build_options_contents = undefined, }; self.computeOutFileNames(); return self; @@ -1096,6 +1099,12 @@ pub const LibExeObjStep = struct { self.include_dirs.append(self.builder.cache_root) catch unreachable; } + pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void { + assert(self.is_zig); + const out = &std.io.BufferOutStream.init(&self.build_options_contents).stream; + out.print("pub const {} = {};\n", name, value) catch unreachable; + } + pub fn addIncludeDir(self: *LibExeObjStep, path: []const u8) void { self.include_dirs.append(path) catch unreachable; } @@ -1155,6 +1164,15 @@ pub const LibExeObjStep = struct { zig_args.append(builder.pathFromRoot(root_src)) catch unreachable; } + if (self.build_options_contents.len() > 0) { + const build_options_file = try os.path.join(builder.allocator, builder.cache_root, builder.fmt("{}_build_options.zig", self.name)); + try std.io.writeFile(builder.allocator, build_options_file, self.build_options_contents.toSliceConst()); + try zig_args.append("--pkg-begin"); + try zig_args.append("build_options"); + try zig_args.append(builder.pathFromRoot(build_options_file)); + try zig_args.append("--pkg-end"); + } + for (self.object_files.toSliceConst()) |object_file| { zig_args.append("--object") catch unreachable; zig_args.append(builder.pathFromRoot(object_file)) catch unreachable; -- cgit v1.2.3 From 50d70d5f498470790f6d58b5e3018e0d89c2c9f3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 8 Jul 2018 02:43:30 -0400 Subject: tests passing with kqueue on macos --- std/c/darwin.zig | 56 +++++++++++ std/event.zig | 277 +++++++++++++++++++++++++++++++++++++++++------------- std/os/darwin.zig | 131 ++++++++++++++++++++++++++ std/os/index.zig | 56 +++++++++++ 4 files changed, 454 insertions(+), 66 deletions(-) diff --git a/std/c/darwin.zig b/std/c/darwin.zig index e3b53d9bea..cbaa2f6811 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -6,6 +6,13 @@ pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, b pub extern "c" fn mach_absolute_time() u64; pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void; +pub extern "c" fn kqueue() c_int; +pub extern "c" fn kevent(kq: c_int, changelist: [*]const Kevent, nchanges: c_int, + eventlist: [*]Kevent, nevents: c_int, timeout: ?*const timespec) c_int; + +pub extern "c" fn kevent64(kq: c_int, changelist: [*]const kevent64_s, nchanges: c_int, + eventlist: [*]kevent64_s, nevents: c_int, flags: c_uint, timeout: ?*const timespec) c_int; + pub use @import("../os/darwin_errno.zig"); pub const _errno = __error; @@ -86,3 +93,52 @@ pub const pthread_attr_t = extern struct { __sig: c_long, __opaque: [56]u8, }; + +/// Renamed from `kevent` to `Kevent` to avoid conflict with function name. +pub const Kevent = extern struct { + ident: usize, + filter: i16, + flags: u16, + fflags: u32, + data: isize, + udata: usize, +}; + +// sys/types.h on macos uses #pragma pack(4) so these checks are +// to make sure the struct is laid out the same. These values were +// produced from C code using the offsetof macro. +const std = @import("../index.zig"); +const assert = std.debug.assert; + +comptime { + assert(@offsetOf(Kevent, "ident") == 0); + assert(@offsetOf(Kevent, "filter") == 8); + assert(@offsetOf(Kevent, "flags") == 10); + assert(@offsetOf(Kevent, "fflags") == 12); + assert(@offsetOf(Kevent, "data") == 16); + assert(@offsetOf(Kevent, "udata") == 24); +} + +pub const kevent64_s = extern struct { + ident: u64, + filter: i16, + flags: u16, + fflags: u32, + data: i64, + udata: u64, + ext: [2]u64, +}; + +// sys/types.h on macos uses #pragma pack() so these checks are +// to make sure the struct is laid out the same. These values were +// produced from C code using the offsetof macro. +comptime { + assert(@offsetOf(kevent64_s, "ident") == 0); + assert(@offsetOf(kevent64_s, "filter") == 8); + assert(@offsetOf(kevent64_s, "flags") == 10); + assert(@offsetOf(kevent64_s, "fflags") == 12); + assert(@offsetOf(kevent64_s, "data") == 16); + assert(@offsetOf(kevent64_s, "udata") == 24); + assert(@offsetOf(kevent64_s, "ext") == 32); +} + diff --git a/std/event.zig b/std/event.zig index f0c45f61bc..12aa2a3fc7 100644 --- a/std/event.zig +++ b/std/event.zig @@ -118,6 +118,11 @@ pub const Loop = struct { extra_threads: []*std.os.Thread, final_resume_node: ResumeNode, + // pre-allocated eventfds. all permanently active. + // this is how we send promises to be resumed on other threads. + available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd), + eventfd_resume_nodes: []std.atomic.Stack(ResumeNode.EventFd).Node, + pub const NextTickNode = std.atomic.QueueMpsc(promise).Node; pub const ResumeNode = struct { @@ -130,10 +135,17 @@ pub const Loop = struct { EventFd, }; - pub const EventFd = struct { - base: ResumeNode, - epoll_op: u32, - eventfd: i32, + pub const EventFd = switch (builtin.os) { + builtin.Os.macosx => struct { + base: ResumeNode, + kevent: posix.Kevent, + }, + builtin.Os.linux => struct { + base: ResumeNode, + epoll_op: u32, + eventfd: i32, + }, + else => @compileError("unsupported OS"), }; }; @@ -168,36 +180,41 @@ pub const Loop = struct { .id = ResumeNode.Id.Stop, .handle = undefined, }, + .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(), + .eventfd_resume_nodes = undefined, }; - try self.initOsData(thread_count); + const extra_thread_count = thread_count - 1; + self.eventfd_resume_nodes = try self.allocator.alloc( + std.atomic.Stack(ResumeNode.EventFd).Node, + extra_thread_count, + ); + errdefer self.allocator.free(self.eventfd_resume_nodes); + + self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count); + errdefer self.allocator.free(self.extra_threads); + + try self.initOsData(extra_thread_count); errdefer self.deinitOsData(); } /// must call stop before deinit pub fn deinit(self: *Loop) void { self.deinitOsData(); + self.allocator.free(self.extra_threads); } const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError || - std.os.SpawnThreadError || std.os.LinuxEpollCtlError; + std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError; const wakeup_bytes = []u8{0x1} ** 8; - fn initOsData(self: *Loop, thread_count: usize) InitOsDataError!void { + fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { switch (builtin.os) { builtin.Os.linux => { - const extra_thread_count = thread_count - 1; - self.os_data.available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(); - self.os_data.eventfd_resume_nodes = try self.allocator.alloc( - std.atomic.Stack(ResumeNode.EventFd).Node, - extra_thread_count, - ); - errdefer self.allocator.free(self.os_data.eventfd_resume_nodes); - errdefer { - while (self.os_data.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); + while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); } - for (self.os_data.eventfd_resume_nodes) |*eventfd_node| { + for (self.eventfd_resume_nodes) |*eventfd_node| { eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ .data = ResumeNode.EventFd{ .base = ResumeNode{ @@ -209,7 +226,7 @@ pub const Loop = struct { }, .next = undefined, }; - self.os_data.available_eventfd_resume_nodes.push(eventfd_node); + self.available_eventfd_resume_nodes.push(eventfd_node); } self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC); @@ -228,15 +245,84 @@ pub const Loop = struct { self.os_data.final_eventfd, &self.os_data.final_eventfd_event, ); - self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count); - errdefer self.allocator.free(self.extra_threads); var extra_thread_index: usize = 0; errdefer { + // writing 8 bytes to an eventfd cannot fail + std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + while (extra_thread_index != 0) { + extra_thread_index -= 1; + self.extra_threads[extra_thread_index].wait(); + } + } + while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { + self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); + } + }, + builtin.Os.macosx => { + self.os_data.kqfd = try std.os.bsdKQueue(); + errdefer std.os.close(self.os_data.kqfd); + + self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count); + errdefer self.allocator.free(self.os_data.kevents); + + const eventlist = ([*]posix.Kevent)(undefined)[0..0]; + + for (self.eventfd_resume_nodes) |*eventfd_node, i| { + eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ + .data = ResumeNode.EventFd{ + .base = ResumeNode{ + .id = ResumeNode.Id.EventFd, + .handle = undefined, + }, + // this one is for sending events + .kevent = posix.Kevent { + .ident = i, + .filter = posix.EVFILT_USER, + .flags = posix.EV_CLEAR|posix.EV_ADD|posix.EV_DISABLE, + .fflags = 0, + .data = 0, + .udata = @ptrToInt(&eventfd_node.data.base), + }, + }, + .next = undefined, + }; + self.available_eventfd_resume_nodes.push(eventfd_node); + const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent); + _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null); + eventfd_node.data.kevent.flags = posix.EV_CLEAR|posix.EV_ENABLE; + eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER; + // this one is for waiting for events + self.os_data.kevents[i] = posix.Kevent { + .ident = i, + .filter = posix.EVFILT_USER, + .flags = 0, + .fflags = 0, + .data = 0, + .udata = @ptrToInt(&eventfd_node.data.base), + }; + } + + // Pre-add so that we cannot get error.SystemResources + // later when we try to activate it. + self.os_data.final_kevent = posix.Kevent{ + .ident = extra_thread_count, + .filter = posix.EVFILT_USER, + .flags = posix.EV_ADD | posix.EV_DISABLE, + .fflags = 0, + .data = 0, + .udata = @ptrToInt(&self.final_resume_node), + }; + const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent); + _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null); + self.os_data.final_kevent.flags = posix.EV_ENABLE; + self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER; + + var extra_thread_index: usize = 0; + errdefer { + _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch unreachable; while (extra_thread_index != 0) { extra_thread_index -= 1; - // writing 8 bytes to an eventfd cannot fail - std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; self.extra_threads[extra_thread_index].wait(); } } @@ -252,10 +338,12 @@ pub const Loop = struct { switch (builtin.os) { builtin.Os.linux => { std.os.close(self.os_data.final_eventfd); - while (self.os_data.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); + while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); std.os.close(self.os_data.epollfd); - self.allocator.free(self.os_data.eventfd_resume_nodes); - self.allocator.free(self.extra_threads); + self.allocator.free(self.eventfd_resume_nodes); + }, + builtin.Os.macosx => { + self.allocator.free(self.os_data.kevents); }, else => {}, } @@ -332,21 +420,38 @@ pub const Loop = struct { continue :start_over; } - // non-last node, stick it in the epoll set so that + // non-last node, stick it in the epoll/kqueue set so that // other threads can get to it - if (self.os_data.available_eventfd_resume_nodes.pop()) |resume_stack_node| { + if (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| { const eventfd_node = &resume_stack_node.data; eventfd_node.base.handle = handle; - // the pending count is already accounted for - const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET; - self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch |_| { - // fine, we didn't need it anyway - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - self.os_data.available_eventfd_resume_nodes.push(resume_stack_node); - resume handle; - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - continue :start_over; - }; + switch (builtin.os) { + builtin.Os.macosx => { + const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); + const eventlist = ([*]posix.Kevent)(undefined)[0..0]; + _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch |_| { + // fine, we didn't need it anyway + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + self.available_eventfd_resume_nodes.push(resume_stack_node); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + }; + }, + builtin.Os.linux => { + // the pending count is already accounted for + const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET; + self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch |_| { + // fine, we didn't need it anyway + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + self.available_eventfd_resume_nodes.push(resume_stack_node); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + }; + }, + else => @compileError("unsupported OS"), + } } else { // threads are too busy, can't add another eventfd to wake one up _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); @@ -359,35 +464,74 @@ pub const Loop = struct { const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst); if (pending_event_count == 0) { // cause all the threads to stop - // writing 8 bytes to an eventfd cannot fail - std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; - return; + switch (builtin.os) { + builtin.Os.linux => { + // writing 8 bytes to an eventfd cannot fail + std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + return; + }, + builtin.Os.macosx => { + const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent); + const eventlist = ([*]posix.Kevent)(undefined)[0..0]; + // cannot fail because we already added it and this just enables it + _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable; + return; + }, + else => @compileError("unsupported OS"), + } } _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); } - // only process 1 event so we don't steal from other threads - var events: [1]std.os.linux.epoll_event = undefined; - const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); - for (events[0..count]) |ev| { - const resume_node = @intToPtr(*ResumeNode, ev.data.ptr); - const handle = resume_node.handle; - const resume_node_id = resume_node.id; - switch (resume_node_id) { - ResumeNode.Id.Basic => {}, - ResumeNode.Id.Stop => return, - ResumeNode.Id.EventFd => { - const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); - event_fd_node.epoll_op = posix.EPOLL_CTL_MOD; - const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); - self.os_data.available_eventfd_resume_nodes.push(stack_node); - }, - } - resume handle; - if (resume_node_id == ResumeNode.Id.EventFd) { - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - } + switch (builtin.os) { + builtin.Os.linux => { + // only process 1 event so we don't steal from other threads + var events: [1]std.os.linux.epoll_event = undefined; + const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); + for (events[0..count]) |ev| { + const resume_node = @intToPtr(*ResumeNode, ev.data.ptr); + const handle = resume_node.handle; + const resume_node_id = resume_node.id; + switch (resume_node_id) { + ResumeNode.Id.Basic => {}, + ResumeNode.Id.Stop => return, + ResumeNode.Id.EventFd => { + const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); + event_fd_node.epoll_op = posix.EPOLL_CTL_MOD; + const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); + self.available_eventfd_resume_nodes.push(stack_node); + }, + } + resume handle; + if (resume_node_id == ResumeNode.Id.EventFd) { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + } + }, + builtin.Os.macosx => { + var eventlist: [1]posix.Kevent = undefined; + const count = std.os.bsdKEvent(self.os_data.kqfd, self.os_data.kevents, eventlist[0..], null) catch unreachable; + for (eventlist[0..count]) |ev| { + const resume_node = @intToPtr(*ResumeNode, ev.udata); + const handle = resume_node.handle; + const resume_node_id = resume_node.id; + switch (resume_node_id) { + ResumeNode.Id.Basic => {}, + ResumeNode.Id.Stop => return, + ResumeNode.Id.EventFd => { + const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); + const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); + self.available_eventfd_resume_nodes.push(stack_node); + }, + } + resume handle; + if (resume_node_id == ResumeNode.Id.EventFd) { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + } + }, + else => @compileError("unsupported OS"), } } } @@ -395,12 +539,13 @@ pub const Loop = struct { const OsData = switch (builtin.os) { builtin.Os.linux => struct { epollfd: i32, - // pre-allocated eventfds. all permanently active. - // this is how we send promises to be resumed on other threads. - available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd), - eventfd_resume_nodes: []std.atomic.Stack(ResumeNode.EventFd).Node, final_eventfd: i32, - final_eventfd_event: posix.epoll_event, + final_eventfd_event: std.os.linux.epoll_event, + }, + builtin.Os.macosx => struct { + kqfd: i32, + final_kevent: posix.Kevent, + kevents: []posix.Kevent, }, else => struct {}, }; diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 15e5608343..7921d1b6f0 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -264,6 +264,119 @@ pub const SIGUSR1 = 30; /// user defined signal 2 pub const SIGUSR2 = 31; +pub const KEVENT_FLAG_NONE = 0x000; /// no flag value +pub const KEVENT_FLAG_IMMEDIATE = 0x001; /// immediate timeout +pub const KEVENT_FLAG_ERROR_EVENTS = 0x002; /// output events only include change + +pub const EV_ADD = 0x0001; /// add event to kq (implies enable) +pub const EV_DELETE = 0x0002; /// delete event from kq +pub const EV_ENABLE = 0x0004; /// enable event +pub const EV_DISABLE = 0x0008; /// disable event (not reported) + +pub const EV_ONESHOT = 0x0010; /// only report one occurrence +pub const EV_CLEAR = 0x0020; /// clear event state after reporting + +/// force immediate event output +/// ... with or without EV_ERROR +/// ... use KEVENT_FLAG_ERROR_EVENTS +/// on syscalls supporting flags +pub const EV_RECEIPT = 0x0040; + +pub const EV_DISPATCH = 0x0080; /// disable event after reporting +pub const EV_UDATA_SPECIFIC = 0x0100; /// unique kevent per udata value + +/// ... in combination with EV_DELETE +/// will defer delete until udata-specific +/// event enabled. EINPROGRESS will be +/// returned to indicate the deferral +pub const EV_DISPATCH2 = EV_DISPATCH | EV_UDATA_SPECIFIC; + +/// report that source has vanished +/// ... only valid with EV_DISPATCH2 +pub const EV_VANISHED = 0x0200; + +pub const EV_SYSFLAGS = 0xF000; /// reserved by system +pub const EV_FLAG0 = 0x1000; /// filter-specific flag +pub const EV_FLAG1 = 0x2000; /// filter-specific flag +pub const EV_EOF = 0x8000; /// EOF detected +pub const EV_ERROR = 0x4000; /// error, data contains errno + +pub const EV_POLL = EV_FLAG0; +pub const EV_OOBAND = EV_FLAG1; + +pub const EVFILT_READ = -1; +pub const EVFILT_WRITE = -2; +pub const EVFILT_AIO = -3; /// attached to aio requests +pub const EVFILT_VNODE = -4; /// attached to vnodes +pub const EVFILT_PROC = -5; /// attached to struct proc +pub const EVFILT_SIGNAL = -6; /// attached to struct proc +pub const EVFILT_TIMER = -7; /// timers +pub const EVFILT_MACHPORT = -8; /// Mach portsets +pub const EVFILT_FS = -9; /// Filesystem events +pub const EVFILT_USER = -10; /// User events +pub const EVFILT_VM = -12; /// Virtual memory events + +pub const EVFILT_EXCEPT = -15; /// Exception events + +pub const EVFILT_SYSCOUNT = 17; + +/// On input, NOTE_TRIGGER causes the event to be triggered for output. +pub const NOTE_TRIGGER = 0x01000000; + +pub const NOTE_FFNOP = 0x00000000; /// ignore input fflags +pub const NOTE_FFAND = 0x40000000; /// and fflags +pub const NOTE_FFOR = 0x80000000; /// or fflags +pub const NOTE_FFCOPY = 0xc0000000; /// copy fflags +pub const NOTE_FFCTRLMASK = 0xc0000000; /// mask for operations +pub const NOTE_FFLAGSMASK = 0x00ffffff; + +pub const NOTE_LOWAT = 0x00000001; /// low water mark + +pub const NOTE_OOB = 0x00000002; /// OOB data + +pub const NOTE_DELETE = 0x00000001; /// vnode was removed +pub const NOTE_WRITE = 0x00000002; /// data contents changed +pub const NOTE_EXTEND = 0x00000004; /// size increased +pub const NOTE_ATTRIB = 0x00000008; /// attributes changed +pub const NOTE_LINK = 0x00000010; /// link count changed +pub const NOTE_RENAME = 0x00000020; /// vnode was renamed +pub const NOTE_REVOKE = 0x00000040; /// vnode access was revoked +pub const NOTE_NONE = 0x00000080; /// No specific vnode event: to test for EVFILT_READ activation +pub const NOTE_FUNLOCK = 0x00000100; /// vnode was unlocked by flock(2) + +pub const NOTE_EXIT = 0x80000000; /// process exited +pub const NOTE_FORK = 0x40000000; /// process forked +pub const NOTE_EXEC = 0x20000000; /// process exec'd +pub const NOTE_SIGNAL = 0x08000000; /// shared with EVFILT_SIGNAL +pub const NOTE_EXITSTATUS = 0x04000000; /// exit status to be returned, valid for child process only +pub const NOTE_EXIT_DETAIL = 0x02000000; /// provide details on reasons for exit + +pub const NOTE_PDATAMASK = 0x000fffff; /// mask for signal & exit status +pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); + +pub const NOTE_EXIT_DETAIL_MASK = 0x00070000; +pub const NOTE_EXIT_DECRYPTFAIL = 0x00010000; +pub const NOTE_EXIT_MEMORY = 0x00020000; +pub const NOTE_EXIT_CSERROR = 0x00040000; + + +pub const NOTE_VM_PRESSURE = 0x80000000; /// will react on memory pressure +pub const NOTE_VM_PRESSURE_TERMINATE = 0x40000000; /// will quit on memory pressure, possibly after cleaning up dirty state +pub const NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000; /// will quit immediately on memory pressure +pub const NOTE_VM_ERROR = 0x10000000; /// there was an error + +pub const NOTE_SECONDS = 0x00000001; /// data is seconds +pub const NOTE_USECONDS = 0x00000002; /// data is microseconds +pub const NOTE_NSECONDS = 0x00000004; /// data is nanoseconds +pub const NOTE_ABSOLUTE = 0x00000008; /// absolute timeout + +pub const NOTE_LEEWAY = 0x00000010; /// ext[1] holds leeway for power aware timers +pub const NOTE_CRITICAL = 0x00000020; /// system does minimal timer coalescing +pub const NOTE_BACKGROUND = 0x00000040; /// system does maximum timer coalescing +pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080; +pub const NOTE_MACHTIME = 0x00000100; /// data is mach absolute time units + + fn wstatus(x: i32) i32 { return x & 0o177; } @@ -385,6 +498,20 @@ pub fn getdirentries64(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usi return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep))); } +pub fn kqueue() usize { + return errnoWrap(c.kqueue()); +} + +pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { + return errnoWrap(c.kevent(kq, changelist.ptr, @intCast(c_int, changelist.len), eventlist.ptr, @intCast(c_int, eventlist.len), timeout,)); +} + +pub fn kevent64(kq: i32, changelist: []const kevent64_s, eventlist: []kevent64_s, flags: u32, + timeout: ?*const timespec) usize +{ + return errnoWrap(c.kevent64(kq, changelist.ptr, changelist.len, eventlist.ptr, eventlist.len, flags, timeout)); +} + pub fn mkdir(path: [*]const u8, mode: u32) usize { return errnoWrap(c.mkdir(path, mode)); } @@ -474,6 +601,10 @@ pub const dirent = c.dirent; pub const sa_family_t = c.sa_family_t; pub const sockaddr = c.sockaddr; +/// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall. +pub const Kevent = c.Kevent; +pub const kevent64_s = c.kevent64_s; + /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = struct { handler: extern fn (i32) void, diff --git a/std/os/index.zig b/std/os/index.zig index c36aae91da..15594edcc8 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2787,3 +2787,59 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { } } } + +pub const BsdKQueueError = error { + /// The per-process limit on the number of open file descriptors has been reached. + ProcessFdQuotaExceeded, + + /// The system-wide limit on the total number of open files has been reached. + SystemFdQuotaExceeded, + + Unexpected, +}; + +pub fn bsdKQueue() BsdKQueueError!i32 { + const rc = posix.kqueue(); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(i32, rc), + posix.EMFILE => return BsdKQueueError.ProcessFdQuotaExceeded, + posix.ENFILE => return BsdKQueueError.SystemFdQuotaExceeded, + else => return unexpectedErrorPosix(err), + } +} + +pub const BsdKEventError = error { + /// The process does not have permission to register a filter. + AccessDenied, + + /// The event could not be found to be modified or deleted. + EventNotFound, + + /// No memory was available to register the event. + SystemResources, + + /// The specified process to attach to does not exist. + ProcessNotFound, +}; + +pub fn bsdKEvent(kq: i32, changelist: []const posix.Kevent, eventlist: []posix.Kevent, + timeout: ?*const posix.timespec) BsdKEventError!usize +{ + while (true) { + const rc = posix.kevent(kq, changelist, eventlist, timeout); + const err = posix.getErrno(rc); + switch (err) { + 0 => return rc, + posix.EACCES => return BsdKEventError.AccessDenied, + posix.EFAULT => unreachable, + posix.EBADF => unreachable, + posix.EINTR => continue, + posix.EINVAL => unreachable, + posix.ENOENT => return BsdKEventError.EventNotFound, + posix.ENOMEM => return BsdKEventError.SystemResources, + posix.ESRCH => return BsdKEventError.ProcessNotFound, + else => unreachable, + } + } +} -- cgit v1.2.3 From 04d3da4bd1d5b8922d3f161c92c6185f33961523 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 01:08:33 -0400 Subject: std.os.cpuCount implementation for macos --- std/c/darwin.zig | 4 +++ std/os/darwin.zig | 12 +++++++++ std/os/index.zig | 74 ++++++++++++++++++++++++++++++++++++------------------- 3 files changed, 64 insertions(+), 26 deletions(-) diff --git a/std/c/darwin.zig b/std/c/darwin.zig index cbaa2f6811..c7e18d94cc 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -13,6 +13,10 @@ pub extern "c" fn kevent(kq: c_int, changelist: [*]const Kevent, nchanges: c_int pub extern "c" fn kevent64(kq: c_int, changelist: [*]const kevent64_s, nchanges: c_int, eventlist: [*]kevent64_s, nevents: c_int, flags: c_uint, timeout: ?*const timespec) c_int; +pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; +pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; +pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; + pub use @import("../os/darwin_errno.zig"); pub const _errno = __error; diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 7921d1b6f0..fc933b7f47 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -520,6 +520,18 @@ pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { return errnoWrap(c.symlink(existing, new)); } +pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { + return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); +} + +pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { + return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); +} + +pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { + return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); +} + pub fn rename(old: [*]const u8, new: [*]const u8) usize { return errnoWrap(c.rename(old, new)); } diff --git a/std/os/index.zig b/std/os/index.zig index 15594edcc8..0d0f63a066 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2756,35 +2756,57 @@ pub const CpuCountError = error{ }; pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { - const usize_count = 16; - const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get(); + switch (builtin.os) { + builtin.Os.macosx => { + var count: c_int = undefined; + var count_len: usize = @sizeOf(c_int); + const rc = posix.sysctlbyname(c"hw.ncpu", @ptrCast(*c_void, &count), &count_len, null, 0); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(usize, count), + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.ENOMEM => return CpuCountError.OutOfMemory, + posix.ENOTDIR => unreachable, + posix.EISDIR => unreachable, + posix.ENOENT => unreachable, + posix.EPERM => unreachable, + else => return os.unexpectedErrorPosix(err), + } + }, + builtin.Os.linux => { + const usize_count = 16; + const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get(); - var set = try allocator.alloc(usize, usize_count); - defer allocator.free(set); + var set = try allocator.alloc(usize, usize_count); + defer allocator.free(set); - while (true) { - const rc = posix.sched_getaffinity(0, set); - const err = posix.getErrno(rc); - switch (err) { - 0 => { - if (rc < set.len * @sizeOf(usize)) { - const result = set[0 .. rc / @sizeOf(usize)]; - var sum: usize = 0; - for (result) |x| { - sum += @popCount(x); - } - return sum; - } else { - set = try allocator.realloc(usize, set, set.len * 2); - continue; + while (true) { + const rc = posix.sched_getaffinity(0, set); + const err = posix.getErrno(rc); + switch (err) { + 0 => { + if (rc < set.len * @sizeOf(usize)) { + const result = set[0 .. rc / @sizeOf(usize)]; + var sum: usize = 0; + for (result) |x| { + sum += @popCount(x); + } + return sum; + } else { + set = try allocator.realloc(usize, set, set.len * 2); + continue; + } + }, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.EPERM => return CpuCountError.PermissionDenied, + posix.ESRCH => unreachable, + else => return os.unexpectedErrorPosix(err), } - }, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EPERM => return CpuCountError.PermissionDenied, - posix.ESRCH => unreachable, - else => return os.unexpectedErrorPosix(err), - } + } + }, + else => @compileError("unsupported OS"), } } -- cgit v1.2.3 From 82e9190d0939a7f71df3d602e381b0ec7cccb561 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Mon, 9 Jul 2018 17:14:04 +1200 Subject: Update zig.parser benchmark program --- std/zig/bench.zig | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/std/zig/bench.zig b/std/zig/bench.zig index 59392889a6..630f6b2233 100644 --- a/std/zig/bench.zig +++ b/std/zig/bench.zig @@ -19,20 +19,18 @@ pub fn main() !void { } const end = timer.read(); memory_used /= iterations; - const elapsed_s = f64(end - start) / std.os.time.ns_per_s; - const bytes_per_sec = f64(source.len * iterations) / elapsed_s; + const elapsed_s = @intToFloat(f64, end - start) / std.os.time.ns_per_s; + const bytes_per_sec = @intToFloat(f64, source.len * iterations) / elapsed_s; const mb_per_sec = bytes_per_sec / (1024 * 1024); var stdout_file = try std.io.getStdOut(); - const stdout = *std.io.FileOutStream.init(*stdout_file).stream; - try stdout.print("{.3} MB/s, {} KB used \n", mb_per_sec, memory_used / 1024); + const stdout = &std.io.FileOutStream.init(&stdout_file).stream; + try stdout.print("{.3} MiB/s, {} KiB used \n", mb_per_sec, memory_used / 1024); } fn testOnce() usize { var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - var allocator = *fixed_buf_alloc.allocator; - var tokenizer = Tokenizer.init(source); - var parser = Parser.init(*tokenizer, allocator, "(memory buffer)"); - _ = parser.parse() catch @panic("parse failure"); + var allocator = &fixed_buf_alloc.allocator; + _ = std.zig.parse(allocator, source) catch @panic("parse failure"); return fixed_buf_alloc.end_index; } -- cgit v1.2.3 From 3ba451778fde48a5463180deea6d6539f91e1303 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 01:22:36 -0400 Subject: fix regressions on linux --- std/event.zig | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/std/event.zig b/std/event.zig index 12aa2a3fc7..3fad81a78b 100644 --- a/std/event.zig +++ b/std/event.zig @@ -136,10 +136,7 @@ pub const Loop = struct { }; pub const EventFd = switch (builtin.os) { - builtin.Os.macosx => struct { - base: ResumeNode, - kevent: posix.Kevent, - }, + builtin.Os.macosx => MacOsEventFd, builtin.Os.linux => struct { base: ResumeNode, epoll_op: u32, @@ -147,6 +144,11 @@ pub const Loop = struct { }, else => @compileError("unsupported OS"), }; + + const MacOsEventFd = struct { + base: ResumeNode, + kevent: posix.Kevent, + }; }; /// After initialization, call run(). @@ -276,10 +278,10 @@ pub const Loop = struct { .handle = undefined, }, // this one is for sending events - .kevent = posix.Kevent { + .kevent = posix.Kevent{ .ident = i, .filter = posix.EVFILT_USER, - .flags = posix.EV_CLEAR|posix.EV_ADD|posix.EV_DISABLE, + .flags = posix.EV_CLEAR | posix.EV_ADD | posix.EV_DISABLE, .fflags = 0, .data = 0, .udata = @ptrToInt(&eventfd_node.data.base), @@ -290,10 +292,10 @@ pub const Loop = struct { self.available_eventfd_resume_nodes.push(eventfd_node); const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent); _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null); - eventfd_node.data.kevent.flags = posix.EV_CLEAR|posix.EV_ENABLE; + eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE; eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER; // this one is for waiting for events - self.os_data.kevents[i] = posix.Kevent { + self.os_data.kevents[i] = posix.Kevent{ .ident = i, .filter = posix.EVFILT_USER, .flags = 0, @@ -542,13 +544,15 @@ pub const Loop = struct { final_eventfd: i32, final_eventfd_event: std.os.linux.epoll_event, }, - builtin.Os.macosx => struct { - kqfd: i32, - final_kevent: posix.Kevent, - kevents: []posix.Kevent, - }, + builtin.Os.macosx => MacOsData, else => struct {}, }; + + const MacOsData = struct { + kqfd: i32, + final_kevent: posix.Kevent, + kevents: posix.Kevent, + }; }; /// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size -- cgit v1.2.3 From a0c564d7621c1bf3f83be59d6f91056b0cfe1e16 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 01:23:47 -0400 Subject: zig fmt --- std/c/darwin.zig | 24 ++- std/os/darwin.zig | 266 +++++++++++++++++++-------- std/os/index.zig | 13 +- std/special/compiler_rt/extendXfYf2_test.zig | 40 ++-- 4 files changed, 237 insertions(+), 106 deletions(-) diff --git a/std/c/darwin.zig b/std/c/darwin.zig index c7e18d94cc..133ef62f05 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -7,11 +7,24 @@ pub extern "c" fn mach_absolute_time() u64; pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void; pub extern "c" fn kqueue() c_int; -pub extern "c" fn kevent(kq: c_int, changelist: [*]const Kevent, nchanges: c_int, - eventlist: [*]Kevent, nevents: c_int, timeout: ?*const timespec) c_int; - -pub extern "c" fn kevent64(kq: c_int, changelist: [*]const kevent64_s, nchanges: c_int, - eventlist: [*]kevent64_s, nevents: c_int, flags: c_uint, timeout: ?*const timespec) c_int; +pub extern "c" fn kevent( + kq: c_int, + changelist: [*]const Kevent, + nchanges: c_int, + eventlist: [*]Kevent, + nevents: c_int, + timeout: ?*const timespec, +) c_int; + +pub extern "c" fn kevent64( + kq: c_int, + changelist: [*]const kevent64_s, + nchanges: c_int, + eventlist: [*]kevent64_s, + nevents: c_int, + flags: c_uint, + timeout: ?*const timespec, +) c_int; pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; @@ -145,4 +158,3 @@ comptime { assert(@offsetOf(kevent64_s, "udata") == 24); assert(@offsetOf(kevent64_s, "ext") == 32); } - diff --git a/std/os/darwin.zig b/std/os/darwin.zig index fc933b7f47..4134e382fc 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -264,17 +264,32 @@ pub const SIGUSR1 = 30; /// user defined signal 2 pub const SIGUSR2 = 31; -pub const KEVENT_FLAG_NONE = 0x000; /// no flag value -pub const KEVENT_FLAG_IMMEDIATE = 0x001; /// immediate timeout -pub const KEVENT_FLAG_ERROR_EVENTS = 0x002; /// output events only include change +/// no flag value +pub const KEVENT_FLAG_NONE = 0x000; -pub const EV_ADD = 0x0001; /// add event to kq (implies enable) -pub const EV_DELETE = 0x0002; /// delete event from kq -pub const EV_ENABLE = 0x0004; /// enable event -pub const EV_DISABLE = 0x0008; /// disable event (not reported) +/// immediate timeout +pub const KEVENT_FLAG_IMMEDIATE = 0x001; -pub const EV_ONESHOT = 0x0010; /// only report one occurrence -pub const EV_CLEAR = 0x0020; /// clear event state after reporting +/// output events only include change +pub const KEVENT_FLAG_ERROR_EVENTS = 0x002; + +/// add event to kq (implies enable) +pub const EV_ADD = 0x0001; + +/// delete event from kq +pub const EV_DELETE = 0x0002; + +/// enable event +pub const EV_ENABLE = 0x0004; + +/// disable event (not reported) +pub const EV_DISABLE = 0x0008; + +/// only report one occurrence +pub const EV_ONESHOT = 0x0010; + +/// clear event state after reporting +pub const EV_CLEAR = 0x0020; /// force immediate event output /// ... with or without EV_ERROR @@ -282,8 +297,11 @@ pub const EV_CLEAR = 0x0020; /// clear event state after reporting /// on syscalls supporting flags pub const EV_RECEIPT = 0x0040; -pub const EV_DISPATCH = 0x0080; /// disable event after reporting -pub const EV_UDATA_SPECIFIC = 0x0100; /// unique kevent per udata value +/// disable event after reporting +pub const EV_DISPATCH = 0x0080; + +/// unique kevent per udata value +pub const EV_UDATA_SPECIFIC = 0x0100; /// ... in combination with EV_DELETE /// will defer delete until udata-specific @@ -291,91 +309,178 @@ pub const EV_UDATA_SPECIFIC = 0x0100; /// unique kevent per udata value /// returned to indicate the deferral pub const EV_DISPATCH2 = EV_DISPATCH | EV_UDATA_SPECIFIC; -/// report that source has vanished +/// report that source has vanished /// ... only valid with EV_DISPATCH2 pub const EV_VANISHED = 0x0200; -pub const EV_SYSFLAGS = 0xF000; /// reserved by system -pub const EV_FLAG0 = 0x1000; /// filter-specific flag -pub const EV_FLAG1 = 0x2000; /// filter-specific flag -pub const EV_EOF = 0x8000; /// EOF detected -pub const EV_ERROR = 0x4000; /// error, data contains errno +/// reserved by system +pub const EV_SYSFLAGS = 0xF000; + +/// filter-specific flag +pub const EV_FLAG0 = 0x1000; + +/// filter-specific flag +pub const EV_FLAG1 = 0x2000; + +/// EOF detected +pub const EV_EOF = 0x8000; + +/// error, data contains errno +pub const EV_ERROR = 0x4000; pub const EV_POLL = EV_FLAG0; pub const EV_OOBAND = EV_FLAG1; pub const EVFILT_READ = -1; pub const EVFILT_WRITE = -2; -pub const EVFILT_AIO = -3; /// attached to aio requests -pub const EVFILT_VNODE = -4; /// attached to vnodes -pub const EVFILT_PROC = -5; /// attached to struct proc -pub const EVFILT_SIGNAL = -6; /// attached to struct proc -pub const EVFILT_TIMER = -7; /// timers -pub const EVFILT_MACHPORT = -8; /// Mach portsets -pub const EVFILT_FS = -9; /// Filesystem events -pub const EVFILT_USER = -10; /// User events -pub const EVFILT_VM = -12; /// Virtual memory events - -pub const EVFILT_EXCEPT = -15; /// Exception events + +/// attached to aio requests +pub const EVFILT_AIO = -3; + +/// attached to vnodes +pub const EVFILT_VNODE = -4; + +/// attached to struct proc +pub const EVFILT_PROC = -5; + +/// attached to struct proc +pub const EVFILT_SIGNAL = -6; + +/// timers +pub const EVFILT_TIMER = -7; + +/// Mach portsets +pub const EVFILT_MACHPORT = -8; + +/// Filesystem events +pub const EVFILT_FS = -9; + +/// User events +pub const EVFILT_USER = -10; + +/// Virtual memory events +pub const EVFILT_VM = -12; + +/// Exception events +pub const EVFILT_EXCEPT = -15; pub const EVFILT_SYSCOUNT = 17; /// On input, NOTE_TRIGGER causes the event to be triggered for output. pub const NOTE_TRIGGER = 0x01000000; -pub const NOTE_FFNOP = 0x00000000; /// ignore input fflags -pub const NOTE_FFAND = 0x40000000; /// and fflags -pub const NOTE_FFOR = 0x80000000; /// or fflags -pub const NOTE_FFCOPY = 0xc0000000; /// copy fflags -pub const NOTE_FFCTRLMASK = 0xc0000000; /// mask for operations +/// ignore input fflags +pub const NOTE_FFNOP = 0x00000000; + +/// and fflags +pub const NOTE_FFAND = 0x40000000; + +/// or fflags +pub const NOTE_FFOR = 0x80000000; + +/// copy fflags +pub const NOTE_FFCOPY = 0xc0000000; + +/// mask for operations +pub const NOTE_FFCTRLMASK = 0xc0000000; pub const NOTE_FFLAGSMASK = 0x00ffffff; -pub const NOTE_LOWAT = 0x00000001; /// low water mark +/// low water mark +pub const NOTE_LOWAT = 0x00000001; + +/// OOB data +pub const NOTE_OOB = 0x00000002; + +/// vnode was removed +pub const NOTE_DELETE = 0x00000001; + +/// data contents changed +pub const NOTE_WRITE = 0x00000002; + +/// size increased +pub const NOTE_EXTEND = 0x00000004; + +/// attributes changed +pub const NOTE_ATTRIB = 0x00000008; + +/// link count changed +pub const NOTE_LINK = 0x00000010; + +/// vnode was renamed +pub const NOTE_RENAME = 0x00000020; + +/// vnode access was revoked +pub const NOTE_REVOKE = 0x00000040; + +/// No specific vnode event: to test for EVFILT_READ activation +pub const NOTE_NONE = 0x00000080; + +/// vnode was unlocked by flock(2) +pub const NOTE_FUNLOCK = 0x00000100; + +/// process exited +pub const NOTE_EXIT = 0x80000000; -pub const NOTE_OOB = 0x00000002; /// OOB data +/// process forked +pub const NOTE_FORK = 0x40000000; -pub const NOTE_DELETE = 0x00000001; /// vnode was removed -pub const NOTE_WRITE = 0x00000002; /// data contents changed -pub const NOTE_EXTEND = 0x00000004; /// size increased -pub const NOTE_ATTRIB = 0x00000008; /// attributes changed -pub const NOTE_LINK = 0x00000010; /// link count changed -pub const NOTE_RENAME = 0x00000020; /// vnode was renamed -pub const NOTE_REVOKE = 0x00000040; /// vnode access was revoked -pub const NOTE_NONE = 0x00000080; /// No specific vnode event: to test for EVFILT_READ activation -pub const NOTE_FUNLOCK = 0x00000100; /// vnode was unlocked by flock(2) +/// process exec'd +pub const NOTE_EXEC = 0x20000000; -pub const NOTE_EXIT = 0x80000000; /// process exited -pub const NOTE_FORK = 0x40000000; /// process forked -pub const NOTE_EXEC = 0x20000000; /// process exec'd -pub const NOTE_SIGNAL = 0x08000000; /// shared with EVFILT_SIGNAL -pub const NOTE_EXITSTATUS = 0x04000000; /// exit status to be returned, valid for child process only -pub const NOTE_EXIT_DETAIL = 0x02000000; /// provide details on reasons for exit +/// shared with EVFILT_SIGNAL +pub const NOTE_SIGNAL = 0x08000000; -pub const NOTE_PDATAMASK = 0x000fffff; /// mask for signal & exit status -pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); +/// exit status to be returned, valid for child process only +pub const NOTE_EXITSTATUS = 0x04000000; -pub const NOTE_EXIT_DETAIL_MASK = 0x00070000; -pub const NOTE_EXIT_DECRYPTFAIL = 0x00010000; -pub const NOTE_EXIT_MEMORY = 0x00020000; -pub const NOTE_EXIT_CSERROR = 0x00040000; +/// provide details on reasons for exit +pub const NOTE_EXIT_DETAIL = 0x02000000; +/// mask for signal & exit status +pub const NOTE_PDATAMASK = 0x000fffff; +pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); -pub const NOTE_VM_PRESSURE = 0x80000000; /// will react on memory pressure -pub const NOTE_VM_PRESSURE_TERMINATE = 0x40000000; /// will quit on memory pressure, possibly after cleaning up dirty state -pub const NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000; /// will quit immediately on memory pressure -pub const NOTE_VM_ERROR = 0x10000000; /// there was an error +pub const NOTE_EXIT_DETAIL_MASK = 0x00070000; +pub const NOTE_EXIT_DECRYPTFAIL = 0x00010000; +pub const NOTE_EXIT_MEMORY = 0x00020000; +pub const NOTE_EXIT_CSERROR = 0x00040000; -pub const NOTE_SECONDS = 0x00000001; /// data is seconds -pub const NOTE_USECONDS = 0x00000002; /// data is microseconds -pub const NOTE_NSECONDS = 0x00000004; /// data is nanoseconds -pub const NOTE_ABSOLUTE = 0x00000008; /// absolute timeout +/// will react on memory pressure +pub const NOTE_VM_PRESSURE = 0x80000000; -pub const NOTE_LEEWAY = 0x00000010; /// ext[1] holds leeway for power aware timers -pub const NOTE_CRITICAL = 0x00000020; /// system does minimal timer coalescing -pub const NOTE_BACKGROUND = 0x00000040; /// system does maximum timer coalescing -pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080; -pub const NOTE_MACHTIME = 0x00000100; /// data is mach absolute time units +/// will quit on memory pressure, possibly after cleaning up dirty state +pub const NOTE_VM_PRESSURE_TERMINATE = 0x40000000; +/// will quit immediately on memory pressure +pub const NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000; + +/// there was an error +pub const NOTE_VM_ERROR = 0x10000000; + +/// data is seconds +pub const NOTE_SECONDS = 0x00000001; + +/// data is microseconds +pub const NOTE_USECONDS = 0x00000002; + +/// data is nanoseconds +pub const NOTE_NSECONDS = 0x00000004; + +/// absolute timeout +pub const NOTE_ABSOLUTE = 0x00000008; + +/// ext[1] holds leeway for power aware timers +pub const NOTE_LEEWAY = 0x00000010; + +/// system does minimal timer coalescing +pub const NOTE_CRITICAL = 0x00000020; + +/// system does maximum timer coalescing +pub const NOTE_BACKGROUND = 0x00000040; +pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080; + +/// data is mach absolute time units +pub const NOTE_MACHTIME = 0x00000100; fn wstatus(x: i32) i32 { return x & 0o177; @@ -503,12 +608,23 @@ pub fn kqueue() usize { } pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { - return errnoWrap(c.kevent(kq, changelist.ptr, @intCast(c_int, changelist.len), eventlist.ptr, @intCast(c_int, eventlist.len), timeout,)); -} - -pub fn kevent64(kq: i32, changelist: []const kevent64_s, eventlist: []kevent64_s, flags: u32, - timeout: ?*const timespec) usize -{ + return errnoWrap(c.kevent( + kq, + changelist.ptr, + @intCast(c_int, changelist.len), + eventlist.ptr, + @intCast(c_int, eventlist.len), + timeout, + )); +} + +pub fn kevent64( + kq: i32, + changelist: []const kevent64_s, + eventlist: []kevent64_s, + flags: u32, + timeout: ?*const timespec, +) usize { return errnoWrap(c.kevent64(kq, changelist.ptr, changelist.len, eventlist.ptr, eventlist.len, flags, timeout)); } diff --git a/std/os/index.zig b/std/os/index.zig index 0d0f63a066..021a29e3d5 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2810,7 +2810,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { } } -pub const BsdKQueueError = error { +pub const BsdKQueueError = error{ /// The per-process limit on the number of open file descriptors has been reached. ProcessFdQuotaExceeded, @@ -2831,7 +2831,7 @@ pub fn bsdKQueue() BsdKQueueError!i32 { } } -pub const BsdKEventError = error { +pub const BsdKEventError = error{ /// The process does not have permission to register a filter. AccessDenied, @@ -2845,9 +2845,12 @@ pub const BsdKEventError = error { ProcessNotFound, }; -pub fn bsdKEvent(kq: i32, changelist: []const posix.Kevent, eventlist: []posix.Kevent, - timeout: ?*const posix.timespec) BsdKEventError!usize -{ +pub fn bsdKEvent( + kq: i32, + changelist: []const posix.Kevent, + eventlist: []posix.Kevent, + timeout: ?*const posix.timespec, +) BsdKEventError!usize { while (true) { const rc = posix.kevent(kq, changelist, eventlist, timeout); const err = posix.getErrno(rc); diff --git a/std/special/compiler_rt/extendXfYf2_test.zig b/std/special/compiler_rt/extendXfYf2_test.zig index 185c83a0ef..9969607011 100644 --- a/std/special/compiler_rt/extendXfYf2_test.zig +++ b/std/special/compiler_rt/extendXfYf2_test.zig @@ -31,7 +31,7 @@ fn test__extendhfsf2(a: u16, expected: u32) void { if (rep == expected) { if (rep & 0x7fffffff > 0x7f800000) { - return; // NaN is always unequal. + return; // NaN is always unequal. } if (x == @bitCast(f32, expected)) { return; @@ -86,33 +86,33 @@ test "extenddftf2" { } test "extendhfsf2" { - test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN - test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN - test__extendhfsf2(0x7c01, 0x7f802000); // sNaN + test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN + test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN + test__extendhfsf2(0x7c01, 0x7f802000); // sNaN - test__extendhfsf2(0, 0); // 0 - test__extendhfsf2(0x8000, 0x80000000); // -0 + test__extendhfsf2(0, 0); // 0 + test__extendhfsf2(0x8000, 0x80000000); // -0 - test__extendhfsf2(0x7c00, 0x7f800000); // inf - test__extendhfsf2(0xfc00, 0xff800000); // -inf + test__extendhfsf2(0x7c00, 0x7f800000); // inf + test__extendhfsf2(0xfc00, 0xff800000); // -inf - test__extendhfsf2(0x0001, 0x33800000); // denormal (min), 2**-24 - test__extendhfsf2(0x8001, 0xb3800000); // denormal (min), -2**-24 + test__extendhfsf2(0x0001, 0x33800000); // denormal (min), 2**-24 + test__extendhfsf2(0x8001, 0xb3800000); // denormal (min), -2**-24 - test__extendhfsf2(0x03ff, 0x387fc000); // denormal (max), 2**-14 - 2**-24 - test__extendhfsf2(0x83ff, 0xb87fc000); // denormal (max), -2**-14 + 2**-24 + test__extendhfsf2(0x03ff, 0x387fc000); // denormal (max), 2**-14 - 2**-24 + test__extendhfsf2(0x83ff, 0xb87fc000); // denormal (max), -2**-14 + 2**-24 - test__extendhfsf2(0x0400, 0x38800000); // normal (min), 2**-14 - test__extendhfsf2(0x8400, 0xb8800000); // normal (min), -2**-14 + test__extendhfsf2(0x0400, 0x38800000); // normal (min), 2**-14 + test__extendhfsf2(0x8400, 0xb8800000); // normal (min), -2**-14 - test__extendhfsf2(0x7bff, 0x477fe000); // normal (max), 65504 - test__extendhfsf2(0xfbff, 0xc77fe000); // normal (max), -65504 + test__extendhfsf2(0x7bff, 0x477fe000); // normal (max), 65504 + test__extendhfsf2(0xfbff, 0xc77fe000); // normal (max), -65504 - test__extendhfsf2(0x3c01, 0x3f802000); // normal, 1 + 2**-10 - test__extendhfsf2(0xbc01, 0xbf802000); // normal, -1 - 2**-10 + test__extendhfsf2(0x3c01, 0x3f802000); // normal, 1 + 2**-10 + test__extendhfsf2(0xbc01, 0xbf802000); // normal, -1 - 2**-10 - test__extendhfsf2(0x3555, 0x3eaaa000); // normal, approx. 1/3 - test__extendhfsf2(0xb555, 0xbeaaa000); // normal, approx. -1/3 + test__extendhfsf2(0x3555, 0x3eaaa000); // normal, approx. 1/3 + test__extendhfsf2(0xb555, 0xbeaaa000); // normal, approx. -1/3 } test "extendsftf2" { -- cgit v1.2.3 From 42ba06133aec995feec3ea24ee7fbbc40d7ac2ca Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 10:33:12 -0400 Subject: std.Hashmap - don't use catch unreachable in tests --- std/hash_map.zig | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/std/hash_map.zig b/std/hash_map.zig index 3bd03d4f28..cebd5272c0 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -259,14 +259,14 @@ test "basic hash map usage" { var map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator); defer map.deinit(); - assert((map.put(1, 11) catch unreachable) == null); - assert((map.put(2, 22) catch unreachable) == null); - assert((map.put(3, 33) catch unreachable) == null); - assert((map.put(4, 44) catch unreachable) == null); - assert((map.put(5, 55) catch unreachable) == null); + assert((try map.put(1, 11)) == null); + assert((try map.put(2, 22)) == null); + assert((try map.put(3, 33)) == null); + assert((try map.put(4, 44)) == null); + assert((try map.put(5, 55)) == null); - assert((map.put(5, 66) catch unreachable).? == 55); - assert((map.put(5, 55) catch unreachable).? == 66); + assert((try map.put(5, 66)).? == 55); + assert((try map.put(5, 55)).? == 66); assert(map.contains(2)); assert(map.get(2).?.value == 22); @@ -282,9 +282,9 @@ test "iterator hash map" { var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator); defer reset_map.deinit(); - assert((reset_map.put(1, 11) catch unreachable) == null); - assert((reset_map.put(2, 22) catch unreachable) == null); - assert((reset_map.put(3, 33) catch unreachable) == null); + assert((try reset_map.put(1, 11)) == null); + assert((try reset_map.put(2, 22)) == null); + assert((try reset_map.put(3, 33)) == null); var keys = []i32{ 1, -- cgit v1.2.3 From 9eb51e20ed1a040a617541303db760f80ffd3aa1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 10:43:29 -0400 Subject: fix crash on @ptrToInt of a *void closes #1192 --- src/ir.cpp | 6 ++++++ test/compile_errors.zig | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 98b1bd85ad..5e4c847e14 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19796,6 +19796,12 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr return ira->codegen->builtin_types.entry_invalid; } + if (!type_has_bits(target->value.type)) { + ir_add_error(ira, target, + buf_sprintf("pointer to size 0 type has no address")); + return ira->codegen->builtin_types.entry_invalid; + } + if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 9071f0ad7e..4ed65e449d 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "@ptrToInt on *void", + \\export fn entry() bool { + \\ return @ptrToInt(&{}) == @ptrToInt(&{}); + \\} + , + ".tmp_source.zig:2:23: error: pointer to size 0 type has no address", + ); + cases.add( "@popCount - non-integer", \\export fn entry(x: f32) u32 { -- cgit v1.2.3 From 2ee67b7642cfeef36d8ebbc08080202b5b1d1958 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 11:13:29 -0400 Subject: langref: docs for invalid error set cast and incorrect pointer alignment also add detection of incorrect pointer alignment at compile-time of pointers that were constructed with `@intToPtr`. --- doc/langref.html.in | 54 ++++++++++++++++++++++++++++++++++++++++++++++--- src/ir.cpp | 9 +++++++++ test/compile_errors.zig | 10 +++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 8eaffb64ad..16e9023f26 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6649,12 +6649,60 @@ pub fn main() void { {#header_close#} {#header_open|Invalid Error Set Cast#} -

    TODO

    +

    At compile-time:

    + {#code_begin|test_err|error.B not a member of error set 'Set2'#} +const Set1 = error{ + A, + B, +}; +const Set2 = error{ + A, + C, +}; +comptime { + _ = @errSetCast(Set2, Set1.B); +} + {#code_end#} +

    At runtime:

    + {#code_begin|exe_err#} +const Set1 = error{ + A, + B, +}; +const Set2 = error{ + A, + C, +}; +pub fn main() void { + _ = foo(Set1.B); +} +fn foo(set1: Set1) Set2 { + return @errSetCast(Set2, set1); +} + {#code_end#} {#header_close#} {#header_open|Incorrect Pointer Alignment#} -

    TODO

    - +

    At compile-time:

    + {#code_begin|test_err|pointer address 0x1 is not aligned to 4 bytes#} +comptime { + const ptr = @intToPtr(*i32, 0x1); + const aligned = @alignCast(4, ptr); +} + {#code_end#} +

    At runtime:

    + {#code_begin|exe_err#} +pub fn main() !void { + var array align(4) = []u32{ 0x11111111, 0x11111111 }; + const bytes = @sliceToBytes(array[0..]); + if (foo(bytes) != 0x11111111) return error.Wrong; +} +fn foo(bytes: []u8) u32 { + const slice4 = bytes[1..5]; + const int_slice = @bytesToSlice(u32, @alignCast(4, slice4)); + return int_slice[0]; +} + {#code_end#} {#header_close#} {#header_open|Wrong Union Field Access#}

    TODO

    diff --git a/src/ir.cpp b/src/ir.cpp index 5e4c847e14..dcd39ccfe5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19370,6 +19370,15 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 if (!val) return ira->codegen->invalid_instruction; + if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && + val->data.x_ptr.data.hard_coded_addr.addr % align_bytes != 0) + { + ir_add_error(ira, target, + buf_sprintf("pointer address 0x%lx is not aligned to %" PRIu32 " bytes", + val->data.x_ptr.data.hard_coded_addr.addr, align_bytes)); + return ira->codegen->invalid_instruction; + } + IrInstruction *result = ir_create_const(&ira->new_irb, target->scope, target->source_node, result_type); copy_const_val(&result->value, val, false); result->value.type = result_type; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 4ed65e449d..1b76c01564 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,16 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "bad @alignCast at comptime", + \\comptime { + \\ const ptr = @intToPtr(*i32, 0x1); + \\ const aligned = @alignCast(4, ptr); + \\} + , + ".tmp_source.zig:3:35: error: pointer address 0x1 is not aligned to 4 bytes", + ); + cases.add( "@ptrToInt on *void", \\export fn entry() bool { -- cgit v1.2.3 From 05f1ea33d2d2f4ffa2bb6686a6a938d1b7983074 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 12:12:37 -0400 Subject: ZIG_DEBUG_COLOR=1 overrides tty detection for runtime stack traces --- doc/docgen.zig | 29 ++++++++++++++++------------- std/debug/index.zig | 11 +++++++++-- std/os/index.zig | 14 +++++++++++--- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/doc/docgen.zig b/doc/docgen.zig index dfda54567f..e2da1fe6cc 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -689,7 +689,10 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 { fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void { var code_progress_index: usize = 0; - const builtin_code = try escapeHtml(allocator, try getBuiltinCode(allocator, zig_exe)); + var env_map = try os.getEnvMap(allocator); + try env_map.set("ZIG_DEBUG_COLOR", "1"); + + const builtin_code = try escapeHtml(allocator, try getBuiltinCode(allocator, &env_map, zig_exe)); for (toc.nodes) |node| { switch (node) { @@ -778,12 +781,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var try build_args.append("c"); try out.print(" --library c"); } - _ = exec(allocator, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile"); + _ = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile"); const run_args = [][]const u8{tmp_bin_file_name}; const result = if (expected_outcome == ExpectedOutcome.Fail) blk: { - const result = try os.ChildProcess.exec(allocator, run_args, null, null, max_doc_file_size); + const result = try os.ChildProcess.exec(allocator, run_args, null, &env_map, max_doc_file_size); switch (result.term) { os.ChildProcess.Term.Exited => |exit_code| { if (exit_code == 0) { @@ -799,7 +802,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } break :blk result; } else blk: { - break :blk exec(allocator, run_args) catch return parseError(tokenizer, code.source_token, "example crashed"); + break :blk exec(allocator, &env_map, run_args) catch return parseError(tokenizer, code.source_token, "example crashed"); }; const escaped_stderr = try escapeHtml(allocator, result.stderr); @@ -845,7 +848,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var "msvc", }); } - const result = exec(allocator, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed"); + const result = exec(allocator, &env_map, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed"); const escaped_stderr = try escapeHtml(allocator, result.stderr); const escaped_stdout = try escapeHtml(allocator, result.stdout); try out.print("\n{}{}
    \n", escaped_stderr, escaped_stdout); @@ -877,7 +880,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var try out.print(" --release-small"); }, } - const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size); + const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, &env_map, max_doc_file_size); switch (result.term) { os.ChildProcess.Term.Exited => |exit_code| { if (exit_code == 0) { @@ -923,7 +926,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var builtin.Mode.ReleaseSmall => try test_args.append("--release-small"), } - const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size); + const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, &env_map, max_doc_file_size); switch (result.term) { os.ChildProcess.Term.Exited => |exit_code| { if (exit_code == 0) { @@ -1000,7 +1003,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } if (maybe_error_match) |error_match| { - const result = try os.ChildProcess.exec(allocator, build_args.toSliceConst(), null, null, max_doc_file_size); + const result = try os.ChildProcess.exec(allocator, build_args.toSliceConst(), null, &env_map, max_doc_file_size); switch (result.term) { os.ChildProcess.Term.Exited => |exit_code| { if (exit_code == 0) { @@ -1032,7 +1035,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var try out.print("\n"); } } else { - _ = exec(allocator, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile"); + _ = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile"); } if (!code.is_inline) { try out.print("\n"); @@ -1045,8 +1048,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } } -fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult { - const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size); +fn exec(allocator: *mem.Allocator, env_map: *std.BufMap, args: []const []const u8) !os.ChildProcess.ExecResult { + const result = try os.ChildProcess.exec(allocator, args, null, env_map, max_doc_file_size); switch (result.term) { os.ChildProcess.Term.Exited => |exit_code| { if (exit_code != 0) { @@ -1070,8 +1073,8 @@ fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.Ex return result; } -fn getBuiltinCode(allocator: *mem.Allocator, zig_exe: []const u8) ![]const u8 { - const result = try exec(allocator, []const []const u8{ +fn getBuiltinCode(allocator: *mem.Allocator, env_map: *std.BufMap, zig_exe: []const u8) ![]const u8 { + const result = try exec(allocator, env_map, []const []const u8{ zig_exe, "builtin", }); diff --git a/std/debug/index.zig b/std/debug/index.zig index 0e2a3a8d39..54a9af4b9e 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -10,6 +10,7 @@ const ArrayList = std.ArrayList; const builtin = @import("builtin"); pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; +pub const failing_allocator = FailingAllocator.init(global_allocator, 0); /// Tries to write to stderr, unbuffered, and ignores any error returned. /// Does not append a newline. @@ -44,6 +45,12 @@ pub fn getSelfDebugInfo() !*ElfStackTrace { } } +fn wantTtyColor() bool { + var bytes: [128]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + return if (std.os.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty(); +} + /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. pub fn dumpCurrentStackTrace(start_addr: ?usize) void { const stderr = getStderrStream() catch return; @@ -51,7 +58,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; return; }; - writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty(), start_addr) catch |err| { + writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, wantTtyColor(), start_addr) catch |err| { stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; return; }; @@ -64,7 +71,7 @@ pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; return; }; - writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty()) catch |err| { + writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, wantTtyColor()) catch |err| { stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; return; }; diff --git a/std/os/index.zig b/std/os/index.zig index 52b36c351c..0f9aea914d 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -544,8 +544,13 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 { return null; } +pub const GetEnvVarOwnedError = error{ + OutOfMemory, + EnvironmentVariableNotFound, +}; + /// Caller must free returned memory. -pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 { +pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { if (is_windows) { const key_with_null = try cstr.addNullByte(allocator, key); defer allocator.free(key_with_null); @@ -554,14 +559,17 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 { errdefer allocator.free(buf); while (true) { - const windows_buf_len = try math.cast(windows.DWORD, buf.len); + const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory; const result = windows.GetEnvironmentVariableA(key_with_null.ptr, buf.ptr, windows_buf_len); if (result == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound, - else => unexpectedErrorWindows(err), + else => { + _ = unexpectedErrorWindows(err); + return error.EnvironmentVariableNotFound; + }, }; } -- cgit v1.2.3 From caa008505729f9511f6f0b070636013e9597b3f7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 13:19:11 -0400 Subject: implement std.os.cpuCount for windows --- std/event.zig | 3 +++ std/heap.zig | 4 ++-- std/os/index.zig | 5 +++++ std/os/windows/index.zig | 21 +++++++++++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/std/event.zig b/std/event.zig index 3fad81a78b..589ab4cb5f 100644 --- a/std/event.zig +++ b/std/event.zig @@ -142,6 +142,9 @@ pub const Loop = struct { epoll_op: u32, eventfd: i32, }, + builtin.Os.windows => struct { + base: ResumeNode, + }, else => @compileError("unsupported OS"), }; diff --git a/std/heap.zig b/std/heap.zig index 6d3fd05cdb..caf972e605 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -96,12 +96,12 @@ pub const DirectAllocator = struct { }, Os.windows => { const amt = n + alignment + @sizeOf(usize); - const optional_heap_handle = @atomicLoad(?HeapHandle, ?self.heap_handle, builtin.AtomicOrder.SeqCst); + const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst); const heap_handle = optional_heap_handle orelse blk: { const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) orelse return error.OutOfMemory; const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh; _ = os.windows.HeapDestroy(hh); - break :blk other_hh; + break :blk other_hh.?; // can't be null because of the cmpxchg }; const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory; const root_addr = @ptrToInt(ptr); diff --git a/std/os/index.zig b/std/os/index.zig index 021a29e3d5..94fdd9dc84 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2806,6 +2806,11 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { } } }, + builtin.Os.windows => { + var system_info: windows.SYSTEM_INFO = undefined; + windows.GetSystemInfo(&system_info); + return @intCast(usize, system_info.dwNumberOfProcessors); + }, else => @compileError("unsupported OS"), } } diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index d631c6adbf..571ac97fac 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -107,6 +107,7 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; +pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void; pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void; pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; @@ -204,6 +205,7 @@ pub const SIZE_T = usize; pub const TCHAR = if (UNICODE) WCHAR else u8; pub const UINT = c_uint; pub const ULONG_PTR = usize; +pub const DWORD_PTR = ULONG_PTR; pub const UNICODE = false; pub const WCHAR = u16; pub const WORD = u16; @@ -413,3 +415,22 @@ pub const FILETIME = extern struct { dwLowDateTime: DWORD, dwHighDateTime: DWORD, }; + +pub const SYSTEM_INFO = extern struct { + anon1: extern union { + dwOemId: DWORD, + anon2: extern struct { + wProcessorArchitecture: WORD, + wReserved: WORD, + }, + }, + dwPageSize: DWORD, + lpMinimumApplicationAddress: LPVOID, + lpMaximumApplicationAddress: LPVOID, + dwActiveProcessorMask: DWORD_PTR, + dwNumberOfProcessors: DWORD, + dwProcessorType: DWORD, + dwAllocationGranularity: DWORD, + wProcessorLevel: WORD, + wProcessorRevision: WORD, +}; -- cgit v1.2.3 From 9462852433a815496e0edf5d5b2e00726f5ea072 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 16:49:46 -0400 Subject: std.event.Loop multithreading for windows using IOCP --- std/event.zig | 122 ++++++++++++++++++++++++++++++++++++++++++++--- std/heap.zig | 2 +- std/os/index.zig | 25 ++++++++-- std/os/windows/index.zig | 7 +++ std/os/windows/util.zig | 47 ++++++++++++++++++ 5 files changed, 191 insertions(+), 12 deletions(-) diff --git a/std/event.zig b/std/event.zig index 589ab4cb5f..90d614d72e 100644 --- a/std/event.zig +++ b/std/event.zig @@ -4,6 +4,7 @@ const assert = std.debug.assert; const event = this; const mem = std.mem; const posix = std.os.posix; +const windows = std.os.windows; const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; @@ -113,10 +114,10 @@ pub const Loop = struct { allocator: *mem.Allocator, next_tick_queue: std.atomic.QueueMpsc(promise), os_data: OsData, + final_resume_node: ResumeNode, dispatch_lock: u8, // TODO make this a bool pending_event_count: usize, extra_threads: []*std.os.Thread, - final_resume_node: ResumeNode, // pre-allocated eventfds. all permanently active. // this is how we send promises to be resumed on other threads. @@ -144,6 +145,7 @@ pub const Loop = struct { }, builtin.Os.windows => struct { base: ResumeNode, + completion_key: usize, }, else => @compileError("unsupported OS"), }; @@ -181,12 +183,12 @@ pub const Loop = struct { .next_tick_queue = std.atomic.QueueMpsc(promise).init(), .dispatch_lock = 1, // start locked so threads go directly into epoll wait .extra_threads = undefined, + .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(), + .eventfd_resume_nodes = undefined, .final_resume_node = ResumeNode{ .id = ResumeNode.Id.Stop, .handle = undefined, }, - .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(), - .eventfd_resume_nodes = undefined, }; const extra_thread_count = thread_count - 1; self.eventfd_resume_nodes = try self.allocator.alloc( @@ -209,7 +211,8 @@ pub const Loop = struct { } const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError || - std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError; + std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError || + std.os.WindowsCreateIoCompletionPortError; const wakeup_bytes = []u8{0x1} ** 8; @@ -335,6 +338,51 @@ pub const Loop = struct { self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); } }, + builtin.Os.windows => { + self.os_data.extra_thread_count = extra_thread_count; + + self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( + windows.INVALID_HANDLE_VALUE, + null, + undefined, + undefined, + ); + errdefer std.os.close(self.os_data.io_port); + + for (self.eventfd_resume_nodes) |*eventfd_node, i| { + eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ + .data = ResumeNode.EventFd{ + .base = ResumeNode{ + .id = ResumeNode.Id.EventFd, + .handle = undefined, + }, + // this one is for sending events + .completion_key = @ptrToInt(&eventfd_node.data.base), + }, + .next = undefined, + }; + self.available_eventfd_resume_nodes.push(eventfd_node); + } + + var extra_thread_index: usize = 0; + errdefer { + var i: usize = 0; + while (i < extra_thread_index) : (i += 1) { + while (true) { + const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); + std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; + break; + } + } + while (extra_thread_index != 0) { + extra_thread_index -= 1; + self.extra_threads[extra_thread_index].wait(); + } + } + while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { + self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); + } + }, else => {}, } } @@ -349,6 +397,10 @@ pub const Loop = struct { }, builtin.Os.macosx => { self.allocator.free(self.os_data.kevents); + std.os.close(self.os_data.kqfd); + }, + builtin.Os.windows => { + std.os.close(self.os_data.io_port); }, else => {}, } @@ -434,7 +486,7 @@ pub const Loop = struct { builtin.Os.macosx => { const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); const eventlist = ([*]posix.Kevent)(undefined)[0..0]; - _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch |_| { + _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch { // fine, we didn't need it anyway _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); self.available_eventfd_resume_nodes.push(resume_stack_node); @@ -446,7 +498,21 @@ pub const Loop = struct { builtin.Os.linux => { // the pending count is already accounted for const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET; - self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch |_| { + self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch { + // fine, we didn't need it anyway + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + self.available_eventfd_resume_nodes.push(resume_stack_node); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + }; + }, + builtin.Os.windows => { + // this value is never dereferenced but we need it to be non-null so that + // the consumer code can decide whether to read the completion key. + // it has to do this for normal I/O, so we match that behavior here. + const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); + std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, eventfd_node.completion_key, overlapped) catch { // fine, we didn't need it anyway _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); self.available_eventfd_resume_nodes.push(resume_stack_node); @@ -482,6 +548,17 @@ pub const Loop = struct { _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable; return; }, + builtin.Os.windows => { + var i: usize = 0; + while (i < self.os_data.extra_thread_count) : (i += 1) { + while (true) { + const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); + std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; + break; + } + } + return; + }, else => @compileError("unsupported OS"), } } @@ -536,6 +613,35 @@ pub const Loop = struct { } } }, + builtin.Os.windows => { + var completion_key: usize = undefined; + while (true) { + var nbytes: windows.DWORD = undefined; + var overlapped: ?*windows.OVERLAPPED = undefined; + switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, + &overlapped, windows.INFINITE)) { + std.os.WindowsWaitResult.Aborted => return, + std.os.WindowsWaitResult.Normal => {}, + } + if (overlapped != null) break; + } + const resume_node = @intToPtr(*ResumeNode, completion_key); + const handle = resume_node.handle; + const resume_node_id = resume_node.id; + switch (resume_node_id) { + ResumeNode.Id.Basic => {}, + ResumeNode.Id.Stop => return, + ResumeNode.Id.EventFd => { + const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); + const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); + self.available_eventfd_resume_nodes.push(stack_node); + }, + } + resume handle; + if (resume_node_id == ResumeNode.Id.EventFd) { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + }, else => @compileError("unsupported OS"), } } @@ -548,6 +654,10 @@ pub const Loop = struct { final_eventfd_event: std.os.linux.epoll_event, }, builtin.Os.macosx => MacOsData, + builtin.Os.windows => struct { + io_port: windows.HANDLE, + extra_thread_count: usize, + }, else => struct {}, }; diff --git a/std/heap.zig b/std/heap.zig index caf972e605..ef22c8d0c5 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -98,7 +98,7 @@ pub const DirectAllocator = struct { const amt = n + alignment + @sizeOf(usize); const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst); const heap_handle = optional_heap_handle orelse blk: { - const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) orelse return error.OutOfMemory; + const hh = os.windows.HeapCreate(0, amt, 0) orelse return error.OutOfMemory; const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh; _ = os.windows.HeapDestroy(hh); break :blk other_hh.?; // can't be null because of the cmpxchg diff --git a/std/os/index.zig b/std/os/index.zig index 94fdd9dc84..896d6b3df8 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -61,6 +61,15 @@ pub const windowsLoadDll = windows_util.windowsLoadDll; pub const windowsUnloadDll = windows_util.windowsUnloadDll; pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; +pub const WindowsCreateIoCompletionPortError = windows_util.WindowsCreateIoCompletionPortError; +pub const windowsCreateIoCompletionPort = windows_util.windowsCreateIoCompletionPort; + +pub const WindowsPostQueuedCompletionStatusError = windows_util.WindowsPostQueuedCompletionStatusError; +pub const windowsPostQueuedCompletionStatus = windows_util.windowsPostQueuedCompletionStatus; + +pub const WindowsWaitResult = windows_util.WindowsWaitResult; +pub const windowsGetQueuedCompletionStatus = windows_util.windowsGetQueuedCompletionStatus; + pub const WindowsWaitError = windows_util.WaitError; pub const WindowsOpenError = windows_util.OpenError; pub const WindowsWriteError = windows_util.WriteError; @@ -2592,11 +2601,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread thread: Thread, inner: Context, }; - extern fn threadMain(arg: windows.LPVOID) windows.DWORD { - if (@sizeOf(Context) == 0) { - return startFn({}); - } else { - return startFn(@ptrCast(*Context, @alignCast(@alignOf(Context), arg)).*); + extern fn threadMain(raw_arg: windows.LPVOID) windows.DWORD { + const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*; + switch (@typeId(@typeOf(startFn).ReturnType)) { + builtin.TypeId.Int => { + return startFn(arg); + }, + builtin.TypeId.Void => { + startFn(arg); + return 0; + }, + else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), } } }; diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 571ac97fac..f73b8ec261 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -59,6 +59,9 @@ pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( dwFlags: DWORD, ) BOOLEAN; + +pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; + pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; @@ -106,6 +109,7 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( ) DWORD; pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; +pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL; pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void; pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void; @@ -130,6 +134,9 @@ pub extern "kernel32" stdcallcc fn MoveFileExA( dwFlags: DWORD, ) BOOL; + +pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL; + pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL; pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 45b205451d..b04e8efc4b 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -214,3 +214,50 @@ pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN3 } return true; } + + +pub const WindowsCreateIoCompletionPortError = error { + Unexpected, +}; + +pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_completion_port: ?windows.HANDLE, completion_key: usize, concurrent_thread_count: windows.DWORD) !windows.HANDLE { + const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse { + const err = windows.GetLastError(); + switch (err) { + else => return os.unexpectedErrorWindows(err), + } + }; + return handle; +} + +pub const WindowsPostQueuedCompletionStatusError = error { + Unexpected, +}; + +pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void { + if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) { + const err = windows.GetLastError(); + switch (err) { + else => return os.unexpectedErrorWindows(err), + } + } +} + +pub const WindowsWaitResult = error { + Normal, + Aborted, +}; + +pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult { + if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) { + if (std.debug.runtime_safety) { + const err = windows.GetLastError(); + if (err != windows.ERROR.ABANDONED_WAIT_0) { + std.debug.warn("err: {}\n", err); + } + assert(err == windows.ERROR.ABANDONED_WAIT_0); + } + return WindowsWaitResult.Aborted; + } + return WindowsWaitResult.Normal; +} -- cgit v1.2.3 From 0ac1b83885c7f2a97a8ac25657afcb5c9b80afb4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 17:13:31 -0400 Subject: fix non-portable format specifier --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index dcd39ccfe5..505a32247e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19374,7 +19374,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 val->data.x_ptr.data.hard_coded_addr.addr % align_bytes != 0) { ir_add_error(ira, target, - buf_sprintf("pointer address 0x%lx is not aligned to %" PRIu32 " bytes", + buf_sprintf("pointer address 0x%" ZIG_PRI_x64 " is not aligned to %" PRIu32 " bytes", val->data.x_ptr.data.hard_coded_addr.addr, align_bytes)); return ira->codegen->invalid_instruction; } -- cgit v1.2.3 From 1a1534ecb55d0273bd9cd62d415ac840eb73b2e5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 17:16:06 -0400 Subject: fix regression on macos --- std/event.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/event.zig b/std/event.zig index 90d614d72e..a72db05e78 100644 --- a/std/event.zig +++ b/std/event.zig @@ -664,7 +664,7 @@ pub const Loop = struct { const MacOsData = struct { kqfd: i32, final_kevent: posix.Kevent, - kevents: posix.Kevent, + kevents: []posix.Kevent, }; }; -- cgit v1.2.3 From a2834d48b9480286549fd9882d67e874396eec79 Mon Sep 17 00:00:00 2001 From: wilsonk Date: Mon, 9 Jul 2018 15:21:20 -0600 Subject: Update throughput_test.zig. (#1211) --- std/crypto/throughput_test.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig index 0ad6845d1a..c21838e607 100644 --- a/std/crypto/throughput_test.zig +++ b/std/crypto/throughput_test.zig @@ -15,8 +15,8 @@ const BytesToHash = 1024 * MiB; pub fn main() !void { var stdout_file = try std.io.getStdOut(); - var stdout_out_stream = std.io.FileOutStream.init(*stdout_file); - const stdout = *stdout_out_stream.stream; + var stdout_out_stream = std.io.FileOutStream.init(&stdout_file); + const stdout = &stdout_out_stream.stream; var block: [HashFunction.block_size]u8 = undefined; std.mem.set(u8, block[0..], 0); @@ -31,8 +31,8 @@ pub fn main() !void { } const end = timer.read(); - const elapsed_s = f64(end - start) / time.ns_per_s; - const throughput = u64(BytesToHash / elapsed_s); + const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; + const throughput = @floatToInt(u64, BytesToHash / elapsed_s); try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB)); } -- cgit v1.2.3 From c89aac85c440ea4cbccf1abdbd6acf84a33077e3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 21:21:59 -0400 Subject: better workaround for guaranteeing memory in coroutine frame See #1194 --- std/atomic/queue_mpsc.zig | 25 +++++++++++++++++++++++++ std/event.zig | 40 ++++++++++++++++++---------------------- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/std/atomic/queue_mpsc.zig b/std/atomic/queue_mpsc.zig index bc0a94258b..978e189453 100644 --- a/std/atomic/queue_mpsc.zig +++ b/std/atomic/queue_mpsc.zig @@ -60,6 +60,31 @@ pub fn QueueMpsc(comptime T: type) type { } return self.outbox.isEmpty(); } + + /// For debugging only. No API guarantees about what this does. + pub fn dump(self: *Self) void { + { + var it = self.outbox.root; + while (it) |node| { + std.debug.warn("0x{x} -> ", @ptrToInt(node)); + it = node.next; + } + } + const inbox_index = self.inbox_index; + const inboxes = []*std.atomic.Stack(T){ + &self.inboxes[self.inbox_index], + &self.inboxes[1 - self.inbox_index], + }; + for (inboxes) |inbox| { + var it = inbox.root; + while (it) |node| { + std.debug.warn("0x{x} -> ", @ptrToInt(node)); + it = node.next; + } + } + + std.debug.warn("null\n"); + } }; } diff --git a/std/event.zig b/std/event.zig index a72db05e78..de51f8c87e 100644 --- a/std/event.zig +++ b/std/event.zig @@ -439,15 +439,14 @@ pub const Loop = struct { pub async fn waitFd(self: *Loop, fd: i32) !void { defer self.removeFd(fd); - var resume_node = ResumeNode{ - .id = ResumeNode.Id.Basic, - .handle = undefined, - }; suspend |p| { - resume_node.handle = p; + // TODO explicitly put this memory in the coroutine frame #1194 + var resume_node = ResumeNode{ + .id = ResumeNode.Id.Basic, + .handle = p, + }; try self.addFd(fd, &resume_node); } - var a = &resume_node; // TODO better way to explicitly put memory in coro frame } /// Bring your own linked list node. This means it can't fail. @@ -618,8 +617,7 @@ pub const Loop = struct { while (true) { var nbytes: windows.DWORD = undefined; var overlapped: ?*windows.OVERLAPPED = undefined; - switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, - &overlapped, windows.INFINITE)) { + switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { std.os.WindowsWaitResult.Aborted => return, std.os.WindowsWaitResult.Normal => {}, } @@ -1062,10 +1060,13 @@ pub const Lock = struct { } pub async fn acquire(self: *Lock) Held { - var my_tick_node: Loop.NextTickNode = undefined; - s: suspend |handle| { - my_tick_node.data = handle; + // TODO explicitly put this memory in the coroutine frame #1194 + var my_tick_node = Loop.NextTickNode{ + .data = handle, + .next = undefined, + }; + self.queue.put(&my_tick_node); // At this point, we are in the queue, so we might have already been resumed and this coroutine @@ -1107,10 +1108,6 @@ pub const Lock = struct { } } - // TODO this workaround to force my_tick_node to be in the coroutine frame should - // not be necessary - var trash1 = &my_tick_node; - return Held{ .lock = self }; } }; @@ -1176,6 +1173,10 @@ test "std.event.Lock" { } async fn testLock(loop: *Loop, lock: *Lock) void { + // TODO explicitly put next tick node memory in the coroutine frame #1194 + suspend |p| { + resume p; + } const handle1 = async lockRunner(lock) catch @panic("out of memory"); var tick_node1 = Loop.NextTickNode{ .next = undefined, @@ -1200,12 +1201,6 @@ async fn testLock(loop: *Loop, lock: *Lock) void { await handle1; await handle2; await handle3; - - // TODO this is to force tick node memory to be in the coro frame - // there should be a way to make it explicit where the memory is - var a = &tick_node1; - var b = &tick_node2; - var c = &tick_node3; } var shared_test_data = [1]i32{0} ** 10; @@ -1216,7 +1211,8 @@ async fn lockRunner(lock: *Lock) void { var i: usize = 0; while (i < shared_test_data.len) : (i += 1) { - const handle = await (async lock.acquire() catch @panic("out of memory")); + const lock_promise = async lock.acquire() catch @panic("out of memory"); + const handle = await lock_promise; defer handle.release(); shared_test_index = 0; -- cgit v1.2.3 From 10cc49db1ca1f9b3ac63277c0742e05f6412f3c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 21:42:05 -0400 Subject: define c macros before importing llvm h files Seems to matter on Ubuntu 16.04. closes #1196 --- src-self-hosted/c.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src-self-hosted/c.zig b/src-self-hosted/c.zig index 08060fbe3a..3912462985 100644 --- a/src-self-hosted/c.zig +++ b/src-self-hosted/c.zig @@ -1,4 +1,6 @@ pub use @cImport({ + @cDefine("__STDC_CONSTANT_MACROS", ""); + @cDefine("__STDC_LIMIT_MACROS", ""); @cInclude("inttypes.h"); @cInclude("config.h"); @cInclude("zig_llvm.h"); -- cgit v1.2.3 From b6eb404831e44a92b4841459068f4fbe9c753541 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 22:22:44 -0400 Subject: organize std.event into directories --- CMakeLists.txt | 5 + std/event.zig | 1234 +------------------------------------------------ std/event/channel.zig | 254 ++++++++++ std/event/lock.zig | 204 ++++++++ std/event/locked.zig | 42 ++ std/event/loop.zig | 577 +++++++++++++++++++++++ std/event/tcp.zig | 183 ++++++++ 7 files changed, 1277 insertions(+), 1222 deletions(-) create mode 100644 std/event/channel.zig create mode 100644 std/event/lock.zig create mode 100644 std/event/locked.zig create mode 100644 std/event/loop.zig create mode 100644 std/event/tcp.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 9701de9e42..fdedcd5eec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -458,6 +458,11 @@ set(ZIG_STD_FILES "elf.zig" "empty.zig" "event.zig" + "event/channel.zig" + "event/lock.zig" + "event/locked.zig" + "event/loop.zig" + "event/tcp.zig" "fmt/errol/enum3.zig" "fmt/errol/index.zig" "fmt/errol/lookup.zig" diff --git a/std/event.zig b/std/event.zig index de51f8c87e..7e9928b3d7 100644 --- a/std/event.zig +++ b/std/event.zig @@ -1,1223 +1,13 @@ -const std = @import("index.zig"); -const builtin = @import("builtin"); -const assert = std.debug.assert; -const event = this; -const mem = std.mem; -const posix = std.os.posix; -const windows = std.os.windows; -const AtomicRmwOp = builtin.AtomicRmwOp; -const AtomicOrder = builtin.AtomicOrder; - -pub const TcpServer = struct { - handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void, - - loop: *Loop, - sockfd: ?i32, - accept_coro: ?promise, - listen_address: std.net.Address, - - waiting_for_emfile_node: PromiseNode, - listen_resume_node: event.Loop.ResumeNode, - - const PromiseNode = std.LinkedList(promise).Node; - - pub fn init(loop: *Loop) TcpServer { - // TODO can't initialize handler coroutine here because we need well defined copy elision - return TcpServer{ - .loop = loop, - .sockfd = null, - .accept_coro = null, - .handleRequestFn = undefined, - .waiting_for_emfile_node = undefined, - .listen_address = undefined, - .listen_resume_node = event.Loop.ResumeNode{ - .id = event.Loop.ResumeNode.Id.Basic, - .handle = undefined, - }, - }; - } - - pub fn listen( - self: *TcpServer, - address: *const std.net.Address, - handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void, - ) !void { - self.handleRequestFn = handleRequestFn; - - const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); - errdefer std.os.close(sockfd); - self.sockfd = sockfd; - - try std.os.posixBind(sockfd, &address.os_addr); - try std.os.posixListen(sockfd, posix.SOMAXCONN); - self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(sockfd)); - - self.accept_coro = try async TcpServer.handler(self); - errdefer cancel self.accept_coro.?; - - self.listen_resume_node.handle = self.accept_coro.?; - try self.loop.addFd(sockfd, &self.listen_resume_node); - errdefer self.loop.removeFd(sockfd); - } - - /// Stop listening - pub fn close(self: *TcpServer) void { - self.loop.removeFd(self.sockfd.?); - std.os.close(self.sockfd.?); - } - - pub fn deinit(self: *TcpServer) void { - if (self.accept_coro) |accept_coro| cancel accept_coro; - if (self.sockfd) |sockfd| std.os.close(sockfd); - } - - pub async fn handler(self: *TcpServer) void { - while (true) { - var accepted_addr: std.net.Address = undefined; - if (std.os.posixAccept(self.sockfd.?, &accepted_addr.os_addr, posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| { - var socket = std.os.File.openHandle(accepted_fd); - _ = async self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) { - error.OutOfMemory => { - socket.close(); - continue; - }, - }; - } else |err| switch (err) { - error.WouldBlock => { - suspend; // we will get resumed by epoll_wait in the event loop - continue; - }, - error.ProcessFdQuotaExceeded => { - errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node); - suspend |p| { - self.waiting_for_emfile_node = PromiseNode.init(p); - std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node); - } - continue; - }, - error.ConnectionAborted, error.FileDescriptorClosed => continue, - - error.PageFault => unreachable, - error.InvalidSyscall => unreachable, - error.FileDescriptorNotASocket => unreachable, - error.OperationNotSupported => unreachable, - - error.SystemFdQuotaExceeded, error.SystemResources, error.ProtocolFailure, error.BlockedByFirewall, error.Unexpected => { - @panic("TODO handle this error"); - }, - } - } - } -}; - -pub const Loop = struct { - allocator: *mem.Allocator, - next_tick_queue: std.atomic.QueueMpsc(promise), - os_data: OsData, - final_resume_node: ResumeNode, - dispatch_lock: u8, // TODO make this a bool - pending_event_count: usize, - extra_threads: []*std.os.Thread, - - // pre-allocated eventfds. all permanently active. - // this is how we send promises to be resumed on other threads. - available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd), - eventfd_resume_nodes: []std.atomic.Stack(ResumeNode.EventFd).Node, - - pub const NextTickNode = std.atomic.QueueMpsc(promise).Node; - - pub const ResumeNode = struct { - id: Id, - handle: promise, - - pub const Id = enum { - Basic, - Stop, - EventFd, - }; - - pub const EventFd = switch (builtin.os) { - builtin.Os.macosx => MacOsEventFd, - builtin.Os.linux => struct { - base: ResumeNode, - epoll_op: u32, - eventfd: i32, - }, - builtin.Os.windows => struct { - base: ResumeNode, - completion_key: usize, - }, - else => @compileError("unsupported OS"), - }; - - const MacOsEventFd = struct { - base: ResumeNode, - kevent: posix.Kevent, - }; - }; - - /// After initialization, call run(). - /// TODO copy elision / named return values so that the threads referencing *Loop - /// have the correct pointer value. - fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void { - return self.initInternal(allocator, 1); - } - - /// The allocator must be thread-safe because we use it for multiplexing - /// coroutines onto kernel threads. - /// After initialization, call run(). - /// TODO copy elision / named return values so that the threads referencing *Loop - /// have the correct pointer value. - fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { - const core_count = try std.os.cpuCount(allocator); - return self.initInternal(allocator, core_count); - } - - /// Thread count is the total thread count. The thread pool size will be - /// max(thread_count - 1, 0) - fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void { - self.* = Loop{ - .pending_event_count = 0, - .allocator = allocator, - .os_data = undefined, - .next_tick_queue = std.atomic.QueueMpsc(promise).init(), - .dispatch_lock = 1, // start locked so threads go directly into epoll wait - .extra_threads = undefined, - .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(), - .eventfd_resume_nodes = undefined, - .final_resume_node = ResumeNode{ - .id = ResumeNode.Id.Stop, - .handle = undefined, - }, - }; - const extra_thread_count = thread_count - 1; - self.eventfd_resume_nodes = try self.allocator.alloc( - std.atomic.Stack(ResumeNode.EventFd).Node, - extra_thread_count, - ); - errdefer self.allocator.free(self.eventfd_resume_nodes); - - self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count); - errdefer self.allocator.free(self.extra_threads); - - try self.initOsData(extra_thread_count); - errdefer self.deinitOsData(); - } - - /// must call stop before deinit - pub fn deinit(self: *Loop) void { - self.deinitOsData(); - self.allocator.free(self.extra_threads); - } - - const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError || - std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError || - std.os.WindowsCreateIoCompletionPortError; - - const wakeup_bytes = []u8{0x1} ** 8; - - fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { - switch (builtin.os) { - builtin.Os.linux => { - errdefer { - while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); - } - for (self.eventfd_resume_nodes) |*eventfd_node| { - eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ - .data = ResumeNode.EventFd{ - .base = ResumeNode{ - .id = ResumeNode.Id.EventFd, - .handle = undefined, - }, - .eventfd = try std.os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK), - .epoll_op = posix.EPOLL_CTL_ADD, - }, - .next = undefined, - }; - self.available_eventfd_resume_nodes.push(eventfd_node); - } - - self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC); - errdefer std.os.close(self.os_data.epollfd); - - self.os_data.final_eventfd = try std.os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK); - errdefer std.os.close(self.os_data.final_eventfd); - - self.os_data.final_eventfd_event = posix.epoll_event{ - .events = posix.EPOLLIN, - .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) }, - }; - try std.os.linuxEpollCtl( - self.os_data.epollfd, - posix.EPOLL_CTL_ADD, - self.os_data.final_eventfd, - &self.os_data.final_eventfd_event, - ); - - var extra_thread_index: usize = 0; - errdefer { - // writing 8 bytes to an eventfd cannot fail - std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; - while (extra_thread_index != 0) { - extra_thread_index -= 1; - self.extra_threads[extra_thread_index].wait(); - } - } - while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { - self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); - } - }, - builtin.Os.macosx => { - self.os_data.kqfd = try std.os.bsdKQueue(); - errdefer std.os.close(self.os_data.kqfd); - - self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count); - errdefer self.allocator.free(self.os_data.kevents); - - const eventlist = ([*]posix.Kevent)(undefined)[0..0]; - - for (self.eventfd_resume_nodes) |*eventfd_node, i| { - eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ - .data = ResumeNode.EventFd{ - .base = ResumeNode{ - .id = ResumeNode.Id.EventFd, - .handle = undefined, - }, - // this one is for sending events - .kevent = posix.Kevent{ - .ident = i, - .filter = posix.EVFILT_USER, - .flags = posix.EV_CLEAR | posix.EV_ADD | posix.EV_DISABLE, - .fflags = 0, - .data = 0, - .udata = @ptrToInt(&eventfd_node.data.base), - }, - }, - .next = undefined, - }; - self.available_eventfd_resume_nodes.push(eventfd_node); - const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent); - _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null); - eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE; - eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER; - // this one is for waiting for events - self.os_data.kevents[i] = posix.Kevent{ - .ident = i, - .filter = posix.EVFILT_USER, - .flags = 0, - .fflags = 0, - .data = 0, - .udata = @ptrToInt(&eventfd_node.data.base), - }; - } - - // Pre-add so that we cannot get error.SystemResources - // later when we try to activate it. - self.os_data.final_kevent = posix.Kevent{ - .ident = extra_thread_count, - .filter = posix.EVFILT_USER, - .flags = posix.EV_ADD | posix.EV_DISABLE, - .fflags = 0, - .data = 0, - .udata = @ptrToInt(&self.final_resume_node), - }; - const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent); - _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null); - self.os_data.final_kevent.flags = posix.EV_ENABLE; - self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER; - - var extra_thread_index: usize = 0; - errdefer { - _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch unreachable; - while (extra_thread_index != 0) { - extra_thread_index -= 1; - self.extra_threads[extra_thread_index].wait(); - } - } - while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { - self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); - } - }, - builtin.Os.windows => { - self.os_data.extra_thread_count = extra_thread_count; - - self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( - windows.INVALID_HANDLE_VALUE, - null, - undefined, - undefined, - ); - errdefer std.os.close(self.os_data.io_port); - - for (self.eventfd_resume_nodes) |*eventfd_node, i| { - eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ - .data = ResumeNode.EventFd{ - .base = ResumeNode{ - .id = ResumeNode.Id.EventFd, - .handle = undefined, - }, - // this one is for sending events - .completion_key = @ptrToInt(&eventfd_node.data.base), - }, - .next = undefined, - }; - self.available_eventfd_resume_nodes.push(eventfd_node); - } - - var extra_thread_index: usize = 0; - errdefer { - var i: usize = 0; - while (i < extra_thread_index) : (i += 1) { - while (true) { - const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); - std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; - break; - } - } - while (extra_thread_index != 0) { - extra_thread_index -= 1; - self.extra_threads[extra_thread_index].wait(); - } - } - while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { - self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); - } - }, - else => {}, - } - } - - fn deinitOsData(self: *Loop) void { - switch (builtin.os) { - builtin.Os.linux => { - std.os.close(self.os_data.final_eventfd); - while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); - std.os.close(self.os_data.epollfd); - self.allocator.free(self.eventfd_resume_nodes); - }, - builtin.Os.macosx => { - self.allocator.free(self.os_data.kevents); - std.os.close(self.os_data.kqfd); - }, - builtin.Os.windows => { - std.os.close(self.os_data.io_port); - }, - else => {}, - } - } - - /// resume_node must live longer than the promise that it holds a reference to. - pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void { - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); - errdefer { - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - } - try self.modFd( - fd, - posix.EPOLL_CTL_ADD, - std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, - resume_node, - ); - } - - pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void { - var ev = std.os.linux.epoll_event{ - .events = events, - .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) }, - }; - try std.os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev); - } - - pub fn removeFd(self: *Loop, fd: i32) void { - self.removeFdNoCounter(fd); - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - } - - fn removeFdNoCounter(self: *Loop, fd: i32) void { - std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; - } - - pub async fn waitFd(self: *Loop, fd: i32) !void { - defer self.removeFd(fd); - suspend |p| { - // TODO explicitly put this memory in the coroutine frame #1194 - var resume_node = ResumeNode{ - .id = ResumeNode.Id.Basic, - .handle = p, - }; - try self.addFd(fd, &resume_node); - } - } - - /// Bring your own linked list node. This means it can't fail. - pub fn onNextTick(self: *Loop, node: *NextTickNode) void { - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); - self.next_tick_queue.put(node); - } - - pub fn run(self: *Loop) void { - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - self.workerRun(); - for (self.extra_threads) |extra_thread| { - extra_thread.wait(); - } - } - - fn workerRun(self: *Loop) void { - start_over: while (true) { - if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { - while (self.next_tick_queue.get()) |next_tick_node| { - const handle = next_tick_node.data; - if (self.next_tick_queue.isEmpty()) { - // last node, just resume it - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - resume handle; - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - continue :start_over; - } - - // non-last node, stick it in the epoll/kqueue set so that - // other threads can get to it - if (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| { - const eventfd_node = &resume_stack_node.data; - eventfd_node.base.handle = handle; - switch (builtin.os) { - builtin.Os.macosx => { - const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); - const eventlist = ([*]posix.Kevent)(undefined)[0..0]; - _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch { - // fine, we didn't need it anyway - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - self.available_eventfd_resume_nodes.push(resume_stack_node); - resume handle; - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - continue :start_over; - }; - }, - builtin.Os.linux => { - // the pending count is already accounted for - const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET; - self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch { - // fine, we didn't need it anyway - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - self.available_eventfd_resume_nodes.push(resume_stack_node); - resume handle; - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - continue :start_over; - }; - }, - builtin.Os.windows => { - // this value is never dereferenced but we need it to be non-null so that - // the consumer code can decide whether to read the completion key. - // it has to do this for normal I/O, so we match that behavior here. - const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); - std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, eventfd_node.completion_key, overlapped) catch { - // fine, we didn't need it anyway - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - self.available_eventfd_resume_nodes.push(resume_stack_node); - resume handle; - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - continue :start_over; - }; - }, - else => @compileError("unsupported OS"), - } - } else { - // threads are too busy, can't add another eventfd to wake one up - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - resume handle; - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - continue :start_over; - } - } - - const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst); - if (pending_event_count == 0) { - // cause all the threads to stop - switch (builtin.os) { - builtin.Os.linux => { - // writing 8 bytes to an eventfd cannot fail - std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; - return; - }, - builtin.Os.macosx => { - const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent); - const eventlist = ([*]posix.Kevent)(undefined)[0..0]; - // cannot fail because we already added it and this just enables it - _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable; - return; - }, - builtin.Os.windows => { - var i: usize = 0; - while (i < self.os_data.extra_thread_count) : (i += 1) { - while (true) { - const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); - std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; - break; - } - } - return; - }, - else => @compileError("unsupported OS"), - } - } - - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - } - - switch (builtin.os) { - builtin.Os.linux => { - // only process 1 event so we don't steal from other threads - var events: [1]std.os.linux.epoll_event = undefined; - const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); - for (events[0..count]) |ev| { - const resume_node = @intToPtr(*ResumeNode, ev.data.ptr); - const handle = resume_node.handle; - const resume_node_id = resume_node.id; - switch (resume_node_id) { - ResumeNode.Id.Basic => {}, - ResumeNode.Id.Stop => return, - ResumeNode.Id.EventFd => { - const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); - event_fd_node.epoll_op = posix.EPOLL_CTL_MOD; - const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); - self.available_eventfd_resume_nodes.push(stack_node); - }, - } - resume handle; - if (resume_node_id == ResumeNode.Id.EventFd) { - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - } - } - }, - builtin.Os.macosx => { - var eventlist: [1]posix.Kevent = undefined; - const count = std.os.bsdKEvent(self.os_data.kqfd, self.os_data.kevents, eventlist[0..], null) catch unreachable; - for (eventlist[0..count]) |ev| { - const resume_node = @intToPtr(*ResumeNode, ev.udata); - const handle = resume_node.handle; - const resume_node_id = resume_node.id; - switch (resume_node_id) { - ResumeNode.Id.Basic => {}, - ResumeNode.Id.Stop => return, - ResumeNode.Id.EventFd => { - const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); - const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); - self.available_eventfd_resume_nodes.push(stack_node); - }, - } - resume handle; - if (resume_node_id == ResumeNode.Id.EventFd) { - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - } - } - }, - builtin.Os.windows => { - var completion_key: usize = undefined; - while (true) { - var nbytes: windows.DWORD = undefined; - var overlapped: ?*windows.OVERLAPPED = undefined; - switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { - std.os.WindowsWaitResult.Aborted => return, - std.os.WindowsWaitResult.Normal => {}, - } - if (overlapped != null) break; - } - const resume_node = @intToPtr(*ResumeNode, completion_key); - const handle = resume_node.handle; - const resume_node_id = resume_node.id; - switch (resume_node_id) { - ResumeNode.Id.Basic => {}, - ResumeNode.Id.Stop => return, - ResumeNode.Id.EventFd => { - const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); - const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); - self.available_eventfd_resume_nodes.push(stack_node); - }, - } - resume handle; - if (resume_node_id == ResumeNode.Id.EventFd) { - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - } - }, - else => @compileError("unsupported OS"), - } - } - } - - const OsData = switch (builtin.os) { - builtin.Os.linux => struct { - epollfd: i32, - final_eventfd: i32, - final_eventfd_event: std.os.linux.epoll_event, - }, - builtin.Os.macosx => MacOsData, - builtin.Os.windows => struct { - io_port: windows.HANDLE, - extra_thread_count: usize, - }, - else => struct {}, - }; - - const MacOsData = struct { - kqfd: i32, - final_kevent: posix.Kevent, - kevents: []posix.Kevent, - }; -}; - -/// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size -/// when buffer is empty, consumers suspend and are resumed by producers -/// when buffer is full, producers suspend and are resumed by consumers -pub fn Channel(comptime T: type) type { - return struct { - loop: *Loop, - - getters: std.atomic.QueueMpsc(GetNode), - putters: std.atomic.QueueMpsc(PutNode), - get_count: usize, - put_count: usize, - dispatch_lock: u8, // TODO make this a bool - need_dispatch: u8, // TODO make this a bool - - // simple fixed size ring buffer - buffer_nodes: []T, - buffer_index: usize, - buffer_len: usize, - - const SelfChannel = this; - const GetNode = struct { - ptr: *T, - tick_node: *Loop.NextTickNode, - }; - const PutNode = struct { - data: T, - tick_node: *Loop.NextTickNode, - }; - - /// call destroy when done - pub fn create(loop: *Loop, capacity: usize) !*SelfChannel { - const buffer_nodes = try loop.allocator.alloc(T, capacity); - errdefer loop.allocator.free(buffer_nodes); - - const self = try loop.allocator.create(SelfChannel{ - .loop = loop, - .buffer_len = 0, - .buffer_nodes = buffer_nodes, - .buffer_index = 0, - .dispatch_lock = 0, - .need_dispatch = 0, - .getters = std.atomic.QueueMpsc(GetNode).init(), - .putters = std.atomic.QueueMpsc(PutNode).init(), - .get_count = 0, - .put_count = 0, - }); - errdefer loop.allocator.destroy(self); - - return self; - } - - /// must be called when all calls to put and get have suspended and no more calls occur - pub fn destroy(self: *SelfChannel) void { - while (self.getters.get()) |get_node| { - cancel get_node.data.tick_node.data; - } - while (self.putters.get()) |put_node| { - cancel put_node.data.tick_node.data; - } - self.loop.allocator.free(self.buffer_nodes); - self.loop.allocator.destroy(self); - } - - /// puts a data item in the channel. The promise completes when the value has been added to the - /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. - pub async fn put(self: *SelfChannel, data: T) void { - // TODO should be able to group memory allocation failure before first suspend point - // so that the async invocation catches it - var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined; - _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable; - - suspend |handle| { - var my_tick_node = Loop.NextTickNode{ - .next = undefined, - .data = handle, - }; - var queue_node = std.atomic.QueueMpsc(PutNode).Node{ - .data = PutNode{ - .tick_node = &my_tick_node, - .data = data, - }, - .next = undefined, - }; - self.putters.put(&queue_node); - _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); - - self.loop.onNextTick(dispatch_tick_node_ptr); - } - } - - /// await this function to get an item from the channel. If the buffer is empty, the promise will - /// complete when the next item is put in the channel. - pub async fn get(self: *SelfChannel) T { - // TODO should be able to group memory allocation failure before first suspend point - // so that the async invocation catches it - var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined; - _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable; - - // TODO integrate this function with named return values - // so we can get rid of this extra result copy - var result: T = undefined; - suspend |handle| { - var my_tick_node = Loop.NextTickNode{ - .next = undefined, - .data = handle, - }; - var queue_node = std.atomic.QueueMpsc(GetNode).Node{ - .data = GetNode{ - .ptr = &result, - .tick_node = &my_tick_node, - }, - .next = undefined, - }; - self.getters.put(&queue_node); - _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); - - self.loop.onNextTick(dispatch_tick_node_ptr); - } - return result; - } - - async fn dispatch(self: *SelfChannel, tick_node_ptr: **Loop.NextTickNode) void { - // resumed by onNextTick - suspend |handle| { - var tick_node = Loop.NextTickNode{ - .data = handle, - .next = undefined, - }; - tick_node_ptr.* = &tick_node; - } - - // set the "need dispatch" flag - _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - - lock: while (true) { - // set the lock flag - const prev_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - if (prev_lock != 0) return; - - // clear the need_dispatch flag since we're about to do it - _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - - while (true) { - one_dispatch: { - // later we correct these extra subtractions - var get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - var put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - - // transfer self.buffer to self.getters - while (self.buffer_len != 0) { - if (get_count == 0) break :one_dispatch; - - const get_node = &self.getters.get().?.data; - get_node.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len]; - self.loop.onNextTick(get_node.tick_node); - self.buffer_len -= 1; - - get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - } - - // direct transfer self.putters to self.getters - while (get_count != 0 and put_count != 0) { - const get_node = &self.getters.get().?.data; - const put_node = &self.putters.get().?.data; - - get_node.ptr.* = put_node.data; - self.loop.onNextTick(get_node.tick_node); - self.loop.onNextTick(put_node.tick_node); - - get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - } - - // transfer self.putters to self.buffer - while (self.buffer_len != self.buffer_nodes.len and put_count != 0) { - const put_node = &self.putters.get().?.data; - - self.buffer_nodes[self.buffer_index] = put_node.data; - self.loop.onNextTick(put_node.tick_node); - self.buffer_index +%= 1; - self.buffer_len += 1; - - put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - } - } - - // undo the extra subtractions - _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); - _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); - - // clear need-dispatch flag - const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - if (need_dispatch != 0) continue; - - const my_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - assert(my_lock != 0); - - // we have to check again now that we unlocked - if (@atomicLoad(u8, &self.need_dispatch, AtomicOrder.SeqCst) != 0) continue :lock; - - return; - } - } - } - }; -} - -pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File { - var address = _address.*; // TODO https://github.com/ziglang/zig/issues/733 - - const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); - errdefer std.os.close(sockfd); - - try std.os.posixConnectAsync(sockfd, &address.os_addr); - try await try async loop.waitFd(sockfd); - try std.os.posixGetSockOptConnectError(sockfd); - - return std.os.File.openHandle(sockfd); -} - -test "listen on a port, send bytes, receive bytes" { - if (builtin.os != builtin.Os.linux) { - // TODO build abstractions for other operating systems - return; - } - const MyServer = struct { - tcp_server: TcpServer, - - const Self = this; - async<*mem.Allocator> fn handler(tcp_server: *TcpServer, _addr: *const std.net.Address, _socket: *const std.os.File) void { - const self = @fieldParentPtr(Self, "tcp_server", tcp_server); - var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 - defer socket.close(); - // TODO guarantee elision of this allocation - const next_handler = async errorableHandler(self, _addr, socket) catch unreachable; - (await next_handler) catch |err| { - std.debug.panic("unable to handle connection: {}\n", err); - }; - suspend |p| { - cancel p; - } - } - async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void { - const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733 - var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 - - var adapter = std.io.FileOutStream.init(&socket); - var stream = &adapter.stream; - try stream.print("hello from server\n"); - } - }; - - const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable; - const addr = std.net.Address.initIp4(ip4addr, 0); - - var loop: Loop = undefined; - try loop.initSingleThreaded(std.debug.global_allocator); - var server = MyServer{ .tcp_server = TcpServer.init(&loop) }; - defer server.tcp_server.deinit(); - try server.tcp_server.listen(addr, MyServer.handler); - - const p = try async doAsyncTest(&loop, server.tcp_server.listen_address, &server.tcp_server); - defer cancel p; - loop.run(); -} - -async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *TcpServer) void { - errdefer @panic("test failure"); - - var socket_file = try await try async event.connect(loop, address); - defer socket_file.close(); - - var buf: [512]u8 = undefined; - const amt_read = try socket_file.read(buf[0..]); - const msg = buf[0..amt_read]; - assert(mem.eql(u8, msg, "hello from server\n")); - server.close(); -} - -test "std.event.Channel" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const allocator = &da.allocator; - - var loop: Loop = undefined; - // TODO make a multi threaded test - try loop.initSingleThreaded(allocator); - defer loop.deinit(); - - const channel = try Channel(i32).create(&loop, 0); - defer channel.destroy(); - - const handle = try async testChannelGetter(&loop, channel); - defer cancel handle; - - const putter = try async testChannelPutter(channel); - defer cancel putter; - - loop.run(); -} - -async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void { - errdefer @panic("test failed"); - - const value1_promise = try async channel.get(); - const value1 = await value1_promise; - assert(value1 == 1234); - - const value2_promise = try async channel.get(); - const value2 = await value2_promise; - assert(value2 == 4567); -} - -async fn testChannelPutter(channel: *Channel(i32)) void { - await (async channel.put(1234) catch @panic("out of memory")); - await (async channel.put(4567) catch @panic("out of memory")); -} - -/// Thread-safe async/await lock. -/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and -/// are resumed when the lock is released, in order. -pub const Lock = struct { - loop: *Loop, - shared_bit: u8, // TODO make this a bool - queue: Queue, - queue_empty_bit: u8, // TODO make this a bool - - const Queue = std.atomic.QueueMpsc(promise); - - pub const Held = struct { - lock: *Lock, - - pub fn release(self: Held) void { - // Resume the next item from the queue. - if (self.lock.queue.get()) |node| { - self.lock.loop.onNextTick(node); - return; - } - - // We need to release the lock. - _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - - // There might be a queue item. If we know the queue is empty, we can be done, - // because the other actor will try to obtain the lock. - // But if there's a queue item, we are the actor which must loop and attempt - // to grab the lock again. - if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) { - return; - } - - while (true) { - const old_bit = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - if (old_bit != 0) { - // We did not obtain the lock. Great, the queue is someone else's problem. - return; - } - - // Resume the next item from the queue. - if (self.lock.queue.get()) |node| { - self.lock.loop.onNextTick(node); - return; - } - - // Release the lock again. - _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - - // Find out if we can be done. - if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) { - return; - } - } - } - }; - - pub fn init(loop: *Loop) Lock { - return Lock{ - .loop = loop, - .shared_bit = 0, - .queue = Queue.init(), - .queue_empty_bit = 1, - }; - } - - /// Must be called when not locked. Not thread safe. - /// All calls to acquire() and release() must complete before calling deinit(). - pub fn deinit(self: *Lock) void { - assert(self.shared_bit == 0); - while (self.queue.get()) |node| cancel node.data; - } - - pub async fn acquire(self: *Lock) Held { - s: suspend |handle| { - // TODO explicitly put this memory in the coroutine frame #1194 - var my_tick_node = Loop.NextTickNode{ - .data = handle, - .next = undefined, - }; - - self.queue.put(&my_tick_node); - - // At this point, we are in the queue, so we might have already been resumed and this coroutine - // frame might be destroyed. For the rest of the suspend block we cannot access the coroutine frame. - - // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor - // will attempt to grab the lock. - _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - - while (true) { - const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - if (old_bit != 0) { - // We did not obtain the lock. Trust that our queue entry will resume us, and allow - // suspend to complete. - break; - } - // We got the lock. However we might have already been resumed from the queue. - if (self.queue.get()) |node| { - // Whether this node is us or someone else, we tail resume it. - resume node.data; - break; - } else { - // We already got resumed, and there are none left in the queue, which means that - // we aren't even supposed to hold the lock right now. - _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - _ = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - - // There might be a queue item. If we know the queue is empty, we can be done, - // because the other actor will try to obtain the lock. - // But if there's a queue item, we are the actor which must loop and attempt - // to grab the lock again. - if (@atomicLoad(u8, &self.queue_empty_bit, AtomicOrder.SeqCst) == 1) { - break; - } else { - continue; - } - } - unreachable; - } - } - - return Held{ .lock = self }; - } -}; - -/// Thread-safe async/await lock that protects one piece of data. -/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and -/// are resumed when the lock is released, in order. -pub fn Locked(comptime T: type) type { - return struct { - lock: Lock, - private_data: T, - - const Self = this; - - pub const HeldLock = struct { - value: *T, - held: Lock.Held, - - pub fn release(self: HeldLock) void { - self.held.release(); - } - }; - - pub fn init(loop: *Loop, data: T) Self { - return Self{ - .lock = Lock.init(loop), - .private_data = data, - }; - } - - pub fn deinit(self: *Self) void { - self.lock.deinit(); - } - - pub async fn acquire(self: *Self) HeldLock { - return HeldLock{ - // TODO guaranteed allocation elision - .held = await (async self.lock.acquire() catch unreachable), - .value = &self.private_data, - }; - } - }; -} - -test "std.event.Lock" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const allocator = &da.allocator; - - var loop: Loop = undefined; - try loop.initMultiThreaded(allocator); - defer loop.deinit(); - - var lock = Lock.init(&loop); - defer lock.deinit(); - - const handle = try async testLock(&loop, &lock); - defer cancel handle; - loop.run(); - - assert(mem.eql(i32, shared_test_data, [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len)); -} - -async fn testLock(loop: *Loop, lock: *Lock) void { - // TODO explicitly put next tick node memory in the coroutine frame #1194 - suspend |p| { - resume p; - } - const handle1 = async lockRunner(lock) catch @panic("out of memory"); - var tick_node1 = Loop.NextTickNode{ - .next = undefined, - .data = handle1, - }; - loop.onNextTick(&tick_node1); - - const handle2 = async lockRunner(lock) catch @panic("out of memory"); - var tick_node2 = Loop.NextTickNode{ - .next = undefined, - .data = handle2, - }; - loop.onNextTick(&tick_node2); - - const handle3 = async lockRunner(lock) catch @panic("out of memory"); - var tick_node3 = Loop.NextTickNode{ - .next = undefined, - .data = handle3, - }; - loop.onNextTick(&tick_node3); - - await handle1; - await handle2; - await handle3; -} - -var shared_test_data = [1]i32{0} ** 10; -var shared_test_index: usize = 0; - -async fn lockRunner(lock: *Lock) void { - suspend; // resumed by onNextTick - - var i: usize = 0; - while (i < shared_test_data.len) : (i += 1) { - const lock_promise = async lock.acquire() catch @panic("out of memory"); - const handle = await lock_promise; - defer handle.release(); - - shared_test_index = 0; - while (shared_test_index < shared_test_data.len) : (shared_test_index += 1) { - shared_test_data[shared_test_index] = shared_test_data[shared_test_index] + 1; - } - } +pub const Locked = @import("event/locked.zig").Locked; +pub const Loop = @import("event/loop.zig").Loop; +pub const Lock = @import("event/lock.zig").Lock; +pub const tcp = @import("event/tcp.zig"); +pub const Channel = @import("event/channel.zig").Channel; + +test "import event tests" { + _ = @import("event/locked.zig"); + _ = @import("event/loop.zig"); + _ = @import("event/lock.zig"); + _ = @import("event/tcp.zig"); + _ = @import("event/channel.zig"); } diff --git a/std/event/channel.zig b/std/event/channel.zig new file mode 100644 index 0000000000..4b3a7177a2 --- /dev/null +++ b/std/event/channel.zig @@ -0,0 +1,254 @@ +const std = @import("../index.zig"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; +const Loop = std.event.Loop; + +/// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size +/// when buffer is empty, consumers suspend and are resumed by producers +/// when buffer is full, producers suspend and are resumed by consumers +pub fn Channel(comptime T: type) type { + return struct { + loop: *Loop, + + getters: std.atomic.QueueMpsc(GetNode), + putters: std.atomic.QueueMpsc(PutNode), + get_count: usize, + put_count: usize, + dispatch_lock: u8, // TODO make this a bool + need_dispatch: u8, // TODO make this a bool + + // simple fixed size ring buffer + buffer_nodes: []T, + buffer_index: usize, + buffer_len: usize, + + const SelfChannel = this; + const GetNode = struct { + ptr: *T, + tick_node: *Loop.NextTickNode, + }; + const PutNode = struct { + data: T, + tick_node: *Loop.NextTickNode, + }; + + /// call destroy when done + pub fn create(loop: *Loop, capacity: usize) !*SelfChannel { + const buffer_nodes = try loop.allocator.alloc(T, capacity); + errdefer loop.allocator.free(buffer_nodes); + + const self = try loop.allocator.create(SelfChannel{ + .loop = loop, + .buffer_len = 0, + .buffer_nodes = buffer_nodes, + .buffer_index = 0, + .dispatch_lock = 0, + .need_dispatch = 0, + .getters = std.atomic.QueueMpsc(GetNode).init(), + .putters = std.atomic.QueueMpsc(PutNode).init(), + .get_count = 0, + .put_count = 0, + }); + errdefer loop.allocator.destroy(self); + + return self; + } + + /// must be called when all calls to put and get have suspended and no more calls occur + pub fn destroy(self: *SelfChannel) void { + while (self.getters.get()) |get_node| { + cancel get_node.data.tick_node.data; + } + while (self.putters.get()) |put_node| { + cancel put_node.data.tick_node.data; + } + self.loop.allocator.free(self.buffer_nodes); + self.loop.allocator.destroy(self); + } + + /// puts a data item in the channel. The promise completes when the value has been added to the + /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. + pub async fn put(self: *SelfChannel, data: T) void { + // TODO should be able to group memory allocation failure before first suspend point + // so that the async invocation catches it + var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined; + _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable; + + suspend |handle| { + var my_tick_node = Loop.NextTickNode{ + .next = undefined, + .data = handle, + }; + var queue_node = std.atomic.QueueMpsc(PutNode).Node{ + .data = PutNode{ + .tick_node = &my_tick_node, + .data = data, + }, + .next = undefined, + }; + self.putters.put(&queue_node); + _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + + self.loop.onNextTick(dispatch_tick_node_ptr); + } + } + + /// await this function to get an item from the channel. If the buffer is empty, the promise will + /// complete when the next item is put in the channel. + pub async fn get(self: *SelfChannel) T { + // TODO should be able to group memory allocation failure before first suspend point + // so that the async invocation catches it + var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined; + _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable; + + // TODO integrate this function with named return values + // so we can get rid of this extra result copy + var result: T = undefined; + suspend |handle| { + var my_tick_node = Loop.NextTickNode{ + .next = undefined, + .data = handle, + }; + var queue_node = std.atomic.QueueMpsc(GetNode).Node{ + .data = GetNode{ + .ptr = &result, + .tick_node = &my_tick_node, + }, + .next = undefined, + }; + self.getters.put(&queue_node); + _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + + self.loop.onNextTick(dispatch_tick_node_ptr); + } + return result; + } + + async fn dispatch(self: *SelfChannel, tick_node_ptr: **Loop.NextTickNode) void { + // resumed by onNextTick + suspend |handle| { + var tick_node = Loop.NextTickNode{ + .data = handle, + .next = undefined, + }; + tick_node_ptr.* = &tick_node; + } + + // set the "need dispatch" flag + _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + + lock: while (true) { + // set the lock flag + const prev_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + if (prev_lock != 0) return; + + // clear the need_dispatch flag since we're about to do it + _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + while (true) { + one_dispatch: { + // later we correct these extra subtractions + var get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + var put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + + // transfer self.buffer to self.getters + while (self.buffer_len != 0) { + if (get_count == 0) break :one_dispatch; + + const get_node = &self.getters.get().?.data; + get_node.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len]; + self.loop.onNextTick(get_node.tick_node); + self.buffer_len -= 1; + + get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + + // direct transfer self.putters to self.getters + while (get_count != 0 and put_count != 0) { + const get_node = &self.getters.get().?.data; + const put_node = &self.putters.get().?.data; + + get_node.ptr.* = put_node.data; + self.loop.onNextTick(get_node.tick_node); + self.loop.onNextTick(put_node.tick_node); + + get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + + // transfer self.putters to self.buffer + while (self.buffer_len != self.buffer_nodes.len and put_count != 0) { + const put_node = &self.putters.get().?.data; + + self.buffer_nodes[self.buffer_index] = put_node.data; + self.loop.onNextTick(put_node.tick_node); + self.buffer_index +%= 1; + self.buffer_len += 1; + + put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + } + + // undo the extra subtractions + _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + + // clear need-dispatch flag + const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + if (need_dispatch != 0) continue; + + const my_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + assert(my_lock != 0); + + // we have to check again now that we unlocked + if (@atomicLoad(u8, &self.need_dispatch, AtomicOrder.SeqCst) != 0) continue :lock; + + return; + } + } + } + }; +} + +test "std.event.Channel" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const allocator = &da.allocator; + + var loop: Loop = undefined; + // TODO make a multi threaded test + try loop.initSingleThreaded(allocator); + defer loop.deinit(); + + const channel = try Channel(i32).create(&loop, 0); + defer channel.destroy(); + + const handle = try async testChannelGetter(&loop, channel); + defer cancel handle; + + const putter = try async testChannelPutter(channel); + defer cancel putter; + + loop.run(); +} + +async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void { + errdefer @panic("test failed"); + + const value1_promise = try async channel.get(); + const value1 = await value1_promise; + assert(value1 == 1234); + + const value2_promise = try async channel.get(); + const value2 = await value2_promise; + assert(value2 == 4567); +} + +async fn testChannelPutter(channel: *Channel(i32)) void { + await (async channel.put(1234) catch @panic("out of memory")); + await (async channel.put(4567) catch @panic("out of memory")); +} + diff --git a/std/event/lock.zig b/std/event/lock.zig new file mode 100644 index 0000000000..2a8d5ada77 --- /dev/null +++ b/std/event/lock.zig @@ -0,0 +1,204 @@ +const std = @import("../index.zig"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const mem = std.mem; +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; +const Loop = std.event.Loop; + +/// Thread-safe async/await lock. +/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and +/// are resumed when the lock is released, in order. +pub const Lock = struct { + loop: *Loop, + shared_bit: u8, // TODO make this a bool + queue: Queue, + queue_empty_bit: u8, // TODO make this a bool + + const Queue = std.atomic.QueueMpsc(promise); + + pub const Held = struct { + lock: *Lock, + + pub fn release(self: Held) void { + // Resume the next item from the queue. + if (self.lock.queue.get()) |node| { + self.lock.loop.onNextTick(node); + return; + } + + // We need to release the lock. + _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + // There might be a queue item. If we know the queue is empty, we can be done, + // because the other actor will try to obtain the lock. + // But if there's a queue item, we are the actor which must loop and attempt + // to grab the lock again. + if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) { + return; + } + + while (true) { + const old_bit = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + if (old_bit != 0) { + // We did not obtain the lock. Great, the queue is someone else's problem. + return; + } + + // Resume the next item from the queue. + if (self.lock.queue.get()) |node| { + self.lock.loop.onNextTick(node); + return; + } + + // Release the lock again. + _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + // Find out if we can be done. + if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) { + return; + } + } + } + }; + + pub fn init(loop: *Loop) Lock { + return Lock{ + .loop = loop, + .shared_bit = 0, + .queue = Queue.init(), + .queue_empty_bit = 1, + }; + } + + /// Must be called when not locked. Not thread safe. + /// All calls to acquire() and release() must complete before calling deinit(). + pub fn deinit(self: *Lock) void { + assert(self.shared_bit == 0); + while (self.queue.get()) |node| cancel node.data; + } + + pub async fn acquire(self: *Lock) Held { + s: suspend |handle| { + // TODO explicitly put this memory in the coroutine frame #1194 + var my_tick_node = Loop.NextTickNode{ + .data = handle, + .next = undefined, + }; + + self.queue.put(&my_tick_node); + + // At this point, we are in the queue, so we might have already been resumed and this coroutine + // frame might be destroyed. For the rest of the suspend block we cannot access the coroutine frame. + + // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor + // will attempt to grab the lock. + _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + while (true) { + const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + if (old_bit != 0) { + // We did not obtain the lock. Trust that our queue entry will resume us, and allow + // suspend to complete. + break; + } + // We got the lock. However we might have already been resumed from the queue. + if (self.queue.get()) |node| { + // Whether this node is us or someone else, we tail resume it. + resume node.data; + break; + } else { + // We already got resumed, and there are none left in the queue, which means that + // we aren't even supposed to hold the lock right now. + _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + _ = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + // There might be a queue item. If we know the queue is empty, we can be done, + // because the other actor will try to obtain the lock. + // But if there's a queue item, we are the actor which must loop and attempt + // to grab the lock again. + if (@atomicLoad(u8, &self.queue_empty_bit, AtomicOrder.SeqCst) == 1) { + break; + } else { + continue; + } + } + unreachable; + } + } + + return Held{ .lock = self }; + } +}; + +test "std.event.Lock" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const allocator = &da.allocator; + + var loop: Loop = undefined; + try loop.initMultiThreaded(allocator); + defer loop.deinit(); + + var lock = Lock.init(&loop); + defer lock.deinit(); + + const handle = try async testLock(&loop, &lock); + defer cancel handle; + loop.run(); + + assert(mem.eql(i32, shared_test_data, [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len)); +} + +async fn testLock(loop: *Loop, lock: *Lock) void { + // TODO explicitly put next tick node memory in the coroutine frame #1194 + suspend |p| { + resume p; + } + const handle1 = async lockRunner(lock) catch @panic("out of memory"); + var tick_node1 = Loop.NextTickNode{ + .next = undefined, + .data = handle1, + }; + loop.onNextTick(&tick_node1); + + const handle2 = async lockRunner(lock) catch @panic("out of memory"); + var tick_node2 = Loop.NextTickNode{ + .next = undefined, + .data = handle2, + }; + loop.onNextTick(&tick_node2); + + const handle3 = async lockRunner(lock) catch @panic("out of memory"); + var tick_node3 = Loop.NextTickNode{ + .next = undefined, + .data = handle3, + }; + loop.onNextTick(&tick_node3); + + await handle1; + await handle2; + await handle3; +} + +var shared_test_data = [1]i32{0} ** 10; +var shared_test_index: usize = 0; + +async fn lockRunner(lock: *Lock) void { + suspend; // resumed by onNextTick + + var i: usize = 0; + while (i < shared_test_data.len) : (i += 1) { + const lock_promise = async lock.acquire() catch @panic("out of memory"); + const handle = await lock_promise; + defer handle.release(); + + shared_test_index = 0; + while (shared_test_index < shared_test_data.len) : (shared_test_index += 1) { + shared_test_data[shared_test_index] = shared_test_data[shared_test_index] + 1; + } + } +} diff --git a/std/event/locked.zig b/std/event/locked.zig new file mode 100644 index 0000000000..41ab112aff --- /dev/null +++ b/std/event/locked.zig @@ -0,0 +1,42 @@ +const std = @import("../index.zig"); +const Lock = std.event.Lock; + +/// Thread-safe async/await lock that protects one piece of data. +/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and +/// are resumed when the lock is released, in order. +pub fn Locked(comptime T: type) type { + return struct { + lock: Lock, + private_data: T, + + const Self = this; + + pub const HeldLock = struct { + value: *T, + held: Lock.Held, + + pub fn release(self: HeldLock) void { + self.held.release(); + } + }; + + pub fn init(loop: *Loop, data: T) Self { + return Self{ + .lock = Lock.init(loop), + .private_data = data, + }; + } + + pub fn deinit(self: *Self) void { + self.lock.deinit(); + } + + pub async fn acquire(self: *Self) HeldLock { + return HeldLock{ + // TODO guaranteed allocation elision + .held = await (async self.lock.acquire() catch unreachable), + .value = &self.private_data, + }; + } + }; +} diff --git a/std/event/loop.zig b/std/event/loop.zig new file mode 100644 index 0000000000..61c7f80cdc --- /dev/null +++ b/std/event/loop.zig @@ -0,0 +1,577 @@ +const std = @import("../index.zig"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const mem = std.mem; +const posix = std.os.posix; +const windows = std.os.windows; +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; + +pub const Loop = struct { + allocator: *mem.Allocator, + next_tick_queue: std.atomic.QueueMpsc(promise), + os_data: OsData, + final_resume_node: ResumeNode, + dispatch_lock: u8, // TODO make this a bool + pending_event_count: usize, + extra_threads: []*std.os.Thread, + + // pre-allocated eventfds. all permanently active. + // this is how we send promises to be resumed on other threads. + available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd), + eventfd_resume_nodes: []std.atomic.Stack(ResumeNode.EventFd).Node, + + pub const NextTickNode = std.atomic.QueueMpsc(promise).Node; + + pub const ResumeNode = struct { + id: Id, + handle: promise, + + pub const Id = enum { + Basic, + Stop, + EventFd, + }; + + pub const EventFd = switch (builtin.os) { + builtin.Os.macosx => MacOsEventFd, + builtin.Os.linux => struct { + base: ResumeNode, + epoll_op: u32, + eventfd: i32, + }, + builtin.Os.windows => struct { + base: ResumeNode, + completion_key: usize, + }, + else => @compileError("unsupported OS"), + }; + + const MacOsEventFd = struct { + base: ResumeNode, + kevent: posix.Kevent, + }; + }; + + /// After initialization, call run(). + /// TODO copy elision / named return values so that the threads referencing *Loop + /// have the correct pointer value. + fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void { + return self.initInternal(allocator, 1); + } + + /// The allocator must be thread-safe because we use it for multiplexing + /// coroutines onto kernel threads. + /// After initialization, call run(). + /// TODO copy elision / named return values so that the threads referencing *Loop + /// have the correct pointer value. + fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { + const core_count = try std.os.cpuCount(allocator); + return self.initInternal(allocator, core_count); + } + + /// Thread count is the total thread count. The thread pool size will be + /// max(thread_count - 1, 0) + fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void { + self.* = Loop{ + .pending_event_count = 0, + .allocator = allocator, + .os_data = undefined, + .next_tick_queue = std.atomic.QueueMpsc(promise).init(), + .dispatch_lock = 1, // start locked so threads go directly into epoll wait + .extra_threads = undefined, + .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(), + .eventfd_resume_nodes = undefined, + .final_resume_node = ResumeNode{ + .id = ResumeNode.Id.Stop, + .handle = undefined, + }, + }; + const extra_thread_count = thread_count - 1; + self.eventfd_resume_nodes = try self.allocator.alloc( + std.atomic.Stack(ResumeNode.EventFd).Node, + extra_thread_count, + ); + errdefer self.allocator.free(self.eventfd_resume_nodes); + + self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count); + errdefer self.allocator.free(self.extra_threads); + + try self.initOsData(extra_thread_count); + errdefer self.deinitOsData(); + } + + /// must call stop before deinit + pub fn deinit(self: *Loop) void { + self.deinitOsData(); + self.allocator.free(self.extra_threads); + } + + const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError || + std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError || + std.os.WindowsCreateIoCompletionPortError; + + const wakeup_bytes = []u8{0x1} ** 8; + + fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { + switch (builtin.os) { + builtin.Os.linux => { + errdefer { + while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); + } + for (self.eventfd_resume_nodes) |*eventfd_node| { + eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ + .data = ResumeNode.EventFd{ + .base = ResumeNode{ + .id = ResumeNode.Id.EventFd, + .handle = undefined, + }, + .eventfd = try std.os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK), + .epoll_op = posix.EPOLL_CTL_ADD, + }, + .next = undefined, + }; + self.available_eventfd_resume_nodes.push(eventfd_node); + } + + self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC); + errdefer std.os.close(self.os_data.epollfd); + + self.os_data.final_eventfd = try std.os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK); + errdefer std.os.close(self.os_data.final_eventfd); + + self.os_data.final_eventfd_event = posix.epoll_event{ + .events = posix.EPOLLIN, + .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) }, + }; + try std.os.linuxEpollCtl( + self.os_data.epollfd, + posix.EPOLL_CTL_ADD, + self.os_data.final_eventfd, + &self.os_data.final_eventfd_event, + ); + + var extra_thread_index: usize = 0; + errdefer { + // writing 8 bytes to an eventfd cannot fail + std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + while (extra_thread_index != 0) { + extra_thread_index -= 1; + self.extra_threads[extra_thread_index].wait(); + } + } + while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { + self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); + } + }, + builtin.Os.macosx => { + self.os_data.kqfd = try std.os.bsdKQueue(); + errdefer std.os.close(self.os_data.kqfd); + + self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count); + errdefer self.allocator.free(self.os_data.kevents); + + const eventlist = ([*]posix.Kevent)(undefined)[0..0]; + + for (self.eventfd_resume_nodes) |*eventfd_node, i| { + eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ + .data = ResumeNode.EventFd{ + .base = ResumeNode{ + .id = ResumeNode.Id.EventFd, + .handle = undefined, + }, + // this one is for sending events + .kevent = posix.Kevent{ + .ident = i, + .filter = posix.EVFILT_USER, + .flags = posix.EV_CLEAR | posix.EV_ADD | posix.EV_DISABLE, + .fflags = 0, + .data = 0, + .udata = @ptrToInt(&eventfd_node.data.base), + }, + }, + .next = undefined, + }; + self.available_eventfd_resume_nodes.push(eventfd_node); + const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent); + _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null); + eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE; + eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER; + // this one is for waiting for events + self.os_data.kevents[i] = posix.Kevent{ + .ident = i, + .filter = posix.EVFILT_USER, + .flags = 0, + .fflags = 0, + .data = 0, + .udata = @ptrToInt(&eventfd_node.data.base), + }; + } + + // Pre-add so that we cannot get error.SystemResources + // later when we try to activate it. + self.os_data.final_kevent = posix.Kevent{ + .ident = extra_thread_count, + .filter = posix.EVFILT_USER, + .flags = posix.EV_ADD | posix.EV_DISABLE, + .fflags = 0, + .data = 0, + .udata = @ptrToInt(&self.final_resume_node), + }; + const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent); + _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null); + self.os_data.final_kevent.flags = posix.EV_ENABLE; + self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER; + + var extra_thread_index: usize = 0; + errdefer { + _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch unreachable; + while (extra_thread_index != 0) { + extra_thread_index -= 1; + self.extra_threads[extra_thread_index].wait(); + } + } + while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { + self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); + } + }, + builtin.Os.windows => { + self.os_data.extra_thread_count = extra_thread_count; + + self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( + windows.INVALID_HANDLE_VALUE, + null, + undefined, + undefined, + ); + errdefer std.os.close(self.os_data.io_port); + + for (self.eventfd_resume_nodes) |*eventfd_node, i| { + eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ + .data = ResumeNode.EventFd{ + .base = ResumeNode{ + .id = ResumeNode.Id.EventFd, + .handle = undefined, + }, + // this one is for sending events + .completion_key = @ptrToInt(&eventfd_node.data.base), + }, + .next = undefined, + }; + self.available_eventfd_resume_nodes.push(eventfd_node); + } + + var extra_thread_index: usize = 0; + errdefer { + var i: usize = 0; + while (i < extra_thread_index) : (i += 1) { + while (true) { + const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); + std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; + break; + } + } + while (extra_thread_index != 0) { + extra_thread_index -= 1; + self.extra_threads[extra_thread_index].wait(); + } + } + while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { + self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); + } + }, + else => {}, + } + } + + fn deinitOsData(self: *Loop) void { + switch (builtin.os) { + builtin.Os.linux => { + std.os.close(self.os_data.final_eventfd); + while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); + std.os.close(self.os_data.epollfd); + self.allocator.free(self.eventfd_resume_nodes); + }, + builtin.Os.macosx => { + self.allocator.free(self.os_data.kevents); + std.os.close(self.os_data.kqfd); + }, + builtin.Os.windows => { + std.os.close(self.os_data.io_port); + }, + else => {}, + } + } + + /// resume_node must live longer than the promise that it holds a reference to. + pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + errdefer { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + try self.modFd( + fd, + posix.EPOLL_CTL_ADD, + std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, + resume_node, + ); + } + + pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void { + var ev = std.os.linux.epoll_event{ + .events = events, + .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) }, + }; + try std.os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev); + } + + pub fn removeFd(self: *Loop, fd: i32) void { + self.removeFdNoCounter(fd); + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + + fn removeFdNoCounter(self: *Loop, fd: i32) void { + std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; + } + + pub async fn waitFd(self: *Loop, fd: i32) !void { + defer self.removeFd(fd); + suspend |p| { + // TODO explicitly put this memory in the coroutine frame #1194 + var resume_node = ResumeNode{ + .id = ResumeNode.Id.Basic, + .handle = p, + }; + try self.addFd(fd, &resume_node); + } + } + + /// Bring your own linked list node. This means it can't fail. + pub fn onNextTick(self: *Loop, node: *NextTickNode) void { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + self.next_tick_queue.put(node); + } + + pub fn run(self: *Loop) void { + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + self.workerRun(); + for (self.extra_threads) |extra_thread| { + extra_thread.wait(); + } + } + + fn workerRun(self: *Loop) void { + start_over: while (true) { + if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { + while (self.next_tick_queue.get()) |next_tick_node| { + const handle = next_tick_node.data; + if (self.next_tick_queue.isEmpty()) { + // last node, just resume it + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + } + + // non-last node, stick it in the epoll/kqueue set so that + // other threads can get to it + if (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| { + const eventfd_node = &resume_stack_node.data; + eventfd_node.base.handle = handle; + switch (builtin.os) { + builtin.Os.macosx => { + const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); + const eventlist = ([*]posix.Kevent)(undefined)[0..0]; + _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch { + // fine, we didn't need it anyway + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + self.available_eventfd_resume_nodes.push(resume_stack_node); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + }; + }, + builtin.Os.linux => { + // the pending count is already accounted for + const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET; + self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch { + // fine, we didn't need it anyway + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + self.available_eventfd_resume_nodes.push(resume_stack_node); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + }; + }, + builtin.Os.windows => { + // this value is never dereferenced but we need it to be non-null so that + // the consumer code can decide whether to read the completion key. + // it has to do this for normal I/O, so we match that behavior here. + const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); + std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, eventfd_node.completion_key, overlapped) catch { + // fine, we didn't need it anyway + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + self.available_eventfd_resume_nodes.push(resume_stack_node); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + }; + }, + else => @compileError("unsupported OS"), + } + } else { + // threads are too busy, can't add another eventfd to wake one up + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + } + } + + const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst); + if (pending_event_count == 0) { + // cause all the threads to stop + switch (builtin.os) { + builtin.Os.linux => { + // writing 8 bytes to an eventfd cannot fail + std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + return; + }, + builtin.Os.macosx => { + const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent); + const eventlist = ([*]posix.Kevent)(undefined)[0..0]; + // cannot fail because we already added it and this just enables it + _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable; + return; + }, + builtin.Os.windows => { + var i: usize = 0; + while (i < self.os_data.extra_thread_count) : (i += 1) { + while (true) { + const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); + std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; + break; + } + } + return; + }, + else => @compileError("unsupported OS"), + } + } + + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + } + + switch (builtin.os) { + builtin.Os.linux => { + // only process 1 event so we don't steal from other threads + var events: [1]std.os.linux.epoll_event = undefined; + const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); + for (events[0..count]) |ev| { + const resume_node = @intToPtr(*ResumeNode, ev.data.ptr); + const handle = resume_node.handle; + const resume_node_id = resume_node.id; + switch (resume_node_id) { + ResumeNode.Id.Basic => {}, + ResumeNode.Id.Stop => return, + ResumeNode.Id.EventFd => { + const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); + event_fd_node.epoll_op = posix.EPOLL_CTL_MOD; + const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); + self.available_eventfd_resume_nodes.push(stack_node); + }, + } + resume handle; + if (resume_node_id == ResumeNode.Id.EventFd) { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + } + }, + builtin.Os.macosx => { + var eventlist: [1]posix.Kevent = undefined; + const count = std.os.bsdKEvent(self.os_data.kqfd, self.os_data.kevents, eventlist[0..], null) catch unreachable; + for (eventlist[0..count]) |ev| { + const resume_node = @intToPtr(*ResumeNode, ev.udata); + const handle = resume_node.handle; + const resume_node_id = resume_node.id; + switch (resume_node_id) { + ResumeNode.Id.Basic => {}, + ResumeNode.Id.Stop => return, + ResumeNode.Id.EventFd => { + const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); + const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); + self.available_eventfd_resume_nodes.push(stack_node); + }, + } + resume handle; + if (resume_node_id == ResumeNode.Id.EventFd) { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + } + }, + builtin.Os.windows => { + var completion_key: usize = undefined; + while (true) { + var nbytes: windows.DWORD = undefined; + var overlapped: ?*windows.OVERLAPPED = undefined; + switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { + std.os.WindowsWaitResult.Aborted => return, + std.os.WindowsWaitResult.Normal => {}, + } + if (overlapped != null) break; + } + const resume_node = @intToPtr(*ResumeNode, completion_key); + const handle = resume_node.handle; + const resume_node_id = resume_node.id; + switch (resume_node_id) { + ResumeNode.Id.Basic => {}, + ResumeNode.Id.Stop => return, + ResumeNode.Id.EventFd => { + const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); + const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); + self.available_eventfd_resume_nodes.push(stack_node); + }, + } + resume handle; + if (resume_node_id == ResumeNode.Id.EventFd) { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + }, + else => @compileError("unsupported OS"), + } + } + } + + const OsData = switch (builtin.os) { + builtin.Os.linux => struct { + epollfd: i32, + final_eventfd: i32, + final_eventfd_event: std.os.linux.epoll_event, + }, + builtin.Os.macosx => MacOsData, + builtin.Os.windows => struct { + io_port: windows.HANDLE, + extra_thread_count: usize, + }, + else => struct {}, + }; + + const MacOsData = struct { + kqfd: i32, + final_kevent: posix.Kevent, + kevents: []posix.Kevent, + }; +}; + +test "std.event.Loop - basic" { + //var da = std.heap.DirectAllocator.init(); + //defer da.deinit(); + + //const allocator = &da.allocator; + + //var loop: Loop = undefined; + //try loop.initMultiThreaded(allocator); + //defer loop.deinit(); + + //loop.run(); +} diff --git a/std/event/tcp.zig b/std/event/tcp.zig new file mode 100644 index 0000000000..5151ecf934 --- /dev/null +++ b/std/event/tcp.zig @@ -0,0 +1,183 @@ +const std = @import("../index.zig"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const event = std.event; +const mem = std.mem; +const posix = std.os.posix; +const windows = std.os.windows; +const Loop = std.event.Loop; + +pub const Server = struct { + handleRequestFn: async<*mem.Allocator> fn (*Server, *const std.net.Address, *const std.os.File) void, + + loop: *Loop, + sockfd: ?i32, + accept_coro: ?promise, + listen_address: std.net.Address, + + waiting_for_emfile_node: PromiseNode, + listen_resume_node: event.Loop.ResumeNode, + + const PromiseNode = std.LinkedList(promise).Node; + + pub fn init(loop: *Loop) Server { + // TODO can't initialize handler coroutine here because we need well defined copy elision + return Server{ + .loop = loop, + .sockfd = null, + .accept_coro = null, + .handleRequestFn = undefined, + .waiting_for_emfile_node = undefined, + .listen_address = undefined, + .listen_resume_node = event.Loop.ResumeNode{ + .id = event.Loop.ResumeNode.Id.Basic, + .handle = undefined, + }, + }; + } + + pub fn listen( + self: *Server, + address: *const std.net.Address, + handleRequestFn: async<*mem.Allocator> fn (*Server, *const std.net.Address, *const std.os.File) void, + ) !void { + self.handleRequestFn = handleRequestFn; + + const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); + errdefer std.os.close(sockfd); + self.sockfd = sockfd; + + try std.os.posixBind(sockfd, &address.os_addr); + try std.os.posixListen(sockfd, posix.SOMAXCONN); + self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(sockfd)); + + self.accept_coro = try async Server.handler(self); + errdefer cancel self.accept_coro.?; + + self.listen_resume_node.handle = self.accept_coro.?; + try self.loop.addFd(sockfd, &self.listen_resume_node); + errdefer self.loop.removeFd(sockfd); + } + + /// Stop listening + pub fn close(self: *Server) void { + self.loop.removeFd(self.sockfd.?); + std.os.close(self.sockfd.?); + } + + pub fn deinit(self: *Server) void { + if (self.accept_coro) |accept_coro| cancel accept_coro; + if (self.sockfd) |sockfd| std.os.close(sockfd); + } + + pub async fn handler(self: *Server) void { + while (true) { + var accepted_addr: std.net.Address = undefined; + if (std.os.posixAccept(self.sockfd.?, &accepted_addr.os_addr, posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| { + var socket = std.os.File.openHandle(accepted_fd); + _ = async self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) { + error.OutOfMemory => { + socket.close(); + continue; + }, + }; + } else |err| switch (err) { + error.WouldBlock => { + suspend; // we will get resumed by epoll_wait in the event loop + continue; + }, + error.ProcessFdQuotaExceeded => { + errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node); + suspend |p| { + self.waiting_for_emfile_node = PromiseNode.init(p); + std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node); + } + continue; + }, + error.ConnectionAborted, error.FileDescriptorClosed => continue, + + error.PageFault => unreachable, + error.InvalidSyscall => unreachable, + error.FileDescriptorNotASocket => unreachable, + error.OperationNotSupported => unreachable, + + error.SystemFdQuotaExceeded, error.SystemResources, error.ProtocolFailure, error.BlockedByFirewall, error.Unexpected => { + @panic("TODO handle this error"); + }, + } + } + } +}; + +pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File { + var address = _address.*; // TODO https://github.com/ziglang/zig/issues/733 + + const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); + errdefer std.os.close(sockfd); + + try std.os.posixConnectAsync(sockfd, &address.os_addr); + try await try async loop.waitFd(sockfd); + try std.os.posixGetSockOptConnectError(sockfd); + + return std.os.File.openHandle(sockfd); +} + +test "listen on a port, send bytes, receive bytes" { + if (builtin.os != builtin.Os.linux) { + // TODO build abstractions for other operating systems + return; + } + const MyServer = struct { + tcp_server: Server, + + const Self = this; + async<*mem.Allocator> fn handler(tcp_server: *Server, _addr: *const std.net.Address, _socket: *const std.os.File) void { + const self = @fieldParentPtr(Self, "tcp_server", tcp_server); + var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 + defer socket.close(); + // TODO guarantee elision of this allocation + const next_handler = async errorableHandler(self, _addr, socket) catch unreachable; + (await next_handler) catch |err| { + std.debug.panic("unable to handle connection: {}\n", err); + }; + suspend |p| { + cancel p; + } + } + async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void { + const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733 + var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 + + var adapter = std.io.FileOutStream.init(&socket); + var stream = &adapter.stream; + try stream.print("hello from server\n"); + } + }; + + const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable; + const addr = std.net.Address.initIp4(ip4addr, 0); + + var loop: Loop = undefined; + try loop.initSingleThreaded(std.debug.global_allocator); + var server = MyServer{ .tcp_server = Server.init(&loop) }; + defer server.tcp_server.deinit(); + try server.tcp_server.listen(addr, MyServer.handler); + + const p = try async doAsyncTest(&loop, server.tcp_server.listen_address, &server.tcp_server); + defer cancel p; + loop.run(); +} + +async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Server) void { + errdefer @panic("test failure"); + + var socket_file = try await try async connect(loop, address); + defer socket_file.close(); + + var buf: [512]u8 = undefined; + const amt_read = try socket_file.read(buf[0..]); + const msg = buf[0..amt_read]; + assert(mem.eql(u8, msg, "hello from server\n")); + server.close(); +} + -- cgit v1.2.3 From 1b82a9defce4daf2fea8e6290c7a8a4689878194 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 22:41:16 -0400 Subject: enable basic event loop test --- std/event/loop.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/std/event/loop.zig b/std/event/loop.zig index 61c7f80cdc..613d4f48a4 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -564,14 +564,14 @@ pub const Loop = struct { }; test "std.event.Loop - basic" { - //var da = std.heap.DirectAllocator.init(); - //defer da.deinit(); + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); - //const allocator = &da.allocator; + const allocator = &da.allocator; - //var loop: Loop = undefined; - //try loop.initMultiThreaded(allocator); - //defer loop.deinit(); + var loop: Loop = undefined; + try loop.initMultiThreaded(allocator); + defer loop.deinit(); - //loop.run(); + loop.run(); } -- cgit v1.2.3 From b5cfbfd84ee26d5174abf3368b75b9e4502898a5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 23:41:28 -0400 Subject: fix regression from b6eb4048 --- std/event/locked.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/std/event/locked.zig b/std/event/locked.zig index 41ab112aff..e7ad544d78 100644 --- a/std/event/locked.zig +++ b/std/event/locked.zig @@ -1,5 +1,6 @@ const std = @import("../index.zig"); const Lock = std.event.Lock; +const Loop = std.event.Loop; /// Thread-safe async/await lock that protects one piece of data. /// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and -- cgit v1.2.3 From 28f9230b40ee7aa179705c39616aaf2a5f303b73 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 Jul 2018 10:12:08 -0400 Subject: fix crash when calling comptime-known undefined function ptr closes #880 closes #1212 --- src/ir.cpp | 2 ++ test/compile_errors.zig | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 2dc6ddad2c..10ce3254fd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13271,6 +13271,8 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction return ir_finish_anal(ira, cast_instruction->value.type); } else if (fn_ref->value.type->id == TypeTableEntryIdFn) { FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref); + if (fn_table_entry == nullptr) + return ira->codegen->builtin_types.entry_invalid; return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, fn_ref, nullptr, is_comptime, call_instruction->fn_inline); } else if (fn_ref->value.type->id == TypeTableEntryIdBoundFn) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 1b76c01564..a6db8d50b4 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,19 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "use of comptime-known undefined function value", + \\const Cmd = struct { + \\ exec: fn () void, + \\}; + \\export fn entry() void { + \\ const command = Cmd{ .exec = undefined }; + \\ command.exec(); + \\} + , + ".tmp_source.zig:6:12: error: use of undefined value", + ); + cases.add( "bad @alignCast at comptime", \\comptime { -- cgit v1.2.3 From 696ef0bc03ccbe61dff5b09a257c2de7b227290a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 Jul 2018 10:37:58 -0400 Subject: langref: docs for union safety --- doc/langref.html.in | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 4 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 16e9023f26..c90c847f92 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6665,6 +6665,8 @@ comptime { {#code_end#}

    At runtime:

    {#code_begin|exe_err#} +const std = @import("std"); + const Set1 = error{ A, B, @@ -6674,10 +6676,11 @@ const Set2 = error{ C, }; pub fn main() void { - _ = foo(Set1.B); + foo(Set1.B); } -fn foo(set1: Set1) Set2 { - return @errSetCast(Set2, set1); +fn foo(set1: Set1) void { + const x = @errSetCast(Set2, set1); + std.debug.warn("value: {}\n", x); } {#code_end#} {#header_close#} @@ -6705,7 +6708,84 @@ fn foo(bytes: []u8) u32 { {#code_end#} {#header_close#} {#header_open|Wrong Union Field Access#} -

    TODO

    +

    At compile-time:

    + {#code_begin|test_err|accessing union field 'float' while field 'int' is set#} +comptime { + var f = Foo{ .int = 42 }; + f.float = 12.34; +} + +const Foo = union { + float: f32, + int: u32, +}; + {#code_end#} +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +const Foo = union { + float: f32, + int: u32, +}; + +pub fn main() void { + var f = Foo{ .int = 42 }; + bar(&f); +} + +fn bar(f: *Foo) void { + f.float = 12.34; + std.debug.warn("value: {}\n", f.float); +} + {#code_end#} +

    + This safety is not available for extern or packed unions. +

    +

    + To change the active field of a union, assign the entire union, like this: +

    + {#code_begin|exe#} +const std = @import("std"); + +const Foo = union { + float: f32, + int: u32, +}; + +pub fn main() void { + var f = Foo{ .int = 42 }; + bar(&f); +} + +fn bar(f: *Foo) void { + f.* = Foo{ .float = 12.34 }; + std.debug.warn("value: {}\n", f.float); +} + {#code_end#} +

    + To change the active field of a union when a meaningful value for the field is not known, + use {#link|undefined#}, like this: +

    + {#code_begin|exe#} +const std = @import("std"); + +const Foo = union { + float: f32, + int: u32, +}; + +pub fn main() void { + var f = Foo{ .int = 42 }; + f = Foo{ .float = undefined }; + bar(&f); + std.debug.warn("value: {}\n", f.float); +} + +fn bar(f: *Foo) void { + f.float = 12.34; +} + {#code_end#} {#header_close#} {#header_open|Out of Bounds Float To Integer Cast#} -- cgit v1.2.3 From 0ce6934e2631eb3beca817d3bce12ecb13aafa13 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 Jul 2018 11:44:47 -0400 Subject: allow var args calls to async functions --- src/ir.cpp | 53 +++++++++++++++++++++++++++++++++-------------------- std/event/loop.zig | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 20 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 10ce3254fd..7f7436010e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12721,14 +12721,22 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal // for extern functions, the var args argument is not counted. // for zig functions, it is. size_t var_args_1_or_0; - if (fn_type_id->cc == CallingConventionUnspecified) { - var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0; - } else { + if (fn_type_id->cc == CallingConventionC) { var_args_1_or_0 = 0; + } else { + var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0; } size_t src_param_count = fn_type_id->param_count - var_args_1_or_0; size_t call_param_count = call_instruction->arg_count + first_arg_1_or_0; + for (size_t i = 0; i < call_instruction->arg_count; i += 1) { + ConstExprValue *arg_tuple_value = &call_instruction->args[i]->other->value; + if (arg_tuple_value->type->id == TypeTableEntryIdArgTuple) { + call_param_count -= 1; + call_param_count += arg_tuple_value->data.x_arg_tuple.end_index - + arg_tuple_value->data.x_arg_tuple.start_index; + } + } AstNode *source_node = call_instruction->base.source_node; AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;; @@ -12909,11 +12917,6 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal buf_sprintf("calling a generic function requires compile-time known function value")); return ira->codegen->builtin_types.entry_invalid; } - if (call_instruction->is_async && fn_type_id->is_var_args) { - ir_add_error(ira, call_instruction->fn_ref, - buf_sprintf("compiler bug: TODO: implement var args async functions. https://github.com/ziglang/zig/issues/557")); - return ira->codegen->builtin_types.entry_invalid; - } // Count the arguments of the function type id we are creating size_t new_fn_arg_count = first_arg_1_or_0; @@ -12988,18 +12991,18 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal if (type_is_invalid(arg->value.type)) return ira->codegen->builtin_types.entry_invalid; - AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); - assert(param_decl_node->type == NodeTypeParamDecl); - bool is_var_args = param_decl_node->data.param_decl.is_var_args; - if (is_var_args && !found_first_var_arg) { - first_var_arg = inst_fn_type_id.param_count; - found_first_var_arg = true; - } - if (arg->value.type->id == TypeTableEntryIdArgTuple) { for (size_t arg_tuple_i = arg->value.data.x_arg_tuple.start_index; arg_tuple_i < arg->value.data.x_arg_tuple.end_index; arg_tuple_i += 1) { + AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); + assert(param_decl_node->type == NodeTypeParamDecl); + bool is_var_args = param_decl_node->data.param_decl.is_var_args; + if (is_var_args && !found_first_var_arg) { + first_var_arg = inst_fn_type_id.param_count; + found_first_var_arg = true; + } + VariableTableEntry *arg_var = get_fn_var_by_index(parent_fn_entry, arg_tuple_i); if (arg_var == nullptr) { ir_add_error(ira, arg, @@ -13020,10 +13023,20 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return ira->codegen->builtin_types.entry_invalid; } } - } else if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope, - &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) - { - return ira->codegen->builtin_types.entry_invalid; + } else { + AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); + assert(param_decl_node->type == NodeTypeParamDecl); + bool is_var_args = param_decl_node->data.param_decl.is_var_args; + if (is_var_args && !found_first_var_arg) { + first_var_arg = inst_fn_type_id.param_count; + found_first_var_arg = true; + } + + if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope, + &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) + { + return ira->codegen->builtin_types.entry_invalid; + } } } diff --git a/std/event/loop.zig b/std/event/loop.zig index 613d4f48a4..646f15875f 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -360,6 +360,28 @@ pub const Loop = struct { } } + /// This is equivalent to an async call, except instead of beginning execution of the async function, + /// it immediately returns to the caller, and the async function is queued in the event loop. It still + /// returns a promise to be awaited. + pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) { + const S = struct { + async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType { + suspend |p| { + handle.* = p; + var my_tick_node = Loop.NextTickNode{ + .next = undefined, + .data = p, + }; + loop.onNextTick(&my_tick_node); + } + // TODO guaranteed allocation elision for await in same func as async + return await (async func(args2) catch unreachable); + } + }; + var handle: promise->@typeOf(func).ReturnType = undefined; + return async S.asyncFunc(self, &handle, args); + } + fn workerRun(self: *Loop) void { start_over: while (true) { if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { @@ -575,3 +597,33 @@ test "std.event.Loop - basic" { loop.run(); } + +test "std.event.Loop - call" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const allocator = &da.allocator; + + var loop: Loop = undefined; + try loop.initMultiThreaded(allocator); + defer loop.deinit(); + + var did_it = false; + const handle = try loop.call(testEventLoop); + const handle2 = try loop.call(testEventLoop2, handle, &did_it); + defer cancel handle2; + + loop.run(); + + assert(did_it); +} + +async fn testEventLoop() i32 { + return 1234; +} + +async fn testEventLoop2(h: promise->i32, did_it: *bool) void { + const value = await h; + assert(value == 1234); + did_it.* = true; +} -- cgit v1.2.3 From 8fba0a6ae862993afa2aeca774347adc399b3605 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 Jul 2018 15:17:01 -0400 Subject: introduce std.event.Group for making parallel async calls --- CMakeLists.txt | 1 + src-self-hosted/module.zig | 36 ++++++++--- std/event.zig | 2 + std/event/group.zig | 158 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 8 deletions(-) create mode 100644 std/event/group.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index fdedcd5eec..eeb0ec2058 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -459,6 +459,7 @@ set(ZIG_STD_FILES "empty.zig" "event.zig" "event/channel.zig" + "event/group.zig" "event/lock.zig" "event/locked.zig" "event/loop.zig" diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 5ce1a7965a..24be228eb8 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -85,6 +85,17 @@ pub const Module = struct { exported_symbol_names: event.Locked(Decl.Table), + /// Before code generation starts, must wait on this group to make sure + /// the build is complete. + build_group: event.Group(BuildError!void), + + const BuildErrorsList = std.SegmentedList(BuildErrorDesc, 1); + + pub const BuildErrorDesc = struct { + code: BuildError, + text: []const u8, + }; + // TODO handle some of these earlier and report them in a way other than error codes pub const BuildError = error{ OutOfMemory, @@ -237,6 +248,7 @@ pub const Module = struct { .emit_file_type = Emit.Binary, .link_out_file = null, .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), + .build_group = event.Group(BuildError!void).init(loop), }); } @@ -310,6 +322,9 @@ pub const Module = struct { const decls = try Scope.Decls.create(self.a(), null); errdefer decls.destroy(); + var decl_group = event.Group(BuildError!void).init(self.loop); + errdefer decl_group.cancelAll(); + var it = tree.root_node.decls.iterator(0); while (it.next()) |decl_ptr| { const decl = decl_ptr.*; @@ -342,25 +357,30 @@ pub const Module = struct { }); errdefer self.a().destroy(fn_decl); - // TODO make this parallel - try await try async self.addTopLevelDecl(tree, &fn_decl.base); + try decl_group.call(addTopLevelDecl, self, tree, &fn_decl.base); }, ast.Node.Id.TestDecl => @panic("TODO"), else => unreachable, } } + try await (async decl_group.wait() catch unreachable); + try await (async self.build_group.wait() catch unreachable); } async fn addTopLevelDecl(self: *Module, tree: *ast.Tree, decl: *Decl) !void { const is_export = decl.isExported(tree); - { - const exported_symbol_names = await try async self.exported_symbol_names.acquire(); - defer exported_symbol_names.release(); + if (is_export) { + try self.build_group.call(verifyUniqueSymbol, self, decl); + } + } - if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { - @panic("TODO report compile error"); - } + async fn verifyUniqueSymbol(self: *Module, decl: *Decl) !void { + const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable); + defer exported_symbol_names.release(); + + if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { + @panic("TODO report compile error"); } } diff --git a/std/event.zig b/std/event.zig index 7e9928b3d7..516defebf8 100644 --- a/std/event.zig +++ b/std/event.zig @@ -3,6 +3,7 @@ pub const Loop = @import("event/loop.zig").Loop; pub const Lock = @import("event/lock.zig").Lock; pub const tcp = @import("event/tcp.zig"); pub const Channel = @import("event/channel.zig").Channel; +pub const Group = @import("event/group.zig").Group; test "import event tests" { _ = @import("event/locked.zig"); @@ -10,4 +11,5 @@ test "import event tests" { _ = @import("event/lock.zig"); _ = @import("event/tcp.zig"); _ = @import("event/channel.zig"); + _ = @import("event/group.zig"); } diff --git a/std/event/group.zig b/std/event/group.zig new file mode 100644 index 0000000000..c286803b53 --- /dev/null +++ b/std/event/group.zig @@ -0,0 +1,158 @@ +const std = @import("../index.zig"); +const builtin = @import("builtin"); +const Lock = std.event.Lock; +const Loop = std.event.Loop; +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; +const assert = std.debug.assert; + +/// ReturnType should be `void` or `E!void` +pub fn Group(comptime ReturnType: type) type { + return struct { + coro_stack: Stack, + alloc_stack: Stack, + lock: Lock, + + const Self = this; + + const Error = switch (@typeInfo(ReturnType)) { + builtin.TypeId.ErrorUnion => |payload| payload.error_set, + else => void, + }; + const Stack = std.atomic.Stack(promise->ReturnType); + + pub fn init(loop: *Loop) Self { + return Self{ + .coro_stack = Stack.init(), + .alloc_stack = Stack.init(), + .lock = Lock.init(loop), + }; + } + + /// Add a promise to the group. Thread-safe. + pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) { + const node = try self.lock.loop.allocator.create(Stack.Node{ + .next = undefined, + .data = handle, + }); + self.alloc_stack.push(node); + } + + /// This is equivalent to an async call, but the async function is added to the group, instead + /// of returning a promise. func must be async and have return type void. + /// Thread-safe. + pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) { + const S = struct { + async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType { + // TODO this is a hack to make the memory following be inside the coro frame + suspend |p| { + var my_node: Stack.Node = undefined; + node.* = &my_node; + resume p; + } + + // TODO this allocation elision should be guaranteed because we await it in + // this coro frame + return await (async func(args2) catch unreachable); + } + }; + var node: *Stack.Node = undefined; + const handle = try async S.asyncFunc(&node, args); + node.* = Stack.Node{ + .next = undefined, + .data = handle, + }; + self.coro_stack.push(node); + } + + /// Wait for all the calls and promises of the group to complete. + /// Thread-safe. + pub async fn wait(self: *Self) ReturnType { + // TODO catch unreachable because the allocation can be grouped with + // the coro frame allocation + const held = await (async self.lock.acquire() catch unreachable); + defer held.release(); + + while (self.coro_stack.pop()) |node| { + if (Error == void) { + await node.data; + } else { + (await node.data) catch |err| { + self.cancelAll(); + return err; + }; + } + } + while (self.alloc_stack.pop()) |node| { + const handle = node.data; + self.lock.loop.allocator.destroy(node); + if (Error == void) { + await handle; + } else { + (await handle) catch |err| { + self.cancelAll(); + return err; + }; + } + } + } + + /// Cancel all the outstanding promises. May only be called if wait was never called. + pub fn cancelAll(self: *Self) void { + while (self.coro_stack.pop()) |node| { + cancel node.data; + } + while (self.alloc_stack.pop()) |node| { + cancel node.data; + self.lock.loop.allocator.destroy(node); + } + } + }; +} + +test "std.event.Group" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const allocator = &da.allocator; + + var loop: Loop = undefined; + try loop.initMultiThreaded(allocator); + defer loop.deinit(); + + const handle = try async testGroup(&loop); + defer cancel handle; + + loop.run(); +} + +async fn testGroup(loop: *Loop) void { + var count: usize = 0; + var group = Group(void).init(loop); + group.add(async sleepALittle(&count) catch @panic("memory")) catch @panic("memory"); + group.call(increaseByTen, &count) catch @panic("memory"); + await (async group.wait() catch @panic("memory")); + assert(count == 11); + + var another = Group(error!void).init(loop); + another.add(async somethingElse() catch @panic("memory")) catch @panic("memory"); + another.call(doSomethingThatFails) catch @panic("memory"); + std.debug.assertError(await (async another.wait() catch @panic("memory")), error.ItBroke); +} + +async fn sleepALittle(count: *usize) void { + std.os.time.sleep(0, 1000000); + _ = @atomicRmw(usize, count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); +} + +async fn increaseByTen(count: *usize) void { + var i: usize = 0; + while (i < 10) : (i += 1) { + _ = @atomicRmw(usize, count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + } +} + +async fn doSomethingThatFails() error!void {} +async fn somethingElse() error!void { + return error.ItBroke; +} -- cgit v1.2.3 From 574e31f0a046aa6e6fad73fff2cbbb3617fe1bae Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 Jul 2018 20:18:43 -0400 Subject: self-hosted: first passing test * introduce std.atomic.Int * add src-self-hosted/test.zig which is tested by the main test suite - it fully utilizes the multithreaded async/await event loop so the tests should Go Fast * `stage2/bin/zig build-obj test.zig` is able to spit out an error if 2 exported functions collide * ability for `zig test` to accept `--object` and `--assembly` arguments * std.build: TestStep supports addLibPath and addObjectFile --- CMakeLists.txt | 1 + build.zig | 152 ++++++++++++++++++++--------------- src-self-hosted/errmsg.zig | 18 +++-- src-self-hosted/introspect.zig | 5 ++ src-self-hosted/main.zig | 36 ++++----- src-self-hosted/module.zig | 98 +++++++++++++++++++---- src-self-hosted/test.zig | 176 +++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 10 ++- std/atomic/index.zig | 2 + std/atomic/int.zig | 19 +++++ std/build.zig | 22 ++++++ 11 files changed, 432 insertions(+), 107 deletions(-) create mode 100644 src-self-hosted/test.zig create mode 100644 std/atomic/int.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index eeb0ec2058..559b3b6964 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -431,6 +431,7 @@ set(ZIG_CPP_SOURCES set(ZIG_STD_FILES "array_list.zig" "atomic/index.zig" + "atomic/int.zig" "atomic/queue_mpmc.zig" "atomic/queue_mpsc.zig" "atomic/stack.zig" diff --git a/build.zig b/build.zig index fd154c7504..273048d458 100644 --- a/build.zig +++ b/build.zig @@ -35,70 +35,27 @@ pub fn build(b: *Builder) !void { "BUILD_INFO", }); var index: usize = 0; - const cmake_binary_dir = nextValue(&index, build_info); - const cxx_compiler = nextValue(&index, build_info); - const llvm_config_exe = nextValue(&index, build_info); - const lld_include_dir = nextValue(&index, build_info); - const lld_libraries = nextValue(&index, build_info); - const std_files = nextValue(&index, build_info); - const c_header_files = nextValue(&index, build_info); - const dia_guids_lib = nextValue(&index, build_info); + var ctx = Context{ + .cmake_binary_dir = nextValue(&index, build_info), + .cxx_compiler = nextValue(&index, build_info), + .llvm_config_exe = nextValue(&index, build_info), + .lld_include_dir = nextValue(&index, build_info), + .lld_libraries = nextValue(&index, build_info), + .std_files = nextValue(&index, build_info), + .c_header_files = nextValue(&index, build_info), + .dia_guids_lib = nextValue(&index, build_info), + .llvm = undefined, + }; + ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); - const llvm = findLLVM(b, llvm_config_exe) catch unreachable; + var test_stage2 = b.addTest("src-self-hosted/test.zig"); + test_stage2.setBuildMode(builtin.Mode.Debug); var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); exe.setBuildMode(mode); - // This is for finding /lib/libz.a on alpine linux. - // TODO turn this into -Dextra-lib-path=/lib option - exe.addLibPath("/lib"); - - exe.addIncludeDir("src"); - exe.addIncludeDir(cmake_binary_dir); - addCppLib(b, exe, cmake_binary_dir, "zig_cpp"); - if (lld_include_dir.len != 0) { - exe.addIncludeDir(lld_include_dir); - var it = mem.split(lld_libraries, ";"); - while (it.next()) |lib| { - exe.addObjectFile(lib); - } - } else { - addCppLib(b, exe, cmake_binary_dir, "embedded_lld_wasm"); - addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf"); - addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff"); - addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib"); - } - dependOnLib(exe, llvm); - - if (exe.target.getOs() == builtin.Os.linux) { - const libstdcxx_path_padded = try b.exec([][]const u8{ - cxx_compiler, - "-print-file-name=libstdc++.a", - }); - const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?; - if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) { - warn( - \\Unable to determine path to libstdc++.a - \\On Fedora, install libstdc++-static and try again. - \\ - ); - return error.RequiredLibraryNotFound; - } - exe.addObjectFile(libstdcxx_path); - - exe.linkSystemLibrary("pthread"); - } else if (exe.target.isDarwin()) { - exe.linkSystemLibrary("c++"); - } - - if (dia_guids_lib.len != 0) { - exe.addObjectFile(dia_guids_lib); - } - - if (exe.target.getOs() != builtin.Os.windows) { - exe.linkSystemLibrary("xml2"); - } - exe.linkSystemLibrary("c"); + try configureStage2(b, test_stage2, ctx); + try configureStage2(b, exe, ctx); b.default_step.dependOn(&exe.step); @@ -110,12 +67,16 @@ pub fn build(b: *Builder) !void { exe.setVerboseLink(verbose_link_exe); b.installArtifact(exe); - installStdLib(b, std_files); - installCHeaders(b, c_header_files); + installStdLib(b, ctx.std_files); + installCHeaders(b, ctx.c_header_files); const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") orelse false; + const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); + test_stage2_step.dependOn(&test_stage2.step); + test_step.dependOn(test_stage2_step); + test_step.dependOn(docs_step); test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", with_lldb)); @@ -133,7 +94,7 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addGenHTests(b, test_filter)); } -fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) void { +fn dependOnLib(lib_exe_obj: var, dep: *const LibraryDep) void { for (dep.libdirs.toSliceConst()) |lib_dir| { lib_exe_obj.addLibPath(lib_dir); } @@ -148,7 +109,7 @@ fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) vo } } -fn addCppLib(b: *Builder, lib_exe_obj: *std.build.LibExeObjStep, cmake_binary_dir: []const u8, lib_name: []const u8) void { +fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void { const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib"; lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable); } @@ -254,3 +215,68 @@ fn nextValue(index: *usize, build_info: []const u8) []const u8 { } } } + +fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { + // This is for finding /lib/libz.a on alpine linux. + // TODO turn this into -Dextra-lib-path=/lib option + exe.addLibPath("/lib"); + + exe.addIncludeDir("src"); + exe.addIncludeDir(ctx.cmake_binary_dir); + addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); + if (ctx.lld_include_dir.len != 0) { + exe.addIncludeDir(ctx.lld_include_dir); + var it = mem.split(ctx.lld_libraries, ";"); + while (it.next()) |lib| { + exe.addObjectFile(lib); + } + } else { + addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_wasm"); + addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_elf"); + addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_coff"); + addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_lib"); + } + dependOnLib(exe, ctx.llvm); + + if (exe.target.getOs() == builtin.Os.linux) { + const libstdcxx_path_padded = try b.exec([][]const u8{ + ctx.cxx_compiler, + "-print-file-name=libstdc++.a", + }); + const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?; + if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) { + warn( + \\Unable to determine path to libstdc++.a + \\On Fedora, install libstdc++-static and try again. + \\ + ); + return error.RequiredLibraryNotFound; + } + exe.addObjectFile(libstdcxx_path); + + exe.linkSystemLibrary("pthread"); + } else if (exe.target.isDarwin()) { + exe.linkSystemLibrary("c++"); + } + + if (ctx.dia_guids_lib.len != 0) { + exe.addObjectFile(ctx.dia_guids_lib); + } + + if (exe.target.getOs() != builtin.Os.windows) { + exe.linkSystemLibrary("xml2"); + } + exe.linkSystemLibrary("c"); +} + +const Context = struct { + cmake_binary_dir: []const u8, + cxx_compiler: []const u8, + llvm_config_exe: []const u8, + lld_include_dir: []const u8, + lld_libraries: []const u8, + std_files: []const u8, + c_header_files: []const u8, + dia_guids_lib: []const u8, + llvm: LibraryDep, +}; diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index b6fd78d8f6..a92b5145ce 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -11,11 +11,15 @@ pub const Color = enum { On, }; +pub const Span = struct { + first: ast.TokenIndex, + last: ast.TokenIndex, +}; + pub const Msg = struct { path: []const u8, text: []u8, - first_token: TokenIndex, - last_token: TokenIndex, + span: Span, tree: *ast.Tree, }; @@ -39,8 +43,10 @@ pub fn createFromParseError( .tree = tree, .path = path, .text = text_buf.toOwnedSlice(), - .first_token = loc_token, - .last_token = loc_token, + .span = Span{ + .first = loc_token, + .last = loc_token, + }, }); errdefer allocator.destroy(msg); @@ -48,8 +54,8 @@ pub fn createFromParseError( } pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void { - const first_token = msg.tree.tokens.at(msg.first_token); - const last_token = msg.tree.tokens.at(msg.last_token); + const first_token = msg.tree.tokens.at(msg.span.first); + const last_token = msg.tree.tokens.at(msg.span.last); const start_loc = msg.tree.tokenLocationPtr(0, first_token); const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token); if (!color_on) { diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig index 74084b48c6..ecd04c4467 100644 --- a/src-self-hosted/introspect.zig +++ b/src-self-hosted/introspect.zig @@ -53,3 +53,8 @@ pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 { return error.ZigLibDirNotFound; }; } + +/// Caller must free result +pub fn resolveZigCacheDir(allocator: *mem.Allocator) ![]u8 { + return std.mem.dupe(allocator, u8, "zig-cache"); +} diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index fe94a4460a..d7ead0ba32 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -481,29 +481,29 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo module.link_out_file = flags.single("out-file"); try module.build(); - const process_build_events_handle = try async processBuildEvents(module, true); + const process_build_events_handle = try async processBuildEvents(module, color); defer cancel process_build_events_handle; loop.run(); } -async fn processBuildEvents(module: *Module, watch: bool) void { - while (watch) { - // TODO directly awaiting async should guarantee memory allocation elision - const build_event = await (async module.events.get() catch unreachable); +async fn processBuildEvents(module: *Module, color: errmsg.Color) void { + // TODO directly awaiting async should guarantee memory allocation elision + const build_event = await (async module.events.get() catch unreachable); - switch (build_event) { - Module.Event.Ok => { - std.debug.warn("Build succeeded\n"); - return; - }, - Module.Event.Error => |err| { - std.debug.warn("build failed: {}\n", @errorName(err)); - @panic("TODO error return trace"); - }, - Module.Event.Fail => |errs| { - @panic("TODO print compile error messages"); - }, - } + switch (build_event) { + Module.Event.Ok => { + std.debug.warn("Build succeeded\n"); + return; + }, + Module.Event.Error => |err| { + std.debug.warn("build failed: {}\n", @errorName(err)); + @panic("TODO error return trace"); + }, + Module.Event.Fail => |msgs| { + for (msgs) |msg| { + errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1); + } + }, } } diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 24be228eb8..44954e4cd1 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -89,12 +89,9 @@ pub const Module = struct { /// the build is complete. build_group: event.Group(BuildError!void), - const BuildErrorsList = std.SegmentedList(BuildErrorDesc, 1); + compile_errors: event.Locked(CompileErrList), - pub const BuildErrorDesc = struct { - code: BuildError, - text: []const u8, - }; + const CompileErrList = std.ArrayList(*errmsg.Msg); // TODO handle some of these earlier and report them in a way other than error codes pub const BuildError = error{ @@ -131,11 +128,12 @@ pub const Module = struct { NoStdHandles, Overflow, NotSupported, + BufferTooSmall, }; pub const Event = union(enum) { Ok, - Fail: []errmsg.Msg, + Fail: []*errmsg.Msg, Error: BuildError, }; @@ -249,6 +247,7 @@ pub const Module = struct { .link_out_file = null, .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), .build_group = event.Group(BuildError!void).init(loop), + .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), }); } @@ -288,7 +287,17 @@ pub const Module = struct { await (async self.events.put(Event{ .Error = err }) catch unreachable); return; }; - await (async self.events.put(Event.Ok) catch unreachable); + const compile_errors = blk: { + const held = await (async self.compile_errors.acquire() catch unreachable); + defer held.release(); + break :blk held.value.toOwnedSlice(); + }; + + if (compile_errors.len == 0) { + await (async self.events.put(Event.Ok) catch unreachable); + } else { + await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable); + } // for now we stop after 1 return; } @@ -310,10 +319,13 @@ pub const Module = struct { }; errdefer self.a().free(source_code); - var parsed_file = ParsedFile{ - .tree = try std.zig.parse(self.a(), source_code), + const parsed_file = try self.a().create(ParsedFile{ + .tree = undefined, .realpath = root_src_real_path, - }; + }); + errdefer self.a().destroy(parsed_file); + + parsed_file.tree = try std.zig.parse(self.a(), source_code); errdefer parsed_file.tree.deinit(); const tree = &parsed_file.tree; @@ -337,7 +349,7 @@ pub const Module = struct { const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { @panic("TODO add compile error"); //try self.addCompileError( - // &parsed_file, + // parsed_file, // fn_proto.fn_token, // fn_proto.fn_token + 1, // "missing function name", @@ -357,7 +369,7 @@ pub const Module = struct { }); errdefer self.a().destroy(fn_decl); - try decl_group.call(addTopLevelDecl, self, tree, &fn_decl.base); + try decl_group.call(addTopLevelDecl, self, parsed_file, &fn_decl.base); }, ast.Node.Id.TestDecl => @panic("TODO"), else => unreachable, @@ -367,20 +379,56 @@ pub const Module = struct { try await (async self.build_group.wait() catch unreachable); } - async fn addTopLevelDecl(self: *Module, tree: *ast.Tree, decl: *Decl) !void { - const is_export = decl.isExported(tree); + async fn addTopLevelDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void { + const is_export = decl.isExported(&parsed_file.tree); if (is_export) { - try self.build_group.call(verifyUniqueSymbol, self, decl); + try self.build_group.call(verifyUniqueSymbol, self, parsed_file, decl); } } - async fn verifyUniqueSymbol(self: *Module, decl: *Decl) !void { + fn addCompileError(self: *Module, parsed_file: *ParsedFile, span: errmsg.Span, comptime fmt: []const u8, args: ...) !void { + const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args); + errdefer self.loop.allocator.free(text); + + try self.build_group.call(addCompileErrorAsync, self, parsed_file, span.first, span.last, text); + } + + async fn addCompileErrorAsync( + self: *Module, + parsed_file: *ParsedFile, + first_token: ast.TokenIndex, + last_token: ast.TokenIndex, + text: []u8, + ) !void { + const msg = try self.loop.allocator.create(errmsg.Msg{ + .path = parsed_file.realpath, + .text = text, + .span = errmsg.Span{ + .first = first_token, + .last = last_token, + }, + .tree = &parsed_file.tree, + }); + errdefer self.loop.allocator.destroy(msg); + + const compile_errors = await (async self.compile_errors.acquire() catch unreachable); + defer compile_errors.release(); + + try compile_errors.value.append(msg); + } + + async fn verifyUniqueSymbol(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void { const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable); defer exported_symbol_names.release(); if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { - @panic("TODO report compile error"); + try self.addCompileError( + parsed_file, + decl.getSpan(), + "exported symbol collision: '{}'", + decl.name, + ); } } @@ -503,6 +551,22 @@ pub const Decl = struct { } } + pub fn getSpan(base: *const Decl) errmsg.Span { + switch (base.id) { + Id.Fn => { + const fn_decl = @fieldParentPtr(Fn, "base", base); + const fn_proto = fn_decl.fn_proto; + const start = fn_proto.fn_token; + const end = fn_proto.name_token orelse start; + return errmsg.Span{ + .first = start, + .last = end + 1, + }; + }, + else => @panic("TODO"), + } + } + pub const Resolution = enum { Unresolved, InProgress, diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig new file mode 100644 index 0000000000..7ce7cf6ee3 --- /dev/null +++ b/src-self-hosted/test.zig @@ -0,0 +1,176 @@ +const std = @import("std"); +const mem = std.mem; +const builtin = @import("builtin"); +const Target = @import("target.zig").Target; +const Module = @import("module.zig").Module; +const introspect = @import("introspect.zig"); +const assertOrPanic = std.debug.assertOrPanic; +const errmsg = @import("errmsg.zig"); + +test "compile errors" { + var ctx: TestContext = undefined; + try ctx.init(); + defer ctx.deinit(); + + try ctx.testCompileError( + \\export fn entry() void {} + \\export fn entry() void {} + , file1, 2, 8, "exported symbol collision: 'entry'"); + + try ctx.run(); +} + +const file1 = "1.zig"; + +const TestContext = struct { + loop: std.event.Loop, + zig_lib_dir: []u8, + direct_allocator: std.heap.DirectAllocator, + arena: std.heap.ArenaAllocator, + zig_cache_dir: []u8, + file_index: std.atomic.Int(usize), + group: std.event.Group(error!void), + any_err: error!void, + + const tmp_dir_name = "stage2_test_tmp"; + + fn init(self: *TestContext) !void { + self.* = TestContext{ + .any_err = {}, + .direct_allocator = undefined, + .arena = undefined, + .loop = undefined, + .zig_lib_dir = undefined, + .zig_cache_dir = undefined, + .group = undefined, + .file_index = std.atomic.Int(usize).init(0), + }; + + self.direct_allocator = std.heap.DirectAllocator.init(); + errdefer self.direct_allocator.deinit(); + + self.arena = std.heap.ArenaAllocator.init(&self.direct_allocator.allocator); + errdefer self.arena.deinit(); + + // TODO faster allocator for coroutines that is thread-safe/lock-free + try self.loop.initMultiThreaded(&self.direct_allocator.allocator); + errdefer self.loop.deinit(); + + self.group = std.event.Group(error!void).init(&self.loop); + errdefer self.group.cancelAll(); + + self.zig_lib_dir = try introspect.resolveZigLibDir(&self.arena.allocator); + errdefer self.arena.allocator.free(self.zig_lib_dir); + + self.zig_cache_dir = try introspect.resolveZigCacheDir(&self.arena.allocator); + errdefer self.arena.allocator.free(self.zig_cache_dir); + + try std.os.makePath(&self.arena.allocator, tmp_dir_name); + errdefer std.os.deleteTree(&self.arena.allocator, tmp_dir_name) catch {}; + } + + fn deinit(self: *TestContext) void { + std.os.deleteTree(&self.arena.allocator, tmp_dir_name) catch {}; + self.arena.allocator.free(self.zig_cache_dir); + self.arena.allocator.free(self.zig_lib_dir); + self.loop.deinit(); + self.arena.deinit(); + self.direct_allocator.deinit(); + } + + fn run(self: *TestContext) !void { + const handle = try self.loop.call(waitForGroup, self); + defer cancel handle; + self.loop.run(); + return self.any_err; + } + + async fn waitForGroup(self: *TestContext) void { + self.any_err = await (async self.group.wait() catch unreachable); + } + + fn testCompileError( + self: *TestContext, + source: []const u8, + path: []const u8, + line: usize, + column: usize, + msg: []const u8, + ) !void { + var file_index_buf: [20]u8 = undefined; + const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.next()); + const file1_path = try std.os.path.join(&self.arena.allocator, tmp_dir_name, file_index, file1); + + if (std.os.path.dirname(file1_path)) |dirname| { + try std.os.makePath(&self.arena.allocator, dirname); + } + + // TODO async I/O + try std.io.writeFile(&self.arena.allocator, file1_path, source); + + var module = try Module.create( + &self.loop, + "test", + file1_path, + Target.Native, + Module.Kind.Obj, + builtin.Mode.Debug, + self.zig_lib_dir, + self.zig_cache_dir, + ); + errdefer module.destroy(); + + try module.build(); + + try self.group.call(getModuleEvent, module, source, path, line, column, msg); + } + + async fn getModuleEvent( + module: *Module, + source: []const u8, + path: []const u8, + line: usize, + column: usize, + text: []const u8, + ) !void { + defer module.destroy(); + const build_event = await (async module.events.get() catch unreachable); + + switch (build_event) { + Module.Event.Ok => { + @panic("build incorrectly succeeded"); + }, + Module.Event.Error => |err| { + @panic("build incorrectly failed"); + }, + Module.Event.Fail => |msgs| { + assertOrPanic(msgs.len != 0); + for (msgs) |msg| { + if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) { + const first_token = msg.tree.tokens.at(msg.span.first); + const last_token = msg.tree.tokens.at(msg.span.first); + const start_loc = msg.tree.tokenLocationPtr(0, first_token); + if (start_loc.line + 1 == line and start_loc.column + 1 == column) { + return; + } + } + } + std.debug.warn( + "\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n", + source, + path, + line, + column, + text, + ); + std.debug.warn("\n====found:========\n"); + var stderr = try std.io.getStdErr(); + for (msgs) |msg| { + try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto); + } + std.debug.warn("============\n"); + return error.TestFailed; + }, + } + } +}; diff --git a/src/main.cpp b/src/main.cpp index a409778a78..5f96953f21 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -891,15 +891,19 @@ int main(int argc, char **argv) { add_package(g, cur_pkg, g->root_package); - if (cmd == CmdBuild || cmd == CmdRun) { - codegen_set_emit_file_type(g, emit_file_type); - + if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) { for (size_t i = 0; i < objects.length; i += 1) { codegen_add_object(g, buf_create_from_str(objects.at(i))); } for (size_t i = 0; i < asm_files.length; i += 1) { codegen_add_assembly(g, buf_create_from_str(asm_files.at(i))); } + } + + + if (cmd == CmdBuild || cmd == CmdRun) { + codegen_set_emit_file_type(g, emit_file_type); + codegen_build(g); codegen_link(g, out_file); if (timing_info) diff --git a/std/atomic/index.zig b/std/atomic/index.zig index c0ea5be183..cf344a8231 100644 --- a/std/atomic/index.zig +++ b/std/atomic/index.zig @@ -1,9 +1,11 @@ pub const Stack = @import("stack.zig").Stack; pub const QueueMpsc = @import("queue_mpsc.zig").QueueMpsc; pub const QueueMpmc = @import("queue_mpmc.zig").QueueMpmc; +pub const Int = @import("int.zig").Int; test "std.atomic" { _ = @import("stack.zig"); _ = @import("queue_mpsc.zig"); _ = @import("queue_mpmc.zig"); + _ = @import("int.zig"); } diff --git a/std/atomic/int.zig b/std/atomic/int.zig new file mode 100644 index 0000000000..7042bca78d --- /dev/null +++ b/std/atomic/int.zig @@ -0,0 +1,19 @@ +const builtin = @import("builtin"); +const AtomicOrder = builtin.AtomicOrder; + +/// Thread-safe, lock-free integer +pub fn Int(comptime T: type) type { + return struct { + value: T, + + pub const Self = this; + + pub fn init(init_val: T) Self { + return Self{ .value = init_val }; + } + + pub fn next(self: *Self) T { + return @atomicRmw(T, &self.value, builtin.AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + } + }; +} diff --git a/std/build.zig b/std/build.zig index 24fa85383a..cea760e8a2 100644 --- a/std/build.zig +++ b/std/build.zig @@ -1596,6 +1596,8 @@ pub const TestStep = struct { target: Target, exec_cmd_args: ?[]const ?[]const u8, include_dirs: ArrayList([]const u8), + lib_paths: ArrayList([]const u8), + object_files: ArrayList([]const u8), pub fn init(builder: *Builder, root_src: []const u8) TestStep { const step_name = builder.fmt("test {}", root_src); @@ -1611,9 +1613,15 @@ pub const TestStep = struct { .target = Target{ .Native = {} }, .exec_cmd_args = null, .include_dirs = ArrayList([]const u8).init(builder.allocator), + .lib_paths = ArrayList([]const u8).init(builder.allocator), + .object_files = ArrayList([]const u8).init(builder.allocator), }; } + pub fn addLibPath(self: *TestStep, path: []const u8) void { + self.lib_paths.append(path) catch unreachable; + } + pub fn setVerbose(self: *TestStep, value: bool) void { self.verbose = value; } @@ -1638,6 +1646,10 @@ pub const TestStep = struct { self.filter = text; } + pub fn addObjectFile(self: *TestStep, path: []const u8) void { + self.object_files.append(path) catch unreachable; + } + pub fn setTarget(self: *TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { self.target = Target{ .Cross = CrossTarget{ @@ -1699,6 +1711,11 @@ pub const TestStep = struct { try zig_args.append(self.name_prefix); } + for (self.object_files.toSliceConst()) |object_file| { + try zig_args.append("--object"); + try zig_args.append(builder.pathFromRoot(object_file)); + } + { var it = self.link_libs.iterator(); while (true) { @@ -1734,6 +1751,11 @@ pub const TestStep = struct { try zig_args.append(rpath); } + for (self.lib_paths.toSliceConst()) |lib_path| { + try zig_args.append("--library-path"); + try zig_args.append(lib_path); + } + for (builder.lib_paths.toSliceConst()) |lib_path| { try zig_args.append("--library-path"); try zig_args.append(lib_path); -- cgit v1.2.3 From 8197a14ceb2938c64526c7b84e2ac8da343960fa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 Jul 2018 20:27:15 -0400 Subject: self-hosted test: use C allocator since we depend on libc --- src-self-hosted/test.zig | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 7ce7cf6ee3..01a857f21d 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -21,12 +21,11 @@ test "compile errors" { } const file1 = "1.zig"; +const allocator = std.heap.c_allocator; const TestContext = struct { loop: std.event.Loop, zig_lib_dir: []u8, - direct_allocator: std.heap.DirectAllocator, - arena: std.heap.ArenaAllocator, zig_cache_dir: []u8, file_index: std.atomic.Int(usize), group: std.event.Group(error!void), @@ -37,8 +36,6 @@ const TestContext = struct { fn init(self: *TestContext) !void { self.* = TestContext{ .any_err = {}, - .direct_allocator = undefined, - .arena = undefined, .loop = undefined, .zig_lib_dir = undefined, .zig_cache_dir = undefined, @@ -46,36 +43,27 @@ const TestContext = struct { .file_index = std.atomic.Int(usize).init(0), }; - self.direct_allocator = std.heap.DirectAllocator.init(); - errdefer self.direct_allocator.deinit(); - - self.arena = std.heap.ArenaAllocator.init(&self.direct_allocator.allocator); - errdefer self.arena.deinit(); - - // TODO faster allocator for coroutines that is thread-safe/lock-free - try self.loop.initMultiThreaded(&self.direct_allocator.allocator); + try self.loop.initMultiThreaded(allocator); errdefer self.loop.deinit(); self.group = std.event.Group(error!void).init(&self.loop); errdefer self.group.cancelAll(); - self.zig_lib_dir = try introspect.resolveZigLibDir(&self.arena.allocator); - errdefer self.arena.allocator.free(self.zig_lib_dir); + self.zig_lib_dir = try introspect.resolveZigLibDir(allocator); + errdefer allocator.free(self.zig_lib_dir); - self.zig_cache_dir = try introspect.resolveZigCacheDir(&self.arena.allocator); - errdefer self.arena.allocator.free(self.zig_cache_dir); + self.zig_cache_dir = try introspect.resolveZigCacheDir(allocator); + errdefer allocator.free(self.zig_cache_dir); - try std.os.makePath(&self.arena.allocator, tmp_dir_name); - errdefer std.os.deleteTree(&self.arena.allocator, tmp_dir_name) catch {}; + try std.os.makePath(allocator, tmp_dir_name); + errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {}; } fn deinit(self: *TestContext) void { - std.os.deleteTree(&self.arena.allocator, tmp_dir_name) catch {}; - self.arena.allocator.free(self.zig_cache_dir); - self.arena.allocator.free(self.zig_lib_dir); + std.os.deleteTree(allocator, tmp_dir_name) catch {}; + allocator.free(self.zig_cache_dir); + allocator.free(self.zig_lib_dir); self.loop.deinit(); - self.arena.deinit(); - self.direct_allocator.deinit(); } fn run(self: *TestContext) !void { @@ -99,14 +87,14 @@ const TestContext = struct { ) !void { var file_index_buf: [20]u8 = undefined; const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.next()); - const file1_path = try std.os.path.join(&self.arena.allocator, tmp_dir_name, file_index, file1); + const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1); if (std.os.path.dirname(file1_path)) |dirname| { - try std.os.makePath(&self.arena.allocator, dirname); + try std.os.makePath(allocator, dirname); } // TODO async I/O - try std.io.writeFile(&self.arena.allocator, file1_path, source); + try std.io.writeFile(allocator, file1_path, source); var module = try Module.create( &self.loop, -- cgit v1.2.3 From c6c49389ebd1503de38d9bb6ff6d9f6fba94d63b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 11 Jul 2018 01:26:46 -0400 Subject: self-hosted: add compile error test for missing fn name --- src-self-hosted/module.zig | 11 ++++------- src-self-hosted/test.zig | 4 ++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 44954e4cd1..4b0c44529b 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -347,13 +347,10 @@ pub const Module = struct { const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { - @panic("TODO add compile error"); - //try self.addCompileError( - // parsed_file, - // fn_proto.fn_token, - // fn_proto.fn_token + 1, - // "missing function name", - //); + try self.addCompileError(parsed_file, errmsg.Span{ + .first = fn_proto.fn_token, + .last = fn_proto.fn_token + 1, + }, "missing function name"); continue; }; diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 01a857f21d..ffad7f1b8d 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -17,6 +17,10 @@ test "compile errors" { \\export fn entry() void {} , file1, 2, 8, "exported symbol collision: 'entry'"); + try ctx.testCompileError( + \\fn() void {} + , file1, 1, 1, "missing function name"); + try ctx.run(); } -- cgit v1.2.3 From 9b054e73f6b9f946a404a7f0c8b6a90d83e4b928 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Wed, 11 Jul 2018 18:16:58 +1200 Subject: Add generic comparator generator functions for sorting - Copy-by-value instead of pointer where appropriate - Clean up old zig fmt issues --- std/macho.zig | 2 +- std/sort.zig | 325 ++++++++++++++++------------------------------------------ 2 files changed, 87 insertions(+), 240 deletions(-) diff --git a/std/macho.zig b/std/macho.zig index fe5409ad4d..33c170ff43 100644 --- a/std/macho.zig +++ b/std/macho.zig @@ -42,7 +42,7 @@ pub const Symbol = struct { name: []const u8, address: u64, - fn addressLessThan(lhs: *const Symbol, rhs: *const Symbol) bool { + fn addressLessThan(lhs: Symbol, rhs: Symbol) bool { return lhs.address < rhs.address; } }; diff --git a/std/sort.zig b/std/sort.zig index 1b44c18dd9..f29f51b7cf 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -5,7 +5,7 @@ const math = std.math; const builtin = @import("builtin"); /// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required). -pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) void { +pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) void { { var i: usize = 1; while (i < items.len) : (i += 1) { @@ -30,7 +30,7 @@ const Range = struct { }; } - fn length(self: *const Range) usize { + fn length(self: Range) usize { return self.end - self.start; } }; @@ -108,7 +108,7 @@ const Pull = struct { /// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required). /// Currently implemented as block sort. -pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) void { +pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) void { // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c var cache: [512]T = undefined; @@ -131,16 +131,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *con // http://pages.ripco.net/~jgamble/nw.html var iterator = Iterator.init(items.len, 4); while (!iterator.finished()) { - var order = []u8{ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - }; + var order = []u8{ 0, 1, 2, 3, 4, 5, 6, 7 }; const range = iterator.nextRange(); const sliced_items = items[range.start..]; @@ -741,7 +732,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *con } // merge operation without a buffer -fn mergeInPlace(comptime T: type, items: []T, A_arg: *const Range, B_arg: *const Range, lessThan: fn (*const T, *const T) bool) void { +fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessThan: fn (T, T) bool) void { if (A_arg.length() == 0 or B_arg.length() == 0) return; // this just repeatedly binary searches into B and rotates A into position. @@ -762,8 +753,8 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: *const Range, B_arg: *const // again, this is NOT a general-purpose solution – it only works well in this case! // kind of like how the O(n^2) insertion sort is used in some places - var A = A_arg.*; - var B = B_arg.*; + var A = A_arg; + var B = B_arg; while (true) { // find the first place in B where the first item in A needs to be inserted @@ -783,7 +774,7 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: *const Range, B_arg: *const } // merge operation using an internal buffer -fn mergeInternal(comptime T: type, items: []T, A: *const Range, B: *const Range, lessThan: fn (*const T, *const T) bool, buffer: *const Range) void { +fn mergeInternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn (T, T) bool, buffer: Range) void { // whenever we find a value to add to the final array, swap it with the value that's already in that spot // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order var A_count: usize = 0; @@ -819,7 +810,7 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s // combine a linear search with a binary search to reduce the number of comparisons in situations // where have some idea as to how many unique values there are and where the next value might be -fn findFirstForward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize { +fn findFirstForward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -833,7 +824,7 @@ fn findFirstForward(comptime T: type, items: []T, value: *const T, range: *const return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan); } -fn findFirstBackward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize { +fn findFirstBackward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -847,7 +838,7 @@ fn findFirstBackward(comptime T: type, items: []T, value: *const T, range: *cons return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan); } -fn findLastForward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize { +fn findLastForward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -861,7 +852,7 @@ fn findLastForward(comptime T: type, items: []T, value: *const T, range: *const return binaryLast(T, items, value, Range.init(index - skip, index), lessThan); } -fn findLastBackward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize { +fn findLastBackward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -875,7 +866,7 @@ fn findLastBackward(comptime T: type, items: []T, value: *const T, range: *const return binaryLast(T, items, value, Range.init(index, index + skip), lessThan); } -fn binaryFirst(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool) usize { +fn binaryFirst(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool) usize { var start = range.start; var end = range.end - 1; if (range.start >= range.end) return range.end; @@ -893,7 +884,7 @@ fn binaryFirst(comptime T: type, items: []T, value: *const T, range: *const Rang return start; } -fn binaryLast(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool) usize { +fn binaryLast(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool) usize { var start = range.start; var end = range.end - 1; if (range.start >= range.end) return range.end; @@ -911,7 +902,7 @@ fn binaryLast(comptime T: type, items: []T, value: *const T, range: *const Range return start; } -fn mergeInto(comptime T: type, from: []T, A: *const Range, B: *const Range, lessThan: fn (*const T, *const T) bool, into: []T) void { +fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T) bool, into: []T) void { var A_index: usize = A.start; var B_index: usize = B.start; const A_last = A.end; @@ -941,7 +932,7 @@ fn mergeInto(comptime T: type, from: []T, A: *const Range, B: *const Range, less } } -fn mergeExternal(comptime T: type, items: []T, A: *const Range, B: *const Range, lessThan: fn (*const T, *const T) bool, cache: []T) void { +fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn (T, T) bool, cache: []T) void { // A fits into the cache, so use that instead of the internal buffer var A_index: usize = 0; var B_index: usize = B.start; @@ -969,27 +960,32 @@ fn mergeExternal(comptime T: type, items: []T, A: *const Range, B: *const Range, mem.copy(T, items[insert_index..], cache[A_index..A_last]); } -fn swap(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool, order: *[8]u8, x: usize, y: usize) void { +fn swap(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool, order: *[8]u8, x: usize, y: usize) void { if (lessThan(items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(items[x], items[y]))) { mem.swap(T, &items[x], &items[y]); mem.swap(u8, &(order.*)[x], &(order.*)[y]); } } -fn i32asc(lhs: *const i32, rhs: *const i32) bool { - return lhs.* < rhs.*; -} +// Use these to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`. +pub fn asc(comptime T: type) fn (T, T) bool { + const impl = struct { + fn inner(a: T, b: T) bool { + return a < b; + } + }; -fn i32desc(lhs: *const i32, rhs: *const i32) bool { - return rhs.* < lhs.*; + return impl.inner; } -fn u8asc(lhs: *const u8, rhs: *const u8) bool { - return lhs.* < rhs.*; -} +pub fn desc(comptime T: type) fn (T, T) bool { + const impl = struct { + fn inner(a: T, b: T) bool { + return a > b; + } + }; -fn u8desc(lhs: *const u8, rhs: *const u8) bool { - return rhs.* < lhs.*; + return impl.inner; } test "stable sort" { @@ -998,119 +994,38 @@ test "stable sort" { } fn testStableSort() void { var expected = []IdAndValue{ - IdAndValue{ - .id = 0, - .value = 0, - }, - IdAndValue{ - .id = 1, - .value = 0, - }, - IdAndValue{ - .id = 2, - .value = 0, - }, - IdAndValue{ - .id = 0, - .value = 1, - }, - IdAndValue{ - .id = 1, - .value = 1, - }, - IdAndValue{ - .id = 2, - .value = 1, - }, - IdAndValue{ - .id = 0, - .value = 2, - }, - IdAndValue{ - .id = 1, - .value = 2, - }, - IdAndValue{ - .id = 2, - .value = 2, - }, + IdAndValue{ .id = 0, .value = 0 }, + IdAndValue{ .id = 1, .value = 0 }, + IdAndValue{ .id = 2, .value = 0 }, + IdAndValue{ .id = 0, .value = 1 }, + IdAndValue{ .id = 1, .value = 1 }, + IdAndValue{ .id = 2, .value = 1 }, + IdAndValue{ .id = 0, .value = 2 }, + IdAndValue{ .id = 1, .value = 2 }, + IdAndValue{ .id = 2, .value = 2 }, }; var cases = [][9]IdAndValue{ []IdAndValue{ - IdAndValue{ - .id = 0, - .value = 0, - }, - IdAndValue{ - .id = 0, - .value = 1, - }, - IdAndValue{ - .id = 0, - .value = 2, - }, - IdAndValue{ - .id = 1, - .value = 0, - }, - IdAndValue{ - .id = 1, - .value = 1, - }, - IdAndValue{ - .id = 1, - .value = 2, - }, - IdAndValue{ - .id = 2, - .value = 0, - }, - IdAndValue{ - .id = 2, - .value = 1, - }, - IdAndValue{ - .id = 2, - .value = 2, - }, + IdAndValue{ .id = 0, .value = 0 }, + IdAndValue{ .id = 0, .value = 1 }, + IdAndValue{ .id = 0, .value = 2 }, + IdAndValue{ .id = 1, .value = 0 }, + IdAndValue{ .id = 1, .value = 1 }, + IdAndValue{ .id = 1, .value = 2 }, + IdAndValue{ .id = 2, .value = 0 }, + IdAndValue{ .id = 2, .value = 1 }, + IdAndValue{ .id = 2, .value = 2 }, }, []IdAndValue{ - IdAndValue{ - .id = 0, - .value = 2, - }, - IdAndValue{ - .id = 0, - .value = 1, - }, - IdAndValue{ - .id = 0, - .value = 0, - }, - IdAndValue{ - .id = 1, - .value = 2, - }, - IdAndValue{ - .id = 1, - .value = 1, - }, - IdAndValue{ - .id = 1, - .value = 0, - }, - IdAndValue{ - .id = 2, - .value = 2, - }, - IdAndValue{ - .id = 2, - .value = 1, - }, - IdAndValue{ - .id = 2, - .value = 0, - }, + IdAndValue{ .id = 0, .value = 2 }, + IdAndValue{ .id = 0, .value = 1 }, + IdAndValue{ .id = 0, .value = 0 }, + IdAndValue{ .id = 1, .value = 2 }, + IdAndValue{ .id = 1, .value = 1 }, + IdAndValue{ .id = 1, .value = 0 }, + IdAndValue{ .id = 2, .value = 2 }, + IdAndValue{ .id = 2, .value = 1 }, + IdAndValue{ .id = 2, .value = 0 }, }, }; for (cases) |*case| { @@ -1125,8 +1040,8 @@ const IdAndValue = struct { id: usize, value: i32, }; -fn cmpByValue(a: *const IdAndValue, b: *const IdAndValue) bool { - return i32asc(a.value, b.value); +fn cmpByValue(a: IdAndValue, b: IdAndValue) bool { + return asc(i32)(a.value, b.value); } test "std.sort" { @@ -1161,7 +1076,7 @@ test "std.sort" { var buf: [8]u8 = undefined; const slice = buf[0..case[0].len]; mem.copy(u8, slice, case[0]); - sort(u8, slice, u8asc); + sort(u8, slice, asc(u8)); assert(mem.eql(u8, slice, case[1])); } @@ -1175,48 +1090,20 @@ test "std.sort" { []i32{1}, }, [][]const i32{ - []i32{ - 0, - 1, - }, - []i32{ - 0, - 1, - }, + []i32{ 0, 1 }, + []i32{ 0, 1 }, }, [][]const i32{ - []i32{ - 1, - 0, - }, - []i32{ - 0, - 1, - }, + []i32{ 1, 0 }, + []i32{ 0, 1 }, }, [][]const i32{ - []i32{ - 1, - -1, - 0, - }, - []i32{ - -1, - 0, - 1, - }, + []i32{ 1, -1, 0 }, + []i32{ -1, 0, 1 }, }, [][]const i32{ - []i32{ - 2, - 1, - 3, - }, - []i32{ - 1, - 2, - 3, - }, + []i32{ 2, 1, 3 }, + []i32{ 1, 2, 3 }, }, }; @@ -1224,7 +1111,7 @@ test "std.sort" { var buf: [8]i32 = undefined; const slice = buf[0..case[0].len]; mem.copy(i32, slice, case[0]); - sort(i32, slice, i32asc); + sort(i32, slice, asc(i32)); assert(mem.eql(i32, slice, case[1])); } } @@ -1240,48 +1127,20 @@ test "std.sort descending" { []i32{1}, }, [][]const i32{ - []i32{ - 0, - 1, - }, - []i32{ - 1, - 0, - }, + []i32{ 0, 1 }, + []i32{ 1, 0 }, }, [][]const i32{ - []i32{ - 1, - 0, - }, - []i32{ - 1, - 0, - }, + []i32{ 1, 0 }, + []i32{ 1, 0 }, }, [][]const i32{ - []i32{ - 1, - -1, - 0, - }, - []i32{ - 1, - 0, - -1, - }, + []i32{ 1, -1, 0 }, + []i32{ 1, 0, -1 }, }, [][]const i32{ - []i32{ - 2, - 1, - 3, - }, - []i32{ - 3, - 2, - 1, - }, + []i32{ 2, 1, 3 }, + []i32{ 3, 2, 1 }, }, }; @@ -1289,28 +1148,16 @@ test "std.sort descending" { var buf: [8]i32 = undefined; const slice = buf[0..case[0].len]; mem.copy(i32, slice, case[0]); - sort(i32, slice, i32desc); + sort(i32, slice, desc(i32)); assert(mem.eql(i32, slice, case[1])); } } test "another sort case" { - var arr = []i32{ - 5, - 3, - 1, - 2, - 4, - }; - sort(i32, arr[0..], i32asc); - - assert(mem.eql(i32, arr, []i32{ - 1, - 2, - 3, - 4, - 5, - })); + var arr = []i32{ 5, 3, 1, 2, 4 }; + sort(i32, arr[0..], asc(i32)); + + assert(mem.eql(i32, arr, []i32{ 1, 2, 3, 4, 5 })); } test "sort fuzz testing" { @@ -1345,7 +1192,7 @@ fn fuzzTest(rng: *std.rand.Random) void { } } -pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) T { +pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) T { var i: usize = 0; var smallest = items[0]; for (items[1..]) |item| { @@ -1356,7 +1203,7 @@ pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *cons return smallest; } -pub fn max(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) T { +pub fn max(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) T { var i: usize = 0; var biggest = items[0]; for (items[1..]) |item| { -- cgit v1.2.3 From 3f30897fdcdb6c5579bc5609dda9746f67551870 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 11 Jul 2018 13:23:37 -0400 Subject: add compile error for disallowed types in extern structs closes #1218 --- src/analyze.cpp | 23 ++++++++++++++++++++--- std/c/darwin.zig | 2 +- test/compile_errors.zig | 27 +++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 9b60f7374a..5635cce411 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1430,10 +1430,10 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdPromise: + case TypeTableEntryIdVoid: return false; case TypeTableEntryIdOpaque: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdVoid: case TypeTableEntryIdBool: return true; case TypeTableEntryIdInt: @@ -1460,7 +1460,10 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdOptional: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; - return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn; + if (child_type->id != TypeTableEntryIdPointer && child_type->id != TypeTableEntryIdFn) { + return false; + } + return type_allowed_in_extern(g, child_type); } case TypeTableEntryIdEnum: return type_entry->data.enumeration.layout == ContainerLayoutExtern || type_entry->data.enumeration.layout == ContainerLayoutPacked; @@ -1637,7 +1640,10 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c fn_type_id.return_type = specified_return_type; } - if (!calling_convention_allows_zig_types(fn_type_id.cc) && !type_allowed_in_extern(g, fn_type_id.return_type)) { + if (!calling_convention_allows_zig_types(fn_type_id.cc) && + fn_type_id.return_type->id != TypeTableEntryIdVoid && + !type_allowed_in_extern(g, fn_type_id.return_type)) + { add_node_error(g, fn_proto->return_type, buf_sprintf("return type '%s' not allowed in function with calling convention '%s'", buf_ptr(&fn_type_id.return_type->name), @@ -1939,6 +1945,17 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) { break; } + if (struct_type->data.structure.layout == ContainerLayoutExtern) { + if (!type_allowed_in_extern(g, field_type)) { + AstNode *field_source_node = decl_node->data.container_decl.fields.at(i); + add_node_error(g, field_source_node, + buf_sprintf("extern structs cannot contain fields of type '%s'", + buf_ptr(&field_type->name))); + struct_type->data.structure.is_invalid = true; + break; + } + } + if (!type_has_bits(field_type)) continue; diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 133ef62f05..4189dfeadc 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -44,7 +44,7 @@ pub const timezone = extern struct { tz_dsttime: i32, }; -pub const mach_timebase_info_data = struct { +pub const mach_timebase_info_data = extern struct { numer: u32, denom: u32, }; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index a6db8d50b4..58c73b8ae4 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,33 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "optional pointer to void in extern struct", + \\const Foo = extern struct { + \\ x: ?*const void, + \\}; + \\const Bar = extern struct { + \\ foo: Foo, + \\ y: i32, + \\}; + \\export fn entry(bar: *Bar) void {} + , + ".tmp_source.zig:2:5: error: extern structs cannot contain fields of type '?*const void'", + ); + + cases.add( + "use of comptime-known undefined function value", + \\const Cmd = struct { + \\ exec: fn () void, + \\}; + \\export fn entry() void { + \\ const command = Cmd{ .exec = undefined }; + \\ command.exec(); + \\} + , + ".tmp_source.zig:6:12: error: use of undefined value", + ); + cases.add( "use of comptime-known undefined function value", \\const Cmd = struct { -- cgit v1.2.3 From 5954c94d2079c9b378ae14bb4c9c24aed719beec Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 11 Jul 2018 14:09:05 -0400 Subject: build system: add -Dskip-release option to test faster --- build.zig | 17 ++++++++++++----- test/tests.zig | 21 ++------------------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/build.zig b/build.zig index 273048d458..fd37138f33 100644 --- a/build.zig +++ b/build.zig @@ -59,6 +59,7 @@ pub fn build(b: *Builder) !void { b.default_step.dependOn(&exe.step); + const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false; if (!skip_self_hosted) { test_step.dependOn(&exe.step); @@ -71,19 +72,24 @@ pub fn build(b: *Builder) !void { installCHeaders(b, ctx.c_header_files); const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); - const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") orelse false; const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); test_stage2_step.dependOn(&test_stage2.step); test_step.dependOn(test_stage2_step); - test_step.dependOn(docs_step); + const all_modes = []builtin.Mode{ + builtin.Mode.Debug, + builtin.Mode.ReleaseSafe, + builtin.Mode.ReleaseFast, + builtin.Mode.ReleaseSmall, + }; + const modes = if (skip_release) []builtin.Mode{builtin.Mode.Debug} else all_modes; - test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", with_lldb)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", modes)); - test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", with_lldb)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", modes)); - test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", with_lldb)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes)); test_step.dependOn(tests.addCompareOutputTests(b, test_filter)); test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); @@ -92,6 +98,7 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter)); test_step.dependOn(tests.addTranslateCTests(b, test_filter)); test_step.dependOn(tests.addGenHTests(b, test_filter)); + test_step.dependOn(docs_step); } fn dependOnLib(lib_exe_obj: var, dep: *const LibraryDep) void { diff --git a/test/tests.zig b/test/tests.zig index 66eb2d93a0..b1453776a8 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -138,16 +138,11 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { return cases.step; } -pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []const u8, name: []const u8, desc: []const u8, with_lldb: bool) *build.Step { +pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []const u8, name: []const u8, desc: []const u8, modes: []const Mode) *build.Step { const step = b.step(b.fmt("test-{}", name), desc); for (test_targets) |test_target| { const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch); - for ([]Mode{ - Mode.Debug, - Mode.ReleaseSafe, - Mode.ReleaseFast, - Mode.ReleaseSmall, - }) |mode| { + for (modes) |mode| { for ([]bool{ false, true, @@ -166,18 +161,6 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons if (link_libc) { these_tests.linkSystemLibrary("c"); } - if (with_lldb) { - these_tests.setExecCmd([]?[]const u8{ - "lldb", - null, - "-o", - "run", - "-o", - "bt", - "-o", - "exit", - }); - } step.dependOn(&these_tests.step); } } -- cgit v1.2.3 From 9bdcd2a495d4189d6536d43f1294dffb38daa9a5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 11 Jul 2018 15:58:48 -0400 Subject: add std.event.Future This is like a promise, but it's for multiple getters, and uses an event loop. --- CMakeLists.txt | 1 + src-self-hosted/module.zig | 19 ++++++++- src-self-hosted/test.zig | 11 +----- std/event.zig | 2 + std/event/future.zig | 87 ++++++++++++++++++++++++++++++++++++++++++ std/event/lock.zig | 11 +++++- test/stage2/compile_errors.zig | 12 ++++++ 7 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 std/event/future.zig create mode 100644 test/stage2/compile_errors.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 559b3b6964..51d348f042 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -460,6 +460,7 @@ set(ZIG_STD_FILES "empty.zig" "event.zig" "event/channel.zig" + "event/future.zig" "event/group.zig" "event/lock.zig" "event/locked.zig" diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 4b0c44529b..5cde12f65c 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -381,6 +381,7 @@ pub const Module = struct { if (is_export) { try self.build_group.call(verifyUniqueSymbol, self, parsed_file, decl); + try self.build_group.call(generateDecl, self, parsed_file, decl); } } @@ -429,6 +430,22 @@ pub const Module = struct { } } + /// This declaration has been blessed as going into the final code generation. + async fn generateDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) void { + switch (decl.id) { + Decl.Id.Var => @panic("TODO"), + Decl.Id.Fn => { + const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl); + return await (async self.generateDeclFn(parsed_file, fn_decl) catch unreachable); + }, + Decl.Id.CompTime => @panic("TODO"), + } + } + + async fn generateDeclFn(self: *Module, parsed_file: *ParsedFile, fn_decl: *Decl.Fn) void { + fn_decl.value = Decl.Fn.Val{ .Ok = Value.Fn{} }; + } + pub fn link(self: *Module, out_file: ?[]const u8) !void { warn("TODO link"); return error.Todo; @@ -589,7 +606,7 @@ pub const Decl = struct { // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous pub const Val = union { Unresolved: void, - Ok: *Value.Fn, + Ok: Value.Fn, }; pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index ffad7f1b8d..4455352f95 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -12,14 +12,7 @@ test "compile errors" { try ctx.init(); defer ctx.deinit(); - try ctx.testCompileError( - \\export fn entry() void {} - \\export fn entry() void {} - , file1, 2, 8, "exported symbol collision: 'entry'"); - - try ctx.testCompileError( - \\fn() void {} - , file1, 1, 1, "missing function name"); + try @import("../test/stage2/compile_errors.zig").addCases(&ctx); try ctx.run(); } @@ -27,7 +20,7 @@ test "compile errors" { const file1 = "1.zig"; const allocator = std.heap.c_allocator; -const TestContext = struct { +pub const TestContext = struct { loop: std.event.Loop, zig_lib_dir: []u8, zig_cache_dir: []u8, diff --git a/std/event.zig b/std/event.zig index 516defebf8..f3913a432b 100644 --- a/std/event.zig +++ b/std/event.zig @@ -4,6 +4,7 @@ pub const Lock = @import("event/lock.zig").Lock; pub const tcp = @import("event/tcp.zig"); pub const Channel = @import("event/channel.zig").Channel; pub const Group = @import("event/group.zig").Group; +pub const Future = @import("event/future.zig").Group; test "import event tests" { _ = @import("event/locked.zig"); @@ -12,4 +13,5 @@ test "import event tests" { _ = @import("event/tcp.zig"); _ = @import("event/channel.zig"); _ = @import("event/group.zig"); + _ = @import("event/future.zig"); } diff --git a/std/event/future.zig b/std/event/future.zig new file mode 100644 index 0000000000..8001f675a2 --- /dev/null +++ b/std/event/future.zig @@ -0,0 +1,87 @@ +const std = @import("../index.zig"); +const assert = std.debug.assert; +const builtin = @import("builtin"); +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; +const Lock = std.event.Lock; +const Loop = std.event.Loop; + +/// This is a value that starts out unavailable, until a value is put(). +/// While it is unavailable, coroutines suspend when they try to get() it, +/// and then are resumed when the value is put(). +/// At this point the value remains forever available, and another put() is not allowed. +pub fn Future(comptime T: type) type { + return struct { + lock: Lock, + data: T, + available: u8, // TODO make this a bool + + const Self = this; + const Queue = std.atomic.QueueMpsc(promise); + + pub fn init(loop: *Loop) Self { + return Self{ + .lock = Lock.initLocked(loop), + .available = 0, + .data = undefined, + }; + } + + /// Obtain the value. If it's not available, wait until it becomes + /// available. + /// Thread-safe. + pub async fn get(self: *Self) T { + if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { + return self.data; + } + const held = await (async self.lock.acquire() catch unreachable); + defer held.release(); + + return self.data; + } + + /// Make the data become available. May be called only once. + pub fn put(self: *Self, value: T) void { + self.data = value; + const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + assert(prev == 0); // put() called twice + Lock.Held.release(Lock.Held{ .lock = &self.lock }); + } + }; +} + +test "std.event.Future" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const allocator = &da.allocator; + + var loop: Loop = undefined; + try loop.initMultiThreaded(allocator); + defer loop.deinit(); + + const handle = try async testFuture(&loop); + defer cancel handle; + + loop.run(); +} + +async fn testFuture(loop: *Loop) void { + var future = Future(i32).init(loop); + + const a = async waitOnFuture(&future) catch @panic("memory"); + const b = async waitOnFuture(&future) catch @panic("memory"); + const c = async resolveFuture(&future) catch @panic("memory"); + + const result = (await a) + (await b); + cancel c; + assert(result == 12); +} + +async fn waitOnFuture(future: *Future(i32)) i32 { + return await (async future.get() catch @panic("memory")); +} + +async fn resolveFuture(future: *Future(i32)) void { + future.put(6); +} diff --git a/std/event/lock.zig b/std/event/lock.zig index 2a8d5ada77..cba3594b50 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -73,6 +73,15 @@ pub const Lock = struct { }; } + pub fn initLocked(loop: *Loop) Lock { + return Lock{ + .loop = loop, + .shared_bit = 1, + .queue = Queue.init(), + .queue_empty_bit = 1, + }; + } + /// Must be called when not locked. Not thread safe. /// All calls to acquire() and release() must complete before calling deinit(). pub fn deinit(self: *Lock) void { @@ -81,7 +90,7 @@ pub const Lock = struct { } pub async fn acquire(self: *Lock) Held { - s: suspend |handle| { + suspend |handle| { // TODO explicitly put this memory in the coroutine frame #1194 var my_tick_node = Loop.NextTickNode{ .data = handle, diff --git a/test/stage2/compile_errors.zig b/test/stage2/compile_errors.zig new file mode 100644 index 0000000000..1dca908e69 --- /dev/null +++ b/test/stage2/compile_errors.zig @@ -0,0 +1,12 @@ +const TestContext = @import("../../src-self-hosted/test.zig").TestContext; + +pub fn addCases(ctx: *TestContext) !void { + try ctx.testCompileError( + \\export fn entry() void {} + \\export fn entry() void {} + , "1.zig", 2, 8, "exported symbol collision: 'entry'"); + + try ctx.testCompileError( + \\fn() void {} + , "1.zig", 1, 1, "missing function name"); +} -- cgit v1.2.3 From 9751a0ae045110fb615c866b94ad47680b9c48c7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 11 Jul 2018 19:38:01 -0400 Subject: std.atomic: use spinlocks the lock-free data structures all had ABA problems and std.atomic.Stack had a possibility to load an unmapped memory address. --- CMakeLists.txt | 3 +- build.zig | 8 +- std/atomic/index.zig | 6 +- std/atomic/queue.zig | 226 ++++++++++++++++++++++++++++++++++++++++++++++ std/atomic/queue_mpmc.zig | 214 ------------------------------------------- std/atomic/queue_mpsc.zig | 185 ------------------------------------- std/atomic/stack.zig | 32 ++++--- std/event/channel.zig | 12 +-- std/event/future.zig | 21 +++-- std/event/lock.zig | 2 +- std/event/loop.zig | 6 +- test/tests.zig | 26 +++--- 12 files changed, 286 insertions(+), 455 deletions(-) create mode 100644 std/atomic/queue.zig delete mode 100644 std/atomic/queue_mpmc.zig delete mode 100644 std/atomic/queue_mpsc.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 51d348f042..e606855555 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -432,8 +432,7 @@ set(ZIG_STD_FILES "array_list.zig" "atomic/index.zig" "atomic/int.zig" - "atomic/queue_mpmc.zig" - "atomic/queue_mpsc.zig" + "atomic/queue.zig" "atomic/stack.zig" "base64.zig" "buf_map.zig" diff --git a/build.zig b/build.zig index fd37138f33..c9e70887e3 100644 --- a/build.zig +++ b/build.zig @@ -91,11 +91,11 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes)); - test_step.dependOn(tests.addCompareOutputTests(b, test_filter)); + test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); - test_step.dependOn(tests.addCompileErrorTests(b, test_filter)); - test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter)); - test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter)); + test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); + test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); + test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); test_step.dependOn(tests.addTranslateCTests(b, test_filter)); test_step.dependOn(tests.addGenHTests(b, test_filter)); test_step.dependOn(docs_step); diff --git a/std/atomic/index.zig b/std/atomic/index.zig index cf344a8231..a94cff1973 100644 --- a/std/atomic/index.zig +++ b/std/atomic/index.zig @@ -1,11 +1,9 @@ pub const Stack = @import("stack.zig").Stack; -pub const QueueMpsc = @import("queue_mpsc.zig").QueueMpsc; -pub const QueueMpmc = @import("queue_mpmc.zig").QueueMpmc; +pub const Queue = @import("queue.zig").Queue; pub const Int = @import("int.zig").Int; test "std.atomic" { _ = @import("stack.zig"); - _ = @import("queue_mpsc.zig"); - _ = @import("queue_mpmc.zig"); + _ = @import("queue.zig"); _ = @import("int.zig"); } diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig new file mode 100644 index 0000000000..1fd07714e8 --- /dev/null +++ b/std/atomic/queue.zig @@ -0,0 +1,226 @@ +const builtin = @import("builtin"); +const AtomicOrder = builtin.AtomicOrder; +const AtomicRmwOp = builtin.AtomicRmwOp; + +/// Many producer, many consumer, non-allocating, thread-safe. +/// Uses a spinlock to protect get() and put(). +pub fn Queue(comptime T: type) type { + return struct { + head: ?*Node, + tail: ?*Node, + lock: u8, + + pub const Self = this; + + pub const Node = struct { + next: ?*Node, + data: T, + }; + + pub fn init() Self { + return Self{ + .head = null, + .tail = null, + .lock = 0, + }; + } + + pub fn put(self: *Self, node: *Node) void { + node.next = null; + + while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} + defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + + const opt_tail = self.tail; + self.tail = node; + if (opt_tail) |tail| { + tail.next = node; + } else { + assert(self.head == null); + self.head = node; + } + } + + pub fn get(self: *Self) ?*Node { + while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} + defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + + const head = self.head orelse return null; + self.head = head.next; + if (head.next == null) self.tail = null; + return head; + } + + pub fn isEmpty(self: *Self) bool { + return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null; + } + + pub fn dump(self: *Self) void { + while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} + defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + + std.debug.warn("head: "); + dumpRecursive(self.head, 0); + std.debug.warn("tail: "); + dumpRecursive(self.tail, 0); + } + + fn dumpRecursive(optional_node: ?*Node, indent: usize) void { + var stderr_file = std.io.getStdErr() catch return; + const stderr = &std.io.FileOutStream.init(&stderr_file).stream; + stderr.writeByteNTimes(' ', indent) catch return; + if (optional_node) |node| { + std.debug.warn("0x{x}={}\n", @ptrToInt(node), node.data); + dumpRecursive(node.next, indent + 1); + } else { + std.debug.warn("(null)\n"); + } + } + }; +} + +const std = @import("../index.zig"); +const assert = std.debug.assert; + +const Context = struct { + allocator: *std.mem.Allocator, + queue: *Queue(i32), + put_sum: isize, + get_sum: isize, + get_count: usize, + puts_done: u8, // TODO make this a bool +}; + +// TODO add lazy evaluated build options and then put puts_per_thread behind +// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor +// CI we would use a less aggressive setting since at 1 core, while we still +// want this test to pass, we need a smaller value since there is so much thrashing +// we would also use a less aggressive setting when running in valgrind +const puts_per_thread = 500; +const put_thread_count = 3; + +test "std.atomic.Queue" { + var direct_allocator = std.heap.DirectAllocator.init(); + defer direct_allocator.deinit(); + + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024); + defer direct_allocator.allocator.free(plenty_of_memory); + + var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); + var a = &fixed_buffer_allocator.allocator; + + var queue = Queue(i32).init(); + var context = Context{ + .allocator = a, + .queue = &queue, + .put_sum = 0, + .get_sum = 0, + .puts_done = 0, + .get_count = 0, + }; + + var putters: [put_thread_count]*std.os.Thread = undefined; + for (putters) |*t| { + t.* = try std.os.spawnThread(&context, startPuts); + } + var getters: [put_thread_count]*std.os.Thread = undefined; + for (getters) |*t| { + t.* = try std.os.spawnThread(&context, startGets); + } + + for (putters) |t| + t.wait(); + _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + for (getters) |t| + t.wait(); + + if (context.put_sum != context.get_sum) { + std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum); + } + + if (context.get_count != puts_per_thread * put_thread_count) { + std.debug.panic( + "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}", + context.get_count, + u32(puts_per_thread), + u32(put_thread_count), + ); + } +} + +fn startPuts(ctx: *Context) u8 { + var put_count: usize = puts_per_thread; + var r = std.rand.DefaultPrng.init(0xdeadbeef); + while (put_count != 0) : (put_count -= 1) { + std.os.time.sleep(0, 1); // let the os scheduler be our fuzz + const x = @bitCast(i32, r.random.scalar(u32)); + const node = ctx.allocator.create(Queue(i32).Node{ + .next = undefined, + .data = x, + }) catch unreachable; + ctx.queue.put(node); + _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); + } + return 0; +} + +fn startGets(ctx: *Context) u8 { + while (true) { + const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1; + + while (ctx.queue.get()) |node| { + std.os.time.sleep(0, 1); // let the os scheduler be our fuzz + _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); + _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); + } + + if (last) return 0; + } +} + +test "std.atomic.Queue single-threaded" { + var queue = Queue(i32).init(); + + var node_0 = Queue(i32).Node{ + .data = 0, + .next = undefined, + }; + queue.put(&node_0); + + var node_1 = Queue(i32).Node{ + .data = 1, + .next = undefined, + }; + queue.put(&node_1); + + assert(queue.get().?.data == 0); + + var node_2 = Queue(i32).Node{ + .data = 2, + .next = undefined, + }; + queue.put(&node_2); + + var node_3 = Queue(i32).Node{ + .data = 3, + .next = undefined, + }; + queue.put(&node_3); + + assert(queue.get().?.data == 1); + + assert(queue.get().?.data == 2); + + var node_4 = Queue(i32).Node{ + .data = 4, + .next = undefined, + }; + queue.put(&node_4); + + assert(queue.get().?.data == 3); + node_3.next = null; + + assert(queue.get().?.data == 4); + + assert(queue.get() == null); +} diff --git a/std/atomic/queue_mpmc.zig b/std/atomic/queue_mpmc.zig deleted file mode 100644 index 7ffc9f9ccb..0000000000 --- a/std/atomic/queue_mpmc.zig +++ /dev/null @@ -1,214 +0,0 @@ -const builtin = @import("builtin"); -const AtomicOrder = builtin.AtomicOrder; -const AtomicRmwOp = builtin.AtomicRmwOp; - -/// Many producer, many consumer, non-allocating, thread-safe, lock-free -/// This implementation has a crippling limitation - it hangs onto node -/// memory for 1 extra get() and 1 extra put() operation - when get() returns a node, that -/// node must not be freed until both the next get() and the next put() completes. -pub fn QueueMpmc(comptime T: type) type { - return struct { - head: *Node, - tail: *Node, - root: Node, - - pub const Self = this; - - pub const Node = struct { - next: ?*Node, - data: T, - }; - - /// TODO: well defined copy elision: https://github.com/ziglang/zig/issues/287 - pub fn init(self: *Self) void { - self.root.next = null; - self.head = &self.root; - self.tail = &self.root; - } - - pub fn put(self: *Self, node: *Node) void { - node.next = null; - - const tail = @atomicRmw(*Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); - _ = @atomicRmw(?*Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); - } - - /// node must not be freed until both the next get() and the next put() complete - pub fn get(self: *Self) ?*Node { - var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst); - while (true) { - const node = head.next orelse return null; - head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return node; - } - } - - ///// This is a debug function that is not thread-safe. - pub fn dump(self: *Self) void { - std.debug.warn("head: "); - dumpRecursive(self.head, 0); - std.debug.warn("tail: "); - dumpRecursive(self.tail, 0); - } - - fn dumpRecursive(optional_node: ?*Node, indent: usize) void { - var stderr_file = std.io.getStdErr() catch return; - const stderr = &std.io.FileOutStream.init(&stderr_file).stream; - stderr.writeByteNTimes(' ', indent) catch return; - if (optional_node) |node| { - std.debug.warn("0x{x}={}\n", @ptrToInt(node), node.data); - dumpRecursive(node.next, indent + 1); - } else { - std.debug.warn("(null)\n"); - } - } - }; -} - -const std = @import("std"); -const assert = std.debug.assert; - -const Context = struct { - allocator: *std.mem.Allocator, - queue: *QueueMpmc(i32), - put_sum: isize, - get_sum: isize, - get_count: usize, - puts_done: u8, // TODO make this a bool -}; - -// TODO add lazy evaluated build options and then put puts_per_thread behind -// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor -// CI we would use a less aggressive setting since at 1 core, while we still -// want this test to pass, we need a smaller value since there is so much thrashing -// we would also use a less aggressive setting when running in valgrind -const puts_per_thread = 500; -const put_thread_count = 3; - -test "std.atomic.queue_mpmc" { - var direct_allocator = std.heap.DirectAllocator.init(); - defer direct_allocator.deinit(); - - var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024); - defer direct_allocator.allocator.free(plenty_of_memory); - - var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); - var a = &fixed_buffer_allocator.allocator; - - var queue: QueueMpmc(i32) = undefined; - queue.init(); - var context = Context{ - .allocator = a, - .queue = &queue, - .put_sum = 0, - .get_sum = 0, - .puts_done = 0, - .get_count = 0, - }; - - var putters: [put_thread_count]*std.os.Thread = undefined; - for (putters) |*t| { - t.* = try std.os.spawnThread(&context, startPuts); - } - var getters: [put_thread_count]*std.os.Thread = undefined; - for (getters) |*t| { - t.* = try std.os.spawnThread(&context, startGets); - } - - for (putters) |t| - t.wait(); - _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - for (getters) |t| - t.wait(); - - if (context.put_sum != context.get_sum) { - std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum); - } - - if (context.get_count != puts_per_thread * put_thread_count) { - std.debug.panic( - "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}", - context.get_count, - u32(puts_per_thread), - u32(put_thread_count), - ); - } -} - -fn startPuts(ctx: *Context) u8 { - var put_count: usize = puts_per_thread; - var r = std.rand.DefaultPrng.init(0xdeadbeef); - while (put_count != 0) : (put_count -= 1) { - std.os.time.sleep(0, 1); // let the os scheduler be our fuzz - const x = @bitCast(i32, r.random.scalar(u32)); - const node = ctx.allocator.create(QueueMpmc(i32).Node{ - .next = undefined, - .data = x, - }) catch unreachable; - ctx.queue.put(node); - _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); - } - return 0; -} - -fn startGets(ctx: *Context) u8 { - while (true) { - const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1; - - while (ctx.queue.get()) |node| { - std.os.time.sleep(0, 1); // let the os scheduler be our fuzz - _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); - _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); - } - - if (last) return 0; - } -} - -test "std.atomic.queue_mpmc single-threaded" { - var queue: QueueMpmc(i32) = undefined; - queue.init(); - - var node_0 = QueueMpmc(i32).Node{ - .data = 0, - .next = undefined, - }; - queue.put(&node_0); - - var node_1 = QueueMpmc(i32).Node{ - .data = 1, - .next = undefined, - }; - queue.put(&node_1); - - assert(queue.get().?.data == 0); - - var node_2 = QueueMpmc(i32).Node{ - .data = 2, - .next = undefined, - }; - queue.put(&node_2); - - var node_3 = QueueMpmc(i32).Node{ - .data = 3, - .next = undefined, - }; - queue.put(&node_3); - - assert(queue.get().?.data == 1); - - assert(queue.get().?.data == 2); - - var node_4 = QueueMpmc(i32).Node{ - .data = 4, - .next = undefined, - }; - queue.put(&node_4); - - assert(queue.get().?.data == 3); - // if we were to set node_3.next to null here, it would cause this test - // to fail. this demonstrates the limitation of hanging on to extra memory. - - assert(queue.get().?.data == 4); - - assert(queue.get() == null); -} diff --git a/std/atomic/queue_mpsc.zig b/std/atomic/queue_mpsc.zig deleted file mode 100644 index 978e189453..0000000000 --- a/std/atomic/queue_mpsc.zig +++ /dev/null @@ -1,185 +0,0 @@ -const std = @import("../index.zig"); -const assert = std.debug.assert; -const builtin = @import("builtin"); -const AtomicOrder = builtin.AtomicOrder; -const AtomicRmwOp = builtin.AtomicRmwOp; - -/// Many producer, single consumer, non-allocating, thread-safe, lock-free -pub fn QueueMpsc(comptime T: type) type { - return struct { - inboxes: [2]std.atomic.Stack(T), - outbox: std.atomic.Stack(T), - inbox_index: usize, - - pub const Self = this; - - pub const Node = std.atomic.Stack(T).Node; - - /// Not thread-safe. The call to init() must complete before any other functions are called. - /// No deinitialization required. - pub fn init() Self { - return Self{ - .inboxes = []std.atomic.Stack(T){ - std.atomic.Stack(T).init(), - std.atomic.Stack(T).init(), - }, - .outbox = std.atomic.Stack(T).init(), - .inbox_index = 0, - }; - } - - /// Fully thread-safe. put() may be called from any thread at any time. - pub fn put(self: *Self, node: *Node) void { - const inbox_index = @atomicLoad(usize, &self.inbox_index, AtomicOrder.SeqCst); - const inbox = &self.inboxes[inbox_index]; - inbox.push(node); - } - - /// Must be called by only 1 consumer at a time. Every call to get() and isEmpty() must complete before - /// the next call to get(). - pub fn get(self: *Self) ?*Node { - if (self.outbox.pop()) |node| { - return node; - } - const prev_inbox_index = @atomicRmw(usize, &self.inbox_index, AtomicRmwOp.Xor, 0x1, AtomicOrder.SeqCst); - const prev_inbox = &self.inboxes[prev_inbox_index]; - while (prev_inbox.pop()) |node| { - self.outbox.push(node); - } - return self.outbox.pop(); - } - - /// Must be called by only 1 consumer at a time. Every call to get() and isEmpty() must complete before - /// the next call to isEmpty(). - pub fn isEmpty(self: *Self) bool { - if (!self.outbox.isEmpty()) return false; - const prev_inbox_index = @atomicRmw(usize, &self.inbox_index, AtomicRmwOp.Xor, 0x1, AtomicOrder.SeqCst); - const prev_inbox = &self.inboxes[prev_inbox_index]; - while (prev_inbox.pop()) |node| { - self.outbox.push(node); - } - return self.outbox.isEmpty(); - } - - /// For debugging only. No API guarantees about what this does. - pub fn dump(self: *Self) void { - { - var it = self.outbox.root; - while (it) |node| { - std.debug.warn("0x{x} -> ", @ptrToInt(node)); - it = node.next; - } - } - const inbox_index = self.inbox_index; - const inboxes = []*std.atomic.Stack(T){ - &self.inboxes[self.inbox_index], - &self.inboxes[1 - self.inbox_index], - }; - for (inboxes) |inbox| { - var it = inbox.root; - while (it) |node| { - std.debug.warn("0x{x} -> ", @ptrToInt(node)); - it = node.next; - } - } - - std.debug.warn("null\n"); - } - }; -} - -const Context = struct { - allocator: *std.mem.Allocator, - queue: *QueueMpsc(i32), - put_sum: isize, - get_sum: isize, - get_count: usize, - puts_done: u8, // TODO make this a bool -}; - -// TODO add lazy evaluated build options and then put puts_per_thread behind -// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor -// CI we would use a less aggressive setting since at 1 core, while we still -// want this test to pass, we need a smaller value since there is so much thrashing -// we would also use a less aggressive setting when running in valgrind -const puts_per_thread = 500; -const put_thread_count = 3; - -test "std.atomic.queue_mpsc" { - var direct_allocator = std.heap.DirectAllocator.init(); - defer direct_allocator.deinit(); - - var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024); - defer direct_allocator.allocator.free(plenty_of_memory); - - var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); - var a = &fixed_buffer_allocator.allocator; - - var queue = QueueMpsc(i32).init(); - var context = Context{ - .allocator = a, - .queue = &queue, - .put_sum = 0, - .get_sum = 0, - .puts_done = 0, - .get_count = 0, - }; - - var putters: [put_thread_count]*std.os.Thread = undefined; - for (putters) |*t| { - t.* = try std.os.spawnThread(&context, startPuts); - } - var getters: [1]*std.os.Thread = undefined; - for (getters) |*t| { - t.* = try std.os.spawnThread(&context, startGets); - } - - for (putters) |t| - t.wait(); - _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - for (getters) |t| - t.wait(); - - if (context.put_sum != context.get_sum) { - std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum); - } - - if (context.get_count != puts_per_thread * put_thread_count) { - std.debug.panic( - "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}", - context.get_count, - u32(puts_per_thread), - u32(put_thread_count), - ); - } -} - -fn startPuts(ctx: *Context) u8 { - var put_count: usize = puts_per_thread; - var r = std.rand.DefaultPrng.init(0xdeadbeef); - while (put_count != 0) : (put_count -= 1) { - std.os.time.sleep(0, 1); // let the os scheduler be our fuzz - const x = @bitCast(i32, r.random.scalar(u32)); - const node = ctx.allocator.create(QueueMpsc(i32).Node{ - .next = undefined, - .data = x, - }) catch unreachable; - ctx.queue.put(node); - _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); - } - return 0; -} - -fn startGets(ctx: *Context) u8 { - while (true) { - const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1; - - while (ctx.queue.get()) |node| { - std.os.time.sleep(0, 1); // let the os scheduler be our fuzz - _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); - _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); - } - - if (last) return 0; - } -} diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index d74bee8e8b..16d5c9503b 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -1,10 +1,13 @@ +const assert = std.debug.assert; const builtin = @import("builtin"); const AtomicOrder = builtin.AtomicOrder; -/// Many reader, many writer, non-allocating, thread-safe, lock-free +/// Many reader, many writer, non-allocating, thread-safe +/// Uses a spinlock to protect push() and pop() pub fn Stack(comptime T: type) type { return struct { root: ?*Node, + lock: u8, pub const Self = this; @@ -14,7 +17,10 @@ pub fn Stack(comptime T: type) type { }; pub fn init() Self { - return Self{ .root = null }; + return Self{ + .root = null, + .lock = 0, + }; } /// push operation, but only if you are the first item in the stack. if you did not succeed in @@ -25,18 +31,20 @@ pub fn Stack(comptime T: type) type { } pub fn push(self: *Self, node: *Node) void { - var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst); - while (true) { - node.next = root; - root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse break; - } + while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} + defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + + node.next = self.root; + self.root = node; } pub fn pop(self: *Self) ?*Node { - var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst); - while (true) { - root = @cmpxchgWeak(?*Node, &self.root, root, (root orelse return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return root; - } + while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} + defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + + const root = self.root orelse return null; + self.root = root.next; + return root; } pub fn isEmpty(self: *Self) bool { @@ -45,7 +53,7 @@ pub fn Stack(comptime T: type) type { }; } -const std = @import("std"); +const std = @import("../index.zig"); const Context = struct { allocator: *std.mem.Allocator, stack: *Stack(i32), diff --git a/std/event/channel.zig b/std/event/channel.zig index 4b3a7177a2..d4d713bdee 100644 --- a/std/event/channel.zig +++ b/std/event/channel.zig @@ -12,8 +12,8 @@ pub fn Channel(comptime T: type) type { return struct { loop: *Loop, - getters: std.atomic.QueueMpsc(GetNode), - putters: std.atomic.QueueMpsc(PutNode), + getters: std.atomic.Queue(GetNode), + putters: std.atomic.Queue(PutNode), get_count: usize, put_count: usize, dispatch_lock: u8, // TODO make this a bool @@ -46,8 +46,8 @@ pub fn Channel(comptime T: type) type { .buffer_index = 0, .dispatch_lock = 0, .need_dispatch = 0, - .getters = std.atomic.QueueMpsc(GetNode).init(), - .putters = std.atomic.QueueMpsc(PutNode).init(), + .getters = std.atomic.Queue(GetNode).init(), + .putters = std.atomic.Queue(PutNode).init(), .get_count = 0, .put_count = 0, }); @@ -81,7 +81,7 @@ pub fn Channel(comptime T: type) type { .next = undefined, .data = handle, }; - var queue_node = std.atomic.QueueMpsc(PutNode).Node{ + var queue_node = std.atomic.Queue(PutNode).Node{ .data = PutNode{ .tick_node = &my_tick_node, .data = data, @@ -111,7 +111,7 @@ pub fn Channel(comptime T: type) type { .next = undefined, .data = handle, }; - var queue_node = std.atomic.QueueMpsc(GetNode).Node{ + var queue_node = std.atomic.Queue(GetNode).Node{ .data = GetNode{ .ptr = &result, .tick_node = &my_tick_node, diff --git a/std/event/future.zig b/std/event/future.zig index 8001f675a2..6c03641828 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -17,7 +17,7 @@ pub fn Future(comptime T: type) type { available: u8, // TODO make this a bool const Self = this; - const Queue = std.atomic.QueueMpsc(promise); + const Queue = std.atomic.Queue(promise); pub fn init(loop: *Loop) Self { return Self{ @@ -30,19 +30,19 @@ pub fn Future(comptime T: type) type { /// Obtain the value. If it's not available, wait until it becomes /// available. /// Thread-safe. - pub async fn get(self: *Self) T { + pub async fn get(self: *Self) *T { if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { - return self.data; + return &self.data; } const held = await (async self.lock.acquire() catch unreachable); - defer held.release(); + held.release(); - return self.data; + return &self.data; } /// Make the data become available. May be called only once. - pub fn put(self: *Self, value: T) void { - self.data = value; + /// Before calling this, modify the `data` property. + pub fn resolve(self: *Self) void { const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); assert(prev == 0); // put() called twice Lock.Held.release(Lock.Held{ .lock = &self.lock }); @@ -57,7 +57,7 @@ test "std.event.Future" { const allocator = &da.allocator; var loop: Loop = undefined; - try loop.initMultiThreaded(allocator); + try loop.initSingleThreaded(allocator); defer loop.deinit(); const handle = try async testFuture(&loop); @@ -79,9 +79,10 @@ async fn testFuture(loop: *Loop) void { } async fn waitOnFuture(future: *Future(i32)) i32 { - return await (async future.get() catch @panic("memory")); + return (await (async future.get() catch @panic("memory"))).*; } async fn resolveFuture(future: *Future(i32)) void { - future.put(6); + future.data = 6; + future.resolve(); } diff --git a/std/event/lock.zig b/std/event/lock.zig index cba3594b50..2013b5595f 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -15,7 +15,7 @@ pub const Lock = struct { queue: Queue, queue_empty_bit: u8, // TODO make this a bool - const Queue = std.atomic.QueueMpsc(promise); + const Queue = std.atomic.Queue(promise); pub const Held = struct { lock: *Lock, diff --git a/std/event/loop.zig b/std/event/loop.zig index 646f15875f..07575cf2e8 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -9,7 +9,7 @@ const AtomicOrder = builtin.AtomicOrder; pub const Loop = struct { allocator: *mem.Allocator, - next_tick_queue: std.atomic.QueueMpsc(promise), + next_tick_queue: std.atomic.Queue(promise), os_data: OsData, final_resume_node: ResumeNode, dispatch_lock: u8, // TODO make this a bool @@ -21,7 +21,7 @@ pub const Loop = struct { available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd), eventfd_resume_nodes: []std.atomic.Stack(ResumeNode.EventFd).Node, - pub const NextTickNode = std.atomic.QueueMpsc(promise).Node; + pub const NextTickNode = std.atomic.Queue(promise).Node; pub const ResumeNode = struct { id: Id, @@ -77,7 +77,7 @@ pub const Loop = struct { .pending_event_count = 0, .allocator = allocator, .os_data = undefined, - .next_tick_queue = std.atomic.QueueMpsc(promise).init(), + .next_tick_queue = std.atomic.Queue(promise).init(), .dispatch_lock = 1, // start locked so threads go directly into epoll wait .extra_threads = undefined, .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(), diff --git a/test/tests.zig b/test/tests.zig index b1453776a8..3a72f58753 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -47,12 +47,13 @@ const test_targets = []TestTarget{ const max_stdout_size = 1 * 1024 * 1024; // 1 MB -pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { +pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { const cases = b.allocator.create(CompareOutputContext{ .b = b, .step = b.step("test-compare-output", "Run the compare output tests"), .test_index = 0, .test_filter = test_filter, + .modes = modes, }) catch unreachable; compare_output.addCases(cases); @@ -60,12 +61,13 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8) *build return cases.step; } -pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { +pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { const cases = b.allocator.create(CompareOutputContext{ .b = b, .step = b.step("test-runtime-safety", "Run the runtime safety tests"), .test_index = 0, .test_filter = test_filter, + .modes = modes, }) catch unreachable; runtime_safety.addCases(cases); @@ -73,12 +75,13 @@ pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8) *build return cases.step; } -pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { +pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { const cases = b.allocator.create(CompileErrorContext{ .b = b, .step = b.step("test-compile-errors", "Run the compile error tests"), .test_index = 0, .test_filter = test_filter, + .modes = modes, }) catch unreachable; compile_errors.addCases(cases); @@ -99,12 +102,13 @@ pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build. return cases.step; } -pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { +pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { const cases = b.allocator.create(CompareOutputContext{ .b = b, .step = b.step("test-asm-link", "Run the assemble and link tests"), .test_index = 0, .test_filter = test_filter, + .modes = modes, }) catch unreachable; assemble_and_link.addCases(cases); @@ -173,6 +177,7 @@ pub const CompareOutputContext = struct { step: *build.Step, test_index: usize, test_filter: ?[]const u8, + modes: []const Mode, const Special = enum { None, @@ -423,12 +428,7 @@ pub const CompareOutputContext = struct { self.step.dependOn(&run_and_cmp_output.step); }, Special.None => { - for ([]Mode{ - Mode.Debug, - Mode.ReleaseSafe, - Mode.ReleaseFast, - Mode.ReleaseSmall, - }) |mode| { + for (self.modes) |mode| { const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", "compare-output", case.name, @tagName(mode)) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; @@ -483,6 +483,7 @@ pub const CompileErrorContext = struct { step: *build.Step, test_index: usize, test_filter: ?[]const u8, + modes: []const Mode, const TestCase = struct { name: []const u8, @@ -673,10 +674,7 @@ pub const CompileErrorContext = struct { pub fn addCase(self: *CompileErrorContext, case: *const TestCase) void { const b = self.b; - for ([]Mode{ - Mode.Debug, - Mode.ReleaseFast, - }) |mode| { + for (self.modes) |mode| { const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {} ({})", case.name, @tagName(mode)) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; -- cgit v1.2.3 From 30c4add85a0f4af727ad7cf8f2134114329d0f07 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 11 Jul 2018 20:17:47 -0400 Subject: std.event.Future: workaround in tests for llvm coro memory See #1194 --- std/event.zig | 2 +- std/event/future.zig | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/std/event.zig b/std/event.zig index f3913a432b..1e52086286 100644 --- a/std/event.zig +++ b/std/event.zig @@ -4,7 +4,7 @@ pub const Lock = @import("event/lock.zig").Lock; pub const tcp = @import("event/tcp.zig"); pub const Channel = @import("event/channel.zig").Channel; pub const Group = @import("event/group.zig").Group; -pub const Future = @import("event/future.zig").Group; +pub const Future = @import("event/future.zig").Future; test "import event tests" { _ = @import("event/locked.zig"); diff --git a/std/event/future.zig b/std/event/future.zig index 6c03641828..b6ec861f77 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -67,6 +67,9 @@ test "std.event.Future" { } async fn testFuture(loop: *Loop) void { + suspend |p| { + resume p; + } var future = Future(i32).init(loop); const a = async waitOnFuture(&future) catch @panic("memory"); @@ -79,10 +82,16 @@ async fn testFuture(loop: *Loop) void { } async fn waitOnFuture(future: *Future(i32)) i32 { + suspend |p| { + resume p; + } return (await (async future.get() catch @panic("memory"))).*; } async fn resolveFuture(future: *Future(i32)) void { + suspend |p| { + resume p; + } future.data = 6; future.resolve(); } -- cgit v1.2.3 From ce11d6d16cf388ec7abff9680ee3a263185a9986 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 11 Jul 2018 21:37:47 -0400 Subject: ir: refactor lvalues --- src/all_types.hpp | 11 ++++---- src/ir.cpp | 77 ++++++++++++++++++++++++++----------------------------- src/ir_print.cpp | 6 ++--- 3 files changed, 44 insertions(+), 50 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 6dcf1894d8..2da0677e1b 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2005,12 +2005,6 @@ struct IrBasicBlock { IrInstruction *must_be_comptime_source_instr; }; -struct LVal { - bool is_ptr; - bool is_const; - bool is_volatile; -}; - enum IrInstructionId { IrInstructionIdInvalid, IrInstructionIdBr, @@ -2972,6 +2966,11 @@ struct IrInstructionTypeName { IrInstruction *type_value; }; +enum LVal { + LValNone, + LValPtr, +}; + struct IrInstructionDeclRef { IrInstruction base; diff --git a/src/ir.cpp b/src/ir.cpp index 7f7436010e..eb62cc8bdf 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -39,9 +39,6 @@ struct IrAnalyze { IrBasicBlock *const_predecessor_bb; }; -static const LVal LVAL_NONE = { false, false, false }; -static const LVal LVAL_PTR = { true, false, false }; - enum ConstCastResultId { ConstCastResultIdOk, ConstCastResultIdErrSet, @@ -3164,7 +3161,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, case ReturnKindError: { assert(expr_node); - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR); + IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr); @@ -3192,7 +3189,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false); - if (lval.is_ptr) + if (lval == LValPtr) return unwrapped_ptr; else return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); @@ -3357,7 +3354,7 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *no } static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) { - IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LVAL_PTR); + IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr); IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction) @@ -3368,7 +3365,7 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) } static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) { - IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LVAL_PTR); + IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr); if (lvalue == irb->codegen->invalid_instruction) return lvalue; IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); @@ -3470,7 +3467,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; - IrInstruction *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LVAL_PTR); + IrInstruction *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr); if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -3657,7 +3654,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, Buf *variable_name = node->data.symbol_expr.symbol; - if (buf_eql_str(variable_name, "_") && lval.is_ptr) { + if (buf_eql_str(variable_name, "_") && lval == LValPtr) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, node); const_instruction->base.value.type = get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_void, false); @@ -3669,8 +3666,8 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(variable_name); if (primitive_table_entry) { IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_table_entry->value); - if (lval.is_ptr) { - return ir_build_ref(irb, scope, node, value, lval.is_const, lval.is_volatile); + if (lval == LValPtr) { + return ir_build_ref(irb, scope, node, value, false, false); } else { return value; } @@ -3679,7 +3676,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name); if (var) { IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var); - if (lval.is_ptr) + if (lval == LValPtr) return var_ptr; else return ir_build_load_ptr(irb, scope, node, var_ptr); @@ -3705,7 +3702,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode assert(node->type == NodeTypeArrayAccessExpr); AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr; - IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LVAL_PTR); + IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr); if (array_ref_instruction == irb->codegen->invalid_instruction) return array_ref_instruction; @@ -3716,7 +3713,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction, subscript_instruction, true, PtrLenSingle); - if (lval.is_ptr) + if (lval == LValPtr) return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); @@ -3728,7 +3725,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode AstNode *container_ref_node = node->data.field_access_expr.struct_expr; Buf *field_name = node->data.field_access_expr.field_name; - IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LVAL_PTR); + IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr); if (container_ref_instruction == irb->codegen->invalid_instruction) return container_ref_instruction; @@ -4386,7 +4383,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdField: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LVAL_PTR); + IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4397,7 +4394,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value); - if (lval.is_ptr) + if (lval == LValPtr) return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); @@ -4928,18 +4925,18 @@ static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, Ast } static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) { - return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LVAL_NONE); + return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone); } static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval) { - if (!lval.is_ptr) + if (lval != LValPtr) return value; if (value == irb->codegen->invalid_instruction) return value; // We needed a pointer to a value, but we got a value. So we create // an instruction which just makes a const pointer of it. - return ir_build_ref(irb, scope, value->source_node, value, lval.is_const, lval.is_volatile); + return ir_build_ref(irb, scope, value->source_node, value, false, false); } static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5001,7 +4998,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval) { - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR); + IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5009,7 +5006,7 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode if (payload_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - if (lval.is_ptr) + if (lval == LValPtr) return payload_ptr; return ir_build_load_ptr(irb, scope, source_node, payload_ptr); @@ -5046,7 +5043,7 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR), lval); + return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr), lval); } } zig_unreachable(); @@ -5186,7 +5183,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } else { payload_scope = scope; } - IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LVAL_PTR); + IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LValPtr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr); @@ -5269,7 +5266,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n VariableTableEntry *payload_var = ir_create_var(irb, symbol_node, scope, var_symbol, true, false, false, is_comptime); Scope *child_scope = payload_var->child_scope; - IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LVAL_PTR); + IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LValPtr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); @@ -5413,7 +5410,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo } assert(elem_node->type == NodeTypeSymbol); - IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LVAL_PTR); + IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LValPtr); if (array_val_ptr == irb->codegen->invalid_instruction) return array_val_ptr; @@ -5700,7 +5697,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no AstNode *else_node = node->data.test_expr.else_node; bool var_is_ptr = node->data.test_expr.var_is_ptr; - IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR); + IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; @@ -5778,7 +5775,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * Buf *var_symbol = node->data.if_err_expr.var_symbol; Buf *err_symbol = node->data.if_err_expr.err_symbol; - IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LVAL_PTR); + IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; @@ -5904,7 +5901,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * assert(node->type == NodeTypeSwitchExpr); AstNode *target_node = node->data.switch_expr.expr; - IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LVAL_PTR); + IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr); if (target_value_ptr == irb->codegen->invalid_instruction) return target_value_ptr; IrInstruction *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr); @@ -6277,7 +6274,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) AstNode *start_node = slice_expr->start; AstNode *end_node = slice_expr->end; - IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LVAL_PTR); + IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr); if (ptr_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6311,11 +6308,11 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); return irb->codegen->invalid_instruction; } - return ir_gen_err_assert_ok(irb, parent_scope, node, op1_node, LVAL_NONE); + return ir_gen_err_assert_ok(irb, parent_scope, node, op1_node, LValNone); } - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LVAL_PTR); + IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6868,7 +6865,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node); if (ptr_instruction == irb->codegen->invalid_instruction) return ptr_instruction; - if (lval.is_ptr) + if (lval == LValPtr) return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); @@ -6884,12 +6881,12 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeUnwrapOptional: { AstNode *expr_node = node->data.unwrap_optional.expr; - IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR); + IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true); - if (lval.is_ptr) + if (lval == LValPtr) return unwrapped_ptr; return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); @@ -6959,7 +6956,7 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *sc } static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) { - return ir_gen_node_extra(irb, node, scope, LVAL_NONE); + return ir_gen_node_extra(irb, node, scope, LValNone); } static void invalidate_exec(IrExecutable *exec) { @@ -7089,7 +7086,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec irb->exec->coro_final_cleanup_block = ir_create_basic_block(irb, scope, "FinalCleanup"); } - IrInstruction *result = ir_gen_node_extra(irb, node, scope, LVAL_NONE); + IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone); assert(result); if (irb->exec->invalid) return false; @@ -19752,7 +19749,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, Tld *tld = instruction->tld; LVal lval = instruction->lval; - resolve_top_level_decl(ira->codegen, tld, lval.is_ptr, instruction->base.source_node); + resolve_top_level_decl(ira->codegen, tld, lval == LValPtr, instruction->base.source_node); if (tld->resolution == TldResolutionInvalid) return ira->codegen->builtin_types.entry_invalid; @@ -19773,7 +19770,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node); } - if (lval.is_ptr) { + if (lval == LValPtr) { ir_link_new_instruction(var_ptr, &instruction->base); return var_ptr->value.type; } else { @@ -19794,7 +19791,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope, instruction->base.source_node, fn_entry); - if (lval.is_ptr) { + if (lval == LValPtr) { IrInstruction *ptr_instr = ir_get_ref(ira, &instruction->base, ref_instruction, true, false); ir_link_new_instruction(ptr_instr, &instruction->base); return ptr_instr->value.type; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 780cf9e756..6182958d0a 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1005,10 +1005,8 @@ static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) { } static void ir_print_decl_ref(IrPrint *irp, IrInstructionDeclRef *instruction) { - const char *ptr_str = instruction->lval.is_ptr ? "ptr " : ""; - const char *const_str = instruction->lval.is_const ? "const " : ""; - const char *volatile_str = instruction->lval.is_volatile ? "volatile " : ""; - fprintf(irp->f, "declref %s%s%s%s", const_str, volatile_str, ptr_str, buf_ptr(instruction->tld->name)); + const char *ptr_str = (instruction->lval == LValPtr) ? "ptr " : ""; + fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name)); } static void ir_print_panic(IrPrint *irp, IrInstructionPanic *instruction) { -- cgit v1.2.3 From 687bd92f9c3d9f521c8fe5884627ef1b00320364 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 12 Jul 2018 15:08:40 -0400 Subject: self-hosted: generate zig IR for simple function no tests for this yet. I think the quickest path to testing will be creating the .o files and linking with libc, executing, and then comparing output. --- src-self-hosted/decl.zig | 96 ++++++ src-self-hosted/ir.zig | 745 ++++++++++++++++++++++++++++++++++------ src-self-hosted/module.zig | 383 ++++++++++----------- src-self-hosted/parsed_file.zig | 6 + src-self-hosted/scope.zig | 230 ++++++++++++- src-self-hosted/type.zig | 268 +++++++++++++++ src-self-hosted/value.zig | 125 +++++++ src-self-hosted/visib.zig | 4 + std/event/future.zig | 2 +- std/zig/ast.zig | 6 - std/zig/parse.zig | 5 - 11 files changed, 1555 insertions(+), 315 deletions(-) create mode 100644 src-self-hosted/decl.zig create mode 100644 src-self-hosted/parsed_file.zig create mode 100644 src-self-hosted/type.zig create mode 100644 src-self-hosted/value.zig create mode 100644 src-self-hosted/visib.zig diff --git a/src-self-hosted/decl.zig b/src-self-hosted/decl.zig new file mode 100644 index 0000000000..1a75a3249e --- /dev/null +++ b/src-self-hosted/decl.zig @@ -0,0 +1,96 @@ +const std = @import("std"); +const Allocator = mem.Allocator; +const mem = std.mem; +const ast = std.zig.ast; +const Visib = @import("visib.zig").Visib; +const ParsedFile = @import("parsed_file.zig").ParsedFile; +const event = std.event; +const Value = @import("value.zig").Value; +const Token = std.zig.Token; +const errmsg = @import("errmsg.zig"); +const Scope = @import("scope.zig").Scope; +const Module = @import("module.zig").Module; + +pub const Decl = struct { + id: Id, + name: []const u8, + visib: Visib, + resolution: event.Future(Module.BuildError!void), + resolution_in_progress: u8, + parsed_file: *ParsedFile, + parent_scope: *Scope, + + pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); + + pub fn isExported(base: *const Decl, tree: *ast.Tree) bool { + switch (base.id) { + Id.Fn => { + const fn_decl = @fieldParentPtr(Fn, "base", base); + return fn_decl.isExported(tree); + }, + else => return false, + } + } + + pub fn getSpan(base: *const Decl) errmsg.Span { + switch (base.id) { + Id.Fn => { + const fn_decl = @fieldParentPtr(Fn, "base", base); + const fn_proto = fn_decl.fn_proto; + const start = fn_proto.fn_token; + const end = fn_proto.name_token orelse start; + return errmsg.Span{ + .first = start, + .last = end + 1, + }; + }, + else => @panic("TODO"), + } + } + + pub const Id = enum { + Var, + Fn, + CompTime, + }; + + pub const Var = struct { + base: Decl, + }; + + pub const Fn = struct { + base: Decl, + value: Val, + fn_proto: *const ast.Node.FnProto, + + // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous + pub const Val = union { + Unresolved: void, + Ok: *Value.Fn, + }; + + pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { + return if (self.fn_proto.extern_export_inline_token) |tok_index| x: { + const token = tree.tokens.at(tok_index); + break :x switch (token.id) { + Token.Id.Extern => tree.tokenSlicePtr(token), + else => null, + }; + } else null; + } + + pub fn isExported(self: Fn, tree: *ast.Tree) bool { + if (self.fn_proto.extern_export_inline_token) |tok_index| { + const token = tree.tokens.at(tok_index); + return token.id == Token.Id.Keyword_export; + } else { + return false; + } + } + }; + + pub const CompTime = struct { + base: Decl, + }; +}; + diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 3334d9511b..f517dfe579 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -1,111 +1,656 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const Module = @import("module.zig").Module; const Scope = @import("scope.zig").Scope; +const ast = std.zig.ast; +const Allocator = std.mem.Allocator; +const Value = @import("value.zig").Value; +const Type = Value.Type; +const assert = std.debug.assert; +const Token = std.zig.Token; +const ParsedFile = @import("parsed_file.zig").ParsedFile; + +pub const LVal = enum { + None, + Ptr, +}; + +pub const Mut = enum { + Mut, + Const, +}; + +pub const Volatility = enum { + NonVolatile, + Volatile, +}; + +pub const IrVal = union(enum) { + Unknown, + Known: *Value, + + pub fn dump(self: IrVal) void { + switch (self) { + IrVal.Unknown => std.debug.warn("Unknown"), + IrVal.Known => |value| { + std.debug.warn("Known("); + value.dump(); + std.debug.warn(")"); + }, + } + } +}; pub const Instruction = struct { id: Id, scope: *Scope, + debug_id: usize, + val: IrVal, + + /// true if this instruction was generated by zig and not from user code + is_generated: bool, + + pub fn cast(base: *Instruction, comptime T: type) ?*T { + if (base.id == comptime typeToId(T)) { + return @fieldParentPtr(T, "base", base); + } + return null; + } + + pub fn typeToId(comptime T: type) Id { + comptime var i = 0; + inline while (i < @memberCount(Id)) : (i += 1) { + if (T == @field(Instruction, @memberName(Id, i))) { + return @field(Id, @memberName(Id, i)); + } + } + unreachable; + } + + pub fn dump(base: *const Instruction) void { + comptime var i = 0; + inline while (i < @memberCount(Id)) : (i += 1) { + if (base.id == @field(Id, @memberName(Id, i))) { + const T = @field(Instruction, @memberName(Id, i)); + std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id)); + @fieldParentPtr(T, "base", base).dump(); + std.debug.warn(")"); + return; + } + } + unreachable; + } + + pub fn setGenerated(base: *Instruction) void { + base.is_generated = true; + } + + pub fn isNoReturn(base: *const Instruction) bool { + switch (base.val) { + IrVal.Unknown => return false, + IrVal.Known => |x| return x.typeof.id == Type.Id.NoReturn, + } + } pub const Id = enum { - Br, - CondBr, - SwitchBr, - SwitchVar, - SwitchTarget, - Phi, - UnOp, - BinOp, - DeclVar, - LoadPtr, - StorePtr, - FieldPtr, - StructFieldPtr, - UnionFieldPtr, - ElemPtr, - VarPtr, - Call, - Const, Return, - Cast, - ContainerInitList, - ContainerInitFields, - StructInit, - UnionInit, - Unreachable, - TypeOf, - ToPtrType, - PtrTypeChild, - SetRuntimeSafety, - SetFloatMode, - ArrayType, - SliceType, - Asm, - SizeOf, - TestNonNull, - UnwrapMaybe, - MaybeWrap, - UnionTag, - Clz, - Ctz, - Import, - CImport, - CInclude, - CDefine, - CUndef, - ArrayLen, + Const, Ref, - MinValue, - MaxValue, - CompileErr, - CompileLog, - ErrName, - EmbedFile, - Cmpxchg, - Fence, - Truncate, - IntType, - BoolNot, - Memset, - Memcpy, - Slice, - MemberCount, - MemberType, - MemberName, - Breakpoint, - ReturnAddress, - FrameAddress, - AlignOf, - OverflowOp, - TestErr, - UnwrapErrCode, - UnwrapErrPayload, - ErrWrapCode, - ErrWrapPayload, - FnProto, - TestComptime, - PtrCast, - BitCast, - WidenOrShorten, - IntToPtr, - PtrToInt, - IntToEnum, - IntToErr, - ErrToInt, - CheckSwitchProngs, - CheckStatementIsVoid, - TypeName, - CanImplicitCast, - DeclRef, - Panic, - TagName, - TagType, - FieldParentPtr, - OffsetOf, - TypeId, - SetEvalBranchQuota, - PtrTypeOf, - AlignCast, - OpaqueType, - SetAlignStack, - ArgType, - Export, + DeclVar, + CheckVoidStmt, + Phi, + Br, + }; + + pub const Const = struct { + base: Instruction, + + pub fn buildBool(irb: *Builder, scope: *Scope, val: bool) !*Instruction { + const inst = try irb.arena().create(Const{ + .base = Instruction{ + .id = Instruction.Id.Const, + .is_generated = false, + .scope = scope, + .debug_id = irb.next_debug_id, + .val = IrVal{ .Known = &Value.Bool.get(irb.module, val).base }, + }, + }); + irb.next_debug_id += 1; + try irb.current_basic_block.instruction_list.append(&inst.base); + return &inst.base; + } + + pub fn buildVoid(irb: *Builder, scope: *Scope, is_generated: bool) !*Instruction { + const inst = try irb.arena().create(Const{ + .base = Instruction{ + .id = Instruction.Id.Const, + .is_generated = is_generated, + .scope = scope, + .debug_id = irb.next_debug_id, + .val = IrVal{ .Known = &Value.Void.get(irb.module).base }, + }, + }); + irb.next_debug_id += 1; + try irb.current_basic_block.instruction_list.append(&inst.base); + return &inst.base; + } + + pub fn dump(inst: *const Const) void { + inst.base.val.Known.dump(); + } + }; + + pub const Return = struct { + base: Instruction, + return_value: *Instruction, + + pub fn build(irb: *Builder, scope: *Scope, return_value: *Instruction) !*Instruction { + const inst = try irb.arena().create(Return{ + .base = Instruction{ + .id = Instruction.Id.Return, + .is_generated = false, + .scope = scope, + .debug_id = irb.next_debug_id, + .val = IrVal{ .Known = &Value.Void.get(irb.module).base }, + }, + .return_value = return_value, + }); + irb.next_debug_id += 1; + try irb.current_basic_block.instruction_list.append(&inst.base); + return &inst.base; + } + + pub fn dump(inst: *const Return) void { + std.debug.warn("#{}", inst.return_value.debug_id); + } + }; + + pub const Ref = struct { + base: Instruction, + target: *Instruction, + mut: Mut, + volatility: Volatility, + + pub fn build( + irb: *Builder, + scope: *Scope, + target: *Instruction, + mut: Mut, + volatility: Volatility, + ) !*Instruction { + const inst = try irb.arena().create(Ref{ + .base = Instruction{ + .id = Instruction.Id.Ref, + .is_generated = false, + .scope = scope, + .debug_id = irb.next_debug_id, + .val = IrVal.Unknown, + }, + .target = target, + .mut = mut, + .volatility = volatility, + }); + irb.next_debug_id += 1; + try irb.current_basic_block.instruction_list.append(&inst.base); + return &inst.base; + } + + pub fn dump(inst: *const Ref) void {} + }; + + pub const DeclVar = struct { + base: Instruction, + variable: *Variable, + + pub fn dump(inst: *const DeclVar) void {} + }; + + pub const CheckVoidStmt = struct { + base: Instruction, + target: *Instruction, + + pub fn build( + irb: *Builder, + scope: *Scope, + target: *Instruction, + ) !*Instruction { + const inst = try irb.arena().create(CheckVoidStmt{ + .base = Instruction{ + .id = Instruction.Id.CheckVoidStmt, + .is_generated = true, + .scope = scope, + .debug_id = irb.next_debug_id, + .val = IrVal{ .Known = &Value.Void.get(irb.module).base }, + }, + .target = target, + }); + irb.next_debug_id += 1; + try irb.current_basic_block.instruction_list.append(&inst.base); + return &inst.base; + } + + pub fn dump(inst: *const CheckVoidStmt) void {} + }; + + pub const Phi = struct { + base: Instruction, + incoming_blocks: []*BasicBlock, + incoming_values: []*Instruction, + + pub fn build( + irb: *Builder, + scope: *Scope, + incoming_blocks: []*BasicBlock, + incoming_values: []*Instruction, + ) !*Instruction { + const inst = try irb.arena().create(Phi{ + .base = Instruction{ + .id = Instruction.Id.Phi, + .is_generated = false, + .scope = scope, + .debug_id = irb.next_debug_id, + .val = IrVal.Unknown, + }, + .incoming_blocks = incoming_blocks, + .incoming_values = incoming_values, + }); + irb.next_debug_id += 1; + try irb.current_basic_block.instruction_list.append(&inst.base); + return &inst.base; + } + + pub fn dump(inst: *const Phi) void {} + }; + + pub const Br = struct { + base: Instruction, + dest_block: *BasicBlock, + is_comptime: *Instruction, + + pub fn build( + irb: *Builder, + scope: *Scope, + dest_block: *BasicBlock, + is_comptime: *Instruction, + ) !*Instruction { + const inst = try irb.arena().create(Br{ + .base = Instruction{ + .id = Instruction.Id.Br, + .is_generated = false, + .scope = scope, + .debug_id = irb.next_debug_id, + .val = IrVal{ .Known = &Value.NoReturn.get(irb.module).base }, + }, + .dest_block = dest_block, + .is_comptime = is_comptime, + }); + irb.next_debug_id += 1; + try irb.current_basic_block.instruction_list.append(&inst.base); + return &inst.base; + } + + pub fn dump(inst: *const Br) void {} }; }; + +pub const Variable = struct { + child_scope: *Scope, +}; + +pub const BasicBlock = struct { + ref_count: usize, + name_hint: []const u8, + debug_id: usize, + scope: *Scope, + instruction_list: std.ArrayList(*Instruction), + + pub fn ref(self: *BasicBlock) void { + self.ref_count += 1; + } +}; + +/// Stuff that survives longer than Builder +pub const Code = struct { + basic_block_list: std.ArrayList(*BasicBlock), + arena: std.heap.ArenaAllocator, + + /// allocator is module.a() + pub fn destroy(self: *Code, allocator: *Allocator) void { + self.arena.deinit(); + allocator.destroy(self); + } + + pub fn dump(self: *Code) void { + var bb_i: usize = 0; + for (self.basic_block_list.toSliceConst()) |bb| { + std.debug.warn("{}_{}:\n", bb.name_hint, bb.debug_id); + for (bb.instruction_list.toSliceConst()) |instr| { + std.debug.warn(" "); + instr.dump(); + std.debug.warn("\n"); + } + } + } +}; + +pub const Builder = struct { + module: *Module, + code: *Code, + current_basic_block: *BasicBlock, + next_debug_id: usize, + parsed_file: *ParsedFile, + is_comptime: bool, + + pub const Error = error{ + OutOfMemory, + Unimplemented, + }; + + pub fn init(module: *Module, parsed_file: *ParsedFile) !Builder { + const code = try module.a().create(Code{ + .basic_block_list = undefined, + .arena = std.heap.ArenaAllocator.init(module.a()), + }); + code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator); + errdefer code.destroy(module.a()); + + return Builder{ + .module = module, + .parsed_file = parsed_file, + .current_basic_block = undefined, + .code = code, + .next_debug_id = 0, + .is_comptime = false, + }; + } + + pub fn abort(self: *Builder) void { + self.code.destroy(self.module.a()); + } + + /// Call code.destroy() when done + pub fn finish(self: *Builder) *Code { + return self.code; + } + + /// No need to clean up resources thanks to the arena allocator. + pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: []const u8) !*BasicBlock { + const basic_block = try self.arena().create(BasicBlock{ + .ref_count = 0, + .name_hint = name_hint, + .debug_id = self.next_debug_id, + .scope = scope, + .instruction_list = std.ArrayList(*Instruction).init(self.arena()), + }); + self.next_debug_id += 1; + return basic_block; + } + + pub fn setCursorAtEndAndAppendBlock(self: *Builder, basic_block: *BasicBlock) !void { + try self.code.basic_block_list.append(basic_block); + self.setCursorAtEnd(basic_block); + } + + pub fn setCursorAtEnd(self: *Builder, basic_block: *BasicBlock) void { + self.current_basic_block = basic_block; + } + + pub fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Instruction { + switch (node.id) { + ast.Node.Id.Root => unreachable, + ast.Node.Id.Use => unreachable, + ast.Node.Id.TestDecl => unreachable, + ast.Node.Id.VarDecl => @panic("TODO"), + ast.Node.Id.Defer => @panic("TODO"), + ast.Node.Id.InfixOp => @panic("TODO"), + ast.Node.Id.PrefixOp => @panic("TODO"), + ast.Node.Id.SuffixOp => @panic("TODO"), + ast.Node.Id.Switch => @panic("TODO"), + ast.Node.Id.While => @panic("TODO"), + ast.Node.Id.For => @panic("TODO"), + ast.Node.Id.If => @panic("TODO"), + ast.Node.Id.ControlFlowExpression => return error.Unimplemented, + ast.Node.Id.Suspend => @panic("TODO"), + ast.Node.Id.VarType => @panic("TODO"), + ast.Node.Id.ErrorType => @panic("TODO"), + ast.Node.Id.FnProto => @panic("TODO"), + ast.Node.Id.PromiseType => @panic("TODO"), + ast.Node.Id.IntegerLiteral => @panic("TODO"), + ast.Node.Id.FloatLiteral => @panic("TODO"), + ast.Node.Id.StringLiteral => @panic("TODO"), + ast.Node.Id.MultilineStringLiteral => @panic("TODO"), + ast.Node.Id.CharLiteral => @panic("TODO"), + ast.Node.Id.BoolLiteral => @panic("TODO"), + ast.Node.Id.NullLiteral => @panic("TODO"), + ast.Node.Id.UndefinedLiteral => @panic("TODO"), + ast.Node.Id.ThisLiteral => @panic("TODO"), + ast.Node.Id.Unreachable => @panic("TODO"), + ast.Node.Id.Identifier => @panic("TODO"), + ast.Node.Id.GroupedExpression => { + const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node); + return irb.genNode(grouped_expr.expr, scope, lval); + }, + ast.Node.Id.BuiltinCall => @panic("TODO"), + ast.Node.Id.ErrorSetDecl => @panic("TODO"), + ast.Node.Id.ContainerDecl => @panic("TODO"), + ast.Node.Id.Asm => @panic("TODO"), + ast.Node.Id.Comptime => @panic("TODO"), + ast.Node.Id.Block => { + const block = @fieldParentPtr(ast.Node.Block, "base", node); + return irb.lvalWrap(scope, try irb.genBlock(block, scope), lval); + }, + ast.Node.Id.DocComment => @panic("TODO"), + ast.Node.Id.SwitchCase => @panic("TODO"), + ast.Node.Id.SwitchElse => @panic("TODO"), + ast.Node.Id.Else => @panic("TODO"), + ast.Node.Id.Payload => @panic("TODO"), + ast.Node.Id.PointerPayload => @panic("TODO"), + ast.Node.Id.PointerIndexPayload => @panic("TODO"), + ast.Node.Id.StructField => @panic("TODO"), + ast.Node.Id.UnionTag => @panic("TODO"), + ast.Node.Id.EnumTag => @panic("TODO"), + ast.Node.Id.ErrorTag => @panic("TODO"), + ast.Node.Id.AsmInput => @panic("TODO"), + ast.Node.Id.AsmOutput => @panic("TODO"), + ast.Node.Id.AsyncAttribute => @panic("TODO"), + ast.Node.Id.ParamDecl => @panic("TODO"), + ast.Node.Id.FieldInitializer => @panic("TODO"), + } + } + + fn isCompTime(irb: *Builder, target_scope: *Scope) bool { + if (irb.is_comptime) + return true; + + var scope = target_scope; + while (true) { + switch (scope.id) { + Scope.Id.CompTime => return true, + Scope.Id.FnDef => return false, + Scope.Id.Decls => unreachable, + Scope.Id.Block, + Scope.Id.Defer, + Scope.Id.DeferExpr, + => scope = scope.parent orelse return false, + } + } + } + + pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Instruction { + const block_scope = try Scope.Block.create(irb.module, parent_scope); + + const outer_block_scope = &block_scope.base; + var child_scope = outer_block_scope; + + if (parent_scope.findFnDef()) |fndef_scope| { + if (fndef_scope.fn_val.child_scope == parent_scope) { + fndef_scope.fn_val.block_scope = block_scope; + } + } + + if (block.statements.len == 0) { + // {} + return Instruction.Const.buildVoid(irb, child_scope, false); + } + + if (block.label) |label| { + block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena()); + block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena()); + block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd"); + block_scope.is_comptime = try Instruction.Const.buildBool(irb, parent_scope, irb.isCompTime(parent_scope)); + } + + var is_continuation_unreachable = false; + var noreturn_return_value: ?*Instruction = null; + + var stmt_it = block.statements.iterator(0); + while (stmt_it.next()) |statement_node_ptr| { + const statement_node = statement_node_ptr.*; + + if (statement_node.cast(ast.Node.Defer)) |defer_node| { + // defer starts a new scope + const defer_token = irb.parsed_file.tree.tokens.at(defer_node.defer_token); + const kind = switch (defer_token.id) { + Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit, + Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit, + else => unreachable, + }; + const defer_expr_scope = try Scope.DeferExpr.create(irb.module, parent_scope, defer_node.expr); + const defer_child_scope = try Scope.Defer.create(irb.module, parent_scope, kind, defer_expr_scope); + child_scope = &defer_child_scope.base; + continue; + } + const statement_value = try irb.genNode(statement_node, child_scope, LVal.None); + + is_continuation_unreachable = statement_value.isNoReturn(); + if (is_continuation_unreachable) { + // keep the last noreturn statement value around in case we need to return it + noreturn_return_value = statement_value; + } + + if (statement_value.cast(Instruction.DeclVar)) |decl_var| { + // variable declarations start a new scope + child_scope = decl_var.variable.child_scope; + } else if (!is_continuation_unreachable) { + // this statement's value must be void + _ = Instruction.CheckVoidStmt.build(irb, child_scope, statement_value); + } + } + + if (is_continuation_unreachable) { + assert(noreturn_return_value != null); + if (block.label == null or block_scope.incoming_blocks.len == 0) { + return noreturn_return_value.?; + } + + try irb.setCursorAtEndAndAppendBlock(block_scope.end_block); + return Instruction.Phi.build( + irb, + parent_scope, + block_scope.incoming_blocks.toOwnedSlice(), + block_scope.incoming_values.toOwnedSlice(), + ); + } + + if (block.label) |label| { + try block_scope.incoming_blocks.append(irb.current_basic_block); + try block_scope.incoming_values.append( + try Instruction.Const.buildVoid(irb, parent_scope, true), + ); + _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit); + (try Instruction.Br.build( + irb, + parent_scope, + block_scope.end_block, + block_scope.is_comptime, + )).setGenerated(); + try irb.setCursorAtEndAndAppendBlock(block_scope.end_block); + return Instruction.Phi.build( + irb, + parent_scope, + block_scope.incoming_blocks.toOwnedSlice(), + block_scope.incoming_values.toOwnedSlice(), + ); + } + + _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit); + const result = try Instruction.Const.buildVoid(irb, child_scope, false); + result.setGenerated(); + return result; + } + + fn genDefersForBlock( + irb: *Builder, + inner_scope: *Scope, + outer_scope: *Scope, + gen_kind: Scope.Defer.Kind, + ) !bool { + var scope = inner_scope; + var is_noreturn = false; + while (true) { + switch (scope.id) { + Scope.Id.Defer => { + const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope); + const generate = switch (defer_scope.kind) { + Scope.Defer.Kind.ScopeExit => true, + Scope.Defer.Kind.ErrorExit => gen_kind == Scope.Defer.Kind.ErrorExit, + }; + if (generate) { + const defer_expr_scope = defer_scope.defer_expr_scope; + const instruction = try irb.genNode( + defer_expr_scope.expr_node, + &defer_expr_scope.base, + LVal.None, + ); + if (instruction.isNoReturn()) { + is_noreturn = true; + } else { + _ = Instruction.CheckVoidStmt.build(irb, &defer_expr_scope.base, instruction); + } + } + }, + Scope.Id.FnDef, + Scope.Id.Decls, + => return is_noreturn, + + Scope.Id.CompTime, + Scope.Id.Block, + => scope = scope.parent orelse return is_noreturn, + + Scope.Id.DeferExpr => unreachable, + } + } + } + + pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Instruction, lval: LVal) !*Instruction { + switch (lval) { + LVal.None => return instruction, + LVal.Ptr => { + // We needed a pointer to a value, but we got a value. So we create + // an instruction which just makes a const pointer of it. + return Instruction.Ref.build(irb, scope, instruction, Mut.Const, Volatility.NonVolatile); + }, + } + } + + fn arena(self: *Builder) *Allocator { + return &self.code.arena.allocator; + } +}; + +pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_file: *ParsedFile) !*Code { + var irb = try Builder.init(module, parsed_file); + errdefer irb.abort(); + + const entry_block = try irb.createBasicBlock(scope, "Entry"); + entry_block.ref(); // Entry block gets a reference because we enter it to begin. + try irb.setCursorAtEndAndAppendBlock(entry_block); + + const result = try irb.genNode(body_node, scope, LVal.None); + if (!result.isNoReturn()) { + const void_inst = try Instruction.Const.buildVoid(&irb, scope, false); + (try Instruction.Return.build(&irb, scope, void_inst)).setGenerated(); + } + + return irb.finish(); +} diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 5cde12f65c..e74c84e02c 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -15,12 +15,21 @@ const errmsg = @import("errmsg.zig"); const ast = std.zig.ast; const event = std.event; const assert = std.debug.assert; +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; +const Scope = @import("scope.zig").Scope; +const Decl = @import("decl.zig").Decl; +const ir = @import("ir.zig"); +const Visib = @import("visib.zig").Visib; +const ParsedFile = @import("parsed_file.zig").ParsedFile; +const Value = @import("value.zig").Value; +const Type = Value.Type; pub const Module = struct { loop: *event.Loop, name: Buffer, root_src_path: ?[]const u8, - module: llvm.ModuleRef, + llvm_module: llvm.ModuleRef, context: llvm.ContextRef, builder: llvm.BuilderRef, target: Target, @@ -91,6 +100,16 @@ pub const Module = struct { compile_errors: event.Locked(CompileErrList), + meta_type: *Type.MetaType, + void_type: *Type.Void, + bool_type: *Type.Bool, + noreturn_type: *Type.NoReturn, + + void_value: *Value.Void, + true_value: *Value.Bool, + false_value: *Value.Bool, + noreturn_value: *Value.NoReturn, + const CompileErrList = std.ArrayList(*errmsg.Msg); // TODO handle some of these earlier and report them in a way other than error codes @@ -129,6 +148,7 @@ pub const Module = struct { Overflow, NotSupported, BufferTooSmall, + Unimplemented, }; pub const Event = union(enum) { @@ -180,8 +200,8 @@ pub const Module = struct { const context = c.LLVMContextCreate() orelse return error.OutOfMemory; errdefer c.LLVMContextDispose(context); - const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory; - errdefer c.LLVMDisposeModule(module); + const llvm_module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory; + errdefer c.LLVMDisposeModule(llvm_module); const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory; errdefer c.LLVMDisposeBuilder(builder); @@ -189,12 +209,12 @@ pub const Module = struct { const events = try event.Channel(Event).create(loop, 0); errdefer events.destroy(); - return loop.allocator.create(Module{ + const module = try loop.allocator.create(Module{ .loop = loop, .events = events, .name = name_buffer, .root_src_path = root_src_path, - .module = module, + .llvm_module = llvm_module, .context = context, .builder = builder, .target = target.*, @@ -248,7 +268,109 @@ pub const Module = struct { .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), .build_group = event.Group(BuildError!void).init(loop), .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), + + .meta_type = undefined, + .void_type = undefined, + .void_value = undefined, + .bool_type = undefined, + .true_value = undefined, + .false_value = undefined, + .noreturn_type = undefined, + .noreturn_value = undefined, + }); + try module.initTypes(); + return module; + } + + fn initTypes(module: *Module) !void { + module.meta_type = try module.a().create(Type.MetaType{ + .base = Type{ + .base = Value{ + .id = Value.Id.Type, + .typeof = undefined, + .ref_count = 3, // 3 because it references itself twice + }, + .id = builtin.TypeId.Type, + }, + .value = undefined, + }); + module.meta_type.value = &module.meta_type.base; + module.meta_type.base.base.typeof = &module.meta_type.base; + errdefer module.a().destroy(module.meta_type); + + module.void_type = try module.a().create(Type.Void{ + .base = Type{ + .base = Value{ + .id = Value.Id.Type, + .typeof = &Type.MetaType.get(module).base, + .ref_count = 1, + }, + .id = builtin.TypeId.Void, + }, + }); + errdefer module.a().destroy(module.void_type); + + module.noreturn_type = try module.a().create(Type.NoReturn{ + .base = Type{ + .base = Value{ + .id = Value.Id.Type, + .typeof = &Type.MetaType.get(module).base, + .ref_count = 1, + }, + .id = builtin.TypeId.NoReturn, + }, + }); + errdefer module.a().destroy(module.noreturn_type); + + module.bool_type = try module.a().create(Type.Bool{ + .base = Type{ + .base = Value{ + .id = Value.Id.Type, + .typeof = &Type.MetaType.get(module).base, + .ref_count = 1, + }, + .id = builtin.TypeId.Bool, + }, + }); + errdefer module.a().destroy(module.bool_type); + + module.void_value = try module.a().create(Value.Void{ + .base = Value{ + .id = Value.Id.Void, + .typeof = &Type.Void.get(module).base, + .ref_count = 1, + }, + }); + errdefer module.a().destroy(module.void_value); + + module.true_value = try module.a().create(Value.Bool{ + .base = Value{ + .id = Value.Id.Bool, + .typeof = &Type.Bool.get(module).base, + .ref_count = 1, + }, + .x = true, + }); + errdefer module.a().destroy(module.true_value); + + module.false_value = try module.a().create(Value.Bool{ + .base = Value{ + .id = Value.Id.Bool, + .typeof = &Type.Bool.get(module).base, + .ref_count = 1, + }, + .x = false, }); + errdefer module.a().destroy(module.false_value); + + module.noreturn_value = try module.a().create(Value.NoReturn{ + .base = Value{ + .id = Value.Id.NoReturn, + .typeof = &Type.NoReturn.get(module).base, + .ref_count = 1, + }, + }); + errdefer module.a().destroy(module.noreturn_value); } fn dump(self: *Module) void { @@ -256,9 +378,17 @@ pub const Module = struct { } pub fn destroy(self: *Module) void { + self.noreturn_value.base.deref(self); + self.void_value.base.deref(self); + self.false_value.base.deref(self); + self.true_value.base.deref(self); + self.noreturn_type.base.base.deref(self); + self.void_type.base.base.deref(self); + self.meta_type.base.base.deref(self); + self.events.destroy(); c.LLVMDisposeBuilder(self.builder); - c.LLVMDisposeModule(self.module); + c.LLVMDisposeModule(self.llvm_module); c.LLVMContextDispose(self.context); self.name.deinit(); @@ -331,8 +461,8 @@ pub const Module = struct { const tree = &parsed_file.tree; // create empty struct for it - const decls = try Scope.Decls.create(self.a(), null); - errdefer decls.destroy(); + const decls = try Scope.Decls.create(self, null); + defer decls.base.deref(self); var decl_group = event.Group(BuildError!void).init(self.loop); errdefer decl_group.cancelAll(); @@ -359,14 +489,17 @@ pub const Module = struct { .id = Decl.Id.Fn, .name = name, .visib = parseVisibToken(tree, fn_proto.visib_token), - .resolution = Decl.Resolution.Unresolved, + .resolution = event.Future(BuildError!void).init(self.loop), + .resolution_in_progress = 0, + .parsed_file = parsed_file, + .parent_scope = &decls.base, }, .value = Decl.Fn.Val{ .Unresolved = {} }, .fn_proto = fn_proto, }); errdefer self.a().destroy(fn_decl); - try decl_group.call(addTopLevelDecl, self, parsed_file, &fn_decl.base); + try decl_group.call(addTopLevelDecl, self, &fn_decl.base); }, ast.Node.Id.TestDecl => @panic("TODO"), else => unreachable, @@ -376,12 +509,12 @@ pub const Module = struct { try await (async self.build_group.wait() catch unreachable); } - async fn addTopLevelDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void { - const is_export = decl.isExported(&parsed_file.tree); + async fn addTopLevelDecl(self: *Module, decl: *Decl) !void { + const is_export = decl.isExported(&decl.parsed_file.tree); if (is_export) { - try self.build_group.call(verifyUniqueSymbol, self, parsed_file, decl); - try self.build_group.call(generateDecl, self, parsed_file, decl); + try self.build_group.call(verifyUniqueSymbol, self, decl); + try self.build_group.call(resolveDecl, self, decl); } } @@ -416,36 +549,21 @@ pub const Module = struct { try compile_errors.value.append(msg); } - async fn verifyUniqueSymbol(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void { + async fn verifyUniqueSymbol(self: *Module, decl: *Decl) !void { const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable); defer exported_symbol_names.release(); if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { try self.addCompileError( - parsed_file, + decl.parsed_file, decl.getSpan(), "exported symbol collision: '{}'", decl.name, ); + // TODO add error note showing location of other symbol } } - /// This declaration has been blessed as going into the final code generation. - async fn generateDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) void { - switch (decl.id) { - Decl.Id.Var => @panic("TODO"), - Decl.Id.Fn => { - const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl); - return await (async self.generateDeclFn(parsed_file, fn_decl) catch unreachable); - }, - Decl.Id.CompTime => @panic("TODO"), - } - } - - async fn generateDeclFn(self: *Module, parsed_file: *ParsedFile, fn_decl: *Decl.Fn) void { - fn_decl.value = Decl.Fn.Val{ .Ok = Value.Fn{} }; - } - pub fn link(self: *Module, out_file: ?[]const u8) !void { warn("TODO link"); return error.Todo; @@ -501,177 +619,48 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib } } -pub const Scope = struct { - id: Id, - parent: ?*Scope, - - pub const Id = enum { - Decls, - Block, - }; - - pub const Decls = struct { - base: Scope, - table: Decl.Table, - - pub fn create(a: *Allocator, parent: ?*Scope) !*Decls { - const self = try a.create(Decls{ - .base = Scope{ - .id = Id.Decls, - .parent = parent, - }, - .table = undefined, - }); - errdefer a.destroy(self); - - self.table = Decl.Table.init(a); - errdefer self.table.deinit(); - - return self; - } - - pub fn destroy(self: *Decls) void { - self.table.deinit(); - self.table.allocator.destroy(self); - self.* = undefined; - } - }; - - pub const Block = struct { - base: Scope, - }; -}; - -pub const Visib = enum { - Private, - Pub, -}; - -pub const Decl = struct { - id: Id, - name: []const u8, - visib: Visib, - resolution: Resolution, - - pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); - - pub fn isExported(base: *const Decl, tree: *ast.Tree) bool { - switch (base.id) { - Id.Fn => { - const fn_decl = @fieldParentPtr(Fn, "base", base); - return fn_decl.isExported(tree); - }, - else => return false, - } +/// This declaration has been blessed as going into the final code generation. +pub async fn resolveDecl(module: *Module, decl: *Decl) !void { + if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { + decl.resolution.data = await (async generateDecl(module, decl) catch unreachable); + decl.resolution.resolve(); + } else { + return (await (async decl.resolution.get() catch unreachable)).*; } +} - pub fn getSpan(base: *const Decl) errmsg.Span { - switch (base.id) { - Id.Fn => { - const fn_decl = @fieldParentPtr(Fn, "base", base); - const fn_proto = fn_decl.fn_proto; - const start = fn_proto.fn_token; - const end = fn_proto.name_token orelse start; - return errmsg.Span{ - .first = start, - .last = end + 1, - }; - }, - else => @panic("TODO"), - } +/// The function that actually does the generation. +async fn generateDecl(module: *Module, decl: *Decl) !void { + switch (decl.id) { + Decl.Id.Var => @panic("TODO"), + Decl.Id.Fn => { + const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl); + return await (async generateDeclFn(module, fn_decl) catch unreachable); + }, + Decl.Id.CompTime => @panic("TODO"), } +} - pub const Resolution = enum { - Unresolved, - InProgress, - Invalid, - Ok, - }; - - pub const Id = enum { - Var, - Fn, - CompTime, - }; - - pub const Var = struct { - base: Decl, - }; +async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void { + const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl"); - pub const Fn = struct { - base: Decl, - value: Val, - fn_proto: *const ast.Node.FnProto, + const fndef_scope = try Scope.FnDef.create(module, fn_decl.base.parent_scope); + defer fndef_scope.base.deref(module); - // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous - pub const Val = union { - Unresolved: void, - Ok: Value.Fn, - }; + const fn_type = try Type.Fn.create(module); + defer fn_type.base.base.deref(module); - pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { - return if (self.fn_proto.extern_export_inline_token) |tok_index| x: { - const token = tree.tokens.at(tok_index); - break :x switch (token.id) { - Token.Id.Extern => tree.tokenSlicePtr(token), - else => null, - }; - } else null; - } + const fn_val = try Value.Fn.create(module, fn_type, fndef_scope); + defer fn_val.base.deref(module); - pub fn isExported(self: Fn, tree: *ast.Tree) bool { - if (self.fn_proto.extern_export_inline_token) |tok_index| { - const token = tree.tokens.at(tok_index); - return token.id == Token.Id.Keyword_export; - } else { - return false; - } - } - }; + fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; - pub const CompTime = struct { - base: Decl, - }; -}; - -pub const Value = struct { - pub const Fn = struct {}; -}; - -pub const Type = struct { - id: Id, - - pub const Id = enum { - Type, - Void, - Bool, - NoReturn, - Int, - Float, - Pointer, - Array, - Struct, - ComptimeFloat, - ComptimeInt, - Undefined, - Null, - Optional, - ErrorUnion, - ErrorSet, - Enum, - Union, - Fn, - Opaque, - Promise, - }; - - pub const Struct = struct { - base: Type, - decls: *Scope.Decls, - }; -}; - -pub const ParsedFile = struct { - tree: ast.Tree, - realpath: []const u8, -}; + const code = try await (async ir.gen( + module, + body_node, + &fndef_scope.base, + fn_decl.base.parsed_file, + ) catch unreachable); + //code.dump(); + //try await (async irAnalyze(module, func) catch unreachable); +} diff --git a/src-self-hosted/parsed_file.zig b/src-self-hosted/parsed_file.zig new file mode 100644 index 0000000000..d728c2fd18 --- /dev/null +++ b/src-self-hosted/parsed_file.zig @@ -0,0 +1,6 @@ +const ast = @import("std").zig.ast; + +pub const ParsedFile = struct { + tree: ast.Tree, + realpath: []const u8, +}; diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index b73dcb4ed3..8f8d016a7c 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -1,16 +1,234 @@ +const std = @import("std"); +const Allocator = mem.Allocator; +const Decl = @import("decl.zig").Decl; +const Module = @import("module.zig").Module; +const mem = std.mem; +const ast = std.zig.ast; +const Value = @import("value.zig").Value; +const ir = @import("ir.zig"); + pub const Scope = struct { id: Id, - parent: *Scope, + parent: ?*Scope, + ref_count: usize, + + pub fn ref(base: *Scope) void { + base.ref_count += 1; + } + + pub fn deref(base: *Scope, module: *Module) void { + base.ref_count -= 1; + if (base.ref_count == 0) { + if (base.parent) |parent| parent.deref(module); + switch (base.id) { + Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(), + Id.Block => @fieldParentPtr(Block, "base", base).destroy(module), + Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(module), + Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(module), + Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(module), + Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(module), + } + } + } + + pub fn findFnDef(base: *Scope) ?*FnDef { + var scope = base; + while (true) { + switch (scope.id) { + Id.FnDef => return @fieldParentPtr(FnDef, "base", base), + Id.Decls => return null, + + Id.Block, + Id.Defer, + Id.DeferExpr, + Id.CompTime, + => scope = scope.parent orelse return null, + } + } + } pub const Id = enum { Decls, Block, - Defer, - DeferExpr, - VarDecl, - CImport, - Loop, FnDef, CompTime, + Defer, + DeferExpr, + }; + + pub const Decls = struct { + base: Scope, + table: Decl.Table, + + /// Creates a Decls scope with 1 reference + pub fn create(module: *Module, parent: ?*Scope) !*Decls { + const self = try module.a().create(Decls{ + .base = Scope{ + .id = Id.Decls, + .parent = parent, + .ref_count = 1, + }, + .table = undefined, + }); + errdefer module.a().destroy(self); + + self.table = Decl.Table.init(module.a()); + errdefer self.table.deinit(); + + if (parent) |p| p.ref(); + + return self; + } + + pub fn destroy(self: *Decls) void { + self.table.deinit(); + self.table.allocator.destroy(self); + } + }; + + pub const Block = struct { + base: Scope, + incoming_values: std.ArrayList(*ir.Instruction), + incoming_blocks: std.ArrayList(*ir.BasicBlock), + end_block: *ir.BasicBlock, + is_comptime: *ir.Instruction, + + /// Creates a Block scope with 1 reference + pub fn create(module: *Module, parent: ?*Scope) !*Block { + const self = try module.a().create(Block{ + .base = Scope{ + .id = Id.Block, + .parent = parent, + .ref_count = 1, + }, + .incoming_values = undefined, + .incoming_blocks = undefined, + .end_block = undefined, + .is_comptime = undefined, + }); + errdefer module.a().destroy(self); + + if (parent) |p| p.ref(); + return self; + } + + pub fn destroy(self: *Block, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const FnDef = struct { + base: Scope, + + /// This reference is not counted so that the scope can get destroyed with the function + fn_val: *Value.Fn, + + /// Creates a FnDef scope with 1 reference + /// Must set the fn_val later + pub fn create(module: *Module, parent: ?*Scope) !*FnDef { + const self = try module.a().create(FnDef{ + .base = Scope{ + .id = Id.FnDef, + .parent = parent, + .ref_count = 1, + }, + .fn_val = undefined, + }); + + if (parent) |p| p.ref(); + + return self; + } + + pub fn destroy(self: *FnDef, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const CompTime = struct { + base: Scope, + + /// Creates a CompTime scope with 1 reference + pub fn create(module: *Module, parent: ?*Scope) !*CompTime { + const self = try module.a().create(CompTime{ + .base = Scope{ + .id = Id.CompTime, + .parent = parent, + .ref_count = 1, + }, + }); + + if (parent) |p| p.ref(); + return self; + } + + pub fn destroy(self: *CompTime, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const Defer = struct { + base: Scope, + defer_expr_scope: *DeferExpr, + kind: Kind, + + pub const Kind = enum { + ScopeExit, + ErrorExit, + }; + + /// Creates a Defer scope with 1 reference + pub fn create( + module: *Module, + parent: ?*Scope, + kind: Kind, + defer_expr_scope: *DeferExpr, + ) !*Defer { + const self = try module.a().create(Defer{ + .base = Scope{ + .id = Id.Defer, + .parent = parent, + .ref_count = 1, + }, + .defer_expr_scope = defer_expr_scope, + .kind = kind, + }); + errdefer module.a().destroy(self); + + defer_expr_scope.base.ref(); + + if (parent) |p| p.ref(); + return self; + } + + pub fn destroy(self: *Defer, module: *Module) void { + self.defer_expr_scope.base.deref(module); + module.a().destroy(self); + } + }; + + pub const DeferExpr = struct { + base: Scope, + expr_node: *ast.Node, + + /// Creates a DeferExpr scope with 1 reference + pub fn create(module: *Module, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr { + const self = try module.a().create(DeferExpr{ + .base = Scope{ + .id = Id.DeferExpr, + .parent = parent, + .ref_count = 1, + }, + .expr_node = expr_node, + }); + errdefer module.a().destroy(self); + + if (parent) |p| p.ref(); + return self; + } + + pub fn destroy(self: *DeferExpr, module: *Module) void { + module.a().destroy(self); + } }; }; diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig new file mode 100644 index 0000000000..4b3918854d --- /dev/null +++ b/src-self-hosted/type.zig @@ -0,0 +1,268 @@ +const builtin = @import("builtin"); +const Scope = @import("scope.zig").Scope; +const Module = @import("module.zig").Module; +const Value = @import("value.zig").Value; + +pub const Type = struct { + base: Value, + id: Id, + + pub const Id = builtin.TypeId; + + pub fn destroy(base: *Type, module: *Module) void { + switch (base.id) { + Id.Struct => @fieldParentPtr(Struct, "base", base).destroy(module), + Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(module), + Id.Type => @fieldParentPtr(MetaType, "base", base).destroy(module), + Id.Void => @fieldParentPtr(Void, "base", base).destroy(module), + Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module), + Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module), + Id.Int => @fieldParentPtr(Int, "base", base).destroy(module), + Id.Float => @fieldParentPtr(Float, "base", base).destroy(module), + Id.Pointer => @fieldParentPtr(Pointer, "base", base).destroy(module), + Id.Array => @fieldParentPtr(Array, "base", base).destroy(module), + Id.ComptimeFloat => @fieldParentPtr(ComptimeFloat, "base", base).destroy(module), + Id.ComptimeInt => @fieldParentPtr(ComptimeInt, "base", base).destroy(module), + Id.Undefined => @fieldParentPtr(Undefined, "base", base).destroy(module), + Id.Null => @fieldParentPtr(Null, "base", base).destroy(module), + Id.Optional => @fieldParentPtr(Optional, "base", base).destroy(module), + Id.ErrorUnion => @fieldParentPtr(ErrorUnion, "base", base).destroy(module), + Id.ErrorSet => @fieldParentPtr(ErrorSet, "base", base).destroy(module), + Id.Enum => @fieldParentPtr(Enum, "base", base).destroy(module), + Id.Union => @fieldParentPtr(Union, "base", base).destroy(module), + Id.Namespace => @fieldParentPtr(Namespace, "base", base).destroy(module), + Id.Block => @fieldParentPtr(Block, "base", base).destroy(module), + Id.BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(module), + Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(module), + Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(module), + Id.Promise => @fieldParentPtr(Promise, "base", base).destroy(module), + } + } + + pub const Struct = struct { + base: Type, + decls: *Scope.Decls, + + pub fn destroy(self: *Struct, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const Fn = struct { + base: Type, + + pub fn create(module: *Module) !*Fn { + return module.a().create(Fn{ + .base = Type{ + .base = Value{ + .id = Value.Id.Type, + .typeof = &MetaType.get(module).base, + .ref_count = 1, + }, + .id = builtin.TypeId.Fn, + }, + }); + } + + pub fn destroy(self: *Fn, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const MetaType = struct { + base: Type, + value: *Type, + + /// Adds 1 reference to the resulting type + pub fn get(module: *Module) *MetaType { + module.meta_type.base.base.ref(); + return module.meta_type; + } + + pub fn destroy(self: *MetaType, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const Void = struct { + base: Type, + + /// Adds 1 reference to the resulting type + pub fn get(module: *Module) *Void { + module.void_type.base.base.ref(); + return module.void_type; + } + + pub fn destroy(self: *Void, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const Bool = struct { + base: Type, + + /// Adds 1 reference to the resulting type + pub fn get(module: *Module) *Bool { + module.bool_type.base.base.ref(); + return module.bool_type; + } + + pub fn destroy(self: *Bool, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const NoReturn = struct { + base: Type, + + /// Adds 1 reference to the resulting type + pub fn get(module: *Module) *NoReturn { + module.noreturn_type.base.base.ref(); + return module.noreturn_type; + } + + pub fn destroy(self: *NoReturn, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const Int = struct { + base: Type, + + pub fn destroy(self: *Int, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const Float = struct { + base: Type, + + pub fn destroy(self: *Float, module: *Module) void { + module.a().destroy(self); + } + }; + pub const Pointer = struct { + base: Type, + + pub fn destroy(self: *Pointer, module: *Module) void { + module.a().destroy(self); + } + }; + pub const Array = struct { + base: Type, + + pub fn destroy(self: *Array, module: *Module) void { + module.a().destroy(self); + } + }; + pub const ComptimeFloat = struct { + base: Type, + + pub fn destroy(self: *ComptimeFloat, module: *Module) void { + module.a().destroy(self); + } + }; + pub const ComptimeInt = struct { + base: Type, + + pub fn destroy(self: *ComptimeInt, module: *Module) void { + module.a().destroy(self); + } + }; + pub const Undefined = struct { + base: Type, + + pub fn destroy(self: *Undefined, module: *Module) void { + module.a().destroy(self); + } + }; + pub const Null = struct { + base: Type, + + pub fn destroy(self: *Null, module: *Module) void { + module.a().destroy(self); + } + }; + pub const Optional = struct { + base: Type, + + pub fn destroy(self: *Optional, module: *Module) void { + module.a().destroy(self); + } + }; + pub const ErrorUnion = struct { + base: Type, + + pub fn destroy(self: *ErrorUnion, module: *Module) void { + module.a().destroy(self); + } + }; + pub const ErrorSet = struct { + base: Type, + + pub fn destroy(self: *ErrorSet, module: *Module) void { + module.a().destroy(self); + } + }; + pub const Enum = struct { + base: Type, + + pub fn destroy(self: *Enum, module: *Module) void { + module.a().destroy(self); + } + }; + pub const Union = struct { + base: Type, + + pub fn destroy(self: *Union, module: *Module) void { + module.a().destroy(self); + } + }; + pub const Namespace = struct { + base: Type, + + pub fn destroy(self: *Namespace, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const Block = struct { + base: Type, + + pub fn destroy(self: *Block, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const BoundFn = struct { + base: Type, + + pub fn destroy(self: *BoundFn, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const ArgTuple = struct { + base: Type, + + pub fn destroy(self: *ArgTuple, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const Opaque = struct { + base: Type, + + pub fn destroy(self: *Opaque, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const Promise = struct { + base: Type, + + pub fn destroy(self: *Promise, module: *Module) void { + module.a().destroy(self); + } + }; +}; diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig new file mode 100644 index 0000000000..b53d03d0ad --- /dev/null +++ b/src-self-hosted/value.zig @@ -0,0 +1,125 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const Scope = @import("scope.zig").Scope; +const Module = @import("module.zig").Module; + +/// Values are ref-counted, heap-allocated, and copy-on-write +/// If there is only 1 ref then write need not copy +pub const Value = struct { + id: Id, + typeof: *Type, + ref_count: usize, + + pub fn ref(base: *Value) void { + base.ref_count += 1; + } + + pub fn deref(base: *Value, module: *Module) void { + base.ref_count -= 1; + if (base.ref_count == 0) { + base.typeof.base.deref(module); + switch (base.id) { + Id.Type => @fieldParentPtr(Type, "base", base).destroy(module), + Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(module), + Id.Void => @fieldParentPtr(Void, "base", base).destroy(module), + Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module), + Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module), + } + } + } + + pub fn dump(base: *const Value) void { + std.debug.warn("{}", @tagName(base.id)); + } + + pub const Id = enum { + Type, + Fn, + Void, + Bool, + NoReturn, + }; + + pub const Type = @import("type.zig").Type; + + pub const Fn = struct { + base: Value, + + /// parent should be the top level decls or container decls + fndef_scope: *Scope.FnDef, + + /// parent is scope for last parameter + child_scope: *Scope, + + /// parent is child_scope + block_scope: *Scope.Block, + + /// Creates a Fn value with 1 ref + pub fn create(module: *Module, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef) !*Fn { + const self = try module.a().create(Fn{ + .base = Value{ + .id = Value.Id.Fn, + .typeof = &fn_type.base, + .ref_count = 1, + }, + .fndef_scope = fndef_scope, + .child_scope = &fndef_scope.base, + .block_scope = undefined, + }); + fn_type.base.base.ref(); + fndef_scope.fn_val = self; + fndef_scope.base.ref(); + return self; + } + + pub fn destroy(self: *Fn, module: *Module) void { + self.fndef_scope.base.deref(module); + module.a().destroy(self); + } + }; + + pub const Void = struct { + base: Value, + + pub fn get(module: *Module) *Void { + module.void_value.base.ref(); + return module.void_value; + } + + pub fn destroy(self: *Void, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const Bool = struct { + base: Value, + x: bool, + + pub fn get(module: *Module, x: bool) *Bool { + if (x) { + module.true_value.base.ref(); + return module.true_value; + } else { + module.false_value.base.ref(); + return module.false_value; + } + } + + pub fn destroy(self: *Bool, module: *Module) void { + module.a().destroy(self); + } + }; + + pub const NoReturn = struct { + base: Value, + + pub fn get(module: *Module) *NoReturn { + module.noreturn_value.base.ref(); + return module.noreturn_value; + } + + pub fn destroy(self: *NoReturn, module: *Module) void { + module.a().destroy(self); + } + }; +}; diff --git a/src-self-hosted/visib.zig b/src-self-hosted/visib.zig new file mode 100644 index 0000000000..3704600cca --- /dev/null +++ b/src-self-hosted/visib.zig @@ -0,0 +1,4 @@ +pub const Visib = enum { + Private, + Pub, +}; diff --git a/std/event/future.zig b/std/event/future.zig index b6ec861f77..23fa570c8f 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -57,7 +57,7 @@ test "std.event.Future" { const allocator = &da.allocator; var loop: Loop = undefined; - try loop.initSingleThreaded(allocator); + try loop.initMultiThreaded(allocator); defer loop.deinit(); const handle = try async testFuture(&loop); diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 63518c5182..004f9278b9 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -970,14 +970,8 @@ pub const Node = struct { pub const Defer = struct { base: Node, defer_token: TokenIndex, - kind: Kind, expr: *Node, - const Kind = enum { - Error, - Unconditional, - }; - pub fn iterate(self: *Defer, index: usize) ?*Node { var i = index; diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 9f0371d4da..9842ba2a17 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -1041,11 +1041,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const node = try arena.create(ast.Node.Defer{ .base = ast.Node{ .id = ast.Node.Id.Defer }, .defer_token = token_index, - .kind = switch (token_ptr.id) { - Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, - Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, - else => unreachable, - }, .expr = undefined, }); const node_ptr = try block.statements.addOne(); -- cgit v1.2.3 From 69e60e351b8b44ca59f814334d6c5d07f1103d96 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 12 Jul 2018 15:22:10 -0400 Subject: self-hosted: better IR for empty fn avoids a void --- src-self-hosted/ir.zig | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index f517dfe579..19bb018472 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -574,9 +574,7 @@ pub const Builder = struct { } _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit); - const result = try Instruction.Const.buildVoid(irb, child_scope, false); - result.setGenerated(); - return result; + return try Instruction.Const.buildVoid(irb, child_scope, true); } fn genDefersForBlock( @@ -648,8 +646,7 @@ pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_fi const result = try irb.genNode(body_node, scope, LVal.None); if (!result.isNoReturn()) { - const void_inst = try Instruction.Const.buildVoid(&irb, scope, false); - (try Instruction.Return.build(&irb, scope, void_inst)).setGenerated(); + (try Instruction.Return.build(&irb, scope, result)).setGenerated(); } return irb.finish(); -- cgit v1.2.3 From ac096c294976b7cb6433a7939adcd664af770201 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 12 Jul 2018 19:24:32 -0400 Subject: zig fmt --- std/os/windows/index.zig | 2 -- std/os/windows/util.zig | 11 +++-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index f73b8ec261..d3525247c9 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -59,7 +59,6 @@ pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( dwFlags: DWORD, ) BOOLEAN; - pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; @@ -134,7 +133,6 @@ pub extern "kernel32" stdcallcc fn MoveFileExA( dwFlags: DWORD, ) BOOL; - pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL; pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index b04e8efc4b..dda9ce7a8b 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -215,10 +215,7 @@ pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN3 return true; } - -pub const WindowsCreateIoCompletionPortError = error { - Unexpected, -}; +pub const WindowsCreateIoCompletionPortError = error{Unexpected}; pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_completion_port: ?windows.HANDLE, completion_key: usize, concurrent_thread_count: windows.DWORD) !windows.HANDLE { const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse { @@ -230,9 +227,7 @@ pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_compl return handle; } -pub const WindowsPostQueuedCompletionStatusError = error { - Unexpected, -}; +pub const WindowsPostQueuedCompletionStatusError = error{Unexpected}; pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void { if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) { @@ -243,7 +238,7 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_ } } -pub const WindowsWaitResult = error { +pub const WindowsWaitResult = error{ Normal, Aborted, }; -- cgit v1.2.3 From 5354d1f5fc496beb8313488ea1690e02e9c630fa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 Jul 2018 12:34:42 -0400 Subject: allow == for comparing optional pointers closes #658 --- src/codegen.cpp | 4 +--- src/ir.cpp | 30 +++++++++++++----------------- test/cases/optional.zig | 21 +++++++++++++++++++++ 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 54e2da7d61..3f54c120b4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2249,10 +2249,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); } else if (type_entry->id == TypeTableEntryIdEnum || type_entry->id == TypeTableEntryIdErrorSet || - type_entry->id == TypeTableEntryIdPointer || type_entry->id == TypeTableEntryIdBool || - type_entry->id == TypeTableEntryIdPromise || - type_entry->id == TypeTableEntryIdFn) + get_codegen_ptr_type(type_entry) != nullptr) { LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); diff --git a/src/ir.cpp b/src/ir.cpp index eb62cc8bdf..f452ef43e0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11147,7 +11147,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp if (type_is_invalid(resolved_type)) return resolved_type; - + bool operator_allowed; switch (resolved_type->id) { case TypeTableEntryIdInvalid: zig_unreachable(); // handled above @@ -11156,6 +11156,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdComptimeInt: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: + operator_allowed = true; break; case TypeTableEntryIdBool: @@ -11170,19 +11171,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdPromise: - if (!is_equality_cmp) { - ir_add_error_node(ira, source_node, - buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - break; - case TypeTableEntryIdEnum: - if (!is_equality_cmp) { - ir_add_error_node(ira, source_node, - buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } + operator_allowed = is_equality_cmp; break; case TypeTableEntryIdUnreachable: @@ -11190,12 +11180,18 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdStruct: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdUnion: - ir_add_error_node(ira, source_node, - buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); - return ira->codegen->builtin_types.entry_invalid; + operator_allowed = false; + break; + case TypeTableEntryIdOptional: + operator_allowed = is_equality_cmp && get_codegen_ptr_type(resolved_type) != nullptr; + break; + } + if (!operator_allowed) { + ir_add_error_node(ira, source_node, + buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); + return ira->codegen->builtin_types.entry_invalid; } IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); diff --git a/test/cases/optional.zig b/test/cases/optional.zig index 0129252dab..d43682bbec 100644 --- a/test/cases/optional.zig +++ b/test/cases/optional.zig @@ -7,3 +7,24 @@ test "optional pointer to size zero struct" { var o: ?*EmptyStruct = &e; assert(o != null); } + +test "equality compare nullable pointers" { + testNullPtrsEql(); + comptime testNullPtrsEql(); +} + +fn testNullPtrsEql() void { + var number: i32 = 1234; + + var x: ?*i32 = null; + var y: ?*i32 = null; + assert(x == y); + y = &number; + assert(x != y); + assert(x != &number); + assert(&number != x); + x = &number; + assert(x == y); + assert(x == &number); + assert(&number == x); +} -- cgit v1.2.3 From 860d3da9156a0b1f4a1e3e644b423da3e768bb86 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 Jul 2018 13:37:01 -0400 Subject: ir: remove dead code --- src/ir.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index f452ef43e0..23ca901c99 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -246,8 +246,6 @@ static void ir_ref_bb(IrBasicBlock *bb) { static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) { assert(instruction->id != IrInstructionIdInvalid); instruction->ref_count += 1; - if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction)) - ir_ref_bb(instruction->owner_bb); } static void ir_ref_var(VariableTableEntry *var) { -- cgit v1.2.3 From 171f33b961fd19a91bc9dc80e1b69865d06f3ff2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 Jul 2018 14:18:37 -0400 Subject: ir: remove unnecessary and probably buggy code --- src/ir.cpp | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 23ca901c99..1b197d3ed1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9237,26 +9237,9 @@ static TypeTableEntry *ir_finish_anal(IrAnalyze *ira, TypeTableEntry *result_typ } static IrInstruction *ir_get_const(IrAnalyze *ira, IrInstruction *old_instruction) { - IrInstruction *new_instruction; - if (old_instruction->id == IrInstructionIdVarPtr) { - IrInstructionVarPtr *old_var_ptr_instruction = (IrInstructionVarPtr *)old_instruction; - IrInstructionVarPtr *var_ptr_instruction = ir_create_instruction(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - var_ptr_instruction->var = old_var_ptr_instruction->var; - new_instruction = &var_ptr_instruction->base; - } else if (old_instruction->id == IrInstructionIdFieldPtr) { - IrInstructionFieldPtr *field_ptr_instruction = ir_create_instruction(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - new_instruction = &field_ptr_instruction->base; - } else if (old_instruction->id == IrInstructionIdElemPtr) { - IrInstructionElemPtr *elem_ptr_instruction = ir_create_instruction(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - new_instruction = &elem_ptr_instruction->base; - } else { - IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - new_instruction = &const_instruction->base; - } + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + old_instruction->scope, old_instruction->source_node); + IrInstruction *new_instruction = &const_instruction->base; new_instruction->value.special = ConstValSpecialStatic; return new_instruction; } -- cgit v1.2.3 From c87102c3046cfabffe0e680e4793f5ce2a88caa2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 Jul 2018 14:53:54 -0400 Subject: ir_get_ref: delete unnecessary and probably buggy code --- src/ir.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 1b197d3ed1..3007bbcf64 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9593,23 +9593,6 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - if (value->id == IrInstructionIdLoadPtr) { - IrInstructionLoadPtr *load_ptr_inst = (IrInstructionLoadPtr *) value; - - if (load_ptr_inst->ptr->value.type->data.pointer.is_const) { - return load_ptr_inst->ptr; - } - - type_ensure_zero_bits_known(ira->codegen, value->value.type); - if (type_is_invalid(value->value.type)) { - return ira->codegen->invalid_instruction; - } - - if (!type_has_bits(value->value.type)) { - return load_ptr_inst->ptr; - } - } - if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefOk); if (!val) -- cgit v1.2.3 From a1cafa650d4ba64404f5db932a40c67b96ccfd1e Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Fri, 13 Jul 2018 22:35:34 +0200 Subject: Improve ArrayList insert unit tests. --- std/array_list.zig | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/std/array_list.zig b/std/array_list.zig index b71f5be6ab..c3e01a4637 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -266,19 +266,36 @@ test "insert ArrayList test" { defer list.deinit(); try list.append(1); + try list.append(2); + try list.append(3); try list.insert(0, 5); assert(list.items[0] == 5); assert(list.items[1] == 1); + assert(list.items[2] == 2); + assert(list.items[3] == 3); +} + +test "insertSlice ArrayList test" { + var list = ArrayList(i32).init(debug.global_allocator); + defer list.deinit(); + try list.append(1); + try list.append(2); + try list.append(3); + try list.append(4); try list.insertSlice(1, []const i32{ 9, 8, }); - assert(list.items[0] == 5); + assert(list.items[0] == 1); assert(list.items[1] == 9); assert(list.items[2] == 8); + assert(list.items[3] == 2); + assert(list.items[4] == 3); + assert(list.items[5] == 4); const items = []const i32{1}; try list.insertSlice(0, items[0..0]); - assert(list.items[0] == 5); + assert(list.len == 6); + assert(list.items[0] == 1); } -- cgit v1.2.3 From fe98a2da70cedaad47ea70d58399e04eb1c7ead2 Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Fri, 13 Jul 2018 23:01:21 +0200 Subject: Add a copyBackwards to fix the broken insert methods for ArrayList. --- std/array_list.zig | 4 ++-- std/mem.zig | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/std/array_list.zig b/std/array_list.zig index c3e01a4637..5deb3c5fb1 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -85,7 +85,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { try self.ensureCapacity(self.len + 1); self.len += 1; - mem.copy(T, self.items[n + 1 .. self.len], self.items[n .. self.len - 1]); + mem.copyBackwards(T, self.items[n + 1 .. self.len], self.items[n .. self.len - 1]); self.items[n] = item; } @@ -93,7 +93,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { try self.ensureCapacity(self.len + items.len); self.len += items.len; - mem.copy(T, self.items[n + items.len .. self.len], self.items[n .. self.len - items.len]); + mem.copyBackwards(T, self.items[n + items.len .. self.len], self.items[n .. self.len - items.len]); mem.copy(T, self.items[n .. n + items.len], items); } diff --git a/std/mem.zig b/std/mem.zig index 555e1e249d..41c5d0c8a3 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -125,6 +125,7 @@ pub const Allocator = struct { /// Copy all of source into dest at position 0. /// dest.len must be >= source.len. +/// dest.ptr must be <= src.ptr. pub fn copy(comptime T: type, dest: []T, source: []const T) void { // TODO instead of manually doing this check for the whole array // and turning off runtime safety, the compiler should detect loops like @@ -135,6 +136,23 @@ pub fn copy(comptime T: type, dest: []T, source: []const T) void { dest[i] = s; } +/// Copy all of source into dest at position 0. +/// dest.len must be >= source.len. +/// dest.ptr must be >= src.ptr. +pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { + // TODO instead of manually doing this check for the whole array + // and turning off runtime safety, the compiler should detect loops like + // this and automatically omit safety checks for loops + @setRuntimeSafety(false); + assert(dest.len >= source.len); + var i = source.len; + while(i > 0){ + i -= 1; + dest[i] = source[i]; + } +} + + pub fn set(comptime T: type, dest: []T, value: T) void { for (dest) |*d| d.* = value; -- cgit v1.2.3 From e78b1b810fd15dfd135c80d06d621851a59f42c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 Jul 2018 21:56:38 -0400 Subject: self-hosted: basic IR pass2 --- src-self-hosted/errmsg.zig | 7 + src-self-hosted/ir.zig | 750 ++++++++++++++++++++++++++++++++++----------- src-self-hosted/main.zig | 2 +- src-self-hosted/module.zig | 85 +++-- src-self-hosted/type.zig | 33 ++ src-self-hosted/value.zig | 21 ++ std/event/loop.zig | 15 + 7 files changed, 708 insertions(+), 205 deletions(-) diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index a92b5145ce..4e353bfb14 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -14,6 +14,13 @@ pub const Color = enum { pub const Span = struct { first: ast.TokenIndex, last: ast.TokenIndex, + + pub fn token(i: TokenIndex) Span { + return Span { + .first = i, + .last = i, + }; + } }; pub const Msg = struct { diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 19bb018472..22161a0c27 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -9,31 +9,34 @@ const Type = Value.Type; const assert = std.debug.assert; const Token = std.zig.Token; const ParsedFile = @import("parsed_file.zig").ParsedFile; +const Span = @import("errmsg.zig").Span; pub const LVal = enum { None, Ptr, }; -pub const Mut = enum { - Mut, - Const, -}; - -pub const Volatility = enum { - NonVolatile, - Volatile, -}; - pub const IrVal = union(enum) { Unknown, - Known: *Value, + KnownType: *Type, + KnownValue: *Value, + + const Init = enum { + Unknown, + NoReturn, + Void, + }; pub fn dump(self: IrVal) void { switch (self) { - IrVal.Unknown => std.debug.warn("Unknown"), - IrVal.Known => |value| { - std.debug.warn("Known("); + IrVal.Unknown => typeof.dump(), + IrVal.KnownType => |typeof| { + std.debug.warn("KnownType("); + typeof.dump(); + std.debug.warn(")"); + }, + IrVal.KnownValue => |value| { + std.debug.warn("KnownValue("); value.dump(); std.debug.warn(")"); }, @@ -46,10 +49,18 @@ pub const Instruction = struct { scope: *Scope, debug_id: usize, val: IrVal, + ref_count: usize, + span: Span, /// true if this instruction was generated by zig and not from user code is_generated: bool, + /// the instruction that is derived from this one in analysis + child: ?*Instruction, + + /// the instruction that this one derives from in analysis + parent: ?*Instruction, + pub fn cast(base: *Instruction, comptime T: type) ?*T { if (base.id == comptime typeToId(T)) { return @fieldParentPtr(T, "base", base); @@ -81,6 +92,47 @@ pub const Instruction = struct { unreachable; } + pub fn hasSideEffects(base: *const Instruction) bool { + comptime var i = 0; + inline while (i < @memberCount(Id)) : (i += 1) { + if (base.id == @field(Id, @memberName(Id, i))) { + const T = @field(Instruction, @memberName(Id, i)); + return @fieldParentPtr(T, "base", base).hasSideEffects(); + } + } + unreachable; + } + + pub fn analyze(base: *Instruction, ira: *Analyze) Analyze.Error!*Instruction { + comptime var i = 0; + inline while (i < @memberCount(Id)) : (i += 1) { + if (base.id == @field(Id, @memberName(Id, i))) { + const T = @field(Instruction, @memberName(Id, i)); + const new_inst = try @fieldParentPtr(T, "base", base).analyze(ira); + new_inst.linkToParent(base); + return new_inst; + } + } + unreachable; + } + + fn getAsParam(param: *Instruction) !*Instruction { + const child = param.child orelse return error.SemanticAnalysisFailed; + switch (child.val) { + IrVal.Unknown => return error.SemanticAnalysisFailed, + else => return child, + } + } + + /// asserts that the type is known + fn getKnownType(self: *Instruction) *Type { + switch (self.val) { + IrVal.KnownType => |typeof| return typeof, + IrVal.KnownValue => |value| return value.typeof, + IrVal.Unknown => unreachable, + } + } + pub fn setGenerated(base: *Instruction) void { base.is_generated = true; } @@ -88,10 +140,18 @@ pub const Instruction = struct { pub fn isNoReturn(base: *const Instruction) bool { switch (base.val) { IrVal.Unknown => return false, - IrVal.Known => |x| return x.typeof.id == Type.Id.NoReturn, + IrVal.KnownValue => |x| return x.typeof.id == Type.Id.NoReturn, + IrVal.KnownType => |typeof| return typeof.id == Type.Id.NoReturn, } } + pub fn linkToParent(self: *Instruction, parent: *Instruction) void { + assert(self.parent == null); + assert(parent.child == null); + self.parent = parent; + parent.child = self; + } + pub const Id = enum { Return, Const, @@ -100,196 +160,231 @@ pub const Instruction = struct { CheckVoidStmt, Phi, Br, + AddImplicitReturnType, }; pub const Const = struct { base: Instruction, + params: Params, - pub fn buildBool(irb: *Builder, scope: *Scope, val: bool) !*Instruction { - const inst = try irb.arena().create(Const{ - .base = Instruction{ - .id = Instruction.Id.Const, - .is_generated = false, - .scope = scope, - .debug_id = irb.next_debug_id, - .val = IrVal{ .Known = &Value.Bool.get(irb.module, val).base }, - }, - }); - irb.next_debug_id += 1; - try irb.current_basic_block.instruction_list.append(&inst.base); - return &inst.base; - } - - pub fn buildVoid(irb: *Builder, scope: *Scope, is_generated: bool) !*Instruction { - const inst = try irb.arena().create(Const{ - .base = Instruction{ - .id = Instruction.Id.Const, - .is_generated = is_generated, - .scope = scope, - .debug_id = irb.next_debug_id, - .val = IrVal{ .Known = &Value.Void.get(irb.module).base }, - }, - }); - irb.next_debug_id += 1; - try irb.current_basic_block.instruction_list.append(&inst.base); - return &inst.base; + const Params = struct {}; + + // Use Builder.buildConst* methods, or, after building a Const instruction, + // manually set the ir_val field. + const ir_val_init = IrVal.Init.Unknown; + + pub fn dump(self: *const Const) void { + self.base.val.KnownValue.dump(); } - pub fn dump(inst: *const Const) void { - inst.base.val.Known.dump(); + pub fn hasSideEffects(self: *const Const) bool { + return false; + } + + pub fn analyze(self: *const Const, ira: *Analyze) !*Instruction { + const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{}); + new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() }; + return new_inst; } }; pub const Return = struct { base: Instruction, - return_value: *Instruction, - - pub fn build(irb: *Builder, scope: *Scope, return_value: *Instruction) !*Instruction { - const inst = try irb.arena().create(Return{ - .base = Instruction{ - .id = Instruction.Id.Return, - .is_generated = false, - .scope = scope, - .debug_id = irb.next_debug_id, - .val = IrVal{ .Known = &Value.Void.get(irb.module).base }, - }, - .return_value = return_value, - }); - irb.next_debug_id += 1; - try irb.current_basic_block.instruction_list.append(&inst.base); - return &inst.base; + params: Params, + + const Params = struct { + return_value: *Instruction, + }; + + const ir_val_init = IrVal.Init.NoReturn; + + pub fn dump(self: *const Return) void { + std.debug.warn("#{}", self.params.return_value.debug_id); } - pub fn dump(inst: *const Return) void { - std.debug.warn("#{}", inst.return_value.debug_id); + pub fn hasSideEffects(self: *const Return) bool { + return true; + } + + pub fn analyze(self: *const Return, ira: *Analyze) !*Instruction { + const value = try self.params.return_value.getAsParam(); + const casted_value = try ira.implicitCast(value, ira.explicit_return_type); + + // TODO detect returning local variable address + + return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value }); } }; pub const Ref = struct { base: Instruction, - target: *Instruction, - mut: Mut, - volatility: Volatility, + params: Params, - pub fn build( - irb: *Builder, - scope: *Scope, + const Params = struct { target: *Instruction, - mut: Mut, - volatility: Volatility, - ) !*Instruction { - const inst = try irb.arena().create(Ref{ - .base = Instruction{ - .id = Instruction.Id.Ref, - .is_generated = false, - .scope = scope, - .debug_id = irb.next_debug_id, - .val = IrVal.Unknown, - }, + mut: Type.Pointer.Mut, + volatility: Type.Pointer.Vol, + }; + + const ir_val_init = IrVal.Init.Unknown; + + pub fn dump(inst: *const Ref) void {} + + pub fn hasSideEffects(inst: *const Ref) bool { + return false; + } + + pub fn analyze(self: *const Ref, ira: *Analyze) !*Instruction { + const target = try self.params.target.getAsParam(); + + if (ira.getCompTimeValOrNullUndefOk(target)) |val| { + return ira.getCompTimeRef( + val, + Value.Ptr.Mut.CompTimeConst, + self.params.mut, + self.params.volatility, + val.typeof.getAbiAlignment(ira.irb.module), + ); + } + + const new_inst = try ira.irb.build(Ref, self.base.scope, self.base.span, Params{ .target = target, - .mut = mut, - .volatility = volatility, + .mut = self.params.mut, + .volatility = self.params.volatility, }); - irb.next_debug_id += 1; - try irb.current_basic_block.instruction_list.append(&inst.base); - return &inst.base; + const elem_type = target.getKnownType(); + const ptr_type = Type.Pointer.get( + ira.irb.module, + elem_type, + self.params.mut, + self.params.volatility, + Type.Pointer.Size.One, + elem_type.getAbiAlignment(ira.irb.module), + ); + // TODO: potentially set the hint that this is a stack pointer. But it might not be - this + // could be a ref of a global, for example + new_inst.val = IrVal{ .KnownType = &ptr_type.base }; + // TODO potentially add an alloca entry here + return new_inst; } - - pub fn dump(inst: *const Ref) void {} }; pub const DeclVar = struct { base: Instruction, - variable: *Variable, + params: Params, + + const Params = struct { + variable: *Variable, + }; + + const ir_val_init = IrVal.Init.Unknown; pub fn dump(inst: *const DeclVar) void {} + + pub fn hasSideEffects(inst: *const DeclVar) bool { + return true; + } + + pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Instruction { + return error.Unimplemented; // TODO + } }; pub const CheckVoidStmt = struct { base: Instruction, - target: *Instruction, + params: Params, - pub fn build( - irb: *Builder, - scope: *Scope, + const Params = struct { target: *Instruction, - ) !*Instruction { - const inst = try irb.arena().create(CheckVoidStmt{ - .base = Instruction{ - .id = Instruction.Id.CheckVoidStmt, - .is_generated = true, - .scope = scope, - .debug_id = irb.next_debug_id, - .val = IrVal{ .Known = &Value.Void.get(irb.module).base }, - }, - .target = target, - }); - irb.next_debug_id += 1; - try irb.current_basic_block.instruction_list.append(&inst.base); - return &inst.base; - } + }; + + const ir_val_init = IrVal.Init.Unknown; pub fn dump(inst: *const CheckVoidStmt) void {} + + pub fn hasSideEffects(inst: *const CheckVoidStmt) bool { + return true; + } + + pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Instruction { + return error.Unimplemented; // TODO + } }; pub const Phi = struct { base: Instruction, - incoming_blocks: []*BasicBlock, - incoming_values: []*Instruction, + params: Params, - pub fn build( - irb: *Builder, - scope: *Scope, + const Params = struct { incoming_blocks: []*BasicBlock, incoming_values: []*Instruction, - ) !*Instruction { - const inst = try irb.arena().create(Phi{ - .base = Instruction{ - .id = Instruction.Id.Phi, - .is_generated = false, - .scope = scope, - .debug_id = irb.next_debug_id, - .val = IrVal.Unknown, - }, - .incoming_blocks = incoming_blocks, - .incoming_values = incoming_values, - }); - irb.next_debug_id += 1; - try irb.current_basic_block.instruction_list.append(&inst.base); - return &inst.base; - } + }; + + const ir_val_init = IrVal.Init.Unknown; pub fn dump(inst: *const Phi) void {} + + pub fn hasSideEffects(inst: *const Phi) bool { + return false; + } + + pub fn analyze(self: *const Phi, ira: *Analyze) !*Instruction { + return error.Unimplemented; // TODO + } }; pub const Br = struct { base: Instruction, - dest_block: *BasicBlock, - is_comptime: *Instruction, + params: Params, - pub fn build( - irb: *Builder, - scope: *Scope, + const Params = struct { dest_block: *BasicBlock, is_comptime: *Instruction, - ) !*Instruction { - const inst = try irb.arena().create(Br{ - .base = Instruction{ - .id = Instruction.Id.Br, - .is_generated = false, - .scope = scope, - .debug_id = irb.next_debug_id, - .val = IrVal{ .Known = &Value.NoReturn.get(irb.module).base }, - }, - .dest_block = dest_block, - .is_comptime = is_comptime, - }); - irb.next_debug_id += 1; - try irb.current_basic_block.instruction_list.append(&inst.base); - return &inst.base; - } + }; + + const ir_val_init = IrVal.Init.NoReturn; pub fn dump(inst: *const Br) void {} + + pub fn hasSideEffects(inst: *const Br) bool { + return true; + } + + pub fn analyze(self: *const Br, ira: *Analyze) !*Instruction { + return error.Unimplemented; // TODO + } + }; + + pub const AddImplicitReturnType = struct { + base: Instruction, + params: Params, + + pub const Params = struct { + target: *Instruction, + }; + + const ir_val_init = IrVal.Init.Unknown; + + pub fn dump(inst: *const AddImplicitReturnType) void { + std.debug.warn("#{}", inst.params.target.debug_id); + } + + pub fn hasSideEffects(inst: *const AddImplicitReturnType) bool { + return true; + } + + pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Instruction { + const target = try self.params.target.getAsParam(); + + try ira.src_implicit_return_type_list.append(target); + + return ira.irb.build( + AddImplicitReturnType, + self.base.scope, + self.base.span, + Params{ .target = target }, + ); + } }; }; @@ -303,16 +398,31 @@ pub const BasicBlock = struct { debug_id: usize, scope: *Scope, instruction_list: std.ArrayList(*Instruction), + ref_instruction: ?*Instruction, + + /// the basic block that is derived from this one in analysis + child: ?*BasicBlock, + + /// the basic block that this one derives from in analysis + parent: ?*BasicBlock, pub fn ref(self: *BasicBlock) void { self.ref_count += 1; } + + pub fn linkToParent(self: *BasicBlock, parent: *BasicBlock) void { + assert(self.parent == null); + assert(parent.child == null); + self.parent = parent; + parent.child = self; + } }; /// Stuff that survives longer than Builder pub const Code = struct { basic_block_list: std.ArrayList(*BasicBlock), arena: std.heap.ArenaAllocator, + return_type: ?*Type, /// allocator is module.a() pub fn destroy(self: *Code, allocator: *Allocator) void { @@ -341,15 +451,13 @@ pub const Builder = struct { parsed_file: *ParsedFile, is_comptime: bool, - pub const Error = error{ - OutOfMemory, - Unimplemented, - }; + pub const Error = Analyze.Error; pub fn init(module: *Module, parsed_file: *ParsedFile) !Builder { const code = try module.a().create(Code{ .basic_block_list = undefined, .arena = std.heap.ArenaAllocator.init(module.a()), + .return_type = null, }); code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator); errdefer code.destroy(module.a()); @@ -381,6 +489,9 @@ pub const Builder = struct { .debug_id = self.next_debug_id, .scope = scope, .instruction_list = std.ArrayList(*Instruction).init(self.arena()), + .child = null, + .parent = null, + .ref_instruction = null, }); self.next_debug_id += 1; return basic_block; @@ -490,14 +601,18 @@ pub const Builder = struct { if (block.statements.len == 0) { // {} - return Instruction.Const.buildVoid(irb, child_scope, false); + return irb.buildConstVoid(child_scope, Span.token(block.lbrace), false); } if (block.label) |label| { block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena()); block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena()); block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd"); - block_scope.is_comptime = try Instruction.Const.buildBool(irb, parent_scope, irb.isCompTime(parent_scope)); + block_scope.is_comptime = try irb.buildConstBool( + parent_scope, + Span.token(block.lbrace), + irb.isCompTime(parent_scope), + ); } var is_continuation_unreachable = false; @@ -530,10 +645,15 @@ pub const Builder = struct { if (statement_value.cast(Instruction.DeclVar)) |decl_var| { // variable declarations start a new scope - child_scope = decl_var.variable.child_scope; + child_scope = decl_var.params.variable.child_scope; } else if (!is_continuation_unreachable) { // this statement's value must be void - _ = Instruction.CheckVoidStmt.build(irb, child_scope, statement_value); + _ = irb.build( + Instruction.CheckVoidStmt, + child_scope, + statement_value.span, + Instruction.CheckVoidStmt.Params{ .target = statement_value }, + ); } } @@ -544,37 +664,34 @@ pub const Builder = struct { } try irb.setCursorAtEndAndAppendBlock(block_scope.end_block); - return Instruction.Phi.build( - irb, - parent_scope, - block_scope.incoming_blocks.toOwnedSlice(), - block_scope.incoming_values.toOwnedSlice(), - ); + return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{ + .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(), + .incoming_values = block_scope.incoming_values.toOwnedSlice(), + }); } if (block.label) |label| { try block_scope.incoming_blocks.append(irb.current_basic_block); try block_scope.incoming_values.append( - try Instruction.Const.buildVoid(irb, parent_scope, true), + try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true), ); _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit); - (try Instruction.Br.build( - irb, - parent_scope, - block_scope.end_block, - block_scope.is_comptime, - )).setGenerated(); + + _ = try irb.buildGen(Instruction.Br, parent_scope, Span.token(block.rbrace), Instruction.Br.Params{ + .dest_block = block_scope.end_block, + .is_comptime = block_scope.is_comptime, + }); + try irb.setCursorAtEndAndAppendBlock(block_scope.end_block); - return Instruction.Phi.build( - irb, - parent_scope, - block_scope.incoming_blocks.toOwnedSlice(), - block_scope.incoming_values.toOwnedSlice(), - ); + + return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{ + .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(), + .incoming_values = block_scope.incoming_values.toOwnedSlice(), + }); } _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit); - return try Instruction.Const.buildVoid(irb, child_scope, true); + return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true); } fn genDefersForBlock( @@ -603,7 +720,12 @@ pub const Builder = struct { if (instruction.isNoReturn()) { is_noreturn = true; } else { - _ = Instruction.CheckVoidStmt.build(irb, &defer_expr_scope.base, instruction); + _ = try irb.build( + Instruction.CheckVoidStmt, + &defer_expr_scope.base, + Span.token(defer_expr_scope.expr_node.lastToken()), + Instruction.CheckVoidStmt.Params{ .target = instruction }, + ); } } }, @@ -626,7 +748,11 @@ pub const Builder = struct { LVal.Ptr => { // We needed a pointer to a value, but we got a value. So we create // an instruction which just makes a const pointer of it. - return Instruction.Ref.build(irb, scope, instruction, Mut.Const, Volatility.NonVolatile); + return irb.build(Instruction.Ref, scope, instruction.span, Instruction.Ref.Params{ + .target = instruction, + .mut = Type.Pointer.Mut.Const, + .volatility = Type.Pointer.Vol.Non, + }); }, } } @@ -634,9 +760,218 @@ pub const Builder = struct { fn arena(self: *Builder) *Allocator { return &self.code.arena.allocator; } + + fn buildExtra( + self: *Builder, + comptime I: type, + scope: *Scope, + span: Span, + params: I.Params, + is_generated: bool, + ) !*Instruction { + const inst = try self.arena().create(I{ + .base = Instruction{ + .id = Instruction.typeToId(I), + .is_generated = is_generated, + .scope = scope, + .debug_id = self.next_debug_id, + .val = switch (I.ir_val_init) { + IrVal.Init.Unknown => IrVal.Unknown, + IrVal.Init.NoReturn => IrVal{ .KnownValue = &Value.NoReturn.get(self.module).base }, + IrVal.Init.Void => IrVal{ .KnownValue = &Value.Void.get(self.module).base }, + }, + .ref_count = 0, + .span = span, + .child = null, + .parent = null, + }, + .params = params, + }); + + // Look at the params and ref() other instructions + comptime var i = 0; + inline while (i < @memberCount(I.Params)) : (i += 1) { + const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i))); + switch (FieldType) { + *Instruction => @field(inst.params, @memberName(I.Params, i)).ref_count += 1, + ?*Instruction => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref_count += 1, + else => {}, + } + } + + self.next_debug_id += 1; + try self.current_basic_block.instruction_list.append(&inst.base); + return &inst.base; + } + + fn build( + self: *Builder, + comptime I: type, + scope: *Scope, + span: Span, + params: I.Params, + ) !*Instruction { + return self.buildExtra(I, scope, span, params, false); + } + + fn buildGen( + self: *Builder, + comptime I: type, + scope: *Scope, + span: Span, + params: I.Params, + ) !*Instruction { + return self.buildExtra(I, scope, span, params, true); + } + + fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Instruction { + const inst = try self.build(Instruction.Const, scope, span, Instruction.Const.Params{}); + inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.module, x).base }; + return inst; + } + + fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Instruction { + const inst = try self.buildExtra(Instruction.Const, scope, span, Instruction.Const.Params{}, is_generated); + inst.val = IrVal{ .KnownValue = &Value.Void.get(self.module).base }; + return inst; + } +}; + +const Analyze = struct { + irb: Builder, + old_bb_index: usize, + const_predecessor_bb: ?*BasicBlock, + parent_basic_block: *BasicBlock, + instruction_index: usize, + src_implicit_return_type_list: std.ArrayList(*Instruction), + explicit_return_type: ?*Type, + + pub const Error = error{ + /// This is only for when we have already reported a compile error. It is the poison value. + SemanticAnalysisFailed, + + /// This is a placeholder - it is useful to use instead of panicking but once the compiler is + /// done this error code will be removed. + Unimplemented, + + OutOfMemory, + }; + + pub fn init(module: *Module, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze { + var irb = try Builder.init(module, parsed_file); + errdefer irb.abort(); + + return Analyze{ + .irb = irb, + .old_bb_index = 0, + .const_predecessor_bb = null, + .parent_basic_block = undefined, // initialized with startBasicBlock + .instruction_index = undefined, // initialized with startBasicBlock + .src_implicit_return_type_list = std.ArrayList(*Instruction).init(irb.arena()), + .explicit_return_type = explicit_return_type, + }; + } + + pub fn abort(self: *Analyze) void { + self.irb.abort(); + } + + pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Instruction) !*BasicBlock { + if (old_bb.child) |child| { + if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction) + return child; + } + + const new_bb = try self.irb.createBasicBlock(old_bb.scope, old_bb.name_hint); + new_bb.linkToParent(old_bb); + new_bb.ref_instruction = ref_old_instruction; + return new_bb; + } + + pub fn startBasicBlock(self: *Analyze, old_bb: *BasicBlock, const_predecessor_bb: ?*BasicBlock) void { + self.instruction_index = 0; + self.parent_basic_block = old_bb; + self.const_predecessor_bb = const_predecessor_bb; + } + + pub fn finishBasicBlock(ira: *Analyze, old_code: *Code) !void { + try ira.irb.code.basic_block_list.append(ira.irb.current_basic_block); + ira.instruction_index += 1; + + while (ira.instruction_index < ira.parent_basic_block.instruction_list.len) { + const next_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index); + + if (!next_instruction.is_generated) { + try ira.addCompileError(next_instruction.span, "unreachable code"); + break; + } + ira.instruction_index += 1; + } + + ira.old_bb_index += 1; + + var need_repeat = true; + while (true) { + while (ira.old_bb_index < old_code.basic_block_list.len) { + const old_bb = old_code.basic_block_list.at(ira.old_bb_index); + const new_bb = old_bb.child orelse { + ira.old_bb_index += 1; + continue; + }; + if (new_bb.instruction_list.len != 0) { + ira.old_bb_index += 1; + continue; + } + ira.irb.current_basic_block = new_bb; + + ira.startBasicBlock(old_bb, null); + return; + } + if (!need_repeat) + return; + need_repeat = false; + ira.old_bb_index = 0; + continue; + } + } + + fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void { + return self.irb.module.addCompileError(self.irb.parsed_file, span, fmt, args); + } + + fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Instruction) Analyze.Error!*Type { + // TODO actual implementation + return &Type.Void.get(self.irb.module).base; + } + + fn implicitCast(self: *Analyze, target: *Instruction, optional_dest_type: ?*Type) Analyze.Error!*Instruction { + const dest_type = optional_dest_type orelse return target; + @panic("TODO implicitCast"); + } + + fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Instruction) ?*Value { + @panic("TODO getCompTimeValOrNullUndefOk"); + } + + fn getCompTimeRef( + self: *Analyze, + value: *Value, + ptr_mut: Value.Ptr.Mut, + mut: Type.Pointer.Mut, + volatility: Type.Pointer.Vol, + ptr_align: u32, + ) Analyze.Error!*Instruction { + @panic("TODO getCompTimeRef"); + } }; -pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_file: *ParsedFile) !*Code { +pub async fn gen( + module: *Module, + body_node: *ast.Node, + scope: *Scope, + end_span: Span, + parsed_file: *ParsedFile, +) !*Code { var irb = try Builder.init(module, parsed_file); errdefer irb.abort(); @@ -646,8 +981,61 @@ pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_fi const result = try irb.genNode(body_node, scope, LVal.None); if (!result.isNoReturn()) { - (try Instruction.Return.build(&irb, scope, result)).setGenerated(); + _ = irb.buildGen( + Instruction.AddImplicitReturnType, + scope, + end_span, + Instruction.AddImplicitReturnType.Params{ .target = result }, + ); + _ = irb.buildGen( + Instruction.Return, + scope, + end_span, + Instruction.Return.Params{ .return_value = result }, + ); } return irb.finish(); } + +pub async fn analyze(module: *Module, parsed_file: *ParsedFile, old_code: *Code, expected_type: ?*Type) !*Code { + var ira = try Analyze.init(module, parsed_file, expected_type); + errdefer ira.abort(); + + const old_entry_bb = old_code.basic_block_list.at(0); + + const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null); + new_entry_bb.ref(); + + ira.irb.current_basic_block = new_entry_bb; + + ira.startBasicBlock(old_entry_bb, null); + + while (ira.old_bb_index < old_code.basic_block_list.len) { + const old_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index); + + if (old_instruction.ref_count == 0 and !old_instruction.hasSideEffects()) { + ira.instruction_index += 1; + continue; + } + + const return_inst = try old_instruction.analyze(&ira); + // Note: if we ever modify the above to handle error.CompileError by continuing analysis, + // then here we want to check if ira.isCompTime() and return early if true + + if (return_inst.isNoReturn()) { + try ira.finishBasicBlock(old_code); + continue; + } + + ira.instruction_index += 1; + } + + if (ira.src_implicit_return_type_list.len == 0) { + ira.irb.code.return_type = &Type.NoReturn.get(module).base; + return ira.irb.finish(); + } + + ira.irb.code.return_type = try ira.resolvePeerTypes(expected_type, ira.src_implicit_return_type_list.toSliceConst()); + return ira.irb.finish(); +} diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index d7ead0ba32..058459a2d8 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -497,7 +497,7 @@ async fn processBuildEvents(module: *Module, color: errmsg.Color) void { }, Module.Event.Error => |err| { std.debug.warn("build failed: {}\n", @errorName(err)); - @panic("TODO error return trace"); + os.exit(1); }, Module.Event.Fail => |msgs| { for (msgs) |msg| { diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index e74c84e02c..3fbe6d54ad 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -24,6 +24,7 @@ const Visib = @import("visib.zig").Visib; const ParsedFile = @import("parsed_file.zig").ParsedFile; const Value = @import("value.zig").Value; const Type = Value.Type; +const Span = errmsg.Span; pub const Module = struct { loop: *event.Loop, @@ -148,13 +149,14 @@ pub const Module = struct { Overflow, NotSupported, BufferTooSmall, - Unimplemented, + Unimplemented, // TODO remove this one + SemanticAnalysisFailed, // TODO remove this one }; pub const Event = union(enum) { Ok, - Fail: []*errmsg.Msg, Error: BuildError, + Fail: []*errmsg.Msg, }; pub const DarwinVersionMin = union(enum) { @@ -413,21 +415,32 @@ pub const Module = struct { while (true) { // TODO directly awaiting async should guarantee memory allocation elision // TODO also async before suspending should guarantee memory allocation elision - (await (async self.addRootSrc() catch unreachable)) catch |err| { - await (async self.events.put(Event{ .Error = err }) catch unreachable); - return; - }; + const build_result = await (async self.addRootSrc() catch unreachable); + + // this makes a handy error return trace and stack trace in debug mode + if (std.debug.runtime_safety) { + build_result catch unreachable; + } + const compile_errors = blk: { const held = await (async self.compile_errors.acquire() catch unreachable); defer held.release(); break :blk held.value.toOwnedSlice(); }; - if (compile_errors.len == 0) { - await (async self.events.put(Event.Ok) catch unreachable); - } else { - await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable); + if (build_result) |_| { + if (compile_errors.len == 0) { + await (async self.events.put(Event.Ok) catch unreachable); + } else { + await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable); + } + } else |err| { + // if there's an error then the compile errors have dangling references + self.a().free(compile_errors); + + await (async self.events.put(Event{ .Error = err }) catch unreachable); } + // for now we stop after 1 return; } @@ -477,7 +490,7 @@ pub const Module = struct { const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { - try self.addCompileError(parsed_file, errmsg.Span{ + try self.addCompileError(parsed_file, Span{ .first = fn_proto.fn_token, .last = fn_proto.fn_token + 1, }, "missing function name"); @@ -518,27 +531,23 @@ pub const Module = struct { } } - fn addCompileError(self: *Module, parsed_file: *ParsedFile, span: errmsg.Span, comptime fmt: []const u8, args: ...) !void { + fn addCompileError(self: *Module, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void { const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args); errdefer self.loop.allocator.free(text); - try self.build_group.call(addCompileErrorAsync, self, parsed_file, span.first, span.last, text); + try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text); } async fn addCompileErrorAsync( self: *Module, parsed_file: *ParsedFile, - first_token: ast.TokenIndex, - last_token: ast.TokenIndex, + span: Span, text: []u8, ) !void { const msg = try self.loop.allocator.create(errmsg.Msg{ .path = parsed_file.realpath, .text = text, - .span = errmsg.Span{ - .first = first_token, - .last = last_token, - }, + .span = span, .tree = &parsed_file.tree, }); errdefer self.loop.allocator.destroy(msg); @@ -624,6 +633,7 @@ pub async fn resolveDecl(module: *Module, decl: *Decl) !void { if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { decl.resolution.data = await (async generateDecl(module, decl) catch unreachable); decl.resolution.resolve(); + return decl.resolution.data; } else { return (await (async decl.resolution.get() catch unreachable)).*; } @@ -655,12 +665,41 @@ async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void { fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; - const code = try await (async ir.gen( + const unanalyzed_code = (await (async ir.gen( module, body_node, &fndef_scope.base, + Span.token(body_node.lastToken()), + fn_decl.base.parsed_file, + ) catch unreachable)) catch |err| switch (err) { + // This poison value should not cause the errdefers to run. It simply means + // that self.compile_errors is populated. + error.SemanticAnalysisFailed => return {}, + else => return err, + }; + defer unanalyzed_code.destroy(module.a()); + + if (module.verbose_ir) { + std.debug.warn("unanalyzed:\n"); + unanalyzed_code.dump(); + } + + const analyzed_code = (await (async ir.analyze( + module, fn_decl.base.parsed_file, - ) catch unreachable); - //code.dump(); - //try await (async irAnalyze(module, func) catch unreachable); + unanalyzed_code, + null, + ) catch unreachable)) catch |err| switch (err) { + // This poison value should not cause the errdefers to run. It simply means + // that self.compile_errors is populated. + error.SemanticAnalysisFailed => return {}, + else => return err, + }; + defer analyzed_code.destroy(module.a()); + + if (module.verbose_ir) { + std.debug.warn("analyzed:\n"); + analyzed_code.dump(); + } + // TODO now render to LLVM module } diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 4b3918854d..66e1470cc0 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -39,6 +39,14 @@ pub const Type = struct { } } + pub fn dump(base: *const Type) void { + std.debug.warn("{}", @tagName(base.id)); + } + + pub fn getAbiAlignment(base: *Type, module: *Module) u32 { + @panic("TODO getAbiAlignment"); + } + pub const Struct = struct { base: Type, decls: *Scope.Decls, @@ -143,10 +151,35 @@ pub const Type = struct { }; pub const Pointer = struct { base: Type, + mut: Mut, + vol: Vol, + size: Size, + alignment: u32, + + pub const Mut = enum { + Mut, + Const, + }; + pub const Vol = enum { + Non, + Volatile, + }; + pub const Size = builtin.TypeInfo.Pointer.Size; pub fn destroy(self: *Pointer, module: *Module) void { module.a().destroy(self); } + + pub fn get( + module: *Module, + elem_type: *Type, + mut: Mut, + vol: Vol, + size: Size, + alignment: u32, + ) *Pointer { + @panic("TODO get pointer"); + } }; pub const Array = struct { base: Type, diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index b53d03d0ad..7ee594b41c 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -24,10 +24,16 @@ pub const Value = struct { Id.Void => @fieldParentPtr(Void, "base", base).destroy(module), Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module), Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module), + Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(module), } } } + pub fn getRef(base: *Value) *Value { + base.ref(); + return base; + } + pub fn dump(base: *const Value) void { std.debug.warn("{}", @tagName(base.id)); } @@ -38,6 +44,7 @@ pub const Value = struct { Void, Bool, NoReturn, + Ptr, }; pub const Type = @import("type.zig").Type; @@ -122,4 +129,18 @@ pub const Value = struct { module.a().destroy(self); } }; + + pub const Ptr = struct { + base: Value, + + pub const Mut = enum { + CompTimeConst, + CompTimeVar, + RunTime, + }; + + pub fn destroy(self: *Ptr, module: *Module) void { + module.a().destroy(self); + } + }; }; diff --git a/std/event/loop.zig b/std/event/loop.zig index 07575cf2e8..ba75109a72 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -382,6 +382,21 @@ pub const Loop = struct { return async S.asyncFunc(self, &handle, args); } + /// Awaiting a yield lets the event loop run, starting any unstarted async operations. + /// Note that async operations automatically start when a function yields for any other reason, + /// for example, when async I/O is performed. This function is intended to be used only when + /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O + /// is performed. + pub async fn yield(self: *Loop) void { + suspend |p| { + var my_tick_node = Loop.NextTickNode{ + .next = undefined, + .data = p, + }; + loop.onNextTick(&my_tick_node); + } + } + fn workerRun(self: *Loop) void { start_over: while (true) { if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { -- cgit v1.2.3 From 317ed57cb1e238664a123b940f4a50697653a9bb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 Jul 2018 21:43:11 -0400 Subject: docs: clarify mem.Allocator.reallocFn --- std/mem.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/std/mem.zig b/std/mem.zig index 41c5d0c8a3..32c17fcb26 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -23,7 +23,10 @@ pub const Allocator = struct { /// * this function must return successfully. /// * alignment <= alignment of old_mem.ptr /// - /// The returned newly allocated memory is undefined. + /// When `reallocFn` returns, + /// `return_value[0..min(old_mem.len, new_byte_count)]` must be the same + /// as `old_mem` was when `reallocFn` is called. The bytes of + /// `return_value[old_mem.len..]` have undefined values. /// `alignment` is guaranteed to be >= 1 /// `alignment` is guaranteed to be a power of 2 reallocFn: fn (self: *Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, -- cgit v1.2.3 From a0c1498e65d34e4f7d003ef1a804a87fa6c83ed3 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Wed, 11 Jul 2018 12:52:30 -0500 Subject: Added `remove` to ArrayList --- std/array_list.zig | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/std/array_list.zig b/std/array_list.zig index 5deb3c5fb1..dd9647f2d5 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -101,6 +101,25 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { const new_item_ptr = try self.addOne(); new_item_ptr.* = item; } + + /// Removes the element at the specified index and returns it. + // The empty slot is filled from the end of the list. + pub fn remove(self: *Self, n: usize) T { + if(self.len - 1 == n) return self.pop(); + + var old_item = self.at(n); + self.set(n, self.pop()); + return old_item; + } + + pub fn removeOrError(self: *Self, n: usize) !T { + if(n >= self.len) return error.OutOfBounds; + if(self.len - 1 == n) return self.pop(); + + var old_item = self.at(n); + try self.setOrError(n, self.pop()); + return old_item; + } pub fn appendSlice(self: *Self, items: []align(A) const T) !void { try self.ensureCapacity(self.len + items.len); @@ -157,7 +176,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { it.count += 1; return val; } - + pub fn reset(it: *Iterator) void { it.count = 0; } @@ -232,6 +251,33 @@ test "basic ArrayList test" { assert(list.pop() == 33); } +test "remove ArrayList test" { + var list = ArrayList(i32).init(debug.global_allocator); + defer list.deinit(); + + try list.append(1); + try list.append(2); + try list.append(3); + try list.append(4); + try list.append(5); + try list.append(6); + try list.append(7); + + //remove from middle + assert(list.remove(3) == 4); + assert(list.at(3) == 7); + assert(list.len == 6); + + //remove from end + assert(list.remove(5) == 6); + assert(list.len == 5); + + //remove from front + assert(list.remove(0) == 1); + assert(list.at(0) == 5); + assert(list.len == 4); +} + test "iterator ArrayList test" { var list = ArrayList(i32).init(debug.global_allocator); defer list.deinit(); -- cgit v1.2.3 From b44332f5a663c8e96e546d6a2b6071bf96ce9bcb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 14 Jul 2018 10:01:45 -0400 Subject: std.ArrayList - rename remove to swapRemove --- std/array_list.zig | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/std/array_list.zig b/std/array_list.zig index dd9647f2d5..8d7bde46a1 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -41,8 +41,8 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return self.items[0..self.len]; } - pub fn at(self: Self, n: usize) T { - return self.toSliceConst()[n]; + pub fn at(self: Self, i: usize) T { + return self.toSliceConst()[i]; } /// Sets the value at index `i`, or returns `error.OutOfBounds` if @@ -101,21 +101,22 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { const new_item_ptr = try self.addOne(); new_item_ptr.* = item; } - + /// Removes the element at the specified index and returns it. - // The empty slot is filled from the end of the list. - pub fn remove(self: *Self, n: usize) T { - if(self.len - 1 == n) return self.pop(); - - var old_item = self.at(n); - self.set(n, self.pop()); + /// The empty slot is filled from the end of the list. + pub fn swapRemove(self: *Self, i: usize) T { + if (self.len - 1 == i) return self.pop(); + + const slice = self.toSlice(); + const old_item = slice[i]; + slice[i] = self.pop(); return old_item; } - + pub fn removeOrError(self: *Self, n: usize) !T { - if(n >= self.len) return error.OutOfBounds; - if(self.len - 1 == n) return self.pop(); - + if (n >= self.len) return error.OutOfBounds; + if (self.len - 1 == n) return self.pop(); + var old_item = self.at(n); try self.setOrError(n, self.pop()); return old_item; @@ -176,7 +177,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { it.count += 1; return val; } - + pub fn reset(it: *Iterator) void { it.count = 0; } @@ -251,7 +252,7 @@ test "basic ArrayList test" { assert(list.pop() == 33); } -test "remove ArrayList test" { +test "std.ArrayList.swapRemove" { var list = ArrayList(i32).init(debug.global_allocator); defer list.deinit(); @@ -262,18 +263,18 @@ test "remove ArrayList test" { try list.append(5); try list.append(6); try list.append(7); - + //remove from middle - assert(list.remove(3) == 4); + assert(list.swapRemove(3) == 4); assert(list.at(3) == 7); assert(list.len == 6); - + //remove from end - assert(list.remove(5) == 6); + assert(list.swapRemove(5) == 6); assert(list.len == 5); - + //remove from front - assert(list.remove(0) == 1); + assert(list.swapRemove(0) == 1); assert(list.at(0) == 5); assert(list.len == 4); } -- cgit v1.2.3 From 722b9b9e595027e76ab4255f13ad0eca539358ac Mon Sep 17 00:00:00 2001 From: Eduardo Sánchez Muñoz Date: Sat, 14 Jul 2018 01:12:23 +0200 Subject: codegen: Store returned value if type is 'handle_is_ptr' and function is not 'first_arg_ret'. Seems to fix #1230, includes test. --- src/codegen.cpp | 4 ++++ test/behavior.zig | 1 + test/cases/bugs/1230.zig | 11 +++++++++++ 3 files changed, 16 insertions(+) create mode 100644 test/cases/bugs/1230.zig diff --git a/src/codegen.cpp b/src/codegen.cpp index 3f54c120b4..0bcc211164 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3166,6 +3166,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr return nullptr; } else if (first_arg_ret) { return instruction->tmp_ptr; + } else if (handle_is_ptr(src_return_type)) { + auto store_instr = LLVMBuildStore(g->builder, result, instruction->tmp_ptr); + LLVMSetAlignment(store_instr, LLVMGetAlignment(instruction->tmp_ptr)); + return instruction->tmp_ptr; } else { return result; } diff --git a/test/behavior.zig b/test/behavior.zig index 450dded56c..21b1c597e1 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -9,6 +9,7 @@ comptime { _ = @import("cases/bitcast.zig"); _ = @import("cases/bool.zig"); _ = @import("cases/bugs/1111.zig"); + _ = @import("cases/bugs/1230.zig"); _ = @import("cases/bugs/394.zig"); _ = @import("cases/bugs/655.zig"); _ = @import("cases/bugs/656.zig"); diff --git a/test/cases/bugs/1230.zig b/test/cases/bugs/1230.zig new file mode 100644 index 0000000000..3fd22357d7 --- /dev/null +++ b/test/cases/bugs/1230.zig @@ -0,0 +1,11 @@ +const S = extern struct { + x: i32, +}; + +extern fn ret_struct() S { + return S { .x = 0 }; +} + +test "extern return small struct (bug 1230)" { + const s = ret_struct(); +} -- cgit v1.2.3 From f78d4ed30c1bd92fc5ecc26c2d5f11751b406255 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 14 Jul 2018 11:31:46 -0400 Subject: add an assertion to the test --- test/cases/bugs/1230.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/cases/bugs/1230.zig b/test/cases/bugs/1230.zig index 3fd22357d7..b782a77f0b 100644 --- a/test/cases/bugs/1230.zig +++ b/test/cases/bugs/1230.zig @@ -1,11 +1,14 @@ +const assert = @import("std").debug.assert; + const S = extern struct { x: i32, }; extern fn ret_struct() S { - return S { .x = 0 }; + return S{ .x = 42 }; } test "extern return small struct (bug 1230)" { const s = ret_struct(); + assert(s.x == 42); } -- cgit v1.2.3 From bf441ed2448fdf4c5f12282ca448a62c5b71d26e Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sun, 15 Jul 2018 00:07:47 +1200 Subject: Add --stdin option to zig fmt --- src-self-hosted/main.zig | 45 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 058459a2d8..972aaae9ac 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -527,6 +527,7 @@ const usage_fmt = \\Options: \\ --help Print this help and exit \\ --color [auto|off|on] Enable or disable colored error messages + \\ --stdin Format code from stdin \\ \\ ; @@ -538,6 +539,7 @@ const args_fmt_spec = []Flag{ "off", "on", }), + Flag.Bool("--stdin"), }; const Fmt = struct { @@ -579,11 +581,6 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { os.exit(0); } - if (flags.positionals.len == 0) { - try stderr.write("expected at least one source file argument\n"); - os.exit(1); - } - const color = blk: { if (flags.single("color")) |color_flag| { if (mem.eql(u8, color_flag, "auto")) { @@ -598,6 +595,44 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { } }; + if (flags.present("stdin")) { + if (flags.positionals.len != 0) { + try stderr.write("cannot use --stdin with positional arguments\n"); + os.exit(1); + } + + var stdin_file = try io.getStdIn(); + var stdin = io.FileInStream.init(&stdin_file); + + const source_code = try stdin.stream.readAllAlloc(allocator, @maxValue(usize)); + defer allocator.free(source_code); + + var tree = std.zig.parse(allocator, source_code) catch |err| { + try stderr.print("error parsing stdin: {}\n", err); + os.exit(1); + }; + defer tree.deinit(); + + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, ""); + defer allocator.destroy(msg); + + try errmsg.printToFile(&stderr_file, msg, color); + } + if (tree.errors.len != 0) { + os.exit(1); + } + + _ = try std.zig.render(allocator, stdout, &tree); + return; + } + + if (flags.positionals.len == 0) { + try stderr.write("expected at least one source file argument\n"); + os.exit(1); + } + var fmt = Fmt{ .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator), .queue = std.LinkedList([]const u8).init(), -- cgit v1.2.3 From 29c756abba20921b76ba0a2611dd56f0123fd68c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 14 Jul 2018 11:52:48 -0400 Subject: docs: correct some misinformation --- doc/langref.html.in | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index c90c847f92..ea672ccb17 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2239,7 +2239,7 @@ test "switch inside function" { // On an OS other than fuchsia, block is not even analyzed, // so this compile error is not triggered. // On fuchsia this compile error would be triggered. - @compileError("windows not supported"); + @compileError("fuchsia not supported"); }, else => {}, } @@ -2303,13 +2303,13 @@ test "while continue" { {#code_begin|test|while#} const assert = @import("std").debug.assert; -test "while loop continuation expression" { +test "while loop continue expression" { var i: usize = 0; while (i < 10) : (i += 1) {} assert(i == 10); } -test "while loop continuation expression, more complicated" { +test "while loop continue expression, more complicated" { var i1: usize = 1; var j1: usize = 1; while (i1 * j1 < 2000) : ({ i1 *= 2; j1 *= 3; }) { @@ -7118,10 +7118,16 @@ Environments: opencl

    The Zig Standard Library (@import("std")) has architecture, environment, and operating sytsem - abstractions, and thus takes additional work to support more platforms. It currently supports - Linux x86_64. Not all standard library code requires operating system abstractions, however, + abstractions, and thus takes additional work to support more platforms. + Not all standard library code requires operating system abstractions, however, so things such as generic data structures work an all above platforms.

    +

    The current list of targets supported by the Zig Standard Library is:

    +
      +
    • Linux x86_64
    • +
    • Windows x86_64
    • +
    • MacOS x86_64
    • +
    {#header_close#} {#header_open|Style Guide#}

    -- cgit v1.2.3 From 8be6c98ca63dac5cf5e769171bb72cd888efbbf4 Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Sat, 14 Jul 2018 18:03:06 +0200 Subject: Create unit test that tests aligned reallocation. --- std/heap.zig | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/std/heap.zig b/std/heap.zig index ef22c8d0c5..4f21ef5cd1 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -442,6 +442,7 @@ test "DirectAllocator" { const allocator = &direct_allocator.allocator; try testAllocator(allocator); + try testAllocatorAligned(allocator, 16); try testAllocatorLargeAlignment(allocator); } @@ -453,6 +454,7 @@ test "ArenaAllocator" { defer arena_allocator.deinit(); try testAllocator(&arena_allocator.allocator); + try testAllocatorAligned(&arena_allocator.allocator, 16); try testAllocatorLargeAlignment(&arena_allocator.allocator); } @@ -461,6 +463,7 @@ test "FixedBufferAllocator" { var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); try testAllocator(&fixed_buffer_allocator.allocator); + try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16); try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); } @@ -468,12 +471,13 @@ test "ThreadSafeFixedBufferAllocator" { var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); try testAllocator(&fixed_buffer_allocator.allocator); + try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16); try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); } fn testAllocator(allocator: *mem.Allocator) !void { var slice = try allocator.alloc(*i32, 100); - + assert(slice.len == 100); for (slice) |*item, i| { item.* = try allocator.create(@intCast(i32, i)); } @@ -483,13 +487,43 @@ fn testAllocator(allocator: *mem.Allocator) !void { } slice = try allocator.realloc(*i32, slice, 20000); + assert(slice.len == 20000); slice = try allocator.realloc(*i32, slice, 50); + assert(slice.len == 50); slice = try allocator.realloc(*i32, slice, 25); + assert(slice.len == 25); + slice = try allocator.realloc(*i32, slice, 0); + assert(slice.len == 0); slice = try allocator.realloc(*i32, slice, 10); + assert(slice.len == 10); allocator.free(slice); } +fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !void { + // initial + var slice = try allocator.alignedAlloc(u8, alignment, 10); + assert(slice.len == 10); + // grow + slice = try allocator.alignedRealloc(u8, alignment, slice, 100); + assert(slice.len == 100); + // shrink + slice = try allocator.alignedRealloc(u8, alignment, slice, 10); + assert(slice.len == 10); + // go to zero + slice = try allocator.alignedRealloc(u8, alignment, slice, 0); + assert(slice.len == 0); + // realloc from zero + slice = try allocator.alignedRealloc(u8, alignment, slice, 100); + assert(slice.len == 100); + // shrink with shrink + slice = allocator.alignedShrink(u8, alignment, slice, 10); + assert(slice.len == 10); + // shrink to zero + slice = allocator.alignedShrink(u8, alignment, slice, 0); + assert(slice.len == 0); +} + fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void { //Maybe a platform's page_size is actually the same as or // very near usize? -- cgit v1.2.3 From c021a445672fde506dbbcc331ca3b846f9b6e2a1 Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Sat, 14 Jul 2018 18:05:05 +0200 Subject: Fix aligned reallocation from zero size. --- std/mem.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/mem.zig b/std/mem.zig index 32c17fcb26..2a5b0366a9 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -74,7 +74,7 @@ pub const Allocator = struct { pub fn alignedRealloc(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { if (old_mem.len == 0) { - return self.alloc(T, n); + return self.alignedAlloc(T, alignment, n); } if (n == 0) { self.free(old_mem); -- cgit v1.2.3 From 278829fc2cc23e55b09915ce07ce1ec2dbf7e68b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 14 Jul 2018 15:45:15 -0400 Subject: self-hosted: adding a fn to an llvm module --- src-self-hosted/codegen.zig | 61 ++++++++++++++++++ src-self-hosted/ir.zig | 9 +-- src-self-hosted/llvm.zig | 23 ++++++- src-self-hosted/main.zig | 7 ++- src-self-hosted/module.zig | 112 ++++++++++++++++++++++----------- src-self-hosted/test.zig | 11 +++- src-self-hosted/type.zig | 147 +++++++++++++++++++++++++++++++++++++++++++- src-self-hosted/value.zig | 20 ++++-- std/atomic/int.zig | 18 ++++-- std/event/loop.zig | 1 - 10 files changed, 346 insertions(+), 63 deletions(-) create mode 100644 src-self-hosted/codegen.zig diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig new file mode 100644 index 0000000000..df8f451856 --- /dev/null +++ b/src-self-hosted/codegen.zig @@ -0,0 +1,61 @@ +const std = @import("std"); +// TODO codegen pretends that Module is renamed to Build because I plan to +// do that refactor at some point +const Build = @import("module.zig").Module; +// we go through llvm instead of c for 2 reasons: +// 1. to avoid accidentally calling the non-thread-safe functions +// 2. patch up some of the types to remove nullability +const llvm = @import("llvm.zig"); +const ir = @import("ir.zig"); +const Value = @import("value.zig").Value; +const Type = @import("type.zig").Type; +const event = std.event; + +pub async fn renderToLlvm(build: *Build, fn_val: *Value.Fn, code: *ir.Code) !void { + fn_val.base.ref(); + defer fn_val.base.deref(build); + defer code.destroy(build.a()); + + const llvm_handle = try build.event_loop_local.getAnyLlvmContext(); + defer llvm_handle.release(build.event_loop_local); + + const context = llvm_handle.node.data; + + const module = llvm.ModuleCreateWithNameInContext(build.name.ptr(), context) orelse return error.OutOfMemory; + defer llvm.DisposeModule(module); + + const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory; + defer llvm.DisposeBuilder(builder); + + var cunit = CompilationUnit{ + .build = build, + .module = module, + .builder = builder, + .context = context, + .lock = event.Lock.init(build.loop), + }; + + try renderToLlvmModule(&cunit, fn_val, code); + + if (build.verbose_llvm_ir) { + llvm.DumpModule(cunit.module); + } +} + +pub const CompilationUnit = struct { + build: *Build, + module: llvm.ModuleRef, + builder: llvm.BuilderRef, + context: llvm.ContextRef, + lock: event.Lock, + + fn a(self: *CompilationUnit) *std.mem.Allocator { + return self.build.a(); + } +}; + +pub fn renderToLlvmModule(cunit: *CompilationUnit, fn_val: *Value.Fn, code: *ir.Code) !void { + // TODO audit more of codegen.cpp:fn_llvm_value and port more logic + const llvm_fn_type = try fn_val.base.typeof.getLlvmType(cunit); + const llvm_fn = llvm.AddFunction(cunit.module, fn_val.symbol_name.ptr(), llvm_fn_type); +} diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 22161a0c27..f1c395a790 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -375,15 +375,8 @@ pub const Instruction = struct { pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Instruction { const target = try self.params.target.getAsParam(); - try ira.src_implicit_return_type_list.append(target); - - return ira.irb.build( - AddImplicitReturnType, - self.base.scope, - self.base.span, - Params{ .target = target }, - ); + return ira.irb.buildConstVoid(self.base.scope, self.base.span, true); } }; }; diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index 391a92cd63..b815f75b05 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -2,10 +2,27 @@ const builtin = @import("builtin"); const c = @import("c.zig"); const assert = @import("std").debug.assert; -pub const ValueRef = removeNullability(c.LLVMValueRef); -pub const ModuleRef = removeNullability(c.LLVMModuleRef); -pub const ContextRef = removeNullability(c.LLVMContextRef); pub const BuilderRef = removeNullability(c.LLVMBuilderRef); +pub const ContextRef = removeNullability(c.LLVMContextRef); +pub const ModuleRef = removeNullability(c.LLVMModuleRef); +pub const ValueRef = removeNullability(c.LLVMValueRef); +pub const TypeRef = removeNullability(c.LLVMTypeRef); + +pub const AddFunction = c.LLVMAddFunction; +pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext; +pub const DisposeBuilder = c.LLVMDisposeBuilder; +pub const DisposeModule = c.LLVMDisposeModule; +pub const DumpModule = c.LLVMDumpModule; +pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; +pub const VoidTypeInContext = c.LLVMVoidTypeInContext; + +pub const FunctionType = LLVMFunctionType; +extern fn LLVMFunctionType( + ReturnType: TypeRef, + ParamTypes: [*]TypeRef, + ParamCount: c_uint, + IsVarArg: c_int, +) ?TypeRef; fn removeNullability(comptime T: type) type { comptime assert(@typeId(T) == builtin.TypeId.Optional); diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 972aaae9ac..77ec7f6d32 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -14,6 +14,7 @@ const c = @import("c.zig"); const introspect = @import("introspect.zig"); const Args = arg.Args; const Flag = arg.Flag; +const EventLoopLocal = @import("module.zig").EventLoopLocal; const Module = @import("module.zig").Module; const Target = @import("target.zig").Target; const errmsg = @import("errmsg.zig"); @@ -386,9 +387,13 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo var loop: event.Loop = undefined; try loop.initMultiThreaded(allocator); + defer loop.deinit(); + + var event_loop_local = EventLoopLocal.init(&loop); + defer event_loop_local.deinit(); var module = try Module.create( - &loop, + &event_loop_local, root_name, root_source_file, Target.Native, diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 3fbe6d54ad..617bd0d44a 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -25,14 +25,58 @@ const ParsedFile = @import("parsed_file.zig").ParsedFile; const Value = @import("value.zig").Value; const Type = Value.Type; const Span = errmsg.Span; +const codegen = @import("codegen.zig"); + +/// Data that is local to the event loop. +pub const EventLoopLocal = struct { + loop: *event.Loop, + llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), + + fn init(loop: *event.Loop) EventLoopLocal { + return EventLoopLocal{ + .loop = loop, + .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), + }; + } + + fn deinit(self: *EventLoopLocal) void { + while (self.llvm_handle_pool.pop()) |node| { + c.LLVMContextDispose(node.data); + self.loop.allocator.destroy(node); + } + } + + /// Gets an exclusive handle on any LlvmContext. + /// Caller must release the handle when done. + pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle { + if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node }; + + const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory; + errdefer c.LLVMContextDispose(context_ref); + + const node = try self.loop.allocator.create(std.atomic.Stack(llvm.ContextRef).Node{ + .next = undefined, + .data = context_ref, + }); + errdefer self.loop.allocator.destroy(node); + + return LlvmHandle{ .node = node }; + } +}; + +pub const LlvmHandle = struct { + node: *std.atomic.Stack(llvm.ContextRef).Node, + + pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void { + event_loop_local.llvm_handle_pool.push(self.node); + } +}; pub const Module = struct { + event_loop_local: *EventLoopLocal, loop: *event.Loop, name: Buffer, root_src_path: ?[]const u8, - llvm_module: llvm.ModuleRef, - context: llvm.ContextRef, - builder: llvm.BuilderRef, target: Target, build_mode: builtin.Mode, zig_lib_dir: []const u8, @@ -187,7 +231,7 @@ pub const Module = struct { }; pub fn create( - loop: *event.Loop, + event_loop_local: *EventLoopLocal, name: []const u8, root_src_path: ?[]const u8, target: *const Target, @@ -196,29 +240,20 @@ pub const Module = struct { zig_lib_dir: []const u8, cache_dir: []const u8, ) !*Module { + const loop = event_loop_local.loop; + var name_buffer = try Buffer.init(loop.allocator, name); errdefer name_buffer.deinit(); - const context = c.LLVMContextCreate() orelse return error.OutOfMemory; - errdefer c.LLVMContextDispose(context); - - const llvm_module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory; - errdefer c.LLVMDisposeModule(llvm_module); - - const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory; - errdefer c.LLVMDisposeBuilder(builder); - const events = try event.Channel(Event).create(loop, 0); errdefer events.destroy(); const module = try loop.allocator.create(Module{ .loop = loop, + .event_loop_local = event_loop_local, .events = events, .name = name_buffer, .root_src_path = root_src_path, - .llvm_module = llvm_module, - .context = context, - .builder = builder, .target = target.*, .kind = kind, .build_mode = build_mode, @@ -290,7 +325,7 @@ pub const Module = struct { .base = Value{ .id = Value.Id.Type, .typeof = undefined, - .ref_count = 3, // 3 because it references itself twice + .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice }, .id = builtin.TypeId.Type, }, @@ -305,7 +340,7 @@ pub const Module = struct { .base = Value{ .id = Value.Id.Type, .typeof = &Type.MetaType.get(module).base, - .ref_count = 1, + .ref_count = std.atomic.Int(usize).init(1), }, .id = builtin.TypeId.Void, }, @@ -317,7 +352,7 @@ pub const Module = struct { .base = Value{ .id = Value.Id.Type, .typeof = &Type.MetaType.get(module).base, - .ref_count = 1, + .ref_count = std.atomic.Int(usize).init(1), }, .id = builtin.TypeId.NoReturn, }, @@ -329,7 +364,7 @@ pub const Module = struct { .base = Value{ .id = Value.Id.Type, .typeof = &Type.MetaType.get(module).base, - .ref_count = 1, + .ref_count = std.atomic.Int(usize).init(1), }, .id = builtin.TypeId.Bool, }, @@ -340,7 +375,7 @@ pub const Module = struct { .base = Value{ .id = Value.Id.Void, .typeof = &Type.Void.get(module).base, - .ref_count = 1, + .ref_count = std.atomic.Int(usize).init(1), }, }); errdefer module.a().destroy(module.void_value); @@ -349,7 +384,7 @@ pub const Module = struct { .base = Value{ .id = Value.Id.Bool, .typeof = &Type.Bool.get(module).base, - .ref_count = 1, + .ref_count = std.atomic.Int(usize).init(1), }, .x = true, }); @@ -359,7 +394,7 @@ pub const Module = struct { .base = Value{ .id = Value.Id.Bool, .typeof = &Type.Bool.get(module).base, - .ref_count = 1, + .ref_count = std.atomic.Int(usize).init(1), }, .x = false, }); @@ -369,16 +404,12 @@ pub const Module = struct { .base = Value{ .id = Value.Id.NoReturn, .typeof = &Type.NoReturn.get(module).base, - .ref_count = 1, + .ref_count = std.atomic.Int(usize).init(1), }, }); errdefer module.a().destroy(module.noreturn_value); } - fn dump(self: *Module) void { - c.LLVMDumpModule(self.module); - } - pub fn destroy(self: *Module) void { self.noreturn_value.base.deref(self); self.void_value.base.deref(self); @@ -389,9 +420,6 @@ pub const Module = struct { self.meta_type.base.base.deref(self); self.events.destroy(); - c.LLVMDisposeBuilder(self.builder); - c.LLVMDisposeModule(self.llvm_module); - c.LLVMContextDispose(self.context); self.name.deinit(); self.a().destroy(self); @@ -657,10 +685,19 @@ async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void { const fndef_scope = try Scope.FnDef.create(module, fn_decl.base.parent_scope); defer fndef_scope.base.deref(module); - const fn_type = try Type.Fn.create(module); + // TODO actually look at the return type of the AST + const return_type = &Type.Void.get(module).base; + defer return_type.base.deref(module); + + const is_var_args = false; + const params = ([*]Type.Fn.Param)(undefined)[0..0]; + const fn_type = try Type.Fn.create(module, return_type, params, is_var_args); defer fn_type.base.base.deref(module); - const fn_val = try Value.Fn.create(module, fn_type, fndef_scope); + var symbol_name = try std.Buffer.init(module.a(), fn_decl.base.name); + errdefer symbol_name.deinit(); + + const fn_val = try Value.Fn.create(module, fn_type, fndef_scope, symbol_name); defer fn_val.base.deref(module); fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; @@ -674,6 +711,7 @@ async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void { ) catch unreachable)) catch |err| switch (err) { // This poison value should not cause the errdefers to run. It simply means // that self.compile_errors is populated. + // TODO https://github.com/ziglang/zig/issues/769 error.SemanticAnalysisFailed => return {}, else => return err, }; @@ -692,14 +730,18 @@ async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void { ) catch unreachable)) catch |err| switch (err) { // This poison value should not cause the errdefers to run. It simply means // that self.compile_errors is populated. + // TODO https://github.com/ziglang/zig/issues/769 error.SemanticAnalysisFailed => return {}, else => return err, }; - defer analyzed_code.destroy(module.a()); + errdefer analyzed_code.destroy(module.a()); if (module.verbose_ir) { std.debug.warn("analyzed:\n"); analyzed_code.dump(); } - // TODO now render to LLVM module + + // Kick off rendering to LLVM module, but it doesn't block the fn decl + // analysis from being complete. + try module.build_group.call(codegen.renderToLlvm, module, fn_val, analyzed_code); } diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 4455352f95..e609eb2791 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -6,6 +6,7 @@ const Module = @import("module.zig").Module; const introspect = @import("introspect.zig"); const assertOrPanic = std.debug.assertOrPanic; const errmsg = @import("errmsg.zig"); +const EventLoopLocal = @import("module.zig").EventLoopLocal; test "compile errors" { var ctx: TestContext = undefined; @@ -22,6 +23,7 @@ const allocator = std.heap.c_allocator; pub const TestContext = struct { loop: std.event.Loop, + event_loop_local: EventLoopLocal, zig_lib_dir: []u8, zig_cache_dir: []u8, file_index: std.atomic.Int(usize), @@ -34,6 +36,7 @@ pub const TestContext = struct { self.* = TestContext{ .any_err = {}, .loop = undefined, + .event_loop_local = undefined, .zig_lib_dir = undefined, .zig_cache_dir = undefined, .group = undefined, @@ -43,6 +46,9 @@ pub const TestContext = struct { try self.loop.initMultiThreaded(allocator); errdefer self.loop.deinit(); + self.event_loop_local = EventLoopLocal.init(&self.loop); + errdefer self.event_loop_local.deinit(); + self.group = std.event.Group(error!void).init(&self.loop); errdefer self.group.cancelAll(); @@ -60,6 +66,7 @@ pub const TestContext = struct { std.os.deleteTree(allocator, tmp_dir_name) catch {}; allocator.free(self.zig_cache_dir); allocator.free(self.zig_lib_dir); + self.event_loop_local.deinit(); self.loop.deinit(); } @@ -83,7 +90,7 @@ pub const TestContext = struct { msg: []const u8, ) !void { var file_index_buf: [20]u8 = undefined; - const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.next()); + const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr()); const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1); if (std.os.path.dirname(file1_path)) |dirname| { @@ -94,7 +101,7 @@ pub const TestContext = struct { try std.io.writeFile(allocator, file1_path, source); var module = try Module.create( - &self.loop, + &self.event_loop_local, "test", file1_path, Target.Native, diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 66e1470cc0..e4c31018a3 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -1,7 +1,10 @@ +const std = @import("std"); const builtin = @import("builtin"); const Scope = @import("scope.zig").Scope; const Module = @import("module.zig").Module; const Value = @import("value.zig").Value; +const llvm = @import("llvm.zig"); +const CompilationUnit = @import("codegen.zig").CompilationUnit; pub const Type = struct { base: Value, @@ -39,6 +42,36 @@ pub const Type = struct { } } + pub fn getLlvmType(base: *Type, cunit: *CompilationUnit) (error{OutOfMemory}!llvm.TypeRef) { + switch (base.id) { + Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(cunit), + Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(cunit), + Id.Type => unreachable, + Id.Void => unreachable, + Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(cunit), + Id.NoReturn => unreachable, + Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(cunit), + Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(cunit), + Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(cunit), + Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(cunit), + Id.ComptimeFloat => unreachable, + Id.ComptimeInt => unreachable, + Id.Undefined => unreachable, + Id.Null => unreachable, + Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(cunit), + Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(cunit), + Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(cunit), + Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(cunit), + Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(cunit), + Id.Namespace => unreachable, + Id.Block => unreachable, + Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(cunit), + Id.ArgTuple => unreachable, + Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(cunit), + Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(cunit), + } + } + pub fn dump(base: *const Type) void { std.debug.warn("{}", @tagName(base.id)); } @@ -54,27 +87,72 @@ pub const Type = struct { pub fn destroy(self: *Struct, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *Struct, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; pub const Fn = struct { base: Type, + return_type: *Type, + params: []Param, + is_var_args: bool, - pub fn create(module: *Module) !*Fn { - return module.a().create(Fn{ + pub const Param = struct { + is_noalias: bool, + typeof: *Type, + }; + + pub fn create(module: *Module, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { + const result = try module.a().create(Fn{ .base = Type{ .base = Value{ .id = Value.Id.Type, .typeof = &MetaType.get(module).base, - .ref_count = 1, + .ref_count = std.atomic.Int(usize).init(1), }, .id = builtin.TypeId.Fn, }, + .return_type = return_type, + .params = params, + .is_var_args = is_var_args, }); + errdefer module.a().destroy(result); + + result.return_type.base.ref(); + for (result.params) |param| { + param.typeof.base.ref(); + } + return result; } pub fn destroy(self: *Fn, module: *Module) void { + self.return_type.base.deref(module); + for (self.params) |param| { + param.typeof.base.deref(module); + } module.a().destroy(self); } + + pub fn getLlvmType(self: *Fn, cunit: *CompilationUnit) !llvm.TypeRef { + const llvm_return_type = switch (self.return_type.id) { + Type.Id.Void => llvm.VoidTypeInContext(cunit.context) orelse return error.OutOfMemory, + else => try self.return_type.getLlvmType(cunit), + }; + const llvm_param_types = try cunit.a().alloc(llvm.TypeRef, self.params.len); + defer cunit.a().free(llvm_param_types); + for (llvm_param_types) |*llvm_param_type, i| { + llvm_param_type.* = try self.params[i].typeof.getLlvmType(cunit); + } + + return llvm.FunctionType( + llvm_return_type, + llvm_param_types.ptr, + @intCast(c_uint, llvm_param_types.len), + @boolToInt(self.is_var_args), + ) orelse error.OutOfMemory; + } }; pub const MetaType = struct { @@ -118,6 +196,10 @@ pub const Type = struct { pub fn destroy(self: *Bool, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *Bool, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; pub const NoReturn = struct { @@ -140,6 +222,10 @@ pub const Type = struct { pub fn destroy(self: *Int, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *Int, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; pub const Float = struct { @@ -148,6 +234,10 @@ pub const Type = struct { pub fn destroy(self: *Float, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *Float, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; pub const Pointer = struct { base: Type, @@ -180,14 +270,24 @@ pub const Type = struct { ) *Pointer { @panic("TODO get pointer"); } + + pub fn getLlvmType(self: *Pointer, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; + pub const Array = struct { base: Type, pub fn destroy(self: *Array, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *Array, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; + pub const ComptimeFloat = struct { base: Type, @@ -195,6 +295,7 @@ pub const Type = struct { module.a().destroy(self); } }; + pub const ComptimeInt = struct { base: Type, @@ -202,6 +303,7 @@ pub const Type = struct { module.a().destroy(self); } }; + pub const Undefined = struct { base: Type, @@ -209,6 +311,7 @@ pub const Type = struct { module.a().destroy(self); } }; + pub const Null = struct { base: Type, @@ -216,41 +319,67 @@ pub const Type = struct { module.a().destroy(self); } }; + pub const Optional = struct { base: Type, pub fn destroy(self: *Optional, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *Optional, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; + pub const ErrorUnion = struct { base: Type, pub fn destroy(self: *ErrorUnion, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *ErrorUnion, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; + pub const ErrorSet = struct { base: Type, pub fn destroy(self: *ErrorSet, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *ErrorSet, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; + pub const Enum = struct { base: Type, pub fn destroy(self: *Enum, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *Enum, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; + pub const Union = struct { base: Type, pub fn destroy(self: *Union, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *Union, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; + pub const Namespace = struct { base: Type, @@ -273,6 +402,10 @@ pub const Type = struct { pub fn destroy(self: *BoundFn, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *BoundFn, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; pub const ArgTuple = struct { @@ -289,6 +422,10 @@ pub const Type = struct { pub fn destroy(self: *Opaque, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *Opaque, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; pub const Promise = struct { @@ -297,5 +434,9 @@ pub const Type = struct { pub fn destroy(self: *Promise, module: *Module) void { module.a().destroy(self); } + + pub fn getLlvmType(self: *Promise, cunit: *CompilationUnit) llvm.TypeRef { + @panic("TODO"); + } }; }; diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 7ee594b41c..779e5c2e45 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -8,15 +8,16 @@ const Module = @import("module.zig").Module; pub const Value = struct { id: Id, typeof: *Type, - ref_count: usize, + ref_count: std.atomic.Int(usize), + /// Thread-safe pub fn ref(base: *Value) void { - base.ref_count += 1; + _ = base.ref_count.incr(); } + /// Thread-safe pub fn deref(base: *Value, module: *Module) void { - base.ref_count -= 1; - if (base.ref_count == 0) { + if (base.ref_count.decr() == 1) { base.typeof.base.deref(module); switch (base.id) { Id.Type => @fieldParentPtr(Type, "base", base).destroy(module), @@ -52,6 +53,10 @@ pub const Value = struct { pub const Fn = struct { base: Value, + /// The main external name that is used in the .o file. + /// TODO https://github.com/ziglang/zig/issues/265 + symbol_name: std.Buffer, + /// parent should be the top level decls or container decls fndef_scope: *Scope.FnDef, @@ -62,16 +67,18 @@ pub const Value = struct { block_scope: *Scope.Block, /// Creates a Fn value with 1 ref - pub fn create(module: *Module, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef) !*Fn { + /// Takes ownership of symbol_name + pub fn create(module: *Module, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn { const self = try module.a().create(Fn{ .base = Value{ .id = Value.Id.Fn, .typeof = &fn_type.base, - .ref_count = 1, + .ref_count = std.atomic.Int(usize).init(1), }, .fndef_scope = fndef_scope, .child_scope = &fndef_scope.base, .block_scope = undefined, + .symbol_name = symbol_name, }); fn_type.base.base.ref(); fndef_scope.fn_val = self; @@ -81,6 +88,7 @@ pub const Value = struct { pub fn destroy(self: *Fn, module: *Module) void { self.fndef_scope.base.deref(module); + self.symbol_name.deinit(); module.a().destroy(self); } }; diff --git a/std/atomic/int.zig b/std/atomic/int.zig index 7042bca78d..d51454c673 100644 --- a/std/atomic/int.zig +++ b/std/atomic/int.zig @@ -4,16 +4,26 @@ const AtomicOrder = builtin.AtomicOrder; /// Thread-safe, lock-free integer pub fn Int(comptime T: type) type { return struct { - value: T, + unprotected_value: T, pub const Self = this; pub fn init(init_val: T) Self { - return Self{ .value = init_val }; + return Self{ .unprotected_value = init_val }; } - pub fn next(self: *Self) T { - return @atomicRmw(T, &self.value, builtin.AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + /// Returns previous value + pub fn incr(self: *Self) T { + return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + } + + /// Returns previous value + pub fn decr(self: *Self) T { + return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + + pub fn get(self: *Self) T { + return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst); } }; } diff --git a/std/event/loop.zig b/std/event/loop.zig index ba75109a72..fc927592b9 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -101,7 +101,6 @@ pub const Loop = struct { errdefer self.deinitOsData(); } - /// must call stop before deinit pub fn deinit(self: *Loop) void { self.deinitOsData(); self.allocator.free(self.extra_threads); -- cgit v1.2.3 From 69e50ad2f54bc446b2258f464f9b09e78e132d45 Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Sat, 14 Jul 2018 22:31:11 +0200 Subject: Improve realloc on fixed buffer allocator (#1238) * Add test to check re-use of memory * Check if realloc has to reallocate the last allocated memory block. If so extend that block instead of allocating a new one. * Also check if the realloc actually preserves the data. --- std/heap.zig | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/std/heap.zig b/std/heap.zig index 4f21ef5cd1..f5e0484b25 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -302,8 +302,17 @@ pub const FixedBufferAllocator = struct { } fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); + assert(old_mem.len <= self.end_index); if (new_size <= old_mem.len) { return old_mem[0..new_size]; + } else if (old_mem.ptr == self.buffer.ptr + self.end_index - old_mem.len) { + const start_index = self.end_index - old_mem.len; + const new_end_index = start_index + new_size; + if (new_end_index > self.buffer.len) return error.OutOfMemory; + const result = self.buffer[start_index..new_end_index]; + self.end_index = new_end_index; + return result; } else { const result = try alloc(allocator, new_size, alignment); mem.copy(u8, result, old_mem); @@ -467,6 +476,35 @@ test "FixedBufferAllocator" { try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); } +test "FixedBufferAllocator Reuse memory on realloc" { + var small_fixed_buffer: [10]u8 = undefined; + // check if we re-use the memory + { + var fixed_buffer_allocator = FixedBufferAllocator.init(small_fixed_buffer[0..]); + + var slice0 = try fixed_buffer_allocator.allocator.alloc(u8, 5); + assert(slice0.len == 5); + var slice1 = try fixed_buffer_allocator.allocator.realloc(u8, slice0, 10); + assert(slice1.ptr == slice0.ptr); + assert(slice1.len == 10); + debug.assertError(fixed_buffer_allocator.allocator.realloc(u8, slice1, 11), error.OutOfMemory); + } + // check that we don't re-use the memory if it's not the most recent block + { + var fixed_buffer_allocator = FixedBufferAllocator.init(small_fixed_buffer[0..]); + + var slice0 = try fixed_buffer_allocator.allocator.alloc(u8, 2); + slice0[0] = 1; + slice0[1] = 2; + var slice1 = try fixed_buffer_allocator.allocator.alloc(u8, 2); + var slice2 = try fixed_buffer_allocator.allocator.realloc(u8, slice0, 4); + assert(slice0.ptr != slice2.ptr); + assert(slice1.ptr != slice2.ptr); + assert(slice2[0] == 1); + assert(slice2[1] == 2); + } +} + test "ThreadSafeFixedBufferAllocator" { var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); @@ -482,12 +520,14 @@ fn testAllocator(allocator: *mem.Allocator) !void { item.* = try allocator.create(@intCast(i32, i)); } - for (slice) |item, i| { + slice = try allocator.realloc(*i32, slice, 20000); + assert(slice.len == 20000); + + for (slice[0..100]) |item, i| { + assert(item.* == @intCast(i32, i)); allocator.destroy(item); } - slice = try allocator.realloc(*i32, slice, 20000); - assert(slice.len == 20000); slice = try allocator.realloc(*i32, slice, 50); assert(slice.len == 50); slice = try allocator.realloc(*i32, slice, 25); -- cgit v1.2.3 From 28c3d4809bc6d497ac81892bc7eb03b95d8c2b32 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 14 Jul 2018 16:12:41 -0400 Subject: rename Module to Compilation and CompilationUnit to ObjectFile --- src-self-hosted/codegen.zig | 42 ++- src-self-hosted/compilation.zig | 747 ++++++++++++++++++++++++++++++++++++++++ src-self-hosted/decl.zig | 4 +- src-self-hosted/ir.zig | 56 +-- src-self-hosted/main.zig | 114 +++--- src-self-hosted/module.zig | 747 ---------------------------------------- src-self-hosted/scope.zig | 72 ++-- src-self-hosted/test.zig | 26 +- src-self-hosted/type.zig | 268 +++++++------- src-self-hosted/value.zig | 66 ++-- 10 files changed, 1070 insertions(+), 1072 deletions(-) create mode 100644 src-self-hosted/compilation.zig delete mode 100644 src-self-hosted/module.zig diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index df8f451856..a07485e74e 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -1,7 +1,5 @@ const std = @import("std"); -// TODO codegen pretends that Module is renamed to Build because I plan to -// do that refactor at some point -const Build = @import("module.zig").Module; +const Compilation = @import("compilation.zig").Compilation; // we go through llvm instead of c for 2 reasons: // 1. to avoid accidentally calling the non-thread-safe functions // 2. patch up some of the types to remove nullability @@ -11,51 +9,51 @@ const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; const event = std.event; -pub async fn renderToLlvm(build: *Build, fn_val: *Value.Fn, code: *ir.Code) !void { +pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void { fn_val.base.ref(); - defer fn_val.base.deref(build); - defer code.destroy(build.a()); + defer fn_val.base.deref(comp); + defer code.destroy(comp.a()); - const llvm_handle = try build.event_loop_local.getAnyLlvmContext(); - defer llvm_handle.release(build.event_loop_local); + const llvm_handle = try comp.event_loop_local.getAnyLlvmContext(); + defer llvm_handle.release(comp.event_loop_local); const context = llvm_handle.node.data; - const module = llvm.ModuleCreateWithNameInContext(build.name.ptr(), context) orelse return error.OutOfMemory; + const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory; defer llvm.DisposeModule(module); const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory; defer llvm.DisposeBuilder(builder); - var cunit = CompilationUnit{ - .build = build, + var ofile = ObjectFile{ + .comp = comp, .module = module, .builder = builder, .context = context, - .lock = event.Lock.init(build.loop), + .lock = event.Lock.init(comp.loop), }; - try renderToLlvmModule(&cunit, fn_val, code); + try renderToLlvmModule(&ofile, fn_val, code); - if (build.verbose_llvm_ir) { - llvm.DumpModule(cunit.module); + if (comp.verbose_llvm_ir) { + llvm.DumpModule(ofile.module); } } -pub const CompilationUnit = struct { - build: *Build, +pub const ObjectFile = struct { + comp: *Compilation, module: llvm.ModuleRef, builder: llvm.BuilderRef, context: llvm.ContextRef, lock: event.Lock, - fn a(self: *CompilationUnit) *std.mem.Allocator { - return self.build.a(); + fn a(self: *ObjectFile) *std.mem.Allocator { + return self.comp.a(); } }; -pub fn renderToLlvmModule(cunit: *CompilationUnit, fn_val: *Value.Fn, code: *ir.Code) !void { +pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void { // TODO audit more of codegen.cpp:fn_llvm_value and port more logic - const llvm_fn_type = try fn_val.base.typeof.getLlvmType(cunit); - const llvm_fn = llvm.AddFunction(cunit.module, fn_val.symbol_name.ptr(), llvm_fn_type); + const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile); + const llvm_fn = llvm.AddFunction(ofile.module, fn_val.symbol_name.ptr(), llvm_fn_type); } diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig new file mode 100644 index 0000000000..cbda7861bc --- /dev/null +++ b/src-self-hosted/compilation.zig @@ -0,0 +1,747 @@ +const std = @import("std"); +const os = std.os; +const io = std.io; +const mem = std.mem; +const Allocator = mem.Allocator; +const Buffer = std.Buffer; +const llvm = @import("llvm.zig"); +const c = @import("c.zig"); +const builtin = @import("builtin"); +const Target = @import("target.zig").Target; +const warn = std.debug.warn; +const Token = std.zig.Token; +const ArrayList = std.ArrayList; +const errmsg = @import("errmsg.zig"); +const ast = std.zig.ast; +const event = std.event; +const assert = std.debug.assert; +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; +const Scope = @import("scope.zig").Scope; +const Decl = @import("decl.zig").Decl; +const ir = @import("ir.zig"); +const Visib = @import("visib.zig").Visib; +const ParsedFile = @import("parsed_file.zig").ParsedFile; +const Value = @import("value.zig").Value; +const Type = Value.Type; +const Span = errmsg.Span; +const codegen = @import("codegen.zig"); + +/// Data that is local to the event loop. +pub const EventLoopLocal = struct { + loop: *event.Loop, + llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), + + fn init(loop: *event.Loop) EventLoopLocal { + return EventLoopLocal{ + .loop = loop, + .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), + }; + } + + fn deinit(self: *EventLoopLocal) void { + while (self.llvm_handle_pool.pop()) |node| { + c.LLVMContextDispose(node.data); + self.loop.allocator.destroy(node); + } + } + + /// Gets an exclusive handle on any LlvmContext. + /// Caller must release the handle when done. + pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle { + if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node }; + + const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory; + errdefer c.LLVMContextDispose(context_ref); + + const node = try self.loop.allocator.create(std.atomic.Stack(llvm.ContextRef).Node{ + .next = undefined, + .data = context_ref, + }); + errdefer self.loop.allocator.destroy(node); + + return LlvmHandle{ .node = node }; + } +}; + +pub const LlvmHandle = struct { + node: *std.atomic.Stack(llvm.ContextRef).Node, + + pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void { + event_loop_local.llvm_handle_pool.push(self.node); + } +}; + +pub const Compilation = struct { + event_loop_local: *EventLoopLocal, + loop: *event.Loop, + name: Buffer, + root_src_path: ?[]const u8, + target: Target, + build_mode: builtin.Mode, + zig_lib_dir: []const u8, + + version_major: u32, + version_minor: u32, + version_patch: u32, + + linker_script: ?[]const u8, + cache_dir: []const u8, + libc_lib_dir: ?[]const u8, + libc_static_lib_dir: ?[]const u8, + libc_include_dir: ?[]const u8, + msvc_lib_dir: ?[]const u8, + kernel32_lib_dir: ?[]const u8, + dynamic_linker: ?[]const u8, + out_h_path: ?[]const u8, + + is_test: bool, + each_lib_rpath: bool, + strip: bool, + is_static: bool, + linker_rdynamic: bool, + + clang_argv: []const []const u8, + llvm_argv: []const []const u8, + lib_dirs: []const []const u8, + rpath_list: []const []const u8, + assembly_files: []const []const u8, + link_objects: []const []const u8, + + windows_subsystem_windows: bool, + windows_subsystem_console: bool, + + link_libs_list: ArrayList(*LinkLib), + libc_link_lib: ?*LinkLib, + + err_color: errmsg.Color, + + verbose_tokenize: bool, + verbose_ast_tree: bool, + verbose_ast_fmt: bool, + verbose_cimport: bool, + verbose_ir: bool, + verbose_llvm_ir: bool, + verbose_link: bool, + + darwin_frameworks: []const []const u8, + darwin_version_min: DarwinVersionMin, + + test_filters: []const []const u8, + test_name_prefix: ?[]const u8, + + emit_file_type: Emit, + + kind: Kind, + + link_out_file: ?[]const u8, + events: *event.Channel(Event), + + exported_symbol_names: event.Locked(Decl.Table), + + /// Before code generation starts, must wait on this group to make sure + /// the build is complete. + build_group: event.Group(BuildError!void), + + compile_errors: event.Locked(CompileErrList), + + meta_type: *Type.MetaType, + void_type: *Type.Void, + bool_type: *Type.Bool, + noreturn_type: *Type.NoReturn, + + void_value: *Value.Void, + true_value: *Value.Bool, + false_value: *Value.Bool, + noreturn_value: *Value.NoReturn, + + const CompileErrList = std.ArrayList(*errmsg.Msg); + + // TODO handle some of these earlier and report them in a way other than error codes + pub const BuildError = error{ + OutOfMemory, + EndOfStream, + BadFd, + Io, + IsDir, + Unexpected, + SystemResources, + SharingViolation, + PathAlreadyExists, + FileNotFound, + AccessDenied, + PipeBusy, + FileTooBig, + SymLinkLoop, + ProcessFdQuotaExceeded, + NameTooLong, + SystemFdQuotaExceeded, + NoDevice, + PathNotFound, + NoSpaceLeft, + NotDir, + FileSystem, + OperationAborted, + IoPending, + BrokenPipe, + WouldBlock, + FileClosed, + DestinationAddressRequired, + DiskQuota, + InputOutput, + NoStdHandles, + Overflow, + NotSupported, + BufferTooSmall, + Unimplemented, // TODO remove this one + SemanticAnalysisFailed, // TODO remove this one + }; + + pub const Event = union(enum) { + Ok, + Error: BuildError, + Fail: []*errmsg.Msg, + }; + + pub const DarwinVersionMin = union(enum) { + None, + MacOS: []const u8, + Ios: []const u8, + }; + + pub const Kind = enum { + Exe, + Lib, + Obj, + }; + + pub const LinkLib = struct { + name: []const u8, + path: ?[]const u8, + + /// the list of symbols we depend on from this lib + symbols: ArrayList([]u8), + provided_explicitly: bool, + }; + + pub const Emit = enum { + Binary, + Assembly, + LlvmIr, + }; + + pub fn create( + event_loop_local: *EventLoopLocal, + name: []const u8, + root_src_path: ?[]const u8, + target: *const Target, + kind: Kind, + build_mode: builtin.Mode, + zig_lib_dir: []const u8, + cache_dir: []const u8, + ) !*Compilation { + const loop = event_loop_local.loop; + + var name_buffer = try Buffer.init(loop.allocator, name); + errdefer name_buffer.deinit(); + + const events = try event.Channel(Event).create(loop, 0); + errdefer events.destroy(); + + const comp = try loop.allocator.create(Compilation{ + .loop = loop, + .event_loop_local = event_loop_local, + .events = events, + .name = name_buffer, + .root_src_path = root_src_path, + .target = target.*, + .kind = kind, + .build_mode = build_mode, + .zig_lib_dir = zig_lib_dir, + .cache_dir = cache_dir, + + .version_major = 0, + .version_minor = 0, + .version_patch = 0, + + .verbose_tokenize = false, + .verbose_ast_tree = false, + .verbose_ast_fmt = false, + .verbose_cimport = false, + .verbose_ir = false, + .verbose_llvm_ir = false, + .verbose_link = false, + + .linker_script = null, + .libc_lib_dir = null, + .libc_static_lib_dir = null, + .libc_include_dir = null, + .msvc_lib_dir = null, + .kernel32_lib_dir = null, + .dynamic_linker = null, + .out_h_path = null, + .is_test = false, + .each_lib_rpath = false, + .strip = false, + .is_static = false, + .linker_rdynamic = false, + .clang_argv = [][]const u8{}, + .llvm_argv = [][]const u8{}, + .lib_dirs = [][]const u8{}, + .rpath_list = [][]const u8{}, + .assembly_files = [][]const u8{}, + .link_objects = [][]const u8{}, + .windows_subsystem_windows = false, + .windows_subsystem_console = false, + .link_libs_list = ArrayList(*LinkLib).init(loop.allocator), + .libc_link_lib = null, + .err_color = errmsg.Color.Auto, + .darwin_frameworks = [][]const u8{}, + .darwin_version_min = DarwinVersionMin.None, + .test_filters = [][]const u8{}, + .test_name_prefix = null, + .emit_file_type = Emit.Binary, + .link_out_file = null, + .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), + .build_group = event.Group(BuildError!void).init(loop), + .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), + + .meta_type = undefined, + .void_type = undefined, + .void_value = undefined, + .bool_type = undefined, + .true_value = undefined, + .false_value = undefined, + .noreturn_type = undefined, + .noreturn_value = undefined, + }); + try comp.initTypes(); + return comp; + } + + fn initTypes(comp: *Compilation) !void { + comp.meta_type = try comp.a().create(Type.MetaType{ + .base = Type{ + .base = Value{ + .id = Value.Id.Type, + .typeof = undefined, + .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice + }, + .id = builtin.TypeId.Type, + }, + .value = undefined, + }); + comp.meta_type.value = &comp.meta_type.base; + comp.meta_type.base.base.typeof = &comp.meta_type.base; + errdefer comp.a().destroy(comp.meta_type); + + comp.void_type = try comp.a().create(Type.Void{ + .base = Type{ + .base = Value{ + .id = Value.Id.Type, + .typeof = &Type.MetaType.get(comp).base, + .ref_count = std.atomic.Int(usize).init(1), + }, + .id = builtin.TypeId.Void, + }, + }); + errdefer comp.a().destroy(comp.void_type); + + comp.noreturn_type = try comp.a().create(Type.NoReturn{ + .base = Type{ + .base = Value{ + .id = Value.Id.Type, + .typeof = &Type.MetaType.get(comp).base, + .ref_count = std.atomic.Int(usize).init(1), + }, + .id = builtin.TypeId.NoReturn, + }, + }); + errdefer comp.a().destroy(comp.noreturn_type); + + comp.bool_type = try comp.a().create(Type.Bool{ + .base = Type{ + .base = Value{ + .id = Value.Id.Type, + .typeof = &Type.MetaType.get(comp).base, + .ref_count = std.atomic.Int(usize).init(1), + }, + .id = builtin.TypeId.Bool, + }, + }); + errdefer comp.a().destroy(comp.bool_type); + + comp.void_value = try comp.a().create(Value.Void{ + .base = Value{ + .id = Value.Id.Void, + .typeof = &Type.Void.get(comp).base, + .ref_count = std.atomic.Int(usize).init(1), + }, + }); + errdefer comp.a().destroy(comp.void_value); + + comp.true_value = try comp.a().create(Value.Bool{ + .base = Value{ + .id = Value.Id.Bool, + .typeof = &Type.Bool.get(comp).base, + .ref_count = std.atomic.Int(usize).init(1), + }, + .x = true, + }); + errdefer comp.a().destroy(comp.true_value); + + comp.false_value = try comp.a().create(Value.Bool{ + .base = Value{ + .id = Value.Id.Bool, + .typeof = &Type.Bool.get(comp).base, + .ref_count = std.atomic.Int(usize).init(1), + }, + .x = false, + }); + errdefer comp.a().destroy(comp.false_value); + + comp.noreturn_value = try comp.a().create(Value.NoReturn{ + .base = Value{ + .id = Value.Id.NoReturn, + .typeof = &Type.NoReturn.get(comp).base, + .ref_count = std.atomic.Int(usize).init(1), + }, + }); + errdefer comp.a().destroy(comp.noreturn_value); + } + + pub fn destroy(self: *Compilation) void { + self.noreturn_value.base.deref(self); + self.void_value.base.deref(self); + self.false_value.base.deref(self); + self.true_value.base.deref(self); + self.noreturn_type.base.base.deref(self); + self.void_type.base.base.deref(self); + self.meta_type.base.base.deref(self); + + self.events.destroy(); + self.name.deinit(); + + self.a().destroy(self); + } + + pub fn build(self: *Compilation) !void { + if (self.llvm_argv.len != 0) { + var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{ + [][]const u8{"zig (LLVM option parsing)"}, + self.llvm_argv, + }); + defer c_compatible_args.deinit(); + // TODO this sets global state + c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr); + } + + _ = try async self.buildAsync(); + } + + async fn buildAsync(self: *Compilation) void { + while (true) { + // TODO directly awaiting async should guarantee memory allocation elision + // TODO also async before suspending should guarantee memory allocation elision + const build_result = await (async self.addRootSrc() catch unreachable); + + // this makes a handy error return trace and stack trace in debug mode + if (std.debug.runtime_safety) { + build_result catch unreachable; + } + + const compile_errors = blk: { + const held = await (async self.compile_errors.acquire() catch unreachable); + defer held.release(); + break :blk held.value.toOwnedSlice(); + }; + + if (build_result) |_| { + if (compile_errors.len == 0) { + await (async self.events.put(Event.Ok) catch unreachable); + } else { + await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable); + } + } else |err| { + // if there's an error then the compile errors have dangling references + self.a().free(compile_errors); + + await (async self.events.put(Event{ .Error = err }) catch unreachable); + } + + // for now we stop after 1 + return; + } + } + + async fn addRootSrc(self: *Compilation) !void { + const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); + // TODO async/await os.path.real + const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| { + try printError("unable to get real path '{}': {}", root_src_path, err); + return err; + }; + errdefer self.a().free(root_src_real_path); + + // TODO async/await readFileAlloc() + const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| { + try printError("unable to open '{}': {}", root_src_real_path, err); + return err; + }; + errdefer self.a().free(source_code); + + const parsed_file = try self.a().create(ParsedFile{ + .tree = undefined, + .realpath = root_src_real_path, + }); + errdefer self.a().destroy(parsed_file); + + parsed_file.tree = try std.zig.parse(self.a(), source_code); + errdefer parsed_file.tree.deinit(); + + const tree = &parsed_file.tree; + + // create empty struct for it + const decls = try Scope.Decls.create(self, null); + defer decls.base.deref(self); + + var decl_group = event.Group(BuildError!void).init(self.loop); + errdefer decl_group.cancelAll(); + + var it = tree.root_node.decls.iterator(0); + while (it.next()) |decl_ptr| { + const decl = decl_ptr.*; + switch (decl.id) { + ast.Node.Id.Comptime => @panic("TODO"), + ast.Node.Id.VarDecl => @panic("TODO"), + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); + + const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { + try self.addCompileError(parsed_file, Span{ + .first = fn_proto.fn_token, + .last = fn_proto.fn_token + 1, + }, "missing function name"); + continue; + }; + + const fn_decl = try self.a().create(Decl.Fn{ + .base = Decl{ + .id = Decl.Id.Fn, + .name = name, + .visib = parseVisibToken(tree, fn_proto.visib_token), + .resolution = event.Future(BuildError!void).init(self.loop), + .resolution_in_progress = 0, + .parsed_file = parsed_file, + .parent_scope = &decls.base, + }, + .value = Decl.Fn.Val{ .Unresolved = {} }, + .fn_proto = fn_proto, + }); + errdefer self.a().destroy(fn_decl); + + try decl_group.call(addTopLevelDecl, self, &fn_decl.base); + }, + ast.Node.Id.TestDecl => @panic("TODO"), + else => unreachable, + } + } + try await (async decl_group.wait() catch unreachable); + try await (async self.build_group.wait() catch unreachable); + } + + async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void { + const is_export = decl.isExported(&decl.parsed_file.tree); + + if (is_export) { + try self.build_group.call(verifyUniqueSymbol, self, decl); + try self.build_group.call(resolveDecl, self, decl); + } + } + + fn addCompileError(self: *Compilation, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void { + const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args); + errdefer self.loop.allocator.free(text); + + try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text); + } + + async fn addCompileErrorAsync( + self: *Compilation, + parsed_file: *ParsedFile, + span: Span, + text: []u8, + ) !void { + const msg = try self.loop.allocator.create(errmsg.Msg{ + .path = parsed_file.realpath, + .text = text, + .span = span, + .tree = &parsed_file.tree, + }); + errdefer self.loop.allocator.destroy(msg); + + const compile_errors = await (async self.compile_errors.acquire() catch unreachable); + defer compile_errors.release(); + + try compile_errors.value.append(msg); + } + + async fn verifyUniqueSymbol(self: *Compilation, decl: *Decl) !void { + const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable); + defer exported_symbol_names.release(); + + if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { + try self.addCompileError( + decl.parsed_file, + decl.getSpan(), + "exported symbol collision: '{}'", + decl.name, + ); + // TODO add error note showing location of other symbol + } + } + + pub fn link(self: *Compilation, out_file: ?[]const u8) !void { + warn("TODO link"); + return error.Todo; + } + + pub fn addLinkLib(self: *Compilation, name: []const u8, provided_explicitly: bool) !*LinkLib { + const is_libc = mem.eql(u8, name, "c"); + + if (is_libc) { + if (self.libc_link_lib) |libc_link_lib| { + return libc_link_lib; + } + } + + for (self.link_libs_list.toSliceConst()) |existing_lib| { + if (mem.eql(u8, name, existing_lib.name)) { + return existing_lib; + } + } + + const link_lib = try self.a().create(LinkLib{ + .name = name, + .path = null, + .provided_explicitly = provided_explicitly, + .symbols = ArrayList([]u8).init(self.a()), + }); + try self.link_libs_list.append(link_lib); + if (is_libc) { + self.libc_link_lib = link_lib; + } + return link_lib; + } + + fn a(self: Compilation) *mem.Allocator { + return self.loop.allocator; + } +}; + +fn printError(comptime format: []const u8, args: ...) !void { + var stderr_file = try std.io.getStdErr(); + var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file); + const out_stream = &stderr_file_out_stream.stream; + try out_stream.print(format, args); +} + +fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib { + if (optional_token_index) |token_index| { + const token = tree.tokens.at(token_index); + assert(token.id == Token.Id.Keyword_pub); + return Visib.Pub; + } else { + return Visib.Private; + } +} + +/// This declaration has been blessed as going into the final code generation. +pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void { + if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { + decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable); + decl.resolution.resolve(); + return decl.resolution.data; + } else { + return (await (async decl.resolution.get() catch unreachable)).*; + } +} + +/// The function that actually does the generation. +async fn generateDecl(comp: *Compilation, decl: *Decl) !void { + switch (decl.id) { + Decl.Id.Var => @panic("TODO"), + Decl.Id.Fn => { + const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl); + return await (async generateDeclFn(comp, fn_decl) catch unreachable); + }, + Decl.Id.CompTime => @panic("TODO"), + } +} + +async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { + const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl"); + + const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope); + defer fndef_scope.base.deref(comp); + + // TODO actually look at the return type of the AST + const return_type = &Type.Void.get(comp).base; + defer return_type.base.deref(comp); + + const is_var_args = false; + const params = ([*]Type.Fn.Param)(undefined)[0..0]; + const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args); + defer fn_type.base.base.deref(comp); + + var symbol_name = try std.Buffer.init(comp.a(), fn_decl.base.name); + errdefer symbol_name.deinit(); + + const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); + defer fn_val.base.deref(comp); + + fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; + + const unanalyzed_code = (await (async ir.gen( + comp, + body_node, + &fndef_scope.base, + Span.token(body_node.lastToken()), + fn_decl.base.parsed_file, + ) catch unreachable)) catch |err| switch (err) { + // This poison value should not cause the errdefers to run. It simply means + // that self.compile_errors is populated. + // TODO https://github.com/ziglang/zig/issues/769 + error.SemanticAnalysisFailed => return {}, + else => return err, + }; + defer unanalyzed_code.destroy(comp.a()); + + if (comp.verbose_ir) { + std.debug.warn("unanalyzed:\n"); + unanalyzed_code.dump(); + } + + const analyzed_code = (await (async ir.analyze( + comp, + fn_decl.base.parsed_file, + unanalyzed_code, + null, + ) catch unreachable)) catch |err| switch (err) { + // This poison value should not cause the errdefers to run. It simply means + // that self.compile_errors is populated. + // TODO https://github.com/ziglang/zig/issues/769 + error.SemanticAnalysisFailed => return {}, + else => return err, + }; + errdefer analyzed_code.destroy(comp.a()); + + if (comp.verbose_ir) { + std.debug.warn("analyzed:\n"); + analyzed_code.dump(); + } + + // Kick off rendering to LLVM comp, but it doesn't block the fn decl + // analysis from being complete. + try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); +} diff --git a/src-self-hosted/decl.zig b/src-self-hosted/decl.zig index 1a75a3249e..c0173266ee 100644 --- a/src-self-hosted/decl.zig +++ b/src-self-hosted/decl.zig @@ -9,13 +9,13 @@ const Value = @import("value.zig").Value; const Token = std.zig.Token; const errmsg = @import("errmsg.zig"); const Scope = @import("scope.zig").Scope; -const Module = @import("module.zig").Module; +const Compilation = @import("compilation.zig").Compilation; pub const Decl = struct { id: Id, name: []const u8, visib: Visib, - resolution: event.Future(Module.BuildError!void), + resolution: event.Future(Compilation.BuildError!void), resolution_in_progress: u8, parsed_file: *ParsedFile, parent_scope: *Scope, diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index f1c395a790..22d5a067a7 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const Module = @import("module.zig").Module; +const Compilation = @import("compilation.zig").Compilation; const Scope = @import("scope.zig").Scope; const ast = std.zig.ast; const Allocator = std.mem.Allocator; @@ -243,7 +243,7 @@ pub const Instruction = struct { Value.Ptr.Mut.CompTimeConst, self.params.mut, self.params.volatility, - val.typeof.getAbiAlignment(ira.irb.module), + val.typeof.getAbiAlignment(ira.irb.comp), ); } @@ -254,12 +254,12 @@ pub const Instruction = struct { }); const elem_type = target.getKnownType(); const ptr_type = Type.Pointer.get( - ira.irb.module, + ira.irb.comp, elem_type, self.params.mut, self.params.volatility, Type.Pointer.Size.One, - elem_type.getAbiAlignment(ira.irb.module), + elem_type.getAbiAlignment(ira.irb.comp), ); // TODO: potentially set the hint that this is a stack pointer. But it might not be - this // could be a ref of a global, for example @@ -417,7 +417,7 @@ pub const Code = struct { arena: std.heap.ArenaAllocator, return_type: ?*Type, - /// allocator is module.a() + /// allocator is comp.a() pub fn destroy(self: *Code, allocator: *Allocator) void { self.arena.deinit(); allocator.destroy(self); @@ -437,7 +437,7 @@ pub const Code = struct { }; pub const Builder = struct { - module: *Module, + comp: *Compilation, code: *Code, current_basic_block: *BasicBlock, next_debug_id: usize, @@ -446,17 +446,17 @@ pub const Builder = struct { pub const Error = Analyze.Error; - pub fn init(module: *Module, parsed_file: *ParsedFile) !Builder { - const code = try module.a().create(Code{ + pub fn init(comp: *Compilation, parsed_file: *ParsedFile) !Builder { + const code = try comp.a().create(Code{ .basic_block_list = undefined, - .arena = std.heap.ArenaAllocator.init(module.a()), + .arena = std.heap.ArenaAllocator.init(comp.a()), .return_type = null, }); code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator); - errdefer code.destroy(module.a()); + errdefer code.destroy(comp.a()); return Builder{ - .module = module, + .comp = comp, .parsed_file = parsed_file, .current_basic_block = undefined, .code = code, @@ -466,7 +466,7 @@ pub const Builder = struct { } pub fn abort(self: *Builder) void { - self.code.destroy(self.module.a()); + self.code.destroy(self.comp.a()); } /// Call code.destroy() when done @@ -581,7 +581,7 @@ pub const Builder = struct { } pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Instruction { - const block_scope = try Scope.Block.create(irb.module, parent_scope); + const block_scope = try Scope.Block.create(irb.comp, parent_scope); const outer_block_scope = &block_scope.base; var child_scope = outer_block_scope; @@ -623,8 +623,8 @@ pub const Builder = struct { Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit, else => unreachable, }; - const defer_expr_scope = try Scope.DeferExpr.create(irb.module, parent_scope, defer_node.expr); - const defer_child_scope = try Scope.Defer.create(irb.module, parent_scope, kind, defer_expr_scope); + const defer_expr_scope = try Scope.DeferExpr.create(irb.comp, parent_scope, defer_node.expr); + const defer_child_scope = try Scope.Defer.create(irb.comp, parent_scope, kind, defer_expr_scope); child_scope = &defer_child_scope.base; continue; } @@ -770,8 +770,8 @@ pub const Builder = struct { .debug_id = self.next_debug_id, .val = switch (I.ir_val_init) { IrVal.Init.Unknown => IrVal.Unknown, - IrVal.Init.NoReturn => IrVal{ .KnownValue = &Value.NoReturn.get(self.module).base }, - IrVal.Init.Void => IrVal{ .KnownValue = &Value.Void.get(self.module).base }, + IrVal.Init.NoReturn => IrVal{ .KnownValue = &Value.NoReturn.get(self.comp).base }, + IrVal.Init.Void => IrVal{ .KnownValue = &Value.Void.get(self.comp).base }, }, .ref_count = 0, .span = span, @@ -819,13 +819,13 @@ pub const Builder = struct { fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Instruction { const inst = try self.build(Instruction.Const, scope, span, Instruction.Const.Params{}); - inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.module, x).base }; + inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.comp, x).base }; return inst; } fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Instruction { const inst = try self.buildExtra(Instruction.Const, scope, span, Instruction.Const.Params{}, is_generated); - inst.val = IrVal{ .KnownValue = &Value.Void.get(self.module).base }; + inst.val = IrVal{ .KnownValue = &Value.Void.get(self.comp).base }; return inst; } }; @@ -850,8 +850,8 @@ const Analyze = struct { OutOfMemory, }; - pub fn init(module: *Module, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze { - var irb = try Builder.init(module, parsed_file); + pub fn init(comp: *Compilation, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze { + var irb = try Builder.init(comp, parsed_file); errdefer irb.abort(); return Analyze{ @@ -929,12 +929,12 @@ const Analyze = struct { } fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void { - return self.irb.module.addCompileError(self.irb.parsed_file, span, fmt, args); + return self.irb.comp.addCompileError(self.irb.parsed_file, span, fmt, args); } fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Instruction) Analyze.Error!*Type { // TODO actual implementation - return &Type.Void.get(self.irb.module).base; + return &Type.Void.get(self.irb.comp).base; } fn implicitCast(self: *Analyze, target: *Instruction, optional_dest_type: ?*Type) Analyze.Error!*Instruction { @@ -959,13 +959,13 @@ const Analyze = struct { }; pub async fn gen( - module: *Module, + comp: *Compilation, body_node: *ast.Node, scope: *Scope, end_span: Span, parsed_file: *ParsedFile, ) !*Code { - var irb = try Builder.init(module, parsed_file); + var irb = try Builder.init(comp, parsed_file); errdefer irb.abort(); const entry_block = try irb.createBasicBlock(scope, "Entry"); @@ -991,8 +991,8 @@ pub async fn gen( return irb.finish(); } -pub async fn analyze(module: *Module, parsed_file: *ParsedFile, old_code: *Code, expected_type: ?*Type) !*Code { - var ira = try Analyze.init(module, parsed_file, expected_type); +pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Code, expected_type: ?*Type) !*Code { + var ira = try Analyze.init(comp, parsed_file, expected_type); errdefer ira.abort(); const old_entry_bb = old_code.basic_block_list.at(0); @@ -1025,7 +1025,7 @@ pub async fn analyze(module: *Module, parsed_file: *ParsedFile, old_code: *Code, } if (ira.src_implicit_return_type_list.len == 0) { - ira.irb.code.return_type = &Type.NoReturn.get(module).base; + ira.irb.code.return_type = &Type.NoReturn.get(comp).base; return ira.irb.finish(); } diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 77ec7f6d32..c9478954c5 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -14,8 +14,8 @@ const c = @import("c.zig"); const introspect = @import("introspect.zig"); const Args = arg.Args; const Flag = arg.Flag; -const EventLoopLocal = @import("module.zig").EventLoopLocal; -const Module = @import("module.zig").Module; +const EventLoopLocal = @import("compilation.zig").EventLoopLocal; +const Compilation = @import("compilation.zig").Compilation; const Target = @import("target.zig").Target; const errmsg = @import("errmsg.zig"); @@ -258,7 +258,7 @@ const args_build_generic = []Flag{ Flag.Arg1("--ver-patch"), }; -fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Module.Kind) !void { +fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Compilation.Kind) !void { var flags = try Args.parse(allocator, args_build_generic, args); defer flags.deinit(); @@ -300,14 +300,14 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo const emit_type = blk: { if (flags.single("emit")) |emit_flag| { if (mem.eql(u8, emit_flag, "asm")) { - break :blk Module.Emit.Assembly; + break :blk Compilation.Emit.Assembly; } else if (mem.eql(u8, emit_flag, "bin")) { - break :blk Module.Emit.Binary; + break :blk Compilation.Emit.Binary; } else if (mem.eql(u8, emit_flag, "llvm-ir")) { - break :blk Module.Emit.LlvmIr; + break :blk Compilation.Emit.LlvmIr; } else unreachable; } else { - break :blk Module.Emit.Binary; + break :blk Compilation.Emit.Binary; } }; @@ -370,7 +370,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo os.exit(1); } - if (out_type == Module.Kind.Obj and link_objects.len != 0) { + if (out_type == Compilation.Kind.Obj and link_objects.len != 0) { try stderr.write("When building an object file, --object arguments are invalid\n"); os.exit(1); } @@ -392,7 +392,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo var event_loop_local = EventLoopLocal.init(&loop); defer event_loop_local.deinit(); - var module = try Module.create( + var comp = try Compilation.create( &event_loop_local, root_name, root_source_file, @@ -402,16 +402,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo zig_lib_dir, full_cache_dir, ); - defer module.destroy(); + defer comp.destroy(); - module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10); - module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10); - module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10); + comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10); + comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10); + comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10); - module.is_test = false; + comp.is_test = false; - module.linker_script = flags.single("linker-script"); - module.each_lib_rpath = flags.present("each-lib-rpath"); + comp.linker_script = flags.single("linker-script"); + comp.each_lib_rpath = flags.present("each-lib-rpath"); var clang_argv_buf = ArrayList([]const u8).init(allocator); defer clang_argv_buf.deinit(); @@ -422,51 +422,51 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo try clang_argv_buf.append(mllvm); } - module.llvm_argv = mllvm_flags; - module.clang_argv = clang_argv_buf.toSliceConst(); + comp.llvm_argv = mllvm_flags; + comp.clang_argv = clang_argv_buf.toSliceConst(); - module.strip = flags.present("strip"); - module.is_static = flags.present("static"); + comp.strip = flags.present("strip"); + comp.is_static = flags.present("static"); if (flags.single("libc-lib-dir")) |libc_lib_dir| { - module.libc_lib_dir = libc_lib_dir; + comp.libc_lib_dir = libc_lib_dir; } if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| { - module.libc_static_lib_dir = libc_static_lib_dir; + comp.libc_static_lib_dir = libc_static_lib_dir; } if (flags.single("libc-include-dir")) |libc_include_dir| { - module.libc_include_dir = libc_include_dir; + comp.libc_include_dir = libc_include_dir; } if (flags.single("msvc-lib-dir")) |msvc_lib_dir| { - module.msvc_lib_dir = msvc_lib_dir; + comp.msvc_lib_dir = msvc_lib_dir; } if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| { - module.kernel32_lib_dir = kernel32_lib_dir; + comp.kernel32_lib_dir = kernel32_lib_dir; } if (flags.single("dynamic-linker")) |dynamic_linker| { - module.dynamic_linker = dynamic_linker; + comp.dynamic_linker = dynamic_linker; } - module.verbose_tokenize = flags.present("verbose-tokenize"); - module.verbose_ast_tree = flags.present("verbose-ast-tree"); - module.verbose_ast_fmt = flags.present("verbose-ast-fmt"); - module.verbose_link = flags.present("verbose-link"); - module.verbose_ir = flags.present("verbose-ir"); - module.verbose_llvm_ir = flags.present("verbose-llvm-ir"); - module.verbose_cimport = flags.present("verbose-cimport"); + comp.verbose_tokenize = flags.present("verbose-tokenize"); + comp.verbose_ast_tree = flags.present("verbose-ast-tree"); + comp.verbose_ast_fmt = flags.present("verbose-ast-fmt"); + comp.verbose_link = flags.present("verbose-link"); + comp.verbose_ir = flags.present("verbose-ir"); + comp.verbose_llvm_ir = flags.present("verbose-llvm-ir"); + comp.verbose_cimport = flags.present("verbose-cimport"); - module.err_color = color; - module.lib_dirs = flags.many("library-path"); - module.darwin_frameworks = flags.many("framework"); - module.rpath_list = flags.many("rpath"); + comp.err_color = color; + comp.lib_dirs = flags.many("library-path"); + comp.darwin_frameworks = flags.many("framework"); + comp.rpath_list = flags.many("rpath"); if (flags.single("output-h")) |output_h| { - module.out_h_path = output_h; + comp.out_h_path = output_h; } - module.windows_subsystem_windows = flags.present("mwindows"); - module.windows_subsystem_console = flags.present("mconsole"); - module.linker_rdynamic = flags.present("rdynamic"); + comp.windows_subsystem_windows = flags.present("mwindows"); + comp.windows_subsystem_console = flags.present("mconsole"); + comp.linker_rdynamic = flags.present("rdynamic"); if (flags.single("mmacosx-version-min") != null and flags.single("mios-version-min") != null) { try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n"); @@ -474,37 +474,37 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo } if (flags.single("mmacosx-version-min")) |ver| { - module.darwin_version_min = Module.DarwinVersionMin{ .MacOS = ver }; + comp.darwin_version_min = Compilation.DarwinVersionMin{ .MacOS = ver }; } if (flags.single("mios-version-min")) |ver| { - module.darwin_version_min = Module.DarwinVersionMin{ .Ios = ver }; + comp.darwin_version_min = Compilation.DarwinVersionMin{ .Ios = ver }; } - module.emit_file_type = emit_type; - module.link_objects = link_objects; - module.assembly_files = assembly_files; - module.link_out_file = flags.single("out-file"); + comp.emit_file_type = emit_type; + comp.link_objects = link_objects; + comp.assembly_files = assembly_files; + comp.link_out_file = flags.single("out-file"); - try module.build(); - const process_build_events_handle = try async processBuildEvents(module, color); + try comp.build(); + const process_build_events_handle = try async processBuildEvents(comp, color); defer cancel process_build_events_handle; loop.run(); } -async fn processBuildEvents(module: *Module, color: errmsg.Color) void { +async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { // TODO directly awaiting async should guarantee memory allocation elision - const build_event = await (async module.events.get() catch unreachable); + const build_event = await (async comp.events.get() catch unreachable); switch (build_event) { - Module.Event.Ok => { + Compilation.Event.Ok => { std.debug.warn("Build succeeded\n"); return; }, - Module.Event.Error => |err| { + Compilation.Event.Error => |err| { std.debug.warn("build failed: {}\n", @errorName(err)); os.exit(1); }, - Module.Event.Fail => |msgs| { + Compilation.Event.Fail => |msgs| { for (msgs) |msg| { errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1); } @@ -513,15 +513,15 @@ async fn processBuildEvents(module: *Module, color: errmsg.Color) void { } fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void { - return buildOutputType(allocator, args, Module.Kind.Exe); + return buildOutputType(allocator, args, Compilation.Kind.Exe); } fn cmdBuildLib(allocator: *Allocator, args: []const []const u8) !void { - return buildOutputType(allocator, args, Module.Kind.Lib); + return buildOutputType(allocator, args, Compilation.Kind.Lib); } fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void { - return buildOutputType(allocator, args, Module.Kind.Obj); + return buildOutputType(allocator, args, Compilation.Kind.Obj); } const usage_fmt = diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig deleted file mode 100644 index 617bd0d44a..0000000000 --- a/src-self-hosted/module.zig +++ /dev/null @@ -1,747 +0,0 @@ -const std = @import("std"); -const os = std.os; -const io = std.io; -const mem = std.mem; -const Allocator = mem.Allocator; -const Buffer = std.Buffer; -const llvm = @import("llvm.zig"); -const c = @import("c.zig"); -const builtin = @import("builtin"); -const Target = @import("target.zig").Target; -const warn = std.debug.warn; -const Token = std.zig.Token; -const ArrayList = std.ArrayList; -const errmsg = @import("errmsg.zig"); -const ast = std.zig.ast; -const event = std.event; -const assert = std.debug.assert; -const AtomicRmwOp = builtin.AtomicRmwOp; -const AtomicOrder = builtin.AtomicOrder; -const Scope = @import("scope.zig").Scope; -const Decl = @import("decl.zig").Decl; -const ir = @import("ir.zig"); -const Visib = @import("visib.zig").Visib; -const ParsedFile = @import("parsed_file.zig").ParsedFile; -const Value = @import("value.zig").Value; -const Type = Value.Type; -const Span = errmsg.Span; -const codegen = @import("codegen.zig"); - -/// Data that is local to the event loop. -pub const EventLoopLocal = struct { - loop: *event.Loop, - llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), - - fn init(loop: *event.Loop) EventLoopLocal { - return EventLoopLocal{ - .loop = loop, - .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), - }; - } - - fn deinit(self: *EventLoopLocal) void { - while (self.llvm_handle_pool.pop()) |node| { - c.LLVMContextDispose(node.data); - self.loop.allocator.destroy(node); - } - } - - /// Gets an exclusive handle on any LlvmContext. - /// Caller must release the handle when done. - pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle { - if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node }; - - const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory; - errdefer c.LLVMContextDispose(context_ref); - - const node = try self.loop.allocator.create(std.atomic.Stack(llvm.ContextRef).Node{ - .next = undefined, - .data = context_ref, - }); - errdefer self.loop.allocator.destroy(node); - - return LlvmHandle{ .node = node }; - } -}; - -pub const LlvmHandle = struct { - node: *std.atomic.Stack(llvm.ContextRef).Node, - - pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void { - event_loop_local.llvm_handle_pool.push(self.node); - } -}; - -pub const Module = struct { - event_loop_local: *EventLoopLocal, - loop: *event.Loop, - name: Buffer, - root_src_path: ?[]const u8, - target: Target, - build_mode: builtin.Mode, - zig_lib_dir: []const u8, - - version_major: u32, - version_minor: u32, - version_patch: u32, - - linker_script: ?[]const u8, - cache_dir: []const u8, - libc_lib_dir: ?[]const u8, - libc_static_lib_dir: ?[]const u8, - libc_include_dir: ?[]const u8, - msvc_lib_dir: ?[]const u8, - kernel32_lib_dir: ?[]const u8, - dynamic_linker: ?[]const u8, - out_h_path: ?[]const u8, - - is_test: bool, - each_lib_rpath: bool, - strip: bool, - is_static: bool, - linker_rdynamic: bool, - - clang_argv: []const []const u8, - llvm_argv: []const []const u8, - lib_dirs: []const []const u8, - rpath_list: []const []const u8, - assembly_files: []const []const u8, - link_objects: []const []const u8, - - windows_subsystem_windows: bool, - windows_subsystem_console: bool, - - link_libs_list: ArrayList(*LinkLib), - libc_link_lib: ?*LinkLib, - - err_color: errmsg.Color, - - verbose_tokenize: bool, - verbose_ast_tree: bool, - verbose_ast_fmt: bool, - verbose_cimport: bool, - verbose_ir: bool, - verbose_llvm_ir: bool, - verbose_link: bool, - - darwin_frameworks: []const []const u8, - darwin_version_min: DarwinVersionMin, - - test_filters: []const []const u8, - test_name_prefix: ?[]const u8, - - emit_file_type: Emit, - - kind: Kind, - - link_out_file: ?[]const u8, - events: *event.Channel(Event), - - exported_symbol_names: event.Locked(Decl.Table), - - /// Before code generation starts, must wait on this group to make sure - /// the build is complete. - build_group: event.Group(BuildError!void), - - compile_errors: event.Locked(CompileErrList), - - meta_type: *Type.MetaType, - void_type: *Type.Void, - bool_type: *Type.Bool, - noreturn_type: *Type.NoReturn, - - void_value: *Value.Void, - true_value: *Value.Bool, - false_value: *Value.Bool, - noreturn_value: *Value.NoReturn, - - const CompileErrList = std.ArrayList(*errmsg.Msg); - - // TODO handle some of these earlier and report them in a way other than error codes - pub const BuildError = error{ - OutOfMemory, - EndOfStream, - BadFd, - Io, - IsDir, - Unexpected, - SystemResources, - SharingViolation, - PathAlreadyExists, - FileNotFound, - AccessDenied, - PipeBusy, - FileTooBig, - SymLinkLoop, - ProcessFdQuotaExceeded, - NameTooLong, - SystemFdQuotaExceeded, - NoDevice, - PathNotFound, - NoSpaceLeft, - NotDir, - FileSystem, - OperationAborted, - IoPending, - BrokenPipe, - WouldBlock, - FileClosed, - DestinationAddressRequired, - DiskQuota, - InputOutput, - NoStdHandles, - Overflow, - NotSupported, - BufferTooSmall, - Unimplemented, // TODO remove this one - SemanticAnalysisFailed, // TODO remove this one - }; - - pub const Event = union(enum) { - Ok, - Error: BuildError, - Fail: []*errmsg.Msg, - }; - - pub const DarwinVersionMin = union(enum) { - None, - MacOS: []const u8, - Ios: []const u8, - }; - - pub const Kind = enum { - Exe, - Lib, - Obj, - }; - - pub const LinkLib = struct { - name: []const u8, - path: ?[]const u8, - - /// the list of symbols we depend on from this lib - symbols: ArrayList([]u8), - provided_explicitly: bool, - }; - - pub const Emit = enum { - Binary, - Assembly, - LlvmIr, - }; - - pub fn create( - event_loop_local: *EventLoopLocal, - name: []const u8, - root_src_path: ?[]const u8, - target: *const Target, - kind: Kind, - build_mode: builtin.Mode, - zig_lib_dir: []const u8, - cache_dir: []const u8, - ) !*Module { - const loop = event_loop_local.loop; - - var name_buffer = try Buffer.init(loop.allocator, name); - errdefer name_buffer.deinit(); - - const events = try event.Channel(Event).create(loop, 0); - errdefer events.destroy(); - - const module = try loop.allocator.create(Module{ - .loop = loop, - .event_loop_local = event_loop_local, - .events = events, - .name = name_buffer, - .root_src_path = root_src_path, - .target = target.*, - .kind = kind, - .build_mode = build_mode, - .zig_lib_dir = zig_lib_dir, - .cache_dir = cache_dir, - - .version_major = 0, - .version_minor = 0, - .version_patch = 0, - - .verbose_tokenize = false, - .verbose_ast_tree = false, - .verbose_ast_fmt = false, - .verbose_cimport = false, - .verbose_ir = false, - .verbose_llvm_ir = false, - .verbose_link = false, - - .linker_script = null, - .libc_lib_dir = null, - .libc_static_lib_dir = null, - .libc_include_dir = null, - .msvc_lib_dir = null, - .kernel32_lib_dir = null, - .dynamic_linker = null, - .out_h_path = null, - .is_test = false, - .each_lib_rpath = false, - .strip = false, - .is_static = false, - .linker_rdynamic = false, - .clang_argv = [][]const u8{}, - .llvm_argv = [][]const u8{}, - .lib_dirs = [][]const u8{}, - .rpath_list = [][]const u8{}, - .assembly_files = [][]const u8{}, - .link_objects = [][]const u8{}, - .windows_subsystem_windows = false, - .windows_subsystem_console = false, - .link_libs_list = ArrayList(*LinkLib).init(loop.allocator), - .libc_link_lib = null, - .err_color = errmsg.Color.Auto, - .darwin_frameworks = [][]const u8{}, - .darwin_version_min = DarwinVersionMin.None, - .test_filters = [][]const u8{}, - .test_name_prefix = null, - .emit_file_type = Emit.Binary, - .link_out_file = null, - .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), - .build_group = event.Group(BuildError!void).init(loop), - .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), - - .meta_type = undefined, - .void_type = undefined, - .void_value = undefined, - .bool_type = undefined, - .true_value = undefined, - .false_value = undefined, - .noreturn_type = undefined, - .noreturn_value = undefined, - }); - try module.initTypes(); - return module; - } - - fn initTypes(module: *Module) !void { - module.meta_type = try module.a().create(Type.MetaType{ - .base = Type{ - .base = Value{ - .id = Value.Id.Type, - .typeof = undefined, - .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice - }, - .id = builtin.TypeId.Type, - }, - .value = undefined, - }); - module.meta_type.value = &module.meta_type.base; - module.meta_type.base.base.typeof = &module.meta_type.base; - errdefer module.a().destroy(module.meta_type); - - module.void_type = try module.a().create(Type.Void{ - .base = Type{ - .base = Value{ - .id = Value.Id.Type, - .typeof = &Type.MetaType.get(module).base, - .ref_count = std.atomic.Int(usize).init(1), - }, - .id = builtin.TypeId.Void, - }, - }); - errdefer module.a().destroy(module.void_type); - - module.noreturn_type = try module.a().create(Type.NoReturn{ - .base = Type{ - .base = Value{ - .id = Value.Id.Type, - .typeof = &Type.MetaType.get(module).base, - .ref_count = std.atomic.Int(usize).init(1), - }, - .id = builtin.TypeId.NoReturn, - }, - }); - errdefer module.a().destroy(module.noreturn_type); - - module.bool_type = try module.a().create(Type.Bool{ - .base = Type{ - .base = Value{ - .id = Value.Id.Type, - .typeof = &Type.MetaType.get(module).base, - .ref_count = std.atomic.Int(usize).init(1), - }, - .id = builtin.TypeId.Bool, - }, - }); - errdefer module.a().destroy(module.bool_type); - - module.void_value = try module.a().create(Value.Void{ - .base = Value{ - .id = Value.Id.Void, - .typeof = &Type.Void.get(module).base, - .ref_count = std.atomic.Int(usize).init(1), - }, - }); - errdefer module.a().destroy(module.void_value); - - module.true_value = try module.a().create(Value.Bool{ - .base = Value{ - .id = Value.Id.Bool, - .typeof = &Type.Bool.get(module).base, - .ref_count = std.atomic.Int(usize).init(1), - }, - .x = true, - }); - errdefer module.a().destroy(module.true_value); - - module.false_value = try module.a().create(Value.Bool{ - .base = Value{ - .id = Value.Id.Bool, - .typeof = &Type.Bool.get(module).base, - .ref_count = std.atomic.Int(usize).init(1), - }, - .x = false, - }); - errdefer module.a().destroy(module.false_value); - - module.noreturn_value = try module.a().create(Value.NoReturn{ - .base = Value{ - .id = Value.Id.NoReturn, - .typeof = &Type.NoReturn.get(module).base, - .ref_count = std.atomic.Int(usize).init(1), - }, - }); - errdefer module.a().destroy(module.noreturn_value); - } - - pub fn destroy(self: *Module) void { - self.noreturn_value.base.deref(self); - self.void_value.base.deref(self); - self.false_value.base.deref(self); - self.true_value.base.deref(self); - self.noreturn_type.base.base.deref(self); - self.void_type.base.base.deref(self); - self.meta_type.base.base.deref(self); - - self.events.destroy(); - self.name.deinit(); - - self.a().destroy(self); - } - - pub fn build(self: *Module) !void { - if (self.llvm_argv.len != 0) { - var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{ - [][]const u8{"zig (LLVM option parsing)"}, - self.llvm_argv, - }); - defer c_compatible_args.deinit(); - // TODO this sets global state - c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr); - } - - _ = try async self.buildAsync(); - } - - async fn buildAsync(self: *Module) void { - while (true) { - // TODO directly awaiting async should guarantee memory allocation elision - // TODO also async before suspending should guarantee memory allocation elision - const build_result = await (async self.addRootSrc() catch unreachable); - - // this makes a handy error return trace and stack trace in debug mode - if (std.debug.runtime_safety) { - build_result catch unreachable; - } - - const compile_errors = blk: { - const held = await (async self.compile_errors.acquire() catch unreachable); - defer held.release(); - break :blk held.value.toOwnedSlice(); - }; - - if (build_result) |_| { - if (compile_errors.len == 0) { - await (async self.events.put(Event.Ok) catch unreachable); - } else { - await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable); - } - } else |err| { - // if there's an error then the compile errors have dangling references - self.a().free(compile_errors); - - await (async self.events.put(Event{ .Error = err }) catch unreachable); - } - - // for now we stop after 1 - return; - } - } - - async fn addRootSrc(self: *Module) !void { - const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); - // TODO async/await os.path.real - const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| { - try printError("unable to get real path '{}': {}", root_src_path, err); - return err; - }; - errdefer self.a().free(root_src_real_path); - - // TODO async/await readFileAlloc() - const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| { - try printError("unable to open '{}': {}", root_src_real_path, err); - return err; - }; - errdefer self.a().free(source_code); - - const parsed_file = try self.a().create(ParsedFile{ - .tree = undefined, - .realpath = root_src_real_path, - }); - errdefer self.a().destroy(parsed_file); - - parsed_file.tree = try std.zig.parse(self.a(), source_code); - errdefer parsed_file.tree.deinit(); - - const tree = &parsed_file.tree; - - // create empty struct for it - const decls = try Scope.Decls.create(self, null); - defer decls.base.deref(self); - - var decl_group = event.Group(BuildError!void).init(self.loop); - errdefer decl_group.cancelAll(); - - var it = tree.root_node.decls.iterator(0); - while (it.next()) |decl_ptr| { - const decl = decl_ptr.*; - switch (decl.id) { - ast.Node.Id.Comptime => @panic("TODO"), - ast.Node.Id.VarDecl => @panic("TODO"), - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); - - const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { - try self.addCompileError(parsed_file, Span{ - .first = fn_proto.fn_token, - .last = fn_proto.fn_token + 1, - }, "missing function name"); - continue; - }; - - const fn_decl = try self.a().create(Decl.Fn{ - .base = Decl{ - .id = Decl.Id.Fn, - .name = name, - .visib = parseVisibToken(tree, fn_proto.visib_token), - .resolution = event.Future(BuildError!void).init(self.loop), - .resolution_in_progress = 0, - .parsed_file = parsed_file, - .parent_scope = &decls.base, - }, - .value = Decl.Fn.Val{ .Unresolved = {} }, - .fn_proto = fn_proto, - }); - errdefer self.a().destroy(fn_decl); - - try decl_group.call(addTopLevelDecl, self, &fn_decl.base); - }, - ast.Node.Id.TestDecl => @panic("TODO"), - else => unreachable, - } - } - try await (async decl_group.wait() catch unreachable); - try await (async self.build_group.wait() catch unreachable); - } - - async fn addTopLevelDecl(self: *Module, decl: *Decl) !void { - const is_export = decl.isExported(&decl.parsed_file.tree); - - if (is_export) { - try self.build_group.call(verifyUniqueSymbol, self, decl); - try self.build_group.call(resolveDecl, self, decl); - } - } - - fn addCompileError(self: *Module, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void { - const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args); - errdefer self.loop.allocator.free(text); - - try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text); - } - - async fn addCompileErrorAsync( - self: *Module, - parsed_file: *ParsedFile, - span: Span, - text: []u8, - ) !void { - const msg = try self.loop.allocator.create(errmsg.Msg{ - .path = parsed_file.realpath, - .text = text, - .span = span, - .tree = &parsed_file.tree, - }); - errdefer self.loop.allocator.destroy(msg); - - const compile_errors = await (async self.compile_errors.acquire() catch unreachable); - defer compile_errors.release(); - - try compile_errors.value.append(msg); - } - - async fn verifyUniqueSymbol(self: *Module, decl: *Decl) !void { - const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable); - defer exported_symbol_names.release(); - - if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { - try self.addCompileError( - decl.parsed_file, - decl.getSpan(), - "exported symbol collision: '{}'", - decl.name, - ); - // TODO add error note showing location of other symbol - } - } - - pub fn link(self: *Module, out_file: ?[]const u8) !void { - warn("TODO link"); - return error.Todo; - } - - pub fn addLinkLib(self: *Module, name: []const u8, provided_explicitly: bool) !*LinkLib { - const is_libc = mem.eql(u8, name, "c"); - - if (is_libc) { - if (self.libc_link_lib) |libc_link_lib| { - return libc_link_lib; - } - } - - for (self.link_libs_list.toSliceConst()) |existing_lib| { - if (mem.eql(u8, name, existing_lib.name)) { - return existing_lib; - } - } - - const link_lib = try self.a().create(LinkLib{ - .name = name, - .path = null, - .provided_explicitly = provided_explicitly, - .symbols = ArrayList([]u8).init(self.a()), - }); - try self.link_libs_list.append(link_lib); - if (is_libc) { - self.libc_link_lib = link_lib; - } - return link_lib; - } - - fn a(self: Module) *mem.Allocator { - return self.loop.allocator; - } -}; - -fn printError(comptime format: []const u8, args: ...) !void { - var stderr_file = try std.io.getStdErr(); - var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file); - const out_stream = &stderr_file_out_stream.stream; - try out_stream.print(format, args); -} - -fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib { - if (optional_token_index) |token_index| { - const token = tree.tokens.at(token_index); - assert(token.id == Token.Id.Keyword_pub); - return Visib.Pub; - } else { - return Visib.Private; - } -} - -/// This declaration has been blessed as going into the final code generation. -pub async fn resolveDecl(module: *Module, decl: *Decl) !void { - if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { - decl.resolution.data = await (async generateDecl(module, decl) catch unreachable); - decl.resolution.resolve(); - return decl.resolution.data; - } else { - return (await (async decl.resolution.get() catch unreachable)).*; - } -} - -/// The function that actually does the generation. -async fn generateDecl(module: *Module, decl: *Decl) !void { - switch (decl.id) { - Decl.Id.Var => @panic("TODO"), - Decl.Id.Fn => { - const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl); - return await (async generateDeclFn(module, fn_decl) catch unreachable); - }, - Decl.Id.CompTime => @panic("TODO"), - } -} - -async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void { - const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl"); - - const fndef_scope = try Scope.FnDef.create(module, fn_decl.base.parent_scope); - defer fndef_scope.base.deref(module); - - // TODO actually look at the return type of the AST - const return_type = &Type.Void.get(module).base; - defer return_type.base.deref(module); - - const is_var_args = false; - const params = ([*]Type.Fn.Param)(undefined)[0..0]; - const fn_type = try Type.Fn.create(module, return_type, params, is_var_args); - defer fn_type.base.base.deref(module); - - var symbol_name = try std.Buffer.init(module.a(), fn_decl.base.name); - errdefer symbol_name.deinit(); - - const fn_val = try Value.Fn.create(module, fn_type, fndef_scope, symbol_name); - defer fn_val.base.deref(module); - - fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; - - const unanalyzed_code = (await (async ir.gen( - module, - body_node, - &fndef_scope.base, - Span.token(body_node.lastToken()), - fn_decl.base.parsed_file, - ) catch unreachable)) catch |err| switch (err) { - // This poison value should not cause the errdefers to run. It simply means - // that self.compile_errors is populated. - // TODO https://github.com/ziglang/zig/issues/769 - error.SemanticAnalysisFailed => return {}, - else => return err, - }; - defer unanalyzed_code.destroy(module.a()); - - if (module.verbose_ir) { - std.debug.warn("unanalyzed:\n"); - unanalyzed_code.dump(); - } - - const analyzed_code = (await (async ir.analyze( - module, - fn_decl.base.parsed_file, - unanalyzed_code, - null, - ) catch unreachable)) catch |err| switch (err) { - // This poison value should not cause the errdefers to run. It simply means - // that self.compile_errors is populated. - // TODO https://github.com/ziglang/zig/issues/769 - error.SemanticAnalysisFailed => return {}, - else => return err, - }; - errdefer analyzed_code.destroy(module.a()); - - if (module.verbose_ir) { - std.debug.warn("analyzed:\n"); - analyzed_code.dump(); - } - - // Kick off rendering to LLVM module, but it doesn't block the fn decl - // analysis from being complete. - try module.build_group.call(codegen.renderToLlvm, module, fn_val, analyzed_code); -} diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 8f8d016a7c..6fd6456b12 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -1,7 +1,7 @@ const std = @import("std"); const Allocator = mem.Allocator; const Decl = @import("decl.zig").Decl; -const Module = @import("module.zig").Module; +const Compilation = @import("compilation.zig").Compilation; const mem = std.mem; const ast = std.zig.ast; const Value = @import("value.zig").Value; @@ -16,17 +16,17 @@ pub const Scope = struct { base.ref_count += 1; } - pub fn deref(base: *Scope, module: *Module) void { + pub fn deref(base: *Scope, comp: *Compilation) void { base.ref_count -= 1; if (base.ref_count == 0) { - if (base.parent) |parent| parent.deref(module); + if (base.parent) |parent| parent.deref(comp); switch (base.id) { Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(), - Id.Block => @fieldParentPtr(Block, "base", base).destroy(module), - Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(module), - Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(module), - Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(module), - Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(module), + Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp), + Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp), + Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp), + Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp), + Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp), } } } @@ -61,8 +61,8 @@ pub const Scope = struct { table: Decl.Table, /// Creates a Decls scope with 1 reference - pub fn create(module: *Module, parent: ?*Scope) !*Decls { - const self = try module.a().create(Decls{ + pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls { + const self = try comp.a().create(Decls{ .base = Scope{ .id = Id.Decls, .parent = parent, @@ -70,9 +70,9 @@ pub const Scope = struct { }, .table = undefined, }); - errdefer module.a().destroy(self); + errdefer comp.a().destroy(self); - self.table = Decl.Table.init(module.a()); + self.table = Decl.Table.init(comp.a()); errdefer self.table.deinit(); if (parent) |p| p.ref(); @@ -94,8 +94,8 @@ pub const Scope = struct { is_comptime: *ir.Instruction, /// Creates a Block scope with 1 reference - pub fn create(module: *Module, parent: ?*Scope) !*Block { - const self = try module.a().create(Block{ + pub fn create(comp: *Compilation, parent: ?*Scope) !*Block { + const self = try comp.a().create(Block{ .base = Scope{ .id = Id.Block, .parent = parent, @@ -106,14 +106,14 @@ pub const Scope = struct { .end_block = undefined, .is_comptime = undefined, }); - errdefer module.a().destroy(self); + errdefer comp.a().destroy(self); if (parent) |p| p.ref(); return self; } - pub fn destroy(self: *Block, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Block, comp: *Compilation) void { + comp.a().destroy(self); } }; @@ -125,8 +125,8 @@ pub const Scope = struct { /// Creates a FnDef scope with 1 reference /// Must set the fn_val later - pub fn create(module: *Module, parent: ?*Scope) !*FnDef { - const self = try module.a().create(FnDef{ + pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef { + const self = try comp.a().create(FnDef{ .base = Scope{ .id = Id.FnDef, .parent = parent, @@ -140,8 +140,8 @@ pub const Scope = struct { return self; } - pub fn destroy(self: *FnDef, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *FnDef, comp: *Compilation) void { + comp.a().destroy(self); } }; @@ -149,8 +149,8 @@ pub const Scope = struct { base: Scope, /// Creates a CompTime scope with 1 reference - pub fn create(module: *Module, parent: ?*Scope) !*CompTime { - const self = try module.a().create(CompTime{ + pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime { + const self = try comp.a().create(CompTime{ .base = Scope{ .id = Id.CompTime, .parent = parent, @@ -162,8 +162,8 @@ pub const Scope = struct { return self; } - pub fn destroy(self: *CompTime, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *CompTime, comp: *Compilation) void { + comp.a().destroy(self); } }; @@ -179,12 +179,12 @@ pub const Scope = struct { /// Creates a Defer scope with 1 reference pub fn create( - module: *Module, + comp: *Compilation, parent: ?*Scope, kind: Kind, defer_expr_scope: *DeferExpr, ) !*Defer { - const self = try module.a().create(Defer{ + const self = try comp.a().create(Defer{ .base = Scope{ .id = Id.Defer, .parent = parent, @@ -193,7 +193,7 @@ pub const Scope = struct { .defer_expr_scope = defer_expr_scope, .kind = kind, }); - errdefer module.a().destroy(self); + errdefer comp.a().destroy(self); defer_expr_scope.base.ref(); @@ -201,9 +201,9 @@ pub const Scope = struct { return self; } - pub fn destroy(self: *Defer, module: *Module) void { - self.defer_expr_scope.base.deref(module); - module.a().destroy(self); + pub fn destroy(self: *Defer, comp: *Compilation) void { + self.defer_expr_scope.base.deref(comp); + comp.a().destroy(self); } }; @@ -212,8 +212,8 @@ pub const Scope = struct { expr_node: *ast.Node, /// Creates a DeferExpr scope with 1 reference - pub fn create(module: *Module, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr { - const self = try module.a().create(DeferExpr{ + pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr { + const self = try comp.a().create(DeferExpr{ .base = Scope{ .id = Id.DeferExpr, .parent = parent, @@ -221,14 +221,14 @@ pub const Scope = struct { }, .expr_node = expr_node, }); - errdefer module.a().destroy(self); + errdefer comp.a().destroy(self); if (parent) |p| p.ref(); return self; } - pub fn destroy(self: *DeferExpr, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *DeferExpr, comp: *Compilation) void { + comp.a().destroy(self); } }; }; diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index e609eb2791..3edb267ca9 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -2,11 +2,11 @@ const std = @import("std"); const mem = std.mem; const builtin = @import("builtin"); const Target = @import("target.zig").Target; -const Module = @import("module.zig").Module; +const Compilation = @import("compilation.zig").Compilation; const introspect = @import("introspect.zig"); const assertOrPanic = std.debug.assertOrPanic; const errmsg = @import("errmsg.zig"); -const EventLoopLocal = @import("module.zig").EventLoopLocal; +const EventLoopLocal = @import("compilation.zig").EventLoopLocal; test "compile errors" { var ctx: TestContext = undefined; @@ -100,42 +100,42 @@ pub const TestContext = struct { // TODO async I/O try std.io.writeFile(allocator, file1_path, source); - var module = try Module.create( + var comp = try Compilation.create( &self.event_loop_local, "test", file1_path, Target.Native, - Module.Kind.Obj, + Compilation.Kind.Obj, builtin.Mode.Debug, self.zig_lib_dir, self.zig_cache_dir, ); - errdefer module.destroy(); + errdefer comp.destroy(); - try module.build(); + try comp.build(); - try self.group.call(getModuleEvent, module, source, path, line, column, msg); + try self.group.call(getModuleEvent, comp, source, path, line, column, msg); } async fn getModuleEvent( - module: *Module, + comp: *Compilation, source: []const u8, path: []const u8, line: usize, column: usize, text: []const u8, ) !void { - defer module.destroy(); - const build_event = await (async module.events.get() catch unreachable); + defer comp.destroy(); + const build_event = await (async comp.events.get() catch unreachable); switch (build_event) { - Module.Event.Ok => { + Compilation.Event.Ok => { @panic("build incorrectly succeeded"); }, - Module.Event.Error => |err| { + Compilation.Event.Error => |err| { @panic("build incorrectly failed"); }, - Module.Event.Fail => |msgs| { + Compilation.Event.Fail => |msgs| { assertOrPanic(msgs.len != 0); for (msgs) |msg| { if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) { diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index e4c31018a3..8349047749 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -1,10 +1,10 @@ const std = @import("std"); const builtin = @import("builtin"); const Scope = @import("scope.zig").Scope; -const Module = @import("module.zig").Module; +const Compilation = @import("compilation.zig").Compilation; const Value = @import("value.zig").Value; const llvm = @import("llvm.zig"); -const CompilationUnit = @import("codegen.zig").CompilationUnit; +const ObjectFile = @import("codegen.zig").ObjectFile; pub const Type = struct { base: Value, @@ -12,63 +12,63 @@ pub const Type = struct { pub const Id = builtin.TypeId; - pub fn destroy(base: *Type, module: *Module) void { + pub fn destroy(base: *Type, comp: *Compilation) void { switch (base.id) { - Id.Struct => @fieldParentPtr(Struct, "base", base).destroy(module), - Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(module), - Id.Type => @fieldParentPtr(MetaType, "base", base).destroy(module), - Id.Void => @fieldParentPtr(Void, "base", base).destroy(module), - Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module), - Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module), - Id.Int => @fieldParentPtr(Int, "base", base).destroy(module), - Id.Float => @fieldParentPtr(Float, "base", base).destroy(module), - Id.Pointer => @fieldParentPtr(Pointer, "base", base).destroy(module), - Id.Array => @fieldParentPtr(Array, "base", base).destroy(module), - Id.ComptimeFloat => @fieldParentPtr(ComptimeFloat, "base", base).destroy(module), - Id.ComptimeInt => @fieldParentPtr(ComptimeInt, "base", base).destroy(module), - Id.Undefined => @fieldParentPtr(Undefined, "base", base).destroy(module), - Id.Null => @fieldParentPtr(Null, "base", base).destroy(module), - Id.Optional => @fieldParentPtr(Optional, "base", base).destroy(module), - Id.ErrorUnion => @fieldParentPtr(ErrorUnion, "base", base).destroy(module), - Id.ErrorSet => @fieldParentPtr(ErrorSet, "base", base).destroy(module), - Id.Enum => @fieldParentPtr(Enum, "base", base).destroy(module), - Id.Union => @fieldParentPtr(Union, "base", base).destroy(module), - Id.Namespace => @fieldParentPtr(Namespace, "base", base).destroy(module), - Id.Block => @fieldParentPtr(Block, "base", base).destroy(module), - Id.BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(module), - Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(module), - Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(module), - Id.Promise => @fieldParentPtr(Promise, "base", base).destroy(module), + Id.Struct => @fieldParentPtr(Struct, "base", base).destroy(comp), + Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp), + Id.Type => @fieldParentPtr(MetaType, "base", base).destroy(comp), + Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp), + Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp), + Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp), + Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp), + Id.Float => @fieldParentPtr(Float, "base", base).destroy(comp), + Id.Pointer => @fieldParentPtr(Pointer, "base", base).destroy(comp), + Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp), + Id.ComptimeFloat => @fieldParentPtr(ComptimeFloat, "base", base).destroy(comp), + Id.ComptimeInt => @fieldParentPtr(ComptimeInt, "base", base).destroy(comp), + Id.Undefined => @fieldParentPtr(Undefined, "base", base).destroy(comp), + Id.Null => @fieldParentPtr(Null, "base", base).destroy(comp), + Id.Optional => @fieldParentPtr(Optional, "base", base).destroy(comp), + Id.ErrorUnion => @fieldParentPtr(ErrorUnion, "base", base).destroy(comp), + Id.ErrorSet => @fieldParentPtr(ErrorSet, "base", base).destroy(comp), + Id.Enum => @fieldParentPtr(Enum, "base", base).destroy(comp), + Id.Union => @fieldParentPtr(Union, "base", base).destroy(comp), + Id.Namespace => @fieldParentPtr(Namespace, "base", base).destroy(comp), + Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp), + Id.BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(comp), + Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(comp), + Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(comp), + Id.Promise => @fieldParentPtr(Promise, "base", base).destroy(comp), } } - pub fn getLlvmType(base: *Type, cunit: *CompilationUnit) (error{OutOfMemory}!llvm.TypeRef) { + pub fn getLlvmType(base: *Type, ofile: *ObjectFile) (error{OutOfMemory}!llvm.TypeRef) { switch (base.id) { - Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(cunit), - Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(cunit), + Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(ofile), + Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(ofile), Id.Type => unreachable, Id.Void => unreachable, - Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(cunit), + Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(ofile), Id.NoReturn => unreachable, - Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(cunit), - Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(cunit), - Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(cunit), - Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(cunit), + Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(ofile), + Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(ofile), + Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(ofile), + Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(ofile), Id.ComptimeFloat => unreachable, Id.ComptimeInt => unreachable, Id.Undefined => unreachable, Id.Null => unreachable, - Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(cunit), - Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(cunit), - Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(cunit), - Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(cunit), - Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(cunit), + Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(ofile), + Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(ofile), + Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(ofile), + Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(ofile), + Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(ofile), Id.Namespace => unreachable, Id.Block => unreachable, - Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(cunit), + Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(ofile), Id.ArgTuple => unreachable, - Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(cunit), - Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(cunit), + Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(ofile), + Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(ofile), } } @@ -76,7 +76,7 @@ pub const Type = struct { std.debug.warn("{}", @tagName(base.id)); } - pub fn getAbiAlignment(base: *Type, module: *Module) u32 { + pub fn getAbiAlignment(base: *Type, comp: *Compilation) u32 { @panic("TODO getAbiAlignment"); } @@ -84,11 +84,11 @@ pub const Type = struct { base: Type, decls: *Scope.Decls, - pub fn destroy(self: *Struct, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Struct, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *Struct, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -104,12 +104,12 @@ pub const Type = struct { typeof: *Type, }; - pub fn create(module: *Module, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { - const result = try module.a().create(Fn{ + pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { + const result = try comp.a().create(Fn{ .base = Type{ .base = Value{ .id = Value.Id.Type, - .typeof = &MetaType.get(module).base, + .typeof = &MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .id = builtin.TypeId.Fn, @@ -118,7 +118,7 @@ pub const Type = struct { .params = params, .is_var_args = is_var_args, }); - errdefer module.a().destroy(result); + errdefer comp.a().destroy(result); result.return_type.base.ref(); for (result.params) |param| { @@ -127,23 +127,23 @@ pub const Type = struct { return result; } - pub fn destroy(self: *Fn, module: *Module) void { - self.return_type.base.deref(module); + pub fn destroy(self: *Fn, comp: *Compilation) void { + self.return_type.base.deref(comp); for (self.params) |param| { - param.typeof.base.deref(module); + param.typeof.base.deref(comp); } - module.a().destroy(self); + comp.a().destroy(self); } - pub fn getLlvmType(self: *Fn, cunit: *CompilationUnit) !llvm.TypeRef { + pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef { const llvm_return_type = switch (self.return_type.id) { - Type.Id.Void => llvm.VoidTypeInContext(cunit.context) orelse return error.OutOfMemory, - else => try self.return_type.getLlvmType(cunit), + Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory, + else => try self.return_type.getLlvmType(ofile), }; - const llvm_param_types = try cunit.a().alloc(llvm.TypeRef, self.params.len); - defer cunit.a().free(llvm_param_types); + const llvm_param_types = try ofile.a().alloc(llvm.TypeRef, self.params.len); + defer ofile.a().free(llvm_param_types); for (llvm_param_types) |*llvm_param_type, i| { - llvm_param_type.* = try self.params[i].typeof.getLlvmType(cunit); + llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile); } return llvm.FunctionType( @@ -160,13 +160,13 @@ pub const Type = struct { value: *Type, /// Adds 1 reference to the resulting type - pub fn get(module: *Module) *MetaType { - module.meta_type.base.base.ref(); - return module.meta_type; + pub fn get(comp: *Compilation) *MetaType { + comp.meta_type.base.base.ref(); + return comp.meta_type; } - pub fn destroy(self: *MetaType, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *MetaType, comp: *Compilation) void { + comp.a().destroy(self); } }; @@ -174,13 +174,13 @@ pub const Type = struct { base: Type, /// Adds 1 reference to the resulting type - pub fn get(module: *Module) *Void { - module.void_type.base.base.ref(); - return module.void_type; + pub fn get(comp: *Compilation) *Void { + comp.void_type.base.base.ref(); + return comp.void_type; } - pub fn destroy(self: *Void, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Void, comp: *Compilation) void { + comp.a().destroy(self); } }; @@ -188,16 +188,16 @@ pub const Type = struct { base: Type, /// Adds 1 reference to the resulting type - pub fn get(module: *Module) *Bool { - module.bool_type.base.base.ref(); - return module.bool_type; + pub fn get(comp: *Compilation) *Bool { + comp.bool_type.base.base.ref(); + return comp.bool_type; } - pub fn destroy(self: *Bool, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Bool, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *Bool, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -206,24 +206,24 @@ pub const Type = struct { base: Type, /// Adds 1 reference to the resulting type - pub fn get(module: *Module) *NoReturn { - module.noreturn_type.base.base.ref(); - return module.noreturn_type; + pub fn get(comp: *Compilation) *NoReturn { + comp.noreturn_type.base.base.ref(); + return comp.noreturn_type; } - pub fn destroy(self: *NoReturn, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *NoReturn, comp: *Compilation) void { + comp.a().destroy(self); } }; pub const Int = struct { base: Type, - pub fn destroy(self: *Int, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Int, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *Int, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -231,11 +231,11 @@ pub const Type = struct { pub const Float = struct { base: Type, - pub fn destroy(self: *Float, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Float, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *Float, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -256,12 +256,12 @@ pub const Type = struct { }; pub const Size = builtin.TypeInfo.Pointer.Size; - pub fn destroy(self: *Pointer, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Pointer, comp: *Compilation) void { + comp.a().destroy(self); } pub fn get( - module: *Module, + comp: *Compilation, elem_type: *Type, mut: Mut, vol: Vol, @@ -271,7 +271,7 @@ pub const Type = struct { @panic("TODO get pointer"); } - pub fn getLlvmType(self: *Pointer, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *Pointer, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -279,11 +279,11 @@ pub const Type = struct { pub const Array = struct { base: Type, - pub fn destroy(self: *Array, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Array, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *Array, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -291,43 +291,43 @@ pub const Type = struct { pub const ComptimeFloat = struct { base: Type, - pub fn destroy(self: *ComptimeFloat, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void { + comp.a().destroy(self); } }; pub const ComptimeInt = struct { base: Type, - pub fn destroy(self: *ComptimeInt, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *ComptimeInt, comp: *Compilation) void { + comp.a().destroy(self); } }; pub const Undefined = struct { base: Type, - pub fn destroy(self: *Undefined, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Undefined, comp: *Compilation) void { + comp.a().destroy(self); } }; pub const Null = struct { base: Type, - pub fn destroy(self: *Null, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Null, comp: *Compilation) void { + comp.a().destroy(self); } }; pub const Optional = struct { base: Type, - pub fn destroy(self: *Optional, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Optional, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *Optional, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -335,11 +335,11 @@ pub const Type = struct { pub const ErrorUnion = struct { base: Type, - pub fn destroy(self: *ErrorUnion, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *ErrorUnion, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *ErrorUnion, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -347,11 +347,11 @@ pub const Type = struct { pub const ErrorSet = struct { base: Type, - pub fn destroy(self: *ErrorSet, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *ErrorSet, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *ErrorSet, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -359,11 +359,11 @@ pub const Type = struct { pub const Enum = struct { base: Type, - pub fn destroy(self: *Enum, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Enum, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *Enum, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -371,11 +371,11 @@ pub const Type = struct { pub const Union = struct { base: Type, - pub fn destroy(self: *Union, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Union, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *Union, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -383,27 +383,27 @@ pub const Type = struct { pub const Namespace = struct { base: Type, - pub fn destroy(self: *Namespace, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Namespace, comp: *Compilation) void { + comp.a().destroy(self); } }; pub const Block = struct { base: Type, - pub fn destroy(self: *Block, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Block, comp: *Compilation) void { + comp.a().destroy(self); } }; pub const BoundFn = struct { base: Type, - pub fn destroy(self: *BoundFn, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *BoundFn, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *BoundFn, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -411,19 +411,19 @@ pub const Type = struct { pub const ArgTuple = struct { base: Type, - pub fn destroy(self: *ArgTuple, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *ArgTuple, comp: *Compilation) void { + comp.a().destroy(self); } }; pub const Opaque = struct { base: Type, - pub fn destroy(self: *Opaque, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Opaque, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *Opaque, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; @@ -431,11 +431,11 @@ pub const Type = struct { pub const Promise = struct { base: Type, - pub fn destroy(self: *Promise, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Promise, comp: *Compilation) void { + comp.a().destroy(self); } - pub fn getLlvmType(self: *Promise, cunit: *CompilationUnit) llvm.TypeRef { + pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef { @panic("TODO"); } }; diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 779e5c2e45..8c047b1513 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const Scope = @import("scope.zig").Scope; -const Module = @import("module.zig").Module; +const Compilation = @import("compilation.zig").Compilation; /// Values are ref-counted, heap-allocated, and copy-on-write /// If there is only 1 ref then write need not copy @@ -16,16 +16,16 @@ pub const Value = struct { } /// Thread-safe - pub fn deref(base: *Value, module: *Module) void { + pub fn deref(base: *Value, comp: *Compilation) void { if (base.ref_count.decr() == 1) { - base.typeof.base.deref(module); + base.typeof.base.deref(comp); switch (base.id) { - Id.Type => @fieldParentPtr(Type, "base", base).destroy(module), - Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(module), - Id.Void => @fieldParentPtr(Void, "base", base).destroy(module), - Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module), - Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module), - Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(module), + Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp), + Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp), + Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp), + Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp), + Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp), + Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp), } } } @@ -68,8 +68,8 @@ pub const Value = struct { /// Creates a Fn value with 1 ref /// Takes ownership of symbol_name - pub fn create(module: *Module, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn { - const self = try module.a().create(Fn{ + pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn { + const self = try comp.a().create(Fn{ .base = Value{ .id = Value.Id.Fn, .typeof = &fn_type.base, @@ -86,23 +86,23 @@ pub const Value = struct { return self; } - pub fn destroy(self: *Fn, module: *Module) void { - self.fndef_scope.base.deref(module); + pub fn destroy(self: *Fn, comp: *Compilation) void { + self.fndef_scope.base.deref(comp); self.symbol_name.deinit(); - module.a().destroy(self); + comp.a().destroy(self); } }; pub const Void = struct { base: Value, - pub fn get(module: *Module) *Void { - module.void_value.base.ref(); - return module.void_value; + pub fn get(comp: *Compilation) *Void { + comp.void_value.base.ref(); + return comp.void_value; } - pub fn destroy(self: *Void, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Void, comp: *Compilation) void { + comp.a().destroy(self); } }; @@ -110,31 +110,31 @@ pub const Value = struct { base: Value, x: bool, - pub fn get(module: *Module, x: bool) *Bool { + pub fn get(comp: *Compilation, x: bool) *Bool { if (x) { - module.true_value.base.ref(); - return module.true_value; + comp.true_value.base.ref(); + return comp.true_value; } else { - module.false_value.base.ref(); - return module.false_value; + comp.false_value.base.ref(); + return comp.false_value; } } - pub fn destroy(self: *Bool, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Bool, comp: *Compilation) void { + comp.a().destroy(self); } }; pub const NoReturn = struct { base: Value, - pub fn get(module: *Module) *NoReturn { - module.noreturn_value.base.ref(); - return module.noreturn_value; + pub fn get(comp: *Compilation) *NoReturn { + comp.noreturn_value.base.ref(); + return comp.noreturn_value; } - pub fn destroy(self: *NoReturn, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *NoReturn, comp: *Compilation) void { + comp.a().destroy(self); } }; @@ -147,8 +147,8 @@ pub const Value = struct { RunTime, }; - pub fn destroy(self: *Ptr, module: *Module) void { - module.a().destroy(self); + pub fn destroy(self: *Ptr, comp: *Compilation) void { + comp.a().destroy(self); } }; }; -- cgit v1.2.3 From 363f4facea7fac2d6cfeab9d1d276ecd8e8e4df0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Jul 2018 00:04:12 -0400 Subject: self-hosted: generate LLVM IR for simple function --- src-self-hosted/codegen.zig | 158 +++++++++++++++++++++++++++++++++++++++- src-self-hosted/compilation.zig | 6 +- src-self-hosted/ir.zig | 56 ++++++++++++-- src-self-hosted/llvm.zig | 64 +++++++++++++++- src-self-hosted/scope.zig | 32 ++++++++ src-self-hosted/type.zig | 75 +++++++++++++++++++ src-self-hosted/value.zig | 22 ++++++ std/event/future.zig | 10 +++ 8 files changed, 412 insertions(+), 11 deletions(-) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index a07485e74e..698f1e5b45 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -8,6 +8,7 @@ const ir = @import("ir.zig"); const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; const event = std.event; +const assert = std.debug.assert; pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void { fn_val.base.ref(); @@ -35,9 +36,23 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) try renderToLlvmModule(&ofile, fn_val, code); + // TODO module level assembly + //if (buf_len(&g->global_asm) != 0) { + // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); + //} + + // TODO + //ZigLLVMDIBuilderFinalize(g->dbuilder); + if (comp.verbose_llvm_ir) { llvm.DumpModule(ofile.module); } + + // verify the llvm module when safety is on + if (std.debug.runtime_safety) { + var error_ptr: ?[*]u8 = null; + _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr); + } } pub const ObjectFile = struct { @@ -55,5 +70,146 @@ pub const ObjectFile = struct { pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void { // TODO audit more of codegen.cpp:fn_llvm_value and port more logic const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile); - const llvm_fn = llvm.AddFunction(ofile.module, fn_val.symbol_name.ptr(), llvm_fn_type); + const llvm_fn = llvm.AddFunction( + ofile.module, + fn_val.symbol_name.ptr(), + llvm_fn_type, + ) orelse return error.OutOfMemory; + + const want_fn_safety = fn_val.block_scope.safety.get(ofile.comp); + if (want_fn_safety and ofile.comp.haveLibC()) { + try addLLVMFnAttr(ofile, llvm_fn, "sspstrong"); + try addLLVMFnAttrStr(ofile, llvm_fn, "stack-protector-buffer-size", "4"); + } + + // TODO + //if (fn_val.align_stack) |align_stack| { + // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack); + //} + + const fn_type = fn_val.base.typeof.cast(Type.Fn).?; + + try addLLVMFnAttr(ofile, llvm_fn, "nounwind"); + //add_uwtable_attr(g, fn_table_entry->llvm_value); + try addLLVMFnAttr(ofile, llvm_fn, "nobuiltin"); + + //if (g->build_mode == BuildModeDebug && fn_table_entry->fn_inline != FnInlineAlways) { + // ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim", "true"); + // ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim-non-leaf", nullptr); + //} + + //if (fn_table_entry->section_name) { + // LLVMSetSection(fn_table_entry->llvm_value, buf_ptr(fn_table_entry->section_name)); + //} + //if (fn_table_entry->align_bytes > 0) { + // LLVMSetAlignment(fn_table_entry->llvm_value, (unsigned)fn_table_entry->align_bytes); + //} else { + // // We'd like to set the best alignment for the function here, but on Darwin LLVM gives + // // "Cannot getTypeInfo() on a type that is unsized!" assertion failure when calling + // // any of the functions for getting alignment. Not specifying the alignment should + // // use the ABI alignment, which is fine. + //} + + //if (!type_has_bits(return_type)) { + // // nothing to do + //} else if (type_is_codegen_pointer(return_type)) { + // addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); + //} else if (handle_is_ptr(return_type) && + // calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc)) + //{ + // addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); + // addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull"); + //} + + // TODO set parameter attributes + + // TODO + //uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); + //if (err_ret_trace_arg_index != UINT32_MAX) { + // addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull"); + //} + + const cur_ret_ptr = if (fn_type.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null; + + // build all basic blocks + for (code.basic_block_list.toSlice()) |bb| { + bb.llvm_block = llvm.AppendBasicBlockInContext( + ofile.context, + llvm_fn, + bb.name_hint, + ) orelse return error.OutOfMemory; + } + const entry_bb = code.basic_block_list.at(0); + llvm.PositionBuilderAtEnd(ofile.builder, entry_bb.llvm_block); + + llvm.ClearCurrentDebugLocation(ofile.builder); + + // TODO set up error return tracing + // TODO allocate temporary stack values + // TODO create debug variable declarations for variables and allocate all local variables + // TODO finishing error return trace setup. we have to do this after all the allocas. + // TODO create debug variable declarations for parameters + + for (code.basic_block_list.toSlice()) |current_block| { + llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block); + for (current_block.instruction_list.toSlice()) |instruction| { + if (instruction.ref_count == 0 and !instruction.hasSideEffects()) continue; + + instruction.llvm_value = try instruction.render(ofile, fn_val); + } + current_block.llvm_exit_block = llvm.GetInsertBlock(ofile.builder); + } +} + +fn addLLVMAttr( + ofile: *ObjectFile, + val: llvm.ValueRef, + attr_index: llvm.AttributeIndex, + attr_name: []const u8, +) !void { + const kind_id = llvm.GetEnumAttributeKindForName(attr_name.ptr, attr_name.len); + assert(kind_id != 0); + const llvm_attr = llvm.CreateEnumAttribute(ofile.context, kind_id, 0) orelse return error.OutOfMemory; + llvm.AddAttributeAtIndex(val, attr_index, llvm_attr); +} + +fn addLLVMAttrStr( + ofile: *ObjectFile, + val: llvm.ValueRef, + attr_index: llvm.AttributeIndex, + attr_name: []const u8, + attr_val: []const u8, +) !void { + const llvm_attr = llvm.CreateStringAttribute( + ofile.context, + attr_name.ptr, + @intCast(c_uint, attr_name.len), + attr_val.ptr, + @intCast(c_uint, attr_val.len), + ) orelse return error.OutOfMemory; + llvm.AddAttributeAtIndex(val, attr_index, llvm_attr); +} + +fn addLLVMAttrInt( + val: llvm.ValueRef, + attr_index: llvm.AttributeIndex, + attr_name: []const u8, + attr_val: u64, +) !void { + const kind_id = llvm.GetEnumAttributeKindForName(attr_name.ptr, attr_name.len); + assert(kind_id != 0); + const llvm_attr = llvm.CreateEnumAttribute(ofile.context, kind_id, attr_val) orelse return error.OutOfMemory; + llvm.AddAttributeAtIndex(val, attr_index, llvm_attr); +} + +fn addLLVMFnAttr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8) !void { + return addLLVMAttr(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name); +} + +fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: []const u8) !void { + return addLLVMAttrStr(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val); +} + +fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void { + return addLLVMAttrInt(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val); } diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index cbda7861bc..1dbbf21206 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -606,6 +606,10 @@ pub const Compilation = struct { return error.Todo; } + pub fn haveLibC(self: *Compilation) bool { + return self.libc_link_lib != null; + } + pub fn addLinkLib(self: *Compilation, name: []const u8, provided_explicitly: bool) !*LinkLib { const is_libc = mem.eql(u8, name, "c"); @@ -741,7 +745,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { analyzed_code.dump(); } - // Kick off rendering to LLVM comp, but it doesn't block the fn decl + // Kick off rendering to LLVM module, but it doesn't block the fn decl // analysis from being complete. try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); } diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 22d5a067a7..0e0a4f9bf3 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -10,6 +10,8 @@ const assert = std.debug.assert; const Token = std.zig.Token; const ParsedFile = @import("parsed_file.zig").ParsedFile; const Span = @import("errmsg.zig").Span; +const llvm = @import("llvm.zig"); +const ObjectFile = @import("codegen.zig").ObjectFile; pub const LVal = enum { None, @@ -61,6 +63,9 @@ pub const Instruction = struct { /// the instruction that this one derives from in analysis parent: ?*Instruction, + /// populated durign codegen + llvm_value: ?llvm.ValueRef, + pub fn cast(base: *Instruction, comptime T: type) ?*T { if (base.id == comptime typeToId(T)) { return @fieldParentPtr(T, "base", base); @@ -108,14 +113,25 @@ pub const Instruction = struct { inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { const T = @field(Instruction, @memberName(Id, i)); - const new_inst = try @fieldParentPtr(T, "base", base).analyze(ira); - new_inst.linkToParent(base); - return new_inst; + return @fieldParentPtr(T, "base", base).analyze(ira); } } unreachable; } + pub fn render(base: *Instruction, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) { + switch (base.id) { + Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val), + Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val), + Id.Ref => @panic("TODO"), + Id.DeclVar => @panic("TODO"), + Id.CheckVoidStmt => @panic("TODO"), + Id.Phi => @panic("TODO"), + Id.Br => @panic("TODO"), + Id.AddImplicitReturnType => @panic("TODO"), + } + } + fn getAsParam(param: *Instruction) !*Instruction { const child = param.child orelse return error.SemanticAnalysisFailed; switch (child.val) { @@ -186,6 +202,10 @@ pub const Instruction = struct { new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() }; return new_inst; } + + pub fn render(self: *Const, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { + return self.base.val.KnownValue.getLlvmConst(ofile); + } }; pub const Return = struct { @@ -214,6 +234,18 @@ pub const Instruction = struct { return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value }); } + + pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) ?llvm.ValueRef { + const value = self.params.return_value.llvm_value; + const return_type = self.params.return_value.getKnownType(); + + if (return_type.handleIsPtr()) { + @panic("TODO"); + } else { + _ = llvm.BuildRet(ofile.builder, value); + } + return null; + } }; pub const Ref = struct { @@ -387,12 +419,16 @@ pub const Variable = struct { pub const BasicBlock = struct { ref_count: usize, - name_hint: []const u8, + name_hint: [*]const u8, // must be a C string literal debug_id: usize, scope: *Scope, instruction_list: std.ArrayList(*Instruction), ref_instruction: ?*Instruction, + /// for codegen + llvm_block: llvm.BasicBlockRef, + llvm_exit_block: llvm.BasicBlockRef, + /// the basic block that is derived from this one in analysis child: ?*BasicBlock, @@ -426,7 +462,7 @@ pub const Code = struct { pub fn dump(self: *Code) void { var bb_i: usize = 0; for (self.basic_block_list.toSliceConst()) |bb| { - std.debug.warn("{}_{}:\n", bb.name_hint, bb.debug_id); + std.debug.warn("{s}_{}:\n", bb.name_hint, bb.debug_id); for (bb.instruction_list.toSliceConst()) |instr| { std.debug.warn(" "); instr.dump(); @@ -475,7 +511,7 @@ pub const Builder = struct { } /// No need to clean up resources thanks to the arena allocator. - pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: []const u8) !*BasicBlock { + pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*]const u8) !*BasicBlock { const basic_block = try self.arena().create(BasicBlock{ .ref_count = 0, .name_hint = name_hint, @@ -485,6 +521,8 @@ pub const Builder = struct { .child = null, .parent = null, .ref_instruction = null, + .llvm_block = undefined, + .llvm_exit_block = undefined, }); self.next_debug_id += 1; return basic_block; @@ -600,7 +638,7 @@ pub const Builder = struct { if (block.label) |label| { block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena()); block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena()); - block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd"); + block_scope.end_block = try irb.createBasicBlock(parent_scope, c"BlockEnd"); block_scope.is_comptime = try irb.buildConstBool( parent_scope, Span.token(block.lbrace), @@ -777,6 +815,7 @@ pub const Builder = struct { .span = span, .child = null, .parent = null, + .llvm_value = undefined, }, .params = params, }); @@ -968,7 +1007,7 @@ pub async fn gen( var irb = try Builder.init(comp, parsed_file); errdefer irb.abort(); - const entry_block = try irb.createBasicBlock(scope, "Entry"); + const entry_block = try irb.createBasicBlock(scope, c"Entry"); entry_block.ref(); // Entry block gets a reference because we enter it to begin. try irb.setCursorAtEndAndAppendBlock(entry_block); @@ -1013,6 +1052,7 @@ pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Co } const return_inst = try old_instruction.analyze(&ira); + return_inst.linkToParent(old_instruction); // Note: if we ever modify the above to handle error.CompileError by continuing analysis, // then here we want to check if ira.isCompTime() and return early if true diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index b815f75b05..13480dc2c6 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -2,29 +2,91 @@ const builtin = @import("builtin"); const c = @import("c.zig"); const assert = @import("std").debug.assert; +pub const AttributeIndex = c_uint; +pub const Bool = c_int; + pub const BuilderRef = removeNullability(c.LLVMBuilderRef); pub const ContextRef = removeNullability(c.LLVMContextRef); pub const ModuleRef = removeNullability(c.LLVMModuleRef); pub const ValueRef = removeNullability(c.LLVMValueRef); pub const TypeRef = removeNullability(c.LLVMTypeRef); +pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef); +pub const AttributeRef = removeNullability(c.LLVMAttributeRef); +pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex; pub const AddFunction = c.LLVMAddFunction; +pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; +pub const ConstInt = c.LLVMConstInt; +pub const ConstStringInContext = c.LLVMConstStringInContext; +pub const ConstStructInContext = c.LLVMConstStructInContext; pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext; +pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute; +pub const CreateStringAttribute = c.LLVMCreateStringAttribute; pub const DisposeBuilder = c.LLVMDisposeBuilder; pub const DisposeModule = c.LLVMDisposeModule; +pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext; pub const DumpModule = c.LLVMDumpModule; +pub const FP128TypeInContext = c.LLVMFP128TypeInContext; +pub const FloatTypeInContext = c.LLVMFloatTypeInContext; +pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName; +pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext; +pub const HalfTypeInContext = c.LLVMHalfTypeInContext; +pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext; +pub const Int128TypeInContext = c.LLVMInt128TypeInContext; +pub const Int16TypeInContext = c.LLVMInt16TypeInContext; +pub const Int1TypeInContext = c.LLVMInt1TypeInContext; +pub const Int32TypeInContext = c.LLVMInt32TypeInContext; +pub const Int64TypeInContext = c.LLVMInt64TypeInContext; +pub const Int8TypeInContext = c.LLVMInt8TypeInContext; +pub const IntPtrTypeForASInContext = c.LLVMIntPtrTypeForASInContext; +pub const IntPtrTypeInContext = c.LLVMIntPtrTypeInContext; +pub const IntTypeInContext = c.LLVMIntTypeInContext; +pub const LabelTypeInContext = c.LLVMLabelTypeInContext; +pub const MDNodeInContext = c.LLVMMDNodeInContext; +pub const MDStringInContext = c.LLVMMDStringInContext; +pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext; pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; +pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext; +pub const StructTypeInContext = c.LLVMStructTypeInContext; +pub const TokenTypeInContext = c.LLVMTokenTypeInContext; pub const VoidTypeInContext = c.LLVMVoidTypeInContext; +pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; +pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; +pub const ConstAllOnes = c.LLVMConstAllOnes; +pub const ConstNull = c.LLVMConstNull; + +pub const VerifyModule = LLVMVerifyModule; +extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool; + +pub const GetInsertBlock = LLVMGetInsertBlock; +extern fn LLVMGetInsertBlock(Builder: BuilderRef) BasicBlockRef; pub const FunctionType = LLVMFunctionType; extern fn LLVMFunctionType( ReturnType: TypeRef, ParamTypes: [*]TypeRef, ParamCount: c_uint, - IsVarArg: c_int, + IsVarArg: Bool, ) ?TypeRef; +pub const GetParam = LLVMGetParam; +extern fn LLVMGetParam(Fn: ValueRef, Index: c_uint) ValueRef; + +pub const AppendBasicBlockInContext = LLVMAppendBasicBlockInContext; +extern fn LLVMAppendBasicBlockInContext(C: ContextRef, Fn: ValueRef, Name: [*]const u8) ?BasicBlockRef; + +pub const PositionBuilderAtEnd = LLVMPositionBuilderAtEnd; +extern fn LLVMPositionBuilderAtEnd(Builder: BuilderRef, Block: BasicBlockRef) void; + +pub const AbortProcessAction = VerifierFailureAction.LLVMAbortProcessAction; +pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction; +pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction; +pub const VerifierFailureAction = c.LLVMVerifierFailureAction; + fn removeNullability(comptime T: type) type { comptime assert(@typeId(T) == builtin.TypeId.Optional); return T.Child; } + +pub const BuildRet = LLVMBuildRet; +extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef; diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 6fd6456b12..4326617fa0 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const Allocator = mem.Allocator; const Decl = @import("decl.zig").Decl; const Compilation = @import("compilation.zig").Compilation; @@ -6,6 +7,7 @@ const mem = std.mem; const ast = std.zig.ast; const Value = @import("value.zig").Value; const ir = @import("ir.zig"); +const Span = @import("errmsg.zig").Span; pub const Scope = struct { id: Id, @@ -93,6 +95,35 @@ pub const Scope = struct { end_block: *ir.BasicBlock, is_comptime: *ir.Instruction, + safety: Safety, + + const Safety = union(enum) { + Auto, + Manual: Manual, + + const Manual = struct { + /// the source span that disabled the safety value + span: Span, + + /// whether safety is enabled + enabled: bool, + }; + + fn get(self: Safety, comp: *Compilation) bool { + return switch (self) { + Safety.Auto => switch (comp.build_mode) { + builtin.Mode.Debug, + builtin.Mode.ReleaseSafe, + => true, + builtin.Mode.ReleaseFast, + builtin.Mode.ReleaseSmall, + => false, + }, + @TagType(Safety).Manual => |man| man.enabled, + }; + } + }; + /// Creates a Block scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope) !*Block { const self = try comp.a().create(Block{ @@ -105,6 +136,7 @@ pub const Scope = struct { .incoming_blocks = undefined, .end_block = undefined, .is_comptime = undefined, + .safety = Safety.Auto, }); errdefer comp.a().destroy(self); diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 8349047749..670547cce2 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -72,6 +72,81 @@ pub const Type = struct { } } + pub fn handleIsPtr(base: *Type) bool { + switch (base.id) { + Id.Type, + Id.ComptimeFloat, + Id.ComptimeInt, + Id.Undefined, + Id.Null, + Id.Namespace, + Id.Block, + Id.BoundFn, + Id.ArgTuple, + Id.Opaque, + => unreachable, + + Id.NoReturn, + Id.Void, + Id.Bool, + Id.Int, + Id.Float, + Id.Pointer, + Id.ErrorSet, + Id.Enum, + Id.Fn, + Id.Promise, + => return false, + + Id.Struct => @panic("TODO"), + Id.Array => @panic("TODO"), + Id.Optional => @panic("TODO"), + Id.ErrorUnion => @panic("TODO"), + Id.Union => @panic("TODO"), + } + } + + pub fn hasBits(base: *Type) bool { + switch (base.id) { + Id.Type, + Id.ComptimeFloat, + Id.ComptimeInt, + Id.Undefined, + Id.Null, + Id.Namespace, + Id.Block, + Id.BoundFn, + Id.ArgTuple, + Id.Opaque, + => unreachable, + + Id.Void, + Id.NoReturn, + => return false, + + Id.Bool, + Id.Int, + Id.Float, + Id.Fn, + Id.Promise, + => return true, + + Id.ErrorSet => @panic("TODO"), + Id.Enum => @panic("TODO"), + Id.Pointer => @panic("TODO"), + Id.Struct => @panic("TODO"), + Id.Array => @panic("TODO"), + Id.Optional => @panic("TODO"), + Id.ErrorUnion => @panic("TODO"), + Id.Union => @panic("TODO"), + } + } + + pub fn cast(base: *Type, comptime T: type) ?*T { + if (base.id != @field(Id, @typeName(T))) return null; + return @fieldParentPtr(T, "base", base); + } + pub fn dump(base: *const Type) void { std.debug.warn("{}", @tagName(base.id)); } diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 8c047b1513..e3b91d2807 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -2,6 +2,8 @@ const std = @import("std"); const builtin = @import("builtin"); const Scope = @import("scope.zig").Scope; const Compilation = @import("compilation.zig").Compilation; +const ObjectFile = @import("codegen.zig").ObjectFile; +const llvm = @import("llvm.zig"); /// Values are ref-counted, heap-allocated, and copy-on-write /// If there is only 1 ref then write need not copy @@ -39,6 +41,17 @@ pub const Value = struct { std.debug.warn("{}", @tagName(base.id)); } + pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) { + switch (base.id) { + Id.Type => unreachable, + Id.Fn => @panic("TODO"), + Id.Void => return null, + Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile), + Id.NoReturn => unreachable, + Id.Ptr => @panic("TODO"), + } + } + pub const Id = enum { Type, Fn, @@ -123,6 +136,15 @@ pub const Value = struct { pub fn destroy(self: *Bool, comp: *Compilation) void { comp.a().destroy(self); } + + pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef { + const llvm_type = llvm.Int1TypeInContext(ofile.context); + if (self.x) { + return llvm.ConstAllOnes(llvm_type); + } else { + return llvm.ConstNull(llvm_type); + } + } }; pub const NoReturn = struct { diff --git a/std/event/future.zig b/std/event/future.zig index 23fa570c8f..0f27b4131b 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -40,6 +40,16 @@ pub fn Future(comptime T: type) type { return &self.data; } + /// Gets the data without waiting for it. If it's available, a pointer is + /// returned. Otherwise, null is returned. + pub fn getOrNull(self: *Self) ?*T { + if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { + return &self.data; + } else { + return null; + } + } + /// Make the data become available. May be called only once. /// Before calling this, modify the `data` property. pub fn resolve(self: *Self) void { -- cgit v1.2.3 From e9a03cccf375f11aa4e0a8a3515e499c88d05cde Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Jul 2018 10:53:15 -0400 Subject: all integer sizes are available as primitives * fix wrong implicit cast for `@IntType` bit_count parameter. * fix incorrect docs for `@IntType` bit_count parameter. closes #1242 closes #745 closes #1240 --- doc/langref.html.in | 12 ++++---- src/all_types.hpp | 4 --- src/analyze.cpp | 73 +++++++++++++++++++++---------------------------- src/analyze.hpp | 3 +- src/codegen.cpp | 13 --------- src/ir.cpp | 21 ++++++++------ src/translate_c.cpp | 2 +- std/buffer.zig | 2 -- std/crypto/sha1.zig | 2 -- std/json.zig | 3 -- std/math/big/int.zig | 1 - std/math/exp2.zig | 24 ++++++++-------- std/math/index.zig | 2 +- std/os/time.zig | 1 - test/cases/misc.zig | 5 ---- test/cases/struct.zig | 1 - test/compile_errors.zig | 9 ++++++ 17 files changed, 74 insertions(+), 104 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index ea672ccb17..46b325832b 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2310,11 +2310,11 @@ test "while loop continue expression" { } test "while loop continue expression, more complicated" { - var i1: usize = 1; - var j1: usize = 1; - while (i1 * j1 < 2000) : ({ i1 *= 2; j1 *= 3; }) { - const my_ij1 = i1 * j1; - assert(my_ij1 < 2000); + var i: usize = 1; + var j: usize = 1; + while (i * j < 2000) : ({ i *= 2; j *= 3; }) { + const my_ij = i * j; + assert(my_ij < 2000); } } {#code_end#} @@ -5424,7 +5424,7 @@ fn add(a: i32, b: i32) i32 { return a + b; } {#header_close#} {#header_open|@IntType#} -

    @IntType(comptime is_signed: bool, comptime bit_count: u8) type
    +
    @IntType(comptime is_signed: bool, comptime bit_count: u32) type

    This function returns an integer type with the given signness and bit count.

    diff --git a/src/all_types.hpp b/src/all_types.hpp index 2da0677e1b..bcd6a04cc3 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1587,7 +1587,6 @@ struct CodeGen { struct { TypeTableEntry *entry_bool; - TypeTableEntry *entry_int[2][12]; // [signed,unsigned][2,3,4,5,6,7,8,16,29,32,64,128] TypeTableEntry *entry_c_int[CIntTypeCount]; TypeTableEntry *entry_c_longdouble; TypeTableEntry *entry_c_void; @@ -1596,12 +1595,9 @@ struct CodeGen { TypeTableEntry *entry_u32; TypeTableEntry *entry_u29; TypeTableEntry *entry_u64; - TypeTableEntry *entry_u128; TypeTableEntry *entry_i8; - TypeTableEntry *entry_i16; TypeTableEntry *entry_i32; TypeTableEntry *entry_i64; - TypeTableEntry *entry_i128; TypeTableEntry *entry_isize; TypeTableEntry *entry_usize; TypeTableEntry *entry_f16; diff --git a/src/analyze.cpp b/src/analyze.cpp index 5635cce411..2ace893508 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3227,9 +3227,8 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { } { - auto entry = g->primitive_type_table.maybe_get(tld->name); - if (entry) { - TypeTableEntry *type = entry->value; + TypeTableEntry *type = get_primitive_type(g, tld->name); + if (type != nullptr) { add_node_error(g, tld->source_node, buf_sprintf("declaration shadows type '%s'", buf_ptr(&type->name))); } @@ -3474,9 +3473,8 @@ VariableTableEntry *add_variable(CodeGen *g, AstNode *source_node, Scope *parent add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); variable_entry->value->type = g->builtin_types.entry_invalid; } else { - auto primitive_table_entry = g->primitive_type_table.maybe_get(name); - if (primitive_table_entry) { - TypeTableEntry *type = primitive_table_entry->value; + TypeTableEntry *type = get_primitive_type(g, name); + if (type != nullptr) { add_node_error(g, source_node, buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); variable_entry->value->type = g->builtin_types.entry_invalid; @@ -4307,43 +4305,7 @@ void semantic_analyze(CodeGen *g) { } } -TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_bits) { - size_t index; - if (size_in_bits == 2) { - index = 0; - } else if (size_in_bits == 3) { - index = 1; - } else if (size_in_bits == 4) { - index = 2; - } else if (size_in_bits == 5) { - index = 3; - } else if (size_in_bits == 6) { - index = 4; - } else if (size_in_bits == 7) { - index = 5; - } else if (size_in_bits == 8) { - index = 6; - } else if (size_in_bits == 16) { - index = 7; - } else if (size_in_bits == 29) { - index = 8; - } else if (size_in_bits == 32) { - index = 9; - } else if (size_in_bits == 64) { - index = 10; - } else if (size_in_bits == 128) { - index = 11; - } else { - return nullptr; - } - return &g->builtin_types.entry_int[is_signed ? 0 : 1][index]; -} - TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { - TypeTableEntry **common_entry = get_int_type_ptr(g, is_signed, size_in_bits); - if (common_entry) - return *common_entry; - TypeId type_id = {}; type_id.id = TypeTableEntryIdInt; type_id.data.integer.is_signed = is_signed; @@ -4953,6 +4915,8 @@ bool fn_eval_cacheable(Scope *scope, TypeTableEntry *return_type) { while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; + if (type_is_invalid(var_scope->var->value->type)) + return false; if (can_mutate_comptime_var_state(var_scope->var->value)) return false; } else if (scope->id == ScopeIdFnDef) { @@ -6310,3 +6274,28 @@ bool type_can_fail(TypeTableEntry *type_entry) { bool fn_type_can_fail(FnTypeId *fn_type_id) { return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync; } + +TypeTableEntry *get_primitive_type(CodeGen *g, Buf *name) { + if (buf_len(name) >= 2) { + uint8_t first_c = buf_ptr(name)[0]; + if (first_c == 'i' || first_c == 'u') { + for (size_t i = 1; i < buf_len(name); i += 1) { + uint8_t c = buf_ptr(name)[i]; + if (c < '0' || c > '9') { + goto not_integer; + } + } + bool is_signed = (first_c == 'i'); + uint32_t bit_count = atoi(buf_ptr(name) + 1); + return get_int_type(g, is_signed, bit_count); + } + } + +not_integer: + + auto primitive_table_entry = g->primitive_type_table.maybe_get(name); + if (primitive_table_entry != nullptr) { + return primitive_table_entry->value; + } + return nullptr; +} diff --git a/src/analyze.hpp b/src/analyze.hpp index 5168509fe0..e4dfae4ecb 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -19,7 +19,6 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count); uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry); uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry); -TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_bits); TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits); TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type); TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type); @@ -204,4 +203,6 @@ bool type_can_fail(TypeTableEntry *type_entry); bool fn_eval_cacheable(Scope *scope, TypeTableEntry *return_type); AstNode *type_decl_node(TypeTableEntry *type_entry); +TypeTableEntry *get_primitive_type(CodeGen *g, Buf *name); + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 0bcc211164..c38ae1036a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6161,16 +6161,6 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_arg_tuple = entry; } - for (size_t int_size_i = 0; int_size_i < array_length(int_sizes_in_bits); int_size_i += 1) { - uint8_t size_in_bits = int_sizes_in_bits[int_size_i]; - for (size_t is_sign_i = 0; is_sign_i < array_length(is_signed_list); is_sign_i += 1) { - bool is_signed = is_signed_list[is_sign_i]; - TypeTableEntry *entry = make_int_type(g, is_signed, size_in_bits); - g->primitive_type_table.put(&entry->name, entry); - get_int_type_ptr(g, is_signed, size_in_bits)[0] = entry; - } - } - for (size_t i = 0; i < array_length(c_int_type_infos); i += 1) { const CIntTypeInfo *info = &c_int_type_infos[i]; uint32_t size_in_bits = target_c_type_size_in_bits(&g->zig_target, info->id); @@ -6286,12 +6276,9 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_u29 = get_int_type(g, false, 29); g->builtin_types.entry_u32 = get_int_type(g, false, 32); g->builtin_types.entry_u64 = get_int_type(g, false, 64); - g->builtin_types.entry_u128 = get_int_type(g, false, 128); g->builtin_types.entry_i8 = get_int_type(g, true, 8); - g->builtin_types.entry_i16 = get_int_type(g, true, 16); g->builtin_types.entry_i32 = get_int_type(g, true, 32); g->builtin_types.entry_i64 = get_int_type(g, true, 64); - g->builtin_types.entry_i128 = get_int_type(g, true, 128); { g->builtin_types.entry_c_void = get_opaque_type(g, nullptr, nullptr, "c_void"); diff --git a/src/ir.cpp b/src/ir.cpp index 3007bbcf64..0804134d2a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3217,9 +3217,8 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); variable_entry->value->type = codegen->builtin_types.entry_invalid; } else { - auto primitive_table_entry = codegen->primitive_type_table.maybe_get(name); - if (primitive_table_entry) { - TypeTableEntry *type = primitive_table_entry->value; + TypeTableEntry *type = get_primitive_type(codegen, name); + if (type != nullptr) { add_node_error(codegen, node, buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); variable_entry->value->type = codegen->builtin_types.entry_invalid; @@ -3661,9 +3660,9 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, return &const_instruction->base; } - auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(variable_name); - if (primitive_table_entry) { - IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_table_entry->value); + TypeTableEntry *primitive_type = get_primitive_type(irb->codegen, variable_name); + if (primitive_type != nullptr) { + IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); if (lval == LValPtr) { return ir_build_ref(irb, scope, node, value, false, false); } else { @@ -10691,11 +10690,11 @@ static bool ir_resolve_align(IrAnalyze *ira, IrInstruction *value, uint32_t *out return true; } -static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) { +static bool ir_resolve_unsigned(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *int_type, uint64_t *out) { if (type_is_invalid(value->value.type)) return false; - IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_usize); + IrInstruction *casted_value = ir_implicit_cast(ira, value, int_type); if (type_is_invalid(casted_value->value.type)) return false; @@ -10707,6 +10706,10 @@ static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out return true; } +static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) { + return ir_resolve_unsigned(ira, value, ira->codegen->builtin_types.entry_usize, out); +} + static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *value, bool *out) { if (type_is_invalid(value->value.type)) return false; @@ -18025,7 +18028,7 @@ static TypeTableEntry *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstruc IrInstruction *bit_count_value = instruction->bit_count->other; uint64_t bit_count; - if (!ir_resolve_usize(ira, bit_count_value, &bit_count)) + if (!ir_resolve_unsigned(ira, bit_count_value, ira->codegen->builtin_types.entry_u32, &bit_count)) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); diff --git a/src/translate_c.cpp b/src/translate_c.cpp index db46d31c5b..267a716a9d 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -427,7 +427,7 @@ static AstNode *get_global(Context *c, Buf *name) { if (entry) return entry->value; } - if (c->codegen->primitive_type_table.maybe_get(name) != nullptr) { + if (get_primitive_type(c->codegen, name) != nullptr) { return trans_create_node_symbol(c, name); } return nullptr; diff --git a/std/buffer.zig b/std/buffer.zig index 0d82918580..aff7fa86ef 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -5,8 +5,6 @@ const Allocator = mem.Allocator; const assert = debug.assert; const ArrayList = std.ArrayList; -const fmt = std.fmt; - /// A buffer that allocates memory and maintains a null byte at the end. pub const Buffer = struct { list: ArrayList(u8), diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig index 5c91590c88..451cfb3122 100644 --- a/std/crypto/sha1.zig +++ b/std/crypto/sha1.zig @@ -4,8 +4,6 @@ const endian = @import("../endian.zig"); const debug = @import("../debug/index.zig"); const builtin = @import("builtin"); -pub const u160 = @IntType(false, 160); - const RoundParam = struct { a: usize, b: usize, diff --git a/std/json.zig b/std/json.zig index 8986034fb4..e62d5a3466 100644 --- a/std/json.zig +++ b/std/json.zig @@ -6,9 +6,6 @@ const std = @import("index.zig"); const debug = std.debug; const mem = std.mem; -const u1 = @IntType(false, 1); -const u256 = @IntType(false, 256); - // A single token slice into the parent string. // // Use `token.slice()` on the input at the current position to get the current slice. diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 29673538eb..caa9d0a7ed 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -996,7 +996,6 @@ pub const Int = struct { // They will still run on larger than this and should pass, but the multi-limb code-paths // may be untested in some cases. -const u256 = @IntType(false, 256); const al = debug.global_allocator; test "big.int comptime_int set" { diff --git a/std/math/exp2.zig b/std/math/exp2.zig index 90ea088181..d590b0b60b 100644 --- a/std/math/exp2.zig +++ b/std/math/exp2.zig @@ -75,18 +75,18 @@ fn exp2_32(x: f32) f32 { } var uf = x + redux; - var i0 = @bitCast(u32, uf); - i0 += tblsiz / 2; + var i_0 = @bitCast(u32, uf); + i_0 += tblsiz / 2; - const k = i0 / tblsiz; + const k = i_0 / tblsiz; // NOTE: musl relies on undefined overflow shift behaviour. Appears that this produces the // intended result but should confirm how GCC/Clang handle this to ensure. const uk = @bitCast(f64, u64(0x3FF + k) << 52); - i0 &= tblsiz - 1; + i_0 &= tblsiz - 1; uf -= redux; const z: f64 = x - uf; - var r: f64 = exp2ft[i0]; + var r: f64 = exp2ft[i_0]; const t: f64 = r * z; r = r + t * (P1 + z * P2) + t * (z * z) * (P3 + z * P4); return @floatCast(f32, r * uk); @@ -401,18 +401,18 @@ fn exp2_64(x: f64) f64 { // reduce x var uf = x + redux; // NOTE: musl performs an implicit 64-bit to 32-bit u32 truncation here - var i0 = @truncate(u32, @bitCast(u64, uf)); - i0 += tblsiz / 2; + var i_0 = @truncate(u32, @bitCast(u64, uf)); + i_0 += tblsiz / 2; - const k: u32 = i0 / tblsiz * tblsiz; + const k: u32 = i_0 / tblsiz * tblsiz; const ik = @bitCast(i32, k / tblsiz); - i0 %= tblsiz; + i_0 %= tblsiz; uf -= redux; - // r = exp2(y) = exp2t[i0] * p(z - eps[i]) + // r = exp2(y) = exp2t[i_0] * p(z - eps[i]) var z = x - uf; - const t = exp2dt[2 * i0]; - z -= exp2dt[2 * i0 + 1]; + const t = exp2dt[2 * i_0]; + z -= exp2dt[2 * i_0 + 1]; const r = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5)))); return math.scalbn(r, ik); diff --git a/std/math/index.zig b/std/math/index.zig index 17b66f5568..e5fd0f3685 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -354,7 +354,7 @@ test "math.rotl" { pub fn Log2Int(comptime T: type) type { // comptime ceil log2 - comptime var count: usize = 0; + comptime var count = 0; comptime var s = T.bit_count - 1; inline while (s != 0) : (s >>= 1) { count += 1; diff --git a/std/os/time.zig b/std/os/time.zig index 73ba5bba82..795605d7a9 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -25,7 +25,6 @@ pub fn sleep(seconds: usize, nanoseconds: usize) void { } } -const u63 = @IntType(false, 63); pub fn posixSleep(seconds: u63, nanoseconds: u63) void { var req = posix.timespec{ .tv_sec = seconds, diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 0f181a7b4e..1c0189571b 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -58,11 +58,6 @@ test "floating point primitive bit counts" { assert(f64.bit_count == 64); } -const u1 = @IntType(false, 1); -const u63 = @IntType(false, 63); -const i1 = @IntType(true, 1); -const i63 = @IntType(true, 63); - test "@minValue and @maxValue" { assert(@maxValue(u1) == 1); assert(@maxValue(u8) == 255); diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 2941ecb56a..20d46999d5 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -240,7 +240,6 @@ fn getC(data: *const BitField1) u2 { return data.c; } -const u24 = @IntType(false, 24); const Foo24Bits = packed struct { field: u24, }; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 58c73b8ae4..d5582b1584 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "optional pointer to void in extern struct", + \\comptime { + \\ _ = @IntType(false, @maxValue(u32) + 1); + \\} + , + ".tmp_source.zig:2:40: error: integer value 4294967296 cannot be implicitly casted to type 'u32'", + ); + cases.add( "optional pointer to void in extern struct", \\const Foo = extern struct { -- cgit v1.2.3 From d3ce9d0643421da77063472cb1124123cda753bb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Jul 2018 11:59:37 -0400 Subject: codegen: remove unused variable --- src/codegen.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index c38ae1036a..f8801ea132 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6062,21 +6062,6 @@ static void do_code_gen(CodeGen *g) { } } -static const uint8_t int_sizes_in_bits[] = { - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 16, - 29, - 32, - 64, - 128, -}; - struct CIntTypeInfo { CIntType id; const char *name; -- cgit v1.2.3 From 9b56efc957dfb70ab70a74df3da55a9215e27b8d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Jul 2018 17:45:18 -0400 Subject: remove std.ArrayList.removeOrError function --- std/array_list.zig | 9 --------- 1 file changed, 9 deletions(-) diff --git a/std/array_list.zig b/std/array_list.zig index 8d7bde46a1..2c8b5070a7 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -113,15 +113,6 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return old_item; } - pub fn removeOrError(self: *Self, n: usize) !T { - if (n >= self.len) return error.OutOfBounds; - if (self.len - 1 == n) return self.pop(); - - var old_item = self.at(n); - try self.setOrError(n, self.pop()); - return old_item; - } - pub fn appendSlice(self: *Self, items: []align(A) const T) !void { try self.ensureCapacity(self.len + items.len); mem.copy(T, self.items[self.len..], items); -- cgit v1.2.3 From 0fa24b6b7568557c29c9b3ee213ce2b06fcd6367 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Jul 2018 19:26:15 -0400 Subject: allow implicit cast of undefined to optional --- src/ir.cpp | 2 +- test/cases/cast.zig | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 0804134d2a..35b6b4cef4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9408,7 +9408,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc if (type_is_invalid(casted_payload->value.type)) return ira->codegen->invalid_instruction; - ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefBad); + ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefOk); if (!val) return ira->codegen->invalid_instruction; diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 5688d90e11..63cc6313e1 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -468,3 +468,20 @@ test "@intCast i32 to u7" { var z = x >> @intCast(u7, y); assert(z == 0xff); } + +test "implicit cast undefined to optional" { + assert(MakeType(void).getNull() == null); + assert(MakeType(void).getNonNull() != null); +} + +fn MakeType(comptime T: type) type { + return struct { + fn getNull() ?T { + return null; + } + + fn getNonNull() ?T { + return T(undefined); + } + }; +} -- cgit v1.2.3 From 97bfeac13f89e1b5a22fcd7d4705341b4c3e1950 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Jul 2018 20:52:50 -0400 Subject: self-hosted: create tmp dir for .o files and emit .o file for fn --- CMakeLists.txt | 1 + src-self-hosted/codegen.zig | 86 ++++++++++- src-self-hosted/compilation.zig | 335 ++++++++++++++++++++++++++++++++-------- src-self-hosted/ir.zig | 10 +- src-self-hosted/llvm.zig | 75 ++++++++- src-self-hosted/main.zig | 8 +- src-self-hosted/package.zig | 29 ++++ src-self-hosted/scope.zig | 32 ++-- src-self-hosted/target.zig | 116 ++++++++++---- src-self-hosted/test.zig | 3 +- src-self-hosted/type.zig | 58 +++---- src-self-hosted/value.zig | 41 ++++- src/zig_llvm.cpp | 5 + src/zig_llvm.h | 3 +- std/atomic/int.zig | 4 + std/buffer.zig | 13 ++ std/dwarf.zig | 37 +++++ std/event/future.zig | 39 ++++- std/index.zig | 3 + std/lazy_init.zig | 85 ++++++++++ 20 files changed, 808 insertions(+), 175 deletions(-) create mode 100644 src-self-hosted/package.zig create mode 100644 std/lazy_init.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index e606855555..0e7c1df350 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -479,6 +479,7 @@ set(ZIG_STD_FILES "index.zig" "io.zig" "json.zig" + "lazy_init.zig" "linked_list.zig" "macho.zig" "math/acos.zig" diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 698f1e5b45..28ba2a1564 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -1,19 +1,22 @@ const std = @import("std"); +const builtin = @import("builtin"); const Compilation = @import("compilation.zig").Compilation; -// we go through llvm instead of c for 2 reasons: -// 1. to avoid accidentally calling the non-thread-safe functions -// 2. patch up some of the types to remove nullability const llvm = @import("llvm.zig"); +const c = @import("c.zig"); const ir = @import("ir.zig"); const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; const event = std.event; const assert = std.debug.assert; +const DW = std.dwarf; pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void { fn_val.base.ref(); defer fn_val.base.deref(comp); - defer code.destroy(comp.a()); + defer code.destroy(comp.gpa()); + + var output_path = try await (async comp.createRandomOutputPath(comp.target.oFileExt()) catch unreachable); + errdefer output_path.deinit(); const llvm_handle = try comp.event_loop_local.getAnyLlvmContext(); defer llvm_handle.release(comp.event_loop_local); @@ -23,13 +26,56 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory; defer llvm.DisposeModule(module); + llvm.SetTarget(module, comp.llvm_triple.ptr()); + llvm.SetDataLayout(module, comp.target_layout_str); + + if (comp.target.getObjectFormat() == builtin.ObjectFormat.coff) { + llvm.AddModuleCodeViewFlag(module); + } else { + llvm.AddModuleDebugInfoFlag(module); + } + const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory; defer llvm.DisposeBuilder(builder); + const dibuilder = llvm.CreateDIBuilder(module, true) orelse return error.OutOfMemory; + defer llvm.DisposeDIBuilder(dibuilder); + + // Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes + // the git revision. + const producer = try std.Buffer.allocPrint( + &code.arena.allocator, + "zig {}.{}.{}", + u32(c.ZIG_VERSION_MAJOR), + u32(c.ZIG_VERSION_MINOR), + u32(c.ZIG_VERSION_PATCH), + ); + const flags = c""; + const runtime_version = 0; + const compile_unit_file = llvm.CreateFile( + dibuilder, + comp.name.ptr(), + comp.root_package.root_src_dir.ptr(), + ) orelse return error.OutOfMemory; + const is_optimized = comp.build_mode != builtin.Mode.Debug; + const compile_unit = llvm.CreateCompileUnit( + dibuilder, + DW.LANG_C99, + compile_unit_file, + producer.ptr(), + is_optimized, + flags, + runtime_version, + c"", + 0, + !comp.strip, + ) orelse return error.OutOfMemory; + var ofile = ObjectFile{ .comp = comp, .module = module, .builder = builder, + .dibuilder = dibuilder, .context = context, .lock = event.Lock.init(comp.loop), }; @@ -41,8 +87,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); //} - // TODO - //ZigLLVMDIBuilderFinalize(g->dbuilder); + llvm.DIBuilderFinalize(dibuilder); if (comp.verbose_llvm_ir) { llvm.DumpModule(ofile.module); @@ -53,17 +98,42 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) var error_ptr: ?[*]u8 = null; _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr); } + + assert(comp.emit_file_type == Compilation.Emit.Binary); // TODO support other types + + const is_small = comp.build_mode == builtin.Mode.ReleaseSmall; + const is_debug = comp.build_mode == builtin.Mode.Debug; + + var err_msg: [*]u8 = undefined; + // TODO integrate this with evented I/O + if (llvm.TargetMachineEmitToFile( + comp.target_machine, + module, + output_path.ptr(), + llvm.EmitBinary, + &err_msg, + is_debug, + is_small, + )) { + if (std.debug.runtime_safety) { + std.debug.panic("unable to write object file {}: {s}\n", output_path.toSliceConst(), err_msg); + } + return error.WritingObjectFileFailed; + } + //validate_inline_fns(g); TODO + fn_val.containing_object = output_path; } pub const ObjectFile = struct { comp: *Compilation, module: llvm.ModuleRef, builder: llvm.BuilderRef, + dibuilder: *llvm.DIBuilder, context: llvm.ContextRef, lock: event.Lock, - fn a(self: *ObjectFile) *std.mem.Allocator { - return self.comp.a(); + fn gpa(self: *ObjectFile) *std.mem.Allocator { + return self.comp.gpa(); } }; diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 1dbbf21206..d5380a0644 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -26,16 +26,31 @@ const Value = @import("value.zig").Value; const Type = Value.Type; const Span = errmsg.Span; const codegen = @import("codegen.zig"); +const Package = @import("package.zig").Package; /// Data that is local to the event loop. pub const EventLoopLocal = struct { loop: *event.Loop, llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), - fn init(loop: *event.Loop) EventLoopLocal { + /// TODO pool these so that it doesn't have to lock + prng: event.Locked(std.rand.DefaultPrng), + + var lazy_init_targets = std.lazyInit(void); + + fn init(loop: *event.Loop) !EventLoopLocal { + lazy_init_targets.get() orelse { + Target.initializeAll(); + lazy_init_targets.resolve(); + }; + + var seed_bytes: [@sizeOf(u64)]u8 = undefined; + try std.os.getRandomBytes(seed_bytes[0..]); + const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big); return EventLoopLocal{ .loop = loop, .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), + .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), }; } @@ -76,10 +91,16 @@ pub const Compilation = struct { event_loop_local: *EventLoopLocal, loop: *event.Loop, name: Buffer, + llvm_triple: Buffer, root_src_path: ?[]const u8, target: Target, + llvm_target: llvm.TargetRef, build_mode: builtin.Mode, zig_lib_dir: []const u8, + zig_std_dir: []const u8, + + /// lazily created when we need it + tmp_dir: event.Future(BuildError![]u8), version_major: u32, version_minor: u32, @@ -106,8 +127,16 @@ pub const Compilation = struct { lib_dirs: []const []const u8, rpath_list: []const []const u8, assembly_files: []const []const u8, + + /// paths that are explicitly provided by the user to link against link_objects: []const []const u8, + /// functions that have their own objects that we need to link + /// it uses an optional pointer so that tombstone removals are possible + fn_link_set: event.Locked(FnLinkSet), + + pub const FnLinkSet = std.LinkedList(?*Value.Fn); + windows_subsystem_windows: bool, windows_subsystem_console: bool, @@ -141,7 +170,7 @@ pub const Compilation = struct { /// Before code generation starts, must wait on this group to make sure /// the build is complete. - build_group: event.Group(BuildError!void), + prelink_group: event.Group(BuildError!void), compile_errors: event.Locked(CompileErrList), @@ -155,6 +184,16 @@ pub const Compilation = struct { false_value: *Value.Bool, noreturn_value: *Value.NoReturn, + target_machine: llvm.TargetMachineRef, + target_data_ref: llvm.TargetDataRef, + target_layout_str: [*]u8, + + /// for allocating things which have the same lifetime as this Compilation + arena_allocator: std.heap.ArenaAllocator, + + root_package: *Package, + std_package: *Package, + const CompileErrList = std.ArrayList(*errmsg.Msg); // TODO handle some of these earlier and report them in a way other than error codes @@ -195,6 +234,9 @@ pub const Compilation = struct { BufferTooSmall, Unimplemented, // TODO remove this one SemanticAnalysisFailed, // TODO remove this one + ReadOnlyFileSystem, + LinkQuotaExceeded, + EnvironmentVariableNotFound, }; pub const Event = union(enum) { @@ -234,31 +276,31 @@ pub const Compilation = struct { event_loop_local: *EventLoopLocal, name: []const u8, root_src_path: ?[]const u8, - target: *const Target, + target: Target, kind: Kind, build_mode: builtin.Mode, + is_static: bool, zig_lib_dir: []const u8, cache_dir: []const u8, ) !*Compilation { const loop = event_loop_local.loop; - - var name_buffer = try Buffer.init(loop.allocator, name); - errdefer name_buffer.deinit(); - - const events = try event.Channel(Event).create(loop, 0); - errdefer events.destroy(); - - const comp = try loop.allocator.create(Compilation{ + const comp = try event_loop_local.loop.allocator.create(Compilation{ .loop = loop, + .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator), .event_loop_local = event_loop_local, - .events = events, - .name = name_buffer, + .events = undefined, .root_src_path = root_src_path, - .target = target.*, + .target = target, + .llvm_target = undefined, .kind = kind, .build_mode = build_mode, .zig_lib_dir = zig_lib_dir, + .zig_std_dir = undefined, .cache_dir = cache_dir, + .tmp_dir = event.Future(BuildError![]u8).init(loop), + + .name = undefined, + .llvm_triple = undefined, .version_major = 0, .version_minor = 0, @@ -283,7 +325,7 @@ pub const Compilation = struct { .is_test = false, .each_lib_rpath = false, .strip = false, - .is_static = false, + .is_static = is_static, .linker_rdynamic = false, .clang_argv = [][]const u8{}, .llvm_argv = [][]const u8{}, @@ -291,9 +333,10 @@ pub const Compilation = struct { .rpath_list = [][]const u8{}, .assembly_files = [][]const u8{}, .link_objects = [][]const u8{}, + .fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()), .windows_subsystem_windows = false, .windows_subsystem_console = false, - .link_libs_list = ArrayList(*LinkLib).init(loop.allocator), + .link_libs_list = undefined, .libc_link_lib = null, .err_color = errmsg.Color.Auto, .darwin_frameworks = [][]const u8{}, @@ -303,7 +346,7 @@ pub const Compilation = struct { .emit_file_type = Emit.Binary, .link_out_file = null, .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), - .build_group = event.Group(BuildError!void).init(loop), + .prelink_group = event.Group(BuildError!void).init(loop), .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), .meta_type = undefined, @@ -314,13 +357,82 @@ pub const Compilation = struct { .false_value = undefined, .noreturn_type = undefined, .noreturn_value = undefined, + + .target_machine = undefined, + .target_data_ref = undefined, + .target_layout_str = undefined, + + .root_package = undefined, + .std_package = undefined, }); + errdefer { + comp.arena_allocator.deinit(); + comp.loop.allocator.destroy(comp); + } + + comp.name = try Buffer.init(comp.arena(), name); + comp.llvm_triple = try target.getTriple(comp.arena()); + comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple); + comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena()); + comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std"); + + const opt_level = switch (build_mode) { + builtin.Mode.Debug => llvm.CodeGenLevelNone, + else => llvm.CodeGenLevelAggressive, + }; + + const reloc_mode = if (is_static) llvm.RelocStatic else llvm.RelocPIC; + + // LLVM creates invalid binaries on Windows sometimes. + // See https://github.com/ziglang/zig/issues/508 + // As a workaround we do not use target native features on Windows. + var target_specific_cpu_args: ?[*]u8 = null; + var target_specific_cpu_features: ?[*]u8 = null; + errdefer llvm.DisposeMessage(target_specific_cpu_args); + errdefer llvm.DisposeMessage(target_specific_cpu_features); + if (target == Target.Native and !target.isWindows()) { + target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory; + target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory; + } + + comp.target_machine = llvm.CreateTargetMachine( + comp.llvm_target, + comp.llvm_triple.ptr(), + target_specific_cpu_args orelse c"", + target_specific_cpu_features orelse c"", + opt_level, + reloc_mode, + llvm.CodeModelDefault, + ) orelse return error.OutOfMemory; + errdefer llvm.DisposeTargetMachine(comp.target_machine); + + comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory; + errdefer llvm.DisposeTargetData(comp.target_data_ref); + + comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory; + errdefer llvm.DisposeMessage(comp.target_layout_str); + + comp.events = try event.Channel(Event).create(comp.loop, 0); + errdefer comp.events.destroy(); + + if (root_src_path) |root_src| { + const dirname = std.os.path.dirname(root_src) orelse "."; + const basename = std.os.path.basename(root_src); + + comp.root_package = try Package.create(comp.arena(), dirname, basename); + comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "index.zig"); + try comp.root_package.add("std", comp.std_package); + } else { + comp.root_package = try Package.create(comp.arena(), ".", ""); + } + try comp.initTypes(); + return comp; } fn initTypes(comp: *Compilation) !void { - comp.meta_type = try comp.a().create(Type.MetaType{ + comp.meta_type = try comp.gpa().create(Type.MetaType{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -333,9 +445,9 @@ pub const Compilation = struct { }); comp.meta_type.value = &comp.meta_type.base; comp.meta_type.base.base.typeof = &comp.meta_type.base; - errdefer comp.a().destroy(comp.meta_type); + errdefer comp.gpa().destroy(comp.meta_type); - comp.void_type = try comp.a().create(Type.Void{ + comp.void_type = try comp.gpa().create(Type.Void{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -345,9 +457,9 @@ pub const Compilation = struct { .id = builtin.TypeId.Void, }, }); - errdefer comp.a().destroy(comp.void_type); + errdefer comp.gpa().destroy(comp.void_type); - comp.noreturn_type = try comp.a().create(Type.NoReturn{ + comp.noreturn_type = try comp.gpa().create(Type.NoReturn{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -357,9 +469,9 @@ pub const Compilation = struct { .id = builtin.TypeId.NoReturn, }, }); - errdefer comp.a().destroy(comp.noreturn_type); + errdefer comp.gpa().destroy(comp.noreturn_type); - comp.bool_type = try comp.a().create(Type.Bool{ + comp.bool_type = try comp.gpa().create(Type.Bool{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -369,18 +481,18 @@ pub const Compilation = struct { .id = builtin.TypeId.Bool, }, }); - errdefer comp.a().destroy(comp.bool_type); + errdefer comp.gpa().destroy(comp.bool_type); - comp.void_value = try comp.a().create(Value.Void{ + comp.void_value = try comp.gpa().create(Value.Void{ .base = Value{ .id = Value.Id.Void, .typeof = &Type.Void.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, }); - errdefer comp.a().destroy(comp.void_value); + errdefer comp.gpa().destroy(comp.void_value); - comp.true_value = try comp.a().create(Value.Bool{ + comp.true_value = try comp.gpa().create(Value.Bool{ .base = Value{ .id = Value.Id.Bool, .typeof = &Type.Bool.get(comp).base, @@ -388,9 +500,9 @@ pub const Compilation = struct { }, .x = true, }); - errdefer comp.a().destroy(comp.true_value); + errdefer comp.gpa().destroy(comp.true_value); - comp.false_value = try comp.a().create(Value.Bool{ + comp.false_value = try comp.gpa().create(Value.Bool{ .base = Value{ .id = Value.Id.Bool, .typeof = &Type.Bool.get(comp).base, @@ -398,19 +510,23 @@ pub const Compilation = struct { }, .x = false, }); - errdefer comp.a().destroy(comp.false_value); + errdefer comp.gpa().destroy(comp.false_value); - comp.noreturn_value = try comp.a().create(Value.NoReturn{ + comp.noreturn_value = try comp.gpa().create(Value.NoReturn{ .base = Value{ .id = Value.Id.NoReturn, .typeof = &Type.NoReturn.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, }); - errdefer comp.a().destroy(comp.noreturn_value); + errdefer comp.gpa().destroy(comp.noreturn_value); } pub fn destroy(self: *Compilation) void { + if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| { + os.deleteTree(self.arena(), tmp_dir) catch {}; + } else |_| {}; + self.noreturn_value.base.deref(self); self.void_value.base.deref(self); self.false_value.base.deref(self); @@ -420,14 +536,18 @@ pub const Compilation = struct { self.meta_type.base.base.deref(self); self.events.destroy(); - self.name.deinit(); - self.a().destroy(self); + llvm.DisposeMessage(self.target_layout_str); + llvm.DisposeTargetData(self.target_data_ref); + llvm.DisposeTargetMachine(self.target_machine); + + self.arena_allocator.deinit(); + self.gpa().destroy(self); } pub fn build(self: *Compilation) !void { if (self.llvm_argv.len != 0) { - var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{ + var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{ [][]const u8{"zig (LLVM option parsing)"}, self.llvm_argv, }); @@ -436,7 +556,7 @@ pub const Compilation = struct { c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr); } - _ = try async self.buildAsync(); + _ = try async self.buildAsync(); } async fn buildAsync(self: *Compilation) void { @@ -464,7 +584,7 @@ pub const Compilation = struct { } } else |err| { // if there's an error then the compile errors have dangling references - self.a().free(compile_errors); + self.gpa().free(compile_errors); await (async self.events.put(Event{ .Error = err }) catch unreachable); } @@ -477,26 +597,26 @@ pub const Compilation = struct { async fn addRootSrc(self: *Compilation) !void { const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); // TODO async/await os.path.real - const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| { + const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { try printError("unable to get real path '{}': {}", root_src_path, err); return err; }; - errdefer self.a().free(root_src_real_path); + errdefer self.gpa().free(root_src_real_path); // TODO async/await readFileAlloc() - const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| { + const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| { try printError("unable to open '{}': {}", root_src_real_path, err); return err; }; - errdefer self.a().free(source_code); + errdefer self.gpa().free(source_code); - const parsed_file = try self.a().create(ParsedFile{ + const parsed_file = try self.gpa().create(ParsedFile{ .tree = undefined, .realpath = root_src_real_path, }); - errdefer self.a().destroy(parsed_file); + errdefer self.gpa().destroy(parsed_file); - parsed_file.tree = try std.zig.parse(self.a(), source_code); + parsed_file.tree = try std.zig.parse(self.gpa(), source_code); errdefer parsed_file.tree.deinit(); const tree = &parsed_file.tree; @@ -525,7 +645,7 @@ pub const Compilation = struct { continue; }; - const fn_decl = try self.a().create(Decl.Fn{ + const fn_decl = try self.gpa().create(Decl.Fn{ .base = Decl{ .id = Decl.Id.Fn, .name = name, @@ -538,7 +658,7 @@ pub const Compilation = struct { .value = Decl.Fn.Val{ .Unresolved = {} }, .fn_proto = fn_proto, }); - errdefer self.a().destroy(fn_decl); + errdefer self.gpa().destroy(fn_decl); try decl_group.call(addTopLevelDecl, self, &fn_decl.base); }, @@ -547,15 +667,15 @@ pub const Compilation = struct { } } try await (async decl_group.wait() catch unreachable); - try await (async self.build_group.wait() catch unreachable); + try await (async self.prelink_group.wait() catch unreachable); } async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void { const is_export = decl.isExported(&decl.parsed_file.tree); if (is_export) { - try self.build_group.call(verifyUniqueSymbol, self, decl); - try self.build_group.call(resolveDecl, self, decl); + try self.prelink_group.call(verifyUniqueSymbol, self, decl); + try self.prelink_group.call(resolveDecl, self, decl); } } @@ -563,7 +683,7 @@ pub const Compilation = struct { const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args); errdefer self.loop.allocator.free(text); - try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text); + try self.prelink_group.call(addCompileErrorAsync, self, parsed_file, span, text); } async fn addCompileErrorAsync( @@ -625,11 +745,11 @@ pub const Compilation = struct { } } - const link_lib = try self.a().create(LinkLib{ + const link_lib = try self.gpa().create(LinkLib{ .name = name, .path = null, .provided_explicitly = provided_explicitly, - .symbols = ArrayList([]u8).init(self.a()), + .symbols = ArrayList([]u8).init(self.gpa()), }); try self.link_libs_list.append(link_lib); if (is_libc) { @@ -638,9 +758,71 @@ pub const Compilation = struct { return link_lib; } - fn a(self: Compilation) *mem.Allocator { + /// General Purpose Allocator. Must free when done. + fn gpa(self: Compilation) *mem.Allocator { return self.loop.allocator; } + + /// Arena Allocator. Automatically freed when the Compilation is destroyed. + fn arena(self: *Compilation) *mem.Allocator { + return &self.arena_allocator.allocator; + } + + /// If the temporary directory for this compilation has not been created, it creates it. + /// Then it creates a random file name in that dir and returns it. + pub async fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer { + const tmp_dir = try await (async self.getTmpDir() catch unreachable); + const file_prefix = await (async self.getRandomFileName() catch unreachable); + + const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix); + defer self.gpa().free(file_name); + + const full_path = try os.path.join(self.gpa(), tmp_dir, file_name[0..]); + errdefer self.gpa().free(full_path); + + return Buffer.fromOwnedSlice(self.gpa(), full_path); + } + + /// If the temporary directory for this Compilation has not been created, creates it. + /// Then returns it. The directory is unique to this Compilation and cleaned up when + /// the Compilation deinitializes. + async fn getTmpDir(self: *Compilation) ![]const u8 { + if (await (async self.tmp_dir.start() catch unreachable)) |ptr| return ptr.*; + self.tmp_dir.data = await (async self.getTmpDirImpl() catch unreachable); + self.tmp_dir.resolve(); + return self.tmp_dir.data; + } + + async fn getTmpDirImpl(self: *Compilation) ![]u8 { + const comp_dir_name = await (async self.getRandomFileName() catch unreachable); + const zig_dir_path = try getZigDir(self.gpa()); + defer self.gpa().free(zig_dir_path); + + const tmp_dir = try os.path.join(self.arena(), zig_dir_path, comp_dir_name[0..]); + try os.makePath(self.gpa(), tmp_dir); + return tmp_dir; + } + + async fn getRandomFileName(self: *Compilation) [12]u8 { + // here we replace the standard +/ with -_ so that it can be used in a file name + const b64_fs_encoder = std.base64.Base64Encoder.init( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", + std.base64.standard_pad_char, + ); + + var rand_bytes: [9]u8 = undefined; + + { + const held = await (async self.event_loop_local.prng.acquire() catch unreachable); + defer held.release(); + + held.value.random.bytes(rand_bytes[0..]); + } + + var result: [12]u8 = undefined; + b64_fs_encoder.encode(result[0..], rand_bytes); + return result; + } }; fn printError(comptime format: []const u8, args: ...) !void { @@ -662,13 +844,11 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib /// This declaration has been blessed as going into the final code generation. pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void { - if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { - decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable); - decl.resolution.resolve(); - return decl.resolution.data; - } else { - return (await (async decl.resolution.get() catch unreachable)).*; - } + if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*; + + decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable); + decl.resolution.resolve(); + return decl.resolution.data; } /// The function that actually does the generation. @@ -698,7 +878,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args); defer fn_type.base.base.deref(comp); - var symbol_name = try std.Buffer.init(comp.a(), fn_decl.base.name); + var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name); errdefer symbol_name.deinit(); const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); @@ -719,7 +899,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { error.SemanticAnalysisFailed => return {}, else => return err, }; - defer unanalyzed_code.destroy(comp.a()); + defer unanalyzed_code.destroy(comp.gpa()); if (comp.verbose_ir) { std.debug.warn("unanalyzed:\n"); @@ -738,7 +918,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { error.SemanticAnalysisFailed => return {}, else => return err, }; - errdefer analyzed_code.destroy(comp.a()); + errdefer analyzed_code.destroy(comp.gpa()); if (comp.verbose_ir) { std.debug.warn("analyzed:\n"); @@ -747,5 +927,30 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { // Kick off rendering to LLVM module, but it doesn't block the fn decl // analysis from being complete. - try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); + try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); + try comp.prelink_group.call(addFnToLinkSet, comp, fn_val); +} + +async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void { + fn_val.base.ref(); + defer fn_val.base.deref(comp); + + fn_val.link_set_node.data = fn_val; + + const held = await (async comp.fn_link_set.acquire() catch unreachable); + defer held.release(); + + held.value.append(fn_val.link_set_node); +} + +fn getZigDir(allocator: *mem.Allocator) ![]u8 { + const home_dir = try getHomeDir(allocator); + defer allocator.free(home_dir); + + return os.path.join(allocator, home_dir, ".zig"); +} + +/// TODO move to zig std lib, and make it work for other OSes +fn getHomeDir(allocator: *mem.Allocator) ![]u8 { + return os.getEnvVarOwned(allocator, "HOME"); } diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 0e0a4f9bf3..c1f9c97001 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -453,7 +453,7 @@ pub const Code = struct { arena: std.heap.ArenaAllocator, return_type: ?*Type, - /// allocator is comp.a() + /// allocator is comp.gpa() pub fn destroy(self: *Code, allocator: *Allocator) void { self.arena.deinit(); allocator.destroy(self); @@ -483,13 +483,13 @@ pub const Builder = struct { pub const Error = Analyze.Error; pub fn init(comp: *Compilation, parsed_file: *ParsedFile) !Builder { - const code = try comp.a().create(Code{ + const code = try comp.gpa().create(Code{ .basic_block_list = undefined, - .arena = std.heap.ArenaAllocator.init(comp.a()), + .arena = std.heap.ArenaAllocator.init(comp.gpa()), .return_type = null, }); code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator); - errdefer code.destroy(comp.a()); + errdefer code.destroy(comp.gpa()); return Builder{ .comp = comp, @@ -502,7 +502,7 @@ pub const Builder = struct { } pub fn abort(self: *Builder) void { - self.code.destroy(self.comp.a()); + self.code.destroy(self.comp.gpa()); } /// Call code.destroy() when done diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index 13480dc2c6..b196656367 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -2,6 +2,12 @@ const builtin = @import("builtin"); const c = @import("c.zig"); const assert = @import("std").debug.assert; +// we wrap the c module for 3 reasons: +// 1. to avoid accidentally calling the non-thread-safe functions +// 2. patch up some of the types to remove nullability +// 3. some functions have been augmented by zig_llvm.cpp to be more powerful, +// such as ZigLLVMTargetMachineEmitToFile + pub const AttributeIndex = c_uint; pub const Bool = c_int; @@ -12,25 +18,51 @@ pub const ValueRef = removeNullability(c.LLVMValueRef); pub const TypeRef = removeNullability(c.LLVMTypeRef); pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef); pub const AttributeRef = removeNullability(c.LLVMAttributeRef); +pub const TargetRef = removeNullability(c.LLVMTargetRef); +pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef); +pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef); +pub const DIBuilder = c.ZigLLVMDIBuilder; pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex; pub const AddFunction = c.LLVMAddFunction; +pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag; +pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag; pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; +pub const ConstAllOnes = c.LLVMConstAllOnes; pub const ConstInt = c.LLVMConstInt; +pub const ConstNull = c.LLVMConstNull; pub const ConstStringInContext = c.LLVMConstStringInContext; pub const ConstStructInContext = c.LLVMConstStructInContext; +pub const CopyStringRepOfTargetData = c.LLVMCopyStringRepOfTargetData; pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext; +pub const CreateCompileUnit = c.ZigLLVMCreateCompileUnit; +pub const CreateDIBuilder = c.ZigLLVMCreateDIBuilder; pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute; +pub const CreateFile = c.ZigLLVMCreateFile; pub const CreateStringAttribute = c.LLVMCreateStringAttribute; +pub const CreateTargetDataLayout = c.LLVMCreateTargetDataLayout; +pub const CreateTargetMachine = c.LLVMCreateTargetMachine; +pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize; pub const DisposeBuilder = c.LLVMDisposeBuilder; +pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder; +pub const DisposeMessage = c.LLVMDisposeMessage; pub const DisposeModule = c.LLVMDisposeModule; +pub const DisposeTargetData = c.LLVMDisposeTargetData; +pub const DisposeTargetMachine = c.LLVMDisposeTargetMachine; pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext; pub const DumpModule = c.LLVMDumpModule; pub const FP128TypeInContext = c.LLVMFP128TypeInContext; pub const FloatTypeInContext = c.LLVMFloatTypeInContext; pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName; +pub const GetHostCPUName = c.ZigLLVMGetHostCPUName; pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext; +pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures; pub const HalfTypeInContext = c.LLVMHalfTypeInContext; +pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers; +pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters; +pub const InitializeAllTargetInfos = c.LLVMInitializeAllTargetInfos; +pub const InitializeAllTargetMCs = c.LLVMInitializeAllTargetMCs; +pub const InitializeAllTargets = c.LLVMInitializeAllTargets; pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext; pub const Int128TypeInContext = c.LLVMInt128TypeInContext; pub const Int16TypeInContext = c.LLVMInt16TypeInContext; @@ -47,13 +79,16 @@ pub const MDStringInContext = c.LLVMMDStringInContext; pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext; pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext; +pub const SetDataLayout = c.LLVMSetDataLayout; +pub const SetTarget = c.LLVMSetTarget; pub const StructTypeInContext = c.LLVMStructTypeInContext; pub const TokenTypeInContext = c.LLVMTokenTypeInContext; pub const VoidTypeInContext = c.LLVMVoidTypeInContext; pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; -pub const ConstAllOnes = c.LLVMConstAllOnes; -pub const ConstNull = c.LLVMConstNull; + +pub const GetTargetFromTriple = LLVMGetTargetFromTriple; +extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool; pub const VerifyModule = LLVMVerifyModule; extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool; @@ -83,6 +118,31 @@ pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction; pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction; pub const VerifierFailureAction = c.LLVMVerifierFailureAction; +pub const CodeGenLevelNone = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelNone; +pub const CodeGenLevelLess = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelLess; +pub const CodeGenLevelDefault = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault; +pub const CodeGenLevelAggressive = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive; +pub const CodeGenOptLevel = c.LLVMCodeGenOptLevel; + +pub const RelocDefault = c.LLVMRelocMode.LLVMRelocDefault; +pub const RelocStatic = c.LLVMRelocMode.LLVMRelocStatic; +pub const RelocPIC = c.LLVMRelocMode.LLVMRelocPIC; +pub const RelocDynamicNoPic = c.LLVMRelocMode.LLVMRelocDynamicNoPic; +pub const RelocMode = c.LLVMRelocMode; + +pub const CodeModelDefault = c.LLVMCodeModel.LLVMCodeModelDefault; +pub const CodeModelJITDefault = c.LLVMCodeModel.LLVMCodeModelJITDefault; +pub const CodeModelSmall = c.LLVMCodeModel.LLVMCodeModelSmall; +pub const CodeModelKernel = c.LLVMCodeModel.LLVMCodeModelKernel; +pub const CodeModelMedium = c.LLVMCodeModel.LLVMCodeModelMedium; +pub const CodeModelLarge = c.LLVMCodeModel.LLVMCodeModelLarge; +pub const CodeModel = c.LLVMCodeModel; + +pub const EmitAssembly = EmitOutputType.ZigLLVM_EmitAssembly; +pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary; +pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr; +pub const EmitOutputType = c.ZigLLVM_EmitOutputType; + fn removeNullability(comptime T: type) type { comptime assert(@typeId(T) == builtin.TypeId.Optional); return T.Child; @@ -90,3 +150,14 @@ fn removeNullability(comptime T: type) type { pub const BuildRet = LLVMBuildRet; extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef; + +pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile; +extern fn ZigLLVMTargetMachineEmitToFile( + targ_machine_ref: TargetMachineRef, + module_ref: ModuleRef, + filename: [*]const u8, + output_type: EmitOutputType, + error_message: *[*]u8, + is_debug: bool, + is_small: bool, +) bool; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index c9478954c5..8b668e35bd 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -363,6 +363,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co } }; + const is_static = flags.present("static"); + const assembly_files = flags.many("assembly"); const link_objects = flags.many("object"); if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) { @@ -389,7 +391,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co try loop.initMultiThreaded(allocator); defer loop.deinit(); - var event_loop_local = EventLoopLocal.init(&loop); + var event_loop_local = try EventLoopLocal.init(&loop); defer event_loop_local.deinit(); var comp = try Compilation.create( @@ -399,6 +401,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co Target.Native, out_type, build_mode, + is_static, zig_lib_dir, full_cache_dir, ); @@ -426,7 +429,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co comp.clang_argv = clang_argv_buf.toSliceConst(); comp.strip = flags.present("strip"); - comp.is_static = flags.present("static"); if (flags.single("libc-lib-dir")) |libc_lib_dir| { comp.libc_lib_dir = libc_lib_dir; @@ -481,9 +483,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co } comp.emit_file_type = emit_type; - comp.link_objects = link_objects; comp.assembly_files = assembly_files; comp.link_out_file = flags.single("out-file"); + comp.link_objects = link_objects; try comp.build(); const process_build_events_handle = try async processBuildEvents(comp, color); diff --git a/src-self-hosted/package.zig b/src-self-hosted/package.zig new file mode 100644 index 0000000000..720b279651 --- /dev/null +++ b/src-self-hosted/package.zig @@ -0,0 +1,29 @@ +const std = @import("std"); +const mem = std.mem; +const assert = std.debug.assert; +const Buffer = std.Buffer; + +pub const Package = struct { + root_src_dir: Buffer, + root_src_path: Buffer, + + /// relative to root_src_dir + table: Table, + + pub const Table = std.HashMap([]const u8, *Package, mem.hash_slice_u8, mem.eql_slice_u8); + + /// makes internal copies of root_src_dir and root_src_path + /// allocator should be an arena allocator because Package never frees anything + pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package { + return allocator.create(Package{ + .root_src_dir = try Buffer.init(allocator, root_src_dir), + .root_src_path = try Buffer.init(allocator, root_src_path), + .table = Table.init(allocator), + }); + } + + pub fn add(self: *Package, name: []const u8, package: *Package) !void { + const entry = try self.table.put(try mem.dupe(self.table.allocator, u8, name), package); + assert(entry == null); + } +}; diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 4326617fa0..1c519d6c08 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -64,7 +64,7 @@ pub const Scope = struct { /// Creates a Decls scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls { - const self = try comp.a().create(Decls{ + const self = try comp.gpa().create(Decls{ .base = Scope{ .id = Id.Decls, .parent = parent, @@ -72,9 +72,9 @@ pub const Scope = struct { }, .table = undefined, }); - errdefer comp.a().destroy(self); + errdefer comp.gpa().destroy(self); - self.table = Decl.Table.init(comp.a()); + self.table = Decl.Table.init(comp.gpa()); errdefer self.table.deinit(); if (parent) |p| p.ref(); @@ -126,7 +126,7 @@ pub const Scope = struct { /// Creates a Block scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope) !*Block { - const self = try comp.a().create(Block{ + const self = try comp.gpa().create(Block{ .base = Scope{ .id = Id.Block, .parent = parent, @@ -138,14 +138,14 @@ pub const Scope = struct { .is_comptime = undefined, .safety = Safety.Auto, }); - errdefer comp.a().destroy(self); + errdefer comp.gpa().destroy(self); if (parent) |p| p.ref(); return self; } pub fn destroy(self: *Block, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -158,7 +158,7 @@ pub const Scope = struct { /// Creates a FnDef scope with 1 reference /// Must set the fn_val later pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef { - const self = try comp.a().create(FnDef{ + const self = try comp.gpa().create(FnDef{ .base = Scope{ .id = Id.FnDef, .parent = parent, @@ -173,7 +173,7 @@ pub const Scope = struct { } pub fn destroy(self: *FnDef, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -182,7 +182,7 @@ pub const Scope = struct { /// Creates a CompTime scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime { - const self = try comp.a().create(CompTime{ + const self = try comp.gpa().create(CompTime{ .base = Scope{ .id = Id.CompTime, .parent = parent, @@ -195,7 +195,7 @@ pub const Scope = struct { } pub fn destroy(self: *CompTime, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -216,7 +216,7 @@ pub const Scope = struct { kind: Kind, defer_expr_scope: *DeferExpr, ) !*Defer { - const self = try comp.a().create(Defer{ + const self = try comp.gpa().create(Defer{ .base = Scope{ .id = Id.Defer, .parent = parent, @@ -225,7 +225,7 @@ pub const Scope = struct { .defer_expr_scope = defer_expr_scope, .kind = kind, }); - errdefer comp.a().destroy(self); + errdefer comp.gpa().destroy(self); defer_expr_scope.base.ref(); @@ -235,7 +235,7 @@ pub const Scope = struct { pub fn destroy(self: *Defer, comp: *Compilation) void { self.defer_expr_scope.base.deref(comp); - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -245,7 +245,7 @@ pub const Scope = struct { /// Creates a DeferExpr scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr { - const self = try comp.a().create(DeferExpr{ + const self = try comp.gpa().create(DeferExpr{ .base = Scope{ .id = Id.DeferExpr, .parent = parent, @@ -253,14 +253,14 @@ pub const Scope = struct { }, .expr_node = expr_node, }); - errdefer comp.a().destroy(self); + errdefer comp.gpa().destroy(self); if (parent) |p| p.ref(); return self; } pub fn destroy(self: *DeferExpr, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; }; diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index 724d99ea23..db673e421a 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -1,60 +1,118 @@ +const std = @import("std"); const builtin = @import("builtin"); -const c = @import("c.zig"); - -pub const CrossTarget = struct { - arch: builtin.Arch, - os: builtin.Os, - environ: builtin.Environ, -}; +const llvm = @import("llvm.zig"); pub const Target = union(enum) { Native, - Cross: CrossTarget, + Cross: Cross, - pub fn oFileExt(self: *const Target) []const u8 { - const environ = switch (self.*) { - Target.Native => builtin.environ, - Target.Cross => |t| t.environ, - }; - return switch (environ) { - builtin.Environ.msvc => ".obj", + pub const Cross = struct { + arch: builtin.Arch, + os: builtin.Os, + environ: builtin.Environ, + object_format: builtin.ObjectFormat, + }; + + pub fn oFileExt(self: Target) []const u8 { + return switch (self.getObjectFormat()) { + builtin.ObjectFormat.coff => ".obj", else => ".o", }; } - pub fn exeFileExt(self: *const Target) []const u8 { + pub fn exeFileExt(self: Target) []const u8 { return switch (self.getOs()) { builtin.Os.windows => ".exe", else => "", }; } - pub fn getOs(self: *const Target) builtin.Os { - return switch (self.*) { + pub fn getOs(self: Target) builtin.Os { + return switch (self) { Target.Native => builtin.os, - Target.Cross => |t| t.os, + @TagType(Target).Cross => |t| t.os, + }; + } + + pub fn getArch(self: Target) builtin.Arch { + return switch (self) { + Target.Native => builtin.arch, + @TagType(Target).Cross => |t| t.arch, + }; + } + + pub fn getEnviron(self: Target) builtin.Environ { + return switch (self) { + Target.Native => builtin.environ, + @TagType(Target).Cross => |t| t.environ, + }; + } + + pub fn getObjectFormat(self: Target) builtin.ObjectFormat { + return switch (self) { + Target.Native => builtin.object_format, + @TagType(Target).Cross => |t| t.object_format, }; } - pub fn isDarwin(self: *const Target) bool { + pub fn isWasm(self: Target) bool { + return switch (self.getArch()) { + builtin.Arch.wasm32, builtin.Arch.wasm64 => true, + else => false, + }; + } + + pub fn isDarwin(self: Target) bool { return switch (self.getOs()) { builtin.Os.ios, builtin.Os.macosx => true, else => false, }; } - pub fn isWindows(self: *const Target) bool { + pub fn isWindows(self: Target) bool { return switch (self.getOs()) { builtin.Os.windows => true, else => false, }; } -}; -pub fn initializeAll() void { - c.LLVMInitializeAllTargets(); - c.LLVMInitializeAllTargetInfos(); - c.LLVMInitializeAllTargetMCs(); - c.LLVMInitializeAllAsmPrinters(); - c.LLVMInitializeAllAsmParsers(); -} + pub fn initializeAll() void { + llvm.InitializeAllTargets(); + llvm.InitializeAllTargetInfos(); + llvm.InitializeAllTargetMCs(); + llvm.InitializeAllAsmPrinters(); + llvm.InitializeAllAsmParsers(); + } + + pub fn getTriple(self: Target, allocator: *std.mem.Allocator) !std.Buffer { + var result = try std.Buffer.initSize(allocator, 0); + errdefer result.deinit(); + + // LLVM WebAssembly output support requires the target to be activated at + // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly. + // + // LLVM determines the output format based on the environment suffix, + // defaulting to an object based on the architecture. The default format in + // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to + // explicitly set this ourself in order for it to work. + // + // This is fixed in LLVM 7 and you will be able to get wasm output by + // using the target triple `wasm32-unknown-unknown-unknown`. + const env_name = if (self.isWasm()) "wasm" else @tagName(self.getEnviron()); + + var out = &std.io.BufferOutStream.init(&result).stream; + try out.print("{}-unknown-{}-{}", @tagName(self.getArch()), @tagName(self.getOs()), env_name); + + return result; + } + + pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef { + var result: llvm.TargetRef = undefined; + var err_msg: [*]u8 = undefined; + if (llvm.GetTargetFromTriple(triple.ptr(), &result, &err_msg) != 0) { + std.debug.warn("triple: {s} error: {s}\n", triple.ptr(), err_msg); + return error.UnsupportedTarget; + } + return result; + } +}; diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 3edb267ca9..45e5362124 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -46,7 +46,7 @@ pub const TestContext = struct { try self.loop.initMultiThreaded(allocator); errdefer self.loop.deinit(); - self.event_loop_local = EventLoopLocal.init(&self.loop); + self.event_loop_local = try EventLoopLocal.init(&self.loop); errdefer self.event_loop_local.deinit(); self.group = std.event.Group(error!void).init(&self.loop); @@ -107,6 +107,7 @@ pub const TestContext = struct { Target.Native, Compilation.Kind.Obj, builtin.Mode.Debug, + true, // is_static self.zig_lib_dir, self.zig_cache_dir, ); diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 670547cce2..bb1fb9bb01 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -160,7 +160,7 @@ pub const Type = struct { decls: *Scope.Decls, pub fn destroy(self: *Struct, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef { @@ -180,7 +180,7 @@ pub const Type = struct { }; pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { - const result = try comp.a().create(Fn{ + const result = try comp.gpa().create(Fn{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -193,7 +193,7 @@ pub const Type = struct { .params = params, .is_var_args = is_var_args, }); - errdefer comp.a().destroy(result); + errdefer comp.gpa().destroy(result); result.return_type.base.ref(); for (result.params) |param| { @@ -207,7 +207,7 @@ pub const Type = struct { for (self.params) |param| { param.typeof.base.deref(comp); } - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef { @@ -215,8 +215,8 @@ pub const Type = struct { Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory, else => try self.return_type.getLlvmType(ofile), }; - const llvm_param_types = try ofile.a().alloc(llvm.TypeRef, self.params.len); - defer ofile.a().free(llvm_param_types); + const llvm_param_types = try ofile.gpa().alloc(llvm.TypeRef, self.params.len); + defer ofile.gpa().free(llvm_param_types); for (llvm_param_types) |*llvm_param_type, i| { llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile); } @@ -241,7 +241,7 @@ pub const Type = struct { } pub fn destroy(self: *MetaType, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -255,7 +255,7 @@ pub const Type = struct { } pub fn destroy(self: *Void, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -269,7 +269,7 @@ pub const Type = struct { } pub fn destroy(self: *Bool, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef { @@ -287,7 +287,7 @@ pub const Type = struct { } pub fn destroy(self: *NoReturn, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -295,7 +295,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Int, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef { @@ -307,7 +307,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Float, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef { @@ -332,7 +332,7 @@ pub const Type = struct { pub const Size = builtin.TypeInfo.Pointer.Size; pub fn destroy(self: *Pointer, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn get( @@ -355,7 +355,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Array, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef { @@ -367,7 +367,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -375,7 +375,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ComptimeInt, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -383,7 +383,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Undefined, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -391,7 +391,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Null, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -399,7 +399,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Optional, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef { @@ -411,7 +411,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ErrorUnion, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef { @@ -423,7 +423,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ErrorSet, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef { @@ -435,7 +435,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Enum, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef { @@ -447,7 +447,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Union, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef { @@ -459,7 +459,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Namespace, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -467,7 +467,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Block, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -475,7 +475,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *BoundFn, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef { @@ -487,7 +487,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ArgTuple, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -495,7 +495,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Opaque, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef { @@ -507,7 +507,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Promise, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef { diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index e3b91d2807..be19c6bccf 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -4,6 +4,7 @@ const Scope = @import("scope.zig").Scope; const Compilation = @import("compilation.zig").Compilation; const ObjectFile = @import("codegen.zig").ObjectFile; const llvm = @import("llvm.zig"); +const Buffer = std.Buffer; /// Values are ref-counted, heap-allocated, and copy-on-write /// If there is only 1 ref then write need not copy @@ -68,7 +69,7 @@ pub const Value = struct { /// The main external name that is used in the .o file. /// TODO https://github.com/ziglang/zig/issues/265 - symbol_name: std.Buffer, + symbol_name: Buffer, /// parent should be the top level decls or container decls fndef_scope: *Scope.FnDef, @@ -79,10 +80,22 @@ pub const Value = struct { /// parent is child_scope block_scope: *Scope.Block, + /// Path to the object file that contains this function + containing_object: Buffer, + + link_set_node: *std.LinkedList(?*Value.Fn).Node, + /// Creates a Fn value with 1 ref /// Takes ownership of symbol_name - pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn { - const self = try comp.a().create(Fn{ + pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn { + const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{ + .data = null, + .next = undefined, + .prev = undefined, + }); + errdefer comp.gpa().destroy(link_set_node); + + const self = try comp.gpa().create(Fn{ .base = Value{ .id = Value.Id.Fn, .typeof = &fn_type.base, @@ -92,6 +105,8 @@ pub const Value = struct { .child_scope = &fndef_scope.base, .block_scope = undefined, .symbol_name = symbol_name, + .containing_object = Buffer.initNull(comp.gpa()), + .link_set_node = link_set_node, }); fn_type.base.base.ref(); fndef_scope.fn_val = self; @@ -100,9 +115,19 @@ pub const Value = struct { } pub fn destroy(self: *Fn, comp: *Compilation) void { + // remove with a tombstone so that we do not have to grab a lock + if (self.link_set_node.data != null) { + // it's now the job of the link step to find this tombstone and + // deallocate it. + self.link_set_node.data = null; + } else { + comp.gpa().destroy(self.link_set_node); + } + + self.containing_object.deinit(); self.fndef_scope.base.deref(comp); self.symbol_name.deinit(); - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -115,7 +140,7 @@ pub const Value = struct { } pub fn destroy(self: *Void, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -134,7 +159,7 @@ pub const Value = struct { } pub fn destroy(self: *Bool, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef { @@ -156,7 +181,7 @@ pub const Value = struct { } pub fn destroy(self: *NoReturn, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -170,7 +195,7 @@ pub const Value = struct { }; pub fn destroy(self: *Ptr, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; }; diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 24f2a8a343..a43d2d182c 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -440,6 +440,11 @@ ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unreso return reinterpret_cast(di_builder); } +void ZigLLVMDisposeDIBuilder(ZigLLVMDIBuilder *dbuilder) { + DIBuilder *di_builder = reinterpret_cast(dbuilder); + delete di_builder; +} + void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) { unwrap(builder)->SetCurrentDebugLocation(DebugLoc::get( line, column, reinterpret_cast(scope))); diff --git a/src/zig_llvm.h b/src/zig_llvm.h index d34300b8ae..6f25df8674 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -39,7 +39,7 @@ struct ZigLLVMInsertionPoint; ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R); ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R); -/// Caller must free memory. +/// Caller must free memory with LLVMDisposeMessage ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void); ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void); @@ -139,6 +139,7 @@ ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void); ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void); ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved); +ZIG_EXTERN_C void ZigLLVMDisposeDIBuilder(struct ZigLLVMDIBuilder *dbuilder); ZIG_EXTERN_C void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module); ZIG_EXTERN_C void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module); diff --git a/std/atomic/int.zig b/std/atomic/int.zig index d51454c673..4103d52719 100644 --- a/std/atomic/int.zig +++ b/std/atomic/int.zig @@ -25,5 +25,9 @@ pub fn Int(comptime T: type) type { pub fn get(self: *Self) T { return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst); } + + pub fn xchg(self: *Self, new_value: T) T { + return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Xchg, new_value, AtomicOrder.SeqCst); + } }; } diff --git a/std/buffer.zig b/std/buffer.zig index aff7fa86ef..3b58002aba 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -54,6 +54,19 @@ pub const Buffer = struct { return result; } + pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: ...) !Buffer { + const countSize = struct { + fn countSize(size: *usize, bytes: []const u8) (error{}!void) { + size.* += bytes.len; + } + }.countSize; + var size: usize = 0; + std.fmt.format(&size, error{}, countSize, format, args) catch |err| switch (err) {}; + var self = try Buffer.initSize(allocator, size); + assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size); + return self; + } + pub fn deinit(self: *Buffer) void { self.list.deinit(); } diff --git a/std/dwarf.zig b/std/dwarf.zig index 76ed122447..2cf8ed953e 100644 --- a/std/dwarf.zig +++ b/std/dwarf.zig @@ -639,3 +639,40 @@ pub const LNE_define_file = 0x03; pub const LNE_set_discriminator = 0x04; pub const LNE_lo_user = 0x80; pub const LNE_hi_user = 0xff; + +pub const LANG_C89 = 0x0001; +pub const LANG_C = 0x0002; +pub const LANG_Ada83 = 0x0003; +pub const LANG_C_plus_plus = 0x0004; +pub const LANG_Cobol74 = 0x0005; +pub const LANG_Cobol85 = 0x0006; +pub const LANG_Fortran77 = 0x0007; +pub const LANG_Fortran90 = 0x0008; +pub const LANG_Pascal83 = 0x0009; +pub const LANG_Modula2 = 0x000a; +pub const LANG_Java = 0x000b; +pub const LANG_C99 = 0x000c; +pub const LANG_Ada95 = 0x000d; +pub const LANG_Fortran95 = 0x000e; +pub const LANG_PLI = 0x000f; +pub const LANG_ObjC = 0x0010; +pub const LANG_ObjC_plus_plus = 0x0011; +pub const LANG_UPC = 0x0012; +pub const LANG_D = 0x0013; +pub const LANG_Python = 0x0014; +pub const LANG_Go = 0x0016; +pub const LANG_C_plus_plus_11 = 0x001a; +pub const LANG_Rust = 0x001c; +pub const LANG_C11 = 0x001d; +pub const LANG_C_plus_plus_14 = 0x0021; +pub const LANG_Fortran03 = 0x0022; +pub const LANG_Fortran08 = 0x0023; +pub const LANG_lo_user = 0x8000; +pub const LANG_hi_user = 0xffff; +pub const LANG_Mips_Assembler = 0x8001; +pub const LANG_Upc = 0x8765; +pub const LANG_HP_Bliss = 0x8003; +pub const LANG_HP_Basic91 = 0x8004; +pub const LANG_HP_Pascal91 = 0x8005; +pub const LANG_HP_IMacro = 0x8006; +pub const LANG_HP_Assembler = 0x8007; diff --git a/std/event/future.zig b/std/event/future.zig index 0f27b4131b..f5d14d1ca6 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -6,15 +6,20 @@ const AtomicOrder = builtin.AtomicOrder; const Lock = std.event.Lock; const Loop = std.event.Loop; -/// This is a value that starts out unavailable, until a value is put(). +/// This is a value that starts out unavailable, until resolve() is called /// While it is unavailable, coroutines suspend when they try to get() it, -/// and then are resumed when the value is put(). -/// At this point the value remains forever available, and another put() is not allowed. +/// and then are resumed when resolve() is called. +/// At this point the value remains forever available, and another resolve() is not allowed. pub fn Future(comptime T: type) type { return struct { lock: Lock, data: T, - available: u8, // TODO make this a bool + + /// TODO make this an enum + /// 0 - not started + /// 1 - started + /// 2 - finished + available: u8, const Self = this; const Queue = std.atomic.Queue(promise); @@ -31,7 +36,7 @@ pub fn Future(comptime T: type) type { /// available. /// Thread-safe. pub async fn get(self: *Self) *T { - if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { + if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) { return &self.data; } const held = await (async self.lock.acquire() catch unreachable); @@ -43,18 +48,36 @@ pub fn Future(comptime T: type) type { /// Gets the data without waiting for it. If it's available, a pointer is /// returned. Otherwise, null is returned. pub fn getOrNull(self: *Self) ?*T { - if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { + if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) { return &self.data; } else { return null; } } + /// If someone else has started working on the data, wait for them to complete + /// and return a pointer to the data. Otherwise, return null, and the caller + /// should start working on the data. + /// It's not required to call start() before resolve() but it can be useful since + /// this method is thread-safe. + pub async fn start(self: *Self) ?*T { + const state = @cmpxchgStrong(u8, &self.available, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null; + switch (state) { + 1 => { + const held = await (async self.lock.acquire() catch unreachable); + held.release(); + return &self.data; + }, + 2 => return &self.data, + else => unreachable, + } + } + /// Make the data become available. May be called only once. /// Before calling this, modify the `data` property. pub fn resolve(self: *Self) void { - const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - assert(prev == 0); // put() called twice + const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst); + assert(prev == 0 or prev == 1); // resolve() called twice Lock.Held.release(Lock.Held{ .lock = &self.lock }); } }; diff --git a/std/index.zig b/std/index.zig index 3b523f519f..2f4cfb7553 100644 --- a/std/index.zig +++ b/std/index.zig @@ -36,6 +36,8 @@ pub const sort = @import("sort.zig"); pub const unicode = @import("unicode.zig"); pub const zig = @import("zig/index.zig"); +pub const lazyInit = @import("lazy_init.zig").lazyInit; + test "std" { // run tests from these _ = @import("atomic/index.zig"); @@ -71,4 +73,5 @@ test "std" { _ = @import("sort.zig"); _ = @import("unicode.zig"); _ = @import("zig/index.zig"); + _ = @import("lazy_init.zig"); } diff --git a/std/lazy_init.zig b/std/lazy_init.zig new file mode 100644 index 0000000000..c46c067810 --- /dev/null +++ b/std/lazy_init.zig @@ -0,0 +1,85 @@ +const std = @import("index.zig"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; + +/// Thread-safe initialization of global data. +/// TODO use a mutex instead of a spinlock +pub fn lazyInit(comptime T: type) LazyInit(T) { + return LazyInit(T){ + .data = undefined, + .state = 0, + }; +} + +fn LazyInit(comptime T: type) type { + return struct { + state: u8, // TODO make this an enum + data: Data, + + const Self = this; + + // TODO this isn't working for void, investigate and then remove this special case + const Data = if (@sizeOf(T) == 0) u8 else T; + const Ptr = if (T == void) void else *T; + + /// Returns a usable pointer to the initialized data, + /// or returns null, indicating that the caller should + /// perform the initialization and then call resolve(). + pub fn get(self: *Self) ?Ptr { + while (true) { + var state = @cmpxchgWeak(u8, &self.state, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null; + switch (state) { + 0 => continue, + 1 => { + // TODO mutex instead of a spinlock + continue; + }, + 2 => { + if (@sizeOf(T) == 0) { + return T(undefined); + } else { + return &self.data; + } + }, + else => unreachable, + } + } + } + + pub fn resolve(self: *Self) void { + const prev = @atomicRmw(u8, &self.state, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst); + assert(prev == 1); // resolve() called twice + } + }; +} + +var global_number = lazyInit(i32); + +test "std.lazyInit" { + if (global_number.get()) |_| @panic("bad") else { + global_number.data = 1234; + global_number.resolve(); + } + if (global_number.get()) |x| { + assert(x.* == 1234); + } else { + @panic("bad"); + } + if (global_number.get()) |x| { + assert(x.* == 1234); + } else { + @panic("bad"); + } +} + +var global_void = lazyInit(void); + +test "std.lazyInit(void)" { + if (global_void.get()) |_| @panic("bad") else { + global_void.resolve(); + } + assert(global_void.get() != null); + assert(global_void.get() != null); +} -- cgit v1.2.3 From 3bb00eac37c6b7be789f7b2f516fc1e44e892c8d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Jul 2018 00:01:36 -0400 Subject: self-hosted: implement getAppDataDir for windows --- src-self-hosted/compilation.zig | 64 ++++++++++++++++++++++++++++--- std/os/windows/index.zig | 85 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 6 deletions(-) diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index d5380a0644..4e7526a199 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -237,6 +237,7 @@ pub const Compilation = struct { ReadOnlyFileSystem, LinkQuotaExceeded, EnvironmentVariableNotFound, + AppDataDirUnavailable, }; pub const Event = union(enum) { @@ -944,13 +945,64 @@ async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void { } fn getZigDir(allocator: *mem.Allocator) ![]u8 { - const home_dir = try getHomeDir(allocator); - defer allocator.free(home_dir); + return getAppDataDir(allocator, "zig"); +} + + +const GetAppDataDirError = error{ + OutOfMemory, + AppDataDirUnavailable, +}; + + +/// Caller owns returned memory. +/// TODO move to zig std lib +fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { + switch (builtin.os) { + builtin.Os.windows => { + var dir_path_ptr: [*]u16 = undefined; + switch (os.windows.SHGetKnownFolderPath(&os.windows.FOLDERID_LocalAppData, os.windows.KF_FLAG_CREATE, + null, &dir_path_ptr,)) + { + os.windows.S_OK => { + defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); + const global_dir = try utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)); + defer allocator.free(global_dir); + return os.path.join(allocator, global_dir, appname); + }, + os.windows.E_OUTOFMEMORY => return error.OutOfMemory, + else => return error.AppDataDirUnavailable, + } + }, + // TODO for macos it should be "~/Library/Application Support/" + else => { + const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.EnvironmentVariableNotFound => return error.AppDataDirUnavailable, // TODO look in /etc/passwd + }; + defer allocator.free(home_dir); + return os.path.join(allocator, home_dir, ".local", "share", appname); + }, + } +} + +test "getAppDataDir" { + const result = try getAppDataDir(std.debug.global_allocator, "zig"); + std.debug.warn("{}...", result); +} - return os.path.join(allocator, home_dir, ".zig"); +// TODO full utf-16 LE support +fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 { + const utf8_bytes = try allocator.alloc(u8, utf16le.len); + for (utf16le) |codepoint, i| { + assert(codepoint < 127); // TODO full utf-16 LE support + utf8_bytes[i] = @intCast(u8, codepoint); + } + return utf8_bytes; } -/// TODO move to zig std lib, and make it work for other OSes -fn getHomeDir(allocator: *mem.Allocator) ![]u8 { - return os.getEnvVarOwned(allocator, "HOME"); +fn utf16lePtrSlice(ptr: [*]const u16) []const u16 { + var index: usize = 0; + while (ptr[index] != 0) : (index += 1) {} + return ptr[0..index]; } diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index d3525247c9..96c4d3861c 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -1,3 +1,5 @@ +const std = @import("../../index.zig"); +const assert = std.debug.assert; test "import" { _ = @import("util.zig"); } @@ -439,3 +441,86 @@ pub const SYSTEM_INFO = extern struct { wProcessorLevel: WORD, wProcessorRevision: WORD, }; + +pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; + +pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT; + +pub const HRESULT = c_long; + +pub const KNOWNFOLDERID = GUID; +pub const GUID = extern struct { + Data1: c_ulong, + Data2: c_ushort, + Data3: c_ushort, + Data4: [8]u8, + + pub fn parse(str: []const u8) GUID { + var guid: GUID = undefined; + var index: usize = 0; + assert(str[index] == '{'); + index += 1; + + guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index..index + 8], 16) catch unreachable; + index += 8; + + assert(str[index] == '-'); + index += 1; + + guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable; + index += 4; + + assert(str[index] == '-'); + index += 1; + + guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable; + index += 4; + + assert(str[index] == '-'); + index += 1; + + guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable; + index += 2; + guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable; + index += 2; + + assert(str[index] == '-'); + index += 1; + + var i: usize = 2; + while (i < guid.Data4.len) : (i += 1) { + guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable; + index += 2; + } + + assert(str[index] == '}'); + index += 1; + return guid; + } +}; + +pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}"); + +pub const KF_FLAG_DEFAULT = 0; +pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536; +pub const KF_FLAG_CREATE = 32768; +pub const KF_FLAG_DONT_VERIFY = 16384; +pub const KF_FLAG_DONT_UNEXPAND = 8192; +pub const KF_FLAG_NO_ALIAS = 4096; +pub const KF_FLAG_INIT = 2048; +pub const KF_FLAG_DEFAULT_PATH = 1024; +pub const KF_FLAG_NOT_PARENT_RELATIVE = 512; +pub const KF_FLAG_SIMPLE_IDLIST = 256; +pub const KF_FLAG_ALIAS_ONLY = -2147483648; + +pub const S_OK = 0; +pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001)); +pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002)); +pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003)); +pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004)); +pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005)); +pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF)); +pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005)); +pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006)); +pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E)); +pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057)); -- cgit v1.2.3 From 69e3b4e7dca816bbdd75207b8f44a5d7e0556a37 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Tue, 17 Jul 2018 23:27:18 +0900 Subject: revert commit 860d3da9156a0b1f4a1e3e644b423da3e768bb86 ; please see #1249 for more information; (#1255) --- src/ir.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 35b6b4cef4..a12bd054a7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -246,6 +246,8 @@ static void ir_ref_bb(IrBasicBlock *bb) { static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) { assert(instruction->id != IrInstructionIdInvalid); instruction->ref_count += 1; + if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction)) + ir_ref_bb(instruction->owner_bb); } static void ir_ref_var(VariableTableEntry *var) { -- cgit v1.2.3 From d1a60243c9e112ccd36334cec3ab55eea6f823a8 Mon Sep 17 00:00:00 2001 From: Wink Saville Date: Tue, 17 Jul 2018 07:28:08 -0700 Subject: Give ArrayList tests consistent names (#1253) The recent change that added swapRemove used std.ArrayList as the test name prefix. Change the other tests to use the same prefix for consistency and making it easier to use --test-filter. --- std/array_list.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/std/array_list.zig b/std/array_list.zig index 2c8b5070a7..8a9037a033 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -183,7 +183,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { }; } -test "basic ArrayList test" { +test "std.ArrayList.basic" { var bytes: [1024]u8 = undefined; const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; @@ -270,7 +270,7 @@ test "std.ArrayList.swapRemove" { assert(list.len == 4); } -test "iterator ArrayList test" { +test "std.ArrayList.iterator" { var list = ArrayList(i32).init(debug.global_allocator); defer list.deinit(); @@ -299,7 +299,7 @@ test "iterator ArrayList test" { assert(it.next().? == 1); } -test "insert ArrayList test" { +test "std.ArrayList.insert" { var list = ArrayList(i32).init(debug.global_allocator); defer list.deinit(); @@ -313,7 +313,7 @@ test "insert ArrayList test" { assert(list.items[3] == 3); } -test "insertSlice ArrayList test" { +test "std.ArrayList.insertSlice" { var list = ArrayList(i32).init(debug.global_allocator); defer list.deinit(); -- cgit v1.2.3 From 3cbf59b4c1a431ebd1ccb0cf46fa37e367b8106e Mon Sep 17 00:00:00 2001 From: Wink Saville Date: Tue, 17 Jul 2018 07:29:42 -0700 Subject: Add swapRemoveOrError (#1254) * Add swapRemoveOrError, this mirrors setOrError. --- std/array_list.zig | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/std/array_list.zig b/std/array_list.zig index 8a9037a033..298026d11c 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -113,6 +113,14 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return old_item; } + /// Removes the element at the specified index and returns it + /// or an error.OutOfBounds is returned. If no error then + /// the empty slot is filled from the end of the list. + pub fn swapRemoveOrError(self: *Self, i: usize) !T { + if (i >= self.len) return error.OutOfBounds; + return self.swapRemove(i); + } + pub fn appendSlice(self: *Self, items: []align(A) const T) !void { try self.ensureCapacity(self.len + items.len); mem.copy(T, self.items[self.len..], items); @@ -270,6 +278,34 @@ test "std.ArrayList.swapRemove" { assert(list.len == 4); } +test "std.ArrayList.swapRemoveOrError" { + var list = ArrayList(i32).init(debug.global_allocator); + defer list.deinit(); + + // Test just after initialization + assertError(list.swapRemoveOrError(0), error.OutOfBounds); + + // Test after adding one item and remote it + try list.append(1); + assert((try list.swapRemoveOrError(0)) == 1); + assertError(list.swapRemoveOrError(0), error.OutOfBounds); + + // Test after adding two items and remote both + try list.append(1); + try list.append(2); + assert((try list.swapRemoveOrError(1)) == 2); + assert((try list.swapRemoveOrError(0)) == 1); + assertError(list.swapRemoveOrError(0), error.OutOfBounds); + + // Test out of bounds with one item + try list.append(1); + assertError(list.swapRemoveOrError(1), error.OutOfBounds); + + // Test out of bounds with two items + try list.append(2); + assertError(list.swapRemoveOrError(2), error.OutOfBounds); +} + test "std.ArrayList.iterator" { var list = ArrayList(i32).init(debug.global_allocator); defer list.deinit(); -- cgit v1.2.3 From 1a7cf4cbce1e157067f27c289c8365c8612de395 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Jul 2018 10:42:44 -0400 Subject: port 69e3b4e to self-hosted compiler See #1249 --- src-self-hosted/ir.zig | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index c1f9c97001..e0c09b1e4b 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -53,6 +53,7 @@ pub const Instruction = struct { val: IrVal, ref_count: usize, span: Span, + owner_bb: *BasicBlock, /// true if this instruction was generated by zig and not from user code is_generated: bool, @@ -132,6 +133,13 @@ pub const Instruction = struct { } } + fn ref(base: *Instruction, builder: *Builder) void { + base.ref_count += 1; + if (base.owner_bb != builder.current_basic_block and !base.isCompTime()) { + base.owner_bb.ref(); + } + } + fn getAsParam(param: *Instruction) !*Instruction { const child = param.child orelse return error.SemanticAnalysisFailed; switch (child.val) { @@ -161,6 +169,10 @@ pub const Instruction = struct { } } + pub fn isCompTime(base: *const Instruction) bool { + return base.val == IrVal.KnownValue; + } + pub fn linkToParent(self: *Instruction, parent: *Instruction) void { assert(self.parent == null); assert(parent.child == null); @@ -816,6 +828,7 @@ pub const Builder = struct { .child = null, .parent = null, .llvm_value = undefined, + .owner_bb = self.current_basic_block, }, .params = params, }); @@ -825,8 +838,8 @@ pub const Builder = struct { inline while (i < @memberCount(I.Params)) : (i += 1) { const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i))); switch (FieldType) { - *Instruction => @field(inst.params, @memberName(I.Params, i)).ref_count += 1, - ?*Instruction => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref_count += 1, + *Instruction => @field(inst.params, @memberName(I.Params, i)).ref(self), + ?*Instruction => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref(self), else => {}, } } -- cgit v1.2.3 From ecf8da00c53b20085cc32e84030caf32e8b3e16b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Jul 2018 13:18:13 -0400 Subject: self-hosted: linking --- src-self-hosted/codegen.zig | 8 + src-self-hosted/compilation.zig | 39 +++-- src-self-hosted/link.zig | 314 ++++++++++++++++++++++++++++++++++++++++ src/zig_llvm.h | 3 + 4 files changed, 348 insertions(+), 16 deletions(-) create mode 100644 src-self-hosted/link.zig diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 28ba2a1564..f8233bc795 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -90,6 +90,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) llvm.DIBuilderFinalize(dibuilder); if (comp.verbose_llvm_ir) { + std.debug.warn("raw module:\n"); llvm.DumpModule(ofile.module); } @@ -122,6 +123,13 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) } //validate_inline_fns(g); TODO fn_val.containing_object = output_path; + if (comp.verbose_llvm_ir) { + std.debug.warn("optimized module:\n"); + llvm.DumpModule(ofile.module); + } + if (comp.verbose_link) { + std.debug.warn("created {}\n", output_path.toSliceConst()); + } } pub const ObjectFile = struct { diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 4e7526a199..5cb7565a98 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -27,6 +27,7 @@ const Type = Value.Type; const Span = errmsg.Span; const codegen = @import("codegen.zig"); const Package = @import("package.zig").Package; +const link = @import("link.zig").link; /// Data that is local to the event loop. pub const EventLoopLocal = struct { @@ -238,6 +239,7 @@ pub const Compilation = struct { LinkQuotaExceeded, EnvironmentVariableNotFound, AppDataDirUnavailable, + LinkFailed, }; pub const Event = union(enum) { @@ -563,8 +565,7 @@ pub const Compilation = struct { async fn buildAsync(self: *Compilation) void { while (true) { // TODO directly awaiting async should guarantee memory allocation elision - // TODO also async before suspending should guarantee memory allocation elision - const build_result = await (async self.addRootSrc() catch unreachable); + const build_result = await (async self.compileAndLink() catch unreachable); // this makes a handy error return trace and stack trace in debug mode if (std.debug.runtime_safety) { @@ -595,7 +596,7 @@ pub const Compilation = struct { } } - async fn addRootSrc(self: *Compilation) !void { + async fn compileAndLink(self: *Compilation) !void { const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); // TODO async/await os.path.real const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { @@ -669,6 +670,17 @@ pub const Compilation = struct { } try await (async decl_group.wait() catch unreachable); try await (async self.prelink_group.wait() catch unreachable); + + const any_prelink_errors = blk: { + const compile_errors = await (async self.compile_errors.acquire() catch unreachable); + defer compile_errors.release(); + + break :blk compile_errors.value.len != 0; + }; + + if (!any_prelink_errors) { + try link(self); + } } async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void { @@ -722,11 +734,6 @@ pub const Compilation = struct { } } - pub fn link(self: *Compilation, out_file: ?[]const u8) !void { - warn("TODO link"); - return error.Todo; - } - pub fn haveLibC(self: *Compilation) bool { return self.libc_link_lib != null; } @@ -882,9 +889,8 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name); errdefer symbol_name.deinit(); + // The Decl.Fn owns the initial 1 reference count const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); - defer fn_val.base.deref(comp); - fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; const unanalyzed_code = (await (async ir.gen( @@ -948,22 +954,23 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 { return getAppDataDir(allocator, "zig"); } - const GetAppDataDirError = error{ OutOfMemory, AppDataDirUnavailable, }; - /// Caller owns returned memory. /// TODO move to zig std lib fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { switch (builtin.os) { builtin.Os.windows => { var dir_path_ptr: [*]u16 = undefined; - switch (os.windows.SHGetKnownFolderPath(&os.windows.FOLDERID_LocalAppData, os.windows.KF_FLAG_CREATE, - null, &dir_path_ptr,)) - { + switch (os.windows.SHGetKnownFolderPath( + &os.windows.FOLDERID_LocalAppData, + os.windows.KF_FLAG_CREATE, + null, + &dir_path_ptr, + )) { os.windows.S_OK => { defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); const global_dir = try utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)); @@ -974,7 +981,7 @@ fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirEr else => return error.AppDataDirUnavailable, } }, - // TODO for macos it should be "~/Library/Application Support/" + // TODO for macos it should be "~/Library/Application Support/" else => { const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig new file mode 100644 index 0000000000..915451c931 --- /dev/null +++ b/src-self-hosted/link.zig @@ -0,0 +1,314 @@ +const std = @import("std"); +const c = @import("c.zig"); +const builtin = @import("builtin"); +const ObjectFormat = builtin.ObjectFormat; +const Compilation = @import("compilation.zig").Compilation; + +const Context = struct { + comp: *Compilation, + arena: std.heap.ArenaAllocator, + args: std.ArrayList([*]const u8), + link_in_crt: bool, + + link_err: error{OutOfMemory}!void, + link_msg: std.Buffer, +}; + +pub fn link(comp: *Compilation) !void { + var ctx = Context{ + .comp = comp, + .arena = std.heap.ArenaAllocator.init(comp.gpa()), + .args = undefined, + .link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe, + .link_err = {}, + .link_msg = undefined, + }; + defer ctx.arena.deinit(); + ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator); + ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator); + + // even though we're calling LLD as a library it thinks the first + // argument is its own exe name + try ctx.args.append(c"lld"); + + try constructLinkerArgs(&ctx); + + if (comp.verbose_link) { + for (ctx.args.toSliceConst()) |arg, i| { + const space = if (i == 0) "" else " "; + std.debug.warn("{}{s}", space, arg); + } + std.debug.warn("\n"); + } + + const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat()); + const args_slice = ctx.args.toSlice(); + if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) { + if (!ctx.link_msg.isNull()) { + // TODO capture these messages and pass them through the system, reporting them through the + // event system instead of printing them directly here. + // perhaps try to parse and understand them. + std.debug.warn("{}\n", ctx.link_msg.toSliceConst()); + } + return error.LinkFailed; + } +} + +extern fn ZigLLDLink( + oformat: c.ZigLLVM_ObjectFormatType, + args: [*]const [*]const u8, + arg_count: usize, + append_diagnostic: extern fn (*c_void, [*]const u8, usize) void, + context: *c_void, +) bool; + +extern fn linkDiagCallback(context: *c_void, ptr: [*]const u8, len: usize) void { + const ctx = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); + ctx.link_err = linkDiagCallbackErrorable(ctx, ptr[0..len]); +} + +fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void { + if (ctx.link_msg.isNull()) { + try ctx.link_msg.resize(0); + } + try ctx.link_msg.append(msg); +} + +fn toExternObjectFormatType(ofmt: ObjectFormat) c.ZigLLVM_ObjectFormatType { + return switch (ofmt) { + ObjectFormat.unknown => c.ZigLLVM_UnknownObjectFormat, + ObjectFormat.coff => c.ZigLLVM_COFF, + ObjectFormat.elf => c.ZigLLVM_ELF, + ObjectFormat.macho => c.ZigLLVM_MachO, + ObjectFormat.wasm => c.ZigLLVM_Wasm, + }; +} + +fn constructLinkerArgs(ctx: *Context) !void { + switch (ctx.comp.target.getObjectFormat()) { + ObjectFormat.unknown => unreachable, + ObjectFormat.coff => return constructLinkerArgsCoff(ctx), + ObjectFormat.elf => return constructLinkerArgsElf(ctx), + ObjectFormat.macho => return constructLinkerArgsMachO(ctx), + ObjectFormat.wasm => return constructLinkerArgsWasm(ctx), + } +} + +fn constructLinkerArgsElf(ctx: *Context) !void { + //if (g->libc_link_lib != nullptr) { + // find_libc_lib_path(g); + //} + + //if (g->linker_script) { + // lj->args.append("-T"); + // lj->args.append(g->linker_script); + //} + + //if (g->no_rosegment_workaround) { + // lj->args.append("--no-rosegment"); + //} + //lj->args.append("--gc-sections"); + + //lj->args.append("-m"); + //lj->args.append(getLDMOption(&g->zig_target)); + + //bool is_lib = g->out_type == OutTypeLib; + //bool shared = !g->is_static && is_lib; + //Buf *soname = nullptr; + //if (g->is_static) { + // if (g->zig_target.arch.arch == ZigLLVM_arm || g->zig_target.arch.arch == ZigLLVM_armeb || + // g->zig_target.arch.arch == ZigLLVM_thumb || g->zig_target.arch.arch == ZigLLVM_thumbeb) + // { + // lj->args.append("-Bstatic"); + // } else { + // lj->args.append("-static"); + // } + //} else if (shared) { + // lj->args.append("-shared"); + + // if (buf_len(&lj->out_file) == 0) { + // buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "", + // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); + // } + // soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major); + //} + + //lj->args.append("-o"); + //lj->args.append(buf_ptr(&lj->out_file)); + + //if (lj->link_in_crt) { + // const char *crt1o; + // const char *crtbegino; + // if (g->is_static) { + // crt1o = "crt1.o"; + // crtbegino = "crtbeginT.o"; + // } else { + // crt1o = "Scrt1.o"; + // crtbegino = "crtbegin.o"; + // } + // lj->args.append(get_libc_file(g, crt1o)); + // lj->args.append(get_libc_file(g, "crti.o")); + // lj->args.append(get_libc_static_file(g, crtbegino)); + //} + + //for (size_t i = 0; i < g->rpath_list.length; i += 1) { + // Buf *rpath = g->rpath_list.at(i); + // add_rpath(lj, rpath); + //} + //if (g->each_lib_rpath) { + // for (size_t i = 0; i < g->lib_dirs.length; i += 1) { + // const char *lib_dir = g->lib_dirs.at(i); + // for (size_t i = 0; i < g->link_libs_list.length; i += 1) { + // LinkLib *link_lib = g->link_libs_list.at(i); + // if (buf_eql_str(link_lib->name, "c")) { + // continue; + // } + // bool does_exist; + // Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name)); + // if (os_file_exists(test_path, &does_exist) != ErrorNone) { + // zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path)); + // } + // if (does_exist) { + // add_rpath(lj, buf_create_from_str(lib_dir)); + // break; + // } + // } + // } + //} + + //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { + // const char *lib_dir = g->lib_dirs.at(i); + // lj->args.append("-L"); + // lj->args.append(lib_dir); + //} + + //if (g->libc_link_lib != nullptr) { + // lj->args.append("-L"); + // lj->args.append(buf_ptr(g->libc_lib_dir)); + + // lj->args.append("-L"); + // lj->args.append(buf_ptr(g->libc_static_lib_dir)); + //} + + //if (!g->is_static) { + // if (g->dynamic_linker != nullptr) { + // assert(buf_len(g->dynamic_linker) != 0); + // lj->args.append("-dynamic-linker"); + // lj->args.append(buf_ptr(g->dynamic_linker)); + // } else { + // Buf *resolved_dynamic_linker = get_dynamic_linker_path(g); + // lj->args.append("-dynamic-linker"); + // lj->args.append(buf_ptr(resolved_dynamic_linker)); + // } + //} + + //if (shared) { + // lj->args.append("-soname"); + // lj->args.append(buf_ptr(soname)); + //} + + // .o files + for (ctx.comp.link_objects) |link_object| { + const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); + try ctx.args.append(link_obj_with_null.ptr); + } + try addFnObjects(ctx); + + //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { + // if (g->libc_link_lib == nullptr) { + // Buf *builtin_o_path = build_o(g, "builtin"); + // lj->args.append(buf_ptr(builtin_o_path)); + // } + + // // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage + // Buf *compiler_rt_o_path = build_compiler_rt(g); + // lj->args.append(buf_ptr(compiler_rt_o_path)); + //} + + //for (size_t i = 0; i < g->link_libs_list.length; i += 1) { + // LinkLib *link_lib = g->link_libs_list.at(i); + // if (buf_eql_str(link_lib->name, "c")) { + // continue; + // } + // Buf *arg; + // if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") || + // buf_ends_with_str(link_lib->name, ".so")) + // { + // arg = link_lib->name; + // } else { + // arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); + // } + // lj->args.append(buf_ptr(arg)); + //} + + //// libc dep + //if (g->libc_link_lib != nullptr) { + // if (g->is_static) { + // lj->args.append("--start-group"); + // lj->args.append("-lgcc"); + // lj->args.append("-lgcc_eh"); + // lj->args.append("-lc"); + // lj->args.append("-lm"); + // lj->args.append("--end-group"); + // } else { + // lj->args.append("-lgcc"); + // lj->args.append("--as-needed"); + // lj->args.append("-lgcc_s"); + // lj->args.append("--no-as-needed"); + // lj->args.append("-lc"); + // lj->args.append("-lm"); + // lj->args.append("-lgcc"); + // lj->args.append("--as-needed"); + // lj->args.append("-lgcc_s"); + // lj->args.append("--no-as-needed"); + // } + //} + + //// crt end + //if (lj->link_in_crt) { + // lj->args.append(get_libc_static_file(g, "crtend.o")); + // lj->args.append(get_libc_file(g, "crtn.o")); + //} + + //if (!g->is_native_target) { + // lj->args.append("--allow-shlib-undefined"); + //} + + //if (g->zig_target.os == OsZen) { + // lj->args.append("-e"); + // lj->args.append("_start"); + + // lj->args.append("--image-base=0x10000000"); + //} +} + +fn constructLinkerArgsCoff(ctx: *Context) void { + @panic("TODO"); +} + +fn constructLinkerArgsMachO(ctx: *Context) void { + @panic("TODO"); +} + +fn constructLinkerArgsWasm(ctx: *Context) void { + @panic("TODO"); +} + +fn addFnObjects(ctx: *Context) !void { + // at this point it's guaranteed nobody else has this lock, so we circumvent it + // and avoid having to be a coroutine + const fn_link_set = &ctx.comp.fn_link_set.private_data; + + var it = fn_link_set.first; + while (it) |node| { + const fn_val = node.data orelse { + // handle the tombstone. See Value.Fn.destroy. + it = node.next; + fn_link_set.remove(node); + ctx.comp.gpa().destroy(node); + continue; + }; + try ctx.args.append(fn_val.containing_object.ptr()); + it = node.next; + } +} diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 6f25df8674..63d69bd23e 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -22,6 +22,9 @@ #define ZIG_EXTERN_C #endif +// ATTENTION: If you modify this file, be sure to update the corresponding +// extern function declarations in the self-hosted compiler. + struct ZigLLVMDIType; struct ZigLLVMDIBuilder; struct ZigLLVMDICompileUnit; -- cgit v1.2.3 From a9ab528e348a3f07dfaffcdcb1031062b1bfe8bb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Jul 2018 15:17:06 -0400 Subject: std.event.Loop.onNextTick dispatches work to waiting threads --- std/atomic/queue.zig | 14 ++++ std/event/loop.zig | 205 +++++++++++++++++++++++++-------------------------- 2 files changed, 113 insertions(+), 106 deletions(-) diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 1fd07714e8..df31c88d2a 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -51,6 +51,20 @@ pub fn Queue(comptime T: type) type { return head; } + pub fn unget(self: *Self, node: *Node) void { + while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} + defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + + const opt_head = self.head; + self.head = node; + if (opt_head) |head| { + head.next = node; + } else { + assert(self.tail == null); + self.tail = node; + } + } + pub fn isEmpty(self: *Self) bool { return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null; } diff --git a/std/event/loop.zig b/std/event/loop.zig index fc927592b9..e2bc9e7eb9 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -12,7 +12,6 @@ pub const Loop = struct { next_tick_queue: std.atomic.Queue(promise), os_data: OsData, final_resume_node: ResumeNode, - dispatch_lock: u8, // TODO make this a bool pending_event_count: usize, extra_threads: []*std.os.Thread, @@ -74,11 +73,10 @@ pub const Loop = struct { /// max(thread_count - 1, 0) fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void { self.* = Loop{ - .pending_event_count = 0, + .pending_event_count = 1, .allocator = allocator, .os_data = undefined, .next_tick_queue = std.atomic.Queue(promise).init(), - .dispatch_lock = 1, // start locked so threads go directly into epoll wait .extra_threads = undefined, .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(), .eventfd_resume_nodes = undefined, @@ -306,7 +304,7 @@ pub const Loop = struct { pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void { _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); errdefer { - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + self.finishOneEvent(); } try self.modFd( fd, @@ -326,7 +324,7 @@ pub const Loop = struct { pub fn removeFd(self: *Loop, fd: i32) void { self.removeFdNoCounter(fd); - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + self.finishOneEvent(); } fn removeFdNoCounter(self: *Loop, fd: i32) void { @@ -345,14 +343,70 @@ pub const Loop = struct { } } + fn dispatch(self: *Loop) void { + while (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| { + const next_tick_node = self.next_tick_queue.get() orelse { + self.available_eventfd_resume_nodes.push(resume_stack_node); + return; + }; + const eventfd_node = &resume_stack_node.data; + eventfd_node.base.handle = next_tick_node.data; + switch (builtin.os) { + builtin.Os.macosx => { + const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); + const eventlist = ([*]posix.Kevent)(undefined)[0..0]; + _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch { + self.next_tick_queue.unget(next_tick_node); + self.available_eventfd_resume_nodes.push(resume_stack_node); + return; + }; + }, + builtin.Os.linux => { + // the pending count is already accounted for + const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | + std.os.linux.EPOLLET; + self.modFd( + eventfd_node.eventfd, + eventfd_node.epoll_op, + epoll_events, + &eventfd_node.base, + ) catch { + self.next_tick_queue.unget(next_tick_node); + self.available_eventfd_resume_nodes.push(resume_stack_node); + return; + }; + }, + builtin.Os.windows => { + // this value is never dereferenced but we need it to be non-null so that + // the consumer code can decide whether to read the completion key. + // it has to do this for normal I/O, so we match that behavior here. + const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); + std.os.windowsPostQueuedCompletionStatus( + self.os_data.io_port, + undefined, + eventfd_node.completion_key, + overlapped, + ) catch { + self.next_tick_queue.unget(next_tick_node); + self.available_eventfd_resume_nodes.push(resume_stack_node); + return; + }; + }, + else => @compileError("unsupported OS"), + } + } + } + /// Bring your own linked list node. This means it can't fail. pub fn onNextTick(self: *Loop, node: *NextTickNode) void { _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); self.next_tick_queue.put(node); + self.dispatch(); } pub fn run(self: *Loop) void { - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + self.finishOneEvent(); // the reference we start with + self.workerRun(); for (self.extra_threads) |extra_thread| { extra_thread.wait(); @@ -396,106 +450,45 @@ pub const Loop = struct { } } - fn workerRun(self: *Loop) void { - start_over: while (true) { - if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { - while (self.next_tick_queue.get()) |next_tick_node| { - const handle = next_tick_node.data; - if (self.next_tick_queue.isEmpty()) { - // last node, just resume it - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - resume handle; - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - continue :start_over; - } - - // non-last node, stick it in the epoll/kqueue set so that - // other threads can get to it - if (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| { - const eventfd_node = &resume_stack_node.data; - eventfd_node.base.handle = handle; - switch (builtin.os) { - builtin.Os.macosx => { - const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); - const eventlist = ([*]posix.Kevent)(undefined)[0..0]; - _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch { - // fine, we didn't need it anyway - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - self.available_eventfd_resume_nodes.push(resume_stack_node); - resume handle; - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - continue :start_over; - }; - }, - builtin.Os.linux => { - // the pending count is already accounted for - const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET; - self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch { - // fine, we didn't need it anyway - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - self.available_eventfd_resume_nodes.push(resume_stack_node); - resume handle; - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - continue :start_over; - }; - }, - builtin.Os.windows => { - // this value is never dereferenced but we need it to be non-null so that - // the consumer code can decide whether to read the completion key. - // it has to do this for normal I/O, so we match that behavior here. - const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); - std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, eventfd_node.completion_key, overlapped) catch { - // fine, we didn't need it anyway - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - self.available_eventfd_resume_nodes.push(resume_stack_node); - resume handle; - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - continue :start_over; - }; - }, - else => @compileError("unsupported OS"), + fn finishOneEvent(self: *Loop) void { + if (@atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) == 1) { + // cause all the threads to stop + switch (builtin.os) { + builtin.Os.linux => { + // writing 8 bytes to an eventfd cannot fail + std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + return; + }, + builtin.Os.macosx => { + const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent); + const eventlist = ([*]posix.Kevent)(undefined)[0..0]; + // cannot fail because we already added it and this just enables it + _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable; + return; + }, + builtin.Os.windows => { + var i: usize = 0; + while (i < self.os_data.extra_thread_count) : (i += 1) { + while (true) { + const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); + std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; + break; } - } else { - // threads are too busy, can't add another eventfd to wake one up - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - resume handle; - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); - continue :start_over; } - } - - const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst); - if (pending_event_count == 0) { - // cause all the threads to stop - switch (builtin.os) { - builtin.Os.linux => { - // writing 8 bytes to an eventfd cannot fail - std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; - return; - }, - builtin.Os.macosx => { - const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent); - const eventlist = ([*]posix.Kevent)(undefined)[0..0]; - // cannot fail because we already added it and this just enables it - _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable; - return; - }, - builtin.Os.windows => { - var i: usize = 0; - while (i < self.os_data.extra_thread_count) : (i += 1) { - while (true) { - const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); - std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; - break; - } - } - return; - }, - else => @compileError("unsupported OS"), - } - } + return; + }, + else => @compileError("unsupported OS"), + } + } + } - _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + fn workerRun(self: *Loop) void { + while (true) { + while (true) { + const next_tick_node = self.next_tick_queue.get() orelse break; + self.dispatch(); + resume next_tick_node.data; + self.finishOneEvent(); } switch (builtin.os) { @@ -519,7 +512,7 @@ pub const Loop = struct { } resume handle; if (resume_node_id == ResumeNode.Id.EventFd) { - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + self.finishOneEvent(); } } }, @@ -541,7 +534,7 @@ pub const Loop = struct { } resume handle; if (resume_node_id == ResumeNode.Id.EventFd) { - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + self.finishOneEvent(); } } }, @@ -570,7 +563,7 @@ pub const Loop = struct { } resume handle; if (resume_node_id == ResumeNode.Id.EventFd) { - _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + self.finishOneEvent(); } }, else => @compileError("unsupported OS"), -- cgit v1.2.3 From 6394f7e9a3f0d6db58287dee96af3cb9fae8ee8a Mon Sep 17 00:00:00 2001 From: Jay Weisskopf Date: Tue, 17 Jul 2018 21:18:41 -0400 Subject: Fixed minor documentation errors (#1256) Changed: - "retuns" to "returns" - "null-terminated pointers" to "pointers to null-terminated arrays" --- doc/langref.html.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 46b325832b..60ba09d391 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1087,7 +1087,7 @@ unwrapped == 1234 If a is false, returns false - without evaluating b. Otherwise, retuns b. + without evaluating b. Otherwise, returns b.
    false and true == false
    @@ -1102,7 +1102,7 @@ unwrapped == 1234 If a is true, returns true - without evaluating b. Otherwise, retuns b. + without evaluating b. Otherwise, returns b.
    false or true == true
    @@ -1483,7 +1483,7 @@ test "pointer array access" { } test "pointer slicing" { - // In Zig, we prefer using slices over null-terminated pointers. + // In Zig, we prefer slices over pointers to null-terminated arrays. // You can turn an array into a slice using slice syntax: var array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; const slice = array[2..4]; -- cgit v1.2.3 From cbfe9a407776d3b56a4da2a3bc3108a2f31b9212 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Jul 2018 23:37:17 -0400 Subject: fix @setEvalBranchQuota not respected in generic fn calls closes #1257 --- src/ir.cpp | 1 + test/cases/eval.zig | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index a12bd054a7..96ade9d392 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13092,6 +13092,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal impl_fn->ir_executable.parent_exec = ira->new_irb.exec; impl_fn->analyzed_executable.source_node = call_instruction->base.source_node; impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec; + impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota; impl_fn->analyzed_executable.is_generic_instantiation = true; ira->codegen->fn_defs.append(impl_fn); diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 83d2e80176..9da475994d 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -642,3 +642,13 @@ test "@tagName of @typeId" { const str = @tagName(@typeId(u8)); assert(std.mem.eql(u8, str, "Int")); } + +test "setting backward branch quota just before a generic fn call" { + @setEvalBranchQuota(1001); + loopNTimes(1001); +} + +fn loopNTimes(comptime n: usize) void { + comptime var i = 0; + inline while (i < n) : (i += 1) {} +} -- cgit v1.2.3 From 843529d23415efcf3fe894c48b3d08c0925edc08 Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Wed, 18 Jul 2018 03:01:01 -0400 Subject: implement proper utf16leToUtf8 --- src-self-hosted/compilation.zig | 89 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 6 deletions(-) diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 5cb7565a98..5ffb43667d 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -4,6 +4,7 @@ const io = std.io; const mem = std.mem; const Allocator = mem.Allocator; const Buffer = std.Buffer; +const unicode = std.unicode; const llvm = @import("llvm.zig"); const c = @import("c.zig"); const builtin = @import("builtin"); @@ -998,14 +999,90 @@ test "getAppDataDir" { std.debug.warn("{}...", result); } -// TODO full utf-16 LE support +// TODO: put general purpose stuff in std.unicode fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 { - const utf8_bytes = try allocator.alloc(u8, utf16le.len); - for (utf16le) |codepoint, i| { - assert(codepoint < 127); // TODO full utf-16 LE support - utf8_bytes[i] = @intCast(u8, codepoint); + var result = ArrayList(u8).init(allocator); + // optimistically guess that it will all be ascii. + try result.ensureCapacity(utf16le.len); + + const utf16le_as_bytes = @sliceToBytes(utf16le); + var i: usize = 0; + var out_index: usize = 0; + while (i < utf16le_as_bytes.len) : (i += 2) { + // decode + const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]); + var codepoint: u32 = undefined; + if (c0 & ~u32(0x03ff) == 0xd800) { + // surrogate pair + i += 2; + if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf; + const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]); + if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf; + codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)); + } else if (c0 & ~u32(0x03ff) == 0xdc00) { + return error.UnexpectedSecondSurrogateHalf; + } else { + codepoint = c0; + } + + // encode + const utf8_len = unicode.utf8CodepointSequenceLength(codepoint) catch unreachable; + try result.resize(result.len + utf8_len); + _ = unicode.utf8Encode(codepoint, result.items[out_index..]) catch unreachable; + out_index += utf8_len; + } + + return result.toOwnedSlice(); +} + +test "utf16leToUtf8" { + var utf16le: [2]u16 = undefined; + const utf16le_as_bytes = @sliceToBytes(utf16le[0..]); + + { + mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "Aa")); + } + + { + mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf")); + } + + { + // the values just outside the surrogate half range + mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80")); + } + + { + // smallest surrogate pair + mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80")); + } + + { + // largest surrogate pair + mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf")); + } + + { + mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80")); } - return utf8_bytes; } fn utf16lePtrSlice(ptr: [*]const u16) []const u16 { -- cgit v1.2.3 From b7be082bd9aaba4cbf7c7c7449e481f9caa1dc24 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Wed, 18 Jul 2018 10:28:14 +0200 Subject: -Dskip-release now also skips build example tests --- build.zig | 2 +- test/tests.zig | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/build.zig b/build.zig index c9e70887e3..e7a5c5cba1 100644 --- a/build.zig +++ b/build.zig @@ -92,7 +92,7 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes)); test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); - test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); + test_step.dependOn(tests.addBuildExampleTests(b, test_filter, modes)); test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); diff --git a/test/tests.zig b/test/tests.zig index 3a72f58753..aa5eed17ee 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -89,12 +89,13 @@ pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes: return cases.step; } -pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { +pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { const cases = b.allocator.create(BuildExamplesContext{ .b = b, .step = b.step("test-build-examples", "Build the examples"), .test_index = 0, .test_filter = test_filter, + .modes = modes, }) catch unreachable; build_examples.addCases(cases); @@ -697,6 +698,7 @@ pub const BuildExamplesContext = struct { step: *build.Step, test_index: usize, test_filter: ?[]const u8, + modes: []const Mode, pub fn addC(self: *BuildExamplesContext, root_src: []const u8) void { self.addAllArgs(root_src, true); @@ -739,12 +741,7 @@ pub const BuildExamplesContext = struct { pub fn addAllArgs(self: *BuildExamplesContext, root_src: []const u8, link_libc: bool) void { const b = self.b; - for ([]Mode{ - Mode.Debug, - Mode.ReleaseSafe, - Mode.ReleaseFast, - Mode.ReleaseSmall, - }) |mode| { + for (self.modes) |mode| { const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", root_src, @tagName(mode)) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; -- cgit v1.2.3 From a8a1b5af07519b49b5ba31ad6c802a7ccc8a5e23 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Jul 2018 10:07:22 -0400 Subject: fix build on windows * move getAppDataDir and utf16leToUtf8 from self-hosted to std lib * fix std.event.Loop on windows --- CMakeLists.txt | 1 + src-self-hosted/compilation.zig | 139 +--------------------------------------- std/event/loop.zig | 4 +- std/os/get_app_data_dir.zig | 60 +++++++++++++++++ std/os/index.zig | 4 ++ std/unicode.zig | 89 +++++++++++++++++++++++++ 6 files changed, 156 insertions(+), 141 deletions(-) create mode 100644 std/os/get_app_data_dir.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e7c1df350..096ac50cfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -557,6 +557,7 @@ set(ZIG_STD_FILES "os/darwin_errno.zig" "os/epoch.zig" "os/file.zig" + "os/get_app_data_dir.zig" "os/get_user_id.zig" "os/index.zig" "os/linux/errno.zig" diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 5ffb43667d..741324c871 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -4,7 +4,6 @@ const io = std.io; const mem = std.mem; const Allocator = mem.Allocator; const Buffer = std.Buffer; -const unicode = std.unicode; const llvm = @import("llvm.zig"); const c = @import("c.zig"); const builtin = @import("builtin"); @@ -952,141 +951,5 @@ async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void { } fn getZigDir(allocator: *mem.Allocator) ![]u8 { - return getAppDataDir(allocator, "zig"); -} - -const GetAppDataDirError = error{ - OutOfMemory, - AppDataDirUnavailable, -}; - -/// Caller owns returned memory. -/// TODO move to zig std lib -fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { - switch (builtin.os) { - builtin.Os.windows => { - var dir_path_ptr: [*]u16 = undefined; - switch (os.windows.SHGetKnownFolderPath( - &os.windows.FOLDERID_LocalAppData, - os.windows.KF_FLAG_CREATE, - null, - &dir_path_ptr, - )) { - os.windows.S_OK => { - defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); - const global_dir = try utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)); - defer allocator.free(global_dir); - return os.path.join(allocator, global_dir, appname); - }, - os.windows.E_OUTOFMEMORY => return error.OutOfMemory, - else => return error.AppDataDirUnavailable, - } - }, - // TODO for macos it should be "~/Library/Application Support/" - else => { - const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.EnvironmentVariableNotFound => return error.AppDataDirUnavailable, // TODO look in /etc/passwd - }; - defer allocator.free(home_dir); - return os.path.join(allocator, home_dir, ".local", "share", appname); - }, - } -} - -test "getAppDataDir" { - const result = try getAppDataDir(std.debug.global_allocator, "zig"); - std.debug.warn("{}...", result); -} - -// TODO: put general purpose stuff in std.unicode -fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 { - var result = ArrayList(u8).init(allocator); - // optimistically guess that it will all be ascii. - try result.ensureCapacity(utf16le.len); - - const utf16le_as_bytes = @sliceToBytes(utf16le); - var i: usize = 0; - var out_index: usize = 0; - while (i < utf16le_as_bytes.len) : (i += 2) { - // decode - const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]); - var codepoint: u32 = undefined; - if (c0 & ~u32(0x03ff) == 0xd800) { - // surrogate pair - i += 2; - if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf; - const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]); - if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf; - codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)); - } else if (c0 & ~u32(0x03ff) == 0xdc00) { - return error.UnexpectedSecondSurrogateHalf; - } else { - codepoint = c0; - } - - // encode - const utf8_len = unicode.utf8CodepointSequenceLength(codepoint) catch unreachable; - try result.resize(result.len + utf8_len); - _ = unicode.utf8Encode(codepoint, result.items[out_index..]) catch unreachable; - out_index += utf8_len; - } - - return result.toOwnedSlice(); -} - -test "utf16leToUtf8" { - var utf16le: [2]u16 = undefined; - const utf16le_as_bytes = @sliceToBytes(utf16le[0..]); - - { - mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little); - const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); - assert(mem.eql(u8, utf8, "Aa")); - } - - { - mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little); - const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); - assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf")); - } - - { - // the values just outside the surrogate half range - mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little); - const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); - assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80")); - } - - { - // smallest surrogate pair - mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); - const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); - assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80")); - } - - { - // largest surrogate pair - mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little); - const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); - assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf")); - } - - { - mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); - const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); - assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80")); - } -} - -fn utf16lePtrSlice(ptr: [*]const u16) []const u16 { - var index: usize = 0; - while (ptr[index] != 0) : (index += 1) {} - return ptr[0..index]; + return os.getAppDataDir(allocator, "zig"); } diff --git a/std/event/loop.zig b/std/event/loop.zig index e2bc9e7eb9..485a5be19c 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -233,8 +233,6 @@ pub const Loop = struct { } }, builtin.Os.windows => { - self.os_data.extra_thread_count = extra_thread_count; - self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( windows.INVALID_HANDLE_VALUE, null, @@ -468,7 +466,7 @@ pub const Loop = struct { }, builtin.Os.windows => { var i: usize = 0; - while (i < self.os_data.extra_thread_count) : (i += 1) { + while (i < self.extra_threads.len + 1) : (i += 1) { while (true) { const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; diff --git a/std/os/get_app_data_dir.zig b/std/os/get_app_data_dir.zig new file mode 100644 index 0000000000..b5efdb826a --- /dev/null +++ b/std/os/get_app_data_dir.zig @@ -0,0 +1,60 @@ +const std = @import("../index.zig"); +const builtin = @import("builtin"); +const unicode = std.unicode; +const mem = std.mem; +const os = std.os; + +pub const GetAppDataDirError = error{ + OutOfMemory, + AppDataDirUnavailable, +}; + +/// Caller owns returned memory. +pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { + switch (builtin.os) { + builtin.Os.windows => { + var dir_path_ptr: [*]u16 = undefined; + switch (os.windows.SHGetKnownFolderPath( + &os.windows.FOLDERID_LocalAppData, + os.windows.KF_FLAG_CREATE, + null, + &dir_path_ptr, + )) { + os.windows.S_OK => { + defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); + const global_dir = unicode.utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) { + error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable, + error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable, + error.DanglingSurrogateHalf => return error.AppDataDirUnavailable, + error.OutOfMemory => return error.OutOfMemory, + }; + defer allocator.free(global_dir); + return os.path.join(allocator, global_dir, appname); + }, + os.windows.E_OUTOFMEMORY => return error.OutOfMemory, + else => return error.AppDataDirUnavailable, + } + }, + // TODO for macos it should be "~/Library/Application Support/" + else => { + const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.EnvironmentVariableNotFound => return error.AppDataDirUnavailable, // TODO look in /etc/passwd + }; + defer allocator.free(home_dir); + return os.path.join(allocator, home_dir, ".local", "share", appname); + }, + } +} + +fn utf16lePtrSlice(ptr: [*]const u16) []const u16 { + var index: usize = 0; + while (ptr[index] != 0) : (index += 1) {} + return ptr[0..index]; +} + +test "getAppDataDir" { + const result = try getAppDataDir(std.debug.global_allocator, "zig"); + std.debug.warn("{}...", result); +} + diff --git a/std/os/index.zig b/std/os/index.zig index 79b2d2ff53..cb4358af4d 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -18,6 +18,7 @@ test "std.os" { _ = @import("test.zig"); _ = @import("time.zig"); _ = @import("windows/index.zig"); + _ = @import("get_app_data_dir.zig"); } pub const windows = @import("windows/index.zig"); @@ -76,6 +77,9 @@ pub const WindowsWriteError = windows_util.WriteError; pub const FileHandle = if (is_windows) windows.HANDLE else i32; +pub const getAppDataDir = @import("get_app_data_dir.zig").getAppDataDir; +pub const GetAppDataDirError = @import("get_app_data_dir.zig").GetAppDataDirError; + const debug = std.debug; const assert = debug.assert; diff --git a/std/unicode.zig b/std/unicode.zig index 9c329acc68..8a9d4a9214 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -1,5 +1,8 @@ const std = @import("./index.zig"); +const builtin = @import("builtin"); const debug = std.debug; +const assert = std.debug.assert; +const mem = std.mem; /// Returns how many bytes the UTF-8 representation would require /// for the given codepoint. @@ -441,3 +444,89 @@ fn testDecode(bytes: []const u8) !u32 { debug.assert(bytes.len == length); return utf8Decode(bytes); } + +// TODO: make this API on top of a non-allocating Utf16LeView +pub fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 { + var result = std.ArrayList(u8).init(allocator); + // optimistically guess that it will all be ascii. + try result.ensureCapacity(utf16le.len); + + const utf16le_as_bytes = @sliceToBytes(utf16le); + var i: usize = 0; + var out_index: usize = 0; + while (i < utf16le_as_bytes.len) : (i += 2) { + // decode + const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]); + var codepoint: u32 = undefined; + if (c0 & ~u32(0x03ff) == 0xd800) { + // surrogate pair + i += 2; + if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf; + const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]); + if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf; + codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)); + } else if (c0 & ~u32(0x03ff) == 0xdc00) { + return error.UnexpectedSecondSurrogateHalf; + } else { + codepoint = c0; + } + + // encode + const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable; + try result.resize(result.len + utf8_len); + _ = utf8Encode(codepoint, result.items[out_index..]) catch unreachable; + out_index += utf8_len; + } + + return result.toOwnedSlice(); +} + +test "utf16leToUtf8" { + var utf16le: [2]u16 = undefined; + const utf16le_as_bytes = @sliceToBytes(utf16le[0..]); + + { + mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "Aa")); + } + + { + mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf")); + } + + { + // the values just outside the surrogate half range + mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80")); + } + + { + // smallest surrogate pair + mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80")); + } + + { + // largest surrogate pair + mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf")); + } + + { + mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80")); + } +} -- cgit v1.2.3 From cd488c9da56a03d2244a67629612445c451441da Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Jul 2018 10:45:17 -0400 Subject: fix std.os.getAppDataDir test on linux --- std/os/get_app_data_dir.zig | 27 ++++++++++++++++++--------- std/os/index.zig | 3 +++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/std/os/get_app_data_dir.zig b/std/os/get_app_data_dir.zig index b5efdb826a..e8ae5dd490 100644 --- a/std/os/get_app_data_dir.zig +++ b/std/os/get_app_data_dir.zig @@ -35,15 +35,21 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD else => return error.AppDataDirUnavailable, } }, - // TODO for macos it should be "~/Library/Application Support/" - else => { - const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.EnvironmentVariableNotFound => return error.AppDataDirUnavailable, // TODO look in /etc/passwd + builtin.Os.macosx => { + const home_dir = os.getEnvPosix("HOME") orelse { + // TODO look in /etc/passwd + return error.AppDataDirUnavailable; + }; + return os.path.join(allocator, home_dir, "Library", "Application Support", appname); + }, + builtin.Os.linux => { + const home_dir = os.getEnvPosix("HOME") orelse { + // TODO look in /etc/passwd + return error.AppDataDirUnavailable; }; - defer allocator.free(home_dir); return os.path.join(allocator, home_dir, ".local", "share", appname); }, + else => @compileError("Unsupported OS"), } } @@ -53,8 +59,11 @@ fn utf16lePtrSlice(ptr: [*]const u16) []const u16 { return ptr[0..index]; } -test "getAppDataDir" { - const result = try getAppDataDir(std.debug.global_allocator, "zig"); - std.debug.warn("{}...", result); +test "std.os.getAppDataDir" { + var buf: [512]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; + + // We can't actually validate the result + _ = getAppDataDir(allocator, "zig") catch return; } diff --git a/std/os/index.zig b/std/os/index.zig index cb4358af4d..87053fd8df 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -498,6 +498,7 @@ pub var linux_aux_raw = []usize{0} ** 38; pub var posix_environ_raw: [][*]u8 = undefined; /// Caller must free result when done. +/// TODO make this go through libc when we have it pub fn getEnvMap(allocator: *Allocator) !BufMap { var result = BufMap.init(allocator); errdefer result.deinit(); @@ -541,6 +542,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { } } +/// TODO make this go through libc when we have it pub fn getEnvPosix(key: []const u8) ?[]const u8 { for (posix_environ_raw) |ptr| { var line_i: usize = 0; @@ -563,6 +565,7 @@ pub const GetEnvVarOwnedError = error{ }; /// Caller must free returned memory. +/// TODO make this go through libc when we have it pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { if (is_windows) { const key_with_null = try cstr.addNullByte(allocator, key); -- cgit v1.2.3 From c393a399fb4b463f095a8510595263759fd82cd6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Jul 2018 10:51:18 -0400 Subject: fix invalid character test on windows --- src/tokenizer.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index f7f41af8a6..1d3db5567a 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -460,16 +460,21 @@ static const char* get_escape_shorthand(uint8_t c) { static void invalid_char_error(Tokenize *t, uint8_t c) { if (c == '\r') { tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported"); - } else if (isprint(c)) { + return; + } + + const char *sh = get_escape_shorthand(c); + if (sh) { + tokenize_error(t, "invalid character: '%s'", sh); + return; + } + + if (isprint(c)) { tokenize_error(t, "invalid character: '%c'", c); - } else { - const char *sh = get_escape_shorthand(c); - if (sh) { - tokenize_error(t, "invalid character: '%s'", sh); - } else { - tokenize_error(t, "invalid character: '\\x%x'", c); - } + return; } + + tokenize_error(t, "invalid character: '\\x%02x'", c); } void tokenize(Buf *buf, Tokenization *out) { -- cgit v1.2.3 From fd3a41dadc92e7b69b409af5f747004996465032 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Wed, 18 Jul 2018 17:00:42 +0200 Subject: Allow pointers to anything in extern/exported declarations (#1258) * type_allowed_in_extern accepts all ptr not size 0 * Generate correct headers for none extern structs/unions/enums --- src/analyze.cpp | 4 ++- src/codegen.cpp | 81 ++++++++++++++++++++++++++++++++------------------------- test/gen_h.zig | 47 +++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 37 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 2ace893508..06d611f80d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1454,7 +1454,9 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdFn: return type_entry->data.fn.fn_type_id.cc == CallingConventionC; case TypeTableEntryIdPointer: - return type_allowed_in_extern(g, type_entry->data.pointer.child_type); + if (type_size(g, type_entry) == 0) + return false; + return true; case TypeTableEntryIdStruct: return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked; case TypeTableEntryIdOptional: diff --git a/src/codegen.cpp b/src/codegen.cpp index f8801ea132..6e121be270 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7508,51 +7508,60 @@ static void gen_h_file(CodeGen *g) { case TypeTableEntryIdPromise: zig_unreachable(); case TypeTableEntryIdEnum: - assert(type_entry->data.enumeration.layout == ContainerLayoutExtern); - fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name)); - for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) { - TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i]; - Buf *value_buf = buf_alloc(); - bigint_append_buf(value_buf, &enum_field->value, 10); - fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); - if (field_i != type_entry->data.enumeration.src_field_count - 1) { - fprintf(out_h, ","); + if (type_entry->data.enumeration.layout == ContainerLayoutExtern) { + fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name)); + for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) { + TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i]; + Buf *value_buf = buf_alloc(); + bigint_append_buf(value_buf, &enum_field->value, 10); + fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); + if (field_i != type_entry->data.enumeration.src_field_count - 1) { + fprintf(out_h, ","); + } + fprintf(out_h, "\n"); } - fprintf(out_h, "\n"); + fprintf(out_h, "};\n\n"); + } else { + fprintf(out_h, "enum %s;\n", buf_ptr(&type_entry->name)); } - fprintf(out_h, "};\n\n"); break; case TypeTableEntryIdStruct: - assert(type_entry->data.structure.layout == ContainerLayoutExtern); - fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name)); - for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) { - TypeStructField *struct_field = &type_entry->data.structure.fields[field_i]; - - Buf *type_name_buf = buf_alloc(); - get_c_type(g, gen_h, struct_field->type_entry, type_name_buf); - - if (struct_field->type_entry->id == TypeTableEntryIdArray) { - fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf), - buf_ptr(struct_field->name), - struct_field->type_entry->data.array.len); - } else { - fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name)); - } + if (type_entry->data.structure.layout == ContainerLayoutExtern) { + fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name)); + for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) { + TypeStructField *struct_field = &type_entry->data.structure.fields[field_i]; + + Buf *type_name_buf = buf_alloc(); + get_c_type(g, gen_h, struct_field->type_entry, type_name_buf); + + if (struct_field->type_entry->id == TypeTableEntryIdArray) { + fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf), + buf_ptr(struct_field->name), + struct_field->type_entry->data.array.len); + } else { + fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name)); + } + } + fprintf(out_h, "};\n\n"); + } else { + fprintf(out_h, "struct %s;\n", buf_ptr(&type_entry->name)); } - fprintf(out_h, "};\n\n"); break; case TypeTableEntryIdUnion: - assert(type_entry->data.unionation.layout == ContainerLayoutExtern); - fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name)); - for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) { - TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i]; - - Buf *type_name_buf = buf_alloc(); - get_c_type(g, gen_h, union_field->type_entry, type_name_buf); - fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); + if (type_entry->data.unionation.layout == ContainerLayoutExtern) { + fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name)); + for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) { + TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i]; + + Buf *type_name_buf = buf_alloc(); + get_c_type(g, gen_h, union_field->type_entry, type_name_buf); + fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); + } + fprintf(out_h, "};\n\n"); + } else { + fprintf(out_h, "union %s;\n", buf_ptr(&type_entry->name)); } - fprintf(out_h, "};\n\n"); break; case TypeTableEntryIdOpaque: fprintf(out_h, "struct %s;\n\n", buf_ptr(&type_entry->name)); diff --git a/test/gen_h.zig b/test/gen_h.zig index e6a757ea6d..b3aaa263d6 100644 --- a/test/gen_h.zig +++ b/test/gen_h.zig @@ -76,4 +76,51 @@ pub fn addCases(cases: *tests.GenHContext) void { \\TEST_EXPORT void entry(struct Foo foo, uint8_t bar[]); \\ ); + + cases.add("ptr to zig struct", + \\const S = struct { + \\ a: u8, + \\}; + \\ + \\export fn a(s: *S) u8 { + \\ return s.a; + \\} + + , + \\struct S; + \\TEST_EXPORT uint8_t a(struct S * s); + \\ + ); + + cases.add("ptr to zig union", + \\const U = union(enum) { + \\ A: u8, + \\ B: u16, + \\}; + \\ + \\export fn a(s: *U) u8 { + \\ return s.A; + \\} + + , + \\union U; + \\TEST_EXPORT uint8_t a(union U * s); + \\ + ); + + cases.add("ptr to zig enum", + \\const E = enum(u8) { + \\ A, + \\ B, + \\}; + \\ + \\export fn a(s: *E) u8 { + \\ return @enumToInt(s.*); + \\} + + , + \\enum E; + \\TEST_EXPORT uint8_t a(enum E * s); + \\ + ); } -- cgit v1.2.3 From 3e4a3fa5b7faadaae0a57088baa392e2bb52fe38 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Jul 2018 18:36:47 -0400 Subject: self-hosted: find libc on linux --- src-self-hosted/compilation.zig | 35 +++-- src-self-hosted/libc_installation.zig | 234 ++++++++++++++++++++++++++++++++++ src-self-hosted/main.zig | 65 ++++++---- std/fmt/index.zig | 8 +- std/os/file.zig | 45 +++---- 5 files changed, 326 insertions(+), 61 deletions(-) create mode 100644 src-self-hosted/libc_installation.zig diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 741324c871..1eb4339bbb 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -28,6 +28,7 @@ const Span = errmsg.Span; const codegen = @import("codegen.zig"); const Package = @import("package.zig").Package; const link = @import("link.zig").link; +const LibCInstallation = @import("libc_installation.zig").LibCInstallation; /// Data that is local to the event loop. pub const EventLoopLocal = struct { @@ -37,6 +38,8 @@ pub const EventLoopLocal = struct { /// TODO pool these so that it doesn't have to lock prng: event.Locked(std.rand.DefaultPrng), + native_libc: event.Future(LibCInstallation), + var lazy_init_targets = std.lazyInit(void); fn init(loop: *event.Loop) !EventLoopLocal { @@ -52,6 +55,7 @@ pub const EventLoopLocal = struct { .loop = loop, .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), + .native_libc = event.Future(LibCInstallation).init(loop), }; } @@ -78,6 +82,13 @@ pub const EventLoopLocal = struct { return LlvmHandle{ .node = node }; } + + pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation { + if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr; + try await (async self.native_libc.data.findNative(self.loop) catch unreachable); + self.native_libc.resolve(); + return &self.native_libc.data; + } }; pub const LlvmHandle = struct { @@ -109,11 +120,6 @@ pub const Compilation = struct { linker_script: ?[]const u8, cache_dir: []const u8, - libc_lib_dir: ?[]const u8, - libc_static_lib_dir: ?[]const u8, - libc_include_dir: ?[]const u8, - msvc_lib_dir: ?[]const u8, - kernel32_lib_dir: ?[]const u8, dynamic_linker: ?[]const u8, out_h_path: ?[]const u8, @@ -318,11 +324,6 @@ pub const Compilation = struct { .verbose_link = false, .linker_script = null, - .libc_lib_dir = null, - .libc_static_lib_dir = null, - .libc_include_dir = null, - .msvc_lib_dir = null, - .kernel32_lib_dir = null, .dynamic_linker = null, .out_h_path = null, .is_test = false, @@ -762,10 +763,24 @@ pub const Compilation = struct { try self.link_libs_list.append(link_lib); if (is_libc) { self.libc_link_lib = link_lib; + + // get a head start on looking for the native libc + if (self.target == Target.Native) { + try async self.startFindingNativeLibC(); + } } return link_lib; } + /// cancels itself so no need to await or cancel the promise. + async fn startFindingNativeLibC(self: *Compilation) void { + // we don't care if it fails, we're just trying to kick off the future resolution + _ = (await (async self.loop.call(EventLoopLocal.getNativeLibC, self.event_loop_local) catch unreachable)) catch {}; + suspend |p| { + cancel p; + } + } + /// General Purpose Allocator. Must free when done. fn gpa(self: Compilation) *mem.Allocator { return self.loop.allocator; diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig new file mode 100644 index 0000000000..5606a467e9 --- /dev/null +++ b/src-self-hosted/libc_installation.zig @@ -0,0 +1,234 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const event = std.event; + +pub const LibCInstallation = struct { + /// The directory that contains `stdlib.h`. + /// On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null` + include_dir: []const u8, + + /// The directory that contains `crt1.o`. + /// On Linux, can be found with `cc -print-file-name=crt1.o`. + /// Not needed when targeting MacOS. + lib_dir: ?[]const u8, + + /// The directory that contains `crtbegin.o`. + /// On Linux, can be found with `cc -print-file-name=crt1.o`. + /// Not needed when targeting MacOS or Windows. + static_lib_dir: ?[]const u8, + + /// The directory that contains `vcruntime.lib`. + /// Only needed when targeting Windows. + msvc_lib_dir: ?[]const u8, + + /// The directory that contains `kernel32.lib`. + /// Only needed when targeting Windows. + kernel32_lib_dir: ?[]const u8, + + pub const Error = error{ + OutOfMemory, + FileSystem, + UnableToSpawnCCompiler, + CCompilerExitCode, + CCompilerCrashed, + CCompilerCannotFindHeaders, + CCompilerCannotFindCRuntime, + LibCStdLibHeaderNotFound, + }; + + /// Finds the default, native libc. + pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void { + self.* = LibCInstallation{ + .lib_dir = null, + .include_dir = ([*]const u8)(undefined)[0..0], + .static_lib_dir = null, + .msvc_lib_dir = null, + .kernel32_lib_dir = null, + }; + var group = event.Group(Error!void).init(loop); + switch (builtin.os) { + builtin.Os.windows => { + try group.call(findNativeIncludeDirWindows, self, loop); + try group.call(findNativeLibDirWindows, self, loop); + try group.call(findNativeMsvcLibDir, self, loop); + try group.call(findNativeKernel32LibDir, self, loop); + }, + builtin.Os.linux => { + try group.call(findNativeIncludeDirLinux, self, loop); + try group.call(findNativeLibDirLinux, self, loop); + try group.call(findNativeStaticLibDir, self, loop); + }, + builtin.Os.macosx => { + try group.call(findNativeIncludeDirMacOS, self, loop); + }, + else => @compileError("unimplemented: find libc for this OS"), + } + return await (async group.wait() catch unreachable); + } + + async fn findNativeIncludeDirLinux(self: *LibCInstallation, loop: *event.Loop) !void { + const cc_exe = std.os.getEnvPosix("CC") orelse "cc"; + const argv = []const []const u8{ + cc_exe, + "-E", + "-Wp,-v", + "-xc", + "/dev/null", + }; + // TODO make this use event loop + const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024); + const exec_result = if (std.debug.runtime_safety) blk: { + break :blk errorable_result catch unreachable; + } else blk: { + break :blk errorable_result catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => return error.UnableToSpawnCCompiler, + }; + }; + defer { + loop.allocator.free(exec_result.stdout); + loop.allocator.free(exec_result.stderr); + } + + switch (exec_result.term) { + std.os.ChildProcess.Term.Exited => |code| { + if (code != 0) return error.CCompilerExitCode; + }, + else => { + return error.CCompilerCrashed; + }, + } + + var it = std.mem.split(exec_result.stderr, "\n\r"); + var search_paths = std.ArrayList([]const u8).init(loop.allocator); + defer search_paths.deinit(); + while (it.next()) |line| { + if (line.len != 0 and line[0] == ' ') { + try search_paths.append(line); + } + } + if (search_paths.len == 0) { + return error.CCompilerCannotFindHeaders; + } + + // search in reverse order + var path_i: usize = 0; + while (path_i < search_paths.len) : (path_i += 1) { + const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1); + const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " "); + const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h"); + defer loop.allocator.free(stdlib_path); + + if (std.os.File.access(loop.allocator, stdlib_path)) |_| { + self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path); + return; + } else |err| switch (err) { + error.NotFound, error.PermissionDenied => continue, + error.OutOfMemory => return error.OutOfMemory, + else => return error.FileSystem, + } + } + + return error.LibCStdLibHeaderNotFound; + } + + async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop) !void { + // TODO + //ZigWindowsSDK *sdk = get_windows_sdk(g); + //g->libc_include_dir = buf_alloc(); + //if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) { + // fprintf(stderr, "Unable to determine libc include path. --libc-include-dir"); + // exit(1); + //} + @panic("TODO"); + } + + async fn findNativeIncludeDirMacOS(self: *LibCInstallation, loop: *event.Loop) !void { + self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include"); + } + + async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop) Error!void { + // TODO + //ZigWindowsSDK *sdk = get_windows_sdk(g); + + //if (g->msvc_lib_dir == nullptr) { + // Buf* vc_lib_dir = buf_alloc(); + // if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { + // fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir"); + // exit(1); + // } + // g->msvc_lib_dir = vc_lib_dir; + //} + + //if (g->libc_lib_dir == nullptr) { + // Buf* ucrt_lib_path = buf_alloc(); + // if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) { + // fprintf(stderr, "Unable to determine ucrt path. --libc-lib-dir"); + // exit(1); + // } + // g->libc_lib_dir = ucrt_lib_path; + //} + + //if (g->kernel32_lib_dir == nullptr) { + // Buf* kern_lib_path = buf_alloc(); + // if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) { + // fprintf(stderr, "Unable to determine kernel32 path. --kernel32-lib-dir"); + // exit(1); + // } + // g->kernel32_lib_dir = kern_lib_path; + //} + @panic("TODO"); + } + + async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) Error!void { + self.lib_dir = try await (async ccPrintFileNameDir(loop, "crt1.o") catch unreachable); + } + + async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) Error!void { + self.static_lib_dir = try await (async ccPrintFileNameDir(loop, "crtbegin.o") catch unreachable); + } + + async fn findNativeMsvcLibDir(self: *LibCInstallation, loop: *event.Loop) Error!void { + @panic("TODO"); + } + + async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop) Error!void { + @panic("TODO"); + } +}; + +/// caller owns returned memory +async fn ccPrintFileNameDir(loop: *event.Loop, o_file: []const u8) ![]u8 { + const cc_exe = std.os.getEnvPosix("CC") orelse "cc"; + const arg1 = try std.fmt.allocPrint(loop.allocator, "-print-file-name={}", o_file); + defer loop.allocator.free(arg1); + const argv = []const []const u8{ cc_exe, arg1 }; + + // TODO evented I/O + const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024); + const exec_result = if (std.debug.runtime_safety) blk: { + break :blk errorable_result catch unreachable; + } else blk: { + break :blk errorable_result catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => return error.UnableToSpawnCCompiler, + }; + }; + defer { + loop.allocator.free(exec_result.stdout); + loop.allocator.free(exec_result.stderr); + } + switch (exec_result.term) { + std.os.ChildProcess.Term.Exited => |code| { + if (code != 0) return error.CCompilerExitCode; + }, + else => { + return error.CCompilerCrashed; + }, + } + var it = std.mem.split(exec_result.stdout, "\n\r"); + const line = it.next() orelse return error.CCompilerCannotFindCRuntime; + const dirname = std.os.path.dirname(line) orelse return error.CCompilerCannotFindCRuntime; + + return std.mem.dupe(loop.allocator, u8, dirname); +} diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 8b668e35bd..ff24677b6d 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -31,6 +31,7 @@ const usage = \\ build-exe [source] Create executable from source or object files \\ build-lib [source] Create library from source or object files \\ build-obj [source] Create object from source or assembly + \\ find-libc Show native libc installation paths \\ fmt [source] Parse file and render in canonical zig format \\ targets List available compilation targets \\ version Print version number and exit @@ -81,6 +82,10 @@ pub fn main() !void { .name = "build-obj", .exec = cmdBuildObj, }, + Command{ + .name = "find-libc", + .exec = cmdFindLibc, + }, Command{ .name = "fmt", .exec = cmdFmt, @@ -134,7 +139,6 @@ const usage_build_generic = \\ --cache-dir [path] Override the cache directory \\ --emit [filetype] Emit a specific file format as compilation output \\ --enable-timing-info Print timing diagnostics - \\ --libc-include-dir [path] Directory where libc stdlib.h resides \\ --name [name] Override output name \\ --output [file] Override destination path \\ --output-h [file] Override generated header file path @@ -165,10 +169,6 @@ const usage_build_generic = \\ --ar-path [path] Set the path to ar \\ --dynamic-linker [path] Set the path to ld.so \\ --each-lib-rpath Add rpath for each used dynamic library - \\ --libc-lib-dir [path] Directory where libc crt1.o resides - \\ --libc-static-lib-dir [path] Directory where libc crtbegin.o resides - \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides - \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides \\ --library [lib] Link against lib \\ --forbid-library [lib] Make it an error to link against lib \\ --library-path [dir] Add a directory to the library search path @@ -210,7 +210,6 @@ const args_build_generic = []Flag{ "llvm-ir", }), Flag.Bool("--enable-timing-info"), - Flag.Arg1("--libc-include-dir"), Flag.Arg1("--name"), Flag.Arg1("--output"), Flag.Arg1("--output-h"), @@ -236,10 +235,6 @@ const args_build_generic = []Flag{ Flag.Arg1("--ar-path"), Flag.Arg1("--dynamic-linker"), Flag.Bool("--each-lib-rpath"), - Flag.Arg1("--libc-lib-dir"), - Flag.Arg1("--libc-static-lib-dir"), - Flag.Arg1("--msvc-lib-dir"), - Flag.Arg1("--kernel32-lib-dir"), Flag.ArgMergeN("--library", 1), Flag.ArgMergeN("--forbid-library", 1), Flag.ArgMergeN("--library-path", 1), @@ -430,21 +425,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co comp.strip = flags.present("strip"); - if (flags.single("libc-lib-dir")) |libc_lib_dir| { - comp.libc_lib_dir = libc_lib_dir; - } - if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| { - comp.libc_static_lib_dir = libc_static_lib_dir; - } - if (flags.single("libc-include-dir")) |libc_include_dir| { - comp.libc_include_dir = libc_include_dir; - } - if (flags.single("msvc-lib-dir")) |msvc_lib_dir| { - comp.msvc_lib_dir = msvc_lib_dir; - } - if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| { - comp.kernel32_lib_dir = kernel32_lib_dir; - } if (flags.single("dynamic-linker")) |dynamic_linker| { comp.dynamic_linker = dynamic_linker; } @@ -579,6 +559,41 @@ const Fmt = struct { } }; +fn cmdFindLibc(allocator: *Allocator, args: []const []const u8) !void { + var loop: event.Loop = undefined; + try loop.initMultiThreaded(allocator); + defer loop.deinit(); + + var event_loop_local = try EventLoopLocal.init(&loop); + defer event_loop_local.deinit(); + + const handle = try async findLibCAsync(&event_loop_local); + defer cancel handle; + + loop.run(); +} + +async fn findLibCAsync(event_loop_local: *EventLoopLocal) void { + const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| { + stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1); + os.exit(1); + }; + stderr.print( + \\include_dir={} + \\lib_dir={} + \\static_lib_dir={} + \\msvc_lib_dir={} + \\kernel32_lib_dir={} + \\ + , + libc.include_dir, + libc.lib_dir, + libc.static_lib_dir orelse "", + libc.msvc_lib_dir orelse "", + libc.kernel32_lib_dir orelse "", + ) catch os.exit(1); +} + fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_fmt_spec, args); defer flags.deinit(); diff --git a/std/fmt/index.zig b/std/fmt/index.zig index c3c17f5322..2188cc5803 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -785,11 +785,15 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { return buf[0 .. buf.len - context.remaining.len]; } -pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { +pub const AllocPrintError = error{OutOfMemory}; + +pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 { var size: usize = 0; format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; const buf = try allocator.alloc(u8, size); - return bufPrint(buf, fmt, args); + return bufPrint(buf, fmt, args) catch |err| switch (err) { + error.BufferTooSmall => unreachable, // we just counted the size above + }; } fn countSize(size: *usize, bytes: []const u8) (error{}!void) { diff --git a/std/os/file.zig b/std/os/file.zig index 055f185121..f468615ec0 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -109,43 +109,40 @@ pub const File = struct { Unexpected, }; - pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) AccessError!bool { + pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void { const path_with_null = try std.cstr.addNullByte(allocator, path); defer allocator.free(path_with_null); if (is_posix) { - // mode is ignored and is always F_OK for now const result = posix.access(path_with_null.ptr, posix.F_OK); const err = posix.getErrno(result); - if (err > 0) { - return switch (err) { - posix.EACCES => error.PermissionDenied, - posix.EROFS => error.PermissionDenied, - posix.ELOOP => error.PermissionDenied, - posix.ETXTBSY => error.PermissionDenied, - posix.ENOTDIR => error.NotFound, - posix.ENOENT => error.NotFound, + switch (err) { + 0 => return, + posix.EACCES => return error.PermissionDenied, + posix.EROFS => return error.PermissionDenied, + posix.ELOOP => return error.PermissionDenied, + posix.ETXTBSY => return error.PermissionDenied, + posix.ENOTDIR => return error.NotFound, + posix.ENOENT => return error.NotFound, - posix.ENAMETOOLONG => error.NameTooLong, - posix.EINVAL => error.BadMode, - posix.EFAULT => error.BadPathName, - posix.EIO => error.Io, - posix.ENOMEM => error.SystemResources, - else => os.unexpectedErrorPosix(err), - }; + posix.ENAMETOOLONG => return error.NameTooLong, + posix.EINVAL => unreachable, + posix.EFAULT => return error.BadPathName, + posix.EIO => return error.Io, + posix.ENOMEM => return error.SystemResources, + else => return os.unexpectedErrorPosix(err), } - return true; } else if (is_windows) { if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) { - return true; + return; } const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.FILE_NOT_FOUND => error.NotFound, - windows.ERROR.ACCESS_DENIED => error.PermissionDenied, - else => os.unexpectedErrorWindows(err), - }; + switch (err) { + windows.ERROR.FILE_NOT_FOUND => return error.NotFound, + windows.ERROR.ACCESS_DENIED => return error.PermissionDenied, + else => return os.unexpectedErrorWindows(err), + } } else { @compileError("TODO implement access for this OS"); } -- cgit v1.2.3 From aa3b41247f297b4fd8b3bdb7920cb479f5aa004b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Jul 2018 00:34:42 -0400 Subject: self-hosted: linking against libc also introduce `zig libc` command to display paths `zig libc file.txt` will parse equivalent text and use that for libc paths. --- src-self-hosted/codegen.zig | 2 +- src-self-hosted/compilation.zig | 51 ++++-- src-self-hosted/libc_installation.zig | 222 ++++++++++++++++++++---- src-self-hosted/link.zig | 197 ++++++++++++--------- src-self-hosted/main.zig | 87 ++++++---- src-self-hosted/target.zig | 316 +++++++++++++++++++++++++++++++++- std/event/group.zig | 4 +- std/event/loop.zig | 2 +- 8 files changed, 709 insertions(+), 172 deletions(-) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index f8233bc795..8faef5f31c 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -15,7 +15,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) defer fn_val.base.deref(comp); defer code.destroy(comp.gpa()); - var output_path = try await (async comp.createRandomOutputPath(comp.target.oFileExt()) catch unreachable); + var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable); errdefer output_path.deinit(); const llvm_handle = try comp.event_loop_local.getAnyLlvmContext(); diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 1eb4339bbb..57809c8e4c 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -120,7 +120,6 @@ pub const Compilation = struct { linker_script: ?[]const u8, cache_dir: []const u8, - dynamic_linker: ?[]const u8, out_h_path: ?[]const u8, is_test: bool, @@ -201,6 +200,13 @@ pub const Compilation = struct { root_package: *Package, std_package: *Package, + override_libc: ?*LibCInstallation, + + /// need to wait on this group before deinitializing + deinit_group: event.Group(void), + + destroy_handle: promise, + const CompileErrList = std.ArrayList(*errmsg.Msg); // TODO handle some of these earlier and report them in a way other than error codes @@ -246,6 +252,8 @@ pub const Compilation = struct { EnvironmentVariableNotFound, AppDataDirUnavailable, LinkFailed, + LibCRequiredButNotProvidedOrFound, + LibCMissingDynamicLinker, }; pub const Event = union(enum) { @@ -324,7 +332,6 @@ pub const Compilation = struct { .verbose_link = false, .linker_script = null, - .dynamic_linker = null, .out_h_path = null, .is_test = false, .each_lib_rpath = false, @@ -351,6 +358,7 @@ pub const Compilation = struct { .link_out_file = null, .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), .prelink_group = event.Group(BuildError!void).init(loop), + .deinit_group = event.Group(void).init(loop), .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), .meta_type = undefined, @@ -368,6 +376,9 @@ pub const Compilation = struct { .root_package = undefined, .std_package = undefined, + + .override_libc = null, + .destroy_handle = undefined, }); errdefer { comp.arena_allocator.deinit(); @@ -431,6 +442,9 @@ pub const Compilation = struct { } try comp.initTypes(); + errdefer comp.derefTypes(); + + comp.destroy_handle = try async comp.internalDeinit(); return comp; } @@ -526,11 +540,7 @@ pub const Compilation = struct { errdefer comp.gpa().destroy(comp.noreturn_value); } - pub fn destroy(self: *Compilation) void { - if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| { - os.deleteTree(self.arena(), tmp_dir) catch {}; - } else |_| {}; - + fn derefTypes(self: *Compilation) void { self.noreturn_value.base.deref(self); self.void_value.base.deref(self); self.false_value.base.deref(self); @@ -538,6 +548,17 @@ pub const Compilation = struct { self.noreturn_type.base.base.deref(self); self.void_type.base.base.deref(self); self.meta_type.base.base.deref(self); + } + + async fn internalDeinit(self: *Compilation) void { + suspend; + await (async self.deinit_group.wait() catch unreachable); + if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| { + // TODO evented I/O? + os.deleteTree(self.arena(), tmp_dir) catch {}; + } else |_| {}; + + self.derefTypes(); self.events.destroy(); @@ -549,6 +570,10 @@ pub const Compilation = struct { self.gpa().destroy(self); } + pub fn destroy(self: *Compilation) void { + resume self.destroy_handle; + } + pub fn build(self: *Compilation) !void { if (self.llvm_argv.len != 0) { var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{ @@ -680,7 +705,7 @@ pub const Compilation = struct { }; if (!any_prelink_errors) { - try link(self); + try await (async link(self) catch unreachable); } } @@ -765,8 +790,8 @@ pub const Compilation = struct { self.libc_link_lib = link_lib; // get a head start on looking for the native libc - if (self.target == Target.Native) { - try async self.startFindingNativeLibC(); + if (self.target == Target.Native and self.override_libc == null) { + try self.deinit_group.call(startFindingNativeLibC, self); } } return link_lib; @@ -774,11 +799,9 @@ pub const Compilation = struct { /// cancels itself so no need to await or cancel the promise. async fn startFindingNativeLibC(self: *Compilation) void { + await (async self.loop.yield() catch unreachable); // we don't care if it fails, we're just trying to kick off the future resolution - _ = (await (async self.loop.call(EventLoopLocal.getNativeLibC, self.event_loop_local) catch unreachable)) catch {}; - suspend |p| { - cancel p; - } + _ = (await (async self.event_loop_local.getNativeLibC() catch unreachable)) catch return; } /// General Purpose Allocator. Must free when done. diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 5606a467e9..8444c47310 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -1,31 +1,18 @@ const std = @import("std"); const builtin = @import("builtin"); const event = std.event; +const Target = @import("target.zig").Target; +/// See the render function implementation for documentation of the fields. pub const LibCInstallation = struct { - /// The directory that contains `stdlib.h`. - /// On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null` include_dir: []const u8, - - /// The directory that contains `crt1.o`. - /// On Linux, can be found with `cc -print-file-name=crt1.o`. - /// Not needed when targeting MacOS. lib_dir: ?[]const u8, - - /// The directory that contains `crtbegin.o`. - /// On Linux, can be found with `cc -print-file-name=crt1.o`. - /// Not needed when targeting MacOS or Windows. static_lib_dir: ?[]const u8, - - /// The directory that contains `vcruntime.lib`. - /// Only needed when targeting Windows. msvc_lib_dir: ?[]const u8, - - /// The directory that contains `kernel32.lib`. - /// Only needed when targeting Windows. kernel32_lib_dir: ?[]const u8, + dynamic_linker_path: ?[]const u8, - pub const Error = error{ + pub const FindError = error{ OutOfMemory, FileSystem, UnableToSpawnCCompiler, @@ -36,16 +23,124 @@ pub const LibCInstallation = struct { LibCStdLibHeaderNotFound, }; + pub fn parse( + self: *LibCInstallation, + allocator: *std.mem.Allocator, + libc_file: []const u8, + stderr: *std.io.OutStream(std.io.FileOutStream.Error), + ) !void { + self.initEmpty(); + + const keys = []const []const u8{ + "include_dir", + "lib_dir", + "static_lib_dir", + "msvc_lib_dir", + "kernel32_lib_dir", + "dynamic_linker_path", + }; + const FoundKey = struct { + found: bool, + allocated: ?[]u8, + }; + var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** keys.len; + errdefer { + self.initEmpty(); + for (found_keys) |found_key| { + if (found_key.allocated) |s| allocator.free(s); + } + } + + const contents = try std.io.readFileAlloc(allocator, libc_file); + defer allocator.free(contents); + + var it = std.mem.split(contents, "\n"); + while (it.next()) |line| { + if (line.len == 0 or line[0] == '#') continue; + var line_it = std.mem.split(line, "="); + const name = line_it.next() orelse { + try stderr.print("missing equal sign after field name\n"); + return error.ParseError; + }; + const value = line_it.rest(); + inline for (keys) |key, i| { + if (std.mem.eql(u8, name, key)) { + found_keys[i].found = true; + switch (@typeInfo(@typeOf(@field(self, key)))) { + builtin.TypeId.Optional => { + if (value.len == 0) { + @field(self, key) = null; + } else { + found_keys[i].allocated = try std.mem.dupe(allocator, u8, value); + @field(self, key) = found_keys[i].allocated; + } + }, + else => { + if (value.len == 0) { + try stderr.print("field cannot be empty: {}\n", key); + return error.ParseError; + } + const dupe = try std.mem.dupe(allocator, u8, value); + found_keys[i].allocated = dupe; + @field(self, key) = dupe; + }, + } + break; + } + } + } + for (found_keys) |found_key, i| { + if (!found_key.found) { + try stderr.print("missing field: {}\n", keys[i]); + return error.ParseError; + } + } + } + + pub fn render(self: *const LibCInstallation, out: *std.io.OutStream(std.io.FileOutStream.Error)) !void { + @setEvalBranchQuota(4000); + try out.print( + \\# The directory that contains `stdlib.h`. + \\# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null` + \\include_dir={} + \\ + \\# The directory that contains `crt1.o`. + \\# On Linux, can be found with `cc -print-file-name=crt1.o`. + \\# Not needed when targeting MacOS. + \\lib_dir={} + \\ + \\# The directory that contains `crtbegin.o`. + \\# On Linux, can be found with `cc -print-file-name=crt1.o`. + \\# Not needed when targeting MacOS or Windows. + \\static_lib_dir={} + \\ + \\# The directory that contains `vcruntime.lib`. + \\# Only needed when targeting Windows. + \\msvc_lib_dir={} + \\ + \\# The directory that contains `kernel32.lib`. + \\# Only needed when targeting Windows. + \\kernel32_lib_dir={} + \\ + \\# The full path to the dynamic linker. + \\# Only needed when targeting Linux. + \\dynamic_linker_path={} + \\ + , + self.include_dir, + self.lib_dir orelse "", + self.static_lib_dir orelse "", + self.msvc_lib_dir orelse "", + self.kernel32_lib_dir orelse "", + self.dynamic_linker_path orelse Target(Target.Native).getDynamicLinkerPath(), + ); + } + /// Finds the default, native libc. pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void { - self.* = LibCInstallation{ - .lib_dir = null, - .include_dir = ([*]const u8)(undefined)[0..0], - .static_lib_dir = null, - .msvc_lib_dir = null, - .kernel32_lib_dir = null, - }; - var group = event.Group(Error!void).init(loop); + self.initEmpty(); + var group = event.Group(FindError!void).init(loop); + errdefer group.cancelAll(); switch (builtin.os) { builtin.Os.windows => { try group.call(findNativeIncludeDirWindows, self, loop); @@ -57,6 +152,7 @@ pub const LibCInstallation = struct { try group.call(findNativeIncludeDirLinux, self, loop); try group.call(findNativeLibDirLinux, self, loop); try group.call(findNativeStaticLibDir, self, loop); + try group.call(findNativeDynamicLinker, self, loop); }, builtin.Os.macosx => { try group.call(findNativeIncludeDirMacOS, self, loop); @@ -147,7 +243,7 @@ pub const LibCInstallation = struct { self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include"); } - async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop) Error!void { + async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop) FindError!void { // TODO //ZigWindowsSDK *sdk = get_windows_sdk(g); @@ -180,31 +276,83 @@ pub const LibCInstallation = struct { @panic("TODO"); } - async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) Error!void { - self.lib_dir = try await (async ccPrintFileNameDir(loop, "crt1.o") catch unreachable); + async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) FindError!void { + self.lib_dir = try await (async ccPrintFileName(loop, "crt1.o", true) catch unreachable); + } + + async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void { + self.static_lib_dir = try await (async ccPrintFileName(loop, "crtbegin.o", true) catch unreachable); + } + + async fn findNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop) FindError!void { + var dyn_tests = []DynTest{ + DynTest{ + .name = "ld-linux-x86-64.so.2", + .result = null, + }, + DynTest{ + .name = "ld-musl-x86_64.so.1", + .result = null, + }, + }; + var group = event.Group(FindError!void).init(loop); + errdefer group.cancelAll(); + for (dyn_tests) |*dyn_test| { + try group.call(testNativeDynamicLinker, self, loop, dyn_test); + } + try await (async group.wait() catch unreachable); + for (dyn_tests) |*dyn_test| { + if (dyn_test.result) |result| { + self.dynamic_linker_path = result; + return; + } + } } - async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) Error!void { - self.static_lib_dir = try await (async ccPrintFileNameDir(loop, "crtbegin.o") catch unreachable); + const DynTest = struct { + name: []const u8, + result: ?[]const u8, + }; + + async fn testNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop, dyn_test: *DynTest) FindError!void { + if (await (async ccPrintFileName(loop, dyn_test.name, false) catch unreachable)) |result| { + dyn_test.result = result; + return; + } else |err| switch (err) { + error.CCompilerCannotFindCRuntime => return, + else => return err, + } } - async fn findNativeMsvcLibDir(self: *LibCInstallation, loop: *event.Loop) Error!void { + async fn findNativeMsvcLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void { @panic("TODO"); } - async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop) Error!void { + async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void { @panic("TODO"); } + + fn initEmpty(self: *LibCInstallation) void { + self.* = LibCInstallation{ + .include_dir = ([*]const u8)(undefined)[0..0], + .lib_dir = null, + .static_lib_dir = null, + .msvc_lib_dir = null, + .kernel32_lib_dir = null, + .dynamic_linker_path = null, + }; + } }; /// caller owns returned memory -async fn ccPrintFileNameDir(loop: *event.Loop, o_file: []const u8) ![]u8 { +async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bool) ![]u8 { const cc_exe = std.os.getEnvPosix("CC") orelse "cc"; const arg1 = try std.fmt.allocPrint(loop.allocator, "-print-file-name={}", o_file); defer loop.allocator.free(arg1); const argv = []const []const u8{ cc_exe, arg1 }; - // TODO evented I/O + // TODO This simulates evented I/O for the child process exec + await (async loop.yield() catch unreachable); const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024); const exec_result = if (std.debug.runtime_safety) blk: { break :blk errorable_result catch unreachable; @@ -230,5 +378,9 @@ async fn ccPrintFileNameDir(loop: *event.Loop, o_file: []const u8) ![]u8 { const line = it.next() orelse return error.CCompilerCannotFindCRuntime; const dirname = std.os.path.dirname(line) orelse return error.CCompilerCannotFindCRuntime; - return std.mem.dupe(loop.allocator, u8, dirname); + if (want_dirname) { + return std.mem.dupe(loop.allocator, u8, dirname); + } else { + return std.mem.dupe(loop.allocator, u8, line); + } } diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 915451c931..16d9939fff 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -3,6 +3,8 @@ const c = @import("c.zig"); const builtin = @import("builtin"); const ObjectFormat = builtin.ObjectFormat; const Compilation = @import("compilation.zig").Compilation; +const Target = @import("target.zig").Target; +const LibCInstallation = @import("libc_installation.zig").LibCInstallation; const Context = struct { comp: *Compilation, @@ -12,9 +14,12 @@ const Context = struct { link_err: error{OutOfMemory}!void, link_msg: std.Buffer, + + libc: *LibCInstallation, + out_file_path: std.Buffer, }; -pub fn link(comp: *Compilation) !void { +pub async fn link(comp: *Compilation) !void { var ctx = Context{ .comp = comp, .arena = std.heap.ArenaAllocator.init(comp.gpa()), @@ -22,15 +27,45 @@ pub fn link(comp: *Compilation) !void { .link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe, .link_err = {}, .link_msg = undefined, + .libc = undefined, + .out_file_path = undefined, }; defer ctx.arena.deinit(); ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator); ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator); + if (comp.link_out_file) |out_file| { + ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, out_file); + } else { + ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, comp.name.toSliceConst()); + switch (comp.kind) { + Compilation.Kind.Exe => { + try ctx.out_file_path.append(comp.target.exeFileExt()); + }, + Compilation.Kind.Lib => { + try ctx.out_file_path.append(comp.target.libFileExt(comp.is_static)); + }, + Compilation.Kind.Obj => { + try ctx.out_file_path.append(comp.target.objFileExt()); + }, + } + } + // even though we're calling LLD as a library it thinks the first // argument is its own exe name try ctx.args.append(c"lld"); + if (comp.haveLibC()) { + ctx.libc = ctx.comp.override_libc orelse blk: { + switch (comp.target) { + Target.Native => { + break :blk (await (async comp.event_loop_local.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound; + }, + else => return error.LibCRequiredButNotProvidedOrFound, + } + }; + } + try constructLinkerArgs(&ctx); if (comp.verbose_link) { @@ -43,6 +78,7 @@ pub fn link(comp: *Compilation) !void { const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat()); const args_slice = ctx.args.toSlice(); + // Not evented I/O. LLD does its own multithreading internally. if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) { if (!ctx.link_msg.isNull()) { // TODO capture these messages and pass them through the system, reporting them through the @@ -95,10 +131,7 @@ fn constructLinkerArgs(ctx: *Context) !void { } fn constructLinkerArgsElf(ctx: *Context) !void { - //if (g->libc_link_lib != nullptr) { - // find_libc_lib_path(g); - //} - + // TODO commented out code in this function //if (g->linker_script) { // lj->args.append("-T"); // lj->args.append(g->linker_script); @@ -107,7 +140,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void { //if (g->no_rosegment_workaround) { // lj->args.append("--no-rosegment"); //} - //lj->args.append("--gc-sections"); + try ctx.args.append(c"--gc-sections"); //lj->args.append("-m"); //lj->args.append(getLDMOption(&g->zig_target)); @@ -115,14 +148,13 @@ fn constructLinkerArgsElf(ctx: *Context) !void { //bool is_lib = g->out_type == OutTypeLib; //bool shared = !g->is_static && is_lib; //Buf *soname = nullptr; - //if (g->is_static) { - // if (g->zig_target.arch.arch == ZigLLVM_arm || g->zig_target.arch.arch == ZigLLVM_armeb || - // g->zig_target.arch.arch == ZigLLVM_thumb || g->zig_target.arch.arch == ZigLLVM_thumbeb) - // { - // lj->args.append("-Bstatic"); - // } else { - // lj->args.append("-static"); - // } + if (ctx.comp.is_static) { + if (ctx.comp.target.isArmOrThumb()) { + try ctx.args.append(c"-Bstatic"); + } else { + try ctx.args.append(c"-static"); + } + } //} else if (shared) { // lj->args.append("-shared"); @@ -133,23 +165,16 @@ fn constructLinkerArgsElf(ctx: *Context) !void { // soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major); //} - //lj->args.append("-o"); - //lj->args.append(buf_ptr(&lj->out_file)); + try ctx.args.append(c"-o"); + try ctx.args.append(ctx.out_file_path.ptr()); - //if (lj->link_in_crt) { - // const char *crt1o; - // const char *crtbegino; - // if (g->is_static) { - // crt1o = "crt1.o"; - // crtbegino = "crtbeginT.o"; - // } else { - // crt1o = "Scrt1.o"; - // crtbegino = "crtbegin.o"; - // } - // lj->args.append(get_libc_file(g, crt1o)); - // lj->args.append(get_libc_file(g, "crti.o")); - // lj->args.append(get_libc_static_file(g, crtbegino)); - //} + if (ctx.link_in_crt) { + const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o"; + const crtbegino = if (ctx.comp.is_static) "crtbeginT.o" else "crtbegin.o"; + try addPathJoin(ctx, ctx.libc.lib_dir.?, crt1o); + try addPathJoin(ctx, ctx.libc.lib_dir.?, "crti.o"); + try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino); + } //for (size_t i = 0; i < g->rpath_list.length; i += 1) { // Buf *rpath = g->rpath_list.at(i); @@ -182,25 +207,23 @@ fn constructLinkerArgsElf(ctx: *Context) !void { // lj->args.append(lib_dir); //} - //if (g->libc_link_lib != nullptr) { - // lj->args.append("-L"); - // lj->args.append(buf_ptr(g->libc_lib_dir)); - - // lj->args.append("-L"); - // lj->args.append(buf_ptr(g->libc_static_lib_dir)); - //} - - //if (!g->is_static) { - // if (g->dynamic_linker != nullptr) { - // assert(buf_len(g->dynamic_linker) != 0); - // lj->args.append("-dynamic-linker"); - // lj->args.append(buf_ptr(g->dynamic_linker)); - // } else { - // Buf *resolved_dynamic_linker = get_dynamic_linker_path(g); - // lj->args.append("-dynamic-linker"); - // lj->args.append(buf_ptr(resolved_dynamic_linker)); - // } - //} + if (ctx.comp.haveLibC()) { + try ctx.args.append(c"-L"); + try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr); + + try ctx.args.append(c"-L"); + try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr); + + if (!ctx.comp.is_static) { + const dl = blk: { + if (ctx.libc.dynamic_linker_path) |dl| break :blk dl; + if (ctx.comp.target.getDynamicLinkerPath()) |dl| break :blk dl; + return error.LibCMissingDynamicLinker; + }; + try ctx.args.append(c"-dynamic-linker"); + try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr); + } + } //if (shared) { // lj->args.append("-soname"); @@ -241,45 +264,51 @@ fn constructLinkerArgsElf(ctx: *Context) !void { // lj->args.append(buf_ptr(arg)); //} - //// libc dep - //if (g->libc_link_lib != nullptr) { - // if (g->is_static) { - // lj->args.append("--start-group"); - // lj->args.append("-lgcc"); - // lj->args.append("-lgcc_eh"); - // lj->args.append("-lc"); - // lj->args.append("-lm"); - // lj->args.append("--end-group"); - // } else { - // lj->args.append("-lgcc"); - // lj->args.append("--as-needed"); - // lj->args.append("-lgcc_s"); - // lj->args.append("--no-as-needed"); - // lj->args.append("-lc"); - // lj->args.append("-lm"); - // lj->args.append("-lgcc"); - // lj->args.append("--as-needed"); - // lj->args.append("-lgcc_s"); - // lj->args.append("--no-as-needed"); - // } - //} + // libc dep + if (ctx.comp.haveLibC()) { + if (ctx.comp.is_static) { + try ctx.args.append(c"--start-group"); + try ctx.args.append(c"-lgcc"); + try ctx.args.append(c"-lgcc_eh"); + try ctx.args.append(c"-lc"); + try ctx.args.append(c"-lm"); + try ctx.args.append(c"--end-group"); + } else { + try ctx.args.append(c"-lgcc"); + try ctx.args.append(c"--as-needed"); + try ctx.args.append(c"-lgcc_s"); + try ctx.args.append(c"--no-as-needed"); + try ctx.args.append(c"-lc"); + try ctx.args.append(c"-lm"); + try ctx.args.append(c"-lgcc"); + try ctx.args.append(c"--as-needed"); + try ctx.args.append(c"-lgcc_s"); + try ctx.args.append(c"--no-as-needed"); + } + } - //// crt end - //if (lj->link_in_crt) { - // lj->args.append(get_libc_static_file(g, "crtend.o")); - // lj->args.append(get_libc_file(g, "crtn.o")); - //} + // crt end + if (ctx.link_in_crt) { + try addPathJoin(ctx, ctx.libc.static_lib_dir.?, "crtend.o"); + try addPathJoin(ctx, ctx.libc.lib_dir.?, "crtn.o"); + } - //if (!g->is_native_target) { - // lj->args.append("--allow-shlib-undefined"); - //} + if (ctx.comp.target != Target.Native) { + try ctx.args.append(c"--allow-shlib-undefined"); + } - //if (g->zig_target.os == OsZen) { - // lj->args.append("-e"); - // lj->args.append("_start"); + if (ctx.comp.target.getOs() == builtin.Os.zen) { + try ctx.args.append(c"-e"); + try ctx.args.append(c"_start"); - // lj->args.append("--image-base=0x10000000"); - //} + try ctx.args.append(c"--image-base=0x10000000"); + } +} + +fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void { + const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename); + const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path); + try ctx.args.append(full_path_with_null.ptr); } fn constructLinkerArgsCoff(ctx: *Context) void { diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index ff24677b6d..5c27e5f57e 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -18,6 +18,7 @@ const EventLoopLocal = @import("compilation.zig").EventLoopLocal; const Compilation = @import("compilation.zig").Compilation; const Target = @import("target.zig").Target; const errmsg = @import("errmsg.zig"); +const LibCInstallation = @import("libc_installation.zig").LibCInstallation; var stderr_file: os.File = undefined; var stderr: *io.OutStream(io.FileOutStream.Error) = undefined; @@ -28,14 +29,14 @@ const usage = \\ \\Commands: \\ - \\ build-exe [source] Create executable from source or object files - \\ build-lib [source] Create library from source or object files - \\ build-obj [source] Create object from source or assembly - \\ find-libc Show native libc installation paths - \\ fmt [source] Parse file and render in canonical zig format - \\ targets List available compilation targets - \\ version Print version number and exit - \\ zen Print zen of zig and exit + \\ build-exe [source] Create executable from source or object files + \\ build-lib [source] Create library from source or object files + \\ build-obj [source] Create object from source or assembly + \\ fmt [source] Parse file and render in canonical zig format + \\ libc [paths_file] Display native libc paths file or validate one + \\ targets List available compilation targets + \\ version Print version number and exit + \\ zen Print zen of zig and exit \\ \\ ; @@ -82,14 +83,14 @@ pub fn main() !void { .name = "build-obj", .exec = cmdBuildObj, }, - Command{ - .name = "find-libc", - .exec = cmdFindLibc, - }, Command{ .name = "fmt", .exec = cmdFmt, }, + Command{ + .name = "libc", + .exec = cmdLibC, + }, Command{ .name = "targets", .exec = cmdTargets, @@ -135,6 +136,7 @@ const usage_build_generic = \\ --color [auto|off|on] Enable or disable colored error messages \\ \\Compile Options: + \\ --libc [file] Provide a file which specifies libc paths \\ --assembly [source] Add assembly file to build \\ --cache-dir [path] Override the cache directory \\ --emit [filetype] Emit a specific file format as compilation output @@ -167,7 +169,6 @@ const usage_build_generic = \\ \\Link Options: \\ --ar-path [path] Set the path to ar - \\ --dynamic-linker [path] Set the path to ld.so \\ --each-lib-rpath Add rpath for each used dynamic library \\ --library [lib] Link against lib \\ --forbid-library [lib] Make it an error to link against lib @@ -210,6 +211,7 @@ const args_build_generic = []Flag{ "llvm-ir", }), Flag.Bool("--enable-timing-info"), + Flag.Arg1("--libc"), Flag.Arg1("--name"), Flag.Arg1("--output"), Flag.Arg1("--output-h"), @@ -233,7 +235,6 @@ const args_build_generic = []Flag{ Flag.Arg1("-mllvm"), Flag.Arg1("--ar-path"), - Flag.Arg1("--dynamic-linker"), Flag.Bool("--each-lib-rpath"), Flag.ArgMergeN("--library", 1), Flag.ArgMergeN("--forbid-library", 1), @@ -382,6 +383,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1); defer allocator.free(zig_lib_dir); + var override_libc: LibCInstallation = undefined; + var loop: event.Loop = undefined; try loop.initMultiThreaded(allocator); defer loop.deinit(); @@ -402,6 +405,15 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co ); defer comp.destroy(); + if (flags.single("libc")) |libc_path| { + parseLibcPaths(loop.allocator, &override_libc, libc_path); + comp.override_libc = &override_libc; + } + + for (flags.many("library")) |lib| { + _ = try comp.addLinkLib(lib, true); + } + comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10); comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10); comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10); @@ -425,10 +437,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co comp.strip = flags.present("strip"); - if (flags.single("dynamic-linker")) |dynamic_linker| { - comp.dynamic_linker = dynamic_linker; - } - comp.verbose_tokenize = flags.present("verbose-tokenize"); comp.verbose_ast_tree = flags.present("verbose-ast-tree"); comp.verbose_ast_fmt = flags.present("verbose-ast-fmt"); @@ -479,7 +487,6 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { switch (build_event) { Compilation.Event.Ok => { - std.debug.warn("Build succeeded\n"); return; }, Compilation.Event.Error => |err| { @@ -559,7 +566,32 @@ const Fmt = struct { } }; -fn cmdFindLibc(allocator: *Allocator, args: []const []const u8) !void { +fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void { + libc.parse(allocator, libc_paths_file, stderr) catch |err| { + stderr.print( + "Unable to parse libc path file '{}': {}.\n" ++ + "Try running `zig libc` to see an example for the native target.\n", + libc_paths_file, + @errorName(err), + ) catch os.exit(1); + os.exit(1); + }; +} + +fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void { + switch (args.len) { + 0 => {}, + 1 => { + var libc_installation: LibCInstallation = undefined; + parseLibcPaths(allocator, &libc_installation, args[0]); + return; + }, + else => { + try stderr.print("unexpected extra parameter: {}\n", args[1]); + os.exit(1); + }, + } + var loop: event.Loop = undefined; try loop.initMultiThreaded(allocator); defer loop.deinit(); @@ -578,20 +610,7 @@ async fn findLibCAsync(event_loop_local: *EventLoopLocal) void { stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1); os.exit(1); }; - stderr.print( - \\include_dir={} - \\lib_dir={} - \\static_lib_dir={} - \\msvc_lib_dir={} - \\kernel32_lib_dir={} - \\ - , - libc.include_dir, - libc.lib_dir, - libc.static_lib_dir orelse "", - libc.msvc_lib_dir orelse "", - libc.kernel32_lib_dir orelse "", - ) catch os.exit(1); + libc.render(stdout) catch os.exit(1); } fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index db673e421a..5a1f43bcf9 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -2,6 +2,12 @@ const std = @import("std"); const builtin = @import("builtin"); const llvm = @import("llvm.zig"); +pub const FloatAbi = enum { + Hard, + Soft, + SoftFp, +}; + pub const Target = union(enum) { Native, Cross: Cross, @@ -13,7 +19,7 @@ pub const Target = union(enum) { object_format: builtin.ObjectFormat, }; - pub fn oFileExt(self: Target) []const u8 { + pub fn objFileExt(self: Target) []const u8 { return switch (self.getObjectFormat()) { builtin.ObjectFormat.coff => ".obj", else => ".o", @@ -27,6 +33,13 @@ pub const Target = union(enum) { }; } + pub fn libFileExt(self: Target, is_static: bool) []const u8 { + return switch (self.getOs()) { + builtin.Os.windows => if (is_static) ".lib" else ".dll", + else => if (is_static) ".a" else ".so", + }; + } + pub fn getOs(self: Target) builtin.Os { return switch (self) { Target.Native => builtin.os, @@ -76,6 +89,56 @@ pub const Target = union(enum) { }; } + /// TODO expose the arch and subarch separately + pub fn isArmOrThumb(self: Target) bool { + return switch (self.getArch()) { + builtin.Arch.armv8_3a, + builtin.Arch.armv8_2a, + builtin.Arch.armv8_1a, + builtin.Arch.armv8, + builtin.Arch.armv8r, + builtin.Arch.armv8m_baseline, + builtin.Arch.armv8m_mainline, + builtin.Arch.armv7, + builtin.Arch.armv7em, + builtin.Arch.armv7m, + builtin.Arch.armv7s, + builtin.Arch.armv7k, + builtin.Arch.armv7ve, + builtin.Arch.armv6, + builtin.Arch.armv6m, + builtin.Arch.armv6k, + builtin.Arch.armv6t2, + builtin.Arch.armv5, + builtin.Arch.armv5te, + builtin.Arch.armv4t, + builtin.Arch.armebv8_3a, + builtin.Arch.armebv8_2a, + builtin.Arch.armebv8_1a, + builtin.Arch.armebv8, + builtin.Arch.armebv8r, + builtin.Arch.armebv8m_baseline, + builtin.Arch.armebv8m_mainline, + builtin.Arch.armebv7, + builtin.Arch.armebv7em, + builtin.Arch.armebv7m, + builtin.Arch.armebv7s, + builtin.Arch.armebv7k, + builtin.Arch.armebv7ve, + builtin.Arch.armebv6, + builtin.Arch.armebv6m, + builtin.Arch.armebv6k, + builtin.Arch.armebv6t2, + builtin.Arch.armebv5, + builtin.Arch.armebv5te, + builtin.Arch.armebv4t, + builtin.Arch.thumb, + builtin.Arch.thumbeb, + => true, + else => false, + }; + } + pub fn initializeAll() void { llvm.InitializeAllTargets(); llvm.InitializeAllTargetInfos(); @@ -106,6 +169,257 @@ pub const Target = union(enum) { return result; } + pub fn is64bit(self: Target) bool { + return self.getArchPtrBitWidth() == 64; + } + + pub fn getArchPtrBitWidth(self: Target) u8 { + switch (self.getArch()) { + builtin.Arch.avr, + builtin.Arch.msp430, + => return 16, + + builtin.Arch.arc, + builtin.Arch.armv8_3a, + builtin.Arch.armv8_2a, + builtin.Arch.armv8_1a, + builtin.Arch.armv8, + builtin.Arch.armv8r, + builtin.Arch.armv8m_baseline, + builtin.Arch.armv8m_mainline, + builtin.Arch.armv7, + builtin.Arch.armv7em, + builtin.Arch.armv7m, + builtin.Arch.armv7s, + builtin.Arch.armv7k, + builtin.Arch.armv7ve, + builtin.Arch.armv6, + builtin.Arch.armv6m, + builtin.Arch.armv6k, + builtin.Arch.armv6t2, + builtin.Arch.armv5, + builtin.Arch.armv5te, + builtin.Arch.armv4t, + builtin.Arch.armebv8_3a, + builtin.Arch.armebv8_2a, + builtin.Arch.armebv8_1a, + builtin.Arch.armebv8, + builtin.Arch.armebv8r, + builtin.Arch.armebv8m_baseline, + builtin.Arch.armebv8m_mainline, + builtin.Arch.armebv7, + builtin.Arch.armebv7em, + builtin.Arch.armebv7m, + builtin.Arch.armebv7s, + builtin.Arch.armebv7k, + builtin.Arch.armebv7ve, + builtin.Arch.armebv6, + builtin.Arch.armebv6m, + builtin.Arch.armebv6k, + builtin.Arch.armebv6t2, + builtin.Arch.armebv5, + builtin.Arch.armebv5te, + builtin.Arch.armebv4t, + builtin.Arch.hexagon, + builtin.Arch.le32, + builtin.Arch.mips, + builtin.Arch.mipsel, + builtin.Arch.nios2, + builtin.Arch.powerpc, + builtin.Arch.r600, + builtin.Arch.riscv32, + builtin.Arch.sparc, + builtin.Arch.sparcel, + builtin.Arch.tce, + builtin.Arch.tcele, + builtin.Arch.thumb, + builtin.Arch.thumbeb, + builtin.Arch.i386, + builtin.Arch.xcore, + builtin.Arch.nvptx, + builtin.Arch.amdil, + builtin.Arch.hsail, + builtin.Arch.spir, + builtin.Arch.kalimbav3, + builtin.Arch.kalimbav4, + builtin.Arch.kalimbav5, + builtin.Arch.shave, + builtin.Arch.lanai, + builtin.Arch.wasm32, + builtin.Arch.renderscript32, + => return 32, + + builtin.Arch.aarch64, + builtin.Arch.aarch64_be, + builtin.Arch.mips64, + builtin.Arch.mips64el, + builtin.Arch.powerpc64, + builtin.Arch.powerpc64le, + builtin.Arch.riscv64, + builtin.Arch.x86_64, + builtin.Arch.nvptx64, + builtin.Arch.le64, + builtin.Arch.amdil64, + builtin.Arch.hsail64, + builtin.Arch.spir64, + builtin.Arch.wasm64, + builtin.Arch.renderscript64, + builtin.Arch.amdgcn, + builtin.Arch.bpfel, + builtin.Arch.bpfeb, + builtin.Arch.sparcv9, + builtin.Arch.s390x, + => return 64, + } + } + + pub fn getFloatAbi(self: Target) FloatAbi { + return switch (self.getEnviron()) { + builtin.Environ.gnueabihf, + builtin.Environ.eabihf, + builtin.Environ.musleabihf, + => FloatAbi.Hard, + else => FloatAbi.Soft, + }; + } + + pub fn getDynamicLinkerPath(self: Target) ?[]const u8 { + const env = self.getEnviron(); + const arch = self.getArch(); + switch (env) { + builtin.Environ.android => { + if (self.is64bit()) { + return "/system/bin/linker64"; + } else { + return "/system/bin/linker"; + } + }, + builtin.Environ.gnux32 => { + if (arch == builtin.Arch.x86_64) { + return "/libx32/ld-linux-x32.so.2"; + } + }, + builtin.Environ.musl, + builtin.Environ.musleabi, + builtin.Environ.musleabihf, + => { + if (arch == builtin.Arch.x86_64) { + return "/lib/ld-musl-x86_64.so.1"; + } + }, + else => {}, + } + switch (arch) { + builtin.Arch.i386, + builtin.Arch.sparc, + builtin.Arch.sparcel, + => return "/lib/ld-linux.so.2", + + builtin.Arch.aarch64 => return "/lib/ld-linux-aarch64.so.1", + builtin.Arch.aarch64_be => return "/lib/ld-linux-aarch64_be.so.1", + + builtin.Arch.armv8_3a, + builtin.Arch.armv8_2a, + builtin.Arch.armv8_1a, + builtin.Arch.armv8, + builtin.Arch.armv8r, + builtin.Arch.armv8m_baseline, + builtin.Arch.armv8m_mainline, + builtin.Arch.armv7, + builtin.Arch.armv7em, + builtin.Arch.armv7m, + builtin.Arch.armv7s, + builtin.Arch.armv7k, + builtin.Arch.armv7ve, + builtin.Arch.armv6, + builtin.Arch.armv6m, + builtin.Arch.armv6k, + builtin.Arch.armv6t2, + builtin.Arch.armv5, + builtin.Arch.armv5te, + builtin.Arch.armv4t, + builtin.Arch.thumb, + => return switch (self.getFloatAbi()) { + FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3", + else => return "/lib/ld-linux.so.3", + }, + + builtin.Arch.armebv8_3a, + builtin.Arch.armebv8_2a, + builtin.Arch.armebv8_1a, + builtin.Arch.armebv8, + builtin.Arch.armebv8r, + builtin.Arch.armebv8m_baseline, + builtin.Arch.armebv8m_mainline, + builtin.Arch.armebv7, + builtin.Arch.armebv7em, + builtin.Arch.armebv7m, + builtin.Arch.armebv7s, + builtin.Arch.armebv7k, + builtin.Arch.armebv7ve, + builtin.Arch.armebv6, + builtin.Arch.armebv6m, + builtin.Arch.armebv6k, + builtin.Arch.armebv6t2, + builtin.Arch.armebv5, + builtin.Arch.armebv5te, + builtin.Arch.armebv4t, + builtin.Arch.thumbeb, + => return switch (self.getFloatAbi()) { + FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3", + else => return "/lib/ld-linux.so.3", + }, + + builtin.Arch.mips, + builtin.Arch.mipsel, + builtin.Arch.mips64, + builtin.Arch.mips64el, + => return null, + + builtin.Arch.powerpc => return "/lib/ld.so.1", + builtin.Arch.powerpc64 => return "/lib64/ld64.so.2", + builtin.Arch.powerpc64le => return "/lib64/ld64.so.2", + builtin.Arch.s390x => return "/lib64/ld64.so.1", + builtin.Arch.sparcv9 => return "/lib64/ld-linux.so.2", + builtin.Arch.x86_64 => return "/lib64/ld-linux-x86-64.so.2", + + builtin.Arch.arc, + builtin.Arch.avr, + builtin.Arch.bpfel, + builtin.Arch.bpfeb, + builtin.Arch.hexagon, + builtin.Arch.msp430, + builtin.Arch.nios2, + builtin.Arch.r600, + builtin.Arch.amdgcn, + builtin.Arch.riscv32, + builtin.Arch.riscv64, + builtin.Arch.tce, + builtin.Arch.tcele, + builtin.Arch.xcore, + builtin.Arch.nvptx, + builtin.Arch.nvptx64, + builtin.Arch.le32, + builtin.Arch.le64, + builtin.Arch.amdil, + builtin.Arch.amdil64, + builtin.Arch.hsail, + builtin.Arch.hsail64, + builtin.Arch.spir, + builtin.Arch.spir64, + builtin.Arch.kalimbav3, + builtin.Arch.kalimbav4, + builtin.Arch.kalimbav5, + builtin.Arch.shave, + builtin.Arch.lanai, + builtin.Arch.wasm32, + builtin.Arch.wasm64, + builtin.Arch.renderscript32, + builtin.Arch.renderscript64, + => return null, + } + } + pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef { var result: llvm.TargetRef = undefined; var err_msg: [*]u8 = undefined; diff --git a/std/event/group.zig b/std/event/group.zig index c286803b53..5ca8f5a0ca 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -6,7 +6,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; const assert = std.debug.assert; -/// ReturnType should be `void` or `E!void` +/// ReturnType must be `void` or `E!void` pub fn Group(comptime ReturnType: type) type { return struct { coro_stack: Stack, @@ -39,7 +39,7 @@ pub fn Group(comptime ReturnType: type) type { } /// This is equivalent to an async call, but the async function is added to the group, instead - /// of returning a promise. func must be async and have return type void. + /// of returning a promise. func must be async and have return type ReturnType. /// Thread-safe. pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) { const S = struct { diff --git a/std/event/loop.zig b/std/event/loop.zig index 485a5be19c..cd805f891f 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -444,7 +444,7 @@ pub const Loop = struct { .next = undefined, .data = p, }; - loop.onNextTick(&my_tick_node); + self.onNextTick(&my_tick_node); } } -- cgit v1.2.3 From bd1c55d2c2c9b68b5b6de175219cf95c815bf275 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Jul 2018 17:40:59 -0400 Subject: self-hosted: compile errors for return in wrong place * outside fn definition * inside defer expression --- src-self-hosted/compilation.zig | 106 +++++--- src-self-hosted/ir.zig | 588 ++++++++++++++++++++++++++++++---------- src-self-hosted/scope.zig | 24 +- test/stage2/compile_errors.zig | 12 + 4 files changed, 555 insertions(+), 175 deletions(-) diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 57809c8e4c..2f0d41ddc8 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -207,6 +207,8 @@ pub const Compilation = struct { destroy_handle: promise, + have_err_ret_tracing: bool, + const CompileErrList = std.ArrayList(*errmsg.Msg); // TODO handle some of these earlier and report them in a way other than error codes @@ -379,6 +381,7 @@ pub const Compilation = struct { .override_libc = null, .destroy_handle = undefined, + .have_err_ret_tracing = false, }); errdefer { comp.arena_allocator.deinit(); @@ -660,7 +663,11 @@ pub const Compilation = struct { while (it.next()) |decl_ptr| { const decl = decl_ptr.*; switch (decl.id) { - ast.Node.Id.Comptime => @panic("TODO"), + ast.Node.Id.Comptime => { + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl); + + try decl_group.call(addCompTimeBlock, self, parsed_file, &decls.base, comptime_node); + }, ast.Node.Id.VarDecl => @panic("TODO"), ast.Node.Id.FnProto => { const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); @@ -709,6 +716,69 @@ pub const Compilation = struct { } } + /// caller takes ownership of resulting Code + async fn genAndAnalyzeCode( + comp: *Compilation, + parsed_file: *ParsedFile, + scope: *Scope, + node: *ast.Node, + expected_type: ?*Type, + ) !?*ir.Code { + const unanalyzed_code = (await (async ir.gen( + comp, + node, + scope, + parsed_file, + ) catch unreachable)) catch |err| switch (err) { + // This poison value should not cause the errdefers to run. It simply means + // that self.compile_errors is populated. + // TODO https://github.com/ziglang/zig/issues/769 + error.SemanticAnalysisFailed => return null, + else => return err, + }; + defer unanalyzed_code.destroy(comp.gpa()); + + if (comp.verbose_ir) { + std.debug.warn("unanalyzed:\n"); + unanalyzed_code.dump(); + } + + const analyzed_code = (await (async ir.analyze( + comp, + parsed_file, + unanalyzed_code, + expected_type, + ) catch unreachable)) catch |err| switch (err) { + // This poison value should not cause the errdefers to run. It simply means + // that self.compile_errors is populated. + // TODO https://github.com/ziglang/zig/issues/769 + error.SemanticAnalysisFailed => return null, + else => return err, + }; + errdefer analyzed_code.destroy(comp.gpa()); + + return analyzed_code; + } + + async fn addCompTimeBlock( + comp: *Compilation, + parsed_file: *ParsedFile, + scope: *Scope, + comptime_node: *ast.Node.Comptime, + ) !void { + const void_type = Type.Void.get(comp); + defer void_type.base.base.deref(comp); + + const analyzed_code = (try await (async genAndAnalyzeCode( + comp, + parsed_file, + scope, + comptime_node.expr, + &void_type.base, + ) catch unreachable)) orelse return; + analyzed_code.destroy(comp.gpa()); + } + async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void { const is_export = decl.isExported(&decl.parsed_file.tree); @@ -931,38 +1001,12 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; - const unanalyzed_code = (await (async ir.gen( - comp, - body_node, - &fndef_scope.base, - Span.token(body_node.lastToken()), + const analyzed_code = (try await (async comp.genAndAnalyzeCode( fn_decl.base.parsed_file, - ) catch unreachable)) catch |err| switch (err) { - // This poison value should not cause the errdefers to run. It simply means - // that self.compile_errors is populated. - // TODO https://github.com/ziglang/zig/issues/769 - error.SemanticAnalysisFailed => return {}, - else => return err, - }; - defer unanalyzed_code.destroy(comp.gpa()); - - if (comp.verbose_ir) { - std.debug.warn("unanalyzed:\n"); - unanalyzed_code.dump(); - } - - const analyzed_code = (await (async ir.analyze( - comp, - fn_decl.base.parsed_file, - unanalyzed_code, + &fndef_scope.base, + body_node, null, - ) catch unreachable)) catch |err| switch (err) { - // This poison value should not cause the errdefers to run. It simply means - // that self.compile_errors is populated. - // TODO https://github.com/ziglang/zig/issues/769 - error.SemanticAnalysisFailed => return {}, - else => return err, - }; + ) catch unreachable)) orelse return; errdefer analyzed_code.destroy(comp.gpa()); if (comp.verbose_ir) { diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index e0c09b1e4b..153afd3de4 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -46,7 +46,7 @@ pub const IrVal = union(enum) { } }; -pub const Instruction = struct { +pub const Inst = struct { id: Id, scope: *Scope, debug_id: usize, @@ -59,15 +59,15 @@ pub const Instruction = struct { is_generated: bool, /// the instruction that is derived from this one in analysis - child: ?*Instruction, + child: ?*Inst, /// the instruction that this one derives from in analysis - parent: ?*Instruction, + parent: ?*Inst, /// populated durign codegen llvm_value: ?llvm.ValueRef, - pub fn cast(base: *Instruction, comptime T: type) ?*T { + pub fn cast(base: *Inst, comptime T: type) ?*T { if (base.id == comptime typeToId(T)) { return @fieldParentPtr(T, "base", base); } @@ -77,18 +77,18 @@ pub const Instruction = struct { pub fn typeToId(comptime T: type) Id { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { - if (T == @field(Instruction, @memberName(Id, i))) { + if (T == @field(Inst, @memberName(Id, i))) { return @field(Id, @memberName(Id, i)); } } unreachable; } - pub fn dump(base: *const Instruction) void { + pub fn dump(base: *const Inst) void { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { - const T = @field(Instruction, @memberName(Id, i)); + const T = @field(Inst, @memberName(Id, i)); std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id)); @fieldParentPtr(T, "base", base).dump(); std.debug.warn(")"); @@ -98,29 +98,29 @@ pub const Instruction = struct { unreachable; } - pub fn hasSideEffects(base: *const Instruction) bool { + pub fn hasSideEffects(base: *const Inst) bool { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { - const T = @field(Instruction, @memberName(Id, i)); + const T = @field(Inst, @memberName(Id, i)); return @fieldParentPtr(T, "base", base).hasSideEffects(); } } unreachable; } - pub fn analyze(base: *Instruction, ira: *Analyze) Analyze.Error!*Instruction { + pub fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { - const T = @field(Instruction, @memberName(Id, i)); + const T = @field(Inst, @memberName(Id, i)); return @fieldParentPtr(T, "base", base).analyze(ira); } } unreachable; } - pub fn render(base: *Instruction, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) { + pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) { switch (base.id) { Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val), Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val), @@ -133,14 +133,14 @@ pub const Instruction = struct { } } - fn ref(base: *Instruction, builder: *Builder) void { + fn ref(base: *Inst, builder: *Builder) void { base.ref_count += 1; if (base.owner_bb != builder.current_basic_block and !base.isCompTime()) { base.owner_bb.ref(); } } - fn getAsParam(param: *Instruction) !*Instruction { + fn getAsParam(param: *Inst) !*Inst { const child = param.child orelse return error.SemanticAnalysisFailed; switch (child.val) { IrVal.Unknown => return error.SemanticAnalysisFailed, @@ -149,7 +149,7 @@ pub const Instruction = struct { } /// asserts that the type is known - fn getKnownType(self: *Instruction) *Type { + fn getKnownType(self: *Inst) *Type { switch (self.val) { IrVal.KnownType => |typeof| return typeof, IrVal.KnownValue => |value| return value.typeof, @@ -157,11 +157,11 @@ pub const Instruction = struct { } } - pub fn setGenerated(base: *Instruction) void { + pub fn setGenerated(base: *Inst) void { base.is_generated = true; } - pub fn isNoReturn(base: *const Instruction) bool { + pub fn isNoReturn(base: *const Inst) bool { switch (base.val) { IrVal.Unknown => return false, IrVal.KnownValue => |x| return x.typeof.id == Type.Id.NoReturn, @@ -169,11 +169,11 @@ pub const Instruction = struct { } } - pub fn isCompTime(base: *const Instruction) bool { + pub fn isCompTime(base: *const Inst) bool { return base.val == IrVal.KnownValue; } - pub fn linkToParent(self: *Instruction, parent: *Instruction) void { + pub fn linkToParent(self: *Inst, parent: *Inst) void { assert(self.parent == null); assert(parent.child == null); self.parent = parent; @@ -192,7 +192,7 @@ pub const Instruction = struct { }; pub const Const = struct { - base: Instruction, + base: Inst, params: Params, const Params = struct {}; @@ -209,7 +209,7 @@ pub const Instruction = struct { return false; } - pub fn analyze(self: *const Const, ira: *Analyze) !*Instruction { + pub fn analyze(self: *const Const, ira: *Analyze) !*Inst { const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{}); new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() }; return new_inst; @@ -221,11 +221,11 @@ pub const Instruction = struct { }; pub const Return = struct { - base: Instruction, + base: Inst, params: Params, const Params = struct { - return_value: *Instruction, + return_value: *Inst, }; const ir_val_init = IrVal.Init.NoReturn; @@ -238,7 +238,7 @@ pub const Instruction = struct { return true; } - pub fn analyze(self: *const Return, ira: *Analyze) !*Instruction { + pub fn analyze(self: *const Return, ira: *Analyze) !*Inst { const value = try self.params.return_value.getAsParam(); const casted_value = try ira.implicitCast(value, ira.explicit_return_type); @@ -261,11 +261,11 @@ pub const Instruction = struct { }; pub const Ref = struct { - base: Instruction, + base: Inst, params: Params, const Params = struct { - target: *Instruction, + target: *Inst, mut: Type.Pointer.Mut, volatility: Type.Pointer.Vol, }; @@ -278,7 +278,7 @@ pub const Instruction = struct { return false; } - pub fn analyze(self: *const Ref, ira: *Analyze) !*Instruction { + pub fn analyze(self: *const Ref, ira: *Analyze) !*Inst { const target = try self.params.target.getAsParam(); if (ira.getCompTimeValOrNullUndefOk(target)) |val| { @@ -314,7 +314,7 @@ pub const Instruction = struct { }; pub const DeclVar = struct { - base: Instruction, + base: Inst, params: Params, const Params = struct { @@ -329,17 +329,17 @@ pub const Instruction = struct { return true; } - pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Instruction { + pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Inst { return error.Unimplemented; // TODO } }; pub const CheckVoidStmt = struct { - base: Instruction, + base: Inst, params: Params, const Params = struct { - target: *Instruction, + target: *Inst, }; const ir_val_init = IrVal.Init.Unknown; @@ -350,18 +350,18 @@ pub const Instruction = struct { return true; } - pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Instruction { + pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Inst { return error.Unimplemented; // TODO } }; pub const Phi = struct { - base: Instruction, + base: Inst, params: Params, const Params = struct { incoming_blocks: []*BasicBlock, - incoming_values: []*Instruction, + incoming_values: []*Inst, }; const ir_val_init = IrVal.Init.Unknown; @@ -372,18 +372,18 @@ pub const Instruction = struct { return false; } - pub fn analyze(self: *const Phi, ira: *Analyze) !*Instruction { + pub fn analyze(self: *const Phi, ira: *Analyze) !*Inst { return error.Unimplemented; // TODO } }; pub const Br = struct { - base: Instruction, + base: Inst, params: Params, const Params = struct { dest_block: *BasicBlock, - is_comptime: *Instruction, + is_comptime: *Inst, }; const ir_val_init = IrVal.Init.NoReturn; @@ -394,17 +394,41 @@ pub const Instruction = struct { return true; } - pub fn analyze(self: *const Br, ira: *Analyze) !*Instruction { + pub fn analyze(self: *const Br, ira: *Analyze) !*Inst { + return error.Unimplemented; // TODO + } + }; + + pub const CondBr = struct { + base: Inst, + params: Params, + + const Params = struct { + condition: *Inst, + then_block: *BasicBlock, + else_block: *BasicBlock, + is_comptime: *Inst, + }; + + const ir_val_init = IrVal.Init.NoReturn; + + pub fn dump(inst: *const CondBr) void {} + + pub fn hasSideEffects(inst: *const CondBr) bool { + return true; + } + + pub fn analyze(self: *const CondBr, ira: *Analyze) !*Inst { return error.Unimplemented; // TODO } }; pub const AddImplicitReturnType = struct { - base: Instruction, + base: Inst, params: Params, pub const Params = struct { - target: *Instruction, + target: *Inst, }; const ir_val_init = IrVal.Init.Unknown; @@ -417,12 +441,117 @@ pub const Instruction = struct { return true; } - pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Instruction { + pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Inst { const target = try self.params.target.getAsParam(); try ira.src_implicit_return_type_list.append(target); return ira.irb.buildConstVoid(self.base.scope, self.base.span, true); } }; + + pub const TestErr = struct { + base: Inst, + params: Params, + + pub const Params = struct { + target: *Inst, + }; + + const ir_val_init = IrVal.Init.Unknown; + + pub fn dump(inst: *const TestErr) void { + std.debug.warn("#{}", inst.params.target.debug_id); + } + + pub fn hasSideEffects(inst: *const TestErr) bool { + return false; + } + + pub fn analyze(self: *const TestErr, ira: *Analyze) !*Inst { + const target = try self.params.target.getAsParam(); + const target_type = target.getKnownType(); + switch (target_type.id) { + Type.Id.ErrorUnion => { + return error.Unimplemented; + // if (instr_is_comptime(value)) { + // ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad); + // if (!err_union_val) + // return ira->codegen->builtin_types.entry_invalid; + + // if (err_union_val->special != ConstValSpecialRuntime) { + // ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + // out_val->data.x_bool = (err_union_val->data.x_err_union.err != nullptr); + // return ira->codegen->builtin_types.entry_bool; + // } + // } + + // TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type; + // if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.source_node)) { + // return ira->codegen->builtin_types.entry_invalid; + // } + // if (!type_is_global_error_set(err_set_type) && + // err_set_type->data.error_set.err_count == 0) + // { + // assert(err_set_type->data.error_set.infer_fn == nullptr); + // ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + // out_val->data.x_bool = false; + // return ira->codegen->builtin_types.entry_bool; + // } + + // ir_build_test_err_from(&ira->new_irb, &instruction->base, value); + // return ira->codegen->builtin_types.entry_bool; + }, + Type.Id.ErrorSet => { + return ira.irb.buildConstBool(self.base.scope, self.base.span, true); + }, + else => { + return ira.irb.buildConstBool(self.base.scope, self.base.span, false); + }, + } + } + }; + + pub const TestCompTime = struct { + base: Inst, + params: Params, + + pub const Params = struct { + target: *Inst, + }; + + const ir_val_init = IrVal.Init.Unknown; + + pub fn dump(inst: *const TestCompTime) void { + std.debug.warn("#{}", inst.params.target.debug_id); + } + + pub fn hasSideEffects(inst: *const TestCompTime) bool { + return false; + } + + pub fn analyze(self: *const TestCompTime, ira: *Analyze) !*Inst { + const target = try self.params.target.getAsParam(); + return ira.irb.buildConstBool(self.base.scope, self.base.span, target.isCompTime()); + } + }; + + pub const SaveErrRetAddr = struct { + base: Inst, + params: Params, + + const Params = struct {}; + + const ir_val_init = IrVal.Init.Unknown; + + pub fn dump(inst: *const SaveErrRetAddr) void {} + + pub fn hasSideEffects(inst: *const SaveErrRetAddr) bool { + return true; + } + + pub fn analyze(self: *const SaveErrRetAddr, ira: *Analyze) !*Inst { + return ira.irb.build(Inst.SaveErrRetAddr, self.base.scope, self.base.span, Params{}); + } + }; }; pub const Variable = struct { @@ -434,8 +563,8 @@ pub const BasicBlock = struct { name_hint: [*]const u8, // must be a C string literal debug_id: usize, scope: *Scope, - instruction_list: std.ArrayList(*Instruction), - ref_instruction: ?*Instruction, + instruction_list: std.ArrayList(*Inst), + ref_instruction: ?*Inst, /// for codegen llvm_block: llvm.BasicBlockRef, @@ -491,10 +620,12 @@ pub const Builder = struct { next_debug_id: usize, parsed_file: *ParsedFile, is_comptime: bool, + is_async: bool, + begin_scope: ?*Scope, pub const Error = Analyze.Error; - pub fn init(comp: *Compilation, parsed_file: *ParsedFile) !Builder { + pub fn init(comp: *Compilation, parsed_file: *ParsedFile, begin_scope: ?*Scope) !Builder { const code = try comp.gpa().create(Code{ .basic_block_list = undefined, .arena = std.heap.ArenaAllocator.init(comp.gpa()), @@ -510,6 +641,8 @@ pub const Builder = struct { .code = code, .next_debug_id = 0, .is_comptime = false, + .is_async = false, + .begin_scope = begin_scope, }; } @@ -529,7 +662,7 @@ pub const Builder = struct { .name_hint = name_hint, .debug_id = self.next_debug_id, .scope = scope, - .instruction_list = std.ArrayList(*Instruction).init(self.arena()), + .instruction_list = std.ArrayList(*Inst).init(self.arena()), .child = null, .parent = null, .ref_instruction = null, @@ -549,66 +682,69 @@ pub const Builder = struct { self.current_basic_block = basic_block; } - pub fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Instruction { + pub fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst { switch (node.id) { ast.Node.Id.Root => unreachable, ast.Node.Id.Use => unreachable, ast.Node.Id.TestDecl => unreachable, - ast.Node.Id.VarDecl => @panic("TODO"), - ast.Node.Id.Defer => @panic("TODO"), - ast.Node.Id.InfixOp => @panic("TODO"), - ast.Node.Id.PrefixOp => @panic("TODO"), - ast.Node.Id.SuffixOp => @panic("TODO"), - ast.Node.Id.Switch => @panic("TODO"), - ast.Node.Id.While => @panic("TODO"), - ast.Node.Id.For => @panic("TODO"), - ast.Node.Id.If => @panic("TODO"), - ast.Node.Id.ControlFlowExpression => return error.Unimplemented, - ast.Node.Id.Suspend => @panic("TODO"), - ast.Node.Id.VarType => @panic("TODO"), - ast.Node.Id.ErrorType => @panic("TODO"), - ast.Node.Id.FnProto => @panic("TODO"), - ast.Node.Id.PromiseType => @panic("TODO"), - ast.Node.Id.IntegerLiteral => @panic("TODO"), - ast.Node.Id.FloatLiteral => @panic("TODO"), - ast.Node.Id.StringLiteral => @panic("TODO"), - ast.Node.Id.MultilineStringLiteral => @panic("TODO"), - ast.Node.Id.CharLiteral => @panic("TODO"), - ast.Node.Id.BoolLiteral => @panic("TODO"), - ast.Node.Id.NullLiteral => @panic("TODO"), - ast.Node.Id.UndefinedLiteral => @panic("TODO"), - ast.Node.Id.ThisLiteral => @panic("TODO"), - ast.Node.Id.Unreachable => @panic("TODO"), - ast.Node.Id.Identifier => @panic("TODO"), + ast.Node.Id.VarDecl => return error.Unimplemented, + ast.Node.Id.Defer => return error.Unimplemented, + ast.Node.Id.InfixOp => return error.Unimplemented, + ast.Node.Id.PrefixOp => return error.Unimplemented, + ast.Node.Id.SuffixOp => return error.Unimplemented, + ast.Node.Id.Switch => return error.Unimplemented, + ast.Node.Id.While => return error.Unimplemented, + ast.Node.Id.For => return error.Unimplemented, + ast.Node.Id.If => return error.Unimplemented, + ast.Node.Id.ControlFlowExpression => { + const control_flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", node); + return irb.genControlFlowExpr(control_flow_expr, scope, lval); + }, + ast.Node.Id.Suspend => return error.Unimplemented, + ast.Node.Id.VarType => return error.Unimplemented, + ast.Node.Id.ErrorType => return error.Unimplemented, + ast.Node.Id.FnProto => return error.Unimplemented, + ast.Node.Id.PromiseType => return error.Unimplemented, + ast.Node.Id.IntegerLiteral => return error.Unimplemented, + ast.Node.Id.FloatLiteral => return error.Unimplemented, + ast.Node.Id.StringLiteral => return error.Unimplemented, + ast.Node.Id.MultilineStringLiteral => return error.Unimplemented, + ast.Node.Id.CharLiteral => return error.Unimplemented, + ast.Node.Id.BoolLiteral => return error.Unimplemented, + ast.Node.Id.NullLiteral => return error.Unimplemented, + ast.Node.Id.UndefinedLiteral => return error.Unimplemented, + ast.Node.Id.ThisLiteral => return error.Unimplemented, + ast.Node.Id.Unreachable => return error.Unimplemented, + ast.Node.Id.Identifier => return error.Unimplemented, ast.Node.Id.GroupedExpression => { const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node); return irb.genNode(grouped_expr.expr, scope, lval); }, - ast.Node.Id.BuiltinCall => @panic("TODO"), - ast.Node.Id.ErrorSetDecl => @panic("TODO"), - ast.Node.Id.ContainerDecl => @panic("TODO"), - ast.Node.Id.Asm => @panic("TODO"), - ast.Node.Id.Comptime => @panic("TODO"), + ast.Node.Id.BuiltinCall => return error.Unimplemented, + ast.Node.Id.ErrorSetDecl => return error.Unimplemented, + ast.Node.Id.ContainerDecl => return error.Unimplemented, + ast.Node.Id.Asm => return error.Unimplemented, + ast.Node.Id.Comptime => return error.Unimplemented, ast.Node.Id.Block => { const block = @fieldParentPtr(ast.Node.Block, "base", node); return irb.lvalWrap(scope, try irb.genBlock(block, scope), lval); }, - ast.Node.Id.DocComment => @panic("TODO"), - ast.Node.Id.SwitchCase => @panic("TODO"), - ast.Node.Id.SwitchElse => @panic("TODO"), - ast.Node.Id.Else => @panic("TODO"), - ast.Node.Id.Payload => @panic("TODO"), - ast.Node.Id.PointerPayload => @panic("TODO"), - ast.Node.Id.PointerIndexPayload => @panic("TODO"), - ast.Node.Id.StructField => @panic("TODO"), - ast.Node.Id.UnionTag => @panic("TODO"), - ast.Node.Id.EnumTag => @panic("TODO"), - ast.Node.Id.ErrorTag => @panic("TODO"), - ast.Node.Id.AsmInput => @panic("TODO"), - ast.Node.Id.AsmOutput => @panic("TODO"), - ast.Node.Id.AsyncAttribute => @panic("TODO"), - ast.Node.Id.ParamDecl => @panic("TODO"), - ast.Node.Id.FieldInitializer => @panic("TODO"), + ast.Node.Id.DocComment => return error.Unimplemented, + ast.Node.Id.SwitchCase => return error.Unimplemented, + ast.Node.Id.SwitchElse => return error.Unimplemented, + ast.Node.Id.Else => return error.Unimplemented, + ast.Node.Id.Payload => return error.Unimplemented, + ast.Node.Id.PointerPayload => return error.Unimplemented, + ast.Node.Id.PointerIndexPayload => return error.Unimplemented, + ast.Node.Id.StructField => return error.Unimplemented, + ast.Node.Id.UnionTag => return error.Unimplemented, + ast.Node.Id.EnumTag => return error.Unimplemented, + ast.Node.Id.ErrorTag => return error.Unimplemented, + ast.Node.Id.AsmInput => return error.Unimplemented, + ast.Node.Id.AsmOutput => return error.Unimplemented, + ast.Node.Id.AsyncAttribute => return error.Unimplemented, + ast.Node.Id.ParamDecl => return error.Unimplemented, + ast.Node.Id.FieldInitializer => return error.Unimplemented, } } @@ -630,7 +766,7 @@ pub const Builder = struct { } } - pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Instruction { + pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst { const block_scope = try Scope.Block.create(irb.comp, parent_scope); const outer_block_scope = &block_scope.base; @@ -648,7 +784,7 @@ pub const Builder = struct { } if (block.label) |label| { - block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena()); + block_scope.incoming_values = std.ArrayList(*Inst).init(irb.arena()); block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena()); block_scope.end_block = try irb.createBasicBlock(parent_scope, c"BlockEnd"); block_scope.is_comptime = try irb.buildConstBool( @@ -659,7 +795,7 @@ pub const Builder = struct { } var is_continuation_unreachable = false; - var noreturn_return_value: ?*Instruction = null; + var noreturn_return_value: ?*Inst = null; var stmt_it = block.statements.iterator(0); while (stmt_it.next()) |statement_node_ptr| { @@ -686,16 +822,16 @@ pub const Builder = struct { noreturn_return_value = statement_value; } - if (statement_value.cast(Instruction.DeclVar)) |decl_var| { + if (statement_value.cast(Inst.DeclVar)) |decl_var| { // variable declarations start a new scope child_scope = decl_var.params.variable.child_scope; } else if (!is_continuation_unreachable) { // this statement's value must be void _ = irb.build( - Instruction.CheckVoidStmt, + Inst.CheckVoidStmt, child_scope, statement_value.span, - Instruction.CheckVoidStmt.Params{ .target = statement_value }, + Inst.CheckVoidStmt.Params{ .target = statement_value }, ); } } @@ -707,7 +843,7 @@ pub const Builder = struct { } try irb.setCursorAtEndAndAppendBlock(block_scope.end_block); - return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{ + return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{ .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(), .incoming_values = block_scope.incoming_values.toOwnedSlice(), }); @@ -720,14 +856,14 @@ pub const Builder = struct { ); _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit); - _ = try irb.buildGen(Instruction.Br, parent_scope, Span.token(block.rbrace), Instruction.Br.Params{ + _ = try irb.buildGen(Inst.Br, parent_scope, Span.token(block.rbrace), Inst.Br.Params{ .dest_block = block_scope.end_block, .is_comptime = block_scope.is_comptime, }); try irb.setCursorAtEndAndAppendBlock(block_scope.end_block); - return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{ + return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{ .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(), .incoming_values = block_scope.incoming_values.toOwnedSlice(), }); @@ -737,6 +873,135 @@ pub const Builder = struct { return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true); } + pub fn genControlFlowExpr( + irb: *Builder, + control_flow_expr: *ast.Node.ControlFlowExpression, + scope: *Scope, + lval: LVal, + ) !*Inst { + switch (control_flow_expr.kind) { + ast.Node.ControlFlowExpression.Kind.Break => |arg| return error.Unimplemented, + ast.Node.ControlFlowExpression.Kind.Continue => |arg| return error.Unimplemented, + ast.Node.ControlFlowExpression.Kind.Return => { + const src_span = Span.token(control_flow_expr.ltoken); + if (scope.findFnDef() == null) { + try irb.comp.addCompileError( + irb.parsed_file, + src_span, + "return expression outside function definition", + ); + return error.SemanticAnalysisFailed; + } + + if (scope.findDeferExpr()) |scope_defer_expr| { + if (!scope_defer_expr.reported_err) { + try irb.comp.addCompileError( + irb.parsed_file, + src_span, + "cannot return from defer expression", + ); + scope_defer_expr.reported_err = true; + } + return error.SemanticAnalysisFailed; + } + + const outer_scope = irb.begin_scope.?; + const return_value = if (control_flow_expr.rhs) |rhs| blk: { + break :blk try irb.genNode(rhs, scope, LVal.None); + } else blk: { + break :blk try irb.buildConstVoid(scope, src_span, true); + }; + + const defer_counts = irb.countDefers(scope, outer_scope); + const have_err_defers = defer_counts.error_exit != 0; + if (have_err_defers or irb.comp.have_err_ret_tracing) { + const err_block = try irb.createBasicBlock(scope, c"ErrRetErr"); + const ok_block = try irb.createBasicBlock(scope, c"ErrRetOk"); + if (!have_err_defers) { + _ = try irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit); + } + + const is_err = try irb.build( + Inst.TestErr, + scope, + src_span, + Inst.TestErr.Params{ .target = return_value }, + ); + + const err_is_comptime = try irb.buildTestCompTime(scope, src_span, is_err); + + _ = try irb.buildGen(Inst.CondBr, scope, src_span, Inst.CondBr.Params{ + .condition = is_err, + .then_block = err_block, + .else_block = ok_block, + .is_comptime = err_is_comptime, + }); + + const ret_stmt_block = try irb.createBasicBlock(scope, c"RetStmt"); + + try irb.setCursorAtEndAndAppendBlock(err_block); + if (have_err_defers) { + _ = try irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ErrorExit); + } + if (irb.comp.have_err_ret_tracing and !irb.isCompTime(scope)) { + _ = try irb.build(Inst.SaveErrRetAddr, scope, src_span, Inst.SaveErrRetAddr.Params{}); + } + _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{ + .dest_block = ret_stmt_block, + .is_comptime = err_is_comptime, + }); + + try irb.setCursorAtEndAndAppendBlock(ok_block); + if (have_err_defers) { + _ = try irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit); + } + _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{ + .dest_block = ret_stmt_block, + .is_comptime = err_is_comptime, + }); + + try irb.setCursorAtEndAndAppendBlock(ret_stmt_block); + return irb.genAsyncReturn(scope, src_span, return_value, false); + } else { + _ = try irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit); + return irb.genAsyncReturn(scope, src_span, return_value, false); + } + }, + } + } + + const DeferCounts = struct { + scope_exit: usize, + error_exit: usize, + }; + + fn countDefers(irb: *Builder, inner_scope: *Scope, outer_scope: *Scope) DeferCounts { + var result = DeferCounts{ .scope_exit = 0, .error_exit = 0 }; + + var scope = inner_scope; + while (scope != outer_scope) { + switch (scope.id) { + Scope.Id.Defer => { + const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope); + switch (defer_scope.kind) { + Scope.Defer.Kind.ScopeExit => result.scope_exit += 1, + Scope.Defer.Kind.ErrorExit => result.error_exit += 1, + } + scope = scope.parent orelse break; + }, + Scope.Id.FnDef => break, + + Scope.Id.CompTime, + Scope.Id.Block, + => scope = scope.parent orelse break, + + Scope.Id.DeferExpr => unreachable, + Scope.Id.Decls => unreachable, + } + } + return result; + } + fn genDefersForBlock( irb: *Builder, inner_scope: *Scope, @@ -764,10 +1029,10 @@ pub const Builder = struct { is_noreturn = true; } else { _ = try irb.build( - Instruction.CheckVoidStmt, + Inst.CheckVoidStmt, &defer_expr_scope.base, Span.token(defer_expr_scope.expr_node.lastToken()), - Instruction.CheckVoidStmt.Params{ .target = instruction }, + Inst.CheckVoidStmt.Params{ .target = instruction }, ); } } @@ -785,13 +1050,13 @@ pub const Builder = struct { } } - pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Instruction, lval: LVal) !*Instruction { + pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Inst, lval: LVal) !*Inst { switch (lval) { LVal.None => return instruction, LVal.Ptr => { // We needed a pointer to a value, but we got a value. So we create // an instruction which just makes a const pointer of it. - return irb.build(Instruction.Ref, scope, instruction.span, Instruction.Ref.Params{ + return irb.build(Inst.Ref, scope, instruction.span, Inst.Ref.Params{ .target = instruction, .mut = Type.Pointer.Mut.Const, .volatility = Type.Pointer.Vol.Non, @@ -811,10 +1076,10 @@ pub const Builder = struct { span: Span, params: I.Params, is_generated: bool, - ) !*Instruction { + ) !*Inst { const inst = try self.arena().create(I{ - .base = Instruction{ - .id = Instruction.typeToId(I), + .base = Inst{ + .id = Inst.typeToId(I), .is_generated = is_generated, .scope = scope, .debug_id = self.next_debug_id, @@ -838,8 +1103,8 @@ pub const Builder = struct { inline while (i < @memberCount(I.Params)) : (i += 1) { const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i))); switch (FieldType) { - *Instruction => @field(inst.params, @memberName(I.Params, i)).ref(self), - ?*Instruction => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref(self), + *Inst => @field(inst.params, @memberName(I.Params, i)).ref(self), + ?*Inst => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref(self), else => {}, } } @@ -855,7 +1120,7 @@ pub const Builder = struct { scope: *Scope, span: Span, params: I.Params, - ) !*Instruction { + ) !*Inst { return self.buildExtra(I, scope, span, params, false); } @@ -865,21 +1130,71 @@ pub const Builder = struct { scope: *Scope, span: Span, params: I.Params, - ) !*Instruction { + ) !*Inst { return self.buildExtra(I, scope, span, params, true); } - fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Instruction { - const inst = try self.build(Instruction.Const, scope, span, Instruction.Const.Params{}); + fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Inst { + const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{}); inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.comp, x).base }; return inst; } - fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Instruction { - const inst = try self.buildExtra(Instruction.Const, scope, span, Instruction.Const.Params{}, is_generated); + fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Inst { + const inst = try self.buildExtra(Inst.Const, scope, span, Inst.Const.Params{}, is_generated); inst.val = IrVal{ .KnownValue = &Value.Void.get(self.comp).base }; return inst; } + + /// If the code is explicitly set to be comptime, then builds a const bool, + /// otherwise builds a TestCompTime instruction. + fn buildTestCompTime(self: *Builder, scope: *Scope, span: Span, target: *Inst) !*Inst { + if (self.isCompTime(scope)) { + return self.buildConstBool(scope, span, true); + } else { + return self.build( + Inst.TestCompTime, + scope, + span, + Inst.TestCompTime.Params{ .target = target }, + ); + } + } + + fn genAsyncReturn(irb: *Builder, scope: *Scope, span: Span, result: *Inst, is_gen: bool) !*Inst { + _ = irb.buildGen( + Inst.AddImplicitReturnType, + scope, + span, + Inst.AddImplicitReturnType.Params{ .target = result }, + ); + + if (!irb.is_async) { + return irb.buildExtra( + Inst.Return, + scope, + span, + Inst.Return.Params{ .return_value = result }, + is_gen, + ); + } + return error.Unimplemented; + + //ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value); + //IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, + // get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); + //// TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig + //IrInstruction *replacement_value = irb->exec->coro_handle; + //IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node, + // promise_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr, + // AtomicRmwOp_xchg, AtomicOrderSeqCst); + //ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, maybe_await_handle); + //IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_await_handle); + //IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); + //return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, irb->exec->coro_early_final, + // is_comptime); + //// the above blocks are rendered by ir_gen after the rest of codegen + } }; const Analyze = struct { @@ -888,7 +1203,7 @@ const Analyze = struct { const_predecessor_bb: ?*BasicBlock, parent_basic_block: *BasicBlock, instruction_index: usize, - src_implicit_return_type_list: std.ArrayList(*Instruction), + src_implicit_return_type_list: std.ArrayList(*Inst), explicit_return_type: ?*Type, pub const Error = error{ @@ -903,7 +1218,7 @@ const Analyze = struct { }; pub fn init(comp: *Compilation, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze { - var irb = try Builder.init(comp, parsed_file); + var irb = try Builder.init(comp, parsed_file, null); errdefer irb.abort(); return Analyze{ @@ -912,7 +1227,7 @@ const Analyze = struct { .const_predecessor_bb = null, .parent_basic_block = undefined, // initialized with startBasicBlock .instruction_index = undefined, // initialized with startBasicBlock - .src_implicit_return_type_list = std.ArrayList(*Instruction).init(irb.arena()), + .src_implicit_return_type_list = std.ArrayList(*Inst).init(irb.arena()), .explicit_return_type = explicit_return_type, }; } @@ -921,7 +1236,7 @@ const Analyze = struct { self.irb.abort(); } - pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Instruction) !*BasicBlock { + pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Inst) !*BasicBlock { if (old_bb.child) |child| { if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction) return child; @@ -984,18 +1299,18 @@ const Analyze = struct { return self.irb.comp.addCompileError(self.irb.parsed_file, span, fmt, args); } - fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Instruction) Analyze.Error!*Type { + fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type { // TODO actual implementation return &Type.Void.get(self.irb.comp).base; } - fn implicitCast(self: *Analyze, target: *Instruction, optional_dest_type: ?*Type) Analyze.Error!*Instruction { + fn implicitCast(self: *Analyze, target: *Inst, optional_dest_type: ?*Type) Analyze.Error!*Inst { const dest_type = optional_dest_type orelse return target; - @panic("TODO implicitCast"); + return error.Unimplemented; } - fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Instruction) ?*Value { - @panic("TODO getCompTimeValOrNullUndefOk"); + fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Inst) ?*Value { + @panic("TODO"); } fn getCompTimeRef( @@ -1005,8 +1320,8 @@ const Analyze = struct { mut: Type.Pointer.Mut, volatility: Type.Pointer.Vol, ptr_align: u32, - ) Analyze.Error!*Instruction { - @panic("TODO getCompTimeRef"); + ) Analyze.Error!*Inst { + return error.Unimplemented; } }; @@ -1014,10 +1329,9 @@ pub async fn gen( comp: *Compilation, body_node: *ast.Node, scope: *Scope, - end_span: Span, parsed_file: *ParsedFile, ) !*Code { - var irb = try Builder.init(comp, parsed_file); + var irb = try Builder.init(comp, parsed_file, scope); errdefer irb.abort(); const entry_block = try irb.createBasicBlock(scope, c"Entry"); @@ -1026,18 +1340,8 @@ pub async fn gen( const result = try irb.genNode(body_node, scope, LVal.None); if (!result.isNoReturn()) { - _ = irb.buildGen( - Instruction.AddImplicitReturnType, - scope, - end_span, - Instruction.AddImplicitReturnType.Params{ .target = result }, - ); - _ = irb.buildGen( - Instruction.Return, - scope, - end_span, - Instruction.Return.Params{ .return_value = result }, - ); + // no need for save_err_ret_addr because this cannot return error + _ = try irb.genAsyncReturn(scope, Span.token(body_node.lastToken()), result, true); } return irb.finish(); diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 1c519d6c08..00be450479 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -49,6 +49,24 @@ pub const Scope = struct { } } + pub fn findDeferExpr(base: *Scope) ?*DeferExpr { + var scope = base; + while (true) { + switch (scope.id) { + Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base), + + Id.FnDef, + Id.Decls, + => return null, + + Id.Block, + Id.Defer, + Id.CompTime, + => scope = scope.parent orelse return null, + } + } + } + pub const Id = enum { Decls, Block, @@ -90,10 +108,10 @@ pub const Scope = struct { pub const Block = struct { base: Scope, - incoming_values: std.ArrayList(*ir.Instruction), + incoming_values: std.ArrayList(*ir.Inst), incoming_blocks: std.ArrayList(*ir.BasicBlock), end_block: *ir.BasicBlock, - is_comptime: *ir.Instruction, + is_comptime: *ir.Inst, safety: Safety, @@ -242,6 +260,7 @@ pub const Scope = struct { pub const DeferExpr = struct { base: Scope, expr_node: *ast.Node, + reported_err: bool, /// Creates a DeferExpr scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr { @@ -252,6 +271,7 @@ pub const Scope = struct { .ref_count = 1, }, .expr_node = expr_node, + .reported_err = false, }); errdefer comp.gpa().destroy(self); diff --git a/test/stage2/compile_errors.zig b/test/stage2/compile_errors.zig index 1dca908e69..d360c07b28 100644 --- a/test/stage2/compile_errors.zig +++ b/test/stage2/compile_errors.zig @@ -9,4 +9,16 @@ pub fn addCases(ctx: *TestContext) !void { try ctx.testCompileError( \\fn() void {} , "1.zig", 1, 1, "missing function name"); + + try ctx.testCompileError( + \\comptime { + \\ return; + \\} + , "1.zig", 2, 5, "return expression outside function definition"); + + try ctx.testCompileError( + \\export fn entry() void { + \\ defer return; + \\} + , "1.zig", 2, 11, "cannot return from defer expression"); } -- cgit v1.2.3 From 7f1a550760a7a01d4ae8de51be16fb02d56b5cd2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Jul 2018 17:56:34 -0400 Subject: std.zig.parse: fix treating integer literals as string literals --- README.md | 8 ++++---- std/zig/parse.zig | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a5868bf44e..f4daee9e59 100644 --- a/README.md +++ b/README.md @@ -21,19 +21,19 @@ clarity. * Compatible with C libraries with no wrapper necessary. Directly include C .h files and get access to the functions and symbols therein. * Provides standard library which competes with the C standard library and is - always compiled against statically in source form. Compile units do not + always compiled against statically in source form. Zig binaries do not depend on libc unless explicitly linked. - * Nullable type instead of null pointers. + * Optional type instead of null pointers. * Safe unions, tagged unions, and C ABI compatible unions. * Generics so that one can write efficient data structures that work for any data type. * No header files required. Top level declarations are entirely order-independent. * Compile-time code execution. Compile-time reflection. - * Partial compile-time function evaluation with eliminates the need for + * Partial compile-time function evaluation which eliminates the need for a preprocessor or macros. * The binaries produced by Zig have complete debugging information so you can, - for example, use GDB or MSVC to debug your software. + for example, use GDB, MSVC, or LLDB to debug your software. * Built-in unit tests with `zig test`. * Friendly toward package maintainers. Reproducible build, bootstrapping process carefully documented. Issues filed by package maintainers are diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 9842ba2a17..73d51e7870 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -2356,7 +2356,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token = nextToken(&tok_it, &tree); switch (token.ptr.id) { Token.Id.IntegerLiteral => { - _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token.index); + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.IntegerLiteral, token.index); continue; }, Token.Id.FloatLiteral => { -- cgit v1.2.3 From 1d85b588eab27830409a62e88bd8444db58787a4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Jul 2018 00:08:47 -0400 Subject: self-hosted: progress on IR for supporting libc hello world * add c int types * some more ir stubs --- CMakeLists.txt | 1 + src-self-hosted/c_int.zig | 68 ++++++ src-self-hosted/compilation.zig | 107 ++++++--- src-self-hosted/ir.zig | 475 +++++++++++++++++++++++++++++++++++++++- src-self-hosted/llvm.zig | 2 + src-self-hosted/target.zig | 99 ++++++++- src-self-hosted/type.zig | 98 ++++++++- src-self-hosted/value.zig | 58 +++++ std/event/group.zig | 11 + std/math/big/int.zig | 3 +- 10 files changed, 879 insertions(+), 43 deletions(-) create mode 100644 src-self-hosted/c_int.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 096ac50cfd..30d7bb4856 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -489,6 +489,7 @@ set(ZIG_STD_FILES "math/atan.zig" "math/atan2.zig" "math/atanh.zig" + "math/big/index.zig" "math/big/int.zig" "math/cbrt.zig" "math/ceil.zig" diff --git a/src-self-hosted/c_int.zig b/src-self-hosted/c_int.zig new file mode 100644 index 0000000000..10ce54da05 --- /dev/null +++ b/src-self-hosted/c_int.zig @@ -0,0 +1,68 @@ +pub const CInt = struct { + id: Id, + zig_name: []const u8, + c_name: []const u8, + is_signed: bool, + + pub const Id = enum { + Short, + UShort, + Int, + UInt, + Long, + ULong, + LongLong, + ULongLong, + }; + + pub const list = []CInt{ + CInt{ + .id = Id.Short, + .zig_name = "c_short", + .c_name = "short", + .is_signed = true, + }, + CInt{ + .id = Id.UShort, + .zig_name = "c_ushort", + .c_name = "unsigned short", + .is_signed = false, + }, + CInt{ + .id = Id.Int, + .zig_name = "c_int", + .c_name = "int", + .is_signed = true, + }, + CInt{ + .id = Id.UInt, + .zig_name = "c_uint", + .c_name = "unsigned int", + .is_signed = false, + }, + CInt{ + .id = Id.Long, + .zig_name = "c_long", + .c_name = "long", + .is_signed = true, + }, + CInt{ + .id = Id.ULong, + .zig_name = "c_ulong", + .c_name = "unsigned long", + .is_signed = false, + }, + CInt{ + .id = Id.LongLong, + .zig_name = "c_longlong", + .c_name = "long long", + .is_signed = true, + }, + CInt{ + .id = Id.ULongLong, + .zig_name = "c_ulonglong", + .c_name = "unsigned long long", + .is_signed = false, + }, + }; +}; diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 2f0d41ddc8..01f2314167 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -29,6 +29,7 @@ const codegen = @import("codegen.zig"); const Package = @import("package.zig").Package; const link = @import("link.zig").link; const LibCInstallation = @import("libc_installation.zig").LibCInstallation; +const CInt = @import("c_int.zig").CInt; /// Data that is local to the event loop. pub const EventLoopLocal = struct { @@ -59,6 +60,7 @@ pub const EventLoopLocal = struct { }; } + /// Must be called only after EventLoop.run completes. fn deinit(self: *EventLoopLocal) void { while (self.llvm_handle_pool.pop()) |node| { c.LLVMContextDispose(node.data); @@ -184,6 +186,7 @@ pub const Compilation = struct { void_type: *Type.Void, bool_type: *Type.Bool, noreturn_type: *Type.NoReturn, + comptime_int_type: *Type.ComptimeInt, void_value: *Value.Void, true_value: *Value.Bool, @@ -209,6 +212,16 @@ pub const Compilation = struct { have_err_ret_tracing: bool, + /// not locked because it is read-only + primitive_type_table: TypeTable, + + int_type_table: event.Locked(IntTypeTable), + + c_int_types: [CInt.list.len]*Type.Int, + + const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql); + const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8); + const CompileErrList = std.ArrayList(*errmsg.Msg); // TODO handle some of these earlier and report them in a way other than error codes @@ -362,6 +375,8 @@ pub const Compilation = struct { .prelink_group = event.Group(BuildError!void).init(loop), .deinit_group = event.Group(void).init(loop), .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), + .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)), + .c_int_types = undefined, .meta_type = undefined, .void_type = undefined, @@ -371,6 +386,7 @@ pub const Compilation = struct { .false_value = undefined, .noreturn_type = undefined, .noreturn_value = undefined, + .comptime_int_type = undefined, .target_machine = undefined, .target_data_ref = undefined, @@ -382,8 +398,10 @@ pub const Compilation = struct { .override_libc = null, .destroy_handle = undefined, .have_err_ret_tracing = false, + .primitive_type_table = undefined, }); errdefer { + comp.int_type_table.private_data.deinit(); comp.arena_allocator.deinit(); comp.loop.allocator.destroy(comp); } @@ -393,6 +411,7 @@ pub const Compilation = struct { comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple); comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena()); comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std"); + comp.primitive_type_table = TypeTable.init(comp.arena()); const opt_level = switch (build_mode) { builtin.Mode.Debug => llvm.CodeGenLevelNone, @@ -445,7 +464,6 @@ pub const Compilation = struct { } try comp.initTypes(); - errdefer comp.derefTypes(); comp.destroy_handle = try async comp.internalDeinit(); @@ -453,8 +471,9 @@ pub const Compilation = struct { } fn initTypes(comp: *Compilation) !void { - comp.meta_type = try comp.gpa().create(Type.MetaType{ + comp.meta_type = try comp.arena().create(Type.MetaType{ .base = Type{ + .name = "type", .base = Value{ .id = Value.Id.Type, .typeof = undefined, @@ -466,10 +485,11 @@ pub const Compilation = struct { }); comp.meta_type.value = &comp.meta_type.base; comp.meta_type.base.base.typeof = &comp.meta_type.base; - errdefer comp.gpa().destroy(comp.meta_type); + assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null); - comp.void_type = try comp.gpa().create(Type.Void{ + comp.void_type = try comp.arena().create(Type.Void{ .base = Type{ + .name = "void", .base = Value{ .id = Value.Id.Type, .typeof = &Type.MetaType.get(comp).base, @@ -478,10 +498,11 @@ pub const Compilation = struct { .id = builtin.TypeId.Void, }, }); - errdefer comp.gpa().destroy(comp.void_type); + assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null); - comp.noreturn_type = try comp.gpa().create(Type.NoReturn{ + comp.noreturn_type = try comp.arena().create(Type.NoReturn{ .base = Type{ + .name = "noreturn", .base = Value{ .id = Value.Id.Type, .typeof = &Type.MetaType.get(comp).base, @@ -490,10 +511,24 @@ pub const Compilation = struct { .id = builtin.TypeId.NoReturn, }, }); - errdefer comp.gpa().destroy(comp.noreturn_type); + assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null); - comp.bool_type = try comp.gpa().create(Type.Bool{ + comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt{ .base = Type{ + .name = "comptime_int", + .base = Value{ + .id = Value.Id.Type, + .typeof = &Type.MetaType.get(comp).base, + .ref_count = std.atomic.Int(usize).init(1), + }, + .id = builtin.TypeId.ComptimeInt, + }, + }); + assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null); + + comp.bool_type = try comp.arena().create(Type.Bool{ + .base = Type{ + .name = "bool", .base = Value{ .id = Value.Id.Type, .typeof = &Type.MetaType.get(comp).base, @@ -502,18 +537,17 @@ pub const Compilation = struct { .id = builtin.TypeId.Bool, }, }); - errdefer comp.gpa().destroy(comp.bool_type); + assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null); - comp.void_value = try comp.gpa().create(Value.Void{ + comp.void_value = try comp.arena().create(Value.Void{ .base = Value{ .id = Value.Id.Void, .typeof = &Type.Void.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, }); - errdefer comp.gpa().destroy(comp.void_value); - comp.true_value = try comp.gpa().create(Value.Bool{ + comp.true_value = try comp.arena().create(Value.Bool{ .base = Value{ .id = Value.Id.Bool, .typeof = &Type.Bool.get(comp).base, @@ -521,9 +555,8 @@ pub const Compilation = struct { }, .x = true, }); - errdefer comp.gpa().destroy(comp.true_value); - comp.false_value = try comp.gpa().create(Value.Bool{ + comp.false_value = try comp.arena().create(Value.Bool{ .base = Value{ .id = Value.Id.Bool, .typeof = &Type.Bool.get(comp).base, @@ -531,44 +564,56 @@ pub const Compilation = struct { }, .x = false, }); - errdefer comp.gpa().destroy(comp.false_value); - comp.noreturn_value = try comp.gpa().create(Value.NoReturn{ + comp.noreturn_value = try comp.arena().create(Value.NoReturn{ .base = Value{ .id = Value.Id.NoReturn, .typeof = &Type.NoReturn.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, }); - errdefer comp.gpa().destroy(comp.noreturn_value); - } - fn derefTypes(self: *Compilation) void { - self.noreturn_value.base.deref(self); - self.void_value.base.deref(self); - self.false_value.base.deref(self); - self.true_value.base.deref(self); - self.noreturn_type.base.base.deref(self); - self.void_type.base.base.deref(self); - self.meta_type.base.base.deref(self); + for (CInt.list) |cint, i| { + const c_int_type = try comp.arena().create(Type.Int{ + .base = Type{ + .name = cint.zig_name, + .base = Value{ + .id = Value.Id.Type, + .typeof = &Type.MetaType.get(comp).base, + .ref_count = std.atomic.Int(usize).init(1), + }, + .id = builtin.TypeId.Int, + }, + .key = Type.Int.Key{ + .is_signed = cint.is_signed, + .bit_count = comp.target.cIntTypeSizeInBits(cint.id), + }, + .garbage_node = undefined, + }); + comp.c_int_types[i] = c_int_type; + assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null); + } } + /// This function can safely use async/await, because it manages Compilation's lifetime, + /// and EventLoopLocal.deinit will not be called until the event.Loop.run() completes. async fn internalDeinit(self: *Compilation) void { suspend; + await (async self.deinit_group.wait() catch unreachable); if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| { // TODO evented I/O? os.deleteTree(self.arena(), tmp_dir) catch {}; } else |_| {}; - self.derefTypes(); - self.events.destroy(); llvm.DisposeMessage(self.target_layout_str); llvm.DisposeTargetData(self.target_data_ref); llvm.DisposeTargetMachine(self.target_machine); + self.primitive_type_table.deinit(); + self.arena_allocator.deinit(); self.gpa().destroy(self); } @@ -939,6 +984,10 @@ pub const Compilation = struct { b64_fs_encoder.encode(result[0..], rand_bytes); return result; } + + fn registerGarbage(comp: *Compilation, comptime T: type, node: *std.atomic.Stack(*T).Node) void { + // TODO put the garbage somewhere + } }; fn printError(comptime format: []const u8, args: ...) !void { @@ -1005,7 +1054,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { fn_decl.base.parsed_file, &fndef_scope.base, body_node, - null, + return_type, ) catch unreachable)) orelse return; errdefer analyzed_code.destroy(comp.gpa()); diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 153afd3de4..5a8a74ef54 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -705,7 +705,10 @@ pub const Builder = struct { ast.Node.Id.ErrorType => return error.Unimplemented, ast.Node.Id.FnProto => return error.Unimplemented, ast.Node.Id.PromiseType => return error.Unimplemented, - ast.Node.Id.IntegerLiteral => return error.Unimplemented, + ast.Node.Id.IntegerLiteral => { + const int_lit = @fieldParentPtr(ast.Node.IntegerLiteral, "base", node); + return irb.lvalWrap(scope, try irb.genIntLit(int_lit, scope), lval); + }, ast.Node.Id.FloatLiteral => return error.Unimplemented, ast.Node.Id.StringLiteral => return error.Unimplemented, ast.Node.Id.MultilineStringLiteral => return error.Unimplemented, @@ -766,6 +769,45 @@ pub const Builder = struct { } } + pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst { + const int_token = irb.parsed_file.tree.tokenSlice(int_lit.token); + + var base: u8 = undefined; + var rest: []const u8 = undefined; + if (int_token.len >= 3 and int_token[0] == '0') { + base = switch (int_token[1]) { + 'b' => u8(2), + 'o' => u8(8), + 'x' => u8(16), + else => unreachable, + }; + rest = int_token[2..]; + } else { + base = 10; + rest = int_token; + } + + const comptime_int_type = Type.ComptimeInt.get(irb.comp); + defer comptime_int_type.base.base.deref(irb.comp); + + const int_val = Value.Int.createFromString( + irb.comp, + &comptime_int_type.base, + base, + rest, + ) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.InvalidBase => unreachable, + error.InvalidCharForDigit => unreachable, + error.DigitTooLargeForBase => unreachable, + }; + errdefer int_val.base.deref(irb.comp); + + const inst = try irb.build(Inst.Const, scope, Span.token(int_lit.token), Inst.Const.Params{}); + inst.val = IrVal{ .KnownValue = &int_val.base }; + return inst; + } + pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst { const block_scope = try Scope.Block.create(irb.comp, parent_scope); @@ -1306,7 +1348,436 @@ const Analyze = struct { fn implicitCast(self: *Analyze, target: *Inst, optional_dest_type: ?*Type) Analyze.Error!*Inst { const dest_type = optional_dest_type orelse return target; - return error.Unimplemented; + const from_type = target.getKnownType(); + if (from_type == dest_type or from_type.id == Type.Id.NoReturn) return target; + return self.analyzeCast(target, target, dest_type); + } + + fn analyzeCast(ira: *Analyze, source_instr: *Inst, target: *Inst, dest_type: *Type) !*Inst { + const from_type = target.getKnownType(); + + //if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) { + // return ira->codegen->invalid_instruction; + //} + + //// perfect match or non-const to const + //ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, + // source_node, false); + //if (const_cast_result.id == ConstCastResultIdOk) { + // return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); + //} + + //// widening conversion + //if (wanted_type->id == TypeTableEntryIdInt && + // actual_type->id == TypeTableEntryIdInt && + // wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && + // wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count) + //{ + // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); + //} + + //// small enough unsigned ints can get casted to large enough signed ints + //if (wanted_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed && + // actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed && + // wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count) + //{ + // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); + //} + + //// float widening conversion + //if (wanted_type->id == TypeTableEntryIdFloat && + // actual_type->id == TypeTableEntryIdFloat && + // wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) + //{ + // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); + //} + + //// cast from [N]T to []const T + //if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) { + // TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; + // assert(ptr_type->id == TypeTableEntryIdPointer); + // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && + // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, + // source_node, false).id == ConstCastResultIdOk) + // { + // return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); + // } + //} + + //// cast from *const [N]T to []const T + //if (is_slice(wanted_type) && + // actual_type->id == TypeTableEntryIdPointer && + // actual_type->data.pointer.is_const && + // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) + //{ + // TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; + // assert(ptr_type->id == TypeTableEntryIdPointer); + + // TypeTableEntry *array_type = actual_type->data.pointer.child_type; + + // if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && + // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, + // source_node, false).id == ConstCastResultIdOk) + // { + // return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); + // } + //} + + //// cast from [N]T to *const []const T + //if (wanted_type->id == TypeTableEntryIdPointer && + // wanted_type->data.pointer.is_const && + // is_slice(wanted_type->data.pointer.child_type) && + // actual_type->id == TypeTableEntryIdArray) + //{ + // TypeTableEntry *ptr_type = + // wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; + // assert(ptr_type->id == TypeTableEntryIdPointer); + // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && + // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, + // source_node, false).id == ConstCastResultIdOk) + // { + // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value); + // if (type_is_invalid(cast1->value.type)) + // return ira->codegen->invalid_instruction; + + // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + // if (type_is_invalid(cast2->value.type)) + // return ira->codegen->invalid_instruction; + + // return cast2; + // } + //} + + //// cast from [N]T to ?[]const T + //if (wanted_type->id == TypeTableEntryIdOptional && + // is_slice(wanted_type->data.maybe.child_type) && + // actual_type->id == TypeTableEntryIdArray) + //{ + // TypeTableEntry *ptr_type = + // wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; + // assert(ptr_type->id == TypeTableEntryIdPointer); + // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && + // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, + // source_node, false).id == ConstCastResultIdOk) + // { + // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value); + // if (type_is_invalid(cast1->value.type)) + // return ira->codegen->invalid_instruction; + + // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + // if (type_is_invalid(cast2->value.type)) + // return ira->codegen->invalid_instruction; + + // return cast2; + // } + //} + + //// *[N]T to [*]T + //if (wanted_type->id == TypeTableEntryIdPointer && + // wanted_type->data.pointer.ptr_len == PtrLenUnknown && + // actual_type->id == TypeTableEntryIdPointer && + // actual_type->data.pointer.ptr_len == PtrLenSingle && + // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray && + // actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment && + // types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + // actual_type->data.pointer.child_type->data.array.child_type, source_node, + // !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + //{ + // return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); + //} + + //// *[N]T to []T + //if (is_slice(wanted_type) && + // actual_type->id == TypeTableEntryIdPointer && + // actual_type->data.pointer.ptr_len == PtrLenSingle && + // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) + //{ + // TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; + // assert(slice_ptr_type->id == TypeTableEntryIdPointer); + // if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, + // actual_type->data.pointer.child_type->data.array.child_type, source_node, + // !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) + // { + // return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type); + // } + //} + + //// cast from T to ?T + //// note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism + //if (wanted_type->id == TypeTableEntryIdOptional) { + // TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; + // if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, + // false).id == ConstCastResultIdOk) + // { + // return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); + // } else if (actual_type->id == TypeTableEntryIdComptimeInt || + // actual_type->id == TypeTableEntryIdComptimeFloat) + // { + // if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { + // return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); + // } else { + // return ira->codegen->invalid_instruction; + // } + // } else if (wanted_child_type->id == TypeTableEntryIdPointer && + // wanted_child_type->data.pointer.is_const && + // (actual_type->id == TypeTableEntryIdPointer || is_container(actual_type))) + // { + // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_child_type, value); + // if (type_is_invalid(cast1->value.type)) + // return ira->codegen->invalid_instruction; + + // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + // if (type_is_invalid(cast2->value.type)) + // return ira->codegen->invalid_instruction; + + // return cast2; + // } + //} + + //// cast from null literal to maybe type + //if (wanted_type->id == TypeTableEntryIdOptional && + // actual_type->id == TypeTableEntryIdNull) + //{ + // return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); + //} + + //// cast from child type of error type to error type + //if (wanted_type->id == TypeTableEntryIdErrorUnion) { + // if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, + // source_node, false).id == ConstCastResultIdOk) + // { + // return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); + // } else if (actual_type->id == TypeTableEntryIdComptimeInt || + // actual_type->id == TypeTableEntryIdComptimeFloat) + // { + // if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) { + // return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); + // } else { + // return ira->codegen->invalid_instruction; + // } + // } + //} + + //// cast from [N]T to E![]const T + //if (wanted_type->id == TypeTableEntryIdErrorUnion && + // is_slice(wanted_type->data.error_union.payload_type) && + // actual_type->id == TypeTableEntryIdArray) + //{ + // TypeTableEntry *ptr_type = + // wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry; + // assert(ptr_type->id == TypeTableEntryIdPointer); + // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && + // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, + // source_node, false).id == ConstCastResultIdOk) + // { + // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); + // if (type_is_invalid(cast1->value.type)) + // return ira->codegen->invalid_instruction; + + // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + // if (type_is_invalid(cast2->value.type)) + // return ira->codegen->invalid_instruction; + + // return cast2; + // } + //} + + //// cast from error set to error union type + //if (wanted_type->id == TypeTableEntryIdErrorUnion && + // actual_type->id == TypeTableEntryIdErrorSet) + //{ + // return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type); + //} + + //// cast from T to E!?T + //if (wanted_type->id == TypeTableEntryIdErrorUnion && + // wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional && + // actual_type->id != TypeTableEntryIdOptional) + //{ + // TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; + // if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk || + // actual_type->id == TypeTableEntryIdNull || + // actual_type->id == TypeTableEntryIdComptimeInt || + // actual_type->id == TypeTableEntryIdComptimeFloat) + // { + // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); + // if (type_is_invalid(cast1->value.type)) + // return ira->codegen->invalid_instruction; + + // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + // if (type_is_invalid(cast2->value.type)) + // return ira->codegen->invalid_instruction; + + // return cast2; + // } + //} + + // cast from number literal to another type + //// cast from number literal to *const integer + //if (actual_type->id == TypeTableEntryIdComptimeFloat || + // actual_type->id == TypeTableEntryIdComptimeInt) + //{ + // ensure_complete_type(ira->codegen, wanted_type); + // if (type_is_invalid(wanted_type)) + // return ira->codegen->invalid_instruction; + // if (wanted_type->id == TypeTableEntryIdEnum) { + // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value); + // if (type_is_invalid(cast1->value.type)) + // return ira->codegen->invalid_instruction; + + // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + // if (type_is_invalid(cast2->value.type)) + // return ira->codegen->invalid_instruction; + + // return cast2; + // } else if (wanted_type->id == TypeTableEntryIdPointer && + // wanted_type->data.pointer.is_const) + // { + // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value); + // if (type_is_invalid(cast1->value.type)) + // return ira->codegen->invalid_instruction; + + // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + // if (type_is_invalid(cast2->value.type)) + // return ira->codegen->invalid_instruction; + + // return cast2; + // } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { + // CastOp op; + // if ((actual_type->id == TypeTableEntryIdComptimeFloat && + // wanted_type->id == TypeTableEntryIdFloat) || + // (actual_type->id == TypeTableEntryIdComptimeInt && + // wanted_type->id == TypeTableEntryIdInt)) + // { + // op = CastOpNumLitToConcrete; + // } else if (wanted_type->id == TypeTableEntryIdInt) { + // op = CastOpFloatToInt; + // } else if (wanted_type->id == TypeTableEntryIdFloat) { + // op = CastOpIntToFloat; + // } else { + // zig_unreachable(); + // } + // return ir_resolve_cast(ira, source_instr, value, wanted_type, op, false); + // } else { + // return ira->codegen->invalid_instruction; + // } + //} + + //// cast from typed number to integer or float literal. + //// works when the number is known at compile time + //if (instr_is_comptime(value) && + // ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) || + // (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdComptimeFloat))) + //{ + // return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); + //} + + //// cast from union to the enum type of the union + //if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) { + // type_ensure_zero_bits_known(ira->codegen, actual_type); + // if (type_is_invalid(actual_type)) + // return ira->codegen->invalid_instruction; + + // if (actual_type->data.unionation.tag_type == wanted_type) { + // return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type); + // } + //} + + //// enum to union which has the enum as the tag type + //if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum && + // (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum || + // wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) + //{ + // type_ensure_zero_bits_known(ira->codegen, wanted_type); + // if (wanted_type->data.unionation.tag_type == actual_type) { + // return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type); + // } + //} + + //// enum to &const union which has the enum as the tag type + //if (actual_type->id == TypeTableEntryIdEnum && wanted_type->id == TypeTableEntryIdPointer) { + // TypeTableEntry *union_type = wanted_type->data.pointer.child_type; + // if (union_type->data.unionation.decl_node->data.container_decl.auto_enum || + // union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr) + // { + // type_ensure_zero_bits_known(ira->codegen, union_type); + // if (union_type->data.unionation.tag_type == actual_type) { + // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, union_type, value); + // if (type_is_invalid(cast1->value.type)) + // return ira->codegen->invalid_instruction; + + // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + // if (type_is_invalid(cast2->value.type)) + // return ira->codegen->invalid_instruction; + + // return cast2; + // } + // } + //} + + //// cast from *T to *[1]T + //if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && + // actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle) + //{ + // TypeTableEntry *array_type = wanted_type->data.pointer.child_type; + // if (array_type->id == TypeTableEntryIdArray && array_type->data.array.len == 1 && + // types_match_const_cast_only(ira, array_type->data.array.child_type, + // actual_type->data.pointer.child_type, source_node, + // !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + // { + // if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) { + // ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); + // add_error_note(ira->codegen, msg, value->source_node, + // buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), + // actual_type->data.pointer.alignment)); + // add_error_note(ira->codegen, msg, source_instr->source_node, + // buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), + // wanted_type->data.pointer.alignment)); + // return ira->codegen->invalid_instruction; + // } + // return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type); + // } + //} + + //// cast from T to *T where T is zero bits + //if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && + // types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + // actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + //{ + // type_ensure_zero_bits_known(ira->codegen, actual_type); + // if (type_is_invalid(actual_type)) { + // return ira->codegen->invalid_instruction; + // } + // if (!type_has_bits(actual_type)) { + // return ir_get_ref(ira, source_instr, value, false, false); + // } + //} + + //// cast from undefined to anything + //if (actual_type->id == TypeTableEntryIdUndefined) { + // return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); + //} + + //// cast from something to const pointer of it + //if (!type_requires_comptime(actual_type)) { + // TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); + // if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node, false).id == ConstCastResultIdOk) { + // return ir_analyze_cast_ref(ira, source_instr, value, wanted_type); + // } + //} + + try ira.addCompileError( + source_instr.span, + "expected type '{}', found '{}'", + dest_type.name, + from_type.name, + ); + //ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, + // buf_sprintf("expected type '%s', found '%s'", + // buf_ptr(&wanted_type->name), + // buf_ptr(&actual_type->name))); + //report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg); + return error.SemanticAnalysisFailed; } fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Inst) ?*Value { diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index b196656367..ade2479f41 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -30,6 +30,8 @@ pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag; pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; pub const ConstAllOnes = c.LLVMConstAllOnes; pub const ConstInt = c.LLVMConstInt; +pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision; +pub const ConstNeg = c.LLVMConstNeg; pub const ConstNull = c.LLVMConstNull; pub const ConstStringInContext = c.LLVMConstStringInContext; pub const ConstStructInContext = c.LLVMConstStructInContext; diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index 5a1f43bcf9..dccf937a47 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const llvm = @import("llvm.zig"); +const CInt = @import("c_int.zig").CInt; pub const FloatAbi = enum { Hard, @@ -173,7 +174,7 @@ pub const Target = union(enum) { return self.getArchPtrBitWidth() == 64; } - pub fn getArchPtrBitWidth(self: Target) u8 { + pub fn getArchPtrBitWidth(self: Target) u32 { switch (self.getArch()) { builtin.Arch.avr, builtin.Arch.msp430, @@ -429,4 +430,100 @@ pub const Target = union(enum) { } return result; } + + pub fn cIntTypeSizeInBits(self: Target, id: CInt.Id) u32 { + const arch = self.getArch(); + switch (self.getOs()) { + builtin.Os.freestanding => switch (self.getArch()) { + builtin.Arch.msp430 => switch (id) { + CInt.Id.Short, + CInt.Id.UShort, + CInt.Id.Int, + CInt.Id.UInt, + => return 16, + CInt.Id.Long, + CInt.Id.ULong, + => return 32, + CInt.Id.LongLong, + CInt.Id.ULongLong, + => return 64, + }, + else => switch (id) { + CInt.Id.Short, + CInt.Id.UShort, + => return 16, + CInt.Id.Int, + CInt.Id.UInt, + => return 32, + CInt.Id.Long, + CInt.Id.ULong, + => return self.getArchPtrBitWidth(), + CInt.Id.LongLong, + CInt.Id.ULongLong, + => return 64, + }, + }, + + builtin.Os.linux, + builtin.Os.macosx, + builtin.Os.openbsd, + builtin.Os.zen, + => switch (id) { + CInt.Id.Short, + CInt.Id.UShort, + => return 16, + CInt.Id.Int, + CInt.Id.UInt, + => return 32, + CInt.Id.Long, + CInt.Id.ULong, + => return self.getArchPtrBitWidth(), + CInt.Id.LongLong, + CInt.Id.ULongLong, + => return 64, + }, + + builtin.Os.windows => switch (id) { + CInt.Id.Short, + CInt.Id.UShort, + => return 16, + CInt.Id.Int, + CInt.Id.UInt, + => return 32, + CInt.Id.Long, + CInt.Id.ULong, + CInt.Id.LongLong, + CInt.Id.ULongLong, + => return 64, + }, + + builtin.Os.ananas, + builtin.Os.cloudabi, + builtin.Os.dragonfly, + builtin.Os.freebsd, + builtin.Os.fuchsia, + builtin.Os.ios, + builtin.Os.kfreebsd, + builtin.Os.lv2, + builtin.Os.netbsd, + builtin.Os.solaris, + builtin.Os.haiku, + builtin.Os.minix, + builtin.Os.rtems, + builtin.Os.nacl, + builtin.Os.cnk, + builtin.Os.aix, + builtin.Os.cuda, + builtin.Os.nvcl, + builtin.Os.amdhsa, + builtin.Os.ps4, + builtin.Os.elfiamcu, + builtin.Os.tvos, + builtin.Os.watchos, + builtin.Os.mesa3d, + builtin.Os.contiki, + builtin.Os.amdpal, + => @panic("TODO specify the C integer type sizes for this OS"), + } + } }; diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index bb1fb9bb01..da290ece13 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -9,6 +9,7 @@ const ObjectFile = @import("codegen.zig").ObjectFile; pub const Type = struct { base: Value, id: Id, + name: []const u8, pub const Id = builtin.TypeId; @@ -151,6 +152,18 @@ pub const Type = struct { std.debug.warn("{}", @tagName(base.id)); } + fn init(base: *Type, comp: *Compilation, id: Id, name: []const u8) void { + base.* = Type{ + .base = Value{ + .id = Value.Id.Type, + .typeof = &MetaType.get(comp).base, + .ref_count = std.atomic.Int(usize).init(1), + }, + .id = id, + .name = name, + }; + } + pub fn getAbiAlignment(base: *Type, comp: *Compilation) u32 { @panic("TODO getAbiAlignment"); } @@ -181,20 +194,15 @@ pub const Type = struct { pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { const result = try comp.gpa().create(Fn{ - .base = Type{ - .base = Value{ - .id = Value.Id.Type, - .typeof = &MetaType.get(comp).base, - .ref_count = std.atomic.Int(usize).init(1), - }, - .id = builtin.TypeId.Fn, - }, + .base = undefined, .return_type = return_type, .params = params, .is_var_args = is_var_args, }); errdefer comp.gpa().destroy(result); + result.base.init(comp, Id.Fn, "TODO fn type name"); + result.return_type.base.ref(); for (result.params) |param| { param.typeof.base.ref(); @@ -293,13 +301,77 @@ pub const Type = struct { pub const Int = struct { base: Type, + key: Key, + garbage_node: std.atomic.Stack(*Int).Node, + + pub const Key = struct { + bit_count: u32, + is_signed: bool, + + pub fn hash(self: *const Key) u32 { + const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 }; + return rands[@boolToInt(self.is_signed)] *% self.bit_count; + } + + pub fn eql(self: *const Key, other: *const Key) bool { + return self.bit_count == other.bit_count and self.is_signed == other.is_signed; + } + }; + + pub async fn get(comp: *Compilation, key: Key) !*Int { + { + const held = await (async comp.int_type_table.acquire() catch unreachable); + defer held.release(); + + if (held.value.get(&key)) |entry| { + return entry.value; + } + } + + const self = try comp.gpa().create(Int{ + .base = undefined, + .key = key, + .garbage_node = undefined, + }); + errdefer comp.gpa().destroy(self); + + const u_or_i = "ui"[@boolToInt(key.is_signed)]; + const name = std.fmt.allocPrint(comp.gpa(), "{c}{}", u_or_i, key.bit_count); + errdefer comp.gpa().free(name); + + self.base.init(comp, Id.Int, name); + + { + const held = await (async comp.int_type_table.acquire() catch unreachable); + defer held.release(); + + held.value.put(&self.key, self); + } + return self; + } pub fn destroy(self: *Int, comp: *Compilation) void { + self.garbage_node = std.atomic.Stack(*Int).Node{ + .data = self, + .next = undefined, + }; + comp.registerGarbage(Int, &self.garbage_node); + } + + pub async fn gcDestroy(self: *Int, comp: *Compilation) void { + { + const held = await (async comp.int_type_table.acquire() catch unreachable); + defer held.release(); + + _ = held.value.remove(&self.key).?; + } + // we allocated the name + comp.gpa().free(self.base.name); comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef { - @panic("TODO"); + pub fn getLlvmType(self: *Int, ofile: *ObjectFile) !llvm.TypeRef { + return llvm.IntTypeInContext(ofile.context, self.key.bit_count) orelse return error.OutOfMemory; } }; @@ -374,6 +446,12 @@ pub const Type = struct { pub const ComptimeInt = struct { base: Type, + /// Adds 1 reference to the resulting type + pub fn get(comp: *Compilation) *ComptimeInt { + comp.comptime_int_type.base.base.ref(); + return comp.comptime_int_type; + } + pub fn destroy(self: *ComptimeInt, comp: *Compilation) void { comp.gpa().destroy(self); } diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index be19c6bccf..e518cd4173 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -29,6 +29,7 @@ pub const Value = struct { Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp), Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp), Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp), + Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp), } } } @@ -50,6 +51,7 @@ pub const Value = struct { Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile), Id.NoReturn => unreachable, Id.Ptr => @panic("TODO"), + Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile), } } @@ -60,6 +62,7 @@ pub const Value = struct { Bool, NoReturn, Ptr, + Int, }; pub const Type = @import("type.zig").Type; @@ -198,4 +201,59 @@ pub const Value = struct { comp.gpa().destroy(self); } }; + + pub const Int = struct { + base: Value, + big_int: std.math.big.Int, + + pub fn createFromString(comp: *Compilation, typeof: *Type, base: u8, value: []const u8) !*Int { + const self = try comp.gpa().create(Value.Int{ + .base = Value{ + .id = Value.Id.Int, + .typeof = typeof, + .ref_count = std.atomic.Int(usize).init(1), + }, + .big_int = undefined, + }); + typeof.base.ref(); + errdefer comp.gpa().destroy(self); + + self.big_int = try std.math.big.Int.init(comp.gpa()); + errdefer self.big_int.deinit(); + + try self.big_int.setString(base, value); + + return self; + } + + pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef { + switch (self.base.typeof.id) { + Type.Id.Int => { + const type_ref = try self.base.typeof.getLlvmType(ofile); + if (self.big_int.len == 0) { + return llvm.ConstNull(type_ref); + } + const unsigned_val = if (self.big_int.len == 1) blk: { + break :blk llvm.ConstInt(type_ref, self.big_int.limbs[0], @boolToInt(false)); + } else if (@sizeOf(std.math.big.Limb) == @sizeOf(u64)) blk: { + break :blk llvm.ConstIntOfArbitraryPrecision( + type_ref, + @intCast(c_uint, self.big_int.len), + @ptrCast([*]u64, self.big_int.limbs.ptr), + ); + } else { + @compileError("std.math.Big.Int.Limb size does not match LLVM"); + }; + return if (self.big_int.positive) unsigned_val else llvm.ConstNeg(unsigned_val); + }, + Type.Id.ComptimeInt => unreachable, + else => unreachable, + } + } + + pub fn destroy(self: *Int, comp: *Compilation) void { + self.big_int.deinit(); + comp.gpa().destroy(self); + } + }; }; diff --git a/std/event/group.zig b/std/event/group.zig index 5ca8f5a0ca..fee31a56a6 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -38,6 +38,15 @@ pub fn Group(comptime ReturnType: type) type { self.alloc_stack.push(node); } + /// Add a node to the group. Thread-safe. Cannot fail. + /// `node.data` should be the promise handle to add to the group. + /// The node's memory should be in the coroutine frame of + /// the handle that is in the node, or somewhere guaranteed to live + /// at least as long. + pub fn addNode(self: *Self, node: *Stack.Node) void { + self.coro_stack.push(node); + } + /// This is equivalent to an async call, but the async function is added to the group, instead /// of returning a promise. func must be async and have return type ReturnType. /// Thread-safe. @@ -98,6 +107,8 @@ pub fn Group(comptime ReturnType: type) type { } /// Cancel all the outstanding promises. May only be called if wait was never called. + /// TODO These should be `cancelasync` not `cancel`. + /// See https://github.com/ziglang/zig/issues/1261 pub fn cancelAll(self: *Self) void { while (self.coro_stack.pop()) |node| { cancel node.data; diff --git a/std/math/big/int.zig b/std/math/big/int.zig index caa9d0a7ed..8b9e89254c 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -60,8 +60,9 @@ pub const Int = struct { self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity); } - pub fn deinit(self: Int) void { + pub fn deinit(self: *Int) void { self.allocator.free(self.limbs); + self.* = undefined; } pub fn clone(other: Int) !Int { -- cgit v1.2.3 From a9f0681f85679b602cd5f322f6b9b4b2d18a5c4a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Jul 2018 10:47:17 -0400 Subject: prevent non-export symbols from clobbering builtins closes #1263 --- src/codegen.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index 6e121be270..7420da9797 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -60,6 +60,33 @@ PackageTableEntry *new_anonymous_package(void) { return new_package("", ""); } +static const char *symbols_that_llvm_depends_on[] = { + "memcpy", + "memset", + "sqrt", + "powi", + "sin", + "cos", + "pow", + "exp", + "exp2", + "log", + "log10", + "log2", + "fma", + "fabs", + "minnum", + "maxnum", + "copysign", + "floor", + "ceil", + "trunc", + "rint", + "nearbyint", + "round", + // TODO probably all of compiler-rt needs to go here +}; + CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, Buf *zig_lib_dir) { @@ -94,6 +121,10 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib); buf_resize(&g->global_asm, 0); + for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) { + g->external_prototypes.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr); + } + if (root_src_path) { Buf *src_basename = buf_alloc(); Buf *src_dir = buf_alloc(); -- cgit v1.2.3 From 0736e6aa347f065c15c7acaa0a7d2cc61b85bba6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Jul 2018 13:06:13 -0400 Subject: std.os.File: add missing pub modifiers --- std/os/file.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/std/os/file.zig b/std/os/file.zig index 055f185121..c160e90e0e 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -15,7 +15,7 @@ pub const File = struct { /// The OS-specific file descriptor or file handle. handle: os.FileHandle, - const OpenError = os.WindowsOpenError || os.PosixOpenError; + pub const OpenError = os.WindowsOpenError || os.PosixOpenError; /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. /// Call close to clean up. @@ -289,7 +289,7 @@ pub const File = struct { Unexpected, }; - fn mode(self: *File) ModeError!os.FileMode { + pub fn mode(self: *File) ModeError!os.FileMode { if (is_posix) { var stat: posix.Stat = undefined; const err = posix.getErrno(posix.fstat(self.handle, &stat)); @@ -364,7 +364,7 @@ pub const File = struct { pub const WriteError = os.WindowsWriteError || os.PosixWriteError; - fn write(self: *File, bytes: []const u8) WriteError!void { + pub fn write(self: *File, bytes: []const u8) WriteError!void { if (is_posix) { try os.posixWrite(self.handle, bytes); } else if (is_windows) { -- cgit v1.2.3 From 3908b4fdee64c0ec6ef4d65e5b31210d4fea4423 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Jul 2018 15:11:39 -0400 Subject: self-hosted: refactor ParsedFile out of existence also we are successfully analyzing the return type of main --- src-self-hosted/compilation.zig | 265 +++++++++++++++++++++++----------------- src-self-hosted/decl.zig | 6 +- src-self-hosted/ir.zig | 141 ++++++++++++++++++--- src-self-hosted/parsed_file.zig | 6 - src-self-hosted/scope.zig | 75 +++++++++--- src-self-hosted/value.zig | 5 + 6 files changed, 343 insertions(+), 155 deletions(-) delete mode 100644 src-self-hosted/parsed_file.zig diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 01f2314167..b11a3408cd 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -21,7 +21,6 @@ const Scope = @import("scope.zig").Scope; const Decl = @import("decl.zig").Decl; const ir = @import("ir.zig"); const Visib = @import("visib.zig").Visib; -const ParsedFile = @import("parsed_file.zig").ParsedFile; const Value = @import("value.zig").Value; const Type = Value.Type; const Span = errmsg.Span; @@ -470,6 +469,35 @@ pub const Compilation = struct { return comp; } + /// it does ref the result because it could be an arbitrary integer size + pub fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type { + if (name.len >= 2) { + switch (name[0]) { + 'i', 'u' => blk: { + for (name[1..]) |byte| + switch (byte) { + '0'...'9' => {}, + else => break :blk, + }; + const is_signed = name[0] == 'i'; + const bit_count = std.fmt.parseUnsigned(u32, name[1..], 10) catch |err| switch (err) { + error.Overflow => return error.Overflow, + error.InvalidCharacter => unreachable, // we just checked the characters above + }; + @panic("get int type - need to make everything async"); + }, + else => {}, + } + } + + if (comp.primitive_type_table.get(name)) |entry| { + entry.value.base.ref(); + return entry.value; + } + + return null; + } + fn initTypes(comp: *Compilation) !void { comp.meta_type = try comp.arena().create(Type.MetaType{ .base = Type{ @@ -671,82 +699,81 @@ pub const Compilation = struct { } async fn compileAndLink(self: *Compilation) !void { - const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); - // TODO async/await os.path.real - const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { - try printError("unable to get real path '{}': {}", root_src_path, err); - return err; - }; - errdefer self.gpa().free(root_src_real_path); - - // TODO async/await readFileAlloc() - const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| { - try printError("unable to open '{}': {}", root_src_real_path, err); - return err; - }; - errdefer self.gpa().free(source_code); + if (self.root_src_path) |root_src_path| { + // TODO async/await os.path.real + const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { + try printError("unable to get real path '{}': {}", root_src_path, err); + return err; + }; + const root_scope = blk: { + errdefer self.gpa().free(root_src_real_path); - const parsed_file = try self.gpa().create(ParsedFile{ - .tree = undefined, - .realpath = root_src_real_path, - }); - errdefer self.gpa().destroy(parsed_file); + // TODO async/await readFileAlloc() + const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| { + try printError("unable to open '{}': {}", root_src_real_path, err); + return err; + }; + errdefer self.gpa().free(source_code); - parsed_file.tree = try std.zig.parse(self.gpa(), source_code); - errdefer parsed_file.tree.deinit(); + var tree = try std.zig.parse(self.gpa(), source_code); + errdefer tree.deinit(); - const tree = &parsed_file.tree; + break :blk try Scope.Root.create(self, tree, root_src_real_path); + }; + defer root_scope.base.deref(self); - // create empty struct for it - const decls = try Scope.Decls.create(self, null); - defer decls.base.deref(self); + const tree = &root_scope.tree; - var decl_group = event.Group(BuildError!void).init(self.loop); - errdefer decl_group.cancelAll(); + const decls = try Scope.Decls.create(self, &root_scope.base); + defer decls.base.deref(self); - var it = tree.root_node.decls.iterator(0); - while (it.next()) |decl_ptr| { - const decl = decl_ptr.*; - switch (decl.id) { - ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl); + var decl_group = event.Group(BuildError!void).init(self.loop); + errdefer decl_group.cancelAll(); - try decl_group.call(addCompTimeBlock, self, parsed_file, &decls.base, comptime_node); - }, - ast.Node.Id.VarDecl => @panic("TODO"), - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); - - const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { - try self.addCompileError(parsed_file, Span{ - .first = fn_proto.fn_token, - .last = fn_proto.fn_token + 1, - }, "missing function name"); - continue; - }; + var it = tree.root_node.decls.iterator(0); + while (it.next()) |decl_ptr| { + const decl = decl_ptr.*; + switch (decl.id) { + ast.Node.Id.Comptime => { + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl); - const fn_decl = try self.gpa().create(Decl.Fn{ - .base = Decl{ - .id = Decl.Id.Fn, - .name = name, - .visib = parseVisibToken(tree, fn_proto.visib_token), - .resolution = event.Future(BuildError!void).init(self.loop), - .resolution_in_progress = 0, - .parsed_file = parsed_file, - .parent_scope = &decls.base, - }, - .value = Decl.Fn.Val{ .Unresolved = {} }, - .fn_proto = fn_proto, - }); - errdefer self.gpa().destroy(fn_decl); - - try decl_group.call(addTopLevelDecl, self, &fn_decl.base); - }, - ast.Node.Id.TestDecl => @panic("TODO"), - else => unreachable, + try decl_group.call(addCompTimeBlock, self, &decls.base, comptime_node); + }, + ast.Node.Id.VarDecl => @panic("TODO"), + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); + + const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { + try self.addCompileError(root_scope, Span{ + .first = fn_proto.fn_token, + .last = fn_proto.fn_token + 1, + }, "missing function name"); + continue; + }; + + const fn_decl = try self.gpa().create(Decl.Fn{ + .base = Decl{ + .id = Decl.Id.Fn, + .name = name, + .visib = parseVisibToken(tree, fn_proto.visib_token), + .resolution = event.Future(BuildError!void).init(self.loop), + .resolution_in_progress = 0, + .parent_scope = &decls.base, + }, + .value = Decl.Fn.Val{ .Unresolved = {} }, + .fn_proto = fn_proto, + }); + errdefer self.gpa().destroy(fn_decl); + + try decl_group.call(addTopLevelDecl, self, &fn_decl.base); + }, + ast.Node.Id.TestDecl => @panic("TODO"), + else => unreachable, + } } + try await (async decl_group.wait() catch unreachable); } - try await (async decl_group.wait() catch unreachable); + try await (async self.prelink_group.wait() catch unreachable); const any_prelink_errors = blk: { @@ -764,23 +791,15 @@ pub const Compilation = struct { /// caller takes ownership of resulting Code async fn genAndAnalyzeCode( comp: *Compilation, - parsed_file: *ParsedFile, scope: *Scope, node: *ast.Node, expected_type: ?*Type, - ) !?*ir.Code { - const unanalyzed_code = (await (async ir.gen( + ) !*ir.Code { + const unanalyzed_code = try await (async ir.gen( comp, node, scope, - parsed_file, - ) catch unreachable)) catch |err| switch (err) { - // This poison value should not cause the errdefers to run. It simply means - // that self.compile_errors is populated. - // TODO https://github.com/ziglang/zig/issues/769 - error.SemanticAnalysisFailed => return null, - else => return err, - }; + ) catch unreachable); defer unanalyzed_code.destroy(comp.gpa()); if (comp.verbose_ir) { @@ -788,44 +807,46 @@ pub const Compilation = struct { unanalyzed_code.dump(); } - const analyzed_code = (await (async ir.analyze( + const analyzed_code = try await (async ir.analyze( comp, - parsed_file, unanalyzed_code, expected_type, - ) catch unreachable)) catch |err| switch (err) { - // This poison value should not cause the errdefers to run. It simply means - // that self.compile_errors is populated. - // TODO https://github.com/ziglang/zig/issues/769 - error.SemanticAnalysisFailed => return null, - else => return err, - }; + ) catch unreachable); errdefer analyzed_code.destroy(comp.gpa()); + if (comp.verbose_ir) { + std.debug.warn("analyzed:\n"); + analyzed_code.dump(); + } + return analyzed_code; } async fn addCompTimeBlock( comp: *Compilation, - parsed_file: *ParsedFile, scope: *Scope, comptime_node: *ast.Node.Comptime, ) !void { const void_type = Type.Void.get(comp); defer void_type.base.base.deref(comp); - const analyzed_code = (try await (async genAndAnalyzeCode( + const analyzed_code = (await (async genAndAnalyzeCode( comp, - parsed_file, scope, comptime_node.expr, &void_type.base, - ) catch unreachable)) orelse return; + ) catch unreachable)) catch |err| switch (err) { + // This poison value should not cause the errdefers to run. It simply means + // that comp.compile_errors is populated. + error.SemanticAnalysisFailed => return {}, + else => return err, + }; analyzed_code.destroy(comp.gpa()); } async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void { - const is_export = decl.isExported(&decl.parsed_file.tree); + const tree = &decl.findRootScope().tree; + const is_export = decl.isExported(tree); if (is_export) { try self.prelink_group.call(verifyUniqueSymbol, self, decl); @@ -833,24 +854,24 @@ pub const Compilation = struct { } } - fn addCompileError(self: *Compilation, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void { - const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args); - errdefer self.loop.allocator.free(text); + fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void { + const text = try std.fmt.allocPrint(self.gpa(), fmt, args); + errdefer self.gpa().free(text); - try self.prelink_group.call(addCompileErrorAsync, self, parsed_file, span, text); + try self.prelink_group.call(addCompileErrorAsync, self, root, span, text); } async fn addCompileErrorAsync( self: *Compilation, - parsed_file: *ParsedFile, + root: *Scope.Root, span: Span, text: []u8, ) !void { const msg = try self.loop.allocator.create(errmsg.Msg{ - .path = parsed_file.realpath, + .path = root.realpath, .text = text, .span = span, - .tree = &parsed_file.tree, + .tree = &root.tree, }); errdefer self.loop.allocator.destroy(msg); @@ -866,7 +887,7 @@ pub const Compilation = struct { if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { try self.addCompileError( - decl.parsed_file, + decl.findRootScope(), decl.getSpan(), "exported symbol collision: '{}'", decl.name, @@ -988,6 +1009,24 @@ pub const Compilation = struct { fn registerGarbage(comp: *Compilation, comptime T: type, node: *std.atomic.Stack(*T).Node) void { // TODO put the garbage somewhere } + + /// Returns a value which has been ref()'d once + async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value { + const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable); + defer analyzed_code.destroy(comp.gpa()); + + return analyzed_code.getCompTimeResult(comp); + } + + async fn analyzeTypeExpr(comp: *Compilation, scope: *Scope, node: *ast.Node) !*Type { + const meta_type = &Type.MetaType.get(comp).base; + defer meta_type.base.deref(comp); + + const result_val = try await (async comp.analyzeConstValue(scope, node, meta_type) catch unreachable); + errdefer result_val.base.deref(comp); + + return result_val.cast(Type).?; + } }; fn printError(comptime format: []const u8, args: ...) !void { @@ -1011,7 +1050,12 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void { if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*; - decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable); + decl.resolution.data = (await (async generateDecl(comp, decl) catch unreachable)) catch |err| switch (err) { + // This poison value should not cause the errdefers to run. It simply means + // that comp.compile_errors is populated. + error.SemanticAnalysisFailed => {}, + else => err, + }; decl.resolution.resolve(); return decl.resolution.data; } @@ -1034,9 +1078,12 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope); defer fndef_scope.base.deref(comp); - // TODO actually look at the return type of the AST - const return_type = &Type.Void.get(comp).base; - defer return_type.base.deref(comp); + const return_type_node = switch (fn_decl.fn_proto.return_type) { + ast.Node.FnProto.ReturnType.Explicit => |n| n, + ast.Node.FnProto.ReturnType.InferErrorSet => |n| n, + }; + const return_type = try await (async comp.analyzeTypeExpr(&fndef_scope.base, return_type_node) catch unreachable); + return_type.base.deref(comp); const is_var_args = false; const params = ([*]Type.Fn.Param)(undefined)[0..0]; @@ -1050,19 +1097,13 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; - const analyzed_code = (try await (async comp.genAndAnalyzeCode( - fn_decl.base.parsed_file, + const analyzed_code = try await (async comp.genAndAnalyzeCode( &fndef_scope.base, body_node, return_type, - ) catch unreachable)) orelse return; + ) catch unreachable); errdefer analyzed_code.destroy(comp.gpa()); - if (comp.verbose_ir) { - std.debug.warn("analyzed:\n"); - analyzed_code.dump(); - } - // Kick off rendering to LLVM module, but it doesn't block the fn decl // analysis from being complete. try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); diff --git a/src-self-hosted/decl.zig b/src-self-hosted/decl.zig index c0173266ee..bb065640c2 100644 --- a/src-self-hosted/decl.zig +++ b/src-self-hosted/decl.zig @@ -3,7 +3,6 @@ const Allocator = mem.Allocator; const mem = std.mem; const ast = std.zig.ast; const Visib = @import("visib.zig").Visib; -const ParsedFile = @import("parsed_file.zig").ParsedFile; const event = std.event; const Value = @import("value.zig").Value; const Token = std.zig.Token; @@ -17,7 +16,6 @@ pub const Decl = struct { visib: Visib, resolution: event.Future(Compilation.BuildError!void), resolution_in_progress: u8, - parsed_file: *ParsedFile, parent_scope: *Scope, pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); @@ -48,6 +46,10 @@ pub const Decl = struct { } } + pub fn findRootScope(base: *const Decl) *Scope.Root { + return base.parent_scope.findRoot(); + } + pub const Id = enum { Var, Fn, diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 5a8a74ef54..bdfccd085a 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -8,7 +8,6 @@ const Value = @import("value.zig").Value; const Type = Value.Type; const assert = std.debug.assert; const Token = std.zig.Token; -const ParsedFile = @import("parsed_file.zig").ParsedFile; const Span = @import("errmsg.zig").Span; const llvm = @import("llvm.zig"); const ObjectFile = @import("codegen.zig").ObjectFile; @@ -611,6 +610,33 @@ pub const Code = struct { } } } + + /// returns a ref-incremented value, or adds a compile error + pub fn getCompTimeResult(self: *Code, comp: *Compilation) !*Value { + const bb = self.basic_block_list.at(0); + for (bb.instruction_list.toSliceConst()) |inst| { + if (inst.cast(Inst.Return)) |ret_inst| { + const ret_value = ret_inst.params.return_value; + if (ret_value.isCompTime()) { + return ret_value.val.KnownValue.getRef(); + } + try comp.addCompileError( + ret_value.scope.findRoot(), + ret_value.span, + "unable to evaluate constant expression", + ); + return error.SemanticAnalysisFailed; + } else if (inst.hasSideEffects()) { + try comp.addCompileError( + inst.scope.findRoot(), + inst.span, + "unable to evaluate constant expression", + ); + return error.SemanticAnalysisFailed; + } + } + unreachable; + } }; pub const Builder = struct { @@ -618,14 +644,14 @@ pub const Builder = struct { code: *Code, current_basic_block: *BasicBlock, next_debug_id: usize, - parsed_file: *ParsedFile, + root_scope: *Scope.Root, is_comptime: bool, is_async: bool, begin_scope: ?*Scope, pub const Error = Analyze.Error; - pub fn init(comp: *Compilation, parsed_file: *ParsedFile, begin_scope: ?*Scope) !Builder { + pub fn init(comp: *Compilation, root_scope: *Scope.Root, begin_scope: ?*Scope) !Builder { const code = try comp.gpa().create(Code{ .basic_block_list = undefined, .arena = std.heap.ArenaAllocator.init(comp.gpa()), @@ -636,7 +662,7 @@ pub const Builder = struct { return Builder{ .comp = comp, - .parsed_file = parsed_file, + .root_scope = root_scope, .current_basic_block = undefined, .code = code, .next_debug_id = 0, @@ -718,7 +744,10 @@ pub const Builder = struct { ast.Node.Id.UndefinedLiteral => return error.Unimplemented, ast.Node.Id.ThisLiteral => return error.Unimplemented, ast.Node.Id.Unreachable => return error.Unimplemented, - ast.Node.Id.Identifier => return error.Unimplemented, + ast.Node.Id.Identifier => { + const identifier = @fieldParentPtr(ast.Node.Identifier, "base", node); + return irb.genIdentifier(identifier, scope, lval); + }, ast.Node.Id.GroupedExpression => { const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node); return irb.genNode(grouped_expr.expr, scope, lval); @@ -761,16 +790,17 @@ pub const Builder = struct { Scope.Id.CompTime => return true, Scope.Id.FnDef => return false, Scope.Id.Decls => unreachable, + Scope.Id.Root => unreachable, Scope.Id.Block, Scope.Id.Defer, Scope.Id.DeferExpr, - => scope = scope.parent orelse return false, + => scope = scope.parent.?, } } } pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst { - const int_token = irb.parsed_file.tree.tokenSlice(int_lit.token); + const int_token = irb.root_scope.tree.tokenSlice(int_lit.token); var base: u8 = undefined; var rest: []const u8 = undefined; @@ -845,7 +875,7 @@ pub const Builder = struct { if (statement_node.cast(ast.Node.Defer)) |defer_node| { // defer starts a new scope - const defer_token = irb.parsed_file.tree.tokens.at(defer_node.defer_token); + const defer_token = irb.root_scope.tree.tokens.at(defer_node.defer_token); const kind = switch (defer_token.id) { Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit, Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit, @@ -928,7 +958,7 @@ pub const Builder = struct { const src_span = Span.token(control_flow_expr.ltoken); if (scope.findFnDef() == null) { try irb.comp.addCompileError( - irb.parsed_file, + irb.root_scope, src_span, "return expression outside function definition", ); @@ -938,7 +968,7 @@ pub const Builder = struct { if (scope.findDeferExpr()) |scope_defer_expr| { if (!scope_defer_expr.reported_err) { try irb.comp.addCompileError( - irb.parsed_file, + irb.root_scope, src_span, "cannot return from defer expression", ); @@ -1012,6 +1042,69 @@ pub const Builder = struct { } } + pub fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst { + const src_span = Span.token(identifier.token); + const name = irb.root_scope.tree.tokenSlice(identifier.token); + + //if (buf_eql_str(variable_name, "_") && lval == LValPtr) { + // IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, node); + // const_instruction->base.value.type = get_pointer_to_type(irb->codegen, + // irb->codegen->builtin_types.entry_void, false); + // const_instruction->base.value.special = ConstValSpecialStatic; + // const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialDiscard; + // return &const_instruction->base; + //} + + if (irb.comp.getPrimitiveType(name)) |result| { + if (result) |primitive_type| { + defer primitive_type.base.deref(irb.comp); + switch (lval) { + LVal.Ptr => return error.Unimplemented, + LVal.None => return irb.buildConstValue(scope, src_span, &primitive_type.base), + } + } + } else |err| switch (err) { + error.Overflow => { + try irb.comp.addCompileError(irb.root_scope, src_span, "integer too large"); + return error.SemanticAnalysisFailed; + }, + } + //TypeTableEntry *primitive_type = get_primitive_type(irb->codegen, variable_name); + //if (primitive_type != nullptr) { + // IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); + // if (lval == LValPtr) { + // return ir_build_ref(irb, scope, node, value, false, false); + // } else { + // return value; + // } + //} + + //VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name); + //if (var) { + // IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var); + // if (lval == LValPtr) + // return var_ptr; + // else + // return ir_build_load_ptr(irb, scope, node, var_ptr); + //} + + //Tld *tld = find_decl(irb->codegen, scope, variable_name); + //if (tld) + // return ir_build_decl_ref(irb, scope, node, tld, lval); + + //if (node->owner->any_imports_failed) { + // // skip the error message since we had a failing import in this file + // // if an import breaks we don't need redundant undeclared identifier errors + // return irb->codegen->invalid_instruction; + //} + + // TODO put a variable of same name with invalid type in global scope + // so that future references to this same name will find a variable with an invalid type + + try irb.comp.addCompileError(irb.root_scope, src_span, "unknown identifier '{}'", name); + return error.SemanticAnalysisFailed; + } + const DeferCounts = struct { scope_exit: usize, error_exit: usize, @@ -1035,10 +1128,11 @@ pub const Builder = struct { Scope.Id.CompTime, Scope.Id.Block, + Scope.Id.Decls, + Scope.Id.Root, => scope = scope.parent orelse break, Scope.Id.DeferExpr => unreachable, - Scope.Id.Decls => unreachable, } } return result; @@ -1081,6 +1175,7 @@ pub const Builder = struct { }, Scope.Id.FnDef, Scope.Id.Decls, + Scope.Id.Root, => return is_noreturn, Scope.Id.CompTime, @@ -1188,6 +1283,12 @@ pub const Builder = struct { return inst; } + fn buildConstValue(self: *Builder, scope: *Scope, span: Span, v: *Value) !*Inst { + const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{}); + inst.val = IrVal{ .KnownValue = v.getRef() }; + return inst; + } + /// If the code is explicitly set to be comptime, then builds a const bool, /// otherwise builds a TestCompTime instruction. fn buildTestCompTime(self: *Builder, scope: *Scope, span: Span, target: *Inst) !*Inst { @@ -1259,8 +1360,8 @@ const Analyze = struct { OutOfMemory, }; - pub fn init(comp: *Compilation, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze { - var irb = try Builder.init(comp, parsed_file, null); + pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze { + var irb = try Builder.init(comp, root_scope, null); errdefer irb.abort(); return Analyze{ @@ -1338,7 +1439,7 @@ const Analyze = struct { } fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void { - return self.irb.comp.addCompileError(self.irb.parsed_file, span, fmt, args); + return self.irb.comp.addCompileError(self.irb.root_scope, span, fmt, args); } fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type { @@ -1800,9 +1901,8 @@ pub async fn gen( comp: *Compilation, body_node: *ast.Node, scope: *Scope, - parsed_file: *ParsedFile, ) !*Code { - var irb = try Builder.init(comp, parsed_file, scope); + var irb = try Builder.init(comp, scope.findRoot(), scope); errdefer irb.abort(); const entry_block = try irb.createBasicBlock(scope, c"Entry"); @@ -1818,11 +1918,12 @@ pub async fn gen( return irb.finish(); } -pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Code, expected_type: ?*Type) !*Code { - var ira = try Analyze.init(comp, parsed_file, expected_type); - errdefer ira.abort(); - +pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code { const old_entry_bb = old_code.basic_block_list.at(0); + const root_scope = old_entry_bb.scope.findRoot(); + + var ira = try Analyze.init(comp, root_scope, expected_type); + errdefer ira.abort(); const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null); new_entry_bb.ref(); diff --git a/src-self-hosted/parsed_file.zig b/src-self-hosted/parsed_file.zig deleted file mode 100644 index d728c2fd18..0000000000 --- a/src-self-hosted/parsed_file.zig +++ /dev/null @@ -1,6 +0,0 @@ -const ast = @import("std").zig.ast; - -pub const ParsedFile = struct { - tree: ast.Tree, - realpath: []const u8, -}; diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 00be450479..7733e61600 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -8,6 +8,7 @@ const ast = std.zig.ast; const Value = @import("value.zig").Value; const ir = @import("ir.zig"); const Span = @import("errmsg.zig").Span; +const assert = std.debug.assert; pub const Scope = struct { id: Id, @@ -23,7 +24,8 @@ pub const Scope = struct { if (base.ref_count == 0) { if (base.parent) |parent| parent.deref(comp); switch (base.id) { - Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(), + Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp), + Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(comp), Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp), Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp), Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp), @@ -33,6 +35,15 @@ pub const Scope = struct { } } + pub fn findRoot(base: *Scope) *Root { + var scope = base; + while (scope.parent) |parent| { + scope = parent; + } + assert(scope.id == Id.Root); + return @fieldParentPtr(Root, "base", scope); + } + pub fn findFnDef(base: *Scope) ?*FnDef { var scope = base; while (true) { @@ -44,6 +55,7 @@ pub const Scope = struct { Id.Defer, Id.DeferExpr, Id.CompTime, + Id.Root, => scope = scope.parent orelse return null, } } @@ -62,12 +74,14 @@ pub const Scope = struct { Id.Block, Id.Defer, Id.CompTime, + Id.Root, => scope = scope.parent orelse return null, } } } pub const Id = enum { + Root, Decls, Block, FnDef, @@ -76,12 +90,43 @@ pub const Scope = struct { DeferExpr, }; + pub const Root = struct { + base: Scope, + tree: ast.Tree, + realpath: []const u8, + + /// Creates a Root scope with 1 reference + /// Takes ownership of realpath + /// Caller must set tree + pub fn create(comp: *Compilation, tree: ast.Tree, realpath: []u8) !*Root { + const self = try comp.gpa().create(Root{ + .base = Scope{ + .id = Id.Root, + .parent = null, + .ref_count = 1, + }, + .tree = tree, + .realpath = realpath, + }); + errdefer comp.gpa().destroy(self); + + return self; + } + + pub fn destroy(self: *Root, comp: *Compilation) void { + comp.gpa().free(self.tree.source); + self.tree.deinit(); + comp.gpa().free(self.realpath); + comp.gpa().destroy(self); + } + }; + pub const Decls = struct { base: Scope, table: Decl.Table, /// Creates a Decls scope with 1 reference - pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls { + pub fn create(comp: *Compilation, parent: *Scope) !*Decls { const self = try comp.gpa().create(Decls{ .base = Scope{ .id = Id.Decls, @@ -95,14 +140,14 @@ pub const Scope = struct { self.table = Decl.Table.init(comp.gpa()); errdefer self.table.deinit(); - if (parent) |p| p.ref(); + parent.ref(); return self; } - pub fn destroy(self: *Decls) void { + pub fn destroy(self: *Decls, comp: *Compilation) void { self.table.deinit(); - self.table.allocator.destroy(self); + comp.gpa().destroy(self); } }; @@ -143,7 +188,7 @@ pub const Scope = struct { }; /// Creates a Block scope with 1 reference - pub fn create(comp: *Compilation, parent: ?*Scope) !*Block { + pub fn create(comp: *Compilation, parent: *Scope) !*Block { const self = try comp.gpa().create(Block{ .base = Scope{ .id = Id.Block, @@ -158,7 +203,7 @@ pub const Scope = struct { }); errdefer comp.gpa().destroy(self); - if (parent) |p| p.ref(); + parent.ref(); return self; } @@ -175,7 +220,7 @@ pub const Scope = struct { /// Creates a FnDef scope with 1 reference /// Must set the fn_val later - pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef { + pub fn create(comp: *Compilation, parent: *Scope) !*FnDef { const self = try comp.gpa().create(FnDef{ .base = Scope{ .id = Id.FnDef, @@ -185,7 +230,7 @@ pub const Scope = struct { .fn_val = undefined, }); - if (parent) |p| p.ref(); + parent.ref(); return self; } @@ -199,7 +244,7 @@ pub const Scope = struct { base: Scope, /// Creates a CompTime scope with 1 reference - pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime { + pub fn create(comp: *Compilation, parent: *Scope) !*CompTime { const self = try comp.gpa().create(CompTime{ .base = Scope{ .id = Id.CompTime, @@ -208,7 +253,7 @@ pub const Scope = struct { }, }); - if (parent) |p| p.ref(); + parent.ref(); return self; } @@ -230,7 +275,7 @@ pub const Scope = struct { /// Creates a Defer scope with 1 reference pub fn create( comp: *Compilation, - parent: ?*Scope, + parent: *Scope, kind: Kind, defer_expr_scope: *DeferExpr, ) !*Defer { @@ -247,7 +292,7 @@ pub const Scope = struct { defer_expr_scope.base.ref(); - if (parent) |p| p.ref(); + parent.ref(); return self; } @@ -263,7 +308,7 @@ pub const Scope = struct { reported_err: bool, /// Creates a DeferExpr scope with 1 reference - pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr { + pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr { const self = try comp.gpa().create(DeferExpr{ .base = Scope{ .id = Id.DeferExpr, @@ -275,7 +320,7 @@ pub const Scope = struct { }); errdefer comp.gpa().destroy(self); - if (parent) |p| p.ref(); + parent.ref(); return self; } diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index e518cd4173..b8563b00b8 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -39,6 +39,11 @@ pub const Value = struct { return base; } + pub fn cast(base: *Value, comptime T: type) ?*T { + if (base.id != @field(Id, @typeName(T))) return null; + return @fieldParentPtr(T, "base", base); + } + pub fn dump(base: *const Value) void { std.debug.warn("{}", @tagName(base.id)); } -- cgit v1.2.3 From 0a880d5e6034ab35778e4f4db33a385b2a5ad7cc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Jul 2018 18:05:01 -0400 Subject: fix generation of error defers for fns inside fns closes #878 --- src/ir.cpp | 84 ++++++++++++++++++++++++++++++++++++---------------- test/cases/defer.zig | 15 ++++++++++ 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 96ade9d392..fe5fb77085 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2961,16 +2961,34 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; - while (inner_scope != outer_scope) { - assert(inner_scope); - if (inner_scope->id == ScopeIdDefer) { - AstNode *defer_node = inner_scope->source_node; - assert(defer_node->type == NodeTypeDefer); - ReturnKind defer_kind = defer_node->data.defer.kind; - results[defer_kind] += 1; + Scope *scope = inner_scope; + while (scope != outer_scope) { + assert(scope); + switch (scope->id) { + case ScopeIdDefer: { + AstNode *defer_node = scope->source_node; + assert(defer_node->type == NodeTypeDefer); + ReturnKind defer_kind = defer_node->data.defer.kind; + results[defer_kind] += 1; + scope = scope->parent; + continue; + } + case ScopeIdDecls: + case ScopeIdFnDef: + return; + case ScopeIdBlock: + case ScopeIdVarDecl: + case ScopeIdLoop: + case ScopeIdSuspend: + case ScopeIdCompTime: + scope = scope->parent; + continue; + case ScopeIdDeferExpr: + case ScopeIdCImport: + case ScopeIdCoroPrelude: + zig_unreachable(); } - inner_scope = inner_scope->parent; } } @@ -2986,27 +3004,43 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o if (!scope) return is_noreturn; - if (scope->id == ScopeIdDefer) { - AstNode *defer_node = scope->source_node; - assert(defer_node->type == NodeTypeDefer); - ReturnKind defer_kind = defer_node->data.defer.kind; - if (defer_kind == ReturnKindUnconditional || - (gen_error_defers && defer_kind == ReturnKindError)) - { - AstNode *defer_expr_node = defer_node->data.defer.expr; - Scope *defer_expr_scope = defer_node->data.defer.expr_scope; - IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); - if (defer_expr_value != irb->codegen->invalid_instruction) { - if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) { - is_noreturn = true; - } else { - ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); + switch (scope->id) { + case ScopeIdDefer: { + AstNode *defer_node = scope->source_node; + assert(defer_node->type == NodeTypeDefer); + ReturnKind defer_kind = defer_node->data.defer.kind; + if (defer_kind == ReturnKindUnconditional || + (gen_error_defers && defer_kind == ReturnKindError)) + { + AstNode *defer_expr_node = defer_node->data.defer.expr; + Scope *defer_expr_scope = defer_node->data.defer.expr_scope; + IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); + if (defer_expr_value != irb->codegen->invalid_instruction) { + if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) { + is_noreturn = true; + } else { + ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); + } } } + scope = scope->parent; + continue; } - + case ScopeIdDecls: + case ScopeIdFnDef: + return is_noreturn; + case ScopeIdBlock: + case ScopeIdVarDecl: + case ScopeIdLoop: + case ScopeIdSuspend: + case ScopeIdCompTime: + scope = scope->parent; + continue; + case ScopeIdDeferExpr: + case ScopeIdCImport: + case ScopeIdCoroPrelude: + zig_unreachable(); } - scope = scope->parent; } return is_noreturn; } diff --git a/test/cases/defer.zig b/test/cases/defer.zig index d2b00d1f91..7d4d1bc3d8 100644 --- a/test/cases/defer.zig +++ b/test/cases/defer.zig @@ -61,3 +61,18 @@ test "defer and labeled break" { assert(i == 1); } + +test "errdefer does not apply to fn inside fn" { + if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad); +} + +fn testNestedFnErrDefer() error!void { + var a: i32 = 0; + errdefer a += 1; + const S = struct { + fn baz() error { + return error.Bad; + } + }; + return S.baz(); +} -- cgit v1.2.3 From d9fc14975232c0aa09e6498763f60515100770b9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Jul 2018 23:52:44 -0400 Subject: relative path to cwd in compile errors --- src-self-hosted/compilation.zig | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index b11a3408cd..acee7c3da5 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -40,6 +40,8 @@ pub const EventLoopLocal = struct { native_libc: event.Future(LibCInstallation), + cwd: []const u8, + var lazy_init_targets = std.lazyInit(void); fn init(loop: *event.Loop) !EventLoopLocal { @@ -51,16 +53,22 @@ pub const EventLoopLocal = struct { var seed_bytes: [@sizeOf(u64)]u8 = undefined; try std.os.getRandomBytes(seed_bytes[0..]); const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big); + + const cwd = try os.getCwd(loop.allocator); + errdefer loop.allocator.free(cwd); + return EventLoopLocal{ .loop = loop, .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), .native_libc = event.Future(LibCInstallation).init(loop), + .cwd = cwd, }; } /// Must be called only after EventLoop.run completes. fn deinit(self: *EventLoopLocal) void { + self.loop.allocator.free(self.cwd); while (self.llvm_handle_pool.pop()) |node| { c.LLVMContextDispose(node.data); self.loop.allocator.destroy(node); @@ -867,13 +875,16 @@ pub const Compilation = struct { span: Span, text: []u8, ) !void { - const msg = try self.loop.allocator.create(errmsg.Msg{ - .path = root.realpath, + const relpath = try os.path.relative(self.gpa(), self.event_loop_local.cwd, root.realpath); + errdefer self.gpa().free(relpath); + + const msg = try self.gpa().create(errmsg.Msg{ + .path = if (relpath.len < root.realpath.len) relpath else root.realpath, .text = text, .span = span, .tree = &root.tree, }); - errdefer self.loop.allocator.destroy(msg); + errdefer self.gpa().destroy(msg); const compile_errors = await (async self.compile_errors.acquire() catch unreachable); defer compile_errors.release(); -- cgit v1.2.3 From 33fbd8c1d333922efa696b5b9384859a2664f8de Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Jul 2018 00:13:48 -0400 Subject: self-hosted: convert some stuff to async/await --- src-self-hosted/compilation.zig | 9 +++++++-- src-self-hosted/ir.zig | 44 +++++++++++++++++++++-------------------- src-self-hosted/type.zig | 5 +++-- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index acee7c3da5..46cbf14141 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -478,7 +478,7 @@ pub const Compilation = struct { } /// it does ref the result because it could be an arbitrary integer size - pub fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type { + pub async fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type { if (name.len >= 2) { switch (name[0]) { 'i', 'u' => blk: { @@ -492,7 +492,12 @@ pub const Compilation = struct { error.Overflow => return error.Overflow, error.InvalidCharacter => unreachable, // we just checked the characters above }; - @panic("get int type - need to make everything async"); + const int_type = try await (async Type.Int.get(comp, Type.Int.Key{ + .bit_count = bit_count, + .is_signed = is_signed, + }) catch unreachable); + errdefer int_type.base.base.deref(); + return &int_type.base; }, else => {}, } diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index bdfccd085a..259c0a99f4 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -708,7 +708,7 @@ pub const Builder = struct { self.current_basic_block = basic_block; } - pub fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst { + pub async fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst { switch (node.id) { ast.Node.Id.Root => unreachable, ast.Node.Id.Use => unreachable, @@ -724,7 +724,7 @@ pub const Builder = struct { ast.Node.Id.If => return error.Unimplemented, ast.Node.Id.ControlFlowExpression => { const control_flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", node); - return irb.genControlFlowExpr(control_flow_expr, scope, lval); + return await (async irb.genControlFlowExpr(control_flow_expr, scope, lval) catch unreachable); }, ast.Node.Id.Suspend => return error.Unimplemented, ast.Node.Id.VarType => return error.Unimplemented, @@ -746,11 +746,11 @@ pub const Builder = struct { ast.Node.Id.Unreachable => return error.Unimplemented, ast.Node.Id.Identifier => { const identifier = @fieldParentPtr(ast.Node.Identifier, "base", node); - return irb.genIdentifier(identifier, scope, lval); + return await (async irb.genIdentifier(identifier, scope, lval) catch unreachable); }, ast.Node.Id.GroupedExpression => { const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node); - return irb.genNode(grouped_expr.expr, scope, lval); + return await (async irb.genNode(grouped_expr.expr, scope, lval) catch unreachable); }, ast.Node.Id.BuiltinCall => return error.Unimplemented, ast.Node.Id.ErrorSetDecl => return error.Unimplemented, @@ -759,7 +759,8 @@ pub const Builder = struct { ast.Node.Id.Comptime => return error.Unimplemented, ast.Node.Id.Block => { const block = @fieldParentPtr(ast.Node.Block, "base", node); - return irb.lvalWrap(scope, try irb.genBlock(block, scope), lval); + const inst = try await (async irb.genBlock(block, scope) catch unreachable); + return irb.lvalWrap(scope, inst, lval); }, ast.Node.Id.DocComment => return error.Unimplemented, ast.Node.Id.SwitchCase => return error.Unimplemented, @@ -838,7 +839,7 @@ pub const Builder = struct { return inst; } - pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst { + pub async fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst { const block_scope = try Scope.Block.create(irb.comp, parent_scope); const outer_block_scope = &block_scope.base; @@ -886,7 +887,7 @@ pub const Builder = struct { child_scope = &defer_child_scope.base; continue; } - const statement_value = try irb.genNode(statement_node, child_scope, LVal.None); + const statement_value = try await (async irb.genNode(statement_node, child_scope, LVal.None) catch unreachable); is_continuation_unreachable = statement_value.isNoReturn(); if (is_continuation_unreachable) { @@ -926,7 +927,7 @@ pub const Builder = struct { try block_scope.incoming_values.append( try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true), ); - _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit); + _ = try await (async irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); _ = try irb.buildGen(Inst.Br, parent_scope, Span.token(block.rbrace), Inst.Br.Params{ .dest_block = block_scope.end_block, @@ -941,11 +942,11 @@ pub const Builder = struct { }); } - _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit); + _ = try await (async irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true); } - pub fn genControlFlowExpr( + pub async fn genControlFlowExpr( irb: *Builder, control_flow_expr: *ast.Node.ControlFlowExpression, scope: *Scope, @@ -979,7 +980,7 @@ pub const Builder = struct { const outer_scope = irb.begin_scope.?; const return_value = if (control_flow_expr.rhs) |rhs| blk: { - break :blk try irb.genNode(rhs, scope, LVal.None); + break :blk try await (async irb.genNode(rhs, scope, LVal.None) catch unreachable); } else blk: { break :blk try irb.buildConstVoid(scope, src_span, true); }; @@ -990,7 +991,7 @@ pub const Builder = struct { const err_block = try irb.createBasicBlock(scope, c"ErrRetErr"); const ok_block = try irb.createBasicBlock(scope, c"ErrRetOk"); if (!have_err_defers) { - _ = try irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit); + _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); } const is_err = try irb.build( @@ -1013,7 +1014,7 @@ pub const Builder = struct { try irb.setCursorAtEndAndAppendBlock(err_block); if (have_err_defers) { - _ = try irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ErrorExit); + _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ErrorExit) catch unreachable); } if (irb.comp.have_err_ret_tracing and !irb.isCompTime(scope)) { _ = try irb.build(Inst.SaveErrRetAddr, scope, src_span, Inst.SaveErrRetAddr.Params{}); @@ -1025,7 +1026,7 @@ pub const Builder = struct { try irb.setCursorAtEndAndAppendBlock(ok_block); if (have_err_defers) { - _ = try irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit); + _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); } _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{ .dest_block = ret_stmt_block, @@ -1035,14 +1036,14 @@ pub const Builder = struct { try irb.setCursorAtEndAndAppendBlock(ret_stmt_block); return irb.genAsyncReturn(scope, src_span, return_value, false); } else { - _ = try irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit); + _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); return irb.genAsyncReturn(scope, src_span, return_value, false); } }, } } - pub fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst { + pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst { const src_span = Span.token(identifier.token); const name = irb.root_scope.tree.tokenSlice(identifier.token); @@ -1055,7 +1056,7 @@ pub const Builder = struct { // return &const_instruction->base; //} - if (irb.comp.getPrimitiveType(name)) |result| { + if (await (async irb.comp.getPrimitiveType(name) catch unreachable)) |result| { if (result) |primitive_type| { defer primitive_type.base.deref(irb.comp); switch (lval) { @@ -1068,6 +1069,7 @@ pub const Builder = struct { try irb.comp.addCompileError(irb.root_scope, src_span, "integer too large"); return error.SemanticAnalysisFailed; }, + error.OutOfMemory => return error.OutOfMemory, } //TypeTableEntry *primitive_type = get_primitive_type(irb->codegen, variable_name); //if (primitive_type != nullptr) { @@ -1138,7 +1140,7 @@ pub const Builder = struct { return result; } - fn genDefersForBlock( + async fn genDefersForBlock( irb: *Builder, inner_scope: *Scope, outer_scope: *Scope, @@ -1156,11 +1158,11 @@ pub const Builder = struct { }; if (generate) { const defer_expr_scope = defer_scope.defer_expr_scope; - const instruction = try irb.genNode( + const instruction = try await (async irb.genNode( defer_expr_scope.expr_node, &defer_expr_scope.base, LVal.None, - ); + ) catch unreachable); if (instruction.isNoReturn()) { is_noreturn = true; } else { @@ -1909,7 +1911,7 @@ pub async fn gen( entry_block.ref(); // Entry block gets a reference because we enter it to begin. try irb.setCursorAtEndAndAppendBlock(entry_block); - const result = try irb.genNode(body_node, scope, LVal.None); + const result = try await (async irb.genNode(body_node, scope, LVal.None) catch unreachable); if (!result.isNoReturn()) { // no need for save_err_ret_addr because this cannot return error _ = try irb.genAsyncReturn(scope, Span.token(body_node.lastToken()), result, true); diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index da290ece13..fa99586383 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -324,6 +324,7 @@ pub const Type = struct { defer held.release(); if (held.value.get(&key)) |entry| { + entry.value.base.base.ref(); return entry.value; } } @@ -336,7 +337,7 @@ pub const Type = struct { errdefer comp.gpa().destroy(self); const u_or_i = "ui"[@boolToInt(key.is_signed)]; - const name = std.fmt.allocPrint(comp.gpa(), "{c}{}", u_or_i, key.bit_count); + const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", u_or_i, key.bit_count); errdefer comp.gpa().free(name); self.base.init(comp, Id.Int, name); @@ -345,7 +346,7 @@ pub const Type = struct { const held = await (async comp.int_type_table.acquire() catch unreachable); defer held.release(); - held.value.put(&self.key, self); + _ = try held.value.put(&self.key, self); } return self; } -- cgit v1.2.3 From f5a67dba08b81c7109c52f6ad957aefdd646b56e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Jul 2018 01:46:49 -0400 Subject: self-hosted: implicit cast comptime ints to other ints we now have successful exit codes from main linking against libc --- src-self-hosted/ir.zig | 39 ++++++++++++++++++++++++++++++++- src-self-hosted/value.zig | 49 ++++++++++++++++++++++++++++++++++++++++++ std/math/big/int.zig | 18 +++++++++++++++- test/stage2/compile_errors.zig | 6 ++++++ 4 files changed, 110 insertions(+), 2 deletions(-) diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 259c0a99f4..04023980e6 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -139,7 +139,15 @@ pub const Inst = struct { } } + fn copyVal(base: *Inst, comp: *Compilation) !*Value { + if (base.parent.?.ref_count == 0) { + return base.val.KnownValue.derefAndCopy(comp); + } + return base.val.KnownValue.copy(comp); + } + fn getAsParam(param: *Inst) !*Inst { + param.ref_count -= 1; const child = param.child orelse return error.SemanticAnalysisFailed; switch (child.val) { IrVal.Unknown => return error.SemanticAnalysisFailed, @@ -1715,8 +1723,37 @@ const Analyze = struct { // } //} + // cast from comptime-known integer to another integer where the value fits + if (target.isCompTime() and (from_type.id == Type.Id.Int or from_type.id == Type.Id.ComptimeInt)) cast: { + const target_val = target.val.KnownValue; + const from_int = &target_val.cast(Value.Int).?.big_int; + const fits = fits: { + if (dest_type.cast(Type.ComptimeInt)) |ctint| { + break :fits true; + } + if (dest_type.cast(Type.Int)) |int| { + break :fits (from_int.positive or from_int.eqZero() or int.key.is_signed) and + int.key.bit_count >= from_int.bitcount(); + } + break :cast; + }; + if (!fits) { + try ira.addCompileError( + source_instr.span, + "integer value '{}' cannot be stored in type '{}'", + from_int, + dest_type.name, + ); + return error.SemanticAnalysisFailed; + } + + const new_val = try target.copyVal(ira.irb.comp); + new_val.setType(dest_type, ira.irb.comp); + return ira.irb.buildConstValue(source_instr.scope, source_instr.span, new_val); + } + // cast from number literal to another type - //// cast from number literal to *const integer + // cast from number literal to *const integer //if (actual_type->id == TypeTableEntryIdComptimeFloat || // actual_type->id == TypeTableEntryIdComptimeInt) //{ diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index b8563b00b8..16c1488838 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -5,6 +5,7 @@ const Compilation = @import("compilation.zig").Compilation; const ObjectFile = @import("codegen.zig").ObjectFile; const llvm = @import("llvm.zig"); const Buffer = std.Buffer; +const assert = std.debug.assert; /// Values are ref-counted, heap-allocated, and copy-on-write /// If there is only 1 ref then write need not copy @@ -34,6 +35,12 @@ pub const Value = struct { } } + pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void { + base.typeof.base.deref(comp); + new_type.base.ref(); + base.typeof = new_type; + } + pub fn getRef(base: *Value) *Value { base.ref(); return base; @@ -60,6 +67,28 @@ pub const Value = struct { } } + pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) { + if (self.ref_count.get() == 1) { + // ( ͡° ͜ʖ ͡°) + return self; + } + + assert(self.ref_count.decr() != 1); + return self.copy(comp); + } + + pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) { + switch (base.id) { + Id.Type => unreachable, + Id.Fn => unreachable, + Id.Void => unreachable, + Id.Bool => unreachable, + Id.NoReturn => unreachable, + Id.Ptr => unreachable, + Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base, + } + } + pub const Id = enum { Type, Fn, @@ -256,6 +285,26 @@ pub const Value = struct { } } + pub fn copy(old: *Int, comp: *Compilation) !*Int { + old.base.typeof.base.ref(); + errdefer old.base.typeof.base.deref(comp); + + const new = try comp.gpa().create(Value.Int{ + .base = Value{ + .id = Value.Id.Int, + .typeof = old.base.typeof, + .ref_count = std.atomic.Int(usize).init(1), + }, + .big_int = undefined, + }); + errdefer comp.gpa().destroy(new); + + new.big_int = try old.big_int.clone(); + errdefer new.big_int.deinit(); + + return new; + } + pub fn destroy(self: *Int, comp: *Compilation) void { self.big_int.deinit(); comp.gpa().destroy(self); diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 8b9e89254c..9af033bd08 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -118,7 +118,7 @@ pub const Int = struct { fn bitcount(self: Int) usize { const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); - return usize(@boolToInt(!self.positive)) + u_bit_count; + return @boolToInt(!self.positive) + u_bit_count; } pub fn sizeInBase(self: Int, base: usize) usize { @@ -275,6 +275,7 @@ pub const Int = struct { self.positive = positive; } + /// TODO make this call format instead of the other way around pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 { if (base < 2 or base > 16) { return error.InvalidBase; @@ -357,6 +358,21 @@ pub const Int = struct { return s; } + /// for the std lib format function + /// TODO make this non-allocating + pub fn format( + self: Int, + comptime fmt: []const u8, + context: var, + comptime FmtError: type, + output: fn (@typeOf(context), []const u8) FmtError!void, + ) FmtError!void { + // TODO look at fmt and support other bases + const str = self.toString(self.allocator, 10) catch @panic("TODO make this non allocating"); + defer self.allocator.free(str); + return output(context, str); + } + // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively. pub fn cmpAbs(a: Int, b: Int) i8 { if (a.len < b.len) { diff --git a/test/stage2/compile_errors.zig b/test/stage2/compile_errors.zig index d360c07b28..2cecd78653 100644 --- a/test/stage2/compile_errors.zig +++ b/test/stage2/compile_errors.zig @@ -21,4 +21,10 @@ pub fn addCases(ctx: *TestContext) !void { \\ defer return; \\} , "1.zig", 2, 11, "cannot return from defer expression"); + + try ctx.testCompileError( + \\export fn entry() c_int { + \\ return 36893488147419103232; + \\} + , "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'"); } -- cgit v1.2.3 From 1f4c7d5ebfd4ae88d57b6c923d9ef4d2154e193d Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Fri, 20 Jul 2018 23:05:53 +0200 Subject: Fixed windows getPos --- std/os/file.zig | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/std/os/file.zig b/std/os/file.zig index c160e90e0e..26fd9ca514 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -242,7 +242,7 @@ pub const File = struct { }, Os.windows => { var pos: windows.LARGE_INTEGER = undefined; - if (windows.SetFilePointerEx(self.handle, 0, *pos, windows.FILE_CURRENT) == 0) { + if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_PARAMETER => error.BadFd, @@ -251,13 +251,7 @@ pub const File = struct { } assert(pos >= 0); - if (@sizeOf(@typeOf(pos)) > @sizeOf(usize)) { - if (pos > @maxValue(usize)) { - return error.FilePosLargerThanPointerRange; - } - } - - return usize(pos); + return math.cast(usize, pos) catch error.FilePosLargerThanPointerRange; }, else => @compileError("unsupported OS"), } -- cgit v1.2.3 From 58c5f94a99a78346286065bbf390e4c30be1b707 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Jul 2018 23:37:37 -0400 Subject: self-hosted: share C++ code for finding libc on windows --- CMakeLists.txt | 7 + src-self-hosted/c.zig | 1 + src-self-hosted/libc_installation.zig | 25 ++- src/analyze.cpp | 7 +- src/os.cpp | 248 +----------------------- src/os.hpp | 10 +- src/windows_sdk.cpp | 346 ++++++++++++++++++++++++++++++++++ src/windows_sdk.h | 45 +++++ std/os/windows/advapi32.zig | 30 +++ std/os/windows/index.zig | 192 +------------------ std/os/windows/kernel32.zig | 162 ++++++++++++++++ std/os/windows/ole32.zig | 18 ++ std/os/windows/shell32.zig | 4 + std/os/windows/shlwapi.zig | 4 + std/os/windows/user32.zig | 4 + 15 files changed, 658 insertions(+), 445 deletions(-) create mode 100644 src/windows_sdk.cpp create mode 100644 src/windows_sdk.h create mode 100644 std/os/windows/advapi32.zig create mode 100644 std/os/windows/kernel32.zig create mode 100644 std/os/windows/ole32.zig create mode 100644 std/os/windows/shell32.zig create mode 100644 std/os/windows/shlwapi.zig create mode 100644 std/os/windows/user32.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 30d7bb4856..20755cfc1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -426,6 +426,7 @@ set(ZIG_SOURCES ) set(ZIG_CPP_SOURCES "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" + "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp" ) set(ZIG_STD_FILES @@ -567,8 +568,14 @@ set(ZIG_STD_FILES "os/linux/x86_64.zig" "os/path.zig" "os/time.zig" + "os/windows/advapi32.zig" "os/windows/error.zig" "os/windows/index.zig" + "os/windows/kernel32.zig" + "os/windows/ole32.zig" + "os/windows/shell32.zig" + "os/windows/shlwapi.zig" + "os/windows/user32.zig" "os/windows/util.zig" "os/zen.zig" "rand/index.zig" diff --git a/src-self-hosted/c.zig b/src-self-hosted/c.zig index 3912462985..778d851240 100644 --- a/src-self-hosted/c.zig +++ b/src-self-hosted/c.zig @@ -4,4 +4,5 @@ pub use @cImport({ @cInclude("inttypes.h"); @cInclude("config.h"); @cInclude("zig_llvm.h"); + @cInclude("windows_sdk.h"); }); diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 8444c47310..5a9b7561fa 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const event = std.event; const Target = @import("target.zig").Target; +const c = @import("c.zig"); /// See the render function implementation for documentation of the fields. pub const LibCInstallation = struct { @@ -122,7 +123,7 @@ pub const LibCInstallation = struct { \\# Only needed when targeting Windows. \\kernel32_lib_dir={} \\ - \\# The full path to the dynamic linker. + \\# The full path to the dynamic linker, on the target system. \\# Only needed when targeting Linux. \\dynamic_linker_path={} \\ @@ -143,10 +144,24 @@ pub const LibCInstallation = struct { errdefer group.cancelAll(); switch (builtin.os) { builtin.Os.windows => { - try group.call(findNativeIncludeDirWindows, self, loop); - try group.call(findNativeLibDirWindows, self, loop); - try group.call(findNativeMsvcLibDir, self, loop); - try group.call(findNativeKernel32LibDir, self, loop); + var sdk: *c.ZigWindowsSDK = undefined; + switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) { + c.ZigFindWindowsSdkError.None => { + defer c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk)); + + errdefer if (self.msvc_lib_dir) |s| loop.allocator.free(s); + if (sdk.msvc_lib_dir_ptr) |ptr| { + self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]); + } + //try group.call(findNativeIncludeDirWindows, self, loop); + //try group.call(findNativeLibDirWindows, self, loop); + //try group.call(findNativeMsvcLibDir, self, loop); + //try group.call(findNativeKernel32LibDir, self, loop); + }, + c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory, + c.ZigFindWindowsSdkError.NotFound => return error.NotFound, + c.ZigFindWindowsSdkError.PathTooLong => return error.NotFound, + } }, builtin.Os.linux => { try group.call(findNativeIncludeDirLinux, self, loop); diff --git a/src/analyze.cpp b/src/analyze.cpp index 06d611f80d..6bbe5f6037 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4379,7 +4379,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { static ZigWindowsSDK *get_windows_sdk(CodeGen *g) { if (g->win_sdk == nullptr) { - if (os_find_windows_sdk(&g->win_sdk)) { + if (zig_find_windows_sdk(&g->win_sdk)) { fprintf(stderr, "unable to determine windows sdk path\n"); exit(1); } @@ -4499,12 +4499,11 @@ void find_libc_lib_path(CodeGen *g) { ZigWindowsSDK *sdk = get_windows_sdk(g); if (g->msvc_lib_dir == nullptr) { - Buf* vc_lib_dir = buf_alloc(); - if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { + if (sdk->msvc_lib_dir_ptr == nullptr) { fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir"); exit(1); } - g->msvc_lib_dir = vc_lib_dir; + g->msvc_lib_dir = buf_create_from_mem(sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len); } if (g->libc_lib_dir == nullptr) { diff --git a/src/os.cpp b/src/os.cpp index d52295950d..91a591a7b6 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -26,7 +26,6 @@ #include #include #include -#include "windows_com.hpp" typedef SSIZE_T ssize_t; #else @@ -1115,249 +1114,10 @@ void os_stderr_set_color(TermColor color) { #endif } -int os_find_windows_sdk(ZigWindowsSDK **out_sdk) { -#if defined(ZIG_OS_WINDOWS) - ZigWindowsSDK *result_sdk = allocate(1); - buf_resize(&result_sdk->path10, 0); - buf_resize(&result_sdk->path81, 0); - - HKEY key; - HRESULT rc; - rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key); - if (rc != ERROR_SUCCESS) { - return ErrorFileNotFound; - } - - { - DWORD tmp_buf_len = MAX_PATH; - buf_resize(&result_sdk->path10, tmp_buf_len); - rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path10), &tmp_buf_len); - if (rc == ERROR_FILE_NOT_FOUND) { - buf_resize(&result_sdk->path10, 0); - } else { - buf_resize(&result_sdk->path10, tmp_buf_len); - } - } - { - DWORD tmp_buf_len = MAX_PATH; - buf_resize(&result_sdk->path81, tmp_buf_len); - rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path81), &tmp_buf_len); - if (rc == ERROR_FILE_NOT_FOUND) { - buf_resize(&result_sdk->path81, 0); - } else { - buf_resize(&result_sdk->path81, tmp_buf_len); - } - } - - if (buf_len(&result_sdk->path10) != 0) { - Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\*", buf_ptr(&result_sdk->path10)); - - // enumerate files in sdk path looking for latest version - WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd); - if (hFind == INVALID_HANDLE_VALUE) { - return ErrorFileNotFound; - } - int v0 = 0, v1 = 0, v2 = 0, v3 = 0; - bool found_version_dir = false; - for (;;) { - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - int c0 = 0, c1 = 0, c2 = 0, c3 = 0; - sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3); - if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) { - // Microsoft released 26624 as 10240 accidentally. - // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive - c2 = 26624; - } - if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) { - v0 = c0, v1 = c1, v2 = c2, v3 = c3; - buf_init_from_str(&result_sdk->version10, ffd.cFileName); - found_version_dir = true; - } - } - if (FindNextFile(hFind, &ffd) == 0) { - FindClose(hFind); - break; - } - } - if (!found_version_dir) { - buf_resize(&result_sdk->path10, 0); - } - } - - if (buf_len(&result_sdk->path81) != 0) { - Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\winv*", buf_ptr(&result_sdk->path81)); - - // enumerate files in sdk path looking for latest version - WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd); - if (hFind == INVALID_HANDLE_VALUE) { - return ErrorFileNotFound; - } - int v0 = 0, v1 = 0; - bool found_version_dir = false; - for (;;) { - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - int c0 = 0, c1 = 0; - sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1); - if ((c0 > v0) || (c1 > v1)) { - v0 = c0, v1 = c1; - buf_init_from_str(&result_sdk->version81, ffd.cFileName); - found_version_dir = true; - } - } - if (FindNextFile(hFind, &ffd) == 0) { - FindClose(hFind); - break; - } - } - if (!found_version_dir) { - buf_resize(&result_sdk->path81, 0); - } - } - - *out_sdk = result_sdk; - return 0; -#else - return ErrorFileNotFound; -#endif -} - -int os_get_win32_vcruntime_path(Buf* output_buf, ZigLLVM_ArchType platform_type) { -#if defined(ZIG_OS_WINDOWS) - buf_resize(output_buf, 0); - //COM Smart Pointerse requires explicit scope - { - HRESULT rc; - rc = CoInitializeEx(NULL, COINIT_MULTITHREADED); - if (rc != S_OK) { - goto com_done; - } - - //This COM class is installed when a VS2017 - ISetupConfigurationPtr setup_config; - rc = setup_config.CreateInstance(__uuidof(SetupConfiguration)); - if (rc != S_OK) { - goto com_done; - } - - IEnumSetupInstancesPtr all_instances; - rc = setup_config->EnumInstances(&all_instances); - if (rc != S_OK) { - goto com_done; - } - - ISetupInstance* curr_instance; - ULONG found_inst; - while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) { - BSTR bstr_inst_path; - rc = curr_instance->GetInstallationPath(&bstr_inst_path); - if (rc != S_OK) { - goto com_done; - } - //BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length - UINT bstr_path_len = *((UINT*)bstr_inst_path - 1); - ULONG tmp_path_len = bstr_path_len / 2 + 1; - char* conv_path = (char*)bstr_inst_path; - char *tmp_path = (char*)alloca(tmp_path_len); - memset(tmp_path, 0, tmp_path_len); - uint32_t c = 0; - for (uint32_t i = 0; i < bstr_path_len; i += 2) { - tmp_path[c] = conv_path[i]; - ++c; - assert(c != tmp_path_len); - } - - buf_append_str(output_buf, tmp_path); - buf_append_char(output_buf, '\\'); - - Buf* tmp_buf = buf_alloc(); - buf_append_buf(tmp_buf, output_buf); - buf_append_str(tmp_buf, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"); - FILE* tools_file = fopen(buf_ptr(tmp_buf), "r"); - if (!tools_file) { - goto com_done; - } - memset(tmp_path, 0, tmp_path_len); - fgets(tmp_path, tmp_path_len, tools_file); - strtok(tmp_path, " \r\n"); - fclose(tools_file); - buf_appendf(output_buf, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path); - switch (platform_type) { - case ZigLLVM_x86: - buf_append_str(output_buf, "x86\\"); - break; - case ZigLLVM_x86_64: - buf_append_str(output_buf, "x64\\"); - break; - case ZigLLVM_arm: - buf_append_str(output_buf, "arm\\"); - break; - default: - zig_panic("Attemped to use vcruntime for non-supported platform."); - } - buf_resize(tmp_buf, 0); - buf_append_buf(tmp_buf, output_buf); - buf_append_str(tmp_buf, "vcruntime.lib"); - - if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) { - return 0; - } - } - } - -com_done:; - HKEY key; - HRESULT rc; - rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key); - if (rc != ERROR_SUCCESS) { - return ErrorFileNotFound; - } - - DWORD dw_type = 0; - DWORD cb_data = 0; - rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data); - if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) { - return ErrorFileNotFound; - } - - Buf* tmp_buf = buf_alloc_fixed(cb_data); - RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)buf_ptr(tmp_buf), &cb_data); - //RegQueryValueExA returns the length of the string INCLUDING the null terminator - buf_resize(tmp_buf, cb_data-1); - buf_append_str(tmp_buf, "VC\\Lib\\"); - switch (platform_type) { - case ZigLLVM_x86: - //x86 is in the root of the Lib folder - break; - case ZigLLVM_x86_64: - buf_append_str(tmp_buf, "amd64\\"); - break; - case ZigLLVM_arm: - buf_append_str(tmp_buf, "arm\\"); - break; - default: - zig_panic("Attemped to use vcruntime for non-supported platform."); - } - - buf_append_buf(output_buf, tmp_buf); - buf_append_str(tmp_buf, "vcruntime.lib"); - - if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) { - return 0; - } else { - buf_resize(output_buf, 0); - return ErrorFileNotFound; - } -#else - return ErrorFileNotFound; -#endif -} - int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { #if defined(ZIG_OS_WINDOWS) buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); + buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr); switch (platform_type) { case ZigLLVM_x86: buf_append_str(output_buf, "x86\\"); @@ -1389,7 +1149,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) { #if defined(ZIG_OS_WINDOWS) buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); + buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr); if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) { return 0; } @@ -1406,7 +1166,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy #if defined(ZIG_OS_WINDOWS) { buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); + buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr); switch (platform_type) { case ZigLLVM_x86: buf_append_str(output_buf, "x86\\"); @@ -1429,7 +1189,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy } { buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path81), buf_ptr(&sdk->version81)); + buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr); switch (platform_type) { case ZigLLVM_x86: buf_append_str(output_buf, "x86\\"); diff --git a/src/os.hpp b/src/os.hpp index b94e98ec3d..cfe4e8f3a2 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -12,6 +12,7 @@ #include "buffer.hpp" #include "error.hpp" #include "zig_llvm.h" +#include "windows_sdk.h" #include #include @@ -79,15 +80,6 @@ bool os_is_sep(uint8_t c); int os_self_exe_path(Buf *out_path); -struct ZigWindowsSDK { - Buf path10; - Buf version10; - Buf path81; - Buf version81; -}; - -int os_find_windows_sdk(ZigWindowsSDK **out_sdk); -int os_get_win32_vcruntime_path(Buf *output_buf, ZigLLVM_ArchType platform_type); int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf); int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); diff --git a/src/windows_sdk.cpp b/src/windows_sdk.cpp new file mode 100644 index 0000000000..059bdee4e9 --- /dev/null +++ b/src/windows_sdk.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2018 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "windows_sdk.h" + +#if defined(_WIN32) + +#include "windows_com.hpp" +#include +#include + +struct ZigWindowsSDKPrivate { + ZigWindowsSDK base; +}; + +enum NativeArch { + NativeArchArm, + NativeArchi386, + NativeArchx86_64, +}; + +#if defined(_M_ARM) || defined(__arm_) +static const NativeArch native_arch = NativeArchArm; +#endif +#if defined(_M_IX86) || defined(__i386__) +static const NativeArch native_arch = NativeArchi386; +#endif +#if defined(_M_X64) || defined(__x86_64__) +static const NativeArch native_arch = NativeArchx86_64; +#endif + +void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) { + if (sdk == nullptr) { + return; + } + free((void*)sdk->path10_ptr); + free((void*)sdk->version10_ptr); + free((void*)sdk->path81_ptr); + free((void*)sdk->version81_ptr); + free((void*)sdk->msvc_lib_dir_ptr); +} + +static ZigFindWindowsSdkError find_msvc_lib_dir(ZigWindowsSDKPrivate *priv) { + //COM Smart Pointers requires explicit scope + { + HRESULT rc = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (rc != S_OK && rc != S_FALSE) { + goto com_done; + } + + //This COM class is installed when a VS2017 + ISetupConfigurationPtr setup_config; + rc = setup_config.CreateInstance(__uuidof(SetupConfiguration)); + if (rc != S_OK) { + goto com_done; + } + + IEnumSetupInstancesPtr all_instances; + rc = setup_config->EnumInstances(&all_instances); + if (rc != S_OK) { + goto com_done; + } + + ISetupInstance* curr_instance; + ULONG found_inst; + while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) { + BSTR bstr_inst_path; + rc = curr_instance->GetInstallationPath(&bstr_inst_path); + if (rc != S_OK) { + goto com_done; + } + //BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length + //TODO call an actual function to do this + UINT bstr_path_len = *((UINT*)bstr_inst_path - 1); + ULONG tmp_path_len = bstr_path_len / 2 + 1; + char* conv_path = (char*)bstr_inst_path; + // TODO don't use alloca + char *tmp_path = (char*)alloca(tmp_path_len); + memset(tmp_path, 0, tmp_path_len); + uint32_t c = 0; + for (uint32_t i = 0; i < bstr_path_len; i += 2) { + tmp_path[c] = conv_path[i]; + ++c; + assert(c != tmp_path_len); + } + char output_path[4096]; + output_path[0] = 0; + char *out_append_ptr = output_path; + + out_append_ptr += sprintf(out_append_ptr, "%s\\", tmp_path); + + char tmp_buf[4096]; + sprintf(tmp_buf, "%s%s", output_path, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"); + FILE* tools_file = fopen(tmp_buf, "rb"); + if (!tools_file) { + goto com_done; + } + memset(tmp_path, 0, tmp_path_len); + fgets(tmp_path, tmp_path_len, tools_file); + strtok(tmp_path, " \r\n"); + fclose(tools_file); + out_append_ptr += sprintf(out_append_ptr, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path); + switch (native_arch) { + case NativeArchi386: + out_append_ptr += sprintf(out_append_ptr, "x86\\"); + break; + case NativeArchx86_64: + out_append_ptr += sprintf(out_append_ptr, "x64\\"); + break; + case NativeArchArm: + out_append_ptr += sprintf(out_append_ptr, "arm\\"); + break; + } + sprintf(tmp_buf, "%s%s", output_path, "vcruntime.lib"); + + if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) { + priv->base.msvc_lib_dir_ptr = strdup(output_path); + if (priv->base.msvc_lib_dir_ptr == nullptr) { + return ZigFindWindowsSdkErrorOutOfMemory; + } + priv->base.msvc_lib_dir_len = strlen(priv->base.msvc_lib_dir_ptr); + return ZigFindWindowsSdkErrorNone; + } + } + } + +com_done:; + HKEY key; + HRESULT rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, + KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key); + if (rc != ERROR_SUCCESS) { + return ZigFindWindowsSdkErrorNotFound; + } + + DWORD dw_type = 0; + DWORD cb_data = 0; + rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data); + if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) { + return ZigFindWindowsSdkErrorNotFound; + } + + char tmp_buf[4096]; + + RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)tmp_buf, &cb_data); + // RegQueryValueExA returns the length of the string INCLUDING the null terminator + char *tmp_buf_append_ptr = tmp_buf + (cb_data - 1); + tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "VC\\Lib\\"); + switch (native_arch) { + case NativeArchi386: + //x86 is in the root of the Lib folder + break; + case NativeArchx86_64: + tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "amd64\\"); + break; + case NativeArchArm: + tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm\\"); + break; + } + + char *output_path = strdup(tmp_buf); + if (output_path == nullptr) { + return ZigFindWindowsSdkErrorOutOfMemory; + } + + tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "vcruntime.lib"); + + if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) { + priv->base.msvc_lib_dir_ptr = output_path; + priv->base.msvc_lib_dir_len = strlen(output_path); + return ZigFindWindowsSdkErrorNone; + } else { + free(output_path); + return ZigFindWindowsSdkErrorNotFound; + } +} + +static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) { + if (priv->base.path10_ptr == nullptr) + return ZigFindWindowsSdkErrorNone; + + char sdk_lib_dir[4096]; + int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\*", priv->base.path10_ptr); + if (n < 0 || n >= 4096) { + return ZigFindWindowsSdkErrorPathTooLong; + } + + // enumerate files in sdk path looking for latest version + WIN32_FIND_DATA ffd; + HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd); + if (hFind == INVALID_HANDLE_VALUE) { + return ZigFindWindowsSdkErrorNotFound; + } + int v0 = 0, v1 = 0, v2 = 0, v3 = 0; + for (;;) { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + int c0 = 0, c1 = 0, c2 = 0, c3 = 0; + sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3); + if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) { + // Microsoft released 26624 as 10240 accidentally. + // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive + c2 = 26624; + } + if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) { + v0 = c0, v1 = c1, v2 = c2, v3 = c3; + free((void*)priv->base.version10_ptr); + priv->base.version10_ptr = strdup(ffd.cFileName); + if (priv->base.version10_ptr == nullptr) { + FindClose(hFind); + return ZigFindWindowsSdkErrorOutOfMemory; + } + } + } + if (FindNextFile(hFind, &ffd) == 0) { + FindClose(hFind); + break; + } + } + priv->base.version10_len = strlen(priv->base.version10_ptr); + return ZigFindWindowsSdkErrorNone; +} + +static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) { + if (priv->base.path81_ptr == nullptr) + return ZigFindWindowsSdkErrorNone; + + char sdk_lib_dir[4096]; + int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\winv*", priv->base.path81_ptr); + if (n < 0 || n >= 4096) { + return ZigFindWindowsSdkErrorPathTooLong; + } + + // enumerate files in sdk path looking for latest version + WIN32_FIND_DATA ffd; + HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd); + if (hFind == INVALID_HANDLE_VALUE) { + return ZigFindWindowsSdkErrorNotFound; + } + int v0 = 0, v1 = 0; + for (;;) { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + int c0 = 0, c1 = 0; + sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1); + if ((c0 > v0) || (c1 > v1)) { + v0 = c0, v1 = c1; + free((void*)priv->base.version81_ptr); + priv->base.version81_ptr = strdup(ffd.cFileName); + if (priv->base.version81_ptr == nullptr) { + FindClose(hFind); + return ZigFindWindowsSdkErrorOutOfMemory; + } + } + } + if (FindNextFile(hFind, &ffd) == 0) { + FindClose(hFind); + break; + } + } + priv->base.version81_len = strlen(priv->base.version81_ptr); + return ZigFindWindowsSdkErrorNone; +} + +ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) { + ZigWindowsSDKPrivate *priv = (ZigWindowsSDKPrivate*)calloc(1, sizeof(ZigWindowsSDKPrivate)); + if (priv == nullptr) { + return ZigFindWindowsSdkErrorOutOfMemory; + } + + HKEY key; + HRESULT rc; + rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, + KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key); + if (rc != ERROR_SUCCESS) { + zig_free_windows_sdk(&priv->base); + return ZigFindWindowsSdkErrorNotFound; + } + + { + DWORD tmp_buf_len = MAX_PATH; + priv->base.path10_ptr = (const char *)calloc(tmp_buf_len, 1); + if (priv->base.path10_ptr == nullptr) { + zig_free_windows_sdk(&priv->base); + return ZigFindWindowsSdkErrorOutOfMemory; + } + rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len); + if (rc == ERROR_SUCCESS) { + priv->base.path10_len = tmp_buf_len; + } else { + free((void*)priv->base.path10_ptr); + priv->base.path10_ptr = nullptr; + } + } + { + DWORD tmp_buf_len = MAX_PATH; + priv->base.path81_ptr = (const char *)calloc(tmp_buf_len, 1); + if (priv->base.path81_ptr == nullptr) { + zig_free_windows_sdk(&priv->base); + return ZigFindWindowsSdkErrorOutOfMemory; + } + rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len); + if (rc == ERROR_SUCCESS) { + priv->base.path81_len = tmp_buf_len; + } else { + free((void*)priv->base.path81_ptr); + priv->base.path81_ptr = nullptr; + } + } + + { + ZigFindWindowsSdkError err = find_10_version(priv); + if (err == ZigFindWindowsSdkErrorOutOfMemory) { + zig_free_windows_sdk(&priv->base); + return err; + } + } + { + ZigFindWindowsSdkError err = find_81_version(priv); + if (err == ZigFindWindowsSdkErrorOutOfMemory) { + zig_free_windows_sdk(&priv->base); + return err; + } + } + + { + ZigFindWindowsSdkError err = find_msvc_lib_dir(priv); + if (err == ZigFindWindowsSdkErrorOutOfMemory) { + zig_free_windows_sdk(&priv->base); + return err; + } + } + + *out_sdk = &priv->base; + return ZigFindWindowsSdkErrorNone; +} + +#else + +void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {} +ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) { + return ZigFindWindowsSdkErrorNotFound; +} + +#endif diff --git a/src/windows_sdk.h b/src/windows_sdk.h new file mode 100644 index 0000000000..080ed55bed --- /dev/null +++ b/src/windows_sdk.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_WINDOWS_SDK_H +#define ZIG_WINDOWS_SDK_H + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif + +struct ZigWindowsSDK { + const char *path10_ptr; + size_t path10_len; + + const char *version10_ptr; + size_t version10_len; + + const char *path81_ptr; + size_t path81_len; + + const char *version81_ptr; + size_t version81_len; + + const char *msvc_lib_dir_ptr; + size_t msvc_lib_dir_len; +}; + +enum ZigFindWindowsSdkError { + ZigFindWindowsSdkErrorNone, + ZigFindWindowsSdkErrorOutOfMemory, + ZigFindWindowsSdkErrorNotFound, + ZigFindWindowsSdkErrorPathTooLong, +}; + +ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk); + +ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk); + +#endif diff --git a/std/os/windows/advapi32.zig b/std/os/windows/advapi32.zig new file mode 100644 index 0000000000..dcb5a636ea --- /dev/null +++ b/std/os/windows/advapi32.zig @@ -0,0 +1,30 @@ +use @import("index.zig"); + +pub const PROV_RSA_FULL = 1; + +pub const REGSAM = ACCESS_MASK; +pub const ACCESS_MASK = DWORD; +pub const PHKEY = &HKEY; +pub const HKEY = &HKEY__; +pub const HKEY__ = extern struct { + unused: c_int, +}; +pub const LSTATUS = LONG; + +pub extern "advapi32" stdcallcc fn CryptAcquireContextA( + phProv: *HCRYPTPROV, + pszContainer: ?LPCSTR, + pszProvider: ?LPCSTR, + dwProvType: DWORD, + dwFlags: DWORD, +) BOOL; + +pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL; + +pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL; + +pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM, + phkResult: &HKEY,) LSTATUS; + +pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD, + lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS; diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 96c4d3861c..90ccfaf6c5 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -1,190 +1,19 @@ const std = @import("../../index.zig"); const assert = std.debug.assert; + +pub use @import("advapi32.zig"); +pub use @import("kernel32.zig"); +pub use @import("ole32.zig"); +pub use @import("shell32.zig"); +pub use @import("shlwapi.zig"); +pub use @import("user32.zig"); + test "import" { _ = @import("util.zig"); } pub const ERROR = @import("error.zig"); -pub extern "advapi32" stdcallcc fn CryptAcquireContextA( - phProv: *HCRYPTPROV, - pszContainer: ?LPCSTR, - pszProvider: ?LPCSTR, - dwProvType: DWORD, - dwFlags: DWORD, -) BOOL; - -pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL; - -pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL; - -pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; - -pub extern "kernel32" stdcallcc fn CreateDirectoryA( - lpPathName: LPCSTR, - lpSecurityAttributes: ?*SECURITY_ATTRIBUTES, -) BOOL; - -pub extern "kernel32" stdcallcc fn CreateFileA( - lpFileName: LPCSTR, - dwDesiredAccess: DWORD, - dwShareMode: DWORD, - lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, - dwCreationDisposition: DWORD, - dwFlagsAndAttributes: DWORD, - hTemplateFile: ?HANDLE, -) HANDLE; - -pub extern "kernel32" stdcallcc fn CreatePipe( - hReadPipe: *HANDLE, - hWritePipe: *HANDLE, - lpPipeAttributes: *const SECURITY_ATTRIBUTES, - nSize: DWORD, -) BOOL; - -pub extern "kernel32" stdcallcc fn CreateProcessA( - lpApplicationName: ?LPCSTR, - lpCommandLine: LPSTR, - lpProcessAttributes: ?*SECURITY_ATTRIBUTES, - lpThreadAttributes: ?*SECURITY_ATTRIBUTES, - bInheritHandles: BOOL, - dwCreationFlags: DWORD, - lpEnvironment: ?*c_void, - lpCurrentDirectory: ?LPCSTR, - lpStartupInfo: *STARTUPINFOA, - lpProcessInformation: *PROCESS_INFORMATION, -) BOOL; - -pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( - lpSymlinkFileName: LPCSTR, - lpTargetFileName: LPCSTR, - dwFlags: DWORD, -) BOOLEAN; - -pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; - -pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; - -pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; - -pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; - -pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE; -pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL; - -pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; - -pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; - -pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; - -pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD; - -pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8; - -pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD; - -pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL; - -pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; - -pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD; - -pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; - -pub extern "kernel32" stdcallcc fn GetLastError() DWORD; - -pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( - in_hFile: HANDLE, - in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, - out_lpFileInformation: *c_void, - in_dwBufferSize: DWORD, -) BOOL; - -pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( - hFile: HANDLE, - lpszFilePath: LPSTR, - cchFilePath: DWORD, - dwFlags: DWORD, -) DWORD; - -pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; -pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL; - -pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void; -pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void; - -pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; -pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; -pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; -pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; -pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; -pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; - -pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; - -pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; - -pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; - -pub extern "kernel32" stdcallcc fn MoveFileExA( - lpExistingFileName: LPCSTR, - lpNewFileName: LPCSTR, - dwFlags: DWORD, -) BOOL; - -pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL; - -pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL; - -pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; - -pub extern "kernel32" stdcallcc fn ReadFile( - in_hFile: HANDLE, - out_lpBuffer: *c_void, - in_nNumberOfBytesToRead: DWORD, - out_lpNumberOfBytesRead: *DWORD, - in_out_lpOverlapped: ?*OVERLAPPED, -) BOOL; - -pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL; - -pub extern "kernel32" stdcallcc fn SetFilePointerEx( - in_fFile: HANDLE, - in_liDistanceToMove: LARGE_INTEGER, - out_opt_ldNewFilePointer: ?*LARGE_INTEGER, - in_dwMoveMethod: DWORD, -) BOOL; - -pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL; - -pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void; - -pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL; - -pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD; - -pub extern "kernel32" stdcallcc fn WriteFile( - in_hFile: HANDLE, - in_lpBuffer: *const c_void, - in_nNumberOfBytesToWrite: DWORD, - out_lpNumberOfBytesWritten: ?*DWORD, - in_out_lpOverlapped: ?*OVERLAPPED, -) BOOL; - -//TODO: call unicode versions instead of relying on ANSI code page -pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE; - -pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; - -pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; - -pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL; - -pub const PROV_RSA_FULL = 1; - pub const BOOL = c_int; pub const BOOLEAN = BYTE; pub const BYTE = u8; @@ -206,6 +35,7 @@ pub const LPSTR = [*]CHAR; pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; pub const LPVOID = *c_void; pub const LPWSTR = [*]WCHAR; +pub const LPCWSTR = [*]const WCHAR; pub const PVOID = *c_void; pub const PWSTR = [*]WCHAR; pub const SIZE_T = usize; @@ -442,10 +272,6 @@ pub const SYSTEM_INFO = extern struct { wProcessorRevision: WORD, }; -pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; - -pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT; - pub const HRESULT = c_long; pub const KNOWNFOLDERID = GUID; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig new file mode 100644 index 0000000000..fa3473ad05 --- /dev/null +++ b/std/os/windows/kernel32.zig @@ -0,0 +1,162 @@ +use @import("index.zig"); + +pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; + +pub extern "kernel32" stdcallcc fn CreateDirectoryA( + lpPathName: LPCSTR, + lpSecurityAttributes: ?*SECURITY_ATTRIBUTES, +) BOOL; + +pub extern "kernel32" stdcallcc fn CreateFileA( + lpFileName: LPCSTR, + dwDesiredAccess: DWORD, + dwShareMode: DWORD, + lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, + dwCreationDisposition: DWORD, + dwFlagsAndAttributes: DWORD, + hTemplateFile: ?HANDLE, +) HANDLE; + +pub extern "kernel32" stdcallcc fn CreatePipe( + hReadPipe: *HANDLE, + hWritePipe: *HANDLE, + lpPipeAttributes: *const SECURITY_ATTRIBUTES, + nSize: DWORD, +) BOOL; + +pub extern "kernel32" stdcallcc fn CreateProcessA( + lpApplicationName: ?LPCSTR, + lpCommandLine: LPSTR, + lpProcessAttributes: ?*SECURITY_ATTRIBUTES, + lpThreadAttributes: ?*SECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: ?*c_void, + lpCurrentDirectory: ?LPCSTR, + lpStartupInfo: *STARTUPINFOA, + lpProcessInformation: *PROCESS_INFORMATION, +) BOOL; + +pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( + lpSymlinkFileName: LPCSTR, + lpTargetFileName: LPCSTR, + dwFlags: DWORD, +) BOOLEAN; + +pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; + +pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; + +pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; + +pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; + +pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE; +pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL; +pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL; + +pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; + +pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; + +pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; + +pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD; + +pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8; + +pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD; + +pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL; + +pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; + +pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD; + +pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; + +pub extern "kernel32" stdcallcc fn GetLastError() DWORD; + +pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( + in_hFile: HANDLE, + in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, + out_lpFileInformation: *c_void, + in_dwBufferSize: DWORD, +) BOOL; + +pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( + hFile: HANDLE, + lpszFilePath: LPSTR, + cchFilePath: DWORD, + dwFlags: DWORD, +) DWORD; + +pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; +pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL; + +pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void; +pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void; + +pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; +pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; +pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; +pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; +pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; +pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; + +pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; + +pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; + +pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; + +pub extern "kernel32" stdcallcc fn MoveFileExA( + lpExistingFileName: LPCSTR, + lpNewFileName: LPCSTR, + dwFlags: DWORD, +) BOOL; + +pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL; + +pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL; + +pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; + +pub extern "kernel32" stdcallcc fn ReadFile( + in_hFile: HANDLE, + out_lpBuffer: *c_void, + in_nNumberOfBytesToRead: DWORD, + out_lpNumberOfBytesRead: *DWORD, + in_out_lpOverlapped: ?*OVERLAPPED, +) BOOL; + +pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL; + +pub extern "kernel32" stdcallcc fn SetFilePointerEx( + in_fFile: HANDLE, + in_liDistanceToMove: LARGE_INTEGER, + out_opt_ldNewFilePointer: ?*LARGE_INTEGER, + in_dwMoveMethod: DWORD, +) BOOL; + +pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL; + +pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void; + +pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL; + +pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD; + +pub extern "kernel32" stdcallcc fn WriteFile( + in_hFile: HANDLE, + in_lpBuffer: *const c_void, + in_nNumberOfBytesToWrite: DWORD, + out_lpNumberOfBytesWritten: ?*DWORD, + in_out_lpOverlapped: ?*OVERLAPPED, +) BOOL; + +//TODO: call unicode versions instead of relying on ANSI code page +pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE; + +pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; diff --git a/std/os/windows/ole32.zig b/std/os/windows/ole32.zig new file mode 100644 index 0000000000..84d8089d07 --- /dev/null +++ b/std/os/windows/ole32.zig @@ -0,0 +1,18 @@ +use @import("index.zig"); + +pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; +pub extern "ole32.dll" stdcallcc fn CoUninitialize() void; +pub extern "ole32.dll" stdcallcc fn CoGetCurrentProcess() DWORD; +pub extern "ole32.dll" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT; + + +pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED; +pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED; +pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE; +pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY; +pub const COINIT = extern enum { + COINIT_APARTMENTTHREADED = 2, + COINIT_MULTITHREADED = 0, + COINIT_DISABLE_OLE1DDE = 4, + COINIT_SPEED_OVER_MEMORY = 8, +}; diff --git a/std/os/windows/shell32.zig b/std/os/windows/shell32.zig new file mode 100644 index 0000000000..f10466add3 --- /dev/null +++ b/std/os/windows/shell32.zig @@ -0,0 +1,4 @@ +use @import("index.zig"); + +pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT; + diff --git a/std/os/windows/shlwapi.zig b/std/os/windows/shlwapi.zig new file mode 100644 index 0000000000..6bccefaf98 --- /dev/null +++ b/std/os/windows/shlwapi.zig @@ -0,0 +1,4 @@ +use @import("index.zig"); + +pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL; + diff --git a/std/os/windows/user32.zig b/std/os/windows/user32.zig new file mode 100644 index 0000000000..37f9f6f3b8 --- /dev/null +++ b/std/os/windows/user32.zig @@ -0,0 +1,4 @@ +use @import("index.zig"); + +pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; + -- cgit v1.2.3 From 7ef110b484b84a0215b715d9defad59138f892d8 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 21 Jul 2018 19:14:14 +0900 Subject: std.os.posix: Add AF_* for darwin; Tracking issue #1271; --- std/os/darwin.zig | 1 + std/os/darwin_socket.zig | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 std/os/darwin_socket.zig diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 4134e382fc..4258e33c93 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -3,6 +3,7 @@ const c = std.c; const assert = std.debug.assert; pub use @import("darwin_errno.zig"); +pub use @import("darwin_socket.zig"); pub const PATH_MAX = 1024; diff --git a/std/os/darwin_socket.zig b/std/os/darwin_socket.zig new file mode 100644 index 0000000000..5f2e32d6f9 --- /dev/null +++ b/std/os/darwin_socket.zig @@ -0,0 +1,75 @@ +pub const AF_UNSPEC: c_int = 0; +pub const AF_LOCAL: c_int = 1; +pub const AF_UNIX: c_int = AF_LOCAL; +pub const AF_INET: c_int = 2; +pub const AF_SYS_CONTROL: c_int = 2; +pub const AF_IMPLINK: c_int = 3; +pub const AF_PUP: c_int = 4; +pub const AF_CHAOS: c_int = 5; +pub const AF_NS: c_int = 6; +pub const AF_ISO: c_int = 7; +pub const AF_OSI: c_int = AF_ISO; +pub const AF_ECMA: c_int = 8; +pub const AF_DATAKIT: c_int = 9; +pub const AF_CCITT: c_int = 10; +pub const AF_SNA: c_int = 11; +pub const AF_DECnet: c_int = 12; +pub const AF_DLI: c_int = 13; +pub const AF_LAT: c_int = 14; +pub const AF_HYLINK: c_int = 15; +pub const AF_APPLETALK: c_int = 16; +pub const AF_ROUTE: c_int = 17; +pub const AF_LINK: c_int = 18; +pub const AF_XTP: c_int = 19; +pub const AF_COIP: c_int = 20; +pub const AF_CNT: c_int = 21; +pub const AF_RTIP: c_int = 22; +pub const AF_IPX: c_int = 23; +pub const AF_SIP: c_int = 24; +pub const AF_PIP: c_int = 25; +pub const AF_ISDN: c_int = 28; +pub const AF_E164: c_int = AF_ISDN; +pub const AF_KEY: c_int = 29; +pub const AF_INET6: c_int = 30; +pub const AF_NATM: c_int = 31; +pub const AF_SYSTEM: c_int = 32; +pub const AF_NETBIOS: c_int = 33; +pub const AF_PPP: c_int = 34; +pub const AF_MAX: c_int = 40; + +pub const PF_UNSPEC: c_int = AF_UNSPEC; +pub const PF_LOCAL: c_int = AF_LOCAL; +pub const PF_UNIX: c_int = PF_LOCAL; +pub const PF_INET: c_int = AF_INET; +pub const PF_IMPLINK: c_int = AF_IMPLINK; +pub const PF_PUP: c_int = AF_PUP; +pub const PF_CHAOS: c_int = AF_CHAOS; +pub const PF_NS: c_int = AF_NS; +pub const PF_ISO: c_int = AF_ISO; +pub const PF_OSI: c_int = AF_ISO; +pub const PF_ECMA: c_int = AF_ECMA; +pub const PF_DATAKIT: c_int = AF_DATAKIT; +pub const PF_CCITT: c_int = AF_CCITT; +pub const PF_SNA: c_int = AF_SNA; +pub const PF_DECnet: c_int = AF_DECnet; +pub const PF_DLI: c_int = AF_DLI; +pub const PF_LAT: c_int = AF_LAT; +pub const PF_HYLINK: c_int = AF_HYLINK; +pub const PF_APPLETALK: c_int = AF_APPLETALK; +pub const PF_ROUTE: c_int = AF_ROUTE; +pub const PF_LINK: c_int = AF_LINK; +pub const PF_XTP: c_int = AF_XTP; +pub const PF_COIP: c_int = AF_COIP; +pub const PF_CNT: c_int = AF_CNT; +pub const PF_SIP: c_int = AF_SIP; +pub const PF_IPX: c_int = AF_IPX; +pub const PF_RTIP: c_int = AF_RTIP; +pub const PF_PIP: c_int = AF_PIP; +pub const PF_ISDN: c_int = AF_ISDN; +pub const PF_KEY: c_int = AF_KEY; +pub const PF_INET6: c_int = AF_INET6; +pub const PF_NATM: c_int = AF_NATM; +pub const PF_SYSTEM: c_int = AF_SYSTEM; +pub const PF_NETBIOS: c_int = AF_NETBIOS; +pub const PF_PPP: c_int = AF_PPP; +pub const PF_MAX: c_int = AF_MAX; -- cgit v1.2.3 From 8062afcb31c1545b13070876809a0f51cb9aa034 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 21 Jul 2018 19:14:40 +0900 Subject: std.os.posix: Add SYSPROTO_* for darwin; Tracking issue #1271; --- std/os/darwin_socket.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/std/os/darwin_socket.zig b/std/os/darwin_socket.zig index 5f2e32d6f9..25968d000c 100644 --- a/std/os/darwin_socket.zig +++ b/std/os/darwin_socket.zig @@ -73,3 +73,6 @@ pub const PF_SYSTEM: c_int = AF_SYSTEM; pub const PF_NETBIOS: c_int = AF_NETBIOS; pub const PF_PPP: c_int = AF_PPP; pub const PF_MAX: c_int = AF_MAX; + +pub const SYSPROTO_EVENT: c_int = 1; +pub const SYSPROTO_CONTROL: c_int = 2; -- cgit v1.2.3 From 460c2662167758b049d46fa8df94a9cd37a3438e Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 21 Jul 2018 19:15:03 +0900 Subject: std.os.posix: Add SOCK_* for darwin; Tracking issue #1271; --- std/os/darwin_socket.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/std/os/darwin_socket.zig b/std/os/darwin_socket.zig index 25968d000c..0c613c6504 100644 --- a/std/os/darwin_socket.zig +++ b/std/os/darwin_socket.zig @@ -76,3 +76,10 @@ pub const PF_MAX: c_int = AF_MAX; pub const SYSPROTO_EVENT: c_int = 1; pub const SYSPROTO_CONTROL: c_int = 2; + +pub const SOCK_STREAM: c_int = 1; +pub const SOCK_DGRAM: c_int = 2; +pub const SOCK_RAW: c_int = 3; +pub const SOCK_RDM: c_int = 4; +pub const SOCK_SEQPACKET: c_int = 5; +pub const SOCK_MAXADDRLEN: c_int = 255; -- cgit v1.2.3 From 501dd5f28452171f8d681c3c54b52e75682838c7 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 22 Jul 2018 01:47:53 +0900 Subject: CMakeLists.txt: add darwin_socket.zig; Tracking issue #1271; thanks @Hejsil; --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 096ac50cfd..662f14b799 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -555,6 +555,7 @@ set(ZIG_STD_FILES "os/child_process.zig" "os/darwin.zig" "os/darwin_errno.zig" + "os/darwin_socket.zig" "os/epoch.zig" "os/file.zig" "os/get_app_data_dir.zig" -- cgit v1.2.3 From df574ccf8655726dc204142e7bcfb36770426257 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 22 Jul 2018 02:20:03 +0900 Subject: std.special.test_runner.zig: make tests skippable; tracking issue #1274; tests can be skipped by returnning `error.skip` : --- std/special/test_runner.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/std/special/test_runner.zig b/std/special/test_runner.zig index 76a54a5018..46ed7e23e9 100644 --- a/std/special/test_runner.zig +++ b/std/special/test_runner.zig @@ -8,7 +8,13 @@ pub fn main() !void { for (test_fn_list) |test_fn, i| { warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name); - try test_fn.func(); + test_fn.func() catch |err| { + if (err == error.skip) { + warn("SKIPPED\n"); + continue; + } + return err; + }; warn("OK\n"); } -- cgit v1.2.3 From bc411af4ffb07fa0e8d713c300c2badd1116acff Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 22 Jul 2018 02:21:52 +0900 Subject: std.event.tcp: SKIP test instead of OKing test; tracking issue #1274 ; --- std/event/tcp.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/event/tcp.zig b/std/event/tcp.zig index 5151ecf934..27eab9f0bb 100644 --- a/std/event/tcp.zig +++ b/std/event/tcp.zig @@ -125,7 +125,7 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File test "listen on a port, send bytes, receive bytes" { if (builtin.os != builtin.Os.linux) { // TODO build abstractions for other operating systems - return; + return error.skip; } const MyServer = struct { tcp_server: Server, -- cgit v1.2.3 From c5c053b6fdde911edd0b488c29fbef0e2aa0a904 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 22 Jul 2018 03:11:55 +0900 Subject: std.event.tcp: add switch statement in preparation for building-out abstractions; depends on issue #1274 ; --- std/event/tcp.zig | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/std/event/tcp.zig b/std/event/tcp.zig index 27eab9f0bb..1542538082 100644 --- a/std/event/tcp.zig +++ b/std/event/tcp.zig @@ -123,10 +123,17 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File } test "listen on a port, send bytes, receive bytes" { - if (builtin.os != builtin.Os.linux) { - // TODO build abstractions for other operating systems + // TODO build abstractions for other operating systems + const skip_test: bool = switch (builtin.os) { + builtin.Os.linux => false, + //builtin.Os.macosx, builtin.Os.ios => false, + else => true, + }; + + if (skip_test == true) { return error.skip; } + const MyServer = struct { tcp_server: Server, -- cgit v1.2.3 From bb1b7967111dd5add6172fb33fd9c17e7a344321 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 22 Jul 2018 12:26:52 +0900 Subject: README: include link to channel logs (#1278) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a5868bf44e..99e224c367 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ that counts as "freestanding" for the purposes of this table. ## Community - * IRC: `#zig` on Freenode. + * IRC: `#zig` on Freenode ([Channel Logs](https://irclog.whitequark.org/zig/)). * Reddit: [/r/zig](https://www.reddit.com/r/zig) * Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang) -- cgit v1.2.3 From 4d9964a457084d41ba2995082d0e25b195757751 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 21 Jul 2018 23:43:43 -0400 Subject: rename error.skip to error.SkipZigTest also print stats at the end of test runner --- std/event/tcp.zig | 12 +++--------- std/special/test_runner.zig | 26 +++++++++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/std/event/tcp.zig b/std/event/tcp.zig index 1542538082..416a8c07dc 100644 --- a/std/event/tcp.zig +++ b/std/event/tcp.zig @@ -123,15 +123,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File } test "listen on a port, send bytes, receive bytes" { - // TODO build abstractions for other operating systems - const skip_test: bool = switch (builtin.os) { - builtin.Os.linux => false, - //builtin.Os.macosx, builtin.Os.ios => false, - else => true, - }; - - if (skip_test == true) { - return error.skip; + if (builtin.os != builtin.Os.linux) { + // TODO build abstractions for other operating systems + return error.SkipZigTest; } const MyServer = struct { diff --git a/std/special/test_runner.zig b/std/special/test_runner.zig index 46ed7e23e9..857739e82d 100644 --- a/std/special/test_runner.zig +++ b/std/special/test_runner.zig @@ -5,17 +5,25 @@ const test_fn_list = builtin.__zig_test_fn_slice; const warn = std.debug.warn; pub fn main() !void { + var ok_count: usize = 0; + var skip_count: usize = 0; for (test_fn_list) |test_fn, i| { warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name); - test_fn.func() catch |err| { - if (err == error.skip) { - warn("SKIPPED\n"); - continue; - } - return err; - }; - - warn("OK\n"); + if (test_fn.func()) |_| { + ok_count += 1; + warn("OK\n"); + } else |err| switch (err) { + error.SkipZigTest => { + skip_count += 1; + warn("SKIP\n"); + }, + else => return err, + } + } + if (ok_count == test_fn_list.len) { + warn("All tests passed.\n"); + } else { + warn("{} passed; {} skipped.\n", ok_count, skip_count); } } -- cgit v1.2.3 From 20f286f22aa1ea06b6c914cfd25a2bac5d324f69 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Jul 2018 00:04:24 -0400 Subject: re-organize std lib darwin files --- CMakeLists.txt | 3 +- std/c/darwin.zig | 2 +- std/os/darwin.zig | 89 ++++++++++++- std/os/darwin/errno.zig | 328 +++++++++++++++++++++++++++++++++++++++++++++++ std/os/darwin_errno.zig | 328 ----------------------------------------------- std/os/darwin_socket.zig | 85 ------------ std/os/index.zig | 2 +- 7 files changed, 418 insertions(+), 419 deletions(-) create mode 100644 std/os/darwin/errno.zig delete mode 100644 std/os/darwin_errno.zig delete mode 100644 std/os/darwin_socket.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 662f14b799..137cefb5b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -554,8 +554,7 @@ set(ZIG_STD_FILES "net.zig" "os/child_process.zig" "os/darwin.zig" - "os/darwin_errno.zig" - "os/darwin_socket.zig" + "os/darwin/errno.zig" "os/epoch.zig" "os/file.zig" "os/get_app_data_dir.zig" diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 4189dfeadc..1bd1d6c4c9 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -30,7 +30,7 @@ pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlen pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; -pub use @import("../os/darwin_errno.zig"); +pub use @import("../os/darwin/errno.zig"); pub const _errno = __error; diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 4258e33c93..cf67b01d5a 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -2,8 +2,7 @@ const std = @import("../index.zig"); const c = std.c; const assert = std.debug.assert; -pub use @import("darwin_errno.zig"); -pub use @import("darwin_socket.zig"); +pub use @import("darwin/errno.zig"); pub const PATH_MAX = 1024; @@ -483,6 +482,92 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080; /// data is mach absolute time units pub const NOTE_MACHTIME = 0x00000100; +pub const AF_UNSPEC: c_int = 0; +pub const AF_LOCAL: c_int = 1; +pub const AF_UNIX: c_int = AF_LOCAL; +pub const AF_INET: c_int = 2; +pub const AF_SYS_CONTROL: c_int = 2; +pub const AF_IMPLINK: c_int = 3; +pub const AF_PUP: c_int = 4; +pub const AF_CHAOS: c_int = 5; +pub const AF_NS: c_int = 6; +pub const AF_ISO: c_int = 7; +pub const AF_OSI: c_int = AF_ISO; +pub const AF_ECMA: c_int = 8; +pub const AF_DATAKIT: c_int = 9; +pub const AF_CCITT: c_int = 10; +pub const AF_SNA: c_int = 11; +pub const AF_DECnet: c_int = 12; +pub const AF_DLI: c_int = 13; +pub const AF_LAT: c_int = 14; +pub const AF_HYLINK: c_int = 15; +pub const AF_APPLETALK: c_int = 16; +pub const AF_ROUTE: c_int = 17; +pub const AF_LINK: c_int = 18; +pub const AF_XTP: c_int = 19; +pub const AF_COIP: c_int = 20; +pub const AF_CNT: c_int = 21; +pub const AF_RTIP: c_int = 22; +pub const AF_IPX: c_int = 23; +pub const AF_SIP: c_int = 24; +pub const AF_PIP: c_int = 25; +pub const AF_ISDN: c_int = 28; +pub const AF_E164: c_int = AF_ISDN; +pub const AF_KEY: c_int = 29; +pub const AF_INET6: c_int = 30; +pub const AF_NATM: c_int = 31; +pub const AF_SYSTEM: c_int = 32; +pub const AF_NETBIOS: c_int = 33; +pub const AF_PPP: c_int = 34; +pub const AF_MAX: c_int = 40; + +pub const PF_UNSPEC: c_int = AF_UNSPEC; +pub const PF_LOCAL: c_int = AF_LOCAL; +pub const PF_UNIX: c_int = PF_LOCAL; +pub const PF_INET: c_int = AF_INET; +pub const PF_IMPLINK: c_int = AF_IMPLINK; +pub const PF_PUP: c_int = AF_PUP; +pub const PF_CHAOS: c_int = AF_CHAOS; +pub const PF_NS: c_int = AF_NS; +pub const PF_ISO: c_int = AF_ISO; +pub const PF_OSI: c_int = AF_ISO; +pub const PF_ECMA: c_int = AF_ECMA; +pub const PF_DATAKIT: c_int = AF_DATAKIT; +pub const PF_CCITT: c_int = AF_CCITT; +pub const PF_SNA: c_int = AF_SNA; +pub const PF_DECnet: c_int = AF_DECnet; +pub const PF_DLI: c_int = AF_DLI; +pub const PF_LAT: c_int = AF_LAT; +pub const PF_HYLINK: c_int = AF_HYLINK; +pub const PF_APPLETALK: c_int = AF_APPLETALK; +pub const PF_ROUTE: c_int = AF_ROUTE; +pub const PF_LINK: c_int = AF_LINK; +pub const PF_XTP: c_int = AF_XTP; +pub const PF_COIP: c_int = AF_COIP; +pub const PF_CNT: c_int = AF_CNT; +pub const PF_SIP: c_int = AF_SIP; +pub const PF_IPX: c_int = AF_IPX; +pub const PF_RTIP: c_int = AF_RTIP; +pub const PF_PIP: c_int = AF_PIP; +pub const PF_ISDN: c_int = AF_ISDN; +pub const PF_KEY: c_int = AF_KEY; +pub const PF_INET6: c_int = AF_INET6; +pub const PF_NATM: c_int = AF_NATM; +pub const PF_SYSTEM: c_int = AF_SYSTEM; +pub const PF_NETBIOS: c_int = AF_NETBIOS; +pub const PF_PPP: c_int = AF_PPP; +pub const PF_MAX: c_int = AF_MAX; + +pub const SYSPROTO_EVENT: c_int = 1; +pub const SYSPROTO_CONTROL: c_int = 2; + +pub const SOCK_STREAM: c_int = 1; +pub const SOCK_DGRAM: c_int = 2; +pub const SOCK_RAW: c_int = 3; +pub const SOCK_RDM: c_int = 4; +pub const SOCK_SEQPACKET: c_int = 5; +pub const SOCK_MAXADDRLEN: c_int = 255; + fn wstatus(x: i32) i32 { return x & 0o177; } diff --git a/std/os/darwin/errno.zig b/std/os/darwin/errno.zig new file mode 100644 index 0000000000..438f3382ad --- /dev/null +++ b/std/os/darwin/errno.zig @@ -0,0 +1,328 @@ +/// Operation not permitted +pub const EPERM = 1; + +/// No such file or directory +pub const ENOENT = 2; + +/// No such process +pub const ESRCH = 3; + +/// Interrupted system call +pub const EINTR = 4; + +/// Input/output error +pub const EIO = 5; + +/// Device not configured +pub const ENXIO = 6; + +/// Argument list too long +pub const E2BIG = 7; + +/// Exec format error +pub const ENOEXEC = 8; + +/// Bad file descriptor +pub const EBADF = 9; + +/// No child processes +pub const ECHILD = 10; + +/// Resource deadlock avoided +pub const EDEADLK = 11; + +/// Cannot allocate memory +pub const ENOMEM = 12; + +/// Permission denied +pub const EACCES = 13; + +/// Bad address +pub const EFAULT = 14; + +/// Block device required +pub const ENOTBLK = 15; + +/// Device / Resource busy +pub const EBUSY = 16; + +/// File exists +pub const EEXIST = 17; + +/// Cross-device link +pub const EXDEV = 18; + +/// Operation not supported by device +pub const ENODEV = 19; + +/// Not a directory +pub const ENOTDIR = 20; + +/// Is a directory +pub const EISDIR = 21; + +/// Invalid argument +pub const EINVAL = 22; + +/// Too many open files in system +pub const ENFILE = 23; + +/// Too many open files +pub const EMFILE = 24; + +/// Inappropriate ioctl for device +pub const ENOTTY = 25; + +/// Text file busy +pub const ETXTBSY = 26; + +/// File too large +pub const EFBIG = 27; + +/// No space left on device +pub const ENOSPC = 28; + +/// Illegal seek +pub const ESPIPE = 29; + +/// Read-only file system +pub const EROFS = 30; + +/// Too many links +pub const EMLINK = 31; +/// Broken pipe + +// math software +pub const EPIPE = 32; + +/// Numerical argument out of domain +pub const EDOM = 33; +/// Result too large + +// non-blocking and interrupt i/o +pub const ERANGE = 34; + +/// Resource temporarily unavailable +pub const EAGAIN = 35; + +/// Operation would block +pub const EWOULDBLOCK = EAGAIN; + +/// Operation now in progress +pub const EINPROGRESS = 36; +/// Operation already in progress + +// ipc/network software -- argument errors +pub const EALREADY = 37; + +/// Socket operation on non-socket +pub const ENOTSOCK = 38; + +/// Destination address required +pub const EDESTADDRREQ = 39; + +/// Message too long +pub const EMSGSIZE = 40; + +/// Protocol wrong type for socket +pub const EPROTOTYPE = 41; + +/// Protocol not available +pub const ENOPROTOOPT = 42; + +/// Protocol not supported +pub const EPROTONOSUPPORT = 43; + +/// Socket type not supported +pub const ESOCKTNOSUPPORT = 44; + +/// Operation not supported +pub const ENOTSUP = 45; + +/// Protocol family not supported +pub const EPFNOSUPPORT = 46; + +/// Address family not supported by protocol family +pub const EAFNOSUPPORT = 47; + +/// Address already in use +pub const EADDRINUSE = 48; +/// Can't assign requested address + +// ipc/network software -- operational errors +pub const EADDRNOTAVAIL = 49; + +/// Network is down +pub const ENETDOWN = 50; + +/// Network is unreachable +pub const ENETUNREACH = 51; + +/// Network dropped connection on reset +pub const ENETRESET = 52; + +/// Software caused connection abort +pub const ECONNABORTED = 53; + +/// Connection reset by peer +pub const ECONNRESET = 54; + +/// No buffer space available +pub const ENOBUFS = 55; + +/// Socket is already connected +pub const EISCONN = 56; + +/// Socket is not connected +pub const ENOTCONN = 57; + +/// Can't send after socket shutdown +pub const ESHUTDOWN = 58; + +/// Too many references: can't splice +pub const ETOOMANYREFS = 59; + +/// Operation timed out +pub const ETIMEDOUT = 60; + +/// Connection refused +pub const ECONNREFUSED = 61; + +/// Too many levels of symbolic links +pub const ELOOP = 62; + +/// File name too long +pub const ENAMETOOLONG = 63; + +/// Host is down +pub const EHOSTDOWN = 64; + +/// No route to host +pub const EHOSTUNREACH = 65; +/// Directory not empty + +// quotas & mush +pub const ENOTEMPTY = 66; + +/// Too many processes +pub const EPROCLIM = 67; + +/// Too many users +pub const EUSERS = 68; +/// Disc quota exceeded + +// Network File System +pub const EDQUOT = 69; + +/// Stale NFS file handle +pub const ESTALE = 70; + +/// Too many levels of remote in path +pub const EREMOTE = 71; + +/// RPC struct is bad +pub const EBADRPC = 72; + +/// RPC version wrong +pub const ERPCMISMATCH = 73; + +/// RPC prog. not avail +pub const EPROGUNAVAIL = 74; + +/// Program version wrong +pub const EPROGMISMATCH = 75; + +/// Bad procedure for program +pub const EPROCUNAVAIL = 76; + +/// No locks available +pub const ENOLCK = 77; + +/// Function not implemented +pub const ENOSYS = 78; + +/// Inappropriate file type or format +pub const EFTYPE = 79; + +/// Authentication error +pub const EAUTH = 80; +/// Need authenticator + +// Intelligent device errors +pub const ENEEDAUTH = 81; + +/// Device power is off +pub const EPWROFF = 82; + +/// Device error, e.g. paper out +pub const EDEVERR = 83; +/// Value too large to be stored in data type + +// Program loading errors +pub const EOVERFLOW = 84; + +/// Bad executable +pub const EBADEXEC = 85; + +/// Bad CPU type in executable +pub const EBADARCH = 86; + +/// Shared library version mismatch +pub const ESHLIBVERS = 87; + +/// Malformed Macho file +pub const EBADMACHO = 88; + +/// Operation canceled +pub const ECANCELED = 89; + +/// Identifier removed +pub const EIDRM = 90; + +/// No message of desired type +pub const ENOMSG = 91; + +/// Illegal byte sequence +pub const EILSEQ = 92; + +/// Attribute not found +pub const ENOATTR = 93; + +/// Bad message +pub const EBADMSG = 94; + +/// Reserved +pub const EMULTIHOP = 95; + +/// No message available on STREAM +pub const ENODATA = 96; + +/// Reserved +pub const ENOLINK = 97; + +/// No STREAM resources +pub const ENOSR = 98; + +/// Not a STREAM +pub const ENOSTR = 99; + +/// Protocol error +pub const EPROTO = 100; + +/// STREAM ioctl timeout +pub const ETIME = 101; + +/// No such policy registered +pub const ENOPOLICY = 103; + +/// State not recoverable +pub const ENOTRECOVERABLE = 104; + +/// Previous owner died +pub const EOWNERDEAD = 105; + +/// Interface output queue is full +pub const EQFULL = 106; + +/// Must be equal largest errno +pub const ELAST = 106; diff --git a/std/os/darwin_errno.zig b/std/os/darwin_errno.zig deleted file mode 100644 index 438f3382ad..0000000000 --- a/std/os/darwin_errno.zig +++ /dev/null @@ -1,328 +0,0 @@ -/// Operation not permitted -pub const EPERM = 1; - -/// No such file or directory -pub const ENOENT = 2; - -/// No such process -pub const ESRCH = 3; - -/// Interrupted system call -pub const EINTR = 4; - -/// Input/output error -pub const EIO = 5; - -/// Device not configured -pub const ENXIO = 6; - -/// Argument list too long -pub const E2BIG = 7; - -/// Exec format error -pub const ENOEXEC = 8; - -/// Bad file descriptor -pub const EBADF = 9; - -/// No child processes -pub const ECHILD = 10; - -/// Resource deadlock avoided -pub const EDEADLK = 11; - -/// Cannot allocate memory -pub const ENOMEM = 12; - -/// Permission denied -pub const EACCES = 13; - -/// Bad address -pub const EFAULT = 14; - -/// Block device required -pub const ENOTBLK = 15; - -/// Device / Resource busy -pub const EBUSY = 16; - -/// File exists -pub const EEXIST = 17; - -/// Cross-device link -pub const EXDEV = 18; - -/// Operation not supported by device -pub const ENODEV = 19; - -/// Not a directory -pub const ENOTDIR = 20; - -/// Is a directory -pub const EISDIR = 21; - -/// Invalid argument -pub const EINVAL = 22; - -/// Too many open files in system -pub const ENFILE = 23; - -/// Too many open files -pub const EMFILE = 24; - -/// Inappropriate ioctl for device -pub const ENOTTY = 25; - -/// Text file busy -pub const ETXTBSY = 26; - -/// File too large -pub const EFBIG = 27; - -/// No space left on device -pub const ENOSPC = 28; - -/// Illegal seek -pub const ESPIPE = 29; - -/// Read-only file system -pub const EROFS = 30; - -/// Too many links -pub const EMLINK = 31; -/// Broken pipe - -// math software -pub const EPIPE = 32; - -/// Numerical argument out of domain -pub const EDOM = 33; -/// Result too large - -// non-blocking and interrupt i/o -pub const ERANGE = 34; - -/// Resource temporarily unavailable -pub const EAGAIN = 35; - -/// Operation would block -pub const EWOULDBLOCK = EAGAIN; - -/// Operation now in progress -pub const EINPROGRESS = 36; -/// Operation already in progress - -// ipc/network software -- argument errors -pub const EALREADY = 37; - -/// Socket operation on non-socket -pub const ENOTSOCK = 38; - -/// Destination address required -pub const EDESTADDRREQ = 39; - -/// Message too long -pub const EMSGSIZE = 40; - -/// Protocol wrong type for socket -pub const EPROTOTYPE = 41; - -/// Protocol not available -pub const ENOPROTOOPT = 42; - -/// Protocol not supported -pub const EPROTONOSUPPORT = 43; - -/// Socket type not supported -pub const ESOCKTNOSUPPORT = 44; - -/// Operation not supported -pub const ENOTSUP = 45; - -/// Protocol family not supported -pub const EPFNOSUPPORT = 46; - -/// Address family not supported by protocol family -pub const EAFNOSUPPORT = 47; - -/// Address already in use -pub const EADDRINUSE = 48; -/// Can't assign requested address - -// ipc/network software -- operational errors -pub const EADDRNOTAVAIL = 49; - -/// Network is down -pub const ENETDOWN = 50; - -/// Network is unreachable -pub const ENETUNREACH = 51; - -/// Network dropped connection on reset -pub const ENETRESET = 52; - -/// Software caused connection abort -pub const ECONNABORTED = 53; - -/// Connection reset by peer -pub const ECONNRESET = 54; - -/// No buffer space available -pub const ENOBUFS = 55; - -/// Socket is already connected -pub const EISCONN = 56; - -/// Socket is not connected -pub const ENOTCONN = 57; - -/// Can't send after socket shutdown -pub const ESHUTDOWN = 58; - -/// Too many references: can't splice -pub const ETOOMANYREFS = 59; - -/// Operation timed out -pub const ETIMEDOUT = 60; - -/// Connection refused -pub const ECONNREFUSED = 61; - -/// Too many levels of symbolic links -pub const ELOOP = 62; - -/// File name too long -pub const ENAMETOOLONG = 63; - -/// Host is down -pub const EHOSTDOWN = 64; - -/// No route to host -pub const EHOSTUNREACH = 65; -/// Directory not empty - -// quotas & mush -pub const ENOTEMPTY = 66; - -/// Too many processes -pub const EPROCLIM = 67; - -/// Too many users -pub const EUSERS = 68; -/// Disc quota exceeded - -// Network File System -pub const EDQUOT = 69; - -/// Stale NFS file handle -pub const ESTALE = 70; - -/// Too many levels of remote in path -pub const EREMOTE = 71; - -/// RPC struct is bad -pub const EBADRPC = 72; - -/// RPC version wrong -pub const ERPCMISMATCH = 73; - -/// RPC prog. not avail -pub const EPROGUNAVAIL = 74; - -/// Program version wrong -pub const EPROGMISMATCH = 75; - -/// Bad procedure for program -pub const EPROCUNAVAIL = 76; - -/// No locks available -pub const ENOLCK = 77; - -/// Function not implemented -pub const ENOSYS = 78; - -/// Inappropriate file type or format -pub const EFTYPE = 79; - -/// Authentication error -pub const EAUTH = 80; -/// Need authenticator - -// Intelligent device errors -pub const ENEEDAUTH = 81; - -/// Device power is off -pub const EPWROFF = 82; - -/// Device error, e.g. paper out -pub const EDEVERR = 83; -/// Value too large to be stored in data type - -// Program loading errors -pub const EOVERFLOW = 84; - -/// Bad executable -pub const EBADEXEC = 85; - -/// Bad CPU type in executable -pub const EBADARCH = 86; - -/// Shared library version mismatch -pub const ESHLIBVERS = 87; - -/// Malformed Macho file -pub const EBADMACHO = 88; - -/// Operation canceled -pub const ECANCELED = 89; - -/// Identifier removed -pub const EIDRM = 90; - -/// No message of desired type -pub const ENOMSG = 91; - -/// Illegal byte sequence -pub const EILSEQ = 92; - -/// Attribute not found -pub const ENOATTR = 93; - -/// Bad message -pub const EBADMSG = 94; - -/// Reserved -pub const EMULTIHOP = 95; - -/// No message available on STREAM -pub const ENODATA = 96; - -/// Reserved -pub const ENOLINK = 97; - -/// No STREAM resources -pub const ENOSR = 98; - -/// Not a STREAM -pub const ENOSTR = 99; - -/// Protocol error -pub const EPROTO = 100; - -/// STREAM ioctl timeout -pub const ETIME = 101; - -/// No such policy registered -pub const ENOPOLICY = 103; - -/// State not recoverable -pub const ENOTRECOVERABLE = 104; - -/// Previous owner died -pub const EOWNERDEAD = 105; - -/// Interface output queue is full -pub const EQFULL = 106; - -/// Must be equal largest errno -pub const ELAST = 106; diff --git a/std/os/darwin_socket.zig b/std/os/darwin_socket.zig deleted file mode 100644 index 0c613c6504..0000000000 --- a/std/os/darwin_socket.zig +++ /dev/null @@ -1,85 +0,0 @@ -pub const AF_UNSPEC: c_int = 0; -pub const AF_LOCAL: c_int = 1; -pub const AF_UNIX: c_int = AF_LOCAL; -pub const AF_INET: c_int = 2; -pub const AF_SYS_CONTROL: c_int = 2; -pub const AF_IMPLINK: c_int = 3; -pub const AF_PUP: c_int = 4; -pub const AF_CHAOS: c_int = 5; -pub const AF_NS: c_int = 6; -pub const AF_ISO: c_int = 7; -pub const AF_OSI: c_int = AF_ISO; -pub const AF_ECMA: c_int = 8; -pub const AF_DATAKIT: c_int = 9; -pub const AF_CCITT: c_int = 10; -pub const AF_SNA: c_int = 11; -pub const AF_DECnet: c_int = 12; -pub const AF_DLI: c_int = 13; -pub const AF_LAT: c_int = 14; -pub const AF_HYLINK: c_int = 15; -pub const AF_APPLETALK: c_int = 16; -pub const AF_ROUTE: c_int = 17; -pub const AF_LINK: c_int = 18; -pub const AF_XTP: c_int = 19; -pub const AF_COIP: c_int = 20; -pub const AF_CNT: c_int = 21; -pub const AF_RTIP: c_int = 22; -pub const AF_IPX: c_int = 23; -pub const AF_SIP: c_int = 24; -pub const AF_PIP: c_int = 25; -pub const AF_ISDN: c_int = 28; -pub const AF_E164: c_int = AF_ISDN; -pub const AF_KEY: c_int = 29; -pub const AF_INET6: c_int = 30; -pub const AF_NATM: c_int = 31; -pub const AF_SYSTEM: c_int = 32; -pub const AF_NETBIOS: c_int = 33; -pub const AF_PPP: c_int = 34; -pub const AF_MAX: c_int = 40; - -pub const PF_UNSPEC: c_int = AF_UNSPEC; -pub const PF_LOCAL: c_int = AF_LOCAL; -pub const PF_UNIX: c_int = PF_LOCAL; -pub const PF_INET: c_int = AF_INET; -pub const PF_IMPLINK: c_int = AF_IMPLINK; -pub const PF_PUP: c_int = AF_PUP; -pub const PF_CHAOS: c_int = AF_CHAOS; -pub const PF_NS: c_int = AF_NS; -pub const PF_ISO: c_int = AF_ISO; -pub const PF_OSI: c_int = AF_ISO; -pub const PF_ECMA: c_int = AF_ECMA; -pub const PF_DATAKIT: c_int = AF_DATAKIT; -pub const PF_CCITT: c_int = AF_CCITT; -pub const PF_SNA: c_int = AF_SNA; -pub const PF_DECnet: c_int = AF_DECnet; -pub const PF_DLI: c_int = AF_DLI; -pub const PF_LAT: c_int = AF_LAT; -pub const PF_HYLINK: c_int = AF_HYLINK; -pub const PF_APPLETALK: c_int = AF_APPLETALK; -pub const PF_ROUTE: c_int = AF_ROUTE; -pub const PF_LINK: c_int = AF_LINK; -pub const PF_XTP: c_int = AF_XTP; -pub const PF_COIP: c_int = AF_COIP; -pub const PF_CNT: c_int = AF_CNT; -pub const PF_SIP: c_int = AF_SIP; -pub const PF_IPX: c_int = AF_IPX; -pub const PF_RTIP: c_int = AF_RTIP; -pub const PF_PIP: c_int = AF_PIP; -pub const PF_ISDN: c_int = AF_ISDN; -pub const PF_KEY: c_int = AF_KEY; -pub const PF_INET6: c_int = AF_INET6; -pub const PF_NATM: c_int = AF_NATM; -pub const PF_SYSTEM: c_int = AF_SYSTEM; -pub const PF_NETBIOS: c_int = AF_NETBIOS; -pub const PF_PPP: c_int = AF_PPP; -pub const PF_MAX: c_int = AF_MAX; - -pub const SYSPROTO_EVENT: c_int = 1; -pub const SYSPROTO_CONTROL: c_int = 2; - -pub const SOCK_STREAM: c_int = 1; -pub const SOCK_DGRAM: c_int = 2; -pub const SOCK_RAW: c_int = 3; -pub const SOCK_RDM: c_int = 4; -pub const SOCK_SEQPACKET: c_int = 5; -pub const SOCK_MAXADDRLEN: c_int = 255; diff --git a/std/os/index.zig b/std/os/index.zig index 87053fd8df..77fd2a78ad 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -11,7 +11,7 @@ const os = this; test "std.os" { _ = @import("child_process.zig"); _ = @import("darwin.zig"); - _ = @import("darwin_errno.zig"); + _ = @import("darwin/errno.zig"); _ = @import("get_user_id.zig"); _ = @import("linux/index.zig"); _ = @import("path.zig"); -- cgit v1.2.3 From 07b6a3d335450778f69a6ee5afa5716e4fe83fe3 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 21 Jul 2018 23:33:28 +1200 Subject: Tighten Int.to bounds and add twos-complement bitcount --- std/math/big/int.zig | 103 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 91 insertions(+), 12 deletions(-) diff --git a/std/math/big/int.zig b/std/math/big/int.zig index caa9d0a7ed..026933bd64 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -115,13 +115,47 @@ pub const Int = struct { return !r.isOdd(); } - fn bitcount(self: Int) usize { - const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); - return usize(@boolToInt(!self.positive)) + u_bit_count; + // Returns the number of bits required to represent the absolute value of self. + fn bitCountAbs(self: Int) usize { + return (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); } + // Returns the number of bits required to represent the integer in twos-complement form. + // + // If the integer is negative the value returned is the number of bits needed by a signed + // integer to represent the value. If positive the value is the number of bits for an + // unsigned integer. Any unsigned integer will fit in the signed integer with bitcount + // one greater than the returned value. + // + // e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7. + fn bitCountTwosComp(self: Int) usize { + var bits = self.bitCountAbs(); + + // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos + // complement requires one less bit. + if (!self.positive) block: { + bits += 1; + + if (@popCount(self.limbs[self.len - 1]) == 1) { + for (self.limbs[0 .. self.len - 1]) |limb| { + if (@popCount(limb) != 0) { + break :block; + } + } + + bits -= 1; + } + } + + return bits; + } + + // Returns the approximate size of the integer in the given base. Negative values accomodate for + // the minus sign. This is used for determining the number of characters needed to print the + // value. It is inexact and will exceed the given value by 1-2 digits. pub fn sizeInBase(self: Int, base: usize) usize { - return (self.bitcount() / math.log2(base)) + 1; + const bit_count = usize(@boolToInt(!self.positive)) + self.bitCountAbs(); + return (bit_count / math.log2(base)) + 1; } pub fn set(self: *Int, value: var) Allocator.Error!void { @@ -189,9 +223,9 @@ pub const Int = struct { pub fn to(self: Int, comptime T: type) ConvertError!T { switch (@typeId(T)) { TypeId.Int => { - const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T; + const UT = @IntType(false, T.bit_count); - if (self.bitcount() > 8 * @sizeOf(UT)) { + if (self.bitCountTwosComp() > T.bit_count) { return error.TargetTooSmall; } @@ -208,9 +242,17 @@ pub const Int = struct { } if (!T.is_signed) { - return if (self.positive) r else error.NegativeIntoUnsigned; + return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned; } else { - return if (self.positive) @intCast(T, r) else -@intCast(T, r); + if (self.positive) { + return @intCast(T, r); + } else { + if (math.cast(T, r)) |ok| { + return -ok; + } else |_| { + return @minValue(T); + } + } } }, else => { @@ -1120,24 +1162,61 @@ test "big.int bitcount + sizeInBase" { var a = try Int.init(al); try a.set(0b100); - debug.assert(a.bitcount() == 3); + debug.assert(a.bitCountAbs() == 3); debug.assert(a.sizeInBase(2) >= 3); debug.assert(a.sizeInBase(10) >= 1); + a.negate(); + debug.assert(a.bitCountAbs() == 3); + debug.assert(a.sizeInBase(2) >= 4); + debug.assert(a.sizeInBase(10) >= 2); + try a.set(0xffffffff); - debug.assert(a.bitcount() == 32); + debug.assert(a.bitCountAbs() == 32); debug.assert(a.sizeInBase(2) >= 32); debug.assert(a.sizeInBase(10) >= 10); try a.shiftLeft(a, 5000); - debug.assert(a.bitcount() == 5032); + debug.assert(a.bitCountAbs() == 5032); debug.assert(a.sizeInBase(2) >= 5032); a.positive = false; - debug.assert(a.bitcount() == 5033); + debug.assert(a.bitCountAbs() == 5032); debug.assert(a.sizeInBase(2) >= 5033); } +test "big.int bitcount/to" { + var a = try Int.init(al); + + try a.set(0); + debug.assert(a.bitCountTwosComp() == 0); + + // TODO: stack smashing + // debug.assert((try a.to(u0)) == 0); + // TODO: sigsegv + // debug.assert((try a.to(i0)) == 0); + + try a.set(-1); + debug.assert(a.bitCountTwosComp() == 1); + debug.assert((try a.to(i1)) == -1); + + try a.set(-8); + debug.assert(a.bitCountTwosComp() == 4); + debug.assert((try a.to(i4)) == -8); + + try a.set(127); + debug.assert(a.bitCountTwosComp() == 7); + debug.assert((try a.to(u7)) == 127); + + try a.set(-128); + debug.assert(a.bitCountTwosComp() == 8); + debug.assert((try a.to(i8)) == -128); + + try a.set(-129); + debug.assert(a.bitCountTwosComp() == 9); + debug.assert((try a.to(i9)) == -129); +} + test "big.int string set" { var a = try Int.init(al); try a.setString(10, "120317241209124781241290847124"); -- cgit v1.2.3 From d53fae3551f6215b99f065e837f1f4439ba850fd Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Mon, 23 Jul 2018 02:11:27 +1200 Subject: Add big int fits function (#1279) Returns whether the current value in an Int fits in the requested type. --- std/math/big/int.zig | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 026933bd64..acd2a68746 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -150,6 +150,18 @@ pub const Int = struct { return bits; } + pub fn fits(self: Int, comptime T: type) bool { + if (self.eqZero()) { + return true; + } + if (!T.is_signed and !self.positive) { + return false; + } + + const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and T.is_signed); + return T.bit_count >= req_bits; + } + // Returns the approximate size of the integer in the given base. Negative values accomodate for // the minus sign. This is used for determining the number of characters needed to print the // value. It is inexact and will exceed the given value by 1-2 digits. @@ -1217,6 +1229,33 @@ test "big.int bitcount/to" { debug.assert((try a.to(i9)) == -129); } +test "big.int fits" { + var a = try Int.init(al); + + try a.set(0); + debug.assert(a.fits(u0)); + debug.assert(a.fits(i0)); + + try a.set(255); + debug.assert(!a.fits(u0)); + debug.assert(!a.fits(u1)); + debug.assert(!a.fits(i8)); + debug.assert(a.fits(u8)); + debug.assert(a.fits(u9)); + debug.assert(a.fits(i9)); + + try a.set(-128); + debug.assert(!a.fits(i7)); + debug.assert(a.fits(i8)); + debug.assert(a.fits(i9)); + debug.assert(!a.fits(u9)); + + try a.set(0x1ffffffffeeeeeeee); + debug.assert(!a.fits(u32)); + debug.assert(!a.fits(u64)); + debug.assert(a.fits(u65)); +} + test "big.int string set" { var a = try Int.init(al); try a.setString(10, "120317241209124781241290847124"); -- cgit v1.2.3 From 99153ac0aa390f01091308073b39947c45851ae6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Jul 2018 10:58:45 -0400 Subject: add std.math.big.Int.fitsInTwosComp so that we can pass runtime-known values --- std/math/big/int.zig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/std/math/big/int.zig b/std/math/big/int.zig index acd2a68746..09bbfe5865 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -150,16 +150,20 @@ pub const Int = struct { return bits; } - pub fn fits(self: Int, comptime T: type) bool { + pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool { if (self.eqZero()) { return true; } - if (!T.is_signed and !self.positive) { + if (!is_signed and !self.positive) { return false; } - const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and T.is_signed); - return T.bit_count >= req_bits; + const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and is_signed); + return bit_count >= req_bits; + } + + pub fn fits(self: Int, comptime T: type) bool { + return self.fitsInTwosComp(T.is_signed, T.bit_count); } // Returns the approximate size of the integer in the given base. Negative values accomodate for -- cgit v1.2.3 From 93e78ee72259b98840f63db0ad87fdddb071e384 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Jul 2018 23:27:58 -0400 Subject: self-hosted can compile libc hello world --- CMakeLists.txt | 1 + src-self-hosted/codegen.zig | 6 +- src-self-hosted/compilation.zig | 185 +++++++++---- src-self-hosted/decl.zig | 8 +- src-self-hosted/errmsg.zig | 9 +- src-self-hosted/ir.zig | 544 +++++++++++++++++++++++++++++++++++---- src-self-hosted/llvm.zig | 38 ++- src-self-hosted/scope.zig | 25 +- src-self-hosted/test.zig | 1 + src-self-hosted/type.zig | 364 +++++++++++++++++++++----- src-self-hosted/value.zig | 280 +++++++++++++++++++- std/event/group.zig | 1 + std/zig/index.zig | 3 + std/zig/parse_string_literal.zig | 76 ++++++ std/zig/tokenizer.zig | 1 + 15 files changed, 1358 insertions(+), 184 deletions(-) create mode 100644 std/zig/parse_string_literal.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 20755cfc1b..c3f0721bb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -624,6 +624,7 @@ set(ZIG_STD_FILES "zig/ast.zig" "zig/index.zig" "zig/parse.zig" + "zig/parse_string_literal.zig" "zig/render.zig" "zig/tokenizer.zig" ) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 8faef5f31c..ad3dce061e 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -78,6 +78,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) .dibuilder = dibuilder, .context = context, .lock = event.Lock.init(comp.loop), + .arena = &code.arena.allocator, }; try renderToLlvmModule(&ofile, fn_val, code); @@ -139,6 +140,7 @@ pub const ObjectFile = struct { dibuilder: *llvm.DIBuilder, context: llvm.ContextRef, lock: event.Lock, + arena: *std.mem.Allocator, fn gpa(self: *ObjectFile) *std.mem.Allocator { return self.comp.gpa(); @@ -147,7 +149,7 @@ pub const ObjectFile = struct { pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void { // TODO audit more of codegen.cpp:fn_llvm_value and port more logic - const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile); + const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context); const llvm_fn = llvm.AddFunction( ofile.module, fn_val.symbol_name.ptr(), @@ -165,7 +167,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack); //} - const fn_type = fn_val.base.typeof.cast(Type.Fn).?; + const fn_type = fn_val.base.typ.cast(Type.Fn).?; try addLLVMFnAttr(ofile, llvm_fn, "nounwind"); //add_uwtable_attr(g, fn_table_entry->llvm_value); diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 46cbf14141..3adeb55b56 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -194,6 +194,7 @@ pub const Compilation = struct { bool_type: *Type.Bool, noreturn_type: *Type.NoReturn, comptime_int_type: *Type.ComptimeInt, + u8_type: *Type.Int, void_value: *Value.Void, true_value: *Value.Bool, @@ -203,6 +204,7 @@ pub const Compilation = struct { target_machine: llvm.TargetMachineRef, target_data_ref: llvm.TargetDataRef, target_layout_str: [*]u8, + target_ptr_bits: u32, /// for allocating things which have the same lifetime as this Compilation arena_allocator: std.heap.ArenaAllocator, @@ -223,10 +225,14 @@ pub const Compilation = struct { primitive_type_table: TypeTable, int_type_table: event.Locked(IntTypeTable), + array_type_table: event.Locked(ArrayTypeTable), + ptr_type_table: event.Locked(PtrTypeTable), c_int_types: [CInt.list.len]*Type.Int, const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql); + const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql); + const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql); const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8); const CompileErrList = std.ArrayList(*errmsg.Msg); @@ -383,6 +389,8 @@ pub const Compilation = struct { .deinit_group = event.Group(void).init(loop), .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)), + .array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)), + .ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)), .c_int_types = undefined, .meta_type = undefined, @@ -394,10 +402,12 @@ pub const Compilation = struct { .noreturn_type = undefined, .noreturn_value = undefined, .comptime_int_type = undefined, + .u8_type = undefined, .target_machine = undefined, .target_data_ref = undefined, .target_layout_str = undefined, + .target_ptr_bits = target.getArchPtrBitWidth(), .root_package = undefined, .std_package = undefined, @@ -409,6 +419,8 @@ pub const Compilation = struct { }); errdefer { comp.int_type_table.private_data.deinit(); + comp.array_type_table.private_data.deinit(); + comp.ptr_type_table.private_data.deinit(); comp.arena_allocator.deinit(); comp.loop.allocator.destroy(comp); } @@ -517,15 +529,16 @@ pub const Compilation = struct { .name = "type", .base = Value{ .id = Value.Id.Type, - .typeof = undefined, + .typ = undefined, .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice }, .id = builtin.TypeId.Type, + .abi_alignment = Type.AbiAlignment.init(comp.loop), }, .value = undefined, }); comp.meta_type.value = &comp.meta_type.base; - comp.meta_type.base.base.typeof = &comp.meta_type.base; + comp.meta_type.base.base.typ = &comp.meta_type.base; assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null); comp.void_type = try comp.arena().create(Type.Void{ @@ -533,10 +546,11 @@ pub const Compilation = struct { .name = "void", .base = Value{ .id = Value.Id.Type, - .typeof = &Type.MetaType.get(comp).base, + .typ = &Type.MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .id = builtin.TypeId.Void, + .abi_alignment = Type.AbiAlignment.init(comp.loop), }, }); assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null); @@ -546,10 +560,11 @@ pub const Compilation = struct { .name = "noreturn", .base = Value{ .id = Value.Id.Type, - .typeof = &Type.MetaType.get(comp).base, + .typ = &Type.MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .id = builtin.TypeId.NoReturn, + .abi_alignment = Type.AbiAlignment.init(comp.loop), }, }); assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null); @@ -559,10 +574,11 @@ pub const Compilation = struct { .name = "comptime_int", .base = Value{ .id = Value.Id.Type, - .typeof = &Type.MetaType.get(comp).base, + .typ = &Type.MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .id = builtin.TypeId.ComptimeInt, + .abi_alignment = Type.AbiAlignment.init(comp.loop), }, }); assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null); @@ -572,10 +588,11 @@ pub const Compilation = struct { .name = "bool", .base = Value{ .id = Value.Id.Type, - .typeof = &Type.MetaType.get(comp).base, + .typ = &Type.MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .id = builtin.TypeId.Bool, + .abi_alignment = Type.AbiAlignment.init(comp.loop), }, }); assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null); @@ -583,7 +600,7 @@ pub const Compilation = struct { comp.void_value = try comp.arena().create(Value.Void{ .base = Value{ .id = Value.Id.Void, - .typeof = &Type.Void.get(comp).base, + .typ = &Type.Void.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, }); @@ -591,7 +608,7 @@ pub const Compilation = struct { comp.true_value = try comp.arena().create(Value.Bool{ .base = Value{ .id = Value.Id.Bool, - .typeof = &Type.Bool.get(comp).base, + .typ = &Type.Bool.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .x = true, @@ -600,7 +617,7 @@ pub const Compilation = struct { comp.false_value = try comp.arena().create(Value.Bool{ .base = Value{ .id = Value.Id.Bool, - .typeof = &Type.Bool.get(comp).base, + .typ = &Type.Bool.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .x = false, @@ -609,7 +626,7 @@ pub const Compilation = struct { comp.noreturn_value = try comp.arena().create(Value.NoReturn{ .base = Value{ .id = Value.Id.NoReturn, - .typeof = &Type.NoReturn.get(comp).base, + .typ = &Type.NoReturn.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, }); @@ -620,10 +637,11 @@ pub const Compilation = struct { .name = cint.zig_name, .base = Value{ .id = Value.Id.Type, - .typeof = &Type.MetaType.get(comp).base, + .typ = &Type.MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .id = builtin.TypeId.Int, + .abi_alignment = Type.AbiAlignment.init(comp.loop), }, .key = Type.Int.Key{ .is_signed = cint.is_signed, @@ -634,6 +652,24 @@ pub const Compilation = struct { comp.c_int_types[i] = c_int_type; assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null); } + comp.u8_type = try comp.arena().create(Type.Int{ + .base = Type{ + .name = "u8", + .base = Value{ + .id = Value.Id.Type, + .typ = &Type.MetaType.get(comp).base, + .ref_count = std.atomic.Int(usize).init(1), + }, + .id = builtin.TypeId.Int, + .abi_alignment = Type.AbiAlignment.init(comp.loop), + }, + .key = Type.Int.Key{ + .is_signed = false, + .bit_count = 8, + }, + .garbage_node = undefined, + }); + assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null); } /// This function can safely use async/await, because it manages Compilation's lifetime, @@ -750,7 +786,7 @@ pub const Compilation = struct { ast.Node.Id.Comptime => { const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl); - try decl_group.call(addCompTimeBlock, self, &decls.base, comptime_node); + try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node); }, ast.Node.Id.VarDecl => @panic("TODO"), ast.Node.Id.FnProto => { @@ -770,7 +806,6 @@ pub const Compilation = struct { .name = name, .visib = parseVisibToken(tree, fn_proto.visib_token), .resolution = event.Future(BuildError!void).init(self.loop), - .resolution_in_progress = 0, .parent_scope = &decls.base, }, .value = Decl.Fn.Val{ .Unresolved = {} }, @@ -778,16 +813,22 @@ pub const Compilation = struct { }); errdefer self.gpa().destroy(fn_decl); - try decl_group.call(addTopLevelDecl, self, &fn_decl.base); + try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base); }, ast.Node.Id.TestDecl => @panic("TODO"), else => unreachable, } } try await (async decl_group.wait() catch unreachable); + + // Now other code can rely on the decls scope having a complete list of names. + decls.name_future.resolve(); } - try await (async self.prelink_group.wait() catch unreachable); + (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) { + error.SemanticAnalysisFailed => {}, + else => return err, + }; const any_prelink_errors = blk: { const compile_errors = await (async self.compile_errors.acquire() catch unreachable); @@ -857,14 +898,31 @@ pub const Compilation = struct { analyzed_code.destroy(comp.gpa()); } - async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void { + async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void { const tree = &decl.findRootScope().tree; const is_export = decl.isExported(tree); + var add_to_table_resolved = false; + const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable; + errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261 + if (is_export) { try self.prelink_group.call(verifyUniqueSymbol, self, decl); try self.prelink_group.call(resolveDecl, self, decl); } + + add_to_table_resolved = true; + try await add_to_table; + } + + async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void { + const held = await (async decls.table.acquire() catch unreachable); + defer held.release(); + + if (try held.value.put(decl.name, decl)) |other_decl| { + try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name); + // TODO note: other definition here + } } fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void { @@ -1043,6 +1101,15 @@ pub const Compilation = struct { return result_val.cast(Type).?; } + + /// This declaration has been blessed as going into the final code generation. + pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void { + if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*; + + decl.resolution.data = try await (async generateDecl(comp, decl) catch unreachable); + decl.resolution.resolve(); + return decl.resolution.data; + } }; fn printError(comptime format: []const u8, args: ...) !void { @@ -1062,20 +1129,6 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib } } -/// This declaration has been blessed as going into the final code generation. -pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void { - if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*; - - decl.resolution.data = (await (async generateDecl(comp, decl) catch unreachable)) catch |err| switch (err) { - // This poison value should not cause the errdefers to run. It simply means - // that comp.compile_errors is populated. - error.SemanticAnalysisFailed => {}, - else => err, - }; - decl.resolution.resolve(); - return decl.resolution.data; -} - /// The function that actually does the generation. async fn generateDecl(comp: *Compilation, decl: *Decl) !void { switch (decl.id) { @@ -1089,34 +1142,27 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void { } async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { - const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl"); + const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable); const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope); defer fndef_scope.base.deref(comp); - const return_type_node = switch (fn_decl.fn_proto.return_type) { - ast.Node.FnProto.ReturnType.Explicit => |n| n, - ast.Node.FnProto.ReturnType.InferErrorSet => |n| n, - }; - const return_type = try await (async comp.analyzeTypeExpr(&fndef_scope.base, return_type_node) catch unreachable); - return_type.base.deref(comp); - - const is_var_args = false; - const params = ([*]Type.Fn.Param)(undefined)[0..0]; - const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args); + const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable); defer fn_type.base.base.deref(comp); var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name); - errdefer symbol_name.deinit(); + var symbol_name_consumed = false; + errdefer if (!symbol_name_consumed) symbol_name.deinit(); // The Decl.Fn owns the initial 1 reference count const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); - fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; + fn_decl.value = Decl.Fn.Val{ .Fn = fn_val }; + symbol_name_consumed = true; const analyzed_code = try await (async comp.genAndAnalyzeCode( &fndef_scope.base, body_node, - return_type, + fn_type.return_type, ) catch unreachable); errdefer analyzed_code.destroy(comp.gpa()); @@ -1141,3 +1187,54 @@ async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void { fn getZigDir(allocator: *mem.Allocator) ![]u8 { return os.getAppDataDir(allocator, "zig"); } + +async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn { + const return_type_node = switch (fn_proto.return_type) { + ast.Node.FnProto.ReturnType.Explicit => |n| n, + ast.Node.FnProto.ReturnType.InferErrorSet => |n| n, + }; + const return_type = try await (async comp.analyzeTypeExpr(scope, return_type_node) catch unreachable); + return_type.base.deref(comp); + + var params = ArrayList(Type.Fn.Param).init(comp.gpa()); + var params_consumed = false; + defer if (params_consumed) { + for (params.toSliceConst()) |param| { + param.typ.base.deref(comp); + } + params.deinit(); + }; + + const is_var_args = false; + { + var it = fn_proto.params.iterator(0); + while (it.next()) |param_node_ptr| { + const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?; + const param_type = try await (async comp.analyzeTypeExpr(scope, param_node.type_node) catch unreachable); + errdefer param_type.base.deref(comp); + try params.append(Type.Fn.Param{ + .typ = param_type, + .is_noalias = param_node.noalias_token != null, + }); + } + } + const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args); + params_consumed = true; + errdefer fn_type.base.base.deref(comp); + + return fn_type; +} + +async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void { + const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable); + defer fn_type.base.base.deref(comp); + + var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name); + var symbol_name_consumed = false; + defer if (!symbol_name_consumed) symbol_name.deinit(); + + // The Decl.Fn owns the initial 1 reference count + const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name); + fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val }; + symbol_name_consumed = true; +} diff --git a/src-self-hosted/decl.zig b/src-self-hosted/decl.zig index bb065640c2..6e80243038 100644 --- a/src-self-hosted/decl.zig +++ b/src-self-hosted/decl.zig @@ -15,7 +15,6 @@ pub const Decl = struct { name: []const u8, visib: Visib, resolution: event.Future(Compilation.BuildError!void), - resolution_in_progress: u8, parent_scope: *Scope, pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); @@ -63,12 +62,13 @@ pub const Decl = struct { pub const Fn = struct { base: Decl, value: Val, - fn_proto: *const ast.Node.FnProto, + fn_proto: *ast.Node.FnProto, // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous - pub const Val = union { + pub const Val = union(enum) { Unresolved: void, - Ok: *Value.Fn, + Fn: *Value.Fn, + FnProto: *Value.FnProto, }; pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index 4e353bfb14..eec4490a81 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -16,11 +16,18 @@ pub const Span = struct { last: ast.TokenIndex, pub fn token(i: TokenIndex) Span { - return Span { + return Span{ .first = i, .last = i, }; } + + pub fn node(n: *ast.Node) Span { + return Span{ + .first = n.firstToken(), + .last = n.lastToken(), + }; + } }; pub const Msg = struct { diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 04023980e6..d624fe4dac 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -11,6 +11,7 @@ const Token = std.zig.Token; const Span = @import("errmsg.zig").Span; const llvm = @import("llvm.zig"); const ObjectFile = @import("codegen.zig").ObjectFile; +const Decl = @import("decl.zig").Decl; pub const LVal = enum { None, @@ -30,10 +31,10 @@ pub const IrVal = union(enum) { pub fn dump(self: IrVal) void { switch (self) { - IrVal.Unknown => typeof.dump(), - IrVal.KnownType => |typeof| { + IrVal.Unknown => std.debug.warn("Unknown"), + IrVal.KnownType => |typ| { std.debug.warn("KnownType("); - typeof.dump(); + typ.dump(); std.debug.warn(")"); }, IrVal.KnownValue => |value| { @@ -108,21 +109,29 @@ pub const Inst = struct { unreachable; } - pub fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst { - comptime var i = 0; - inline while (i < @memberCount(Id)) : (i += 1) { - if (base.id == @field(Id, @memberName(Id, i))) { - const T = @field(Inst, @memberName(Id, i)); - return @fieldParentPtr(T, "base", base).analyze(ira); - } + pub async fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst { + switch (base.id) { + Id.Return => return @fieldParentPtr(Return, "base", base).analyze(ira), + Id.Const => return @fieldParentPtr(Const, "base", base).analyze(ira), + Id.Call => return @fieldParentPtr(Call, "base", base).analyze(ira), + Id.DeclRef => return await (async @fieldParentPtr(DeclRef, "base", base).analyze(ira) catch unreachable), + Id.Ref => return await (async @fieldParentPtr(Ref, "base", base).analyze(ira) catch unreachable), + Id.DeclVar => return @fieldParentPtr(DeclVar, "base", base).analyze(ira), + Id.CheckVoidStmt => return @fieldParentPtr(CheckVoidStmt, "base", base).analyze(ira), + Id.Phi => return @fieldParentPtr(Phi, "base", base).analyze(ira), + Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira), + Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira), + Id.PtrType => return await (async @fieldParentPtr(PtrType, "base", base).analyze(ira) catch unreachable), } - unreachable; } pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) { switch (base.id) { Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val), Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val), + Id.Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val), + Id.DeclRef => unreachable, + Id.PtrType => unreachable, Id.Ref => @panic("TODO"), Id.DeclVar => @panic("TODO"), Id.CheckVoidStmt => @panic("TODO"), @@ -135,7 +144,7 @@ pub const Inst = struct { fn ref(base: *Inst, builder: *Builder) void { base.ref_count += 1; if (base.owner_bb != builder.current_basic_block and !base.isCompTime()) { - base.owner_bb.ref(); + base.owner_bb.ref(builder); } } @@ -155,11 +164,51 @@ pub const Inst = struct { } } + fn getConstVal(self: *Inst, ira: *Analyze) !*Value { + if (self.isCompTime()) { + return self.val.KnownValue; + } else { + try ira.addCompileError(self.span, "unable to evaluate constant expression"); + return error.SemanticAnalysisFailed; + } + } + + fn getAsConstType(param: *Inst, ira: *Analyze) !*Type { + const meta_type = Type.MetaType.get(ira.irb.comp); + meta_type.base.base.deref(ira.irb.comp); + + const inst = try param.getAsParam(); + const casted = try ira.implicitCast(inst, &meta_type.base); + const val = try casted.getConstVal(ira); + return val.cast(Value.Type).?; + } + + fn getAsConstAlign(param: *Inst, ira: *Analyze) !u32 { + return error.Unimplemented; + //const align_type = Type.Int.get_align(ira.irb.comp); + //align_type.base.base.deref(ira.irb.comp); + + //const inst = try param.getAsParam(); + //const casted = try ira.implicitCast(inst, align_type); + //const val = try casted.getConstVal(ira); + + //uint32_t align_bytes = bigint_as_unsigned(&const_val->data.x_bigint); + //if (align_bytes == 0) { + // ir_add_error(ira, value, buf_sprintf("alignment must be >= 1")); + // return false; + //} + + //if (!is_power_of_2(align_bytes)) { + // ir_add_error(ira, value, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes)); + // return false; + //} + } + /// asserts that the type is known fn getKnownType(self: *Inst) *Type { switch (self.val) { - IrVal.KnownType => |typeof| return typeof, - IrVal.KnownValue => |value| return value.typeof, + IrVal.KnownType => |typ| return typ, + IrVal.KnownValue => |value| return value.typ, IrVal.Unknown => unreachable, } } @@ -171,8 +220,8 @@ pub const Inst = struct { pub fn isNoReturn(base: *const Inst) bool { switch (base.val) { IrVal.Unknown => return false, - IrVal.KnownValue => |x| return x.typeof.id == Type.Id.NoReturn, - IrVal.KnownType => |typeof| return typeof.id == Type.Id.NoReturn, + IrVal.KnownValue => |x| return x.typ.id == Type.Id.NoReturn, + IrVal.KnownType => |typ| return typ.id == Type.Id.NoReturn, } } @@ -196,6 +245,85 @@ pub const Inst = struct { Phi, Br, AddImplicitReturnType, + Call, + DeclRef, + PtrType, + }; + + pub const Call = struct { + base: Inst, + params: Params, + + const Params = struct { + fn_ref: *Inst, + args: []*Inst, + }; + + const ir_val_init = IrVal.Init.Unknown; + + pub fn dump(self: *const Call) void { + std.debug.warn("#{}(", self.params.fn_ref.debug_id); + for (self.params.args) |arg| { + std.debug.warn("#{},", arg.debug_id); + } + std.debug.warn(")"); + } + + pub fn hasSideEffects(self: *const Call) bool { + return true; + } + + pub fn analyze(self: *const Call, ira: *Analyze) !*Inst { + const fn_ref = try self.params.fn_ref.getAsParam(); + const fn_ref_type = fn_ref.getKnownType(); + const fn_type = fn_ref_type.cast(Type.Fn) orelse { + try ira.addCompileError(fn_ref.span, "type '{}' not a function", fn_ref_type.name); + return error.SemanticAnalysisFailed; + }; + + if (fn_type.params.len != self.params.args.len) { + try ira.addCompileError( + self.base.span, + "expected {} arguments, found {}", + fn_type.params.len, + self.params.args.len, + ); + return error.SemanticAnalysisFailed; + } + + const args = try ira.irb.arena().alloc(*Inst, self.params.args.len); + for (self.params.args) |arg, i| { + args[i] = try arg.getAsParam(); + } + const new_inst = try ira.irb.build(Call, self.base.scope, self.base.span, Params{ + .fn_ref = fn_ref, + .args = args, + }); + new_inst.val = IrVal{ .KnownType = fn_type.return_type }; + return new_inst; + } + + pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { + const fn_ref = self.params.fn_ref.llvm_value.?; + + const args = try ofile.arena.alloc(llvm.ValueRef, self.params.args.len); + for (self.params.args) |arg, i| { + args[i] = arg.llvm_value.?; + } + + const llvm_cc = llvm.CCallConv; + const fn_inline = llvm.FnInline.Auto; + + return llvm.BuildCall( + ofile.builder, + fn_ref, + args.ptr, + @intCast(c_uint, args.len), + llvm_cc, + fn_inline, + c"", + ) orelse error.OutOfMemory; + } }; pub const Const = struct { @@ -254,14 +382,14 @@ pub const Inst = struct { return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value }); } - pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) ?llvm.ValueRef { + pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { const value = self.params.return_value.llvm_value; const return_type = self.params.return_value.getKnownType(); if (return_type.handleIsPtr()) { @panic("TODO"); } else { - _ = llvm.BuildRet(ofile.builder, value); + _ = llvm.BuildRet(ofile.builder, value) orelse return error.OutOfMemory; } return null; } @@ -285,7 +413,7 @@ pub const Inst = struct { return false; } - pub fn analyze(self: *const Ref, ira: *Analyze) !*Inst { + pub async fn analyze(self: *const Ref, ira: *Analyze) !*Inst { const target = try self.params.target.getAsParam(); if (ira.getCompTimeValOrNullUndefOk(target)) |val| { @@ -294,7 +422,6 @@ pub const Inst = struct { Value.Ptr.Mut.CompTimeConst, self.params.mut, self.params.volatility, - val.typeof.getAbiAlignment(ira.irb.comp), ); } @@ -304,14 +431,13 @@ pub const Inst = struct { .volatility = self.params.volatility, }); const elem_type = target.getKnownType(); - const ptr_type = Type.Pointer.get( - ira.irb.comp, - elem_type, - self.params.mut, - self.params.volatility, - Type.Pointer.Size.One, - elem_type.getAbiAlignment(ira.irb.comp), - ); + const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ + .child_type = elem_type, + .mut = self.params.mut, + .vol = self.params.volatility, + .size = Type.Pointer.Size.One, + .alignment = Type.Pointer.Align.Abi, + }) catch unreachable); // TODO: potentially set the hint that this is a stack pointer. But it might not be - this // could be a ref of a global, for example new_inst.val = IrVal{ .KnownType = &ptr_type.base }; @@ -320,6 +446,97 @@ pub const Inst = struct { } }; + pub const DeclRef = struct { + base: Inst, + params: Params, + + const Params = struct { + decl: *Decl, + lval: LVal, + }; + + const ir_val_init = IrVal.Init.Unknown; + + pub fn dump(inst: *const DeclRef) void {} + + pub fn hasSideEffects(inst: *const DeclRef) bool { + return false; + } + + pub async fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst { + (await (async ira.irb.comp.resolveDecl(self.params.decl) catch unreachable)) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => return error.SemanticAnalysisFailed, + }; + switch (self.params.decl.id) { + Decl.Id.CompTime => unreachable, + Decl.Id.Var => return error.Unimplemented, + Decl.Id.Fn => { + const fn_decl = @fieldParentPtr(Decl.Fn, "base", self.params.decl); + const decl_val = switch (fn_decl.value) { + Decl.Fn.Val.Unresolved => unreachable, + Decl.Fn.Val.Fn => |fn_val| &fn_val.base, + Decl.Fn.Val.FnProto => |fn_proto| &fn_proto.base, + }; + switch (self.params.lval) { + LVal.None => { + return ira.irb.buildConstValue(self.base.scope, self.base.span, decl_val); + }, + LVal.Ptr => return error.Unimplemented, + } + }, + } + } + }; + + pub const PtrType = struct { + base: Inst, + params: Params, + + const Params = struct { + child_type: *Inst, + mut: Type.Pointer.Mut, + vol: Type.Pointer.Vol, + size: Type.Pointer.Size, + alignment: ?*Inst, + }; + + const ir_val_init = IrVal.Init.Unknown; + + pub fn dump(inst: *const PtrType) void {} + + pub fn hasSideEffects(inst: *const PtrType) bool { + return false; + } + + pub async fn analyze(self: *const PtrType, ira: *Analyze) !*Inst { + const child_type = try self.params.child_type.getAsConstType(ira); + // if (child_type->id == TypeTableEntryIdUnreachable) { + // ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed")); + // return ira->codegen->builtin_types.entry_invalid; + // } else if (child_type->id == TypeTableEntryIdOpaque && instruction->ptr_len == PtrLenUnknown) { + // ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque")); + // return ira->codegen->builtin_types.entry_invalid; + // } + const alignment = if (self.params.alignment) |align_inst| blk: { + const amt = try align_inst.getAsConstAlign(ira); + break :blk Type.Pointer.Align{ .Override = amt }; + } else blk: { + break :blk Type.Pointer.Align{ .Abi = {} }; + }; + const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ + .child_type = child_type, + .mut = self.params.mut, + .vol = self.params.vol, + .size = self.params.size, + .alignment = alignment, + }) catch unreachable); + ptr_type.base.base.deref(ira.irb.comp); + + return ira.irb.buildConstValue(self.base.scope, self.base.span, &ptr_type.base.base); + } + }; + pub const DeclVar = struct { base: Inst, params: Params, @@ -351,14 +568,21 @@ pub const Inst = struct { const ir_val_init = IrVal.Init.Unknown; - pub fn dump(inst: *const CheckVoidStmt) void {} + pub fn dump(self: *const CheckVoidStmt) void { + std.debug.warn("#{}", self.params.target.debug_id); + } pub fn hasSideEffects(inst: *const CheckVoidStmt) bool { return true; } pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Inst { - return error.Unimplemented; // TODO + const target = try self.params.target.getAsParam(); + if (target.getKnownType().id != Type.Id.Void) { + try ira.addCompileError(self.base.span, "expression value is ignored"); + return error.SemanticAnalysisFailed; + } + return ira.irb.buildConstVoid(self.base.scope, self.base.span, true); } }; @@ -583,7 +807,7 @@ pub const BasicBlock = struct { /// the basic block that this one derives from in analysis parent: ?*BasicBlock, - pub fn ref(self: *BasicBlock) void { + pub fn ref(self: *BasicBlock, builder: *Builder) void { self.ref_count += 1; } @@ -724,8 +948,42 @@ pub const Builder = struct { ast.Node.Id.VarDecl => return error.Unimplemented, ast.Node.Id.Defer => return error.Unimplemented, ast.Node.Id.InfixOp => return error.Unimplemented, - ast.Node.Id.PrefixOp => return error.Unimplemented, - ast.Node.Id.SuffixOp => return error.Unimplemented, + ast.Node.Id.PrefixOp => { + const prefix_op = @fieldParentPtr(ast.Node.PrefixOp, "base", node); + switch (prefix_op.op) { + ast.Node.PrefixOp.Op.AddressOf => return error.Unimplemented, + ast.Node.PrefixOp.Op.ArrayType => |n| return error.Unimplemented, + ast.Node.PrefixOp.Op.Await => return error.Unimplemented, + ast.Node.PrefixOp.Op.BitNot => return error.Unimplemented, + ast.Node.PrefixOp.Op.BoolNot => return error.Unimplemented, + ast.Node.PrefixOp.Op.Cancel => return error.Unimplemented, + ast.Node.PrefixOp.Op.OptionalType => return error.Unimplemented, + ast.Node.PrefixOp.Op.Negation => return error.Unimplemented, + ast.Node.PrefixOp.Op.NegationWrap => return error.Unimplemented, + ast.Node.PrefixOp.Op.Resume => return error.Unimplemented, + ast.Node.PrefixOp.Op.PtrType => |ptr_info| { + const inst = try await (async irb.genPtrType(prefix_op, ptr_info, scope) catch unreachable); + return irb.lvalWrap(scope, inst, lval); + }, + ast.Node.PrefixOp.Op.SliceType => |ptr_info| return error.Unimplemented, + ast.Node.PrefixOp.Op.Try => return error.Unimplemented, + } + }, + ast.Node.Id.SuffixOp => { + const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node); + switch (suffix_op.op) { + @TagType(ast.Node.SuffixOp.Op).Call => |*call| { + const inst = try await (async irb.genCall(suffix_op, call, scope) catch unreachable); + return irb.lvalWrap(scope, inst, lval); + }, + @TagType(ast.Node.SuffixOp.Op).ArrayAccess => |n| return error.Unimplemented, + @TagType(ast.Node.SuffixOp.Op).Slice => |slice| return error.Unimplemented, + @TagType(ast.Node.SuffixOp.Op).ArrayInitializer => |init_list| return error.Unimplemented, + @TagType(ast.Node.SuffixOp.Op).StructInitializer => |init_list| return error.Unimplemented, + @TagType(ast.Node.SuffixOp.Op).Deref => return error.Unimplemented, + @TagType(ast.Node.SuffixOp.Op).UnwrapOptional => return error.Unimplemented, + } + }, ast.Node.Id.Switch => return error.Unimplemented, ast.Node.Id.While => return error.Unimplemented, ast.Node.Id.For => return error.Unimplemented, @@ -744,7 +1002,11 @@ pub const Builder = struct { return irb.lvalWrap(scope, try irb.genIntLit(int_lit, scope), lval); }, ast.Node.Id.FloatLiteral => return error.Unimplemented, - ast.Node.Id.StringLiteral => return error.Unimplemented, + ast.Node.Id.StringLiteral => { + const str_lit = @fieldParentPtr(ast.Node.StringLiteral, "base", node); + const inst = try await (async irb.genStrLit(str_lit, scope) catch unreachable); + return irb.lvalWrap(scope, inst, lval); + }, ast.Node.Id.MultilineStringLiteral => return error.Unimplemented, ast.Node.Id.CharLiteral => return error.Unimplemented, ast.Node.Id.BoolLiteral => return error.Unimplemented, @@ -789,6 +1051,99 @@ pub const Builder = struct { } } + async fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst { + const fn_ref = try await (async irb.genNode(suffix_op.lhs, scope, LVal.None) catch unreachable); + + const args = try irb.arena().alloc(*Inst, call.params.len); + var it = call.params.iterator(0); + var i: usize = 0; + while (it.next()) |arg_node_ptr| : (i += 1) { + args[i] = try await (async irb.genNode(arg_node_ptr.*, scope, LVal.None) catch unreachable); + } + + //bool is_async = node->data.fn_call_expr.is_async; + //IrInstruction *async_allocator = nullptr; + //if (is_async) { + // if (node->data.fn_call_expr.async_allocator) { + // async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope); + // if (async_allocator == irb->codegen->invalid_instruction) + // return async_allocator; + // } + //} + + return irb.build(Inst.Call, scope, Span.token(suffix_op.rtoken), Inst.Call.Params{ + .fn_ref = fn_ref, + .args = args, + }); + //IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr); + //return ir_lval_wrap(irb, scope, fn_call, lval); + } + + async fn genPtrType( + irb: *Builder, + prefix_op: *ast.Node.PrefixOp, + ptr_info: ast.Node.PrefixOp.PtrInfo, + scope: *Scope, + ) !*Inst { + // TODO port more logic + + //assert(node->type == NodeTypePointerType); + //PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar || + // node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown; + //bool is_const = node->data.pointer_type.is_const; + //bool is_volatile = node->data.pointer_type.is_volatile; + //AstNode *expr_node = node->data.pointer_type.op_expr; + //AstNode *align_expr = node->data.pointer_type.align_expr; + + //IrInstruction *align_value; + //if (align_expr != nullptr) { + // align_value = ir_gen_node(irb, align_expr, scope); + // if (align_value == irb->codegen->invalid_instruction) + // return align_value; + //} else { + // align_value = nullptr; + //} + const child_type = try await (async irb.genNode(prefix_op.rhs, scope, LVal.None) catch unreachable); + + //uint32_t bit_offset_start = 0; + //if (node->data.pointer_type.bit_offset_start != nullptr) { + // if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) { + // Buf *val_buf = buf_alloc(); + // bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10); + // exec_add_error_node(irb->codegen, irb->exec, node, + // buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); + // return irb->codegen->invalid_instruction; + // } + // bit_offset_start = bigint_as_unsigned(node->data.pointer_type.bit_offset_start); + //} + + //uint32_t bit_offset_end = 0; + //if (node->data.pointer_type.bit_offset_end != nullptr) { + // if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_end, 32, false)) { + // Buf *val_buf = buf_alloc(); + // bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_end, 10); + // exec_add_error_node(irb->codegen, irb->exec, node, + // buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); + // return irb->codegen->invalid_instruction; + // } + // bit_offset_end = bigint_as_unsigned(node->data.pointer_type.bit_offset_end); + //} + + //if ((bit_offset_start != 0 || bit_offset_end != 0) && bit_offset_start >= bit_offset_end) { + // exec_add_error_node(irb->codegen, irb->exec, node, + // buf_sprintf("bit offset start must be less than bit offset end")); + // return irb->codegen->invalid_instruction; + //} + + return irb.build(Inst.PtrType, scope, Span.node(&prefix_op.base), Inst.PtrType.Params{ + .child_type = child_type, + .mut = Type.Pointer.Mut.Mut, + .vol = Type.Pointer.Vol.Non, + .size = Type.Pointer.Size.Many, + .alignment = null, + }); + } + fn isCompTime(irb: *Builder, target_scope: *Scope) bool { if (irb.is_comptime) return true; @@ -847,6 +1202,56 @@ pub const Builder = struct { return inst; } + pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst { + const str_token = irb.root_scope.tree.tokenSlice(str_lit.token); + const src_span = Span.token(str_lit.token); + + var bad_index: usize = undefined; + var buf = std.zig.parseStringLiteral(irb.comp.gpa(), str_token, &bad_index) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.InvalidCharacter => { + try irb.comp.addCompileError( + irb.root_scope, + src_span, + "invalid character in string literal: '{c}'", + str_token[bad_index], + ); + return error.SemanticAnalysisFailed; + }, + }; + var buf_cleaned = false; + errdefer if (!buf_cleaned) irb.comp.gpa().free(buf); + + if (str_token[0] == 'c') { + // first we add a null + buf = try irb.comp.gpa().realloc(u8, buf, buf.len + 1); + buf[buf.len - 1] = 0; + + // next make an array value + const array_val = try await (async Value.Array.createOwnedBuffer(irb.comp, buf) catch unreachable); + buf_cleaned = true; + defer array_val.base.deref(irb.comp); + + // then make a pointer value pointing at the first element + const ptr_val = try await (async Value.Ptr.createArrayElemPtr( + irb.comp, + array_val, + Type.Pointer.Mut.Const, + Type.Pointer.Size.Many, + 0, + ) catch unreachable); + defer ptr_val.base.deref(irb.comp); + + return irb.buildConstValue(scope, src_span, &ptr_val.base); + } else { + const array_val = try await (async Value.Array.createOwnedBuffer(irb.comp, buf) catch unreachable); + buf_cleaned = true; + defer array_val.base.deref(irb.comp); + + return irb.buildConstValue(scope, src_span, &array_val.base); + } + } + pub async fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst { const block_scope = try Scope.Block.create(irb.comp, parent_scope); @@ -911,7 +1316,10 @@ pub const Builder = struct { _ = irb.build( Inst.CheckVoidStmt, child_scope, - statement_value.span, + Span{ + .first = statement_node.firstToken(), + .last = statement_node.lastToken(), + }, Inst.CheckVoidStmt.Params{ .target = statement_value }, ); } @@ -1068,6 +1476,8 @@ pub const Builder = struct { if (result) |primitive_type| { defer primitive_type.base.deref(irb.comp); switch (lval) { + // if (lval == LValPtr) { + // return ir_build_ref(irb, scope, node, value, false, false); LVal.Ptr => return error.Unimplemented, LVal.None => return irb.buildConstValue(scope, src_span, &primitive_type.base), } @@ -1079,15 +1489,6 @@ pub const Builder = struct { }, error.OutOfMemory => return error.OutOfMemory, } - //TypeTableEntry *primitive_type = get_primitive_type(irb->codegen, variable_name); - //if (primitive_type != nullptr) { - // IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); - // if (lval == LValPtr) { - // return ir_build_ref(irb, scope, node, value, false, false); - // } else { - // return value; - // } - //} //VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name); //if (var) { @@ -1098,9 +1499,12 @@ pub const Builder = struct { // return ir_build_load_ptr(irb, scope, node, var_ptr); //} - //Tld *tld = find_decl(irb->codegen, scope, variable_name); - //if (tld) - // return ir_build_decl_ref(irb, scope, node, tld, lval); + if (await (async irb.findDecl(scope, name) catch unreachable)) |decl| { + return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{ + .decl = decl, + .lval = lval, + }); + } //if (node->owner->any_imports_failed) { // // skip the error message since we had a failing import in this file @@ -1251,8 +1655,26 @@ pub const Builder = struct { const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i))); switch (FieldType) { *Inst => @field(inst.params, @memberName(I.Params, i)).ref(self), + *BasicBlock => @field(inst.params, @memberName(I.Params, i)).ref(self), ?*Inst => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref(self), - else => {}, + []*Inst => { + // TODO https://github.com/ziglang/zig/issues/1269 + for (@field(inst.params, @memberName(I.Params, i))) |other| + other.ref(self); + }, + []*BasicBlock => { + // TODO https://github.com/ziglang/zig/issues/1269 + for (@field(inst.params, @memberName(I.Params, i))) |other| + other.ref(self); + }, + Type.Pointer.Mut, + Type.Pointer.Vol, + Type.Pointer.Size, + LVal, + *Decl, + => {}, + // it's ok to add more types here, just make sure any instructions are ref'd appropriately + else => @compileError("unrecognized type in Params: " ++ @typeName(FieldType)), } } @@ -1348,6 +1770,24 @@ pub const Builder = struct { // is_comptime); //// the above blocks are rendered by ir_gen after the rest of codegen } + + async fn findDecl(irb: *Builder, scope: *Scope, name: []const u8) ?*Decl { + var s = scope; + while (true) { + switch (s.id) { + Scope.Id.Decls => { + const decls = @fieldParentPtr(Scope.Decls, "base", s); + const table = await (async decls.getTableReadOnly() catch unreachable); + if (table.get(name)) |entry| { + return entry.value; + } + }, + Scope.Id.Root => return null, + else => {}, + } + s = s.parent.?; + } + } }; const Analyze = struct { @@ -1930,7 +2370,6 @@ const Analyze = struct { ptr_mut: Value.Ptr.Mut, mut: Type.Pointer.Mut, volatility: Type.Pointer.Vol, - ptr_align: u32, ) Analyze.Error!*Inst { return error.Unimplemented; } @@ -1945,7 +2384,7 @@ pub async fn gen( errdefer irb.abort(); const entry_block = try irb.createBasicBlock(scope, c"Entry"); - entry_block.ref(); // Entry block gets a reference because we enter it to begin. + entry_block.ref(&irb); // Entry block gets a reference because we enter it to begin. try irb.setCursorAtEndAndAppendBlock(entry_block); const result = try await (async irb.genNode(body_node, scope, LVal.None) catch unreachable); @@ -1965,7 +2404,7 @@ pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) errdefer ira.abort(); const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null); - new_entry_bb.ref(); + new_entry_bb.ref(&ira.irb); ira.irb.current_basic_block = new_entry_bb; @@ -1979,7 +2418,8 @@ pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) continue; } - const return_inst = try old_instruction.analyze(&ira); + const return_inst = try await (async old_instruction.analyze(&ira) catch unreachable); + assert(return_inst.val != IrVal.Unknown); // at least the type should be known at this point return_inst.linkToParent(old_instruction); // Note: if we ever modify the above to handle error.CompileError by continuing analysis, // then here we want to check if ira.isCompTime() and return early if true diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index ade2479f41..8bb45ac616 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -23,12 +23,17 @@ pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef); pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef); pub const DIBuilder = c.ZigLLVMDIBuilder; +pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType; pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex; pub const AddFunction = c.LLVMAddFunction; +pub const AddGlobal = c.LLVMAddGlobal; pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag; pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag; +pub const ArrayType = c.LLVMArrayType; pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; pub const ConstAllOnes = c.LLVMConstAllOnes; +pub const ConstArray = c.LLVMConstArray; +pub const ConstBitCast = c.LLVMConstBitCast; pub const ConstInt = c.LLVMConstInt; pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision; pub const ConstNeg = c.LLVMConstNeg; @@ -59,6 +64,7 @@ pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName; pub const GetHostCPUName = c.ZigLLVMGetHostCPUName; pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext; pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures; +pub const GetUndef = c.LLVMGetUndef; pub const HalfTypeInContext = c.LLVMHalfTypeInContext; pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers; pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters; @@ -81,14 +87,24 @@ pub const MDStringInContext = c.LLVMMDStringInContext; pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext; pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext; +pub const PointerType = c.LLVMPointerType; +pub const SetAlignment = c.LLVMSetAlignment; pub const SetDataLayout = c.LLVMSetDataLayout; +pub const SetGlobalConstant = c.LLVMSetGlobalConstant; +pub const SetInitializer = c.LLVMSetInitializer; +pub const SetLinkage = c.LLVMSetLinkage; pub const SetTarget = c.LLVMSetTarget; +pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr; pub const StructTypeInContext = c.LLVMStructTypeInContext; pub const TokenTypeInContext = c.LLVMTokenTypeInContext; +pub const TypeOf = c.LLVMTypeOf; pub const VoidTypeInContext = c.LLVMVoidTypeInContext; pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; +pub const ConstInBoundsGEP = LLVMConstInBoundsGEP; +pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef; + pub const GetTargetFromTriple = LLVMGetTargetFromTriple; extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool; @@ -145,13 +161,28 @@ pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary; pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr; pub const EmitOutputType = c.ZigLLVM_EmitOutputType; +pub const CCallConv = c.LLVMCCallConv; +pub const FastCallConv = c.LLVMFastCallConv; +pub const ColdCallConv = c.LLVMColdCallConv; +pub const WebKitJSCallConv = c.LLVMWebKitJSCallConv; +pub const AnyRegCallConv = c.LLVMAnyRegCallConv; +pub const X86StdcallCallConv = c.LLVMX86StdcallCallConv; +pub const X86FastcallCallConv = c.LLVMX86FastcallCallConv; +pub const CallConv = c.LLVMCallConv; + +pub const FnInline = extern enum { + Auto, + Always, + Never, +}; + fn removeNullability(comptime T: type) type { comptime assert(@typeId(T) == builtin.TypeId.Optional); return T.Child; } pub const BuildRet = LLVMBuildRet; -extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef; +extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef; pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile; extern fn ZigLLVMTargetMachineEmitToFile( @@ -163,3 +194,8 @@ extern fn ZigLLVMTargetMachineEmitToFile( is_debug: bool, is_small: bool, ) bool; + +pub const BuildCall = ZigLLVMBuildCall; +extern fn ZigLLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: [*]ValueRef, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?ValueRef; + +pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage; diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 7733e61600..878ddc1495 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -9,6 +9,7 @@ const Value = @import("value.zig").Value; const ir = @import("ir.zig"); const Span = @import("errmsg.zig").Span; const assert = std.debug.assert; +const event = std.event; pub const Scope = struct { id: Id, @@ -123,7 +124,15 @@ pub const Scope = struct { pub const Decls = struct { base: Scope, - table: Decl.Table, + + /// The lock must be respected for writing. However once name_future resolves, + /// readers can freely access it. + table: event.Locked(Decl.Table), + + /// Once this future is resolved, the table is complete and available for unlocked + /// read-only access. It does not mean all the decls are resolved; it means only that + /// the table has all the names. Each decl in the table has its own resolution state. + name_future: event.Future(void), /// Creates a Decls scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope) !*Decls { @@ -133,15 +142,10 @@ pub const Scope = struct { .parent = parent, .ref_count = 1, }, - .table = undefined, + .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())), + .name_future = event.Future(void).init(comp.loop), }); - errdefer comp.gpa().destroy(self); - - self.table = Decl.Table.init(comp.gpa()); - errdefer self.table.deinit(); - parent.ref(); - return self; } @@ -149,6 +153,11 @@ pub const Scope = struct { self.table.deinit(); comp.gpa().destroy(self); } + + pub async fn getTableReadOnly(self: *Decls) *Decl.Table { + _ = await (async self.name_future.get() catch unreachable); + return &self.table.private_data; + } }; pub const Block = struct { diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 45e5362124..d3541c73b6 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -14,6 +14,7 @@ test "compile errors" { defer ctx.deinit(); try @import("../test/stage2/compile_errors.zig").addCases(&ctx); + //try @import("../test/stage2/compare_output.zig").addCases(&ctx); try ctx.run(); } diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index fa99586383..217c1d50a7 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -4,12 +4,17 @@ const Scope = @import("scope.zig").Scope; const Compilation = @import("compilation.zig").Compilation; const Value = @import("value.zig").Value; const llvm = @import("llvm.zig"); -const ObjectFile = @import("codegen.zig").ObjectFile; +const event = std.event; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; pub const Type = struct { base: Value, id: Id, name: []const u8, + abi_alignment: AbiAlignment, + + pub const AbiAlignment = event.Future(error{OutOfMemory}!u32); pub const Id = builtin.TypeId; @@ -43,33 +48,37 @@ pub const Type = struct { } } - pub fn getLlvmType(base: *Type, ofile: *ObjectFile) (error{OutOfMemory}!llvm.TypeRef) { + pub fn getLlvmType( + base: *Type, + allocator: *Allocator, + llvm_context: llvm.ContextRef, + ) (error{OutOfMemory}!llvm.TypeRef) { switch (base.id) { - Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(ofile), - Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(ofile), + Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context), + Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context), Id.Type => unreachable, Id.Void => unreachable, - Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(ofile), + Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(allocator, llvm_context), Id.NoReturn => unreachable, - Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(ofile), - Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(ofile), - Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(ofile), - Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(ofile), + Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context), + Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context), + Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context), + Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context), Id.ComptimeFloat => unreachable, Id.ComptimeInt => unreachable, Id.Undefined => unreachable, Id.Null => unreachable, - Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(ofile), - Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(ofile), - Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(ofile), - Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(ofile), - Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(ofile), + Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context), + Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context), + Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context), + Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context), + Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context), Id.Namespace => unreachable, Id.Block => unreachable, - Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(ofile), + Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context), Id.ArgTuple => unreachable, - Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(ofile), - Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(ofile), + Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context), + Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context), } } @@ -156,16 +165,45 @@ pub const Type = struct { base.* = Type{ .base = Value{ .id = Value.Id.Type, - .typeof = &MetaType.get(comp).base, + .typ = &MetaType.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .id = id, .name = name, + .abi_alignment = AbiAlignment.init(comp.loop), }; } - pub fn getAbiAlignment(base: *Type, comp: *Compilation) u32 { - @panic("TODO getAbiAlignment"); + /// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead. + /// Otherwise, this one will grab one from the pool and then release it. + pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 { + if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*; + + { + const held = try comp.event_loop_local.getAnyLlvmContext(); + defer held.release(comp.event_loop_local); + + const llvm_context = held.node.data; + + base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable); + } + base.abi_alignment.resolve(); + return base.abi_alignment.data; + } + + /// If you have an llvm conext handy, you can use it here. + pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 { + if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*; + + base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable); + base.abi_alignment.resolve(); + return base.abi_alignment.data; + } + + /// Lower level function that does the work. See getAbiAlignment. + async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 { + const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context); + return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type)); } pub const Struct = struct { @@ -176,7 +214,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef { + pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { @panic("TODO"); } }; @@ -189,7 +227,7 @@ pub const Type = struct { pub const Param = struct { is_noalias: bool, - typeof: *Type, + typ: *Type, }; pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { @@ -205,7 +243,7 @@ pub const Type = struct { result.return_type.base.ref(); for (result.params) |param| { - param.typeof.base.ref(); + param.typ.base.ref(); } return result; } @@ -213,20 +251,20 @@ pub const Type = struct { pub fn destroy(self: *Fn, comp: *Compilation) void { self.return_type.base.deref(comp); for (self.params) |param| { - param.typeof.base.deref(comp); + param.typ.base.deref(comp); } comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef { + pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { const llvm_return_type = switch (self.return_type.id) { - Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory, - else => try self.return_type.getLlvmType(ofile), + Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory, + else => try self.return_type.getLlvmType(allocator, llvm_context), }; - const llvm_param_types = try ofile.gpa().alloc(llvm.TypeRef, self.params.len); - defer ofile.gpa().free(llvm_param_types); + const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len); + defer allocator.free(llvm_param_types); for (llvm_param_types) |*llvm_param_type, i| { - llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile); + llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context); } return llvm.FunctionType( @@ -280,7 +318,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef { + pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { @panic("TODO"); } }; @@ -318,6 +356,11 @@ pub const Type = struct { } }; + pub fn get_u8(comp: *Compilation) *Int { + comp.u8_type.base.base.ref(); + return comp.u8_type; + } + pub async fn get(comp: *Compilation, key: Key) !*Int { { const held = await (async comp.int_type_table.acquire() catch unreachable); @@ -371,8 +414,8 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Int, ofile: *ObjectFile) !llvm.TypeRef { - return llvm.IntTypeInContext(ofile.context, self.key.bit_count) orelse return error.OutOfMemory; + pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { + return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory; } }; @@ -383,56 +426,236 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef { + pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { @panic("TODO"); } }; pub const Pointer = struct { base: Type, - mut: Mut, - vol: Vol, - size: Size, - alignment: u32, + key: Key, + garbage_node: std.atomic.Stack(*Pointer).Node, + + pub const Key = struct { + child_type: *Type, + mut: Mut, + vol: Vol, + size: Size, + alignment: Align, + + pub fn hash(self: *const Key) u32 { + const align_hash = switch (self.alignment) { + Align.Abi => 0xf201c090, + Align.Override => |x| x, + }; + return hash_usize(@ptrToInt(self.child_type)) *% + hash_enum(self.mut) *% + hash_enum(self.vol) *% + hash_enum(self.size) *% + align_hash; + } + + pub fn eql(self: *const Key, other: *const Key) bool { + if (self.child_type != other.child_type or + self.mut != other.mut or + self.vol != other.vol or + self.size != other.size or + @TagType(Align)(self.alignment) != @TagType(Align)(other.alignment)) + { + return false; + } + switch (self.alignment) { + Align.Abi => return true, + Align.Override => |x| return x == other.alignment.Override, + } + } + }; pub const Mut = enum { Mut, Const, }; + pub const Vol = enum { Non, Volatile, }; + + pub const Align = union(enum) { + Abi, + Override: u32, + }; + pub const Size = builtin.TypeInfo.Pointer.Size; pub fn destroy(self: *Pointer, comp: *Compilation) void { + self.garbage_node = std.atomic.Stack(*Pointer).Node{ + .data = self, + .next = undefined, + }; + comp.registerGarbage(Pointer, &self.garbage_node); + } + + pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void { + { + const held = await (async comp.ptr_type_table.acquire() catch unreachable); + defer held.release(); + + _ = held.value.remove(&self.key).?; + } + self.key.child_type.base.deref(comp); comp.gpa().destroy(self); } - pub fn get( + pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 { + switch (self.key.alignment) { + Align.Abi => return await (async self.key.child_type.getAbiAlignment(comp) catch unreachable), + Align.Override => |alignment| return alignment, + } + } + + pub async fn get( comp: *Compilation, - elem_type: *Type, - mut: Mut, - vol: Vol, - size: Size, - alignment: u32, - ) *Pointer { - @panic("TODO get pointer"); + key: Key, + ) !*Pointer { + var normal_key = key; + switch (key.alignment) { + Align.Abi => {}, + Align.Override => |alignment| { + const abi_align = try await (async key.child_type.getAbiAlignment(comp) catch unreachable); + if (abi_align == alignment) { + normal_key.alignment = Align.Abi; + } + }, + } + { + const held = await (async comp.ptr_type_table.acquire() catch unreachable); + defer held.release(); + + if (held.value.get(&normal_key)) |entry| { + entry.value.base.base.ref(); + return entry.value; + } + } + + const self = try comp.gpa().create(Pointer{ + .base = undefined, + .key = normal_key, + .garbage_node = undefined, + }); + errdefer comp.gpa().destroy(self); + + const size_str = switch (self.key.size) { + Size.One => "*", + Size.Many => "[*]", + Size.Slice => "[]", + }; + const mut_str = switch (self.key.mut) { + Mut.Const => "const ", + Mut.Mut => "", + }; + const vol_str = switch (self.key.vol) { + Vol.Volatile => "volatile ", + Vol.Non => "", + }; + const name = switch (self.key.alignment) { + Align.Abi => try std.fmt.allocPrint( + comp.gpa(), + "{}{}{}{}", + size_str, + mut_str, + vol_str, + self.key.child_type.name, + ), + Align.Override => |alignment| try std.fmt.allocPrint( + comp.gpa(), + "{}align<{}> {}{}{}", + size_str, + alignment, + mut_str, + vol_str, + self.key.child_type.name, + ), + }; + errdefer comp.gpa().free(name); + + self.base.init(comp, Id.Pointer, name); + + { + const held = await (async comp.ptr_type_table.acquire() catch unreachable); + defer held.release(); + + _ = try held.value.put(&self.key, self); + } + return self; } - pub fn getLlvmType(self: *Pointer, ofile: *ObjectFile) llvm.TypeRef { - @panic("TODO"); + pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { + const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context); + return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory; } }; pub const Array = struct { base: Type, + key: Key, + garbage_node: std.atomic.Stack(*Array).Node, + + pub const Key = struct { + elem_type: *Type, + len: usize, + + pub fn hash(self: *const Key) u32 { + return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len); + } + + pub fn eql(self: *const Key, other: *const Key) bool { + return self.elem_type == other.elem_type and self.len == other.len; + } + }; pub fn destroy(self: *Array, comp: *Compilation) void { + self.key.elem_type.base.deref(comp); comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef { - @panic("TODO"); + pub async fn get(comp: *Compilation, key: Key) !*Array { + key.elem_type.base.ref(); + errdefer key.elem_type.base.deref(comp); + + { + const held = await (async comp.array_type_table.acquire() catch unreachable); + defer held.release(); + + if (held.value.get(&key)) |entry| { + entry.value.base.base.ref(); + return entry.value; + } + } + + const self = try comp.gpa().create(Array{ + .base = undefined, + .key = key, + .garbage_node = undefined, + }); + errdefer comp.gpa().destroy(self); + + const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", key.len, key.elem_type.name); + errdefer comp.gpa().free(name); + + self.base.init(comp, Id.Array, name); + + { + const held = await (async comp.array_type_table.acquire() catch unreachable); + defer held.release(); + + _ = try held.value.put(&self.key, self); + } + return self; + } + + pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { + const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context); + return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory; } }; @@ -481,7 +704,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef { + pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { @panic("TODO"); } }; @@ -493,7 +716,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef { + pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { @panic("TODO"); } }; @@ -505,7 +728,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef { + pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { @panic("TODO"); } }; @@ -517,7 +740,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef { + pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { @panic("TODO"); } }; @@ -529,7 +752,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef { + pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { @panic("TODO"); } }; @@ -557,7 +780,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef { + pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { @panic("TODO"); } }; @@ -577,7 +800,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef { + pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { @panic("TODO"); } }; @@ -589,8 +812,33 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef { + pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { @panic("TODO"); } }; }; + +fn hash_usize(x: usize) u32 { + return switch (@sizeOf(usize)) { + 4 => x, + 8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d), + else => @compileError("implement this hash function"), + }; +} + +fn hash_enum(x: var) u32 { + const rands = []u32{ + 0x85ebf64f, + 0x3fcb3211, + 0x240a4e8e, + 0x40bb0e3c, + 0x78be45af, + 0x1ca98e37, + 0xec56053a, + 0x906adc48, + 0xd4fe9763, + 0x54c80dac, + }; + comptime assert(@memberCount(@typeOf(x)) < rands.len); + return rands[@enumToInt(x)]; +} diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 16c1488838..2005e3c119 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -11,7 +11,7 @@ const assert = std.debug.assert; /// If there is only 1 ref then write need not copy pub const Value = struct { id: Id, - typeof: *Type, + typ: *Type, ref_count: std.atomic.Int(usize), /// Thread-safe @@ -22,23 +22,25 @@ pub const Value = struct { /// Thread-safe pub fn deref(base: *Value, comp: *Compilation) void { if (base.ref_count.decr() == 1) { - base.typeof.base.deref(comp); + base.typ.base.deref(comp); switch (base.id) { Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp), Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp), + Id.FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp), Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp), Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp), Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp), Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp), Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp), + Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp), } } } pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void { - base.typeof.base.deref(comp); + base.typ.base.deref(comp); new_type.base.ref(); - base.typeof = new_type; + base.typ = new_type; } pub fn getRef(base: *Value) *Value { @@ -59,11 +61,13 @@ pub const Value = struct { switch (base.id) { Id.Type => unreachable, Id.Fn => @panic("TODO"), + Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile), Id.Void => return null, Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile), Id.NoReturn => unreachable, - Id.Ptr => @panic("TODO"), + Id.Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile), Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile), + Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile), } } @@ -81,26 +85,87 @@ pub const Value = struct { switch (base.id) { Id.Type => unreachable, Id.Fn => unreachable, + Id.FnProto => unreachable, Id.Void => unreachable, Id.Bool => unreachable, Id.NoReturn => unreachable, Id.Ptr => unreachable, + Id.Array => unreachable, Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base, } } + pub const Parent = union(enum) { + None, + BaseStruct: BaseStruct, + BaseArray: BaseArray, + BaseUnion: *Value, + BaseScalar: *Value, + + pub const BaseStruct = struct { + val: *Value, + field_index: usize, + }; + + pub const BaseArray = struct { + val: *Value, + elem_index: usize, + }; + }; + pub const Id = enum { Type, Fn, Void, Bool, NoReturn, + Array, Ptr, Int, + FnProto, }; pub const Type = @import("type.zig").Type; + pub const FnProto = struct { + base: Value, + + /// The main external name that is used in the .o file. + /// TODO https://github.com/ziglang/zig/issues/265 + symbol_name: Buffer, + + pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto { + const self = try comp.gpa().create(FnProto{ + .base = Value{ + .id = Value.Id.FnProto, + .typ = &fn_type.base, + .ref_count = std.atomic.Int(usize).init(1), + }, + .symbol_name = symbol_name, + }); + fn_type.base.base.ref(); + return self; + } + + pub fn destroy(self: *FnProto, comp: *Compilation) void { + self.symbol_name.deinit(); + comp.gpa().destroy(self); + } + + pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?llvm.ValueRef { + const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); + const llvm_fn = llvm.AddFunction( + ofile.module, + self.symbol_name.ptr(), + llvm_fn_type, + ) orelse return error.OutOfMemory; + + // TODO port more logic from codegen.cpp:fn_llvm_value + + return llvm_fn; + } + }; + pub const Fn = struct { base: Value, @@ -135,7 +200,7 @@ pub const Value = struct { const self = try comp.gpa().create(Fn{ .base = Value{ .id = Value.Id.Fn, - .typeof = &fn_type.base, + .typ = &fn_type.base, .ref_count = std.atomic.Int(usize).init(1), }, .fndef_scope = fndef_scope, @@ -224,6 +289,8 @@ pub const Value = struct { pub const Ptr = struct { base: Value, + special: Special, + mut: Mut, pub const Mut = enum { CompTimeConst, @@ -231,25 +298,210 @@ pub const Value = struct { RunTime, }; + pub const Special = union(enum) { + Scalar: *Value, + BaseArray: BaseArray, + BaseStruct: BaseStruct, + HardCodedAddr: u64, + Discard, + }; + + pub const BaseArray = struct { + val: *Value, + elem_index: usize, + }; + + pub const BaseStruct = struct { + val: *Value, + field_index: usize, + }; + + pub async fn createArrayElemPtr( + comp: *Compilation, + array_val: *Array, + mut: Type.Pointer.Mut, + size: Type.Pointer.Size, + elem_index: usize, + ) !*Ptr { + array_val.base.ref(); + errdefer array_val.base.deref(comp); + + const elem_type = array_val.base.typ.cast(Type.Array).?.key.elem_type; + const ptr_type = try await (async Type.Pointer.get(comp, Type.Pointer.Key{ + .child_type = elem_type, + .mut = mut, + .vol = Type.Pointer.Vol.Non, + .size = size, + .alignment = Type.Pointer.Align.Abi, + }) catch unreachable); + var ptr_type_consumed = false; + errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp); + + const self = try comp.gpa().create(Value.Ptr{ + .base = Value{ + .id = Value.Id.Ptr, + .typ = &ptr_type.base, + .ref_count = std.atomic.Int(usize).init(1), + }, + .special = Special{ + .BaseArray = BaseArray{ + .val = &array_val.base, + .elem_index = 0, + }, + }, + .mut = Mut.CompTimeConst, + }); + ptr_type_consumed = true; + errdefer comp.gpa().destroy(self); + + return self; + } + pub fn destroy(self: *Ptr, comp: *Compilation) void { comp.gpa().destroy(self); } + + pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?llvm.ValueRef { + const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context); + // TODO carefully port the logic from codegen.cpp:gen_const_val_ptr + switch (self.special) { + Special.Scalar => |scalar| @panic("TODO"), + Special.BaseArray => |base_array| { + // TODO put this in one .o file only, and after that, generate extern references to it + const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?; + const ptr_bit_count = ofile.comp.target_ptr_bits; + const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory; + const indices = []llvm.ValueRef{ + llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory, + llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory, + }; + return llvm.ConstInBoundsGEP( + array_llvm_value, + &indices, + @intCast(c_uint, indices.len), + ) orelse return error.OutOfMemory; + }, + Special.BaseStruct => |base_struct| @panic("TODO"), + Special.HardCodedAddr => |addr| @panic("TODO"), + Special.Discard => unreachable, + } + } + }; + + pub const Array = struct { + base: Value, + special: Special, + + pub const Special = union(enum) { + Undefined, + OwnedBuffer: []u8, + Explicit: Data, + }; + + pub const Data = struct { + parent: Parent, + elements: []*Value, + }; + + /// Takes ownership of buffer + pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array { + const u8_type = Type.Int.get_u8(comp); + defer u8_type.base.base.deref(comp); + + const array_type = try await (async Type.Array.get(comp, Type.Array.Key{ + .elem_type = &u8_type.base, + .len = buffer.len, + }) catch unreachable); + errdefer array_type.base.base.deref(comp); + + const self = try comp.gpa().create(Value.Array{ + .base = Value{ + .id = Value.Id.Array, + .typ = &array_type.base, + .ref_count = std.atomic.Int(usize).init(1), + }, + .special = Special{ .OwnedBuffer = buffer }, + }); + errdefer comp.gpa().destroy(self); + + return self; + } + + pub fn destroy(self: *Array, comp: *Compilation) void { + switch (self.special) { + Special.Undefined => {}, + Special.OwnedBuffer => |buf| { + comp.gpa().free(buf); + }, + Special.Explicit => {}, + } + comp.gpa().destroy(self); + } + + pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?llvm.ValueRef { + switch (self.special) { + Special.Undefined => { + const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); + return llvm.GetUndef(llvm_type); + }, + Special.OwnedBuffer => |buf| { + const dont_null_terminate = 1; + const llvm_str_init = llvm.ConstStringInContext( + ofile.context, + buf.ptr, + @intCast(c_uint, buf.len), + dont_null_terminate, + ) orelse return error.OutOfMemory; + const str_init_type = llvm.TypeOf(llvm_str_init); + const global = llvm.AddGlobal(ofile.module, str_init_type, c"") orelse return error.OutOfMemory; + llvm.SetInitializer(global, llvm_str_init); + llvm.SetLinkage(global, llvm.PrivateLinkage); + llvm.SetGlobalConstant(global, 1); + llvm.SetUnnamedAddr(global, 1); + llvm.SetAlignment(global, llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, str_init_type)); + return global; + }, + Special.Explicit => @panic("TODO"), + } + + //{ + // uint64_t len = type_entry->data.array.len; + // if (const_val->data.x_array.special == ConstArraySpecialUndef) { + // return LLVMGetUndef(type_entry->type_ref); + // } + + // LLVMValueRef *values = allocate(len); + // LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref; + // bool make_unnamed_struct = false; + // for (uint64_t i = 0; i < len; i += 1) { + // ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i]; + // LLVMValueRef val = gen_const_val(g, elem_value, ""); + // values[i] = val; + // make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val); + // } + // if (make_unnamed_struct) { + // return LLVMConstStruct(values, len, true); + // } else { + // return LLVMConstArray(element_type_ref, values, (unsigned)len); + // } + //} + } }; pub const Int = struct { base: Value, big_int: std.math.big.Int, - pub fn createFromString(comp: *Compilation, typeof: *Type, base: u8, value: []const u8) !*Int { + pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int { const self = try comp.gpa().create(Value.Int{ .base = Value{ .id = Value.Id.Int, - .typeof = typeof, + .typ = typ, .ref_count = std.atomic.Int(usize).init(1), }, .big_int = undefined, }); - typeof.base.ref(); + typ.base.ref(); errdefer comp.gpa().destroy(self); self.big_int = try std.math.big.Int.init(comp.gpa()); @@ -261,9 +513,9 @@ pub const Value = struct { } pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef { - switch (self.base.typeof.id) { + switch (self.base.typ.id) { Type.Id.Int => { - const type_ref = try self.base.typeof.getLlvmType(ofile); + const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context); if (self.big_int.len == 0) { return llvm.ConstNull(type_ref); } @@ -286,13 +538,13 @@ pub const Value = struct { } pub fn copy(old: *Int, comp: *Compilation) !*Int { - old.base.typeof.base.ref(); - errdefer old.base.typeof.base.deref(comp); + old.base.typ.base.ref(); + errdefer old.base.typ.base.deref(comp); const new = try comp.gpa().create(Value.Int{ .base = Value{ .id = Value.Id.Int, - .typeof = old.base.typeof, + .typ = old.base.typ, .ref_count = std.atomic.Int(usize).init(1), }, .big_int = undefined, diff --git a/std/event/group.zig b/std/event/group.zig index fee31a56a6..26c098399e 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -76,6 +76,7 @@ pub fn Group(comptime ReturnType: type) type { /// Wait for all the calls and promises of the group to complete. /// Thread-safe. + /// Safe to call any number of times. pub async fn wait(self: *Self) ReturnType { // TODO catch unreachable because the allocation can be grouped with // the coro frame allocation diff --git a/std/zig/index.zig b/std/zig/index.zig index 4dd68fa8b3..da84bc5bb0 100644 --- a/std/zig/index.zig +++ b/std/zig/index.zig @@ -2,6 +2,7 @@ const tokenizer = @import("tokenizer.zig"); pub const Token = tokenizer.Token; pub const Tokenizer = tokenizer.Tokenizer; pub const parse = @import("parse.zig").parse; +pub const parseStringLiteral = @import("parse_string_literal.zig").parseStringLiteral; pub const render = @import("render.zig").render; pub const ast = @import("ast.zig"); @@ -10,4 +11,6 @@ test "std.zig tests" { _ = @import("parse.zig"); _ = @import("render.zig"); _ = @import("tokenizer.zig"); + _ = @import("parse_string_literal.zig"); } + diff --git a/std/zig/parse_string_literal.zig b/std/zig/parse_string_literal.zig new file mode 100644 index 0000000000..00c92a7651 --- /dev/null +++ b/std/zig/parse_string_literal.zig @@ -0,0 +1,76 @@ +const std = @import("../index.zig"); +const assert = std.debug.assert; + +const State = enum { + Start, + Backslash, +}; + +pub const ParseStringLiteralError = error{ + OutOfMemory, + + /// When this is returned, index will be the position of the character. + InvalidCharacter, +}; + +/// caller owns returned memory +pub fn parseStringLiteral( + allocator: *std.mem.Allocator, + bytes: []const u8, + bad_index: *usize, // populated if error.InvalidCharacter is returned +) ParseStringLiteralError![]u8 { + const first_index = if (bytes[0] == 'c') usize(2) else usize(1); + assert(bytes[bytes.len - 1] == '"'); + + var list = std.ArrayList(u8).init(allocator); + errdefer list.deinit(); + + const slice = bytes[first_index..]; + try list.ensureCapacity(slice.len - 1); + + var state = State.Start; + for (slice) |b, index| { + switch (state) { + State.Start => switch (b) { + '\\' => state = State.Backslash, + '\n' => { + bad_index.* = index; + return error.InvalidCharacter; + }, + '"' => return list.toOwnedSlice(), + else => try list.append(b), + }, + State.Backslash => switch (b) { + 'x' => @panic("TODO"), + 'u' => @panic("TODO"), + 'U' => @panic("TODO"), + 'n' => { + try list.append('\n'); + state = State.Start; + }, + 'r' => { + try list.append('\r'); + state = State.Start; + }, + '\\' => { + try list.append('\\'); + state = State.Start; + }, + 't' => { + try list.append('\t'); + state = State.Start; + }, + '"' => { + try list.append('"'); + state = State.Start; + }, + else => { + bad_index.* = index; + return error.InvalidCharacter; + }, + }, + else => unreachable, + } + } + unreachable; +} diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 79f1871b64..3c7ab1f0a8 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -73,6 +73,7 @@ pub const Token = struct { return null; } + /// TODO remove this enum const StrLitKind = enum { Normal, C, -- cgit v1.2.3 From d767fae47e89aef53505191cf11ce7e592a784b2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Jul 2018 00:35:53 -0400 Subject: self-hosted: add first compare-output test --- src-self-hosted/compilation.zig | 6 +-- src-self-hosted/main.zig | 12 +----- src-self-hosted/test.zig | 91 ++++++++++++++++++++++++++++++++++++----- src/windows_sdk.h | 2 + std/os/test.zig | 6 +-- test/stage2/compare_output.zig | 12 ++++++ 6 files changed, 101 insertions(+), 28 deletions(-) create mode 100644 test/stage2/compare_output.zig diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 3adeb55b56..356fecc6da 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -128,7 +128,6 @@ pub const Compilation = struct { version_patch: u32, linker_script: ?[]const u8, - cache_dir: []const u8, out_h_path: ?[]const u8, is_test: bool, @@ -326,7 +325,6 @@ pub const Compilation = struct { build_mode: builtin.Mode, is_static: bool, zig_lib_dir: []const u8, - cache_dir: []const u8, ) !*Compilation { const loop = event_loop_local.loop; const comp = try event_loop_local.loop.allocator.create(Compilation{ @@ -341,7 +339,6 @@ pub const Compilation = struct { .build_mode = build_mode, .zig_lib_dir = zig_lib_dir, .zig_std_dir = undefined, - .cache_dir = cache_dir, .tmp_dir = event.Future(BuildError![]u8).init(loop), .name = undefined, @@ -777,7 +774,8 @@ pub const Compilation = struct { defer decls.base.deref(self); var decl_group = event.Group(BuildError!void).init(self.loop); - errdefer decl_group.cancelAll(); + // TODO https://github.com/ziglang/zig/issues/1261 + //errdefer decl_group.cancelAll(); var it = tree.root_node.decls.iterator(0); while (it.next()) |decl_ptr| { diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 5c27e5f57e..d7e52bc404 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -138,7 +138,6 @@ const usage_build_generic = \\Compile Options: \\ --libc [file] Provide a file which specifies libc paths \\ --assembly [source] Add assembly file to build - \\ --cache-dir [path] Override the cache directory \\ --emit [filetype] Emit a specific file format as compilation output \\ --enable-timing-info Print timing diagnostics \\ --name [name] Override output name @@ -204,7 +203,6 @@ const args_build_generic = []Flag{ }), Flag.ArgMergeN("--assembly", 1), - Flag.Arg1("--cache-dir"), Flag.Option("--emit", []const []const u8{ "asm", "bin", @@ -373,13 +371,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co os.exit(1); } - const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..]; - const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch { - try stderr.print("invalid cache dir: {}\n", rel_cache_dir); - os.exit(1); - }; - defer allocator.free(full_cache_dir); - const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1); defer allocator.free(zig_lib_dir); @@ -401,7 +392,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co build_mode, is_static, zig_lib_dir, - full_cache_dir, ); defer comp.destroy(); @@ -472,7 +462,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co comp.emit_file_type = emit_type; comp.assembly_files = assembly_files; - comp.link_out_file = flags.single("out-file"); + comp.link_out_file = flags.single("output"); comp.link_objects = link_objects; try comp.build(); diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index d3541c73b6..1c76bc9e11 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -8,13 +8,14 @@ const assertOrPanic = std.debug.assertOrPanic; const errmsg = @import("errmsg.zig"); const EventLoopLocal = @import("compilation.zig").EventLoopLocal; -test "compile errors" { - var ctx: TestContext = undefined; +var ctx: TestContext = undefined; + +test "stage2" { try ctx.init(); defer ctx.deinit(); try @import("../test/stage2/compile_errors.zig").addCases(&ctx); - //try @import("../test/stage2/compare_output.zig").addCases(&ctx); + try @import("../test/stage2/compare_output.zig").addCases(&ctx); try ctx.run(); } @@ -26,7 +27,6 @@ pub const TestContext = struct { loop: std.event.Loop, event_loop_local: EventLoopLocal, zig_lib_dir: []u8, - zig_cache_dir: []u8, file_index: std.atomic.Int(usize), group: std.event.Group(error!void), any_err: error!void, @@ -39,7 +39,6 @@ pub const TestContext = struct { .loop = undefined, .event_loop_local = undefined, .zig_lib_dir = undefined, - .zig_cache_dir = undefined, .group = undefined, .file_index = std.atomic.Int(usize).init(0), }; @@ -56,16 +55,12 @@ pub const TestContext = struct { self.zig_lib_dir = try introspect.resolveZigLibDir(allocator); errdefer allocator.free(self.zig_lib_dir); - self.zig_cache_dir = try introspect.resolveZigCacheDir(allocator); - errdefer allocator.free(self.zig_cache_dir); - try std.os.makePath(allocator, tmp_dir_name); errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {}; } fn deinit(self: *TestContext) void { std.os.deleteTree(allocator, tmp_dir_name) catch {}; - allocator.free(self.zig_cache_dir); allocator.free(self.zig_lib_dir); self.event_loop_local.deinit(); self.loop.deinit(); @@ -110,7 +105,6 @@ pub const TestContext = struct { builtin.Mode.Debug, true, // is_static self.zig_lib_dir, - self.zig_cache_dir, ); errdefer comp.destroy(); @@ -119,6 +113,83 @@ pub const TestContext = struct { try self.group.call(getModuleEvent, comp, source, path, line, column, msg); } + fn testCompareOutputLibC( + self: *TestContext, + source: []const u8, + expected_output: []const u8, + ) !void { + var file_index_buf: [20]u8 = undefined; + const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr()); + const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1); + + const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt()); + if (std.os.path.dirname(file1_path)) |dirname| { + try std.os.makePath(allocator, dirname); + } + + // TODO async I/O + try std.io.writeFile(allocator, file1_path, source); + + var comp = try Compilation.create( + &self.event_loop_local, + "test", + file1_path, + Target.Native, + Compilation.Kind.Exe, + builtin.Mode.Debug, + false, + self.zig_lib_dir, + ); + errdefer comp.destroy(); + + _ = try comp.addLinkLib("c", true); + comp.link_out_file = output_file; + try comp.build(); + + try self.group.call(getModuleEventSuccess, comp, output_file, expected_output); + } + + async fn getModuleEventSuccess( + comp: *Compilation, + exe_file: []const u8, + expected_output: []const u8, + ) !void { + // TODO this should not be necessary + const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file); + + defer comp.destroy(); + const build_event = await (async comp.events.get() catch unreachable); + + switch (build_event) { + Compilation.Event.Ok => { + const argv = []const []const u8{exe_file_2}; + // TODO use event loop + const child = try std.os.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024); + switch (child.term) { + std.os.ChildProcess.Term.Exited => |code| { + if (code != 0) { + return error.BadReturnCode; + } + }, + else => { + return error.Crashed; + }, + } + if (!mem.eql(u8, child.stdout, expected_output)) { + return error.OutputMismatch; + } + }, + Compilation.Event.Error => |err| return err, + Compilation.Event.Fail => |msgs| { + var stderr = try std.io.getStdErr(); + try stderr.write("build incorrectly failed:\n"); + for (msgs) |msg| { + try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto); + } + }, + } + } + async fn getModuleEvent( comp: *Compilation, source: []const u8, diff --git a/src/windows_sdk.h b/src/windows_sdk.h index 080ed55bed..2d531ad372 100644 --- a/src/windows_sdk.h +++ b/src/windows_sdk.h @@ -14,6 +14,8 @@ #define ZIG_EXTERN_C #endif +#include + struct ZigWindowsSDK { const char *path10_ptr; size_t path10_len; diff --git a/std/os/test.zig b/std/os/test.zig index 52e6ffdc1c..9e795e8ad2 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" { test "access file" { try os.makePath(a, "os_test_tmp"); - if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| { - unreachable; + if (os.File.access(a, "os_test_tmp/file.txt")) |ok| { + @panic("expected error"); } else |err| { assert(err == error.NotFound); } try io.writeFile(a, "os_test_tmp/file.txt", ""); - assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true); + try os.File.access(a, "os_test_tmp/file.txt"); try os.deleteTree(a, "os_test_tmp"); } diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig new file mode 100644 index 0000000000..35adcbb96b --- /dev/null +++ b/test/stage2/compare_output.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const TestContext = @import("../../src-self-hosted/test.zig").TestContext; + +pub fn addCases(ctx: *TestContext) !void { + try ctx.testCompareOutputLibC( + \\extern fn puts([*]const u8) void; + \\export fn main() c_int { + \\ puts(c"Hello, world!"); + \\ return 0; + \\} + , "Hello, world!" ++ std.cstr.line_sep); +} -- cgit v1.2.3 From 10d2f08d376a302bd79e87d17c06922d3145a939 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Jul 2018 14:28:14 -0400 Subject: self-hosted: fix error messages not cleaning up correctly --- src-self-hosted/compilation.zig | 59 ++++----- src-self-hosted/errmsg.zig | 260 ++++++++++++++++++++++++++++++---------- src-self-hosted/main.zig | 15 +-- src-self-hosted/scope.zig | 7 +- src-self-hosted/test.zig | 14 ++- std/mem.zig | 12 +- 6 files changed, 255 insertions(+), 112 deletions(-) diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 356fecc6da..f1886bd93d 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -24,6 +24,7 @@ const Visib = @import("visib.zig").Visib; const Value = @import("value.zig").Value; const Type = Value.Type; const Span = errmsg.Span; +const Msg = errmsg.Msg; const codegen = @import("codegen.zig"); const Package = @import("package.zig").Package; const link = @import("link.zig").link; @@ -40,8 +41,6 @@ pub const EventLoopLocal = struct { native_libc: event.Future(LibCInstallation), - cwd: []const u8, - var lazy_init_targets = std.lazyInit(void); fn init(loop: *event.Loop) !EventLoopLocal { @@ -54,21 +53,16 @@ pub const EventLoopLocal = struct { try std.os.getRandomBytes(seed_bytes[0..]); const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big); - const cwd = try os.getCwd(loop.allocator); - errdefer loop.allocator.free(cwd); - return EventLoopLocal{ .loop = loop, .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), .native_libc = event.Future(LibCInstallation).init(loop), - .cwd = cwd, }; } /// Must be called only after EventLoop.run completes. fn deinit(self: *EventLoopLocal) void { - self.loop.allocator.free(self.cwd); while (self.llvm_handle_pool.pop()) |node| { c.LLVMContextDispose(node.data); self.loop.allocator.destroy(node); @@ -234,7 +228,7 @@ pub const Compilation = struct { const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql); const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8); - const CompileErrList = std.ArrayList(*errmsg.Msg); + const CompileErrList = std.ArrayList(*Msg); // TODO handle some of these earlier and report them in a way other than error codes pub const BuildError = error{ @@ -286,7 +280,7 @@ pub const Compilation = struct { pub const Event = union(enum) { Ok, Error: BuildError, - Fail: []*errmsg.Msg, + Fail: []*Msg, }; pub const DarwinVersionMin = union(enum) { @@ -761,21 +755,35 @@ pub const Compilation = struct { }; errdefer self.gpa().free(source_code); - var tree = try std.zig.parse(self.gpa(), source_code); - errdefer tree.deinit(); + const tree = try self.gpa().createOne(ast.Tree); + tree.* = try std.zig.parse(self.gpa(), source_code); + errdefer { + tree.deinit(); + self.gpa().destroy(tree); + } break :blk try Scope.Root.create(self, tree, root_src_real_path); }; defer root_scope.base.deref(self); + const tree = root_scope.tree; + + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error); + errdefer msg.destroy(); - const tree = &root_scope.tree; + try await (async self.addCompileErrorAsync(msg) catch unreachable); + } + if (tree.errors.len != 0) { + return; + } const decls = try Scope.Decls.create(self, &root_scope.base); defer decls.base.deref(self); var decl_group = event.Group(BuildError!void).init(self.loop); - // TODO https://github.com/ziglang/zig/issues/1261 - //errdefer decl_group.cancelAll(); + var decl_group_consumed = false; + errdefer if (!decl_group_consumed) decl_group.cancelAll(); var it = tree.root_node.decls.iterator(0); while (it.next()) |decl_ptr| { @@ -817,6 +825,7 @@ pub const Compilation = struct { else => unreachable, } } + decl_group_consumed = true; try await (async decl_group.wait() catch unreachable); // Now other code can rely on the decls scope having a complete list of names. @@ -897,7 +906,7 @@ pub const Compilation = struct { } async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void { - const tree = &decl.findRootScope().tree; + const tree = decl.findRootScope().tree; const is_export = decl.isExported(tree); var add_to_table_resolved = false; @@ -927,25 +936,17 @@ pub const Compilation = struct { const text = try std.fmt.allocPrint(self.gpa(), fmt, args); errdefer self.gpa().free(text); - try self.prelink_group.call(addCompileErrorAsync, self, root, span, text); + const msg = try Msg.createFromScope(self, root, span, text); + errdefer msg.destroy(); + + try self.prelink_group.call(addCompileErrorAsync, self, msg); } async fn addCompileErrorAsync( self: *Compilation, - root: *Scope.Root, - span: Span, - text: []u8, + msg: *Msg, ) !void { - const relpath = try os.path.relative(self.gpa(), self.event_loop_local.cwd, root.realpath); - errdefer self.gpa().free(relpath); - - const msg = try self.gpa().create(errmsg.Msg{ - .path = if (relpath.len < root.realpath.len) relpath else root.realpath, - .text = text, - .span = span, - .tree = &root.tree, - }); - errdefer self.gpa().destroy(msg); + errdefer msg.destroy(); const compile_errors = await (async self.compile_errors.acquire() catch unreachable); defer compile_errors.release(); diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index eec4490a81..51e135686a 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -4,6 +4,8 @@ const os = std.os; const Token = std.zig.Token; const ast = std.zig.ast; const TokenIndex = std.zig.ast.TokenIndex; +const Compilation = @import("compilation.zig").Compilation; +const Scope = @import("scope.zig").Scope; pub const Color = enum { Auto, @@ -31,77 +33,205 @@ pub const Span = struct { }; pub const Msg = struct { - path: []const u8, - text: []u8, span: Span, - tree: *ast.Tree, -}; + text: []u8, + data: Data, + + const Data = union(enum) { + PathAndTree: PathAndTree, + ScopeAndComp: ScopeAndComp, + }; + + const PathAndTree = struct { + realpath: []const u8, + tree: *ast.Tree, + allocator: *mem.Allocator, + }; + + const ScopeAndComp = struct { + root_scope: *Scope.Root, + compilation: *Compilation, + }; + + pub fn destroy(self: *Msg) void { + switch (self.data) { + Data.PathAndTree => |path_and_tree| { + path_and_tree.allocator.free(self.text); + path_and_tree.allocator.destroy(self); + }, + Data.ScopeAndComp => |scope_and_comp| { + scope_and_comp.root_scope.base.deref(scope_and_comp.compilation); + scope_and_comp.compilation.gpa().free(self.text); + scope_and_comp.compilation.gpa().destroy(self); + }, + } + } + + fn getAllocator(self: *const Msg) *mem.Allocator { + switch (self.data) { + Data.PathAndTree => |path_and_tree| { + return path_and_tree.allocator; + }, + Data.ScopeAndComp => |scope_and_comp| { + return scope_and_comp.compilation.gpa(); + }, + } + } + + pub fn getRealPath(self: *const Msg) []const u8 { + switch (self.data) { + Data.PathAndTree => |path_and_tree| { + return path_and_tree.realpath; + }, + Data.ScopeAndComp => |scope_and_comp| { + return scope_and_comp.root_scope.realpath; + }, + } + } + + pub fn getTree(self: *const Msg) *ast.Tree { + switch (self.data) { + Data.PathAndTree => |path_and_tree| { + return path_and_tree.tree; + }, + Data.ScopeAndComp => |scope_and_comp| { + return scope_and_comp.root_scope.tree; + }, + } + } + + /// Takes ownership of text + /// References root_scope, and derefs when the msg is freed + pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg { + const msg = try comp.gpa().create(Msg{ + .text = text, + .span = span, + .data = Data{ + .ScopeAndComp = ScopeAndComp{ + .root_scope = root_scope, + .compilation = comp, + }, + }, + }); + root_scope.base.ref(); + return msg; + } + + pub fn createFromParseErrorAndScope( + comp: *Compilation, + root_scope: *Scope.Root, + parse_error: *const ast.Error, + ) !*Msg { + const loc_token = parse_error.loc(); + var text_buf = try std.Buffer.initSize(comp.gpa(), 0); + defer text_buf.deinit(); + + var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; + try parse_error.render(&root_scope.tree.tokens, out_stream); + + const msg = try comp.gpa().create(Msg{ + .text = undefined, + .span = Span{ + .first = loc_token, + .last = loc_token, + }, + .data = Data{ + .ScopeAndComp = ScopeAndComp{ + .root_scope = root_scope, + .compilation = comp, + }, + }, + }); + root_scope.base.ref(); + msg.text = text_buf.toOwnedSlice(); + return msg; + } + + /// `realpath` must outlive the returned Msg + /// `tree` must outlive the returned Msg + /// Caller owns returned Msg and must free with `allocator` + /// allocator will additionally be used for printing messages later. + pub fn createFromParseError( + allocator: *mem.Allocator, + parse_error: *const ast.Error, + tree: *ast.Tree, + realpath: []const u8, + ) !*Msg { + const loc_token = parse_error.loc(); + var text_buf = try std.Buffer.initSize(allocator, 0); + defer text_buf.deinit(); + + var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; + try parse_error.render(&tree.tokens, out_stream); + + const msg = try allocator.create(Msg{ + .text = undefined, + .data = Data{ + .PathAndTree = PathAndTree{ + .allocator = allocator, + .realpath = realpath, + .tree = tree, + }, + }, + .span = Span{ + .first = loc_token, + .last = loc_token, + }, + }); + msg.text = text_buf.toOwnedSlice(); + errdefer allocator.destroy(msg); + + return msg; + } + + pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void { + const allocator = msg.getAllocator(); + const realpath = msg.getRealPath(); + const tree = msg.getTree(); + + const cwd = try os.getCwd(allocator); + defer allocator.free(cwd); + + const relpath = try os.path.relative(allocator, cwd, realpath); + defer allocator.free(relpath); + + const path = if (relpath.len < realpath.len) relpath else realpath; + + const first_token = tree.tokens.at(msg.span.first); + const last_token = tree.tokens.at(msg.span.last); + const start_loc = tree.tokenLocationPtr(0, first_token); + const end_loc = tree.tokenLocationPtr(first_token.end, last_token); + if (!color_on) { + try stream.print( + "{}:{}:{}: error: {}\n", + path, + start_loc.line + 1, + start_loc.column + 1, + msg.text, + ); + return; + } -/// `path` must outlive the returned Msg -/// `tree` must outlive the returned Msg -/// Caller owns returned Msg and must free with `allocator` -pub fn createFromParseError( - allocator: *mem.Allocator, - parse_error: *const ast.Error, - tree: *ast.Tree, - path: []const u8, -) !*Msg { - const loc_token = parse_error.loc(); - var text_buf = try std.Buffer.initSize(allocator, 0); - defer text_buf.deinit(); - - var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; - try parse_error.render(&tree.tokens, out_stream); - - const msg = try allocator.create(Msg{ - .tree = tree, - .path = path, - .text = text_buf.toOwnedSlice(), - .span = Span{ - .first = loc_token, - .last = loc_token, - }, - }); - errdefer allocator.destroy(msg); - - return msg; -} - -pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void { - const first_token = msg.tree.tokens.at(msg.span.first); - const last_token = msg.tree.tokens.at(msg.span.last); - const start_loc = msg.tree.tokenLocationPtr(0, first_token); - const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token); - if (!color_on) { try stream.print( - "{}:{}:{}: error: {}\n", - msg.path, + "{}:{}:{}: error: {}\n{}\n", + path, start_loc.line + 1, start_loc.column + 1, msg.text, + tree.source[start_loc.line_start..start_loc.line_end], ); - return; + try stream.writeByteNTimes(' ', start_loc.column); + try stream.writeByteNTimes('~', last_token.end - first_token.start); + try stream.write("\n"); } - try stream.print( - "{}:{}:{}: error: {}\n{}\n", - msg.path, - start_loc.line + 1, - start_loc.column + 1, - msg.text, - msg.tree.source[start_loc.line_start..start_loc.line_end], - ); - try stream.writeByteNTimes(' ', start_loc.column); - try stream.writeByteNTimes('~', last_token.end - first_token.start); - try stream.write("\n"); -} - -pub fn printToFile(file: *os.File, msg: *const Msg, color: Color) !void { - const color_on = switch (color) { - Color.Auto => file.isTty(), - Color.On => true, - Color.Off => false, - }; - var stream = &std.io.FileOutStream.init(file).stream; - return printToStream(stream, msg, color_on); -} + pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void { + const color_on = switch (color) { + Color.Auto => file.isTty(), + Color.On => true, + Color.Off => false, + }; + var stream = &std.io.FileOutStream.init(file).stream; + return msg.printToStream(stream, color_on); + } +}; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index d7e52bc404..37bb435c1b 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -485,7 +485,8 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { }, Compilation.Event.Fail => |msgs| { for (msgs) |msg| { - errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1); + defer msg.destroy(); + msg.printToFile(&stderr_file, color) catch os.exit(1); } }, } @@ -646,10 +647,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { var error_it = tree.errors.iterator(0); while (error_it.next()) |parse_error| { - const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, ""); - defer allocator.destroy(msg); + const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, ""); + defer msg.destroy(); - try errmsg.printToFile(&stderr_file, msg, color); + try msg.printToFile(&stderr_file, color); } if (tree.errors.len != 0) { os.exit(1); @@ -702,10 +703,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { var error_it = tree.errors.iterator(0); while (error_it.next()) |parse_error| { - const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path); - defer allocator.destroy(msg); + const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path); + defer msg.destroy(); - try errmsg.printToFile(&stderr_file, msg, color); + try msg.printToFile(&stderr_file, color); } if (tree.errors.len != 0) { fmt.any_error = true; diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 878ddc1495..7a41083f44 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -93,13 +93,13 @@ pub const Scope = struct { pub const Root = struct { base: Scope, - tree: ast.Tree, + tree: *ast.Tree, realpath: []const u8, /// Creates a Root scope with 1 reference /// Takes ownership of realpath - /// Caller must set tree - pub fn create(comp: *Compilation, tree: ast.Tree, realpath: []u8) !*Root { + /// Takes ownership of tree, will deinit and destroy when done. + pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root { const self = try comp.gpa().create(Root{ .base = Scope{ .id = Id.Root, @@ -117,6 +117,7 @@ pub const Scope = struct { pub fn destroy(self: *Root, comp: *Compilation) void { comp.gpa().free(self.tree.source); self.tree.deinit(); + comp.gpa().destroy(self.tree); comp.gpa().free(self.realpath); comp.gpa().destroy(self); } diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 1c76bc9e11..47e45d1bb0 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -184,7 +184,8 @@ pub const TestContext = struct { var stderr = try std.io.getStdErr(); try stderr.write("build incorrectly failed:\n"); for (msgs) |msg| { - try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto); + defer msg.destroy(); + try msg.printToFile(&stderr, errmsg.Color.Auto); } }, } @@ -211,10 +212,10 @@ pub const TestContext = struct { Compilation.Event.Fail => |msgs| { assertOrPanic(msgs.len != 0); for (msgs) |msg| { - if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) { - const first_token = msg.tree.tokens.at(msg.span.first); - const last_token = msg.tree.tokens.at(msg.span.first); - const start_loc = msg.tree.tokenLocationPtr(0, first_token); + if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) { + const first_token = msg.getTree().tokens.at(msg.span.first); + const last_token = msg.getTree().tokens.at(msg.span.first); + const start_loc = msg.getTree().tokenLocationPtr(0, first_token); if (start_loc.line + 1 == line and start_loc.column + 1 == column) { return; } @@ -231,7 +232,8 @@ pub const TestContext = struct { std.debug.warn("\n====found:========\n"); var stderr = try std.io.getStdErr(); for (msgs) |msg| { - try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto); + defer msg.destroy(); + try msg.printToFile(&stderr, errmsg.Color.Auto); } std.debug.warn("============\n"); return error.TestFailed; diff --git a/std/mem.zig b/std/mem.zig index 2a5b0366a9..43961a6d14 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -35,6 +35,7 @@ pub const Allocator = struct { freeFn: fn (self: *Allocator, old_mem: []u8) void, /// Call `destroy` with the result + /// TODO this is deprecated. use createOne instead pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) { const T = @typeOf(init); if (@sizeOf(T) == 0) return &(T{}); @@ -44,6 +45,14 @@ pub const Allocator = struct { return ptr; } + /// Call `destroy` with the result. + /// Returns undefined memory. + pub fn createOne(self: *Allocator, comptime T: type) Error!*T { + if (@sizeOf(T) == 0) return &(T{}); + const slice = try self.alloc(T, 1); + return &slice[0]; + } + /// `ptr` should be the return value of `create` pub fn destroy(self: *Allocator, ptr: var) void { const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr)); @@ -149,13 +158,12 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { @setRuntimeSafety(false); assert(dest.len >= source.len); var i = source.len; - while(i > 0){ + while (i > 0) { i -= 1; dest[i] = source[i]; } } - pub fn set(comptime T: type, dest: []T, value: T) void { for (dest) |*d| d.* = value; -- cgit v1.2.3 From 7dbbddf2a693ba929ee164310dd73db1fa5d8df8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Jul 2018 15:36:45 -0400 Subject: macho backtraces - use std.sort.sort instead of insertion sort it's way faster --- std/macho.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/macho.zig b/std/macho.zig index 33c170ff43..ddc4d334e4 100644 --- a/std/macho.zig +++ b/std/macho.zig @@ -141,7 +141,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable } // Effectively a no-op, lld emits symbols in ascending order. - std.sort.insertionSort(Symbol, symbols[0..nsyms], Symbol.addressLessThan); + std.sort.sort(Symbol, symbols[0..nsyms], Symbol.addressLessThan); // Insert the sentinel. Since we don't know where the last function ends, // we arbitrarily limit it to the start address + 4 KB. -- cgit v1.2.3 From 2614ef056a83842ec9501bf2e4f21ae840f981f8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Jul 2018 17:38:03 -0400 Subject: self-hosted: basic linker code for macos --- src-self-hosted/compilation.zig | 1 + src-self-hosted/libc_installation.zig | 6 +- src-self-hosted/link.zig | 243 +++++++++++++++++++++++++++++++++- src-self-hosted/target.zig | 33 +++++ src/link.cpp | 2 +- 5 files changed, 277 insertions(+), 8 deletions(-) diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index f1886bd93d..6abb650a62 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -275,6 +275,7 @@ pub const Compilation = struct { LinkFailed, LibCRequiredButNotProvidedOrFound, LibCMissingDynamicLinker, + InvalidDarwinVersionString, }; pub const Event = union(enum) { diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 5a9b7561fa..c9c631a7fb 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -170,7 +170,7 @@ pub const LibCInstallation = struct { try group.call(findNativeDynamicLinker, self, loop); }, builtin.Os.macosx => { - try group.call(findNativeIncludeDirMacOS, self, loop); + self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include"); }, else => @compileError("unimplemented: find libc for this OS"), } @@ -254,10 +254,6 @@ pub const LibCInstallation = struct { @panic("TODO"); } - async fn findNativeIncludeDirMacOS(self: *LibCInstallation, loop: *event.Loop) !void { - self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include"); - } - async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop) FindError!void { // TODO //ZigWindowsSDK *sdk = get_windows_sdk(g); diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 16d9939fff..0a83743ef8 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -1,10 +1,12 @@ const std = @import("std"); +const mem = std.mem; const c = @import("c.zig"); const builtin = @import("builtin"); const ObjectFormat = builtin.ObjectFormat; const Compilation = @import("compilation.zig").Compilation; const Target = @import("target.zig").Target; const LibCInstallation = @import("libc_installation.zig").LibCInstallation; +const assert = std.debug.assert; const Context = struct { comp: *Compilation, @@ -315,8 +317,163 @@ fn constructLinkerArgsCoff(ctx: *Context) void { @panic("TODO"); } -fn constructLinkerArgsMachO(ctx: *Context) void { - @panic("TODO"); +fn constructLinkerArgsMachO(ctx: *Context) !void { + try ctx.args.append(c"-demangle"); + + if (ctx.comp.linker_rdynamic) { + try ctx.args.append(c"-export_dynamic"); + } + + const is_lib = ctx.comp.kind == Compilation.Kind.Lib; + const shared = !ctx.comp.is_static and is_lib; + if (ctx.comp.is_static) { + try ctx.args.append(c"-static"); + } else { + try ctx.args.append(c"-dynamic"); + } + + //if (is_lib) { + // if (!g->is_static) { + // lj->args.append("-dylib"); + + // Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major); + // lj->args.append("-compatibility_version"); + // lj->args.append(buf_ptr(compat_vers)); + + // Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, + // g->version_major, g->version_minor, g->version_patch); + // lj->args.append("-current_version"); + // lj->args.append(buf_ptr(cur_vers)); + + // // TODO getting an error when running an executable when doing this rpath thing + // //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib", + // // buf_ptr(g->root_out_name), g->version_major); + // //lj->args.append("-install_name"); + // //lj->args.append(buf_ptr(dylib_install_name)); + + // if (buf_len(&lj->out_file) == 0) { + // buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", + // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); + // } + // } + //} + + try ctx.args.append(c"-arch"); + const darwin_arch_str = try std.cstr.addNullByte( + &ctx.arena.allocator, + ctx.comp.target.getDarwinArchString(), + ); + try ctx.args.append(darwin_arch_str.ptr); + + const platform = try DarwinPlatform.get(ctx.comp); + switch (platform.kind) { + DarwinPlatform.Kind.MacOS => try ctx.args.append(c"-macosx_version_min"), + DarwinPlatform.Kind.IPhoneOS => try ctx.args.append(c"-iphoneos_version_min"), + DarwinPlatform.Kind.IPhoneOSSimulator => try ctx.args.append(c"-ios_simulator_version_min"), + } + const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro); + try ctx.args.append(ver_str.ptr); + + if (ctx.comp.kind == Compilation.Kind.Exe) { + if (ctx.comp.is_static) { + try ctx.args.append(c"-no_pie"); + } else { + try ctx.args.append(c"-pie"); + } + } + + try ctx.args.append(c"-o"); + try ctx.args.append(ctx.out_file_path.ptr()); + + //for (size_t i = 0; i < g->rpath_list.length; i += 1) { + // Buf *rpath = g->rpath_list.at(i); + // add_rpath(lj, rpath); + //} + //add_rpath(lj, &lj->out_file); + + if (shared) { + try ctx.args.append(c"-headerpad_max_install_names"); + } else if (ctx.comp.is_static) { + try ctx.args.append(c"-lcrt0.o"); + } else { + switch (platform.kind) { + DarwinPlatform.Kind.MacOS => { + if (platform.versionLessThan(10, 5)) { + try ctx.args.append(c"-lcrt1.o"); + } else if (platform.versionLessThan(10, 6)) { + try ctx.args.append(c"-lcrt1.10.5.o"); + } else if (platform.versionLessThan(10, 8)) { + try ctx.args.append(c"-lcrt1.10.6.o"); + } + }, + DarwinPlatform.Kind.IPhoneOS => { + if (ctx.comp.target.getArch() == builtin.Arch.aarch64) { + // iOS does not need any crt1 files for arm64 + } else if (platform.versionLessThan(3, 1)) { + try ctx.args.append(c"-lcrt1.o"); + } else if (platform.versionLessThan(6, 0)) { + try ctx.args.append(c"-lcrt1.3.1.o"); + } + }, + DarwinPlatform.Kind.IPhoneOSSimulator => {}, // no crt1.o needed + } + } + + //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { + // const char *lib_dir = g->lib_dirs.at(i); + // lj->args.append("-L"); + // lj->args.append(lib_dir); + //} + + for (ctx.comp.link_objects) |link_object| { + const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); + try ctx.args.append(link_obj_with_null.ptr); + } + try addFnObjects(ctx); + + //// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce + //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { + // Buf *compiler_rt_o_path = build_compiler_rt(g); + // lj->args.append(buf_ptr(compiler_rt_o_path)); + //} + + if (ctx.comp.target == Target.Native) { + for (ctx.comp.link_libs_list.toSliceConst()) |lib| { + if (mem.eql(u8, lib.name, "c")) { + // on Darwin, libSystem has libc in it, but also you have to use it + // to make syscalls because the syscall numbers are not documented + // and change between versions. + // so we always link against libSystem + try ctx.args.append(c"-lSystem"); + } else { + if (mem.indexOfScalar(u8, lib.name, '/') == null) { + const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name); + try ctx.args.append(arg.ptr); + } else { + const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name); + try ctx.args.append(arg.ptr); + } + } + } + } else { + try ctx.args.append(c"-undefined"); + try ctx.args.append(c"dynamic_lookup"); + } + + if (platform.kind == DarwinPlatform.Kind.MacOS) { + if (platform.versionLessThan(10, 5)) { + try ctx.args.append(c"-lgcc_s.10.4"); + } else if (platform.versionLessThan(10, 6)) { + try ctx.args.append(c"-lgcc_s.10.5"); + } + } else { + @panic("TODO"); + } + + //for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) { + // lj->args.append("-framework"); + // lj->args.append(buf_ptr(g->darwin_frameworks.at(i))); + //} } fn constructLinkerArgsWasm(ctx: *Context) void { @@ -341,3 +498,85 @@ fn addFnObjects(ctx: *Context) !void { it = node.next; } } + +const DarwinPlatform = struct { + kind: Kind, + major: u32, + minor: u32, + micro: u32, + + const Kind = enum { + MacOS, + IPhoneOS, + IPhoneOSSimulator, + }; + + fn get(comp: *Compilation) !DarwinPlatform { + var result: DarwinPlatform = undefined; + const ver_str = switch (comp.darwin_version_min) { + Compilation.DarwinVersionMin.MacOS => |ver| blk: { + result.kind = Kind.MacOS; + break :blk ver; + }, + Compilation.DarwinVersionMin.Ios => |ver| blk: { + result.kind = Kind.IPhoneOS; + break :blk ver; + }, + Compilation.DarwinVersionMin.None => blk: { + assert(comp.target.getOs() == builtin.Os.macosx); + result.kind = Kind.MacOS; + break :blk "10.10"; + }, + }; + + var had_extra: bool = undefined; + try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,); + if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) { + return error.InvalidDarwinVersionString; + } + + if (result.kind == Kind.IPhoneOS) { + switch (comp.target.getArch()) { + builtin.Arch.i386, + builtin.Arch.x86_64, + => result.kind = Kind.IPhoneOSSimulator, + else => {}, + } + } + return result; + } + + fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool { + if (self.major < major) + return true; + if (self.major > major) + return false; + if (self.minor < minor) + return true; + return false; + } +}; + +/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the +/// grouped values as integers. Numbers which are not provided are set to 0. +/// return true if the entire string was parsed (9.2), or all groups were +/// parsed (10.3.5extrastuff). +fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void { + major.* = 0; + minor.* = 0; + micro.* = 0; + had_extra.* = false; + + if (str.len == 0) + return error.InvalidDarwinVersionString; + + var start_pos: usize = 0; + for ([]*u32{major, minor, micro}) |v| { + const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.'); + const end_pos = dot_pos orelse str.len; + v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString; + start_pos = (dot_pos orelse return) + 1; + if (start_pos == str.len) return; + } + had_extra.* = true; +} diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index dccf937a47..0cc8d02a62 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -526,4 +526,37 @@ pub const Target = union(enum) { => @panic("TODO specify the C integer type sizes for this OS"), } } + + pub fn getDarwinArchString(self: Target) []const u8 { + const arch = self.getArch(); + switch (arch) { + builtin.Arch.aarch64 => return "arm64", + builtin.Arch.thumb, + builtin.Arch.armv8_3a, + builtin.Arch.armv8_2a, + builtin.Arch.armv8_1a, + builtin.Arch.armv8, + builtin.Arch.armv8r, + builtin.Arch.armv8m_baseline, + builtin.Arch.armv8m_mainline, + builtin.Arch.armv7, + builtin.Arch.armv7em, + builtin.Arch.armv7m, + builtin.Arch.armv7s, + builtin.Arch.armv7k, + builtin.Arch.armv7ve, + builtin.Arch.armv6, + builtin.Arch.armv6m, + builtin.Arch.armv6k, + builtin.Arch.armv6t2, + builtin.Arch.armv5, + builtin.Arch.armv5te, + builtin.Arch.armv4t, + => return "arm", + builtin.Arch.powerpc => return "ppc", + builtin.Arch.powerpc64 => return "ppc64", + builtin.Arch.powerpc64le => return "ppc64le", + else => return @tagName(arch), + } + } }; diff --git a/src/link.cpp b/src/link.cpp index 2d9a79585f..f65c072bac 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -901,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) { if (strchr(buf_ptr(link_lib->name), '/') == nullptr) { Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); lj->args.append(buf_ptr(arg)); - } else { + } else { lj->args.append(buf_ptr(link_lib->name)); } } -- cgit v1.2.3 From 72599d420b1bebb37efb2179a91d8256287f7c28 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Jul 2018 00:06:34 -0400 Subject: self-hosted: find all libc paths; windows linker code --- src-self-hosted/compilation.zig | 1 + src-self-hosted/libc_installation.zig | 187 +++++++++++++++++++++++----------- src-self-hosted/link.zig | 146 +++++++++++++++++++++++++- src/windows_sdk.cpp | 10 +- std/os/file.zig | 4 +- 5 files changed, 282 insertions(+), 66 deletions(-) diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 6abb650a62..093aab21da 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -276,6 +276,7 @@ pub const Compilation = struct { LibCRequiredButNotProvidedOrFound, LibCMissingDynamicLinker, InvalidDarwinVersionString, + UnsupportedLinkArchitecture, }; pub const Event = union(enum) { diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index c9c631a7fb..3938c0d90c 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -20,8 +20,10 @@ pub const LibCInstallation = struct { CCompilerExitCode, CCompilerCrashed, CCompilerCannotFindHeaders, - CCompilerCannotFindCRuntime, + LibCRuntimeNotFound, LibCStdLibHeaderNotFound, + LibCKernel32LibNotFound, + UnsupportedArchitecture, }; pub fn parse( @@ -111,7 +113,7 @@ pub const LibCInstallation = struct { \\lib_dir={} \\ \\# The directory that contains `crtbegin.o`. - \\# On Linux, can be found with `cc -print-file-name=crt1.o`. + \\# On Linux, can be found with `cc -print-file-name=crtbegin.o`. \\# Not needed when targeting MacOS or Windows. \\static_lib_dir={} \\ @@ -142,21 +144,22 @@ pub const LibCInstallation = struct { self.initEmpty(); var group = event.Group(FindError!void).init(loop); errdefer group.cancelAll(); + var windows_sdk: ?*c.ZigWindowsSDK = null; + errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk)); + switch (builtin.os) { builtin.Os.windows => { var sdk: *c.ZigWindowsSDK = undefined; switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) { c.ZigFindWindowsSdkError.None => { - defer c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk)); + windows_sdk = sdk; - errdefer if (self.msvc_lib_dir) |s| loop.allocator.free(s); if (sdk.msvc_lib_dir_ptr) |ptr| { self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]); } - //try group.call(findNativeIncludeDirWindows, self, loop); - //try group.call(findNativeLibDirWindows, self, loop); - //try group.call(findNativeMsvcLibDir, self, loop); - //try group.call(findNativeKernel32LibDir, self, loop); + try group.call(findNativeKernel32LibDir, self, loop, sdk); + try group.call(findNativeIncludeDirWindows, self, loop, sdk); + try group.call(findNativeLibDirWindows, self, loop, sdk); }, c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory, c.ZigFindWindowsSdkError.NotFound => return error.NotFound, @@ -230,61 +233,64 @@ pub const LibCInstallation = struct { const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h"); defer loop.allocator.free(stdlib_path); - if (std.os.File.access(loop.allocator, stdlib_path)) |_| { + if (try fileExists(loop.allocator, stdlib_path)) { self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path); return; - } else |err| switch (err) { - error.NotFound, error.PermissionDenied => continue, - error.OutOfMemory => return error.OutOfMemory, - else => return error.FileSystem, } } return error.LibCStdLibHeaderNotFound; } - async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop) !void { - // TODO - //ZigWindowsSDK *sdk = get_windows_sdk(g); - //g->libc_include_dir = buf_alloc(); - //if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) { - // fprintf(stderr, "Unable to determine libc include path. --libc-include-dir"); - // exit(1); - //} - @panic("TODO"); + async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) !void { + var search_buf: [2]Search = undefined; + const searches = fillSearch(&search_buf, sdk); + + var result_buf = try std.Buffer.initSize(loop.allocator, 0); + defer result_buf.deinit(); + + for (searches) |search| { + result_buf.shrink(0); + const stream = &std.io.BufferOutStream.init(&result_buf).stream; + try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version); + + const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h"); + defer loop.allocator.free(stdlib_path); + + if (try fileExists(loop.allocator, stdlib_path)) { + self.include_dir = result_buf.toOwnedSlice(); + return; + } + } + + return error.LibCStdLibHeaderNotFound; } - async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop) FindError!void { - // TODO - //ZigWindowsSDK *sdk = get_windows_sdk(g); - - //if (g->msvc_lib_dir == nullptr) { - // Buf* vc_lib_dir = buf_alloc(); - // if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { - // fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir"); - // exit(1); - // } - // g->msvc_lib_dir = vc_lib_dir; - //} - - //if (g->libc_lib_dir == nullptr) { - // Buf* ucrt_lib_path = buf_alloc(); - // if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) { - // fprintf(stderr, "Unable to determine ucrt path. --libc-lib-dir"); - // exit(1); - // } - // g->libc_lib_dir = ucrt_lib_path; - //} - - //if (g->kernel32_lib_dir == nullptr) { - // Buf* kern_lib_path = buf_alloc(); - // if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) { - // fprintf(stderr, "Unable to determine kernel32 path. --kernel32-lib-dir"); - // exit(1); - // } - // g->kernel32_lib_dir = kern_lib_path; - //} - @panic("TODO"); + async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void { + var search_buf: [2]Search = undefined; + const searches = fillSearch(&search_buf, sdk); + + var result_buf = try std.Buffer.initSize(loop.allocator, 0); + defer result_buf.deinit(); + + for (searches) |search| { + result_buf.shrink(0); + const stream = &std.io.BufferOutStream.init(&result_buf).stream; + try stream.print("{}\\Lib\\{}\\ucrt\\", search.path, search.version); + switch (builtin.arch) { + builtin.Arch.i386 => try stream.write("x86"), + builtin.Arch.x86_64 => try stream.write("x64"), + builtin.Arch.aarch64 => try stream.write("arm"), + else => return error.UnsupportedArchitecture, + } + const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib"); + defer loop.allocator.free(ucrt_lib_path); + if (try fileExists(loop.allocator, ucrt_lib_path)) { + self.lib_dir = result_buf.toOwnedSlice(); + return; + } + } + return error.LibCRuntimeNotFound; } async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) FindError!void { @@ -330,17 +336,37 @@ pub const LibCInstallation = struct { dyn_test.result = result; return; } else |err| switch (err) { - error.CCompilerCannotFindCRuntime => return, + error.LibCRuntimeNotFound => return, else => return err, } } - async fn findNativeMsvcLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void { - @panic("TODO"); - } - async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void { - @panic("TODO"); + async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void { + var search_buf: [2]Search = undefined; + const searches = fillSearch(&search_buf, sdk); + + var result_buf = try std.Buffer.initSize(loop.allocator, 0); + defer result_buf.deinit(); + + for (searches) |search| { + result_buf.shrink(0); + const stream = &std.io.BufferOutStream.init(&result_buf).stream; + try stream.print("{}\\Lib\\{}\\um\\", search.path, search.version); + switch (builtin.arch) { + builtin.Arch.i386 => try stream.write("x86\\"), + builtin.Arch.x86_64 => try stream.write("x64\\"), + builtin.Arch.aarch64 => try stream.write("arm\\"), + else => return error.UnsupportedArchitecture, + } + const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib"); + defer loop.allocator.free(kernel32_path); + if (try fileExists(loop.allocator, kernel32_path)) { + self.kernel32_lib_dir = result_buf.toOwnedSlice(); + return; + } + } + return error.LibCKernel32LibNotFound; } fn initEmpty(self: *LibCInstallation) void { @@ -386,8 +412,8 @@ async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bo }, } var it = std.mem.split(exec_result.stdout, "\n\r"); - const line = it.next() orelse return error.CCompilerCannotFindCRuntime; - const dirname = std.os.path.dirname(line) orelse return error.CCompilerCannotFindCRuntime; + const line = it.next() orelse return error.LibCRuntimeNotFound; + const dirname = std.os.path.dirname(line) orelse return error.LibCRuntimeNotFound; if (want_dirname) { return std.mem.dupe(loop.allocator, u8, dirname); @@ -395,3 +421,42 @@ async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bo return std.mem.dupe(loop.allocator, u8, line); } } + +const Search = struct { + path: []const u8, + version: []const u8, +}; + +fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search { + var search_end: usize = 0; + if (sdk.path10_ptr) |path10_ptr| { + if (sdk.version10_ptr) |ver10_ptr| { + search_buf[search_end] = Search{ + .path = path10_ptr[0..sdk.path10_len], + .version = ver10_ptr[0..sdk.version10_len], + }; + search_end += 1; + } + } + if (sdk.path81_ptr) |path81_ptr| { + if (sdk.version81_ptr) |ver81_ptr| { + search_buf[search_end] = Search{ + .path = path81_ptr[0..sdk.path81_len], + .version = ver81_ptr[0..sdk.version81_len], + }; + search_end += 1; + } + } + return search_buf[0..search_end]; +} + + +fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool { + if (std.os.File.access(allocator, path)) |_| { + return true; + } else |err| switch (err) { + error.NotFound, error.PermissionDenied => return false, + error.OutOfMemory => return error.OutOfMemory, + else => return error.FileSystem, + } +} diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 0a83743ef8..b9eefa9d4f 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -313,8 +313,150 @@ fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void { try ctx.args.append(full_path_with_null.ptr); } -fn constructLinkerArgsCoff(ctx: *Context) void { - @panic("TODO"); +fn constructLinkerArgsCoff(ctx: *Context) !void { + try ctx.args.append(c"-NOLOGO"); + + if (!ctx.comp.strip) { + try ctx.args.append(c"-DEBUG"); + } + + switch (ctx.comp.target.getArch()) { + builtin.Arch.i386 => try ctx.args.append(c"-MACHINE:X86"), + builtin.Arch.x86_64 => try ctx.args.append(c"-MACHINE:X64"), + builtin.Arch.aarch64 => try ctx.args.append(c"-MACHINE:ARM"), + else => return error.UnsupportedLinkArchitecture, + } + + if (ctx.comp.windows_subsystem_windows) { + try ctx.args.append(c"/SUBSYSTEM:windows"); + } else if (ctx.comp.windows_subsystem_console) { + try ctx.args.append(c"/SUBSYSTEM:console"); + } + + const is_library = ctx.comp.kind == Compilation.Kind.Lib; + + const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst()); + try ctx.args.append(out_arg.ptr); + + if (ctx.comp.haveLibC()) { + try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr); + try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr); + try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr); + } + + if (ctx.link_in_crt) { + const lib_str = if (ctx.comp.is_static) "lib" else ""; + const d_str = if (ctx.comp.build_mode == builtin.Mode.Debug) "d" else ""; + + if (ctx.comp.is_static) { + const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str); + try ctx.args.append(cmt_lib_name.ptr); + } else { + const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str); + try ctx.args.append(msvcrt_lib_name.ptr); + } + + const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str); + try ctx.args.append(vcruntime_lib_name.ptr); + + const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str); + try ctx.args.append(crt_lib_name.ptr); + + // Visual C++ 2015 Conformance Changes + // https://msdn.microsoft.com/en-us/library/bb531344.aspx + try ctx.args.append(c"legacy_stdio_definitions.lib"); + + // msvcrt depends on kernel32 + try ctx.args.append(c"kernel32.lib"); + } else { + try ctx.args.append(c"-NODEFAULTLIB"); + if (!is_library) { + try ctx.args.append(c"-ENTRY:WinMainCRTStartup"); + // TODO + //if (g->have_winmain) { + // lj->args.append("-ENTRY:WinMain"); + //} else { + // lj->args.append("-ENTRY:WinMainCRTStartup"); + //} + } + } + + if (is_library and !ctx.comp.is_static) { + try ctx.args.append(c"-DLL"); + } + + //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { + // const char *lib_dir = g->lib_dirs.at(i); + // lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir))); + //} + + for (ctx.comp.link_objects) |link_object| { + const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); + try ctx.args.append(link_obj_with_null.ptr); + } + try addFnObjects(ctx); + + switch (ctx.comp.kind) { + Compilation.Kind.Exe, Compilation.Kind.Lib => { + if (!ctx.comp.haveLibC()) { + @panic("TODO"); + //Buf *builtin_o_path = build_o(g, "builtin"); + //lj->args.append(buf_ptr(builtin_o_path)); + } + + // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage + // TODO + //Buf *compiler_rt_o_path = build_compiler_rt(g); + //lj->args.append(buf_ptr(compiler_rt_o_path)); + }, + Compilation.Kind.Obj => {}, + } + + //Buf *def_contents = buf_alloc(); + //ZigList gen_lib_args = {0}; + //for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { + // LinkLib *link_lib = g->link_libs_list.at(lib_i); + // if (buf_eql_str(link_lib->name, "c")) { + // continue; + // } + // if (link_lib->provided_explicitly) { + // if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) { + // Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); + // lj->args.append(buf_ptr(arg)); + // } + // else { + // lj->args.append(buf_ptr(link_lib->name)); + // } + // } else { + // buf_resize(def_contents, 0); + // buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name)); + // for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) { + // Buf *symbol_name = link_lib->symbols.at(exp_i); + // buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name)); + // } + // buf_appendf(def_contents, "\n"); + + // Buf *def_path = buf_alloc(); + // os_path_join(g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path); + // os_write_file(def_path, def_contents); + + // Buf *generated_lib_path = buf_alloc(); + // os_path_join(g->cache_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path); + + // gen_lib_args.resize(0); + // gen_lib_args.append("link"); + + // coff_append_machine_arg(g, &gen_lib_args); + // gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path)))); + // gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path)))); + // Buf diag = BUF_INIT; + // if (!zig_lld_link(g->zig_target.oformat, gen_lib_args.items, gen_lib_args.length, &diag)) { + // fprintf(stderr, "%s\n", buf_ptr(&diag)); + // exit(1); + // } + // lj->args.append(buf_ptr(generated_lib_path)); + // } + //} } fn constructLinkerArgsMachO(ctx: *Context) !void { diff --git a/src/windows_sdk.cpp b/src/windows_sdk.cpp index 059bdee4e9..0f9d0fc301 100644 --- a/src/windows_sdk.cpp +++ b/src/windows_sdk.cpp @@ -287,7 +287,10 @@ ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) { } rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len); if (rc == ERROR_SUCCESS) { - priv->base.path10_len = tmp_buf_len; + priv->base.path10_len = tmp_buf_len - 1; + if (priv->base.path10_ptr[priv->base.path10_len - 1] == '\\') { + priv->base.path10_len -= 1; + } } else { free((void*)priv->base.path10_ptr); priv->base.path10_ptr = nullptr; @@ -302,7 +305,10 @@ ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) { } rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len); if (rc == ERROR_SUCCESS) { - priv->base.path81_len = tmp_buf_len; + priv->base.path81_len = tmp_buf_len - 1; + if (priv->base.path81_ptr[priv->base.path81_len - 1] == '\\') { + priv->base.path81_len -= 1; + } } else { free((void*)priv->base.path81_ptr); priv->base.path81_ptr = nullptr; diff --git a/std/os/file.zig b/std/os/file.zig index 52bc590f77..6998ba00d1 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -139,7 +139,9 @@ pub const File = struct { const err = windows.GetLastError(); switch (err) { - windows.ERROR.FILE_NOT_FOUND => return error.NotFound, + windows.ERROR.FILE_NOT_FOUND, + windows.ERROR.PATH_NOT_FOUND, + => return error.NotFound, windows.ERROR.ACCESS_DENIED => return error.PermissionDenied, else => return os.unexpectedErrorWindows(err), } -- cgit v1.2.3 From 0046551852fe97dca2c295b84be3b0b4c3004e45 Mon Sep 17 00:00:00 2001 From: Nathan Sharp Date: Mon, 23 Jul 2018 23:24:53 -0700 Subject: std.io: PeekStream and SliceStream SliceStream is a read-only stream wrapper around a slice of bytes. It allows adapting algorithms which work on InStreams to in-memory data. PeekStream is a stream wrapper which allows "putting back" bytes into the stream so that they can be read again. This will help make look-ahead parsers easier to write. --- std/io.zig | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ std/io_test.zig | 51 ++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) diff --git a/std/io.zig b/std/io.zig index 1c468f6f4f..71a9822399 100644 --- a/std/io.zig +++ b/std/io.zig @@ -331,6 +331,104 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) }; } +/// Creates a stream which supports 'un-reading' data, so that it can be read again. +/// This makes look-ahead style parsing much easier. +pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) type { + return struct { + const Self = this; + pub const Error = InStreamError; + pub const Stream = InStream(Error); + + pub stream: Stream, + base: *Stream, + + // Right now the look-ahead space is statically allocated, but a version with dynamic allocation + // is not too difficult to derive from this. + buffer: [buffer_size]u8, + index: usize, + at_end: bool, + + pub fn init(base: *Stream) Self { + return Self{ + .base = base, + .buffer = undefined, + .index = 0, + .at_end = false, + .stream = Stream{ .readFn = readFn }, + }; + } + + pub fn putBackByte(self: *Self, byte: u8) void { + self.buffer[self.index] = byte; + self.index += 1; + } + + pub fn putBack(self: *Self, bytes: []const u8) void { + var pos = bytes.len; + while (pos != 0) { + pos -= 1; + self.putBackByte(bytes[pos]); + } + } + + fn readFn(in_stream: *Stream, dest: []u8) Error!usize { + const self = @fieldParentPtr(Self, "stream", in_stream); + + // copy over anything putBack()'d + var pos: usize = 0; + while (pos < dest.len and self.index != 0) { + dest[pos] = self.buffer[self.index - 1]; + self.index -= 1; + pos += 1; + } + + if (pos == dest.len or self.at_end) { + return pos; + } + + // ask the backing stream for more + const left = dest.len - pos; + const read = try self.base.read(dest[pos..]); + assert(read <= left); + + self.at_end = (read < left); + return pos + read; + } + + }; +} + +pub const SliceStream = struct { + const Self = this; + pub const Error = error { }; + pub const Stream = InStream(Error); + + pub stream: Stream, + + pos: usize, + slice: []const u8, + + pub fn init(slice: []const u8) Self { + return Self{ + .slice = slice, + .pos = 0, + .stream = Stream{ .readFn = readFn }, + }; + } + + fn readFn(in_stream: *Stream, dest: []u8) Error!usize { + const self = @fieldParentPtr(Self, "stream", in_stream); + const size = math.min(dest.len, self.slice.len - self.pos); + const end = self.pos + size; + + mem.copy(u8, dest[0..size], self.slice[self.pos..end]); + self.pos = end; + + return size; + } + +}; + pub fn BufferedOutStream(comptime Error: type) type { return BufferedOutStreamCustom(os.page_size, Error); } diff --git a/std/io_test.zig b/std/io_test.zig index 301a9a4cd0..8d5c35c5fd 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -60,3 +60,54 @@ test "BufferOutStream" { assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n")); } + +test "SliceStream" { + const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7 }; + var ss = io.SliceStream.init(bytes); + + var dest: [4]u8 = undefined; + + var read = try ss.stream.read(dest[0..4]); + assert(read == 4); + assert(mem.eql(u8, dest[0..4], bytes[0..4])); + + read = try ss.stream.read(dest[0..4]); + assert(read == 3); + assert(mem.eql(u8, dest[0..3], bytes[4..7])); + + read = try ss.stream.read(dest[0..4]); + assert(read == 0); +} + +test "PeekStream" { + const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; + var ss = io.SliceStream.init(bytes); + var ps = io.PeekStream(2, io.SliceStream.Error).init(&ss.stream); + + var dest: [4]u8 = undefined; + + ps.putBackByte(9); + ps.putBackByte(10); + + var read = try ps.stream.read(dest[0..4]); + assert(read == 4); + assert(dest[0] == 10); + assert(dest[1] == 9); + assert(mem.eql(u8, dest[2..4], bytes[0..2])); + + read = try ps.stream.read(dest[0..4]); + assert(read == 4); + assert(mem.eql(u8, dest[0..4], bytes[2..6])); + + read = try ps.stream.read(dest[0..4]); + assert(read == 2); + assert(mem.eql(u8, dest[0..2], bytes[6..8])); + + ps.putBackByte(11); + ps.putBackByte(12); + + read = try ps.stream.read(dest[0..4]); + assert(read == 2); + assert(dest[0] == 12); + assert(dest[1] == 11); +} -- cgit v1.2.3 From 29e19ace362e7a1910b9f105257f2bce2491e32b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Jul 2018 10:13:40 -0400 Subject: fix logic for determining whether param requires comptime closes #778 closes #1213 --- src/analyze.cpp | 14 +++++++++----- test/compile_errors.zig | 11 +++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 6bbe5f6037..f399ab8305 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1585,10 +1585,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdMetaType: - add_node_error(g, param_node->data.param_decl.type, - buf_sprintf("parameter of type '%s' must be declared comptime", - buf_ptr(&type_entry->name))); - return g->builtin_types.entry_invalid; case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: @@ -1603,6 +1599,13 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdPromise: + type_ensure_zero_bits_known(g, type_entry); + if (type_requires_comptime(type_entry)) { + add_node_error(g, param_node->data.param_decl.type, + buf_sprintf("parameter of type '%s' must be declared comptime", + buf_ptr(&type_entry->name))); + return g->builtin_types.entry_invalid; + } break; } FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; @@ -5019,9 +5022,10 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { } else { return type_requires_comptime(type_entry->data.pointer.child_type); } + case TypeTableEntryIdFn: + return type_entry->data.fn.is_generic; case TypeTableEntryIdEnum: case TypeTableEntryIdErrorSet: - case TypeTableEntryIdFn: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d5582b1584..b7bd39f29e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,17 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "generic fn as parameter without comptime keyword", + \\fn f(_: fn (var) void) void {} + \\fn g(_: var) void {} + \\export fn entry() void { + \\ f(g); + \\} + , + ".tmp_source.zig:1:9: error: parameter of type 'fn(var)var' must be declared comptime", + ); + cases.add( "optional pointer to void in extern struct", \\comptime { -- cgit v1.2.3 From 1d4a94b63525b7f9a980069de1807d03d0ad98e0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Jul 2018 11:04:01 -0400 Subject: remove old section from readme we still want all these people but I think there are better ways to communicate this than the readme file --- README.md | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/README.md b/README.md index 6e582a27e7..2ee3f178ce 100644 --- a/README.md +++ b/README.md @@ -74,44 +74,6 @@ that counts as "freestanding" for the purposes of this table. * Reddit: [/r/zig](https://www.reddit.com/r/zig) * Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang) -### Wanted: Windows Developers - -Flesh out the standard library for Windows, streamline Zig installation and -distribution for Windows. Work with LLVM and LLD teams to improve -PDB/CodeView/MSVC debugging. Implement stack traces for Windows in the MinGW -environment and the MSVC environment. - -### Wanted: MacOS and iOS Developers - -Flesh out the standard library for MacOS. Improve the MACH-O linker. Implement -stack traces for MacOS. Streamline the process of using Zig to build for -iOS. - -### Wanted: Android Developers - -Flesh out the standard library for Android. Streamline the process of using -Zig to build for Android and for depending on Zig code on Android. - -### Wanted: Web Developers - -Figure out what are the use cases for compiling Zig to WebAssembly. Create demo -projects with it and streamline experience for users trying to output -WebAssembly. Work on the documentation generator outputting useful searchable html -documentation. Create Zig modules for common web tasks such as WebSockets and gzip. - -### Wanted: Embedded Developers - -Flesh out the standard library for uncommon CPU architectures and OS targets. -Drive issue discussion for cross compiling and using Zig in constrained -or unusual environments. - -### Wanted: Game Developers - -Create cross platform Zig modules to compete with SDL and GLFW. Create an -OpenGL library that does not depend on libc. Drive the usability of Zig -for video games. Create a general purpose allocator that does not depend on -libc. Create demo games using Zig. - ## Building [![Build Status](https://travis-ci.org/ziglang/zig.svg?branch=master)](https://travis-ci.org/ziglang/zig) -- cgit v1.2.3 From 2ea08561cf69dabc99722ffc24cb0e4327605506 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Jul 2018 14:20:49 -0400 Subject: self-hosted: function types use table lookup --- src-self-hosted/codegen.zig | 3 +- src-self-hosted/compilation.zig | 69 +++++++- src-self-hosted/ir.zig | 8 +- src-self-hosted/type.zig | 338 +++++++++++++++++++++++++++++++++------- src/analyze.cpp | 8 +- 5 files changed, 356 insertions(+), 70 deletions(-) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index ad3dce061e..88293c845e 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -168,6 +168,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) //} const fn_type = fn_val.base.typ.cast(Type.Fn).?; + const fn_type_normal = &fn_type.key.data.Normal; try addLLVMFnAttr(ofile, llvm_fn, "nounwind"); //add_uwtable_attr(g, fn_table_entry->llvm_value); @@ -209,7 +210,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) // addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull"); //} - const cur_ret_ptr = if (fn_type.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null; + const cur_ret_ptr = if (fn_type_normal.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null; // build all basic blocks for (code.basic_block_list.toSlice()) |bb| { diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 093aab21da..8d41e2439b 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -220,12 +220,14 @@ pub const Compilation = struct { int_type_table: event.Locked(IntTypeTable), array_type_table: event.Locked(ArrayTypeTable), ptr_type_table: event.Locked(PtrTypeTable), + fn_type_table: event.Locked(FnTypeTable), c_int_types: [CInt.list.len]*Type.Int, const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql); const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql); const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql); + const FnTypeTable = std.HashMap(*const Type.Fn.Key, *Type.Fn, Type.Fn.Key.hash, Type.Fn.Key.eql); const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8); const CompileErrList = std.ArrayList(*Msg); @@ -384,6 +386,7 @@ pub const Compilation = struct { .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)), .array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)), .ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)), + .fn_type_table = event.Locked(FnTypeTable).init(loop, FnTypeTable.init(loop.allocator)), .c_int_types = undefined, .meta_type = undefined, @@ -414,6 +417,7 @@ pub const Compilation = struct { comp.int_type_table.private_data.deinit(); comp.array_type_table.private_data.deinit(); comp.ptr_type_table.private_data.deinit(); + comp.fn_type_table.private_data.deinit(); comp.arena_allocator.deinit(); comp.loop.allocator.destroy(comp); } @@ -1160,10 +1164,47 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { fn_decl.value = Decl.Fn.Val{ .Fn = fn_val }; symbol_name_consumed = true; + // Define local parameter variables + //for (size_t i = 0; i < fn_type_id->param_count; i += 1) { + // FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; + // AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i); + // Buf *param_name; + // bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args; + // if (param_decl_node && !is_var_args) { + // param_name = param_decl_node->data.param_decl.name; + // } else { + // param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i); + // } + // if (param_name == nullptr) { + // continue; + // } + + // TypeTableEntry *param_type = param_info->type; + // bool is_noalias = param_info->is_noalias; + + // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) { + // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter")); + // } + + // VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope, + // param_name, true, create_const_runtime(param_type), nullptr); + // var->src_arg_index = i; + // fn_table_entry->child_scope = var->child_scope; + // var->shadowable = var->shadowable || is_var_args; + + // if (type_has_bits(param_type)) { + // fn_table_entry->variable_list.append(var); + // } + + // if (fn_type->data.fn.gen_param_info) { + // var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; + // } + //} + const analyzed_code = try await (async comp.genAndAnalyzeCode( &fndef_scope.base, body_node, - fn_type.return_type, + fn_type.key.data.Normal.return_type, ) catch unreachable); errdefer analyzed_code.destroy(comp.gpa()); @@ -1199,14 +1240,13 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn var params = ArrayList(Type.Fn.Param).init(comp.gpa()); var params_consumed = false; - defer if (params_consumed) { + defer if (!params_consumed) { for (params.toSliceConst()) |param| { param.typ.base.deref(comp); } params.deinit(); }; - const is_var_args = false; { var it = fn_proto.params.iterator(0); while (it.next()) |param_node_ptr| { @@ -1219,8 +1259,29 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn }); } } - const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args); + + const key = Type.Fn.Key{ + .alignment = null, + .data = Type.Fn.Key.Data{ + .Normal = Type.Fn.Normal{ + .return_type = return_type, + .params = params.toOwnedSlice(), + .is_var_args = false, // TODO + .cc = Type.Fn.CallingConvention.Auto, // TODO + }, + }, + }; params_consumed = true; + var key_consumed = false; + defer if (!key_consumed) { + for (key.data.Normal.params) |param| { + param.typ.base.deref(comp); + } + comp.gpa().free(key.data.Normal.params); + }; + + const fn_type = try await (async Type.Fn.get(comp, key) catch unreachable); + key_consumed = true; errdefer fn_type.base.base.deref(comp); return fn_type; diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index c34f06753d..45355bbf2c 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -281,11 +281,13 @@ pub const Inst = struct { return error.SemanticAnalysisFailed; }; - if (fn_type.params.len != self.params.args.len) { + const fn_type_param_count = fn_type.paramCount(); + + if (fn_type_param_count != self.params.args.len) { try ira.addCompileError( self.base.span, "expected {} arguments, found {}", - fn_type.params.len, + fn_type_param_count, self.params.args.len, ); return error.SemanticAnalysisFailed; @@ -299,7 +301,7 @@ pub const Inst = struct { .fn_ref = fn_ref, .args = args, }); - new_inst.val = IrVal{ .KnownType = fn_type.return_type }; + new_inst.val = IrVal{ .KnownType = fn_type.key.data.Normal.return_type }; return new_inst; } diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 217c1d50a7..3b57260447 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -221,57 +221,267 @@ pub const Type = struct { pub const Fn = struct { base: Type, - return_type: *Type, - params: []Param, - is_var_args: bool, + key: Key, + garbage_node: std.atomic.Stack(*Fn).Node, + + pub const Key = struct { + data: Data, + alignment: ?u32, + + pub const Data = union(enum) { + Generic: Generic, + Normal: Normal, + }; + + pub fn hash(self: *const Key) u32 { + var result: u32 = 0; + result +%= hashAny(self.alignment, 0); + switch (self.data) { + Data.Generic => |generic| { + result +%= hashAny(generic.param_count, 1); + switch (generic.cc) { + CallingConvention.Async => |allocator_type| result +%= hashAny(allocator_type, 2), + else => result +%= hashAny(CallingConvention(generic.cc), 3), + } + }, + Data.Normal => |normal| { + result +%= hashAny(normal.return_type, 4); + result +%= hashAny(normal.is_var_args, 5); + result +%= hashAny(normal.cc, 6); + for (normal.params) |param| { + result +%= hashAny(param.is_noalias, 7); + result +%= hashAny(param.typ, 8); + } + }, + } + return result; + } + + pub fn eql(self: *const Key, other: *const Key) bool { + if ((self.alignment == null) != (other.alignment == null)) return false; + if (self.alignment) |self_align| { + if (self_align != other.alignment.?) return false; + } + if (@TagType(Data)(self.data) != @TagType(Data)(other.data)) return false; + switch (self.data) { + Data.Generic => |*self_generic| { + const other_generic = &other.data.Generic; + if (self_generic.param_count != other_generic.param_count) return false; + if (CallingConvention(self_generic.cc) != CallingConvention(other_generic.cc)) return false; + switch (self_generic.cc) { + CallingConvention.Async => |self_allocator_type| { + const other_allocator_type = other_generic.cc.Async; + if (self_allocator_type != other_allocator_type) return false; + }, + else => {}, + } + }, + Data.Normal => |*self_normal| { + const other_normal = &other.data.Normal; + if (self_normal.cc != other_normal.cc) return false; + if (self_normal.is_var_args != other_normal.is_var_args) return false; + if (self_normal.return_type != other_normal.return_type) return false; + for (self_normal.params) |*self_param, i| { + const other_param = &other_normal.params[i]; + if (self_param.is_noalias != other_param.is_noalias) return false; + if (self_param.typ != other_param.typ) return false; + } + }, + } + return true; + } + + pub fn deref(key: Key, comp: *Compilation) void { + switch (key.data) { + Key.Data.Generic => |generic| { + switch (generic.cc) { + CallingConvention.Async => |allocator_type| allocator_type.base.deref(comp), + else => {}, + } + }, + Key.Data.Normal => |normal| { + normal.return_type.base.deref(comp); + for (normal.params) |param| { + param.typ.base.deref(comp); + } + }, + } + } + + pub fn ref(key: Key) void { + switch (key.data) { + Key.Data.Generic => |generic| { + switch (generic.cc) { + CallingConvention.Async => |allocator_type| allocator_type.base.ref(), + else => {}, + } + }, + Key.Data.Normal => |normal| { + normal.return_type.base.ref(); + for (normal.params) |param| { + param.typ.base.ref(); + } + }, + } + } + }; + + pub const Normal = struct { + params: []Param, + return_type: *Type, + is_var_args: bool, + cc: CallingConvention, + }; + + pub const Generic = struct { + param_count: usize, + cc: CC, + + pub const CC = union(CallingConvention) { + Auto, + C, + Cold, + Naked, + Stdcall, + Async: *Type, // allocator type + }; + }; + + pub const CallingConvention = enum { + Auto, + C, + Cold, + Naked, + Stdcall, + Async, + }; pub const Param = struct { is_noalias: bool, typ: *Type, }; - pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { - const result = try comp.gpa().create(Fn{ + fn ccFnTypeStr(cc: CallingConvention) []const u8 { + return switch (cc) { + CallingConvention.Auto => "", + CallingConvention.C => "extern ", + CallingConvention.Cold => "coldcc ", + CallingConvention.Naked => "nakedcc ", + CallingConvention.Stdcall => "stdcallcc ", + CallingConvention.Async => unreachable, + }; + } + + pub fn paramCount(self: *Fn) usize { + return switch (self.key.data) { + Key.Data.Generic => |generic| generic.param_count, + Key.Data.Normal => |normal| normal.params.len, + }; + } + + /// takes ownership of key.Normal.params on success + pub async fn get(comp: *Compilation, key: Key) !*Fn { + { + const held = await (async comp.fn_type_table.acquire() catch unreachable); + defer held.release(); + + if (held.value.get(&key)) |entry| { + entry.value.base.base.ref(); + return entry.value; + } + } + + key.ref(); + errdefer key.deref(comp); + + const self = try comp.gpa().create(Fn{ .base = undefined, - .return_type = return_type, - .params = params, - .is_var_args = is_var_args, + .key = key, + .garbage_node = undefined, }); - errdefer comp.gpa().destroy(result); + errdefer comp.gpa().destroy(self); - result.base.init(comp, Id.Fn, "TODO fn type name"); + var name_buf = try std.Buffer.initSize(comp.gpa(), 0); + defer name_buf.deinit(); + + const name_stream = &std.io.BufferOutStream.init(&name_buf).stream; + + switch (key.data) { + Key.Data.Generic => |generic| { + switch (generic.cc) { + CallingConvention.Async => |async_allocator_type| { + try name_stream.print("async<{}> ", async_allocator_type.name); + }, + else => { + const cc_str = ccFnTypeStr(generic.cc); + try name_stream.write(cc_str); + }, + } + try name_stream.write("fn("); + var param_i: usize = 0; + while (param_i < generic.param_count) : (param_i += 1) { + const arg = if (param_i == 0) "var" else ", var"; + try name_stream.write(arg); + } + try name_stream.write(")"); + if (key.alignment) |alignment| { + try name_stream.print(" align<{}>", alignment); + } + try name_stream.write(" var"); + }, + Key.Data.Normal => |normal| { + const cc_str = ccFnTypeStr(normal.cc); + try name_stream.print("{}fn(", cc_str); + for (normal.params) |param, i| { + if (i != 0) try name_stream.write(", "); + if (param.is_noalias) try name_stream.write("noalias "); + try name_stream.write(param.typ.name); + } + if (normal.is_var_args) { + if (normal.params.len != 0) try name_stream.write(", "); + try name_stream.write("..."); + } + try name_stream.write(")"); + if (key.alignment) |alignment| { + try name_stream.print(" align<{}>", alignment); + } + try name_stream.print(" {}", normal.return_type.name); + }, + } + + self.base.init(comp, Id.Fn, name_buf.toOwnedSlice()); - result.return_type.base.ref(); - for (result.params) |param| { - param.typ.base.ref(); + { + const held = await (async comp.fn_type_table.acquire() catch unreachable); + defer held.release(); + + _ = try held.value.put(&self.key, self); } - return result; + return self; } pub fn destroy(self: *Fn, comp: *Compilation) void { - self.return_type.base.deref(comp); - for (self.params) |param| { - param.typ.base.deref(comp); - } + self.key.deref(comp); comp.gpa().destroy(self); } pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { - const llvm_return_type = switch (self.return_type.id) { + const normal = &self.key.data.Normal; + const llvm_return_type = switch (normal.return_type.id) { Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory, - else => try self.return_type.getLlvmType(allocator, llvm_context), + else => try normal.return_type.getLlvmType(allocator, llvm_context), }; - const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len); + const llvm_param_types = try allocator.alloc(llvm.TypeRef, normal.params.len); defer allocator.free(llvm_param_types); for (llvm_param_types) |*llvm_param_type, i| { - llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context); + llvm_param_type.* = try normal.params[i].typ.getLlvmType(allocator, llvm_context); } return llvm.FunctionType( llvm_return_type, llvm_param_types.ptr, @intCast(c_uint, llvm_param_types.len), - @boolToInt(self.is_var_args), + @boolToInt(normal.is_var_args), ) orelse error.OutOfMemory; } }; @@ -347,8 +557,10 @@ pub const Type = struct { is_signed: bool, pub fn hash(self: *const Key) u32 { - const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 }; - return rands[@boolToInt(self.is_signed)] *% self.bit_count; + var result: u32 = 0; + result +%= hashAny(self.is_signed, 0); + result +%= hashAny(self.bit_count, 1); + return result; } pub fn eql(self: *const Key, other: *const Key) bool { @@ -443,15 +655,16 @@ pub const Type = struct { alignment: Align, pub fn hash(self: *const Key) u32 { - const align_hash = switch (self.alignment) { + var result: u32 = 0; + result +%= switch (self.alignment) { Align.Abi => 0xf201c090, - Align.Override => |x| x, + Align.Override => |x| hashAny(x, 0), }; - return hash_usize(@ptrToInt(self.child_type)) *% - hash_enum(self.mut) *% - hash_enum(self.vol) *% - hash_enum(self.size) *% - align_hash; + result +%= hashAny(self.child_type, 1); + result +%= hashAny(self.mut, 2); + result +%= hashAny(self.vol, 3); + result +%= hashAny(self.size, 4); + return result; } pub fn eql(self: *const Key, other: *const Key) bool { @@ -605,7 +818,10 @@ pub const Type = struct { len: usize, pub fn hash(self: *const Key) u32 { - return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len); + var result: u32 = 0; + result +%= hashAny(self.elem_type, 0); + result +%= hashAny(self.len, 1); + return result; } pub fn eql(self: *const Key, other: *const Key) bool { @@ -818,27 +1034,37 @@ pub const Type = struct { }; }; -fn hash_usize(x: usize) u32 { - return switch (@sizeOf(usize)) { - 4 => x, - 8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d), - else => @compileError("implement this hash function"), - }; -} - -fn hash_enum(x: var) u32 { - const rands = []u32{ - 0x85ebf64f, - 0x3fcb3211, - 0x240a4e8e, - 0x40bb0e3c, - 0x78be45af, - 0x1ca98e37, - 0xec56053a, - 0x906adc48, - 0xd4fe9763, - 0x54c80dac, - }; - comptime assert(@memberCount(@typeOf(x)) < rands.len); - return rands[@enumToInt(x)]; +fn hashAny(x: var, comptime seed: u64) u32 { + switch (@typeInfo(@typeOf(x))) { + builtin.TypeId.Int => |info| { + comptime var rng = comptime std.rand.DefaultPrng.init(seed); + const unsigned_x = @bitCast(@IntType(false, info.bits), x); + if (info.bits <= 32) { + return u32(unsigned_x) *% comptime rng.random.scalar(u32); + } else { + return @truncate(u32, unsigned_x *% comptime rng.random.scalar(@typeOf(unsigned_x))); + } + }, + builtin.TypeId.Pointer => |info| { + switch (info.size) { + builtin.TypeInfo.Pointer.Size.One => return hashAny(@ptrToInt(x), seed), + builtin.TypeInfo.Pointer.Size.Many => @compileError("implement hash function"), + builtin.TypeInfo.Pointer.Size.Slice => @compileError("implement hash function"), + } + }, + builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed), + builtin.TypeId.Bool => { + comptime var rng = comptime std.rand.DefaultPrng.init(seed); + const vals = comptime [2]u32{ rng.random.scalar(u32), rng.random.scalar(u32) }; + return vals[@boolToInt(x)]; + }, + builtin.TypeId.Optional => { + if (x) |non_opt| { + return hashAny(non_opt, seed); + } else { + return hashAny(u32(1), seed); + } + }, + else => @compileError("implement hash function for " ++ @typeName(@typeOf(x))), + } } diff --git a/src/analyze.cpp b/src/analyze.cpp index f399ab8305..a4bfff78c3 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3941,7 +3941,7 @@ AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index) { return nullptr; } -static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars) { +static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry) { TypeTableEntry *fn_type = fn_table_entry->type_entry; assert(!fn_type->data.fn.is_generic); FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; @@ -3979,10 +3979,6 @@ static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entr if (fn_type->data.fn.gen_param_info) { var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; } - - if (arg_vars) { - arg_vars[i] = var; - } } } @@ -4082,7 +4078,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { if (!fn_table_entry->child_scope) fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base; - define_local_param_variables(g, fn_table_entry, nullptr); + define_local_param_variables(g, fn_table_entry); TypeTableEntry *fn_type = fn_table_entry->type_entry; assert(!fn_type->data.fn.is_generic); -- cgit v1.2.3 From adefd1a52b812813dd3e3590d398f927ffc5b9af Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Jul 2018 20:24:05 -0400 Subject: self-hosted: function calling another function --- src-self-hosted/codegen.zig | 160 +++++++++++++++++++++++++++++++- src-self-hosted/compilation.zig | 72 +++++++------- src-self-hosted/ir.zig | 197 ++++++++++++++++++++++++++++++++++----- src-self-hosted/llvm.zig | 15 ++- src-self-hosted/scope.zig | 201 +++++++++++++++++++++++++--------------- src-self-hosted/type.zig | 105 +++++++++++++-------- src-self-hosted/value.zig | 22 ++++- 7 files changed, 597 insertions(+), 175 deletions(-) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 88293c845e..5ca01ca7e7 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -6,6 +6,7 @@ const c = @import("c.zig"); const ir = @import("ir.zig"); const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; +const Scope = @import("scope.zig").Scope; const event = std.event; const assert = std.debug.assert; const DW = std.dwarf; @@ -156,7 +157,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) llvm_fn_type, ) orelse return error.OutOfMemory; - const want_fn_safety = fn_val.block_scope.safety.get(ofile.comp); + const want_fn_safety = fn_val.block_scope.?.safety.get(ofile.comp); if (want_fn_safety and ofile.comp.haveLibC()) { try addLLVMFnAttr(ofile, llvm_fn, "sspstrong"); try addLLVMFnAttrStr(ofile, llvm_fn, "stack-protector-buffer-size", "4"); @@ -227,9 +228,86 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) // TODO set up error return tracing // TODO allocate temporary stack values - // TODO create debug variable declarations for variables and allocate all local variables + + const var_list = fn_type.non_key.Normal.variable_list.toSliceConst(); + // create debug variable declarations for variables and allocate all local variables + for (var_list) |var_scope, i| { + const var_type = switch (var_scope.data) { + Scope.Var.Data.Const => unreachable, + Scope.Var.Data.Param => |param| param.typ, + }; + // if (!type_has_bits(var->value->type)) { + // continue; + // } + // if (ir_get_var_is_comptime(var)) + // continue; + // if (type_requires_comptime(var->value->type)) + // continue; + // if (var->src_arg_index == SIZE_MAX) { + // var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes); + + // var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), + // buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1), + // var->value->type->di_type, !g->strip_debug_symbols, 0); + + // } else { + // it's a parameter + // assert(var->gen_arg_index != SIZE_MAX); + // TypeTableEntry *gen_type; + // FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index]; + + if (var_type.handleIsPtr()) { + // if (gen_info->is_byval) { + // gen_type = var->value->type; + // } else { + // gen_type = gen_info->type; + // } + var_scope.data.Param.llvm_value = llvm.GetParam(llvm_fn, @intCast(c_uint, i)); + } else { + // gen_type = var->value->type; + var_scope.data.Param.llvm_value = try renderAlloca(ofile, var_type, var_scope.name, Type.Pointer.Align.Abi); + } + // if (var->decl_node) { + // var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), + // buf_ptr(&var->name), import->di_file, + // (unsigned)(var->decl_node->line + 1), + // gen_type->di_type, !g->strip_debug_symbols, 0, (unsigned)(var->gen_arg_index + 1)); + // } + + // } + } + // TODO finishing error return trace setup. we have to do this after all the allocas. - // TODO create debug variable declarations for parameters + + // create debug variable declarations for parameters + // rely on the first variables in the variable_list being parameters. + //size_t next_var_i = 0; + for (fn_type.key.data.Normal.params) |param, i| { + //FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i]; + //if (info->gen_index == SIZE_MAX) + // continue; + const scope_var = var_list[i]; + //assert(variable->src_arg_index != SIZE_MAX); + //next_var_i += 1; + //assert(variable); + //assert(variable->value_ref); + + if (!param.typ.handleIsPtr()) { + //clear_debug_source_node(g); + const llvm_param = llvm.GetParam(llvm_fn, @intCast(c_uint, i)); + _ = renderStoreUntyped( + ofile, + llvm_param, + scope_var.data.Param.llvm_value, + Type.Pointer.Align.Abi, + Type.Pointer.Vol.Non, + ); + } + + //if (variable->decl_node) { + // gen_var_debug_decl(g, variable); + //} + } for (code.basic_block_list.toSlice()) |current_block| { llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block); @@ -294,3 +372,79 @@ fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []cons fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void { return addLLVMAttrInt(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val); } + +fn renderLoadUntyped( + ofile: *ObjectFile, + ptr: llvm.ValueRef, + alignment: Type.Pointer.Align, + vol: Type.Pointer.Vol, + name: [*]const u8, +) !llvm.ValueRef { + const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory; + switch (vol) { + Type.Pointer.Vol.Non => {}, + Type.Pointer.Vol.Volatile => llvm.SetVolatile(result, 1), + } + llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.GetElementType(llvm.TypeOf(ptr)))); + return result; +} + +fn renderLoad(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer, name: [*]const u8) !llvm.ValueRef { + return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name); +} + +pub fn getHandleValue(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer) !?llvm.ValueRef { + const child_type = ptr_type.key.child_type; + if (!child_type.hasBits()) { + return null; + } + if (child_type.handleIsPtr()) { + return ptr; + } + return try renderLoad(ofile, ptr, ptr_type, c""); +} + +pub fn renderStoreUntyped( + ofile: *ObjectFile, + value: llvm.ValueRef, + ptr: llvm.ValueRef, + alignment: Type.Pointer.Align, + vol: Type.Pointer.Vol, +) !llvm.ValueRef { + const result = llvm.BuildStore(ofile.builder, value, ptr) orelse return error.OutOfMemory; + switch (vol) { + Type.Pointer.Vol.Non => {}, + Type.Pointer.Vol.Volatile => llvm.SetVolatile(result, 1), + } + llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.TypeOf(value))); + return result; +} + +pub fn renderStore( + ofile: *ObjectFile, + value: llvm.ValueRef, + ptr: llvm.ValueRef, + ptr_type: *Type.Pointer, +) !llvm.ValueRef { + return renderStoreUntyped(ofile, value, ptr, ptr_type.key.alignment, ptr_type.key.vol); +} + +pub fn renderAlloca( + ofile: *ObjectFile, + var_type: *Type, + name: []const u8, + alignment: Type.Pointer.Align, +) !llvm.ValueRef { + const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context); + const name_with_null = try std.cstr.addNullByte(ofile.arena, name); + const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, name_with_null.ptr) orelse return error.OutOfMemory; + llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm_var_type)); + return result; +} + +pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: llvm.TypeRef) u32 { + return switch (alignment) { + Type.Pointer.Align.Abi => return llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, llvm_type), + Type.Pointer.Align.Override => |a| a, + }; +} diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 8d41e2439b..1564abfbd3 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -1165,49 +1165,47 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { symbol_name_consumed = true; // Define local parameter variables - //for (size_t i = 0; i < fn_type_id->param_count; i += 1) { - // FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; - // AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i); - // Buf *param_name; - // bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args; - // if (param_decl_node && !is_var_args) { - // param_name = param_decl_node->data.param_decl.name; - // } else { - // param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i); - // } - // if (param_name == nullptr) { - // continue; - // } - - // TypeTableEntry *param_type = param_info->type; - // bool is_noalias = param_info->is_noalias; - - // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) { - // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter")); - // } - - // VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope, - // param_name, true, create_const_runtime(param_type), nullptr); - // var->src_arg_index = i; - // fn_table_entry->child_scope = var->child_scope; - // var->shadowable = var->shadowable || is_var_args; - - // if (type_has_bits(param_type)) { - // fn_table_entry->variable_list.append(var); - // } - - // if (fn_type->data.fn.gen_param_info) { - // var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; - // } - //} + const root_scope = fn_decl.base.findRootScope(); + for (fn_type.key.data.Normal.params) |param, i| { + //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i); + const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*); + const name_token = param_decl.name_token orelse { + try comp.addCompileError(root_scope, Span{ + .first = param_decl.firstToken(), + .last = param_decl.type_node.firstToken(), + }, "missing parameter name"); + return error.SemanticAnalysisFailed; + }; + const param_name = root_scope.tree.tokenSlice(name_token); + + // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) { + // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter")); + // } + + // TODO check for shadowing + + const var_scope = try Scope.Var.createParam( + comp, + fn_val.child_scope, + param_name, + ¶m_decl.base, + i, + param.typ, + ); + fn_val.child_scope = &var_scope.base; + + try fn_type.non_key.Normal.variable_list.append(var_scope); + } const analyzed_code = try await (async comp.genAndAnalyzeCode( - &fndef_scope.base, + fn_val.child_scope, body_node, fn_type.key.data.Normal.return_type, ) catch unreachable); errdefer analyzed_code.destroy(comp.gpa()); + assert(fn_val.block_scope != null); + // Kick off rendering to LLVM module, but it doesn't block the fn decl // analysis from being complete. try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); @@ -1263,7 +1261,7 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn const key = Type.Fn.Key{ .alignment = null, .data = Type.Fn.Key.Data{ - .Normal = Type.Fn.Normal{ + .Normal = Type.Fn.Key.Normal{ .return_type = return_type, .params = params.toOwnedSlice(), .is_var_args = false, // TODO diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 45355bbf2c..619cd4f330 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -10,8 +10,10 @@ const assert = std.debug.assert; const Token = std.zig.Token; const Span = @import("errmsg.zig").Span; const llvm = @import("llvm.zig"); -const ObjectFile = @import("codegen.zig").ObjectFile; +const codegen = @import("codegen.zig"); +const ObjectFile = codegen.ObjectFile; const Decl = @import("decl.zig").Decl; +const mem = std.mem; pub const LVal = enum { None, @@ -122,6 +124,8 @@ pub const Inst = struct { Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira), Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira), Id.PtrType => return await (async @fieldParentPtr(PtrType, "base", base).analyze(ira) catch unreachable), + Id.VarPtr => return await (async @fieldParentPtr(VarPtr, "base", base).analyze(ira) catch unreachable), + Id.LoadPtr => return await (async @fieldParentPtr(LoadPtr, "base", base).analyze(ira) catch unreachable), } } @@ -130,6 +134,8 @@ pub const Inst = struct { Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val), Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val), Id.Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val), + Id.VarPtr => return @fieldParentPtr(VarPtr, "base", base).render(ofile, fn_val), + Id.LoadPtr => return @fieldParentPtr(LoadPtr, "base", base).render(ofile, fn_val), Id.DeclRef => unreachable, Id.PtrType => unreachable, Id.Ref => @panic("TODO"), @@ -248,6 +254,8 @@ pub const Inst = struct { Call, DeclRef, PtrType, + VarPtr, + LoadPtr, }; pub const Call = struct { @@ -491,6 +499,133 @@ pub const Inst = struct { } }; + pub const VarPtr = struct { + base: Inst, + params: Params, + + const Params = struct { + var_scope: *Scope.Var, + }; + + const ir_val_init = IrVal.Init.Unknown; + + pub fn dump(inst: *const VarPtr) void { + std.debug.warn("{}", inst.params.var_scope.name); + } + + pub fn hasSideEffects(inst: *const VarPtr) bool { + return false; + } + + pub async fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst { + switch (self.params.var_scope.data) { + Scope.Var.Data.Const => @panic("TODO"), + Scope.Var.Data.Param => |param| { + const new_inst = try ira.irb.build( + Inst.VarPtr, + self.base.scope, + self.base.span, + Inst.VarPtr.Params{ .var_scope = self.params.var_scope }, + ); + const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ + .child_type = param.typ, + .mut = Type.Pointer.Mut.Const, + .vol = Type.Pointer.Vol.Non, + .size = Type.Pointer.Size.One, + .alignment = Type.Pointer.Align.Abi, + }) catch unreachable); + new_inst.val = IrVal{ .KnownType = &ptr_type.base }; + return new_inst; + }, + } + } + + pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) llvm.ValueRef { + switch (self.params.var_scope.data) { + Scope.Var.Data.Const => unreachable, // turned into Inst.Const in analyze pass + Scope.Var.Data.Param => |param| return param.llvm_value, + } + } + }; + + pub const LoadPtr = struct { + base: Inst, + params: Params, + + const Params = struct { + target: *Inst, + }; + + const ir_val_init = IrVal.Init.Unknown; + + pub fn dump(inst: *const LoadPtr) void {} + + pub fn hasSideEffects(inst: *const LoadPtr) bool { + return false; + } + + pub async fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst { + const target = try self.params.target.getAsParam(); + const target_type = target.getKnownType(); + if (target_type.id != Type.Id.Pointer) { + try ira.addCompileError(self.base.span, "dereference of non pointer type '{}'", target_type.name); + return error.SemanticAnalysisFailed; + } + const ptr_type = @fieldParentPtr(Type.Pointer, "base", target_type); + // if (instr_is_comptime(ptr)) { + // if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst || + // ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) + // { + // ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &ptr->value); + // if (pointee->special != ConstValSpecialRuntime) { + // IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, + // source_instruction->source_node, child_type); + // copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst); + // result->value.type = child_type; + // return result; + // } + // } + // } + const new_inst = try ira.irb.build( + Inst.LoadPtr, + self.base.scope, + self.base.span, + Inst.LoadPtr.Params{ .target = target }, + ); + new_inst.val = IrVal{ .KnownType = ptr_type.key.child_type }; + return new_inst; + } + + pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { + const child_type = self.base.getKnownType(); + if (!child_type.hasBits()) { + return null; + } + const ptr = self.params.target.llvm_value.?; + const ptr_type = self.params.target.getKnownType().cast(Type.Pointer).?; + + return try codegen.getHandleValue(ofile, ptr, ptr_type); + + //uint32_t unaligned_bit_count = ptr_type->data.pointer.unaligned_bit_count; + //if (unaligned_bit_count == 0) + // return get_handle_value(g, ptr, child_type, ptr_type); + + //bool big_endian = g->is_big_endian; + + //assert(!handle_is_ptr(child_type)); + //LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, ""); + + //uint32_t bit_offset = ptr_type->data.pointer.bit_offset; + //uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); + //uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset; + + //LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); + //LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, ""); + + //return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, ""); + } + }; + pub const PtrType = struct { base: Inst, params: Params, @@ -1160,6 +1295,7 @@ pub const Builder = struct { Scope.Id.Block, Scope.Id.Defer, Scope.Id.DeferExpr, + Scope.Id.Var, => scope = scope.parent.?, } } @@ -1261,8 +1397,8 @@ pub const Builder = struct { var child_scope = outer_block_scope; if (parent_scope.findFnDef()) |fndef_scope| { - if (fndef_scope.fn_val.child_scope == parent_scope) { - fndef_scope.fn_val.block_scope = block_scope; + if (fndef_scope.fn_val.?.block_scope == null) { + fndef_scope.fn_val.?.block_scope = block_scope; } } @@ -1492,20 +1628,23 @@ pub const Builder = struct { error.OutOfMemory => return error.OutOfMemory, } - //VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name); - //if (var) { - // IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var); - // if (lval == LValPtr) - // return var_ptr; - // else - // return ir_build_load_ptr(irb, scope, node, var_ptr); - //} - - if (await (async irb.findDecl(scope, name) catch unreachable)) |decl| { - return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{ - .decl = decl, - .lval = lval, - }); + switch (await (async irb.findIdent(scope, name) catch unreachable)) { + Ident.Decl => |decl| { + return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{ + .decl = decl, + .lval = lval, + }); + }, + Ident.VarScope => |var_scope| { + const var_ptr = try irb.build(Inst.VarPtr, scope, src_span, Inst.VarPtr.Params{ .var_scope = var_scope }); + switch (lval) { + LVal.Ptr => return var_ptr, + LVal.None => { + return irb.build(Inst.LoadPtr, scope, src_span, Inst.LoadPtr.Params{ .target = var_ptr }); + }, + } + }, + Ident.NotFound => {}, } //if (node->owner->any_imports_failed) { @@ -1546,6 +1685,7 @@ pub const Builder = struct { Scope.Id.Block, Scope.Id.Decls, Scope.Id.Root, + Scope.Id.Var, => scope = scope.parent orelse break, Scope.Id.DeferExpr => unreachable, @@ -1596,6 +1736,7 @@ pub const Builder = struct { Scope.Id.CompTime, Scope.Id.Block, + Scope.Id.Var, => scope = scope.parent orelse return is_noreturn, Scope.Id.DeferExpr => unreachable, @@ -1674,8 +1815,10 @@ pub const Builder = struct { Type.Pointer.Size, LVal, *Decl, + *Scope.Var, => {}, - // it's ok to add more types here, just make sure any instructions are ref'd appropriately + // it's ok to add more types here, just make sure that + // any instructions and basic blocks are ref'd appropriately else => @compileError("unrecognized type in Params: " ++ @typeName(FieldType)), } } @@ -1773,18 +1916,30 @@ pub const Builder = struct { //// the above blocks are rendered by ir_gen after the rest of codegen } - async fn findDecl(irb: *Builder, scope: *Scope, name: []const u8) ?*Decl { + const Ident = union(enum) { + NotFound, + Decl: *Decl, + VarScope: *Scope.Var, + }; + + async fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident { var s = scope; while (true) { switch (s.id) { + Scope.Id.Root => return Ident.NotFound, Scope.Id.Decls => { const decls = @fieldParentPtr(Scope.Decls, "base", s); const table = await (async decls.getTableReadOnly() catch unreachable); if (table.get(name)) |entry| { - return entry.value; + return Ident{ .Decl = entry.value }; + } + }, + Scope.Id.Var => { + const var_scope = @fieldParentPtr(Scope.Var, "base", s); + if (mem.eql(u8, var_scope.name, name)) { + return Ident{ .VarScope = var_scope }; } }, - Scope.Id.Root => return null, else => {}, } s = s.parent.?; diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index 8bb45ac616..778d3fae07 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -30,6 +30,7 @@ pub const AddGlobal = c.LLVMAddGlobal; pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag; pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag; pub const ArrayType = c.LLVMArrayType; +pub const BuildLoad = c.LLVMBuildLoad; pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; pub const ConstAllOnes = c.LLVMConstAllOnes; pub const ConstArray = c.LLVMConstArray; @@ -95,13 +96,25 @@ pub const SetInitializer = c.LLVMSetInitializer; pub const SetLinkage = c.LLVMSetLinkage; pub const SetTarget = c.LLVMSetTarget; pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr; +pub const SetVolatile = c.LLVMSetVolatile; pub const StructTypeInContext = c.LLVMStructTypeInContext; pub const TokenTypeInContext = c.LLVMTokenTypeInContext; -pub const TypeOf = c.LLVMTypeOf; pub const VoidTypeInContext = c.LLVMVoidTypeInContext; pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; +pub const GetElementType = LLVMGetElementType; +extern fn LLVMGetElementType(Ty: TypeRef) TypeRef; + +pub const TypeOf = LLVMTypeOf; +extern fn LLVMTypeOf(Val: ValueRef) TypeRef; + +pub const BuildStore = LLVMBuildStore; +extern fn LLVMBuildStore(arg0: BuilderRef, Val: ValueRef, Ptr: ValueRef) ?ValueRef; + +pub const BuildAlloca = LLVMBuildAlloca; +extern fn LLVMBuildAlloca(arg0: BuilderRef, Ty: TypeRef, Name: ?[*]const u8) ?ValueRef; + pub const ConstInBoundsGEP = LLVMConstInBoundsGEP; pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef; diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 7a41083f44..a38e765c6e 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -6,23 +6,26 @@ const Compilation = @import("compilation.zig").Compilation; const mem = std.mem; const ast = std.zig.ast; const Value = @import("value.zig").Value; +const Type = @import("type.zig").Type; const ir = @import("ir.zig"); const Span = @import("errmsg.zig").Span; const assert = std.debug.assert; const event = std.event; +const llvm = @import("llvm.zig"); pub const Scope = struct { id: Id, parent: ?*Scope, - ref_count: usize, + ref_count: std.atomic.Int(usize), + /// Thread-safe pub fn ref(base: *Scope) void { - base.ref_count += 1; + _ = base.ref_count.incr(); } + /// Thread-safe pub fn deref(base: *Scope, comp: *Compilation) void { - base.ref_count -= 1; - if (base.ref_count == 0) { + if (base.ref_count.decr() == 1) { if (base.parent) |parent| parent.deref(comp); switch (base.id) { Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp), @@ -32,6 +35,7 @@ pub const Scope = struct { Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp), Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp), Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp), + Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp), } } } @@ -49,15 +53,15 @@ pub const Scope = struct { var scope = base; while (true) { switch (scope.id) { - Id.FnDef => return @fieldParentPtr(FnDef, "base", base), - Id.Decls => return null, + Id.FnDef => return @fieldParentPtr(FnDef, "base", scope), + Id.Root, Id.Decls => return null, Id.Block, Id.Defer, Id.DeferExpr, Id.CompTime, - Id.Root, - => scope = scope.parent orelse return null, + Id.Var, + => scope = scope.parent.?, } } } @@ -66,7 +70,7 @@ pub const Scope = struct { var scope = base; while (true) { switch (scope.id) { - Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base), + Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", scope), Id.FnDef, Id.Decls, @@ -76,11 +80,21 @@ pub const Scope = struct { Id.Defer, Id.CompTime, Id.Root, + Id.Var, => scope = scope.parent orelse return null, } } } + fn init(base: *Scope, id: Id, parent: *Scope) void { + base.* = Scope{ + .id = id, + .parent = parent, + .ref_count = std.atomic.Int(usize).init(1), + }; + parent.ref(); + } + pub const Id = enum { Root, Decls, @@ -89,6 +103,7 @@ pub const Scope = struct { CompTime, Defer, DeferExpr, + Var, }; pub const Root = struct { @@ -100,16 +115,16 @@ pub const Scope = struct { /// Takes ownership of realpath /// Takes ownership of tree, will deinit and destroy when done. pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root { - const self = try comp.gpa().create(Root{ + const self = try comp.gpa().createOne(Root); + self.* = Root{ .base = Scope{ .id = Id.Root, .parent = null, - .ref_count = 1, + .ref_count = std.atomic.Int(usize).init(1), }, .tree = tree, .realpath = realpath, - }); - errdefer comp.gpa().destroy(self); + }; return self; } @@ -137,16 +152,13 @@ pub const Scope = struct { /// Creates a Decls scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope) !*Decls { - const self = try comp.gpa().create(Decls{ - .base = Scope{ - .id = Id.Decls, - .parent = parent, - .ref_count = 1, - }, + const self = try comp.gpa().createOne(Decls); + self.* = Decls{ + .base = undefined, .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())), .name_future = event.Future(void).init(comp.loop), - }); - parent.ref(); + }; + self.base.init(Id.Decls, parent); return self; } @@ -199,21 +211,16 @@ pub const Scope = struct { /// Creates a Block scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope) !*Block { - const self = try comp.gpa().create(Block{ - .base = Scope{ - .id = Id.Block, - .parent = parent, - .ref_count = 1, - }, + const self = try comp.gpa().createOne(Block); + self.* = Block{ + .base = undefined, .incoming_values = undefined, .incoming_blocks = undefined, .end_block = undefined, .is_comptime = undefined, .safety = Safety.Auto, - }); - errdefer comp.gpa().destroy(self); - - parent.ref(); + }; + self.base.init(Id.Block, parent); return self; } @@ -226,22 +233,17 @@ pub const Scope = struct { base: Scope, /// This reference is not counted so that the scope can get destroyed with the function - fn_val: *Value.Fn, + fn_val: ?*Value.Fn, /// Creates a FnDef scope with 1 reference /// Must set the fn_val later pub fn create(comp: *Compilation, parent: *Scope) !*FnDef { - const self = try comp.gpa().create(FnDef{ - .base = Scope{ - .id = Id.FnDef, - .parent = parent, - .ref_count = 1, - }, - .fn_val = undefined, - }); - - parent.ref(); - + const self = try comp.gpa().createOne(FnDef); + self.* = FnDef{ + .base = undefined, + .fn_val = null, + }; + self.base.init(Id.FnDef, parent); return self; } @@ -255,15 +257,9 @@ pub const Scope = struct { /// Creates a CompTime scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope) !*CompTime { - const self = try comp.gpa().create(CompTime{ - .base = Scope{ - .id = Id.CompTime, - .parent = parent, - .ref_count = 1, - }, - }); - - parent.ref(); + const self = try comp.gpa().createOne(CompTime); + self.* = CompTime{ .base = undefined }; + self.base.init(Id.CompTime, parent); return self; } @@ -289,20 +285,14 @@ pub const Scope = struct { kind: Kind, defer_expr_scope: *DeferExpr, ) !*Defer { - const self = try comp.gpa().create(Defer{ - .base = Scope{ - .id = Id.Defer, - .parent = parent, - .ref_count = 1, - }, + const self = try comp.gpa().createOne(Defer); + self.* = Defer{ + .base = undefined, .defer_expr_scope = defer_expr_scope, .kind = kind, - }); - errdefer comp.gpa().destroy(self); - + }; + self.base.init(Id.Defer, parent); defer_expr_scope.base.ref(); - - parent.ref(); return self; } @@ -319,18 +309,13 @@ pub const Scope = struct { /// Creates a DeferExpr scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr { - const self = try comp.gpa().create(DeferExpr{ - .base = Scope{ - .id = Id.DeferExpr, - .parent = parent, - .ref_count = 1, - }, + const self = try comp.gpa().createOne(DeferExpr); + self.* = DeferExpr{ + .base = undefined, .expr_node = expr_node, .reported_err = false, - }); - errdefer comp.gpa().destroy(self); - - parent.ref(); + }; + self.base.init(Id.DeferExpr, parent); return self; } @@ -338,4 +323,74 @@ pub const Scope = struct { comp.gpa().destroy(self); } }; + + pub const Var = struct { + base: Scope, + name: []const u8, + src_node: *ast.Node, + data: Data, + + pub const Data = union(enum) { + Param: Param, + Const: *Value, + }; + + pub const Param = struct { + index: usize, + typ: *Type, + llvm_value: llvm.ValueRef, + }; + + pub fn createParam( + comp: *Compilation, + parent: *Scope, + name: []const u8, + src_node: *ast.Node, + param_index: usize, + param_type: *Type, + ) !*Var { + const self = try create(comp, parent, name, src_node); + self.data = Data{ + .Param = Param{ + .index = param_index, + .typ = param_type, + .llvm_value = undefined, + }, + }; + return self; + } + + pub fn createConst( + comp: *Compilation, + parent: *Scope, + name: []const u8, + src_node: *ast.Node, + value: *Value, + ) !*Var { + const self = try create(comp, parent, name, src_node); + self.data = Data{ .Const = value }; + value.ref(); + return self; + } + + fn create(comp: *Compilation, parent: *Scope, name: []const u8, src_node: *ast.Node) !*Var { + const self = try comp.gpa().createOne(Var); + self.* = Var{ + .base = undefined, + .name = name, + .src_node = src_node, + .data = undefined, + }; + self.base.init(Id.Var, parent); + return self; + } + + pub fn destroy(self: *Var, comp: *Compilation) void { + switch (self.data) { + Data.Param => {}, + Data.Const => |value| value.deref(comp), + } + comp.gpa().destroy(self); + } + }; }; diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 3b57260447..6783130fc7 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -141,9 +141,13 @@ pub const Type = struct { Id.Promise, => return true, + Id.Pointer => { + const ptr_type = @fieldParentPtr(Pointer, "base", base); + return ptr_type.key.child_type.hasBits(); + }, + Id.ErrorSet => @panic("TODO"), Id.Enum => @panic("TODO"), - Id.Pointer => @panic("TODO"), Id.Struct => @panic("TODO"), Id.Array => @panic("TODO"), Id.Optional => @panic("TODO"), @@ -222,29 +226,65 @@ pub const Type = struct { pub const Fn = struct { base: Type, key: Key, + non_key: NonKey, garbage_node: std.atomic.Stack(*Fn).Node, + pub const Kind = enum { + Normal, + Generic, + }; + + pub const NonKey = union { + Normal: Normal, + Generic: void, + + pub const Normal = struct { + variable_list: std.ArrayList(*Scope.Var), + }; + }; + pub const Key = struct { data: Data, alignment: ?u32, - pub const Data = union(enum) { + pub const Data = union(Kind) { Generic: Generic, Normal: Normal, }; + pub const Normal = struct { + params: []Param, + return_type: *Type, + is_var_args: bool, + cc: CallingConvention, + }; + + pub const Generic = struct { + param_count: usize, + cc: CC, + + pub const CC = union(CallingConvention) { + Auto, + C, + Cold, + Naked, + Stdcall, + Async: *Type, // allocator type + }; + }; + pub fn hash(self: *const Key) u32 { var result: u32 = 0; result +%= hashAny(self.alignment, 0); switch (self.data) { - Data.Generic => |generic| { + Kind.Generic => |generic| { result +%= hashAny(generic.param_count, 1); switch (generic.cc) { CallingConvention.Async => |allocator_type| result +%= hashAny(allocator_type, 2), else => result +%= hashAny(CallingConvention(generic.cc), 3), } }, - Data.Normal => |normal| { + Kind.Normal => |normal| { result +%= hashAny(normal.return_type, 4); result +%= hashAny(normal.is_var_args, 5); result +%= hashAny(normal.cc, 6); @@ -264,7 +304,7 @@ pub const Type = struct { } if (@TagType(Data)(self.data) != @TagType(Data)(other.data)) return false; switch (self.data) { - Data.Generic => |*self_generic| { + Kind.Generic => |*self_generic| { const other_generic = &other.data.Generic; if (self_generic.param_count != other_generic.param_count) return false; if (CallingConvention(self_generic.cc) != CallingConvention(other_generic.cc)) return false; @@ -276,7 +316,7 @@ pub const Type = struct { else => {}, } }, - Data.Normal => |*self_normal| { + Kind.Normal => |*self_normal| { const other_normal = &other.data.Normal; if (self_normal.cc != other_normal.cc) return false; if (self_normal.is_var_args != other_normal.is_var_args) return false; @@ -293,13 +333,13 @@ pub const Type = struct { pub fn deref(key: Key, comp: *Compilation) void { switch (key.data) { - Key.Data.Generic => |generic| { + Kind.Generic => |generic| { switch (generic.cc) { CallingConvention.Async => |allocator_type| allocator_type.base.deref(comp), else => {}, } }, - Key.Data.Normal => |normal| { + Kind.Normal => |normal| { normal.return_type.base.deref(comp); for (normal.params) |param| { param.typ.base.deref(comp); @@ -310,13 +350,13 @@ pub const Type = struct { pub fn ref(key: Key) void { switch (key.data) { - Key.Data.Generic => |generic| { + Kind.Generic => |generic| { switch (generic.cc) { CallingConvention.Async => |allocator_type| allocator_type.base.ref(), else => {}, } }, - Key.Data.Normal => |normal| { + Kind.Normal => |normal| { normal.return_type.base.ref(); for (normal.params) |param| { param.typ.base.ref(); @@ -326,27 +366,6 @@ pub const Type = struct { } }; - pub const Normal = struct { - params: []Param, - return_type: *Type, - is_var_args: bool, - cc: CallingConvention, - }; - - pub const Generic = struct { - param_count: usize, - cc: CC, - - pub const CC = union(CallingConvention) { - Auto, - C, - Cold, - Naked, - Stdcall, - Async: *Type, // allocator type - }; - }; - pub const CallingConvention = enum { Auto, C, @@ -374,8 +393,8 @@ pub const Type = struct { pub fn paramCount(self: *Fn) usize { return switch (self.key.data) { - Key.Data.Generic => |generic| generic.param_count, - Key.Data.Normal => |normal| normal.params.len, + Kind.Generic => |generic| generic.param_count, + Kind.Normal => |normal| normal.params.len, }; } @@ -394,11 +413,13 @@ pub const Type = struct { key.ref(); errdefer key.deref(comp); - const self = try comp.gpa().create(Fn{ + const self = try comp.gpa().createOne(Fn); + self.* = Fn{ .base = undefined, .key = key, + .non_key = undefined, .garbage_node = undefined, - }); + }; errdefer comp.gpa().destroy(self); var name_buf = try std.Buffer.initSize(comp.gpa(), 0); @@ -407,7 +428,8 @@ pub const Type = struct { const name_stream = &std.io.BufferOutStream.init(&name_buf).stream; switch (key.data) { - Key.Data.Generic => |generic| { + Kind.Generic => |generic| { + self.non_key = NonKey{ .Generic = {} }; switch (generic.cc) { CallingConvention.Async => |async_allocator_type| { try name_stream.print("async<{}> ", async_allocator_type.name); @@ -429,7 +451,10 @@ pub const Type = struct { } try name_stream.write(" var"); }, - Key.Data.Normal => |normal| { + Kind.Normal => |normal| { + self.non_key = NonKey{ + .Normal = NonKey.Normal{ .variable_list = std.ArrayList(*Scope.Var).init(comp.gpa()) }, + }; const cc_str = ccFnTypeStr(normal.cc); try name_stream.print("{}fn(", cc_str); for (normal.params) |param, i| { @@ -462,6 +487,12 @@ pub const Type = struct { pub fn destroy(self: *Fn, comp: *Compilation) void { self.key.deref(comp); + switch (self.key.data) { + Kind.Generic => {}, + Kind.Normal => { + self.non_key.Normal.variable_list.deinit(); + }, + } comp.gpa().destroy(self); } diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 2005e3c119..e6dca4eff7 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -60,7 +60,7 @@ pub const Value = struct { pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) { switch (base.id) { Id.Type => unreachable, - Id.Fn => @panic("TODO"), + Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile), Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile), Id.Void => return null, Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile), @@ -180,7 +180,7 @@ pub const Value = struct { child_scope: *Scope, /// parent is child_scope - block_scope: *Scope.Block, + block_scope: ?*Scope.Block, /// Path to the object file that contains this function containing_object: Buffer, @@ -205,7 +205,7 @@ pub const Value = struct { }, .fndef_scope = fndef_scope, .child_scope = &fndef_scope.base, - .block_scope = undefined, + .block_scope = null, .symbol_name = symbol_name, .containing_object = Buffer.initNull(comp.gpa()), .link_set_node = link_set_node, @@ -231,6 +231,22 @@ pub const Value = struct { self.symbol_name.deinit(); comp.gpa().destroy(self); } + + /// We know that the function definition will end up in an .o file somewhere. + /// Here, all we have to do is generate a global prototype. + /// TODO cache the prototype per ObjectFile + pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?llvm.ValueRef { + const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); + const llvm_fn = llvm.AddFunction( + ofile.module, + self.symbol_name.ptr(), + llvm_fn_type, + ) orelse return error.OutOfMemory; + + // TODO port more logic from codegen.cpp:fn_llvm_value + + return llvm_fn; + } }; pub const Void = struct { -- cgit v1.2.3 From 02713e8d8aa9641616bd85e77dda784009c96113 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Jul 2018 21:28:54 -0400 Subject: fix race conditions in self-hosted compiler; add test * fix race condition in std.event.Channel deinit * add support to zig build for --no-rosegment * add passing self-hosted compare-output test for calling a function * put a global lock on LLD linking because it's not thread safe --- build.zig | 4 ++++ src-self-hosted/compilation.zig | 3 +++ src-self-hosted/link.zig | 33 +++++++++++++++++++++++---------- std/build.zig | 21 +++++++++++++++++++++ std/event/channel.zig | 25 +++---------------------- test/stage2/compare_output.zig | 13 +++++++++++++ 6 files changed, 67 insertions(+), 32 deletions(-) diff --git a/build.zig b/build.zig index e7a5c5cba1..dd939365a2 100644 --- a/build.zig +++ b/build.zig @@ -45,6 +45,7 @@ pub fn build(b: *Builder) !void { .c_header_files = nextValue(&index, build_info), .dia_guids_lib = nextValue(&index, build_info), .llvm = undefined, + .no_rosegment = b.option(bool, "no-rosegment", "Workaround to enable valgrind builds") orelse false, }; ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); @@ -228,6 +229,8 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { // TODO turn this into -Dextra-lib-path=/lib option exe.addLibPath("/lib"); + exe.setNoRoSegment(ctx.no_rosegment); + exe.addIncludeDir("src"); exe.addIncludeDir(ctx.cmake_binary_dir); addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); @@ -286,4 +289,5 @@ const Context = struct { c_header_files: []const u8, dia_guids_lib: []const u8, llvm: LibraryDep, + no_rosegment: bool, }; diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 1564abfbd3..5ff8b1a858 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -35,6 +35,7 @@ const CInt = @import("c_int.zig").CInt; pub const EventLoopLocal = struct { loop: *event.Loop, llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), + lld_lock: event.Lock, /// TODO pool these so that it doesn't have to lock prng: event.Locked(std.rand.DefaultPrng), @@ -55,6 +56,7 @@ pub const EventLoopLocal = struct { return EventLoopLocal{ .loop = loop, + .lld_lock = event.Lock.init(loop), .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), .native_libc = event.Future(LibCInstallation).init(loop), @@ -63,6 +65,7 @@ pub const EventLoopLocal = struct { /// Must be called only after EventLoop.run completes. fn deinit(self: *EventLoopLocal) void { + self.lld_lock.deinit(); while (self.llvm_handle_pool.pop()) |node| { c.LLVMContextDispose(node.data); self.loop.allocator.destroy(node); diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index b9eefa9d4f..3b79c5b891 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -80,15 +80,22 @@ pub async fn link(comp: *Compilation) !void { const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat()); const args_slice = ctx.args.toSlice(); - // Not evented I/O. LLD does its own multithreading internally. - if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) { - if (!ctx.link_msg.isNull()) { - // TODO capture these messages and pass them through the system, reporting them through the - // event system instead of printing them directly here. - // perhaps try to parse and understand them. - std.debug.warn("{}\n", ctx.link_msg.toSliceConst()); + + { + // LLD is not thread-safe, so we grab a global lock. + const held = await (async comp.event_loop_local.lld_lock.acquire() catch unreachable); + defer held.release(); + + // Not evented I/O. LLD does its own multithreading internally. + if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) { + if (!ctx.link_msg.isNull()) { + // TODO capture these messages and pass them through the system, reporting them through the + // event system instead of printing them directly here. + // perhaps try to parse and understand them. + std.debug.warn("{}\n", ctx.link_msg.toSliceConst()); + } + return error.LinkFailed; } - return error.LinkFailed; } } @@ -672,7 +679,13 @@ const DarwinPlatform = struct { }; var had_extra: bool = undefined; - try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,); + try darwinGetReleaseVersion( + ver_str, + &result.major, + &result.minor, + &result.micro, + &had_extra, + ); if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) { return error.InvalidDarwinVersionString; } @@ -713,7 +726,7 @@ fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u3 return error.InvalidDarwinVersionString; var start_pos: usize = 0; - for ([]*u32{major, minor, micro}) |v| { + for ([]*u32{ major, minor, micro }) |v| { const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.'); const end_pos = dot_pos orelse str.len; v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString; diff --git a/std/build.zig b/std/build.zig index cea760e8a2..68cf13c1eb 100644 --- a/std/build.zig +++ b/std/build.zig @@ -807,6 +807,7 @@ pub const LibExeObjStep = struct { disable_libc: bool, frameworks: BufSet, verbose_link: bool, + no_rosegment: bool, // zig only stuff root_src: ?[]const u8, @@ -874,6 +875,7 @@ pub const LibExeObjStep = struct { fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: *const Version) LibExeObjStep { var self = LibExeObjStep{ + .no_rosegment = false, .strip = false, .builder = builder, .verbose_link = false, @@ -914,6 +916,7 @@ pub const LibExeObjStep = struct { fn initC(builder: *Builder, name: []const u8, kind: Kind, version: *const Version, static: bool) LibExeObjStep { var self = LibExeObjStep{ + .no_rosegment = false, .builder = builder, .name = name, .kind = kind, @@ -953,6 +956,10 @@ pub const LibExeObjStep = struct { return self; } + pub fn setNoRoSegment(self: *LibExeObjStep, value: bool) void { + self.no_rosegment = value; + } + fn computeOutFileNames(self: *LibExeObjStep) void { switch (self.kind) { Kind.Obj => { @@ -1306,6 +1313,10 @@ pub const LibExeObjStep = struct { } } + if (self.no_rosegment) { + try zig_args.append("--no-rosegment"); + } + try builder.spawnChild(zig_args.toSliceConst()); if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) { @@ -1598,6 +1609,7 @@ pub const TestStep = struct { include_dirs: ArrayList([]const u8), lib_paths: ArrayList([]const u8), object_files: ArrayList([]const u8), + no_rosegment: bool, pub fn init(builder: *Builder, root_src: []const u8) TestStep { const step_name = builder.fmt("test {}", root_src); @@ -1615,9 +1627,14 @@ pub const TestStep = struct { .include_dirs = ArrayList([]const u8).init(builder.allocator), .lib_paths = ArrayList([]const u8).init(builder.allocator), .object_files = ArrayList([]const u8).init(builder.allocator), + .no_rosegment = false, }; } + pub fn setNoRoSegment(self: *TestStep, value: bool) void { + self.no_rosegment = value; + } + pub fn addLibPath(self: *TestStep, path: []const u8) void { self.lib_paths.append(path) catch unreachable; } @@ -1761,6 +1778,10 @@ pub const TestStep = struct { try zig_args.append(lib_path); } + if (self.no_rosegment) { + try zig_args.append("--no-rosegment"); + } + try builder.spawnChild(zig_args.toSliceConst()); } }; diff --git a/std/event/channel.zig b/std/event/channel.zig index d4d713bdee..03a036042b 100644 --- a/std/event/channel.zig +++ b/std/event/channel.zig @@ -71,11 +71,6 @@ pub fn Channel(comptime T: type) type { /// puts a data item in the channel. The promise completes when the value has been added to the /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. pub async fn put(self: *SelfChannel, data: T) void { - // TODO should be able to group memory allocation failure before first suspend point - // so that the async invocation catches it - var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined; - _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable; - suspend |handle| { var my_tick_node = Loop.NextTickNode{ .next = undefined, @@ -91,18 +86,13 @@ pub fn Channel(comptime T: type) type { self.putters.put(&queue_node); _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); - self.loop.onNextTick(dispatch_tick_node_ptr); + self.dispatch(); } } /// await this function to get an item from the channel. If the buffer is empty, the promise will /// complete when the next item is put in the channel. pub async fn get(self: *SelfChannel) T { - // TODO should be able to group memory allocation failure before first suspend point - // so that the async invocation catches it - var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined; - _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable; - // TODO integrate this function with named return values // so we can get rid of this extra result copy var result: T = undefined; @@ -121,21 +111,12 @@ pub fn Channel(comptime T: type) type { self.getters.put(&queue_node); _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); - self.loop.onNextTick(dispatch_tick_node_ptr); + self.dispatch(); } return result; } - async fn dispatch(self: *SelfChannel, tick_node_ptr: **Loop.NextTickNode) void { - // resumed by onNextTick - suspend |handle| { - var tick_node = Loop.NextTickNode{ - .data = handle, - .next = undefined, - }; - tick_node_ptr.* = &tick_node; - } - + fn dispatch(self: *SelfChannel) void { // set the "need dispatch" flag _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index 35adcbb96b..fdc3d49145 100644 --- a/test/stage2/compare_output.zig +++ b/test/stage2/compare_output.zig @@ -2,6 +2,7 @@ const std = @import("std"); const TestContext = @import("../../src-self-hosted/test.zig").TestContext; pub fn addCases(ctx: *TestContext) !void { + // hello world try ctx.testCompareOutputLibC( \\extern fn puts([*]const u8) void; \\export fn main() c_int { @@ -9,4 +10,16 @@ pub fn addCases(ctx: *TestContext) !void { \\ return 0; \\} , "Hello, world!" ++ std.cstr.line_sep); + + // function calling another function + try ctx.testCompareOutputLibC( + \\extern fn puts(s: [*]const u8) void; + \\export fn main() c_int { + \\ return foo(c"OK"); + \\} + \\fn foo(s: [*]const u8) c_int { + \\ puts(s); + \\ return 0; + \\} + , "OK" ++ std.cstr.line_sep); } -- cgit v1.2.3 From 95f45cfc34cd5e77dad2318cab27194535e14d16 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 25 Jul 2018 02:36:29 -0400 Subject: patch LLD to fix COFF crashing when linking twice in same process closes #1289 --- deps/lld/COFF/Driver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deps/lld/COFF/Driver.cpp b/deps/lld/COFF/Driver.cpp index 0f3d8fb0b4..34b968fe5e 100644 --- a/deps/lld/COFF/Driver.cpp +++ b/deps/lld/COFF/Driver.cpp @@ -72,6 +72,9 @@ bool link(ArrayRef Args, bool CanExitEarly, raw_ostream &Diag) { exitLld(errorCount() ? 1 : 0); freeArena(); + ObjFile::Instances.clear(); + ImportFile::Instances.clear(); + BitcodeFile::Instances.clear(); return !errorCount(); } -- cgit v1.2.3 From 2257660916a8c92d953a5a71da6c2d4f7cc031e6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 25 Jul 2018 13:12:03 -0400 Subject: fix assertion failure when some compile errors happen I don't actually know of a test case to trigger this self-hosted won't have this problem because get_pointer_to_type will return error.SemanticAnalysisFailed --- src/ir.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index fe5fb77085..fd2558c5eb 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18991,6 +18991,9 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; } else if (type_entry->id == TypeTableEntryIdErrorUnion) { TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; + if (type_is_invalid(payload_type)) { + return ira->codegen->builtin_types.entry_invalid; + } TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, -- cgit v1.2.3 From 84195467ad974f9b7201e4e1bbd6dccbd5e7ab90 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 25 Jul 2018 17:08:55 -0400 Subject: add compile error for non-inline for loop on comptime type --- src/ir.cpp | 2 +- test/compile_errors.zig | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index fd2558c5eb..424987823b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12142,7 +12142,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc result_type = ira->codegen->builtin_types.entry_invalid; } else if (type_requires_comptime(result_type)) { var_class_requires_const = true; - if (!var->src_is_const && !is_comptime_var) { + if (!var->gen_is_const && !is_comptime_var) { ir_add_error_node(ira, source_node, buf_sprintf("variable of type '%s' must be const or comptime", buf_ptr(&result_type->name))); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index b7bd39f29e..91693e091c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,20 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "non-inline for loop on a type that requires comptime", + \\const Foo = struct { + \\ name: []const u8, + \\ T: type, + \\}; + \\export fn entry() void { + \\ const xx: [2]Foo = undefined; + \\ for (xx) |f| {} + \\} + , + ".tmp_source.zig:7:15: error: variable of type 'Foo' must be const or comptime", + ); + cases.add( "generic fn as parameter without comptime keyword", \\fn f(_: fn (var) void) void {} -- cgit v1.2.3 From fd575fe1f3b45806f2cf823a2abe3727d381d4ed Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 25 Jul 2018 18:15:55 -0400 Subject: add compile error for missing parameter name of generic function --- src/ir.cpp | 1 + test/compile_errors.zig | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 424987823b..e40c129953 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12591,6 +12591,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod } Buf *param_name = param_decl_node->data.param_decl.name; + if (!param_name) return false; if (!is_var_args) { VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, *child_scope, param_name, true, arg_val, nullptr); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 91693e091c..83bf715f78 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,17 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "missing parameter name of generic function", + \\fn dump(var) void {} + \\export fn entry() void { + \\ var a: u8 = 9; + \\ dump(a); + \\} + , + ".tmp_source.zig:1:9: error: missing parameter name", + ); + cases.add( "non-inline for loop on a type that requires comptime", \\const Foo = struct { -- cgit v1.2.3 From 2cbad364c1d23b64ae064f8547590c133b4f070a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 26 Jul 2018 18:29:07 -0400 Subject: add compile error for ignoring return value of while loop bodies closes #1049 --- src/analyze.cpp | 2 +- src/ir.cpp | 12 +++++++++--- src/ir_print.cpp | 4 ++++ test/compile_errors.zig | 22 ++++++++++++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index a4bfff78c3..aadee29fc8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4056,7 +4056,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ } if (g->verbose_ir) { - fprintf(stderr, "{ // (analyzed)\n"); + fprintf(stderr, "fn %s() { // (analyzed)\n", buf_ptr(&fn_table_entry->symbol_name)); ir_print(g, stderr, &fn_table_entry->analyzed_executable, 4); fprintf(stderr, "}\n"); } diff --git a/src/ir.cpp b/src/ir.cpp index e40c129953..a6007852e0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5251,8 +5251,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (body_result == irb->codegen->invalid_instruction) return body_result; - if (!instr_is_unreachable(body_result)) + if (!instr_is_unreachable(body_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result)); ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime)); + } if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); @@ -5331,8 +5333,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (body_result == irb->codegen->invalid_instruction) return body_result; - if (!instr_is_unreachable(body_result)) + if (!instr_is_unreachable(body_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result)); ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); + } if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); @@ -5392,8 +5396,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (body_result == irb->codegen->invalid_instruction) return body_result; - if (!instr_is_unreachable(body_result)) + if (!instr_is_unreachable(body_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result)); ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime)); + } if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 6182958d0a..127afa94a5 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -45,6 +45,10 @@ static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) { } static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) { + if (instruction == nullptr) { + fprintf(irp->f, "(null)"); + return; + } if (instruction->value.special != ConstValSpecialRuntime) { ir_print_const_value(irp, &instruction->value); } else { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 83bf715f78..2c4c9208eb 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,28 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "while loop body expression ignored", + \\fn returns() usize { + \\ return 2; + \\} + \\export fn f1() void { + \\ while (true) returns(); + \\} + \\export fn f2() void { + \\ var x: ?i32 = null; + \\ while (x) |_| returns(); + \\} + \\export fn f3() void { + \\ var x: error!i32 = error.Bad; + \\ while (x) |_| returns() else |_| unreachable; + \\} + , + ".tmp_source.zig:5:25: error: expression value is ignored", + ".tmp_source.zig:9:26: error: expression value is ignored", + ".tmp_source.zig:13:26: error: expression value is ignored", + ); + cases.add( "missing parameter name of generic function", \\fn dump(var) void {} -- cgit v1.2.3 From b3f4182ca1756ccf84fe5bbc88594a91ead617b5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 26 Jul 2018 22:26:00 -0400 Subject: coroutines have 3 more bits of atomic state --- src/all_types.hpp | 2 +- src/analyze.cpp | 13 ++++++--- src/ir.cpp | 80 ++++++++++++++++++++++++++++++++++++++----------------- 3 files changed, 66 insertions(+), 29 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index bcd6a04cc3..70ea629c59 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3245,7 +3245,7 @@ static const size_t stack_trace_ptr_count = 30; #define RESULT_FIELD_NAME "result" #define ASYNC_ALLOC_FIELD_NAME "allocFn" #define ASYNC_FREE_FIELD_NAME "freeFn" -#define AWAITER_HANDLE_FIELD_NAME "awaiter_handle" +#define ATOMIC_STATE_FIELD_NAME "atomic_state" // these point to data belonging to the awaiter #define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr" #define RESULT_PTR_FIELD_NAME "result_ptr" diff --git a/src/analyze.cpp b/src/analyze.cpp index aadee29fc8..74d59f966a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -519,11 +519,11 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) return return_type->promise_frame_parent; } - TypeTableEntry *awaiter_handle_type = get_optional_type(g, g->builtin_types.entry_promise); + TypeTableEntry *atomic_state_type = g->builtin_types.entry_usize; TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false); ZigList field_names = {}; - field_names.append(AWAITER_HANDLE_FIELD_NAME); + field_names.append(ATOMIC_STATE_FIELD_NAME); field_names.append(RESULT_FIELD_NAME); field_names.append(RESULT_PTR_FIELD_NAME); if (g->have_err_ret_tracing) { @@ -533,7 +533,7 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) } ZigList field_types = {}; - field_types.append(awaiter_handle_type); + field_types.append(atomic_state_type); field_types.append(return_type); field_types.append(result_ptr_type); if (g->have_err_ret_tracing) { @@ -6228,7 +6228,12 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) { } else if (type_entry->id == TypeTableEntryIdOpaque) { return 1; } else { - return LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref); + uint32_t llvm_alignment = LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref); + // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw + if (type_entry->id == TypeTableEntryIdPromise && llvm_alignment < 8) { + return 8; + } + return llvm_alignment; } } diff --git a/src/ir.cpp b/src/ir.cpp index a6007852e0..5466e64e55 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3097,19 +3097,47 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode return return_inst; } + IrBasicBlock *canceled_block = ir_create_basic_block(irb, scope, "Canceled"); + IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "Suspended"); + IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "NotSuspended"); + ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value); - IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, - get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - // TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig - IrInstruction *replacement_value = irb->exec->coro_handle; - IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node, - promise_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr, - AtomicRmwOp_xchg, AtomicOrderSeqCst); - ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, maybe_await_handle); - IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_await_handle); + IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *replacement_value = ir_build_const_usize(irb, scope, node, 0xa); // 0b1010 + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + usize_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr, + AtomicRmwOp_or, AtomicOrderSeqCst); + + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); - return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, irb->exec->coro_early_final, - is_comptime); + IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 + IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); + IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); + ir_build_cond_br(irb, scope, node, is_canceled_bool, canceled_block, not_canceled_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, canceled_block); + ir_mark_gen(ir_build_br(irb, scope, node, irb->exec->coro_final_cleanup_block, is_comptime)); + + ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 + IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, inverted_ptr_mask, false); + IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); + ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, suspended_block); + ir_build_unreachable(irb, scope, node); + + ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); + IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 + IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); + IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); + // if we ever add null checking safety to the ptrtoint instruction, it needs to be disabled here + IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr); + ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, await_handle); + IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); + return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, + irb->exec->coro_early_final, is_comptime); // the above blocks are rendered by ir_gen after the rest of codegen } @@ -6708,9 +6736,9 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast ir_build_store_ptr(irb, parent_scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); } - Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME); - IrInstruction *awaiter_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, - awaiter_handle_field_name); + Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); + IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, + atomic_state_field_name); IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); VariableTableEntry *result_var = ir_create_var(irb, node, parent_scope, nullptr, @@ -6723,12 +6751,16 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var); ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle); - IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, - get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, parent_scope, node, - promise_type_val, awaiter_field_ptr, nullptr, irb->exec->coro_handle, nullptr, - AtomicRmwOp_xchg, AtomicOrderSeqCst); - IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_await_handle); + IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, parent_scope, node, irb->exec->coro_handle); + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node, + usize_type_val, atomic_state_ptr, nullptr, coro_handle_addr, nullptr, + AtomicRmwOp_or, AtomicOrderSeqCst); + IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0); + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111 + IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 + IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); + IrInstruction *is_non_null = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend"); IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend"); IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "MergeSuspend"); @@ -7087,10 +7119,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); - Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME); + Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - awaiter_handle_field_name); - ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, null_value); + atomic_state_field_name); + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); + ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, zero); Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); @@ -7108,7 +7141,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec // coordinate with builtin.zig Buf *index_name = buf_create_from_str("index"); IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name); - IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); ir_build_store_ptr(irb, scope, node, index_ptr, zero); Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses"); -- cgit v1.2.3 From 7113f109a4111acadf0533ca84e529d229892c8c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 15:50:26 -0400 Subject: update coroutine return codegen with new status bits --- src/all_types.hpp | 2 +- src/ir.cpp | 49 ++++++++++++++++++++++++------------------------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 70ea629c59..3ac7afe474 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -60,7 +60,7 @@ struct IrExecutable { ZigList tld_list; IrInstruction *coro_handle; - IrInstruction *coro_awaiter_field_ptr; // this one is shared and in the promise + IrInstruction *atomic_state_field_ptr; // this one is shared and in the promise IrInstruction *coro_result_ptr_field_ptr; IrInstruction *coro_result_field_ptr; IrInstruction *await_handle_var_ptr; // this one is where we put the one we extracted from the promise diff --git a/src/ir.cpp b/src/ir.cpp index 5466e64e55..8ebac847ac 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3097,31 +3097,26 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode return return_inst; } - IrBasicBlock *canceled_block = ir_create_basic_block(irb, scope, "Canceled"); - IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "Suspended"); IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "NotSuspended"); + IrBasicBlock *store_awaiter_block = ir_create_basic_block(irb, scope, "StoreAwaiter"); + IrBasicBlock *check_canceled_block = ir_create_basic_block(irb, scope, "CheckCanceled"); + + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 + IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 + IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 + IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 + IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); + IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); - IrInstruction *replacement_value = ir_build_const_usize(irb, scope, node, 0xa); // 0b1010 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, - usize_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr, + usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, ptr_mask, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); - IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); - IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); - IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 - IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); - IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, scope, node, is_canceled_bool, canceled_block, not_canceled_block, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, canceled_block); - ir_mark_gen(ir_build_br(irb, scope, node, irb->exec->coro_final_cleanup_block, is_comptime)); - - ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); - IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 - IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, inverted_ptr_mask, false); + IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); @@ -3129,16 +3124,20 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode ir_build_unreachable(irb, scope, node); ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); - IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); - IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); // if we ever add null checking safety to the ptrtoint instruction, it needs to be disabled here + IrInstruction *have_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); + ir_build_cond_br(irb, scope, node, have_await_handle, store_awaiter_block, check_canceled_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, store_awaiter_block); IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr); ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, await_handle); - IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); - return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, - irb->exec->coro_early_final, is_comptime); - // the above blocks are rendered by ir_gen after the rest of codegen + ir_build_br(irb, scope, node, irb->exec->coro_normal_final, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, check_canceled_block); + IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); + IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); + return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime); } static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { @@ -7120,10 +7119,10 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); - irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + irb->exec->atomic_state_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); - ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, zero); + ir_build_store_ptr(irb, scope, node, irb->exec->atomic_state_field_ptr, zero); Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); -- cgit v1.2.3 From 10764ee0e66e5d9a815073340d8f16a58e225422 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 17:00:41 -0400 Subject: resume clears suspend bit --- src/ir.cpp | 137 ++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 52 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 8ebac847ac..2eff986694 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6686,20 +6686,53 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode return ir_build_cancel(irb, parent_scope, node, target_inst); } -static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeResume); - IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, parent_scope); + IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - return ir_build_coro_resume(irb, parent_scope, node, target_inst); + IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone"); + IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + + IrInstruction *inverted_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 + IrInstruction *mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_mask); + IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 + IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); + IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); + IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node, + get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); + + // TODO relies on Zig not re-ordering fields + IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); + Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); + IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + atomic_state_field_name); + + // clear the is_suspended bit + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + usize_type_val, atomic_state_ptr, nullptr, mask, nullptr, + AtomicRmwOp_and, AtomicOrderSeqCst); + + IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); + IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); + ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); + ir_build_coro_resume(irb, scope, node, target_inst); + ir_build_br(irb, scope, node, done_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, done_block); + return ir_build_const_void(irb, scope, node); } -static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeAwaitExpr); - IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, parent_scope); + IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6713,7 +6746,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast return irb->codegen->invalid_instruction; } - ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope); + ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope); if (scope_defer_expr) { if (!scope_defer_expr->reported_err) { add_node_error(irb->codegen, node, buf_sprintf("cannot await inside defer expression")); @@ -6724,85 +6757,85 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast Scope *outer_scope = irb->exec->begin_scope; - IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, parent_scope, node, target_inst); + IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, target_inst); Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); - IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_ptr_field_name); + IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name); if (irb->codegen->have_err_ret_tracing) { - IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull); + IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); - IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); - ir_build_store_ptr(irb, parent_scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); + IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); + ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); } Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); - IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, + IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); - IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); - VariableTableEntry *result_var = ir_create_var(irb, node, parent_scope, nullptr, + IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false); + VariableTableEntry *result_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); - IrInstruction *undefined_value = ir_build_const_undefined(irb, parent_scope, node); - IrInstruction *target_promise_type = ir_build_typeof(irb, parent_scope, node, target_inst); - IrInstruction *promise_result_type = ir_build_promise_result_type(irb, parent_scope, node, target_promise_type); - ir_build_await_bookkeeping(irb, parent_scope, node, promise_result_type); - ir_build_var_decl(irb, parent_scope, node, result_var, promise_result_type, nullptr, undefined_value); - IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var); - ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr); - IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle); - IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); - IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, parent_scope, node, irb->exec->coro_handle); - IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node, + IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node); + IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); + IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); + ir_build_await_bookkeeping(irb, scope, node, promise_result_type); + ir_build_var_decl(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value); + IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var); + ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr); + IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); + IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle); + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, atomic_state_ptr, nullptr, coro_handle_addr, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); - IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0); - IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111 - IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 - IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); - IrInstruction *is_non_null = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); - IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend"); - IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend"); - IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "MergeSuspend"); - ir_build_cond_br(irb, parent_scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 + IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 + IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); + IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); + IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend"); + IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend"); + IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend"); + ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, no_suspend_block); if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); - IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_field_name); - IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull); - ir_build_merge_err_ret_traces(irb, parent_scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr); + IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name); + IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); + ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr); } Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); - IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name); + IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, // because we're about to destroy the memory. So we store it into our result variable. - IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr); - ir_build_store_ptr(irb, parent_scope, node, my_result_var_ptr, no_suspend_result); - ir_build_cancel(irb, parent_scope, node, target_inst); - ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); + IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr); + ir_build_store_ptr(irb, scope, node, my_result_var_ptr, no_suspend_result); + ir_build_cancel(irb, scope, node, target_inst); + ir_build_br(irb, scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block); - IrInstruction *suspend_code = ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false); - IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); - IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); + IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false); + IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup"); + IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume"); IrInstructionSwitchBrCase *cases = allocate(2); - cases[0].value = ir_build_const_u8(irb, parent_scope, node, 0); + cases[0].value = ir_build_const_u8(irb, scope, node, 0); cases[0].block = resume_block; - cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1); + cases[1].value = ir_build_const_u8(irb, scope, node, 1); cases[1].block = cleanup_block; - ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, + ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); - ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); - ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); + ir_gen_defers_for_block(irb, scope, outer_scope, true); + ir_mark_gen(ir_build_br(irb, scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, resume_block); - ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); + ir_build_br(irb, scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, merge_block); - return ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr); + return ir_build_load_ptr(irb, scope, node, my_result_var_ptr); } static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { -- cgit v1.2.3 From 442e244b4dd371d674436a163d62efdcd4e17a00 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 17:16:00 -0400 Subject: suspend sets suspend bit --- src/ir.cpp | 19 ++++++++++++++++++- test/cases/coroutines.zig | 4 ++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 2eff986694..cd791fb189 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6874,9 +6874,26 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); + IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended"); + IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, parent_scope, "NotAlreadySuspended"); - IrInstruction *suspend_code; IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); + IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010 + IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0); + + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node, + usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr, + AtomicRmwOp_or, AtomicOrderSeqCst); + IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); + IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); + ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, suspended_block); + ir_build_unreachable(irb, parent_scope, node); + + ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); + IrInstruction *suspend_code; if (node->data.suspend.block == nullptr) { suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false); } else { diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index f7f2af62a6..72a4ed0b38 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -244,8 +244,8 @@ test "break from suspend" { std.debug.assert(my_result == 2); } async fn testBreakFromSuspend(my_result: *i32) void { - s: suspend |p| { - break :s; + suspend |p| { + resume p; } my_result.* += 1; suspend; -- cgit v1.2.3 From 02c5bda704d30e95e6af23804f9a552e9d8ca2d7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 17:27:03 -0400 Subject: remove ability to break from suspend blocks closes #803 --- doc/langref.html.in | 2 +- src/all_types.hpp | 2 -- src/analyze.cpp | 1 - src/ir.cpp | 17 ++--------------- src/parser.cpp | 25 ++----------------------- 5 files changed, 5 insertions(+), 42 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 60ba09d391..d91fb6e8fb 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7336,7 +7336,7 @@ Defer(body) = ("defer" | "deferror") body IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) -SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) +SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body) diff --git a/src/all_types.hpp b/src/all_types.hpp index 3ac7afe474..2f09e70301 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -898,7 +898,6 @@ struct AstNodeAwaitExpr { }; struct AstNodeSuspend { - Buf *name; AstNode *block; AstNode *promise_symbol; }; @@ -1929,7 +1928,6 @@ struct ScopeLoop { struct ScopeSuspend { Scope base; - Buf *name; IrBasicBlock *resume_block; bool reported_err; }; diff --git a/src/analyze.cpp b/src/analyze.cpp index 74d59f966a..03cfa5b67b 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -161,7 +161,6 @@ ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) { assert(node->type == NodeTypeSuspend); ScopeSuspend *scope = allocate(1); init_scope(&scope->base, ScopeIdSuspend, node, parent); - scope->name = node->data.suspend.name; return scope; } diff --git a/src/ir.cpp b/src/ir.cpp index cd791fb189..799d7e3bc5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6186,15 +6186,6 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop return ir_build_br(irb, break_scope, node, dest_block, is_comptime); } -static IrInstruction *ir_gen_break_from_suspend(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeSuspend *suspend_scope) { - IrInstruction *is_comptime = ir_build_const_bool(irb, break_scope, node, false); - - IrBasicBlock *dest_block = suspend_scope->resume_block; - ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false); - - return ir_build_br(irb, break_scope, node, dest_block, is_comptime); -} - static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) { assert(node->type == NodeTypeBreak); @@ -6235,12 +6226,8 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode * return ir_gen_return_from_block(irb, break_scope, node, this_block_scope); } } else if (search_scope->id == ScopeIdSuspend) { - ScopeSuspend *this_suspend_scope = (ScopeSuspend *)search_scope; - if (node->data.break_expr.name != nullptr && - (this_suspend_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_suspend_scope->name))) - { - return ir_gen_break_from_suspend(irb, break_scope, node, this_suspend_scope); - } + add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block")); + return irb->codegen->invalid_instruction; } search_scope = search_scope->parent; } diff --git a/src/parser.cpp b/src/parser.cpp index adb1633f5d..a93d8de830 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -648,30 +648,12 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m } /* -SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) +SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) */ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { size_t orig_token_index = *token_index; - Token *name_token = nullptr; - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdSymbol) { - *token_index += 1; - Token *colon_token = &pc->tokens->at(*token_index); - if (colon_token->id == TokenIdColon) { - *token_index += 1; - name_token = token; - token = &pc->tokens->at(*token_index); - } else if (mandatory) { - ast_expect_token(pc, colon_token, TokenIdColon); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; - } - } - - Token *suspend_token = token; + Token *suspend_token = &pc->tokens->at(*token_index); if (suspend_token->id == TokenIdKeywordSuspend) { *token_index += 1; } else if (mandatory) { @@ -693,9 +675,6 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b } AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); - if (name_token != nullptr) { - node->data.suspend.name = token_buf(name_token); - } node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index); ast_eat_token(pc, token_index, TokenIdBinOr); node->data.suspend.block = ast_parse_block(pc, token_index, true); -- cgit v1.2.3 From 0b7a9c072203c2f9999ddcc2231a42334cd028e3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 17:42:09 -0400 Subject: cancel sets the cancel bit --- src/ir.cpp | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 799d7e3bc5..4a381a26fa 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6663,14 +6663,45 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo async_allocator_type_value, is_var_args); } -static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeCancel); - IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, parent_scope); + IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - return ir_build_cancel(irb, parent_scope, node, target_inst); + IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone"); + IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); + IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); + IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 + IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node, + get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); + + // TODO relies on Zig not re-ordering fields + IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); + Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); + IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + atomic_state_field_name); + + // set the is_canceled bit + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + usize_type_val, atomic_state_ptr, nullptr, is_canceled_mask, nullptr, + AtomicRmwOp_and, AtomicOrderSeqCst); + + IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); + IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); + ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); + ir_build_cancel(irb, scope, node, target_inst); + ir_build_br(irb, scope, node, done_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, done_block); + return ir_build_const_void(irb, scope, node); } static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { -- cgit v1.2.3 From 341bd0dfa42a729aefff56e48a67a21cb3ea0822 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 17:47:15 -0400 Subject: await sets the await bit --- src/ir.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 4a381a26fa..a933106884 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6791,9 +6791,15 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n atomic_state_field_name); IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false); + IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node); + IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 + IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 + IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100 + VariableTableEntry *result_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); - IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node); IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); ir_build_await_bookkeeping(irb, scope, node, promise_result_type); @@ -6801,14 +6807,12 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var); ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); - IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle); + IrInstruction *mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, coro_handle_addr, await_mask, false); IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, - usize_type_val, atomic_state_ptr, nullptr, coro_handle_addr, nullptr, + usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); - IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); - IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 - IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend"); -- cgit v1.2.3 From e491c381896b6f36366bf554f149f79d5be8f9dd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 18:01:26 -0400 Subject: resume detects resuming when not suspended --- src/ir.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index a933106884..bc3ef11481 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6713,13 +6713,15 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "IsSuspended"); + IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "IsNotSuspended"); - IrInstruction *inverted_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 - IrInstruction *mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_mask); + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 + IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 + IrInstruction *and_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, is_suspended_mask); IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); - IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node, get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); @@ -6732,7 +6734,7 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) // clear the is_suspended bit IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, - usize_type_val, atomic_state_ptr, nullptr, mask, nullptr, + usize_type_val, atomic_state_ptr, nullptr, and_mask, nullptr, AtomicRmwOp_and, AtomicOrderSeqCst); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); @@ -6740,6 +6742,14 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); + IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); + IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); + ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); + ir_build_unreachable(irb, scope, node); + + ir_set_cursor_at_end_and_append_block(irb, suspended_block); ir_build_coro_resume(irb, scope, node, target_inst); ir_build_br(irb, scope, node, done_block, is_comptime); -- cgit v1.2.3 From e5beca886ddf3138351765e1239b626e7bd612c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 18:07:30 -0400 Subject: suspend checks the cancel bit --- src/ir.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index bc3ef11481..c94efdf61f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6907,16 +6907,24 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended"); + IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, parent_scope, "NotCanceled"); IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, parent_scope, "NotAlreadySuspended"); IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010 IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0); IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); + + IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); + IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); + ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, cleanup_block, not_canceled_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false); -- cgit v1.2.3 From f0c049d02b68a8740096df633a41b78d90fd34cd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 18:37:30 -0400 Subject: detect double await --- src/ir.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index c94efdf61f..2f5ba86627 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6796,6 +6796,9 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); } + IrBasicBlock *already_awaited_block = ir_create_basic_block(irb, scope, "AlreadyAwaited"); + IrBasicBlock *not_awaited_block = ir_create_basic_block(irb, scope, "NotAwaited"); + Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); @@ -6823,6 +6826,15 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); + + IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); + IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); + ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, already_awaited_block); + ir_build_unreachable(irb, scope, node); + + ir_set_cursor_at_end_and_append_block(irb, not_awaited_block); IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend"); -- cgit v1.2.3 From 6fed777637daafc077ec2c079b0557f3c8bdce51 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 01:22:51 -0400 Subject: cancel detects if the target handle has already returned --- src/ir.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 2f5ba86627..6d6e1430fe 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6672,6 +6672,8 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + IrBasicBlock *post_return_block = ir_create_basic_block(irb, scope, "PostReturn"); + IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel"); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); @@ -6679,6 +6681,9 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node, get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 + IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 + IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100 // TODO relies on Zig not re-ordering fields IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); @@ -6697,6 +6702,16 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); + IrInstruction *awaiter_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); + IrInstruction *is_returned_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, awaiter_addr, ptr_mask, false); + ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, do_cancel_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, post_return_block); + IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); + IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); + ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, done_block, is_comptime); -- cgit v1.2.3 From c6f9a4c0445000e59bca16838cc413140d399f35 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 01:26:11 -0400 Subject: cancel detects suspend bit --- src/ir.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 6d6e1430fe..48148e6768 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6672,6 +6672,7 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + IrBasicBlock *pre_return_block = ir_create_basic_block(irb, scope, "PreReturn"); IrBasicBlock *post_return_block = ir_create_basic_block(irb, scope, "PostReturn"); IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel"); @@ -6684,6 +6685,7 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100 + IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 // TODO relies on Zig not re-ordering fields IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); @@ -6704,13 +6706,18 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *awaiter_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *is_returned_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, awaiter_addr, ptr_mask, false); - ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, do_cancel_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, post_return_block); IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime); + ir_set_cursor_at_end_and_append_block(irb, pre_return_block); + IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); + IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); + ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime); + ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, done_block, is_comptime); -- cgit v1.2.3 From 60cda3713fdc2d8bce01bfc229109a3c8b3efb6f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 12:04:09 -0400 Subject: suspend cancels awaiter when it gets canceled --- src/ir.cpp | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 48148e6768..4b0080d562 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6663,13 +6663,7 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo async_allocator_type_value, is_var_args); } -static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeCancel); - - IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope); - if (target_inst == irb->codegen->invalid_instruction) - return irb->codegen->invalid_instruction; - +static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *target_inst) { IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); IrBasicBlock *pre_return_block = ir_create_basic_block(irb, scope, "PreReturn"); @@ -6726,6 +6720,16 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) return ir_build_const_void(irb, scope, node); } +static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeCancel); + + IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope); + if (target_inst == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + return ir_gen_cancel_target(irb, scope, node, target_inst); +} + static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeResume); @@ -6941,14 +6945,19 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended"); + IrBasicBlock *canceled_block = ir_create_basic_block(irb, parent_scope, "IsCanceled"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, parent_scope, "NotCanceled"); IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, parent_scope, "NotAlreadySuspended"); + IrBasicBlock *cancel_awaiter_block = ir_create_basic_block(irb, parent_scope, "CancelAwaiter"); + IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_promise); IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010 IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0); + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111 + IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr, @@ -6956,7 +6965,17 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, cleanup_block, not_canceled_block, const_bool_false); + ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, canceled_block); + IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); + IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); + ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block); + IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr); + ir_gen_cancel_target(irb, parent_scope, node, await_handle); + ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); @@ -6995,7 +7014,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0)); cases[0].block = resume_block; cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1)); - cases[1].block = cleanup_block; + cases[1].block = canceled_block; ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr)); -- cgit v1.2.3 From 0ba2bc38d76537a83bd6c27143779857dbb711cf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 12:23:47 -0400 Subject: await checks the cancel bit --- src/ir.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 4b0080d562..c246b75d78 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6824,6 +6824,12 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrBasicBlock *already_awaited_block = ir_create_basic_block(irb, scope, "AlreadyAwaited"); IrBasicBlock *not_awaited_block = ir_create_basic_block(irb, scope, "NotAwaited"); + IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend"); + IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend"); + IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend"); + IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup"); + IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume"); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, @@ -6836,6 +6842,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100 + IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 VariableTableEntry *result_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); @@ -6861,11 +6868,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_unreachable(irb, scope, node); ir_set_cursor_at_end_and_append_block(irb, not_awaited_block); + IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); + IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); + ir_build_cond_br(irb, scope, node, is_canceled_bool, cleanup_block, not_canceled_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); - IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend"); - IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend"); - IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend"); ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, no_suspend_block); @@ -6886,8 +6895,6 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block); IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false); - IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup"); - IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume"); IrInstructionSwitchBrCase *cases = allocate(2); cases[0].value = ir_build_const_u8(irb, scope, node, 0); -- cgit v1.2.3 From dd272d13161d7a7069af78669d891d280c65529f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 12:36:02 -0400 Subject: await cancels the await target when it is canceled --- src/ir.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index c246b75d78..f298cff24b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6830,11 +6830,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend"); IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup"); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume"); + IrBasicBlock *cancel_target_block = ir_create_basic_block(irb, scope, "CancelTarget"); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); + IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false); IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); @@ -6900,10 +6902,15 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n cases[0].value = ir_build_const_u8(irb, scope, node, 0); cases[0].block = resume_block; cases[1].value = ir_build_const_u8(irb, scope, node, 1); - cases[1].block = cleanup_block; + cases[1].block = cancel_target_block; ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); + ir_set_cursor_at_end_and_append_block(irb, cancel_target_block); + IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr); + ir_gen_cancel_target(irb, scope, node, await_handle); + ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); + ir_set_cursor_at_end_and_append_block(irb, cleanup_block); ir_gen_defers_for_block(irb, scope, outer_scope, true); ir_mark_gen(ir_build_br(irb, scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); -- cgit v1.2.3 From 05456eb275dd1d5aee6630ffdc743057db73872f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 12:53:33 -0400 Subject: make some functions in std.event.Loop public --- std/event/loop.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/event/loop.zig b/std/event/loop.zig index cd805f891f..4e219653be 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -55,7 +55,7 @@ pub const Loop = struct { /// After initialization, call run(). /// TODO copy elision / named return values so that the threads referencing *Loop /// have the correct pointer value. - fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void { + pub fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void { return self.initInternal(allocator, 1); } @@ -64,7 +64,7 @@ pub const Loop = struct { /// After initialization, call run(). /// TODO copy elision / named return values so that the threads referencing *Loop /// have the correct pointer value. - fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { + pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { const core_count = try std.os.cpuCount(allocator); return self.initInternal(allocator, core_count); } -- cgit v1.2.3 From 0d79e0381601404b844b4912d15d20f4b529e837 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 13:52:48 -0400 Subject: canceling an await also cancels things awaiting it --- src/ir.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index f298cff24b..7134b4d9ac 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6831,6 +6831,8 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup"); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume"); IrBasicBlock *cancel_target_block = ir_create_basic_block(irb, scope, "CancelTarget"); + IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel"); + IrBasicBlock *do_defers_block = ir_create_basic_block(irb, scope, "DoDefers"); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, @@ -6912,6 +6914,20 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); + IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false); + IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr, + AtomicRmwOp_or, AtomicOrderSeqCst); + IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, ptr_mask, false); + IrInstruction *have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_await_handle_addr, zero, false); + ir_build_cond_br(irb, scope, node, have_my_await_handle, do_cancel_block, do_defers_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); + IrInstruction *my_await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, my_await_handle_addr); + ir_gen_cancel_target(irb, scope, node, my_await_handle); + ir_mark_gen(ir_build_br(irb, scope, node, do_defers_block, const_bool_false)); + + ir_set_cursor_at_end_and_append_block(irb, do_defers_block); ir_gen_defers_for_block(irb, scope, outer_scope, true); ir_mark_gen(ir_build_br(irb, scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); -- cgit v1.2.3 From 3ce0ea884f8a64e1173ab814de10b6c33833b3b8 Mon Sep 17 00:00:00 2001 From: dbandstra Date: Sat, 28 Jul 2018 17:30:05 -0700 Subject: add int writing functions to OutStream added: writeInt, writeIntLe, and writeIntBe --- std/io.zig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/std/io.zig b/std/io.zig index 71a9822399..1b1c56b69b 100644 --- a/std/io.zig +++ b/std/io.zig @@ -230,6 +230,20 @@ pub fn OutStream(comptime WriteError: type) type { try self.writeFn(self, slice); } } + + pub fn writeIntLe(self: *Self, comptime T: type, value: T) !void { + return self.writeInt(builtin.Endian.Little, T, value); + } + + pub fn writeIntBe(self: *Self, comptime T: type, value: T) !void { + return self.writeInt(builtin.Endian.Big, T, value); + } + + pub fn writeInt(self: *Self, endian: builtin.Endian, comptime T: type, value: T) !void { + var bytes: [@sizeOf(T)]u8 = undefined; + mem.writeInt(bytes[0..], value, endian); + return self.writeFn(self, bytes); + } }; } -- cgit v1.2.3 From f36faa32c46897a12852da9f382ab10979511d6f Mon Sep 17 00:00:00 2001 From: dbandstra Date: Sat, 28 Jul 2018 17:34:28 -0700 Subject: add skipBytes function to InStream this reads N bytes, discarding their values --- std/io.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/std/io.zig b/std/io.zig index 1b1c56b69b..5d73b4e7d8 100644 --- a/std/io.zig +++ b/std/io.zig @@ -200,6 +200,13 @@ pub fn InStream(comptime ReadError: type) type { try self.readNoEof(input_slice); return mem.readInt(input_slice, T, endian); } + + pub fn skipBytes(self: *Self, num_bytes: usize) !void { + var i: usize = 0; + while (i < num_bytes) : (i += 1) { + _ = try self.readByte(); + } + } }; } -- cgit v1.2.3 From 608ff52dc3ea356b23fb6ae92fbca9fbb18c7892 Mon Sep 17 00:00:00 2001 From: dbandstra Date: Sun, 29 Jul 2018 11:52:10 -0700 Subject: add SliceOutStream, rename SliceStream to SliceInStream (#1301) --- std/io.zig | 48 +++++++++++++++++++++++++++++++++++++++++++++++- std/io_test.zig | 29 +++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/std/io.zig b/std/io.zig index 5d73b4e7d8..ff73c04f78 100644 --- a/std/io.zig +++ b/std/io.zig @@ -419,7 +419,7 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ }; } -pub const SliceStream = struct { +pub const SliceInStream = struct { const Self = this; pub const Error = error { }; pub const Stream = InStream(Error); @@ -447,7 +447,53 @@ pub const SliceStream = struct { return size; } +}; + +/// This is a simple OutStream that writes to a slice, and returns an error +/// when it runs out of space. +pub const SliceOutStream = struct { + pub const Error = error{OutOfSpace}; + pub const Stream = OutStream(Error); + + pub stream: Stream, + + pos: usize, + slice: []u8, + pub fn init(slice: []u8) SliceOutStream { + return SliceOutStream{ + .slice = slice, + .pos = 0, + .stream = Stream{ .writeFn = writeFn }, + }; + } + + pub fn getWritten(self: *const SliceOutStream) []const u8 { + return self.slice[0..self.pos]; + } + + pub fn reset(self: *SliceOutStream) void { + self.pos = 0; + } + + fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void { + const self = @fieldParentPtr(SliceOutStream, "stream", out_stream); + + assert(self.pos <= self.slice.len); + + const n = + if (self.pos + bytes.len <= self.slice.len) + bytes.len + else + self.slice.len - self.pos; + + std.mem.copy(u8, self.slice[self.pos..self.pos + n], bytes[0..n]); + self.pos += n; + + if (n < bytes.len) { + return Error.OutOfSpace; + } + } }; pub fn BufferedOutStream(comptime Error: type) type { diff --git a/std/io_test.zig b/std/io_test.zig index 8d5c35c5fd..56f8a9a6ad 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -2,6 +2,7 @@ const std = @import("index.zig"); const io = std.io; const DefaultPrng = std.rand.DefaultPrng; const assert = std.debug.assert; +const assertError = std.debug.assertError; const mem = std.mem; const os = std.os; const builtin = @import("builtin"); @@ -61,9 +62,9 @@ test "BufferOutStream" { assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n")); } -test "SliceStream" { +test "SliceInStream" { const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7 }; - var ss = io.SliceStream.init(bytes); + var ss = io.SliceInStream.init(bytes); var dest: [4]u8 = undefined; @@ -81,8 +82,8 @@ test "SliceStream" { test "PeekStream" { const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; - var ss = io.SliceStream.init(bytes); - var ps = io.PeekStream(2, io.SliceStream.Error).init(&ss.stream); + var ss = io.SliceInStream.init(bytes); + var ps = io.PeekStream(2, io.SliceInStream.Error).init(&ss.stream); var dest: [4]u8 = undefined; @@ -111,3 +112,23 @@ test "PeekStream" { assert(dest[0] == 12); assert(dest[1] == 11); } + +test "SliceOutStream" { + var buffer: [10]u8 = undefined; + var ss = io.SliceOutStream.init(buffer[0..]); + + try ss.stream.write("Hello"); + assert(mem.eql(u8, ss.getWritten(), "Hello")); + + try ss.stream.write("world"); + assert(mem.eql(u8, ss.getWritten(), "Helloworld")); + + assertError(ss.stream.write("!"), error.OutOfSpace); + assert(mem.eql(u8, ss.getWritten(), "Helloworld")); + + ss.reset(); + assert(ss.getWritten().len == 0); + + assertError(ss.stream.write("Hello world!"), error.OutOfSpace); + assert(mem.eql(u8, ss.getWritten(), "Hello worl")); +} -- cgit v1.2.3 From 09304aab77a7b6af5693600fa6b3a35322f7469d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Jul 2018 23:25:40 -0400 Subject: fix cancel and await semantics --- src/ir.cpp | 61 +++++++++++++++++++++++++++++++++-------------------- std/debug/index.zig | 14 ++++++++++-- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 7134b4d9ac..e91fa14170 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6663,7 +6663,9 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo async_allocator_type_value, is_var_args); } -static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *target_inst) { +static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode *node, + IrInstruction *target_inst, bool cancel_non_suspended, bool cancel_awaited) +{ IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); IrBasicBlock *pre_return_block = ir_create_basic_block(irb, scope, "PreReturn"); @@ -6691,7 +6693,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode // set the is_canceled bit IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, atomic_state_ptr, nullptr, is_canceled_mask, nullptr, - AtomicRmwOp_and, AtomicOrderSeqCst); + AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); @@ -6703,14 +6705,26 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, post_return_block); - IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); - IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); - ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime); + if (cancel_awaited) { + ir_build_br(irb, scope, node, do_cancel_block, is_comptime); + } else { + IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); + IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); + ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime); + } ir_set_cursor_at_end_and_append_block(irb, pre_return_block); - IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); - IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); - ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime); + if (cancel_awaited) { + if (cancel_non_suspended) { + ir_build_br(irb, scope, node, do_cancel_block, is_comptime); + } else { + IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); + IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); + ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime); + } + } else { + ir_build_br(irb, scope, node, done_block, is_comptime); + } ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); ir_build_cancel(irb, scope, node, target_inst); @@ -6727,7 +6741,7 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - return ir_gen_cancel_target(irb, scope, node, target_inst); + return ir_gen_cancel_target(irb, scope, node, target_inst, false, true); } static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -6872,15 +6886,19 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_unreachable(irb, scope, node); ir_set_cursor_at_end_and_append_block(irb, not_awaited_block); + IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); + IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, scope, node, is_canceled_bool, cleanup_block, not_canceled_block, const_bool_false); + ir_build_cond_br(irb, scope, node, is_canceled_bool, cancel_target_block, not_canceled_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); - IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); - IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); + ir_set_cursor_at_end_and_append_block(irb, cancel_target_block); + ir_build_cancel(irb, scope, node, target_inst); + ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); + ir_set_cursor_at_end_and_append_block(irb, no_suspend_block); if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); @@ -6897,6 +6915,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, merge_block, const_bool_false); + ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block); IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false); @@ -6904,32 +6923,28 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n cases[0].value = ir_build_const_u8(irb, scope, node, 0); cases[0].block = resume_block; cases[1].value = ir_build_const_u8(irb, scope, node, 1); - cases[1].block = cancel_target_block; + cases[1].block = cleanup_block; ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); - ir_set_cursor_at_end_and_append_block(irb, cancel_target_block); - IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr); - ir_gen_cancel_target(irb, scope, node, await_handle); - ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); - ir_set_cursor_at_end_and_append_block(irb, cleanup_block); IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false); IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, ptr_mask, false); - IrInstruction *have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_await_handle_addr, zero, false); - ir_build_cond_br(irb, scope, node, have_my_await_handle, do_cancel_block, do_defers_block, const_bool_false); + IrInstruction *dont_have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, my_await_handle_addr, zero, false); + IrInstruction *dont_destroy_ourselves = ir_build_bin_op(irb, scope, node, IrBinOpBoolAnd, dont_have_my_await_handle, is_canceled_bool, false); + ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); IrInstruction *my_await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, my_await_handle_addr); - ir_gen_cancel_target(irb, scope, node, my_await_handle); + ir_gen_cancel_target(irb, scope, node, my_await_handle, true, false); ir_mark_gen(ir_build_br(irb, scope, node, do_defers_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, do_defers_block); ir_gen_defers_for_block(irb, scope, outer_scope, true); - ir_mark_gen(ir_build_br(irb, scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); + ir_mark_gen(ir_build_cond_br(irb, scope, node, dont_destroy_ourselves, irb->exec->coro_early_final, irb->exec->coro_final_cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, resume_block); ir_build_br(irb, scope, node, merge_block, const_bool_false); @@ -7004,7 +7019,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block); IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr); - ir_gen_cancel_target(irb, parent_scope, node, await_handle); + ir_gen_cancel_target(irb, parent_scope, node, await_handle, true, false); ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); diff --git a/std/debug/index.zig b/std/debug/index.zig index 3070e0b40b..ab50d79db3 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -27,7 +27,7 @@ pub fn warn(comptime fmt: []const u8, args: ...) void { const stderr = getStderrStream() catch return; stderr.print(fmt, args) catch return; } -fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) { +pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) { if (stderr_stream) |st| { return st; } else { @@ -172,6 +172,16 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, } } +pub inline fn getReturnAddress(frame_count: usize) usize { + var fp = @ptrToInt(@frameAddress()); + var i: usize = 0; + while (fp != 0 and i < frame_count) { + fp = @intToPtr(*const usize, fp).*; + i += 1; + } + return @intToPtr(*const usize, fp + @sizeOf(usize)).*; +} + pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void { const AddressState = union(enum) { NotLookingForStartAddress, @@ -205,7 +215,7 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_ } } -fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void { +pub fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void { switch (builtin.os) { builtin.Os.windows => return error.UnsupportedDebugInfo, builtin.Os.macosx => { -- cgit v1.2.3 From 6fd6bc94f57b815774f9717d52bacbf304a496be Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Jul 2018 12:22:54 -0400 Subject: await sets suspend bit; return clears suspend bit --- src/ir.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index e91fa14170..699baa152e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6744,13 +6744,9 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) return ir_gen_cancel_target(irb, scope, node, target_inst, false, true); } -static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeResume); - - IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope); - if (target_inst == irb->codegen->invalid_instruction) - return irb->codegen->invalid_instruction; - +static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode *node, + IrInstruction *target_inst) +{ IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "IsSuspended"); @@ -6797,6 +6793,16 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) return ir_build_const_void(irb, scope, node); } +static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeResume); + + IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope); + if (target_inst == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + return ir_gen_resume_target(irb, scope, node, target_inst); +} + static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeAwaitExpr); @@ -6847,6 +6853,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrBasicBlock *cancel_target_block = ir_create_basic_block(irb, scope, "CancelTarget"); IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel"); IrBasicBlock *do_defers_block = ir_create_basic_block(irb, scope, "DoDefers"); + IrBasicBlock *destroy_block = ir_create_basic_block(irb, scope, "DestroyBlock"); + IrBasicBlock *my_suspended_block = ir_create_basic_block(irb, scope, "AlreadySuspended"); + IrBasicBlock *my_not_suspended_block = ir_create_basic_block(irb, scope, "NotAlreadySuspended"); + IrBasicBlock *do_suspend_block = ir_create_basic_block(irb, scope, "DoSuspend"); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, @@ -6861,6 +6871,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 + IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 VariableTableEntry *result_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); @@ -6917,22 +6928,42 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block); + IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr, + AtomicRmwOp_or, AtomicOrderSeqCst); + IrInstruction *my_is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_suspended_mask, false); + IrInstruction *my_is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_suspended_value, zero, false); + ir_build_cond_br(irb, scope, node, my_is_suspended_bool, my_suspended_block, my_not_suspended_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, my_suspended_block); + ir_build_unreachable(irb, scope, node); + + ir_set_cursor_at_end_and_append_block(irb, my_not_suspended_block); + IrInstruction *my_is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_canceled_mask, false); + IrInstruction *my_is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_canceled_value, zero, false); + ir_build_cond_br(irb, scope, node, my_is_canceled_bool, cleanup_block, do_suspend_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, do_suspend_block); IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false); IrInstructionSwitchBrCase *cases = allocate(2); cases[0].value = ir_build_const_u8(irb, scope, node, 0); cases[0].block = resume_block; cases[1].value = ir_build_const_u8(irb, scope, node, 1); - cases[1].block = cleanup_block; + cases[1].block = destroy_block; ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); + ir_set_cursor_at_end_and_append_block(irb, destroy_block); + ir_gen_cancel_target(irb, scope, node, target_inst, false, true); + ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); + ir_set_cursor_at_end_and_append_block(irb, cleanup_block); IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false); - IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + IrInstruction *b_my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); - IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, ptr_mask, false); + IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, b_my_prev_atomic_value, ptr_mask, false); IrInstruction *dont_have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, my_await_handle_addr, zero, false); IrInstruction *dont_destroy_ourselves = ir_build_bin_op(irb, scope, node, IrBinOpBoolAnd, dont_have_my_await_handle, is_canceled_bool, false); ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false); @@ -6996,6 +7027,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod IrBasicBlock *cancel_awaiter_block = ir_create_basic_block(irb, parent_scope, "CancelAwaiter"); IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_promise); + IrInstruction *const_bool_true = ir_build_const_bool(irb, parent_scope, node, true); IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001 @@ -7015,11 +7047,13 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod ir_set_cursor_at_end_and_append_block(irb, canceled_block); IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); + IrBasicBlock *post_canceled_block = irb->current_basic_block; ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block); IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr); ir_gen_cancel_target(irb, parent_scope, node, await_handle, true, false); + IrBasicBlock *post_cancel_awaiter_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); @@ -7064,8 +7098,15 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod 2, cases, const_bool_false, nullptr)); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); + IrBasicBlock **incoming_blocks = allocate(2); + IrInstruction **incoming_values = allocate(2); + incoming_blocks[0] = post_canceled_block; + incoming_values[0] = const_bool_true; + incoming_blocks[1] = post_cancel_awaiter_block; + incoming_values[1] = const_bool_false; + IrInstruction *destroy_ourselves = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); - ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); + ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, resume_block); return ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); @@ -7450,7 +7491,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, resume_block); - ir_build_coro_resume(irb, scope, node, awaiter_handle); + ir_gen_resume_target(irb, scope, node, awaiter_handle); ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false); } -- cgit v1.2.3 From c91c781952bd9750c029a48d0dd7a5a24ec089cf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Jul 2018 12:49:53 -0400 Subject: add behavior tests for cancel semantics --- test/behavior.zig | 1 + test/cases/cancel.zig | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 test/cases/cancel.zig diff --git a/test/behavior.zig b/test/behavior.zig index 21b1c597e1..b03336eb71 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -16,6 +16,7 @@ comptime { _ = @import("cases/bugs/828.zig"); _ = @import("cases/bugs/920.zig"); _ = @import("cases/byval_arg_var.zig"); + _ = @import("cases/cancel.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); _ = @import("cases/coroutine_await_struct.zig"); diff --git a/test/cases/cancel.zig b/test/cases/cancel.zig new file mode 100644 index 0000000000..edf11d687d --- /dev/null +++ b/test/cases/cancel.zig @@ -0,0 +1,92 @@ +const std = @import("std"); + +var defer_f1: bool = false; +var defer_f2: bool = false; +var defer_f3: bool = false; + +test "cancel forwards" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = async<&da.allocator> f1() catch unreachable; + cancel p; + std.debug.assert(defer_f1); + std.debug.assert(defer_f2); + std.debug.assert(defer_f3); +} + +async fn f1() void { + defer { + defer_f1 = true; + } + await (async f2() catch unreachable); +} + +async fn f2() void { + defer { + defer_f2 = true; + } + await (async f3() catch unreachable); +} + +async fn f3() void { + defer { + defer_f3 = true; + } + suspend; +} + +var defer_b1: bool = false; +var defer_b2: bool = false; +var defer_b3: bool = false; +var defer_b4: bool = false; + +test "cancel backwards" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = async<&da.allocator> b1() catch unreachable; + cancel p; + std.debug.assert(defer_b1); + std.debug.assert(defer_b2); + std.debug.assert(defer_b3); + std.debug.assert(defer_b4); +} + +async fn b1() void { + defer { + defer_b1 = true; + } + await (async b2() catch unreachable); +} + +var b4_handle: promise = undefined; + +async fn b2() void { + const b3_handle = async b3() catch unreachable; + resume b4_handle; + cancel b4_handle; + defer { + defer_b2 = true; + } + const value = await b3_handle; + @panic("unreachable"); +} + +async fn b3() i32 { + defer { + defer_b3 = true; + } + await (async b4() catch unreachable); + return 1234; +} + +async fn b4() void { + defer { + defer_b4 = true; + } + suspend |p| { + b4_handle = p; + } + suspend; +} -- cgit v1.2.3 From cfe03c764de0d2edfbad74a71d7c18f0fd68b506 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Jul 2018 13:07:04 -0400 Subject: fix docs for break from suspend --- doc/langref.html.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index d91fb6e8fb..0499c632e2 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4665,24 +4665,24 @@ async fn testSuspendBlock() void { block, while the old thread continued executing the suspend block.

    - However, if you use labeled break on the suspend block, the coroutine + However, the coroutine can be directly resumed from the suspend block, in which case it never returns to its resumer and continues executing.

    {#code_begin|test#} const std = @import("std"); const assert = std.debug.assert; -test "break from suspend" { +test "resume from suspend" { var buf: [500]u8 = undefined; var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; var my_result: i32 = 1; - const p = try async testBreakFromSuspend(&my_result); + const p = try async testResumeFromSuspend(&my_result); cancel p; std.debug.assert(my_result == 2); } -async fn testBreakFromSuspend(my_result: *i32) void { - s: suspend |p| { - break :s; +async fn testResumeFromSuspend(my_result: *i32) void { + suspend |p| { + resume p; } my_result.* += 1; suspend; -- cgit v1.2.3 From 0db33e9c86ff43d3fe7f7f8fb2e29333c2fad2af Mon Sep 17 00:00:00 2001 From: "Matthew D. Steele" Date: Mon, 30 Jul 2018 22:27:07 -0400 Subject: Add "Comments" section to language reference (#1309) The contents of this section come from the discussion on issue #1305. --- doc/langref.html.in | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/doc/langref.html.in b/doc/langref.html.in index 0499c632e2..7fde550338 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -134,6 +134,58 @@ pub fn main() void {

    {#see_also|Values|@import|Errors|Root Source File#} {#header_close#} + {#header_open|Comments#} + {#code_begin|test|comments#} +const assert = @import("std").debug.assert; + +test "comments" { + // Comments in Zig start with "//" and end at the next LF byte (end of line). + // The below line is a comment, and won't be executed. + + //assert(false); + + const x = true; // another comment + assert(x); +} + {#code_end#} +

    + There are no multiline comments in Zig (e.g. like /* */ + comments in C). This helps allow Zig to have the property that each line + of code can be tokenized out of context. +

    + {#header_open|Doc comments#} +

    + A doc comment is one that begins with exactly three slashes (i.e. + /// but not ////); + multiple doc comments in a row are merged together to form a multiline + doc comment. The doc comment documents whatever immediately follows it. +

    + {#code_begin|syntax|doc_comments#} +/// A structure for storing a timestamp, with nanosecond precision (this is a +/// multiline doc comment). +const Timestamp = struct { + /// The number of seconds since the epoch (this is also a doc comment). + seconds: i64, // signed so we can represent pre-1970 (not a doc comment) + /// The number of nanoseconds past the second (doc comment again). + nanos: u32, + + /// Returns a `Timestamp` struct representing the Unix epoch; that is, the + /// moment of 1970 Jan 1 00:00:00 UTC (this is a doc comment too). + pub fn unixEpoch() Timestamp { + return Timestamp{ + .seconds = 0, + .nanos = 0, + }; + } +}; + {#code_end#} +

    + Doc comments are only allowed in certain places; eventually, it will + become a compile error have a doc comment in an unexpected place, such as + in the middle of an expression, or just before a non-doc comment. +

    + {#header_close#} + {#header_close#} {#header_open|Values#} {#code_begin|exe|values#} const std = @import("std"); -- cgit v1.2.3 From 058bfb254c4c0e1cfb254791f771c88c74f299e8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 Jul 2018 11:34:42 -0400 Subject: std.fmt.format: add '*' for formatting things as pointers closes #1285 --- std/fmt/index.zig | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 2188cc5803..8daec50f17 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -18,6 +18,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), OpenBrace, CloseBrace, FormatString, + Pointer, }; comptime var start_index = 0; @@ -54,6 +55,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), state = State.Start; start_index = i + 1; }, + '*' => state = State.Pointer, else => { state = State.FormatString; }, @@ -75,6 +77,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), }, else => {}, }, + State.Pointer => switch (c) { + '}' => { + try output(context, @typeName(@typeOf(args[next_arg]).Child)); + try output(context, "@"); + try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output); + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + else => @compileError("Unexpected format character after '*'"), + }, } } comptime { @@ -861,6 +874,27 @@ test "fmt.format" { const value: u8 = 'a'; try testFmt("u8: a\n", "u8: {c}\n", value); } + { + const value: [3]u8 = "abc"; + try testFmt("array: abc\n", "array: {}\n", value); + try testFmt("array: abc\n", "array: {}\n", &value); + + var buf: [100]u8 = undefined; + try testFmt( + try bufPrint(buf[0..], "array: [3]u8@{x}\n", @ptrToInt(&value)), + "array: {*}\n", + &value, + ); + } + { + const value: []const u8 = "abc"; + try testFmt("slice: abc\n", "slice: {}\n", value); + } + { + const value = @intToPtr(*i32, 0xdeadbeef); + try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); + try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", value); + } try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); -- cgit v1.2.3 From de949b72c791477c29155ee09a1c5c3d4ab56033 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 Jul 2018 19:57:46 -0400 Subject: simpler std.event.Lock implementation --- std/event/lock.zig | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/std/event/lock.zig b/std/event/lock.zig index 2013b5595f..0bd7183db2 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -106,35 +106,12 @@ pub const Lock = struct { // will attempt to grab the lock. _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - while (true) { - const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - if (old_bit != 0) { - // We did not obtain the lock. Trust that our queue entry will resume us, and allow - // suspend to complete. - break; - } - // We got the lock. However we might have already been resumed from the queue. + const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + if (old_bit == 0) { if (self.queue.get()) |node| { // Whether this node is us or someone else, we tail resume it. resume node.data; - break; - } else { - // We already got resumed, and there are none left in the queue, which means that - // we aren't even supposed to hold the lock right now. - _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - _ = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - - // There might be a queue item. If we know the queue is empty, we can be done, - // because the other actor will try to obtain the lock. - // But if there's a queue item, we are the actor which must loop and attempt - // to grab the lock again. - if (@atomicLoad(u8, &self.queue_empty_bit, AtomicOrder.SeqCst) == 1) { - break; - } else { - continue; - } } - unreachable; } } -- cgit v1.2.3 From e66f538972d1278ed95513aa1d97c196818a73c6 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 2 Aug 2018 03:38:04 +1200 Subject: Add integer binary output format (#1313) --- std/fmt/index.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 8daec50f17..f4f9efee37 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -248,6 +248,11 @@ pub fn formatIntValue( return formatAsciiChar(value, context, Errors, output); } }, + 'b' => { + radix = 2; + uppercase = false; + width = 0; + }, 'd' => { radix = 10; uppercase = false; @@ -874,6 +879,10 @@ test "fmt.format" { const value: u8 = 'a'; try testFmt("u8: a\n", "u8: {c}\n", value); } + { + const value: u8 = 0b1100; + try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value); + } { const value: [3]u8 = "abc"; try testFmt("array: abc\n", "array: {}\n", value); -- cgit v1.2.3 From e79c913cbcb03834fcc04e2258ed5da7d533c9db Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:16:31 +0900 Subject: src/all_types.hpp: add enums for Handle Builtin; Tracking Issue #1296 ; --- src/all_types.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/all_types.hpp b/src/all_types.hpp index 2f09e70301..f03a250aea 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1358,6 +1358,7 @@ enum BuiltinFnId { BuiltinFnIdBreakpoint, BuiltinFnIdReturnAddress, BuiltinFnIdFrameAddress, + BuiltinFnIdHandle, BuiltinFnIdEmbedFile, BuiltinFnIdCmpxchgWeak, BuiltinFnIdCmpxchgStrong, @@ -2076,6 +2077,7 @@ enum IrInstructionId { IrInstructionIdBreakpoint, IrInstructionIdReturnAddress, IrInstructionIdFrameAddress, + IrInstructionIdHandle, IrInstructionIdAlignOf, IrInstructionIdOverflowOp, IrInstructionIdTestErr, @@ -2793,6 +2795,10 @@ struct IrInstructionFrameAddress { IrInstruction base; }; +struct IrInstructionHandle { + IrInstruction base; +}; + enum IrOverflowOp { IrOverflowOpAdd, IrOverflowOpSub, -- cgit v1.2.3 From a9ea22d4f9816998cccfca4df2ef56d5069e1814 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:17:38 +0900 Subject: src/ir.cpp: wire-up IR for handle builtin; Tracking Issue #1296 ; --- src/ir.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 699baa152e..50c8c70290 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -580,6 +580,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *) return IrInstructionIdFrameAddress; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionHandle *) { + return IrInstructionIdHandle; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) { return IrInstructionIdAlignOf; } @@ -2240,6 +2244,17 @@ static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction return new_instruction; } +static IrInstruction *ir_build_handle(IrBuilder *irb, Scope *scope, AstNode *source_node) { + IrInstructionHandle *instruction = ir_build_instruction(irb, scope, source_node); + return &instruction->base; +} + +static IrInstruction *ir_build_handle_from(IrBuilder *irb, IrInstruction *old_instruction) { + IrInstruction *new_instruction = ir_build_handle(irb, old_instruction->scope, old_instruction->source_node); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *result_ptr, TypeTableEntry *result_ptr_type) @@ -4475,6 +4490,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval); case BuiltinFnIdFrameAddress: return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); + case BuiltinFnIdHandle: + return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval); case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -19007,6 +19024,13 @@ static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIn return u8_ptr_const; } +static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) { + ir_build_handle_from(&ira->new_irb, &instruction->base); + + TypeTableEntry *promise_type = get_promise_type(ira->codegen, nullptr); + return promise_type; +} + static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) { IrInstruction *type_value = instruction->type_value->other; if (type_is_invalid(type_value->value.type)) @@ -20982,6 +21006,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction); case IrInstructionIdFrameAddress: return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction); + case IrInstructionIdHandle: + return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction); case IrInstructionIdAlignOf: return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction); case IrInstructionIdOverflowOp: @@ -21274,6 +21300,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAlignOf: case IrInstructionIdReturnAddress: case IrInstructionIdFrameAddress: + case IrInstructionIdHandle: case IrInstructionIdTestErr: case IrInstructionIdUnwrapErrCode: case IrInstructionIdOptionalWrap: -- cgit v1.2.3 From cd18186715bf532715c03d8e67096606fe0f2bee Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:18:29 +0900 Subject: src/codegen.cpp: base handle builtin on `@frameAddress()`; Tracking Issue #1296 ; --- src/codegen.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index 7420da9797..336aded82c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4146,6 +4146,15 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, ""); } +static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, + IrInstructionHandle *instruction) +{ + // @andrewrk, not sure what to place here ? + // `get_promise_frame_type` ? + LLVMValueRef handle = LLVMConstNull(g->builtin_types.entry_promise->type_ref); + return LLVMBuildRet(g->builder, handle); +} + static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { TypeTableEntry *int_type = instruction->result_ptr_type; assert(int_type->id == TypeTableEntryIdInt); @@ -4910,6 +4919,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction); case IrInstructionIdFrameAddress: return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction); + case IrInstructionIdHandle: + return ir_render_handle(g, executable, (IrInstructionHandle *)instruction); case IrInstructionIdOverflowOp: return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction); case IrInstructionIdTestErr: @@ -6344,6 +6355,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0); create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0); create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0); + create_builtin_fn(g, BuiltinFnIdHandle, "handle", 0); create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3); create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3); create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1); -- cgit v1.2.3 From da5f3d5c4c79b1d0d0919a0226f5304081b2f049 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:19:16 +0900 Subject: src/ir_print.cpp: support `@handle()`; Tracking Issue #1296 ; --- src/ir_print.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 127afa94a5..77c7ef47b6 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -791,6 +791,10 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst fprintf(irp->f, "@frameAddress()"); } +static void ir_print_handle(IrPrint *irp, IrInstructionHandle *instruction) { + fprintf(irp->f, "@handle()"); +} + static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) { fprintf(irp->f, "@returnAddress()"); } @@ -1556,6 +1560,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFrameAddress: ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction); break; + case IrInstructionIdHandle: + ir_print_handle(irp, (IrInstructionHandle *)instruction); + break; case IrInstructionIdAlignOf: ir_print_align_of(irp, (IrInstructionAlignOf *)instruction); break; -- cgit v1.2.3 From 9366a58bdd91a8b5e7bc7d4babb6f91b989769db Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:19:47 +0900 Subject: test/cases/couroutines.zig: test @handle(); Tracking Issue #1296 ; --- test/cases/coroutines.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 72a4ed0b38..53c5c3f906 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -66,6 +66,11 @@ async fn testSuspendBlock() void { comptime assert(@typeOf(p) == promise->void); a_promise = p; } + + //Test to make sure that @handle() works as advertised (issue #1296) + //var our_handle: promise = @handle(); + assert( a_promise == @handle() ); + result = true; } -- cgit v1.2.3 From a2e5691228c61e5c56220fc8f5f72e47b0611000 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 11:46:31 +0900 Subject: src/codegen.cpp: return null if calling convention is not async; Tracking Issue #1296 ; --- src/codegen.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 336aded82c..cbd1955839 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4149,9 +4149,15 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionHandle *instruction) { - // @andrewrk, not sure what to place here ? - // `get_promise_frame_type` ? - LLVMValueRef handle = LLVMConstNull(g->builtin_types.entry_promise->type_ref); + + bool is_async = executable->fn_entry != nullptr && + executable->fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; + + if (!is_async || !executable->coro_handle) { + return LLVMConstNull(g->builtin_types.entry_promise->type_ref); + } + + LLVMValueRef handle = ir_llvm_value(g, executable->coro_handle); return LLVMBuildRet(g->builder, handle); } -- cgit v1.2.3 From 81f463626ad09dd09e525cda140fb63baf11bc73 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 14:13:26 +0900 Subject: src/codegen.cpp: add/throw error for @handle() in a non async context; Tracking Issue #1296 ; I removed/commented-out the assert checking for no errors since we now have some errors rendered. --- src/codegen.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index cbd1955839..43e2a0b694 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4154,6 +4154,7 @@ static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, executable->fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; if (!is_async || !executable->coro_handle) { + add_node_error(g, instruction->base.source_node, buf_sprintf("@handle() in non-async function")); return LLVMConstNull(g->builtin_types.entry_promise->type_ref); } @@ -6022,7 +6023,8 @@ static void do_code_gen(CodeGen *g) { ir_render(g, fn_table_entry); } - assert(!g->errors.length); + + //assert(!g->errors.length); if (buf_len(&g->global_asm) != 0) { LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); -- cgit v1.2.3 From 0ee65025623c0440ed655d68b579f17e6d6e5f5c Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:20:56 +0900 Subject: src/codegen.cpp: remove `add_node_error` from `ir_render_handle`; Tracking Issue #1296 ; Thanks @andrewrk ; --- src/codegen.cpp | 12 +----------- test/cases/coroutines.zig | 3 +++ 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 43e2a0b694..8a18a3f8dd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4149,17 +4149,7 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionHandle *instruction) { - - bool is_async = executable->fn_entry != nullptr && - executable->fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; - - if (!is_async || !executable->coro_handle) { - add_node_error(g, instruction->base.source_node, buf_sprintf("@handle() in non-async function")); - return LLVMConstNull(g->builtin_types.entry_promise->type_ref); - } - - LLVMValueRef handle = ir_llvm_value(g, executable->coro_handle); - return LLVMBuildRet(g->builder, handle); + return LLVMConstNull(g->builtin_types.entry_promise->type_ref); } static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 53c5c3f906..deb4aa1b24 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -57,6 +57,9 @@ test "coroutine suspend with block" { resume a_promise; std.debug.assert(result); cancel p; + + assert( @handle() ); + } var a_promise: promise = undefined; -- cgit v1.2.3 From db362bec185d2eeb5a4dd018deb2a988479f0f1a Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:22:00 +0900 Subject: src/codegen.cpp: reassert that there are no generated errors in codegen; Tracking Issue #1296 ; Thanks @andrewrk ; --- src/codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 8a18a3f8dd..54effb9480 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6014,7 +6014,7 @@ static void do_code_gen(CodeGen *g) { } - //assert(!g->errors.length); + assert(!g->errors.length); if (buf_len(&g->global_asm) != 0) { LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); -- cgit v1.2.3 From c1a3b0cb0af3c0639ce09b2a17a3cc90977346fe Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:23:09 +0900 Subject: src/ir.cpp: add/throw error for @handle() in a non async context; Tracking Issue #1296 ; Thanks @andrewrk ; --- src/ir.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 50c8c70290..1e9d1bdb7e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3858,6 +3858,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return irb->codegen->invalid_instruction; } + bool is_async = exec_is_async(irb->exec); + switch (builtin_fn->id) { case BuiltinFnIdInvalid: zig_unreachable(); @@ -4491,6 +4493,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdFrameAddress: return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); case BuiltinFnIdHandle: + if (!is_async) { + add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function")); + return irb->codegen->invalid_instruction; + } return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval); case BuiltinFnIdAlignOf: { -- cgit v1.2.3 From 1f0040dd92f64cebf77a5ce34bd72984d92b3f2f Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:37:23 +0900 Subject: test/cases/coroutines.zig: remove dummy assert used for testing; --- test/cases/coroutines.zig | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index deb4aa1b24..53c5c3f906 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -57,9 +57,6 @@ test "coroutine suspend with block" { resume a_promise; std.debug.assert(result); cancel p; - - assert( @handle() ); - } var a_promise: promise = undefined; -- cgit v1.2.3 From fcf53b31fc2899cba682c4c9a8cade40c6e0ab9e Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:38:22 +0900 Subject: src/ir.cpp: return promise->T instead of promise; Tracking Issue #1296 ; Thanks @andrewrk ; --- src/ir.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 1e9d1bdb7e..9712cb0bc2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19033,8 +19033,9 @@ static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIn static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) { ir_build_handle_from(&ira->new_irb, &instruction->base); - TypeTableEntry *promise_type = get_promise_type(ira->codegen, nullptr); - return promise_type; + FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); + assert(fn_entry != nullptr); + return get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type); } static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) { -- cgit v1.2.3 From a8ea2360958354ba8310c7cea388351299e72e44 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 17:35:50 +0900 Subject: src/ir.cpp: don't allow `@handle()` outside of a function; Tracking Issue #1296 ; --- src/ir.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 9712cb0bc2..966d8e9f33 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4493,6 +4493,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdFrameAddress: return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); case BuiltinFnIdHandle: + if (!irb->exec->fn_entry) { + add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition")); + return irb->codegen->invalid_instruction; + } if (!is_async) { add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function")); return irb->codegen->invalid_instruction; -- cgit v1.2.3 From 104bdb03d6b5906716efeb84045079a424bf650a Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 23:29:40 +0900 Subject: src/codegen.cpp: return promise instead of null promise; Tracking Issue #1296 ; --- src/codegen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 54effb9480..bd708e3824 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4149,7 +4149,8 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionHandle *instruction) { - return LLVMConstNull(g->builtin_types.entry_promise->type_ref); + LLVMValueRef ptr = ir_llvm_value(g, executable->fn_entry->ir_executable.coro_handle->other); + return LLVMBuildBitCast(g->builder, ptr, g->builtin_types.entry_promise->type_ref, ""); } static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { -- cgit v1.2.3 From c546f750f14e63b80c01d707c5559524313edfe4 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 23:51:48 +0900 Subject: test/compile_errors.zig: @handle() called outside of function definition; Tracking Issue #1296 ; --- test/compile_errors.zig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2c4c9208eb..c34b325a78 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -4738,4 +4738,20 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", ); + + cases.add( + "@handle() called outside of function definition", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\ + \\var handle_undef: promise = undefined; + \\var handle_dummy: promise = @handle(); + \\ + \\pub fn main() void { + \\ if (handle_undef == handle_dummy) return 0; + \\} + , + ".tmp_source.zig:6:29: error: @handle() called outside of function definition", + ); } -- cgit v1.2.3 From 13ec5db2348a0f6a4464aa8a513dbf11b72dc3ae Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 23:52:12 +0900 Subject: test/compile_errors.zig: @handle() in non-async function Tracking Issue #1296 ; --- test/compile_errors.zig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index c34b325a78..241f3a47a8 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -4754,4 +4754,18 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:6:29: error: @handle() called outside of function definition", ); + + cases.add( + "@handle() in non-async function", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\ + \\pub fn main() void { + \\ var handle_undef: promise = undefined; + \\ if (handle_undef == @handle()) return 0; + \\} + , + ".tmp_source.zig:7:25: error: @handle() in non-async function", + ); } -- cgit v1.2.3 From 92cb330e160388bca7ddd7ceecbd7157ce92247b Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 12:27:50 +0900 Subject: src/codegen.cpp: @handle(): replace hacky ref chain with llvm intrinsic; Tracking Issue #1296 ; --- src/all_types.hpp | 1 + src/codegen.cpp | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index f03a250aea..8d55a75f9f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1717,6 +1717,7 @@ struct CodeGen { LLVMValueRef coro_save_fn_val; LLVMValueRef coro_promise_fn_val; LLVMValueRef coro_alloc_helper_fn_val; + LLVMValueRef coro_frame_fn_val; LLVMValueRef merge_err_ret_traces_fn_val; LLVMValueRef add_error_return_trace_addr_fn_val; LLVMValueRef stacksave_fn_val; diff --git a/src/codegen.cpp b/src/codegen.cpp index bd708e3824..539356ef2f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4146,11 +4146,24 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, ""); } +static LLVMValueRef get_handle_fn_val(CodeGen *g) { + if (g->coro_frame_fn_val) + return g->coro_frame_fn_val; + + LLVMTypeRef fn_type = LLVMFunctionType( LLVMPointerType(LLVMInt8Type(), 0) + , nullptr, 0, false); + Buf *name = buf_sprintf("llvm.coro.frame"); + g->coro_frame_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type); + assert(LLVMGetIntrinsicID(g->coro_frame_fn_val)); + + return g->coro_frame_fn_val; +} + static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionHandle *instruction) { - LLVMValueRef ptr = ir_llvm_value(g, executable->fn_entry->ir_executable.coro_handle->other); - return LLVMBuildBitCast(g->builder, ptr, g->builtin_types.entry_promise->type_ref, ""); + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_promise->type_ref); + return LLVMBuildCall(g->builder, get_handle_fn_val(g), &zero, 0, ""); } static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { -- cgit v1.2.3 From ca1b3563372dbf4442b5c3ff0fecaa174aea06b7 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:09:00 +0900 Subject: src/all_types.hpp: remove promise_symbol from suspend; Tracking Issue #1296 ; --- src/all_types.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 8d55a75f9f..b1e8a3746d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -899,7 +899,6 @@ struct AstNodeAwaitExpr { struct AstNodeSuspend { AstNode *block; - AstNode *promise_symbol; }; struct AstNodePromiseType { -- cgit v1.2.3 From 5e5685c1174350b00ebf3e9ac49e244fa38fc0ac Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:09:39 +0900 Subject: src/ast_render.cpp: remove promise_symbol from suspend; Tracking Issue #1296 ; --- src/ast_render.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 2ace00885d..984b4230b1 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -1112,9 +1112,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { { fprintf(ar->f, "suspend"); if (node->data.suspend.block != nullptr) { - fprintf(ar->f, " |"); - render_node_grouped(ar, node->data.suspend.promise_symbol); - fprintf(ar->f, "| "); render_node_grouped(ar, node->data.suspend.block); } break; -- cgit v1.2.3 From b3cd65d56e2efc3e0f67461dbad75747b2db3aa7 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:10:48 +0900 Subject: src/ir.cpp: remove promise_symbol from suspend; Tracking Issue #1296 ; --- src/ir.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 966d8e9f33..7d2881744d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7096,19 +7096,8 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod if (node->data.suspend.block == nullptr) { suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false); } else { - assert(node->data.suspend.promise_symbol != nullptr); - assert(node->data.suspend.promise_symbol->type == NodeTypeSymbol); - Buf *promise_symbol_name = node->data.suspend.promise_symbol->data.symbol_expr.symbol; Scope *child_scope; - if (!buf_eql_str(promise_symbol_name, "_")) { - VariableTableEntry *promise_var = ir_create_var(irb, node, parent_scope, promise_symbol_name, - true, true, false, const_bool_false); - ir_build_var_decl(irb, parent_scope, node, promise_var, nullptr, nullptr, irb->exec->coro_handle); - child_scope = promise_var->child_scope; - } else { - child_scope = parent_scope; - } - ScopeSuspend *suspend_scope = create_suspend_scope(node, child_scope); + ScopeSuspend *suspend_scope = create_suspend_scope(node, parent_scope); suspend_scope->resume_block = resume_block; child_scope = &suspend_scope->base; IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle); -- cgit v1.2.3 From d3f628907a6b9e5b863c9d5235e6dadf57f42ca2 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:11:43 +0900 Subject: src/parser.cpp: remove promise_symbol from suspend; Tracking Issue #1296 ; --- src/parser.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index a93d8de830..e2a818a56c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -648,35 +648,37 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m } /* -SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) +SuspendExpression(body) = "suspend" option( body ) */ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { size_t orig_token_index = *token_index; + Token *token = &pc->tokens->at(*token_index); + Token *suspend_token = nullptr; - Token *suspend_token = &pc->tokens->at(*token_index); if (suspend_token->id == TokenIdKeywordSuspend) { *token_index += 1; + suspend_token = token; + token = &pc->tokens->at(*token_index); } else if (mandatory) { - ast_expect_token(pc, suspend_token, TokenIdKeywordSuspend); + ast_expect_token(pc, token, TokenIdKeywordSuspend); zig_unreachable(); } else { return nullptr; } - Token *bar_token = &pc->tokens->at(*token_index); - if (bar_token->id == TokenIdBinOr) { - *token_index += 1; - } else if (mandatory) { - ast_expect_token(pc, suspend_token, TokenIdBinOr); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; + //guessing that semicolon is checked elsewhere? + if (token->id != TokenIdLBrace) { + if (mandatory) { + ast_expect_token(pc, token, TokenIdLBrace); + zig_unreachable(); + } else { + *token_index = orig_token_index; + return nullptr; + } } + //Expect that we have a block; AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); - node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index); - ast_eat_token(pc, token_index, TokenIdBinOr); node->data.suspend.block = ast_parse_block(pc, token_index, true); return node; @@ -3134,7 +3136,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.await_expr.expr, visit, context); break; case NodeTypeSuspend: - visit_field(&node->data.suspend.promise_symbol, visit, context); visit_field(&node->data.suspend.block, visit, context); break; } -- cgit v1.2.3 From 29057e5511ed007ee8db8306e89b674384c35964 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:12:33 +0900 Subject: std/event/channel.zig: remove promise_symbol from suspend and use @handle(); Tracking Issue #1296 ; --- std/event/channel.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/std/event/channel.zig b/std/event/channel.zig index 03a036042b..71e97f6e78 100644 --- a/std/event/channel.zig +++ b/std/event/channel.zig @@ -71,10 +71,10 @@ pub fn Channel(comptime T: type) type { /// puts a data item in the channel. The promise completes when the value has been added to the /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. pub async fn put(self: *SelfChannel, data: T) void { - suspend |handle| { + suspend { var my_tick_node = Loop.NextTickNode{ .next = undefined, - .data = handle, + .data = @handle(), }; var queue_node = std.atomic.Queue(PutNode).Node{ .data = PutNode{ @@ -96,10 +96,10 @@ pub fn Channel(comptime T: type) type { // TODO integrate this function with named return values // so we can get rid of this extra result copy var result: T = undefined; - suspend |handle| { + suspend { var my_tick_node = Loop.NextTickNode{ .next = undefined, - .data = handle, + .data = @handle(), }; var queue_node = std.atomic.Queue(GetNode).Node{ .data = GetNode{ -- cgit v1.2.3 From 244a7fdafb97b215e0e9e3e8aaa23777eccebd14 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:12:52 +0900 Subject: std/event/future.zig: remove promise_symbol from suspend and use @handle(); Tracking Issue #1296 ; --- std/event/future.zig | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/std/event/future.zig b/std/event/future.zig index f5d14d1ca6..f9b9db86a7 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -100,8 +100,9 @@ test "std.event.Future" { } async fn testFuture(loop: *Loop) void { - suspend |p| { - resume p; + suspend { + var h: promise = @handle(); + resume h; } var future = Future(i32).init(loop); @@ -115,15 +116,17 @@ async fn testFuture(loop: *Loop) void { } async fn waitOnFuture(future: *Future(i32)) i32 { - suspend |p| { - resume p; + suspend { + var h: promise = @handle(); + resume h; } return (await (async future.get() catch @panic("memory"))).*; } async fn resolveFuture(future: *Future(i32)) void { - suspend |p| { - resume p; + suspend { + var h: promise = @handle(); + resume h; } future.data = 6; future.resolve(); -- cgit v1.2.3 From b4ff464d39038fe840ed6fce3f73cd075fde25f2 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:13:16 +0900 Subject: std/event/group.zig: remove promise_symbol from suspend and use @handle(); Tracking Issue #1296 ; --- std/event/group.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/std/event/group.zig b/std/event/group.zig index 26c098399e..493913010f 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -54,10 +54,11 @@ pub fn Group(comptime ReturnType: type) type { const S = struct { async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType { // TODO this is a hack to make the memory following be inside the coro frame - suspend |p| { + suspend { var my_node: Stack.Node = undefined; node.* = &my_node; - resume p; + var h: promise = @handle(); + resume h; } // TODO this allocation elision should be guaranteed because we await it in -- cgit v1.2.3 From efec3a0e342be01f7cf74cf7906dd50b98e5aa97 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:13:42 +0900 Subject: std/event/lock.zig: remove promise_symbol from suspend and use @handle(); Tracking Issue #1296 ; --- std/event/lock.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/std/event/lock.zig b/std/event/lock.zig index 0bd7183db2..2769a2153c 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -90,10 +90,10 @@ pub const Lock = struct { } pub async fn acquire(self: *Lock) Held { - suspend |handle| { + suspend { // TODO explicitly put this memory in the coroutine frame #1194 var my_tick_node = Loop.NextTickNode{ - .data = handle, + .data = @handle(), .next = undefined, }; @@ -141,8 +141,9 @@ test "std.event.Lock" { async fn testLock(loop: *Loop, lock: *Lock) void { // TODO explicitly put next tick node memory in the coroutine frame #1194 - suspend |p| { - resume p; + suspend { + var h: promise = @handle(); + resume h; } const handle1 = async lockRunner(lock) catch @panic("out of memory"); var tick_node1 = Loop.NextTickNode{ -- cgit v1.2.3 From a3705b425134d8e9c22ce238e7de9568d3394a44 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:14:02 +0900 Subject: std/event/loop.zig: remove promise_symbol from suspend and use @handle(); Tracking Issue #1296 ; --- std/event/loop.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/std/event/loop.zig b/std/event/loop.zig index 4e219653be..8b1b2e53db 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -331,11 +331,11 @@ pub const Loop = struct { pub async fn waitFd(self: *Loop, fd: i32) !void { defer self.removeFd(fd); - suspend |p| { + suspend { // TODO explicitly put this memory in the coroutine frame #1194 var resume_node = ResumeNode{ .id = ResumeNode.Id.Basic, - .handle = p, + .handle = @handle(), }; try self.addFd(fd, &resume_node); } @@ -417,11 +417,11 @@ pub const Loop = struct { pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) { const S = struct { async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType { - suspend |p| { - handle.* = p; + suspend { + handle.* = @handle(); var my_tick_node = Loop.NextTickNode{ .next = undefined, - .data = p, + .data = @handle(), }; loop.onNextTick(&my_tick_node); } @@ -439,10 +439,10 @@ pub const Loop = struct { /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O /// is performed. pub async fn yield(self: *Loop) void { - suspend |p| { + suspend { var my_tick_node = Loop.NextTickNode{ .next = undefined, - .data = p, + .data = @handle(), }; self.onNextTick(&my_tick_node); } -- cgit v1.2.3 From 9fe140abad11a15a8bb81b3599600ca1ec6f00d0 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:14:35 +0900 Subject: std/event/tcp.zig: remove promise_symbol from suspend and use @handle(); Tracking Issue #1296 ; --- std/event/tcp.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/std/event/tcp.zig b/std/event/tcp.zig index 416a8c07dc..9a3c6f95ca 100644 --- a/std/event/tcp.zig +++ b/std/event/tcp.zig @@ -88,8 +88,8 @@ pub const Server = struct { }, error.ProcessFdQuotaExceeded => { errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node); - suspend |p| { - self.waiting_for_emfile_node = PromiseNode.init(p); + suspend { + self.waiting_for_emfile_node = PromiseNode.init( @handle() ); std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node); } continue; @@ -141,8 +141,9 @@ test "listen on a port, send bytes, receive bytes" { (await next_handler) catch |err| { std.debug.panic("unable to handle connection: {}\n", err); }; - suspend |p| { - cancel p; + suspend { + var h: promise = @handle(); + cancel h; } } async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void { -- cgit v1.2.3 From bc032a89cc13e483a55c58bcba4593229dd7f3ed Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:16:36 +0900 Subject: std/zig/parser_test.zig: update test to reflect that the promise symbol is no in scope with suspend; Tracking Issue #1296 ; --- std/zig/parser_test.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 21259bec3c..32cdc8121f 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1784,7 +1784,7 @@ test "zig fmt: coroutines" { \\ x += 1; \\ suspend; \\ x += 1; - \\ suspend |p| {} + \\ suspend; \\ const p: promise->void = async simpleAsyncFn() catch unreachable; \\ await p; \\} -- cgit v1.2.3 From 79792a32e19b5ef7a918655f09ac32dd94c8f19e Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:17:13 +0900 Subject: test/cases/coroutine_await_struct.zig: update test to reflect that the promise symbol is no in scope with suspend; Tracking Issue #1296 ; --- test/cases/coroutine_await_struct.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/coroutine_await_struct.zig b/test/cases/coroutine_await_struct.zig index 56c526092d..79168715d8 100644 --- a/test/cases/coroutine_await_struct.zig +++ b/test/cases/coroutine_await_struct.zig @@ -30,9 +30,9 @@ async fn await_amain() void { } async fn await_another() Foo { await_seq('c'); - suspend |p| { + suspend { await_seq('d'); - await_a_promise = p; + await_a_promise = @handle(); } await_seq('g'); return Foo{ .x = 1234 }; -- cgit v1.2.3 From 3241ada4682b6953431876edfb8fc7af0f346a34 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:17:48 +0900 Subject: test/cases/coroutines.zig: update test to reflect that the promise symbol is no in scope with suspend; Tracking Issue #1296 ; --- test/cases/coroutines.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 53c5c3f906..c2aeb5de83 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -62,9 +62,9 @@ test "coroutine suspend with block" { var a_promise: promise = undefined; var result = false; async fn testSuspendBlock() void { - suspend |p| { - comptime assert(@typeOf(p) == promise->void); - a_promise = p; + suspend { + comptime assert(@typeOf(@handle()) == promise->void); + a_promise = @handle(); } //Test to make sure that @handle() works as advertised (issue #1296) @@ -98,9 +98,9 @@ async fn await_amain() void { } async fn await_another() i32 { await_seq('c'); - suspend |p| { + suspend { await_seq('d'); - await_a_promise = p; + await_a_promise = @handle(); } await_seq('g'); return 1234; -- cgit v1.2.3 From 9b3cebcdb9bedfa2ef49ddfb0dffea0899ab558d Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:18:31 +0900 Subject: test/cases/coroutines.zig: test for immediate resume inside of suspend with @handle(); Tracking Issue #1296 ; --- test/cases/coroutines.zig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index c2aeb5de83..4dc20b9cfc 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -256,3 +256,19 @@ async fn testBreakFromSuspend(my_result: *i32) void { suspend; my_result.* += 1; } + +test "suspend resume @handle()" { + var buf: [500]u8 = undefined; + var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; + var my_result: i32 = 1; + const p = try async
    testBreakFromSuspend(&my_result); + std.debug.assert(my_result == 2); +} +async fn testSuspendResumeAtHandle() void { + suspend { + resume @handle(); + } + my_result.* += 1; + suspend; + my_result.* += 1; +} \ No newline at end of file -- cgit v1.2.3 From 51955a5ca2b6f3f005e28cd3758dc481c2eea0c3 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:18:54 +0900 Subject: test/compile_errors.zig: update test to reflect that the promise symbol is no in scope with suspend; Tracking Issue #1296 ; --- test/compile_errors.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 241f3a47a8..f4b289f70d 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -367,8 +367,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\ \\async fn foo() void { - \\ suspend |p| { - \\ suspend |p1| { + \\ suspend { + \\ suspend { \\ } \\ } \\} -- cgit v1.2.3 From ff4a03f35157c3c82d581a599bf98db86504ecc1 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:19:36 +0900 Subject: doc/langref.html.in: update docs to reflect that the promise symbol is no in scope with suspend; Tracking Issue #1296 ; --- doc/langref.html.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 7fde550338..58b63f7f40 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4690,9 +4690,9 @@ test "coroutine suspend with block" { var a_promise: promise = undefined; var result = false; async fn testSuspendBlock() void { - suspend |p| { - comptime assert(@typeOf(p) == promise->void); - a_promise = p; + suspend { + comptime assert(@typeOf(@handle()) == promise->void); + a_promise = @handle(); } result = true; } @@ -4791,9 +4791,9 @@ async fn amain() void { } async fn another() i32 { seq('c'); - suspend |p| { + suspend { seq('d'); - a_promise = p; + a_promise = @handle(); } seq('g'); return 1234; @@ -7388,7 +7388,7 @@ Defer(body) = ("defer" | "deferror") body IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) -SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) +SuspendExpression(body) = "suspend" option( body ) IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body) -- cgit v1.2.3 From 5de92425d57338ba6c94a90360f96eb2fa8efdda Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:11:37 +0900 Subject: src/parser.cpp: fix typo from rebase; --- src/parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.cpp b/src/parser.cpp index e2a818a56c..84ccdbeea8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -655,7 +655,7 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b Token *token = &pc->tokens->at(*token_index); Token *suspend_token = nullptr; - if (suspend_token->id == TokenIdKeywordSuspend) { + if (token->id == TokenIdKeywordSuspend) { *token_index += 1; suspend_token = token; token = &pc->tokens->at(*token_index); -- cgit v1.2.3 From 915e321a2334b32ae398ec669d059f43fc2a8774 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:45:35 +0900 Subject: doc/langref.html.in: update suspend example with @handle(); Tracking Issue #1296 ; --- doc/langref.html.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 58b63f7f40..92fae2347d 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4733,8 +4733,8 @@ test "resume from suspend" { std.debug.assert(my_result == 2); } async fn testResumeFromSuspend(my_result: *i32) void { - suspend |p| { - resume p; + suspend { + resume @handle(); } my_result.* += 1; suspend; -- cgit v1.2.3 From 9bed23f8b77531b1699ff79bbdd8bc9281ddfe5d Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:46:41 +0900 Subject: test/cases/coroutines.zig: update suspend to use @handle(); Tracking Issue #1296 ; --- test/cases/coroutines.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 4dc20b9cfc..a955eeac37 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -249,8 +249,8 @@ test "break from suspend" { std.debug.assert(my_result == 2); } async fn testBreakFromSuspend(my_result: *i32) void { - suspend |p| { - resume p; + suspend { + resume @handle(); } my_result.* += 1; suspend; -- cgit v1.2.3 From 9b890d70675b105ba2810e6b08010098e198043c Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:47:03 +0900 Subject: test/cases/cancel.zig: update suspend to use @handle(); Tracking Issue #1296 ; --- test/cases/cancel.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/cancel.zig b/test/cases/cancel.zig index edf11d687d..c0f74fd34f 100644 --- a/test/cases/cancel.zig +++ b/test/cases/cancel.zig @@ -85,8 +85,8 @@ async fn b4() void { defer { defer_b4 = true; } - suspend |p| { - b4_handle = p; + suspend { + b4_handle = @handle(); } suspend; } -- cgit v1.2.3 From ac0a87d58d72a8c1fbb9b0af2d3818075729d85a Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:47:39 +0900 Subject: doc/langref.html.in: add builtin @handle() to docs; Tracking Issue #1296 ; --- doc/langref.html.in | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/langref.html.in b/doc/langref.html.in index 92fae2347d..54677bc5b5 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5383,6 +5383,16 @@ test "main" { This function is only valid within function scope.

    {#header_close#} + {#header_open|@handle#} +
    @handle()
    +

    + This function returns a promise->T type, where T + is the return type of the async function in scope. +

    +

    + This function is only valid within an async function scope. +

    + {#header_close#} {#header_open|@import#}
    @import(comptime path: []u8) (namespace)

    -- cgit v1.2.3 From 96a94e7da933dafec25356c435f5725c3cb0ce04 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:52:40 +0900 Subject: std/event: directly return @handle(); Tracking Issue #1296 ; --- std/event/future.zig | 9 +++------ std/event/group.zig | 3 +-- std/event/lock.zig | 3 +-- std/event/tcp.zig | 3 +-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/std/event/future.zig b/std/event/future.zig index f9b9db86a7..8abdce7d02 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -101,8 +101,7 @@ test "std.event.Future" { async fn testFuture(loop: *Loop) void { suspend { - var h: promise = @handle(); - resume h; + resume @handle(); } var future = Future(i32).init(loop); @@ -117,16 +116,14 @@ async fn testFuture(loop: *Loop) void { async fn waitOnFuture(future: *Future(i32)) i32 { suspend { - var h: promise = @handle(); - resume h; + resume @handle(); } return (await (async future.get() catch @panic("memory"))).*; } async fn resolveFuture(future: *Future(i32)) void { suspend { - var h: promise = @handle(); - resume h; + resume @handle(); } future.data = 6; future.resolve(); diff --git a/std/event/group.zig b/std/event/group.zig index 493913010f..6c7fc63699 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -57,8 +57,7 @@ pub fn Group(comptime ReturnType: type) type { suspend { var my_node: Stack.Node = undefined; node.* = &my_node; - var h: promise = @handle(); - resume h; + resume @handle(); } // TODO this allocation elision should be guaranteed because we await it in diff --git a/std/event/lock.zig b/std/event/lock.zig index 2769a2153c..c4cb1a3f0e 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -142,8 +142,7 @@ test "std.event.Lock" { async fn testLock(loop: *Loop, lock: *Lock) void { // TODO explicitly put next tick node memory in the coroutine frame #1194 suspend { - var h: promise = @handle(); - resume h; + resume @handle(); } const handle1 = async lockRunner(lock) catch @panic("out of memory"); var tick_node1 = Loop.NextTickNode{ diff --git a/std/event/tcp.zig b/std/event/tcp.zig index 9a3c6f95ca..ea803a9322 100644 --- a/std/event/tcp.zig +++ b/std/event/tcp.zig @@ -142,8 +142,7 @@ test "listen on a port, send bytes, receive bytes" { std.debug.panic("unable to handle connection: {}\n", err); }; suspend { - var h: promise = @handle(); - cancel h; + cancel @handle(); } } async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void { -- cgit v1.2.3 From 432b7685bfa840a459b492a37894f7ffed870c7e Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 01:59:59 +0900 Subject: std/os/index.zig: use "hw.logicalcpu" instead of "hw.ncpu" in macOS; (#1317) Tracking Issue #1252 ; hw.ncpu was deprecated in macOS. Among 4 new options available (hw.{physicalcpu, physicalcpu_max, logicalcpu, logicalcpu_max}), hw.logicalcpu was chosen because it actually reflects the number of logical cores the OS sees. --- std/os/index.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/os/index.zig b/std/os/index.zig index 77fd2a78ad..0a6f598a2e 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2790,7 +2790,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { builtin.Os.macosx => { var count: c_int = undefined; var count_len: usize = @sizeOf(c_int); - const rc = posix.sysctlbyname(c"hw.ncpu", @ptrCast(*c_void, &count), &count_len, null, 0); + const rc = posix.sysctlbyname(c"hw.logicalcpu", @ptrCast(*c_void, &count), &count_len, null, 0); const err = posix.getErrno(rc); switch (err) { 0 => return @intCast(usize, count), -- cgit v1.2.3 From 22fd359e2c601c9d0a009705bed60c88821f2b0f Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 02:14:06 +0900 Subject: std/os/windows/advapi32.zig: add SystemFunction036; Tracking Issue #1318 ; --- std/os/windows/advapi32.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/std/os/windows/advapi32.zig b/std/os/windows/advapi32.zig index dcb5a636ea..64a820a3b1 100644 --- a/std/os/windows/advapi32.zig +++ b/std/os/windows/advapi32.zig @@ -28,3 +28,8 @@ pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD, lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS; + +// RtlGenRandom is known as SystemFunction036 under advapi32 +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */ +pub extern "advapi32" stdcallcc fn SystemFunction036(output: PVOID, length: ULONG_PTR) BOOL; +pub const RtlGenRandom = SystemFunction036; -- cgit v1.2.3 From c44653f40f37c93fc68a2e455d693bb11f1c11d3 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 02:14:52 +0900 Subject: std/os/index.zig: swap CryptGetRandom() with RtlGenRandom(); Tracking Issue #1318 ; --- std/os/index.zig | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index 77fd2a78ad..7042247a2f 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -130,16 +130,10 @@ pub fn getRandomBytes(buf: []u8) !void { try posixRead(fd, buf); }, Os.windows => { - var hCryptProv: windows.HCRYPTPROV = undefined; - if (windows.CryptAcquireContextA(&hCryptProv, null, null, windows.PROV_RSA_FULL, 0) == 0) { - const err = windows.GetLastError(); - return switch (err) { - else => unexpectedErrorWindows(err), - }; - } - defer _ = windows.CryptReleaseContext(hCryptProv, 0); - - if (windows.CryptGenRandom(hCryptProv, @intCast(windows.DWORD, buf.len), buf.ptr) == 0) { + // Call RtlGenRandom() instead of CryptGetRandom() on Windows + // https://github.com/rust-lang-nursery/rand/issues/111 + // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 + if (!windows.RtlGenRandom(buf.ptr, buf.len)) { const err = windows.GetLastError(); return switch (err) { else => unexpectedErrorWindows(err), -- cgit v1.2.3 From dde7eb45c51dd96242a6b028eb1912ee6ffa2c55 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 02:16:19 +0900 Subject: std/os/index.zig: call getRandomBytes() twice and compare; Tracking Issue #1318 ; --- std/os/index.zig | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index 7042247a2f..87edce3dac 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -153,8 +153,14 @@ pub fn getRandomBytes(buf: []u8) !void { } test "os.getRandomBytes" { - var buf: [50]u8 = undefined; - try getRandomBytes(buf[0..]); + var buf_a: [50]u8 = undefined; + var buf_b: [50]u8 = undefined; + // Call Twice + try getRandomBytes(buf_a[0..]); + try getRandomBytes(buf_b[0..]); + + // Check if random (not 100% conclusive) + assert( !mem.eql(u8, buf_a, buf_b) ); } /// Raises a signal in the current kernel thread, ending its execution. -- cgit v1.2.3 From 782043e2e66ab5833cb20dc6e787b87eb84165f8 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 02:16:49 +0900 Subject: std/os/windows/util.zig: SKIP instead of PASS on non-windows systems; Tracking Issue #1318 ; --- std/os/windows/util.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index dda9ce7a8b..c9d2c3c3e6 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -166,7 +166,7 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void { } test "InvalidDll" { - if (builtin.os != builtin.Os.windows) return; + if (builtin.os != builtin.Os.windows) return error.SkipZigTest; const DllName = "asdf.dll"; const allocator = std.debug.global_allocator; -- cgit v1.2.3 From 729f2aceb045e54b2b74a33fa5f64cb9802988c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Aug 2018 13:34:31 -0400 Subject: fix API of RtlGenRandom --- std/os/index.zig | 2 +- std/os/windows/advapi32.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index b5e0129da4..425a900a71 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -133,7 +133,7 @@ pub fn getRandomBytes(buf: []u8) !void { // Call RtlGenRandom() instead of CryptGetRandom() on Windows // https://github.com/rust-lang-nursery/rand/issues/111 // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 - if (!windows.RtlGenRandom(buf.ptr, buf.len)) { + if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) { const err = windows.GetLastError(); return switch (err) { else => unexpectedErrorWindows(err), diff --git a/std/os/windows/advapi32.zig b/std/os/windows/advapi32.zig index 64a820a3b1..2f3195475c 100644 --- a/std/os/windows/advapi32.zig +++ b/std/os/windows/advapi32.zig @@ -31,5 +31,5 @@ pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPC // RtlGenRandom is known as SystemFunction036 under advapi32 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */ -pub extern "advapi32" stdcallcc fn SystemFunction036(output: PVOID, length: ULONG_PTR) BOOL; +pub extern "advapi32" stdcallcc fn SystemFunction036(output: [*]u8, length: usize) BOOL; pub const RtlGenRandom = SystemFunction036; -- cgit v1.2.3 From 895f262a55b9951647efef4528c17cf64d6b7c07 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Aug 2018 14:15:31 -0400 Subject: pull request fixups * clean up parser code * fix stage2 parse and render code * remove redundant test * make stage1 compile tests leaner --- src/parser.cpp | 36 +++++++++++++-------------------- std/zig/ast.zig | 12 ----------- std/zig/parse.zig | 33 +++++++++++++----------------- std/zig/parser_test.zig | 6 +++--- std/zig/render.zig | 16 +-------------- test/cases/coroutines.zig | 16 --------------- test/compile_errors.zig | 51 +++++++++++++++++++---------------------------- 7 files changed, 53 insertions(+), 117 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index 84ccdbeea8..453ab7ce2c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -651,37 +651,29 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m SuspendExpression(body) = "suspend" option( body ) */ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { - size_t orig_token_index = *token_index; - Token *token = &pc->tokens->at(*token_index); - Token *suspend_token = nullptr; + Token *suspend_token = &pc->tokens->at(*token_index); - if (token->id == TokenIdKeywordSuspend) { + if (suspend_token->id == TokenIdKeywordSuspend) { *token_index += 1; - suspend_token = token; - token = &pc->tokens->at(*token_index); } else if (mandatory) { - ast_expect_token(pc, token, TokenIdKeywordSuspend); + ast_expect_token(pc, suspend_token, TokenIdKeywordSuspend); zig_unreachable(); } else { return nullptr; } - //guessing that semicolon is checked elsewhere? - if (token->id != TokenIdLBrace) { - if (mandatory) { - ast_expect_token(pc, token, TokenIdLBrace); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; - } + Token *lbrace = &pc->tokens->at(*token_index); + if (lbrace->id == TokenIdLBrace) { + AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); + node->data.suspend.block = ast_parse_block(pc, token_index, true); + return node; + } else if (mandatory) { + ast_expect_token(pc, lbrace, TokenIdLBrace); + zig_unreachable(); + } else { + *token_index -= 1; + return nullptr; } - - //Expect that we have a block; - AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); - node->data.suspend.block = ast_parse_block(pc, token_index, true); - - return node; } /* diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 004f9278b9..95e899fb92 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1778,19 +1778,12 @@ pub const Node = struct { pub const Suspend = struct { base: Node, - label: ?TokenIndex, suspend_token: TokenIndex, - payload: ?*Node, body: ?*Node, pub fn iterate(self: *Suspend, index: usize) ?*Node { var i = index; - if (self.payload) |payload| { - if (i < 1) return payload; - i -= 1; - } - if (self.body) |body| { if (i < 1) return body; i -= 1; @@ -1800,7 +1793,6 @@ pub const Node = struct { } pub fn firstToken(self: *Suspend) TokenIndex { - if (self.label) |label| return label; return self.suspend_token; } @@ -1809,10 +1801,6 @@ pub const Node = struct { return body.lastToken(); } - if (self.payload) |payload| { - return payload.lastToken(); - } - return self.suspend_token; } }; diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 73d51e7870..fb49d2a2ba 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -852,19 +852,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }) catch unreachable; continue; }, - Token.Id.Keyword_suspend => { - const node = try arena.create(ast.Node.Suspend{ - .base = ast.Node{ .id = ast.Node.Id.Suspend }, - .label = ctx.label, - .suspend_token = token_index, - .payload = null, - .body = null, - }); - ctx.opt_ctx.store(&node.base); - stack.append(State{ .SuspendBody = node }) catch unreachable; - try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); - continue; - }, Token.Id.Keyword_inline => { stack.append(State{ .Inline = InlineCtx{ @@ -1415,10 +1402,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.SuspendBody => |suspend_node| { - if (suspend_node.payload != null) { - try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } }); + const token = nextToken(&tok_it, &tree); + switch (token.ptr.id) { + Token.Id.Semicolon => { + prevToken(&tok_it, &tree); + continue; + }, + Token.Id.LBrace => { + prevToken(&tok_it, &tree); + try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } }); + continue; + }, + else => { + ((try tree.errors.addOne())).* = Error{ .InvalidToken = Error.InvalidToken{ .token = token.index } }; + }, } - continue; }, State.AsyncAllocator => |async_node| { if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) { @@ -3086,15 +3084,12 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *con Token.Id.Keyword_suspend => { const node = try arena.create(ast.Node.Suspend{ .base = ast.Node{ .id = ast.Node.Id.Suspend }, - .label = null, .suspend_token = token_index, - .payload = null, .body = null, }); ctx.store(&node.base); stack.append(State{ .SuspendBody = node }) catch unreachable; - try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); return true; }, Token.Id.Keyword_if => { diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 32cdc8121f..582bffdf3d 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -898,11 +898,11 @@ test "zig fmt: union(enum(u32)) with assigned enum values" { ); } -test "zig fmt: labeled suspend" { +test "zig fmt: resume from suspend block" { try testCanonical( \\fn foo() void { - \\ s: suspend |p| { - \\ break :s; + \\ suspend { + \\ resume @handle(); \\ } \\} \\ diff --git a/std/zig/render.zig b/std/zig/render.zig index bc45768fa3..868902a0d1 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -323,21 +323,7 @@ fn renderExpression( ast.Node.Id.Suspend => { const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); - if (suspend_node.label) |label| { - try renderToken(tree, stream, label, indent, start_col, Space.None); - try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); - } - - if (suspend_node.payload) |payload| { - if (suspend_node.body) |body| { - try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); - try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space); - return renderExpression(allocator, stream, tree, indent, start_col, body, space); - } else { - try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); - return renderExpression(allocator, stream, tree, indent, start_col, payload, space); - } - } else if (suspend_node.body) |body| { + if (suspend_node.body) |body| { try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); return renderExpression(allocator, stream, tree, indent, start_col, body, space); } else { diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index a955eeac37..bd6b6abf6f 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -256,19 +256,3 @@ async fn testBreakFromSuspend(my_result: *i32) void { suspend; my_result.* += 1; } - -test "suspend resume @handle()" { - var buf: [500]u8 = undefined; - var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; - var my_result: i32 = 1; - const p = try async testBreakFromSuspend(&my_result); - std.debug.assert(my_result == 2); -} -async fn testSuspendResumeAtHandle() void { - suspend { - resume @handle(); - } - my_result.* += 1; - suspend; - my_result.* += 1; -} \ No newline at end of file diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f4b289f70d..948d212e58 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,27 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "@handle() called outside of function definition", + \\var handle_undef: promise = undefined; + \\var handle_dummy: promise = @handle(); + \\export fn entry() bool { + \\ return handle_undef == handle_dummy; + \\} + , + ".tmp_source.zig:2:29: error: @handle() called outside of function definition", + ); + + cases.add( + "@handle() in non-async function", + \\export fn entry() bool { + \\ var handle_undef: promise = undefined; + \\ return handle_undef == @handle(); + \\} + , + ".tmp_source.zig:3:28: error: @handle() in non-async function", + ); + cases.add( "while loop body expression ignored", \\fn returns() usize { @@ -4738,34 +4759,4 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", ); - - cases.add( - "@handle() called outside of function definition", - \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { - \\ @import("std").os.exit(126); - \\} - \\ - \\var handle_undef: promise = undefined; - \\var handle_dummy: promise = @handle(); - \\ - \\pub fn main() void { - \\ if (handle_undef == handle_dummy) return 0; - \\} - , - ".tmp_source.zig:6:29: error: @handle() called outside of function definition", - ); - - cases.add( - "@handle() in non-async function", - \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { - \\ @import("std").os.exit(126); - \\} - \\ - \\pub fn main() void { - \\ var handle_undef: promise = undefined; - \\ if (handle_undef == @handle()) return 0; - \\} - , - ".tmp_source.zig:7:25: error: @handle() in non-async function", - ); } -- cgit v1.2.3 From 298abbcff86629273f24891d243fb6e503392e8f Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 02:55:31 +0900 Subject: better support for `_` identifier * disallow variable declaration of `_` * prevent `_` from shadowing itself * prevent read access of `_` closes #1204 closes #1320 --- src/ir.cpp | 16 ++++++++++++- test/behavior.zig | 1 + test/cases/underscore.zig | 28 +++++++++++++++++++++++ test/compile_errors.zig | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 test/cases/underscore.zig diff --git a/src/ir.cpp b/src/ir.cpp index 7d2881744d..8dd4bb41db 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3332,7 +3332,15 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime) { - VariableTableEntry *var = create_local_var(irb->codegen, node, scope, name, src_is_const, gen_is_const, is_shadowable, is_comptime); + bool is_underscored = name ? buf_eql_str(name, "_") : false; + VariableTableEntry *var = create_local_var( irb->codegen + , node + , scope + , (is_underscored ? nullptr : name) + , src_is_const + , gen_is_const + , (is_underscored ? true : is_shadowable) + , is_comptime ); if (is_comptime != nullptr || gen_is_const) { var->mem_slot_index = exec_next_mem_slot(irb->exec); var->owner_exec = irb->exec; @@ -5186,6 +5194,11 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; + if (buf_eql_str(variable_declaration->symbol, "_")) { + add_node_error(irb->codegen, node, buf_sprintf("`_` is not a declarable symbol")); + return irb->codegen->invalid_instruction; + } + IrInstruction *type_instruction; if (variable_declaration->type != nullptr) { type_instruction = ir_gen_node(irb, variable_declaration->type, scope); @@ -5198,6 +5211,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod bool is_shadowable = false; bool is_const = variable_declaration->is_const; bool is_extern = variable_declaration->is_extern; + IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime); VariableTableEntry *var = ir_create_var(irb, node, scope, variable_declaration->symbol, diff --git a/test/behavior.zig b/test/behavior.zig index b03336eb71..e993d7e0dc 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -60,6 +60,7 @@ comptime { _ = @import("cases/try.zig"); _ = @import("cases/type_info.zig"); _ = @import("cases/undefined.zig"); + _ = @import("cases/underscore.zig"); _ = @import("cases/union.zig"); _ = @import("cases/var_args.zig"); _ = @import("cases/void.zig"); diff --git a/test/cases/underscore.zig b/test/cases/underscore.zig new file mode 100644 index 0000000000..44451e3723 --- /dev/null +++ b/test/cases/underscore.zig @@ -0,0 +1,28 @@ +const std = @import("std"); +const assert = std.debug.assert; + +test "ignore lval with underscore" { + _ = false; +} + +test "ignore lval with underscore (for loop)" { + for ([]void{}) |_, i| { + for ([]void{}) |_, j| { + break; + } + break; + } +} + +test "ignore lval with underscore (while loop)" { + while (optionalReturnError()) |_| { + while (optionalReturnError()) |_| { + break; + } else |_| { } + break; + } else |_| { } +} + +fn optionalReturnError() !?u32 { + return error.optionalReturnError; +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 948d212e58..56b2c51d74 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -22,6 +22,64 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:28: error: @handle() in non-async function", ); + cases.add( + "`_` is not a declarable symbol", + \\export fn f1() usize { + \\ var _: usize = 2; + \\ return _; + \\} + , + ".tmp_source.zig:2:5: error: `_` is not a declarable symbol", + ".tmp_source.zig:3:12: error: use of undeclared identifier '_'", + ); + + cases.add( + "`_` should not be usable inside for", + \\export fn returns() void { + \\ for ([]void{}) |_, i| { + \\ for ([]void{}) |_, j| { + \\ return _; + \\ } + \\ } + \\} + , + ".tmp_source.zig:4:20: error: use of undeclared identifier '_'", + ); + + cases.add( + "`_` should not be usable inside while", + \\export fn returns() void { + \\ while (optionalReturn()) |_| { + \\ while (optionalReturn()) |_| { + \\ return _; + \\ } + \\ } + \\} + \\fn optionalReturn() ?u32 { + \\ return 1; + \\} + , + ".tmp_source.zig:4:20: error: use of undeclared identifier '_'", + ); + + cases.add( + "`_` should not be usable inside while else", + \\export fn returns() void { + \\ while (optionalReturnError()) |_| { + \\ while (optionalReturnError()) |_| { + \\ return; + \\ } else |_| { + \\ if (_ == error.optionalReturnError) return; + \\ } + \\ } + \\} + \\fn optionalReturnError() !?u32 { + \\ return error.optionalReturnError; + \\} + , + ".tmp_source.zig:6:17: error: use of undeclared identifier '_'", + ); + cases.add( "while loop body expression ignored", \\fn returns() usize { -- cgit v1.2.3 From c2a08d7c516899b4c794bd6c3fc07ddb22d5876a Mon Sep 17 00:00:00 2001 From: "Matthew D. Steele" Date: Fri, 3 Aug 2018 11:44:39 -0400 Subject: Fix the start-less-than-end assertion in std.rand.Random.range (#1325) The function returns a value in [start, end), but was asserting start <= end instead of start < end. With this fix, range(1, 1) will now assertion error instead of dividing by zero. --- std/rand/index.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/std/rand/index.zig b/std/rand/index.zig index 7daa558f13..2cbff049ea 100644 --- a/std/rand/index.zig +++ b/std/rand/index.zig @@ -30,7 +30,7 @@ pub const DefaultCsprng = Isaac64; pub const Random = struct { fillFn: fn (r: *Random, buf: []u8) void, - /// Read random bytes into the specified buffer until fill. + /// Read random bytes into the specified buffer until full. pub fn bytes(r: *Random, buf: []u8) void { r.fillFn(r, buf); } @@ -48,10 +48,10 @@ pub const Random = struct { } } - /// Get a random unsigned integer with even distribution between `start` - /// inclusive and `end` exclusive. + /// Return a random integer with even distribution between `start` + /// inclusive and `end` exclusive. `start` must be less than `end`. pub fn range(r: *Random, comptime T: type, start: T, end: T) T { - assert(start <= end); + assert(start < end); if (T.is_signed) { const uint = @IntType(false, T.bit_count); if (start >= 0 and end >= 0) { @@ -664,6 +664,7 @@ test "Random range" { testRange(&prng.random, -4, 3); testRange(&prng.random, -4, -1); testRange(&prng.random, 10, 14); + // TODO: test that prng.random.range(1, 1) causes an assertion error } fn testRange(r: *Random, start: i32, end: i32) void { -- cgit v1.2.3 From dcaaa241dfb7ce77b7070df8d6a939a9f0749e2c Mon Sep 17 00:00:00 2001 From: "Matthew D. Steele" Date: Fri, 3 Aug 2018 11:45:23 -0400 Subject: Fix a type error in std.os.linux.getpid() (#1326) syscall0() returns usize, but we were trying to @bitCast to i32. --- std/os/linux/index.zig | 2 +- std/os/linux/test.zig | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 69bc30bad0..15607ea6c0 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -944,7 +944,7 @@ pub fn setgroups(size: usize, list: *const u32) usize { } pub fn getpid() i32 { - return @bitCast(i32, u32(syscall0(SYS_getpid))); + return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid))); } pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig index e7dae3a584..4de26012c7 100644 --- a/std/os/linux/test.zig +++ b/std/os/linux/test.zig @@ -3,6 +3,10 @@ const builtin = @import("builtin"); const linux = std.os.linux; const assert = std.debug.assert; +test "getpid" { + assert(linux.getpid() != 0); +} + test "timer" { const epoll_fd = linux.epoll_create(); var err = linux.getErrno(epoll_fd); -- cgit v1.2.3 From c66c6304f90aa9ea9983134c857ba7ea014004a3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Aug 2018 15:20:19 -0400 Subject: add a friendly note in .gitignore --- .gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.gitignore b/.gitignore index 5616da8e58..4b7bff11a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,14 @@ +# This file is for zig-specific build artifacts. +# If you have OS-specific or editor-specific files to ignore, +# such as *.swp or .DS_Store, put those in your global +# ~/.gitignore and put this in your ~/.gitconfig: +# +# [core] +# excludesfile = ~/.gitignore +# +# Cheers! +# -andrewrk + zig-cache/ build/ build-*/ -- cgit v1.2.3 From 9bd8b01650f9cf21e601117951711b21aa5fd216 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Aug 2018 15:21:08 -0400 Subject: fix tagged union initialization with a runtime void closes #1328 --- src/ir.cpp | 11 ++++++++++- test/cases/union.zig | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 8dd4bb41db..3e423487aa 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9628,6 +9628,9 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un case ConstValSpecialStatic: return &value->value; case ConstValSpecialRuntime: + if (!type_has_bits(value->value.type)) { + return &value->value; + } ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression")); return nullptr; case ConstValSpecialUndef: @@ -16129,8 +16132,14 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir if (casted_field_value == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; + type_ensure_zero_bits_known(ira->codegen, casted_field_value->value.type); + if (type_is_invalid(casted_field_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); - if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) { + if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime || + !type_has_bits(casted_field_value->value.type)) + { ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk); if (!field_val) return ira->codegen->builtin_types.entry_invalid; diff --git a/test/cases/union.zig b/test/cases/union.zig index 78b2dc8dd7..08969e64fe 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -297,3 +297,17 @@ test "access a member of tagged union with conflicting enum tag name" { comptime assert(Bar.A == u8); } + +test "tagged union initialization with runtime void" { + assert(testTaggedUnionInit({})); +} + +const TaggedUnionWithAVoid = union(enum) { + A, + B: i32, +}; + +fn testTaggedUnionInit(x: var) bool { + const y = TaggedUnionWithAVoid{ .A = x }; + return @TagType(TaggedUnionWithAVoid)(y) == TaggedUnionWithAVoid.A; +} -- cgit v1.2.3 From aa232089f2beaa273458a9fa75b2ba5c70f71805 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 5 Aug 2018 18:06:39 -0400 Subject: translate-c: fix while loop with no body --- src/translate_c.cpp | 9 +++++++-- test/translate_c.zig | 10 ++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 267a716a9d..ceb6f2a0bc 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -3015,9 +3015,14 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt)); case Stmt::DeclStmtClass: return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope); - case Stmt::WhileStmtClass: + case Stmt::WhileStmtClass: { + AstNode *while_node = trans_while_loop(c, scope, (const WhileStmt *)stmt); + if (while_node->data.while_expr.body == nullptr) { + while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); + } return wrap_stmt(out_node, out_child_scope, scope, - trans_while_loop(c, scope, (const WhileStmt *)stmt)); + while_node); + } case Stmt::IfStmtClass: return wrap_stmt(out_node, out_child_scope, scope, trans_if_statement(c, scope, (const IfStmt *)stmt)); diff --git a/test/translate_c.zig b/test/translate_c.zig index 417171d2c2..1d1e0ee1c6 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,16 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("while with empty body", + \\void foo(void) { + \\ while (1); + \\} + , + \\pub fn foo() void { + \\ while (1 != 0) {} + \\} + ); + cases.add("double define struct", \\typedef struct Bar Bar; \\typedef struct Foo Foo; -- cgit v1.2.3 From c420b234cc5698d44b11cf313f14a9608328f111 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 5 Aug 2018 18:18:24 -0400 Subject: translate-c: handle for loop with empty body --- src/translate_c.cpp | 13 ++++++++----- test/translate_c.zig | 10 ++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index ceb6f2a0bc..bf31dc7c3d 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -3020,8 +3020,7 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, if (while_node->data.while_expr.body == nullptr) { while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); } - return wrap_stmt(out_node, out_child_scope, scope, - while_node); + return wrap_stmt(out_node, out_child_scope, scope, while_node); } case Stmt::IfStmtClass: return wrap_stmt(out_node, out_child_scope, scope, @@ -3048,9 +3047,13 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, case Stmt::DoStmtClass: return wrap_stmt(out_node, out_child_scope, scope, trans_do_loop(c, scope, (const DoStmt *)stmt)); - case Stmt::ForStmtClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_for_loop(c, scope, (const ForStmt *)stmt)); + case Stmt::ForStmtClass: { + AstNode *while_node = trans_for_loop(c, scope, (const ForStmt *)stmt); + if (while_node->data.while_expr.body == nullptr) { + while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); + } + return wrap_stmt(out_node, out_child_scope, scope, while_node); + } case Stmt::StringLiteralClass: return wrap_stmt(out_node, out_child_scope, scope, trans_string_literal(c, scope, (const StringLiteral *)stmt)); diff --git a/test/translate_c.zig b/test/translate_c.zig index 1d1e0ee1c6..8840ce1413 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,16 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("for with empty body", + \\void foo(void) { + \\ for (;;); + \\} + , + \\pub fn foo() void { + \\ while (true) {} + \\} + ); + cases.add("while with empty body", \\void foo(void) { \\ while (1); -- cgit v1.2.3 From 387fab60a61611c6cb48a5d9dd9d356911733210 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 5 Aug 2018 18:32:38 -0400 Subject: translate-c: fix do while with empty body --- src/translate_c.cpp | 15 +++++++++++---- test/translate_c.zig | 12 ++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index bf31dc7c3d..eaa6621147 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -2707,7 +2707,9 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt AstNode *child_statement; child_scope = trans_stmt(c, &child_block_scope->base, stmt->getBody(), &child_statement); if (child_scope == nullptr) return nullptr; - body_node->data.block.statements.append(child_statement); + if (child_statement != nullptr) { + body_node->data.block.statements.append(child_statement); + } } // if (!cond) break; @@ -2717,6 +2719,7 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt terminator_node->data.if_bool_expr.condition = trans_create_node_prefix_op(c, PrefixOpBoolNot, condition_node); terminator_node->data.if_bool_expr.then_block = trans_create_node(c, NodeTypeBreak); + assert(terminator_node != nullptr); body_node->data.block.statements.append(terminator_node); while_scope->node->data.while_expr.body = body_node; @@ -3044,9 +3047,13 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, case Stmt::UnaryExprOrTypeTraitExprClass: return wrap_stmt(out_node, out_child_scope, scope, trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt)); - case Stmt::DoStmtClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_do_loop(c, scope, (const DoStmt *)stmt)); + case Stmt::DoStmtClass: { + AstNode *while_node = trans_do_loop(c, scope, (const DoStmt *)stmt); + if (while_node->data.while_expr.body == nullptr) { + while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); + } + return wrap_stmt(out_node, out_child_scope, scope, while_node); + } case Stmt::ForStmtClass: { AstNode *while_node = trans_for_loop(c, scope, (const ForStmt *)stmt); if (while_node->data.while_expr.body == nullptr) { diff --git a/test/translate_c.zig b/test/translate_c.zig index 8840ce1413..c14d75c254 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,18 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("do while with empty body", + \\void foo(void) { + \\ do ; while (1); + \\} + , // TODO this should be if (1 != 0) break + \\pub fn foo() void { + \\ while (true) { + \\ if (!1) break; + \\ } + \\} + ); + cases.add("for with empty body", \\void foo(void) { \\ for (;;); -- cgit v1.2.3 From 63a23e848a62d5f167f8d5478de9766cb24aa6eb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 5 Aug 2018 18:40:14 -0400 Subject: translate-c: fix for loops with var init and empty body --- src/translate_c.cpp | 16 ++++++++++------ test/translate_c.zig | 13 +++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index eaa6621147..735a671bcc 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -2783,7 +2783,12 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(), &body_statement); if (body_scope == nullptr) return nullptr; - while_scope->node->data.while_expr.body = body_statement; + + if (body_statement == nullptr) { + while_scope->node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); + } else { + while_scope->node->data.while_expr.body = body_statement; + } return loop_block_node; } @@ -3020,6 +3025,7 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope); case Stmt::WhileStmtClass: { AstNode *while_node = trans_while_loop(c, scope, (const WhileStmt *)stmt); + assert(while_node->type == NodeTypeWhileExpr); if (while_node->data.while_expr.body == nullptr) { while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); } @@ -3049,17 +3055,15 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt)); case Stmt::DoStmtClass: { AstNode *while_node = trans_do_loop(c, scope, (const DoStmt *)stmt); + assert(while_node->type == NodeTypeWhileExpr); if (while_node->data.while_expr.body == nullptr) { while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); } return wrap_stmt(out_node, out_child_scope, scope, while_node); } case Stmt::ForStmtClass: { - AstNode *while_node = trans_for_loop(c, scope, (const ForStmt *)stmt); - if (while_node->data.while_expr.body == nullptr) { - while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); - } - return wrap_stmt(out_node, out_child_scope, scope, while_node); + AstNode *node = trans_for_loop(c, scope, (const ForStmt *)stmt); + return wrap_stmt(out_node, out_child_scope, scope, node); } case Stmt::StringLiteralClass: return wrap_stmt(out_node, out_child_scope, scope, diff --git a/test/translate_c.zig b/test/translate_c.zig index c14d75c254..b31e515aa2 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,19 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("for loop with var init but empty body", + \\void foo(void) { + \\ for (int x = 0; x < 10; x++); + \\} + , + \\pub fn foo() void { + \\ { + \\ var x: c_int = 0; + \\ while (x < 10) : (x += 1) {} + \\ } + \\} + ); + cases.add("do while with empty body", \\void foo(void) { \\ do ; while (1); -- cgit v1.2.3